@serifpersia/dango 3.4.2 → 3.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/client/dist/assets/{AnimeCard-CIwTZI3D.js → AnimeCard-4CEz8Buu.js} +1 -1
- package/client/dist/assets/AnimeInfo-DyX9qTwg.js +1 -0
- package/client/dist/assets/AnimeInfoPage-Dvp_c8vK.js +2 -0
- package/client/dist/assets/Asmr-qg3lMAJn.js +2 -0
- package/client/dist/assets/{AsmrResetProgressModal-BDpA07Fa.js → AsmrResetProgressModal-BpPlg_Vv.js} +1 -1
- package/client/dist/assets/AvSyncCalibrator-C2wu1ydI.css +1 -0
- package/client/dist/assets/AvSyncCalibrator-FJllOgOH.js +10 -0
- package/client/dist/assets/Home-A7gCeEST.js +1 -0
- package/client/dist/assets/Insights-G9IXepHJ.js +1 -0
- package/client/dist/assets/{LibraryListPage-DWbcek1i.js → LibraryListPage-Bh2qCJPh.js} +1 -1
- package/client/dist/assets/{ListeningList-CcuxBzy1.js → ListeningList-Czv4cTRl.js} +1 -1
- package/client/dist/assets/{Manga-DpLaW39C.js → Manga-qdAAS1z7.js} +1 -1
- package/client/dist/assets/MangaInfoPage-1rTgZzf2.js +1 -0
- package/client/dist/assets/MangaReadPage-CeyxPG6w.js +1 -0
- package/client/dist/assets/{MangaResetProgressModal-CYguBG2f.js → MangaResetProgressModal-C_ppotmh.js} +1 -1
- package/client/dist/assets/Mature-Bkhxnr3K.js +1 -0
- package/client/dist/assets/{MediaCard-p29DqyKX.js → MediaCard-BQZsutEo.js} +1 -1
- package/client/dist/assets/Music-C0rGzSbi.js +1 -0
- package/client/dist/assets/{OptionListMenu-C1pvpu8X.js → OptionListMenu-CyfpCBEy.js} +1 -1
- package/client/dist/assets/Player-yhiTlpYU.js +56 -0
- package/client/dist/assets/PlayerSettings-icy4SfQa.js +1 -0
- package/client/dist/assets/{QueueOptionsButton-CWdoe1_I.js → QueueOptionsButton-5eloKF5J.js} +1 -1
- package/client/dist/assets/{QueueRail-BBpn9CII.js → QueueRail-DaMjlKL4.js} +1 -1
- package/client/dist/assets/Radio-FxGqCxR4.js +1 -0
- package/client/dist/assets/{ReadingList-D4xQ9tlw.js → ReadingList-CCm3IAbt.js} +1 -1
- package/client/dist/assets/{ResetProgressModal-JFlbXy7W.js → ResetProgressModal-CeI4V3Y5.js} +1 -1
- package/client/dist/assets/{Search-D-ocogP-.js → Search-qpILaAiQ.js} +1 -1
- package/client/dist/assets/Settings-Bw9M3NGz.js +21 -0
- package/client/dist/assets/{SkeletonGrid-N67HqYXx.js → SkeletonGrid-0dY_xMq3.js} +1 -1
- package/client/dist/assets/{Trackers-C_SPMGKx.js → Trackers-Zy5GOs0P.js} +2 -2
- package/client/dist/assets/Tv-Bx7tGS57.css +1 -0
- package/client/dist/assets/Tv-DREwc7BX.js +4 -0
- package/client/dist/assets/TvInfo-CHIsVzZ0.js +1 -0
- package/client/dist/assets/{TvPopup-DEB6Ufq4.js → TvPopup-D_tVTyZ2.js} +1 -1
- package/client/dist/assets/{TvSearch-grOUL0Bc.js → TvSearch-BVxedBW-.js} +1 -1
- package/client/dist/assets/{TvWatchlist-CBI21Vge.js → TvWatchlist-BEIwq9R9.js} +1 -1
- package/client/dist/assets/{Watchlist-5cDHUoC8.js → Watchlist-CSngjqlz.js} +1 -1
- package/client/dist/assets/{hls-DySVERd_.js → hls-D48GVTkk.js} +1 -1
- package/client/dist/assets/index-CVB5dYhT.js +178 -0
- package/client/dist/assets/{useAnimeInfoData-BMwVSGhZ.js → useAnimeInfoData-DxdOg_B7.js} +1 -1
- package/client/dist/assets/useAsmr-ClckxnPe.js +1 -0
- package/client/dist/assets/{useAsmrPopup-CY-6b88q.js → useAsmrPopup-Bbgxefy_.js} +1 -1
- package/client/dist/assets/useMangaLibrary-B3_BUKVk.js +1 -0
- package/client/dist/assets/{useMangaPopup-_pNXvRYJ.js → useMangaPopup-Celk4lzf.js} +1 -1
- package/client/dist/assets/{useProviders-CKSedfqb.js → useProviders-pwxPZPHh.js} +1 -1
- package/client/dist/assets/useTv-D9sQrDop.js +1 -0
- package/client/dist/assets/useTvLibrary-UsQpgBTB.js +1 -0
- package/client/dist/index.html +1 -1
- package/package.json +3 -3
- package/server/dist/app-hono.js +15 -37
- package/server/dist/db/drizzle.js +2 -18
- package/server/dist/db.js +3 -2
- package/server/dist/hono/tv.js +9 -5
- package/server/dist/hono/watchlist.js +10 -108
- package/server/dist/lib/migration.js +0 -5
- package/server/dist/providers/remote-loader.js +40 -1
- package/server/dist/providers/remote-types.js +9 -2
- package/server/dist/server.js +50 -182
- package/server/dist/sync.js +173 -704
- package/server/dist/trpc/routers/auth.js +2 -71
- package/server/dist/trpc/routers/continue-watching.js +0 -2
- package/server/dist/trpc/routers/data.js +0 -1
- package/server/dist/trpc/routers/discord.js +8 -0
- package/server/dist/trpc/routers/progress.js +95 -1
- package/server/dist/trpc/routers/queue.js +0 -2
- package/server/dist/trpc/routers/watchlist.js +0 -7
- package/server/dist/trpc/validation.js +64 -0
- package/server/package.json +2 -2
- package/client/dist/assets/AnimeInfo-DlfvDaR8.js +0 -1
- package/client/dist/assets/AnimeInfoPage-B3EPwtzx.js +0 -2
- package/client/dist/assets/Asmr-DCirAWM6.js +0 -2
- package/client/dist/assets/AvSyncCalibrator-BPa6KaGC.css +0 -1
- package/client/dist/assets/AvSyncCalibrator-BroSW86T.js +0 -10
- package/client/dist/assets/Home-DEjYtHbs.js +0 -1
- package/client/dist/assets/Insights-B3b6u8RW.js +0 -1
- package/client/dist/assets/MangaInfoPage-B7FPC4a_.js +0 -1
- package/client/dist/assets/MangaReadPage-Dx0qAAFe.js +0 -1
- package/client/dist/assets/Mature-AScDPmWW.js +0 -1
- package/client/dist/assets/Music-DFInMskb.js +0 -1
- package/client/dist/assets/Player-D-8zY3cx.js +0 -56
- package/client/dist/assets/PlayerSettings-CfJjs_0Q.js +0 -1
- package/client/dist/assets/Radio-DCgO5hAS.js +0 -1
- package/client/dist/assets/Settings-BsFbgkdy.js +0 -21
- package/client/dist/assets/Tv-D7s0Kqqx.js +0 -4
- package/client/dist/assets/Tv-ZEA45gXK.css +0 -1
- package/client/dist/assets/TvInfo-CrmCSFDF.js +0 -1
- package/client/dist/assets/index-HZNJp28e.js +0 -178
- package/client/dist/assets/useAsmr-BLc7FwPm.js +0 -1
- package/client/dist/assets/useMangaLibrary-XFRTq5Qg.js +0 -1
- package/client/dist/assets/useMatureConsent-Cra09TA0.js +0 -1
- package/client/dist/assets/useTv-Dizj6G4R.js +0 -1
- package/client/dist/assets/useTvLibrary-twjHQ89n.js +0 -1
package/server/dist/sync.js
CHANGED
|
@@ -16,42 +16,76 @@ import { ANIME_SYNC_TABLES, exportTables, importTables, MANGA_SYNC_TABLES, TV_SY
|
|
|
16
16
|
const log = logger.child({ module: 'Sync' });
|
|
17
17
|
export const SYNC_TABLES = ANIME_SYNC_TABLES;
|
|
18
18
|
export { MANGA_SYNC_TABLES, TV_SYNC_TABLES, ASMR_SYNC_TABLES };
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
export const MEDIA_KINDS = ['anime', 'manga', 'tv', 'asmr'];
|
|
20
|
+
function cap(s) {
|
|
21
|
+
return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
|
|
22
|
+
}
|
|
23
|
+
export const KIND_SYNC = {
|
|
24
|
+
anime: {
|
|
25
|
+
mid: '',
|
|
26
|
+
Head: '',
|
|
27
|
+
rcloneLabel: 'rclone',
|
|
28
|
+
tables: SYNC_TABLES,
|
|
24
29
|
libraryTables: ['watchlist', 'watched_episodes'],
|
|
25
30
|
backupName: 'pre-sync-backup.db',
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
manifestPath: CONFIG.LOCAL_MANIFEST_PATH,
|
|
32
|
+
rcloneFile: CONFIG.RCLONE_SYNC_FILENAME,
|
|
33
|
+
githubDown: (db) => githubSyncService.syncDown(db),
|
|
34
|
+
githubUp: (db) => githubSyncService.syncUp(db),
|
|
35
|
+
googleDown: (db) => googleDriveService.syncDown(db),
|
|
36
|
+
githubVersion: () => githubSyncService.getRemoteVersion(),
|
|
37
|
+
googleVersion: () => googleDriveService.getRemoteVersion(),
|
|
38
|
+
googleUp: (db) => googleDriveService.syncUp(db),
|
|
39
|
+
},
|
|
40
|
+
manga: {
|
|
41
|
+
mid: 'manga ',
|
|
42
|
+
Head: 'Manga ',
|
|
43
|
+
rcloneLabel: 'rclone manga',
|
|
44
|
+
tables: MANGA_SYNC_TABLES,
|
|
39
45
|
libraryTables: ['manga_library', 'manga_progress'],
|
|
40
46
|
backupName: 'pre-sync-manga-backup.db',
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
manifestPath: CONFIG.MANGA_LOCAL_MANIFEST_PATH,
|
|
48
|
+
rcloneFile: CONFIG.MANGA_RCLONE_SYNC_FILENAME,
|
|
49
|
+
githubDown: (db) => githubSyncService.syncMangaDown(db),
|
|
50
|
+
githubUp: (db) => githubSyncService.syncMangaUp(db),
|
|
51
|
+
googleDown: (db) => googleDriveService.syncMangaDown(db),
|
|
52
|
+
githubVersion: () => githubSyncService.getMangaRemoteVersion(),
|
|
53
|
+
googleVersion: () => googleDriveService.getMangaRemoteVersion(),
|
|
54
|
+
googleUp: (db) => googleDriveService.syncMangaUp(db),
|
|
55
|
+
},
|
|
56
|
+
tv: {
|
|
57
|
+
mid: 'TV ',
|
|
58
|
+
Head: 'TV ',
|
|
59
|
+
rcloneLabel: 'rclone tv',
|
|
60
|
+
tables: TV_SYNC_TABLES,
|
|
45
61
|
libraryTables: ['tv_library', 'tv_progress'],
|
|
46
62
|
backupName: 'pre-sync-tv-backup.db',
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
63
|
+
manifestPath: CONFIG.TV_LOCAL_MANIFEST_PATH,
|
|
64
|
+
rcloneFile: CONFIG.TV_RCLONE_SYNC_FILENAME,
|
|
65
|
+
githubDown: (db) => githubSyncService.syncTvDown(db),
|
|
66
|
+
githubUp: (db) => githubSyncService.syncTvUp(db),
|
|
67
|
+
googleDown: (db) => googleDriveService.syncTvDown(db),
|
|
68
|
+
githubVersion: () => githubSyncService.getTvRemoteVersion(),
|
|
69
|
+
googleVersion: () => googleDriveService.getTvRemoteVersion(),
|
|
70
|
+
googleUp: (db) => googleDriveService.syncTvUp(db),
|
|
71
|
+
},
|
|
72
|
+
asmr: {
|
|
73
|
+
mid: 'ASMR ',
|
|
74
|
+
Head: 'ASMR ',
|
|
75
|
+
rcloneLabel: 'rclone asmr',
|
|
76
|
+
tables: ASMR_SYNC_TABLES,
|
|
51
77
|
libraryTables: ['asmr_library', 'asmr_progress'],
|
|
52
78
|
backupName: 'pre-sync-asmr-backup.db',
|
|
53
|
-
|
|
54
|
-
|
|
79
|
+
manifestPath: CONFIG.ASMR_LOCAL_MANIFEST_PATH,
|
|
80
|
+
rcloneFile: CONFIG.ASMR_RCLONE_SYNC_FILENAME,
|
|
81
|
+
githubDown: (db) => githubSyncService.syncAsmrDown(db),
|
|
82
|
+
githubUp: (db) => githubSyncService.syncAsmrUp(db),
|
|
83
|
+
googleDown: (db) => googleDriveService.syncAsmrDown(db),
|
|
84
|
+
githubVersion: () => githubSyncService.getAsmrRemoteVersion(),
|
|
85
|
+
googleVersion: () => googleDriveService.getAsmrRemoteVersion(),
|
|
86
|
+
googleUp: (db) => googleDriveService.syncAsmrUp(db),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
55
89
|
async function getRcloneRemotePayloadVersion(remoteFolder, fileName = CONFIG.RCLONE_SYNC_FILENAME) {
|
|
56
90
|
const exists = await rcloneService.fileExists(remoteFolder, fileName);
|
|
57
91
|
if (!exists)
|
|
@@ -71,103 +105,30 @@ async function getRcloneRemotePayloadVersion(remoteFolder, fileName = CONFIG.RCL
|
|
|
71
105
|
await fs.unlink(tempPath);
|
|
72
106
|
}
|
|
73
107
|
}
|
|
74
|
-
async function rcloneSyncUp(db,
|
|
75
|
-
const
|
|
76
|
-
const
|
|
108
|
+
async function rcloneSyncUp(db, kind, remoteFolder) {
|
|
109
|
+
const def = KIND_SYNC[kind];
|
|
110
|
+
const payload = exportTables(db, def.tables);
|
|
111
|
+
const tempPath = path.join(CONFIG.ROOT, `temp_${Date.now()}_rclone_${kind}_up.json`);
|
|
77
112
|
try {
|
|
78
113
|
await fs.writeFile(tempPath, JSON.stringify(payload, null, 2));
|
|
79
|
-
await rcloneService.uploadFile(tempPath, remoteFolder,
|
|
80
|
-
}
|
|
81
|
-
finally {
|
|
82
|
-
if (existsSync(tempPath))
|
|
83
|
-
await fs.unlink(tempPath).catch(() => { });
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
async function rcloneSyncDown(db, remoteFolder, fileName = CONFIG.RCLONE_SYNC_FILENAME) {
|
|
87
|
-
const tempPath = path.join(CONFIG.ROOT, `temp_${Date.now()}_rclone_down.json`);
|
|
88
|
-
try {
|
|
89
|
-
await rcloneService.downloadFile(remoteFolder, fileName, tempPath);
|
|
90
|
-
const content = await fs.readFile(tempPath, 'utf-8');
|
|
91
|
-
const payload = normalizePayload(JSON.parse(content), SYNC_TABLES, 'rclone');
|
|
92
|
-
importSyncPayload(db, payload);
|
|
93
|
-
return readPayloadVersion(payload);
|
|
94
|
-
}
|
|
95
|
-
finally {
|
|
96
|
-
if (existsSync(tempPath))
|
|
97
|
-
await fs.unlink(tempPath).catch(() => { });
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
async function rcloneMangaSyncUp(db, remoteFolder) {
|
|
101
|
-
const payload = await exportMangaSyncPayload(db);
|
|
102
|
-
const tempPath = path.join(CONFIG.ROOT, `temp_${Date.now()}_rclone_manga_up.json`);
|
|
103
|
-
try {
|
|
104
|
-
await fs.writeFile(tempPath, JSON.stringify(payload, null, 2));
|
|
105
|
-
await rcloneService.uploadFile(tempPath, remoteFolder, CONFIG.MANGA_RCLONE_SYNC_FILENAME);
|
|
106
|
-
}
|
|
107
|
-
finally {
|
|
108
|
-
if (existsSync(tempPath))
|
|
109
|
-
await fs.unlink(tempPath).catch(() => { });
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
async function rcloneTvSyncUp(db, remoteFolder) {
|
|
113
|
-
const payload = await exportTvSyncPayload(db);
|
|
114
|
-
const tempPath = path.join(CONFIG.ROOT, `temp_${Date.now()}_rclone_tv_up.json`);
|
|
115
|
-
try {
|
|
116
|
-
await fs.writeFile(tempPath, JSON.stringify(payload, null, 2));
|
|
117
|
-
await rcloneService.uploadFile(tempPath, remoteFolder, CONFIG.TV_RCLONE_SYNC_FILENAME);
|
|
118
|
-
}
|
|
119
|
-
finally {
|
|
120
|
-
if (existsSync(tempPath))
|
|
121
|
-
await fs.unlink(tempPath).catch(() => { });
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
async function rcloneAsmrSyncUp(db, remoteFolder) {
|
|
125
|
-
const payload = await exportAsmrSyncPayload(db);
|
|
126
|
-
const tempPath = path.join(CONFIG.ROOT, `temp_${Date.now()}_rclone_asmr_up.json`);
|
|
127
|
-
try {
|
|
128
|
-
await fs.writeFile(tempPath, JSON.stringify(payload, null, 2));
|
|
129
|
-
await rcloneService.uploadFile(tempPath, remoteFolder, CONFIG.ASMR_RCLONE_SYNC_FILENAME);
|
|
130
|
-
}
|
|
131
|
-
finally {
|
|
132
|
-
if (existsSync(tempPath))
|
|
133
|
-
await fs.unlink(tempPath).catch(() => { });
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
async function rcloneMangaSyncDown(db, remoteFolder) {
|
|
137
|
-
const tempPath = path.join(CONFIG.ROOT, `temp_${Date.now()}_rclone_manga_down.json`);
|
|
138
|
-
try {
|
|
139
|
-
await rcloneService.downloadFile(remoteFolder, CONFIG.MANGA_RCLONE_SYNC_FILENAME, tempPath);
|
|
140
|
-
const content = await fs.readFile(tempPath, 'utf-8');
|
|
141
|
-
const payload = normalizePayload(JSON.parse(content), MANGA_SYNC_TABLES, 'rclone manga');
|
|
142
|
-
importMangaSyncPayload(db, payload);
|
|
143
|
-
return readPayloadVersion(payload);
|
|
114
|
+
await rcloneService.uploadFile(tempPath, remoteFolder, def.rcloneFile);
|
|
144
115
|
}
|
|
145
116
|
finally {
|
|
146
117
|
if (existsSync(tempPath))
|
|
147
118
|
await fs.unlink(tempPath).catch(() => { });
|
|
148
119
|
}
|
|
149
120
|
}
|
|
150
|
-
async function
|
|
151
|
-
const
|
|
121
|
+
async function rcloneSyncDown(db, kind, remoteFolder) {
|
|
122
|
+
const def = KIND_SYNC[kind];
|
|
123
|
+
const tempPath = path.join(CONFIG.ROOT, `temp_${Date.now()}_rclone_${kind}_down.json`);
|
|
152
124
|
try {
|
|
153
|
-
await rcloneService.downloadFile(remoteFolder,
|
|
125
|
+
await rcloneService.downloadFile(remoteFolder, def.rcloneFile, tempPath);
|
|
154
126
|
const content = await fs.readFile(tempPath, 'utf-8');
|
|
155
|
-
const payload = normalizePayload(JSON.parse(content),
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (existsSync(tempPath))
|
|
161
|
-
await fs.unlink(tempPath).catch(() => { });
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
async function rcloneAsmrSyncDown(db, remoteFolder) {
|
|
165
|
-
const tempPath = path.join(CONFIG.ROOT, `temp_${Date.now()}_rclone_asmr_down.json`);
|
|
166
|
-
try {
|
|
167
|
-
await rcloneService.downloadFile(remoteFolder, CONFIG.ASMR_RCLONE_SYNC_FILENAME, tempPath);
|
|
168
|
-
const content = await fs.readFile(tempPath, 'utf-8');
|
|
169
|
-
const payload = normalizePayload(JSON.parse(content), ASMR_SYNC_TABLES, 'rclone asmr');
|
|
170
|
-
importAsmrSyncPayload(db, payload);
|
|
127
|
+
const payload = normalizePayload(JSON.parse(content), def.tables, def.rcloneLabel);
|
|
128
|
+
importTables(db, def.tables, payload, {
|
|
129
|
+
libraryTables: def.libraryTables,
|
|
130
|
+
backupName: def.backupName,
|
|
131
|
+
});
|
|
171
132
|
return readPayloadVersion(payload);
|
|
172
133
|
}
|
|
173
134
|
finally {
|
|
@@ -253,40 +214,10 @@ export async function initSyncProvider(preferred) {
|
|
|
253
214
|
activeProvider = 'none';
|
|
254
215
|
log.info('No sync provider available.');
|
|
255
216
|
}
|
|
256
|
-
export async function getLocalManifestVersion() {
|
|
257
|
-
if (existsSync(
|
|
258
|
-
try {
|
|
259
|
-
const content = await fs.readFile(CONFIG.LOCAL_MANIFEST_PATH, 'utf-8');
|
|
260
|
-
return JSON.parse(content).version || 0;
|
|
261
|
-
}
|
|
262
|
-
catch {
|
|
263
|
-
return 0;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
return 0;
|
|
267
|
-
}
|
|
268
|
-
export async function setLocalManifestVersion(version) {
|
|
269
|
-
await fs.writeFile(CONFIG.LOCAL_MANIFEST_PATH, JSON.stringify({ version }));
|
|
270
|
-
}
|
|
271
|
-
export async function getLocalMangaManifestVersion() {
|
|
272
|
-
if (existsSync(CONFIG.MANGA_LOCAL_MANIFEST_PATH)) {
|
|
273
|
-
try {
|
|
274
|
-
const content = await fs.readFile(CONFIG.MANGA_LOCAL_MANIFEST_PATH, 'utf-8');
|
|
275
|
-
return JSON.parse(content).version || 0;
|
|
276
|
-
}
|
|
277
|
-
catch {
|
|
278
|
-
return 0;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
return 0;
|
|
282
|
-
}
|
|
283
|
-
export async function setLocalMangaManifestVersion(version) {
|
|
284
|
-
await fs.writeFile(CONFIG.MANGA_LOCAL_MANIFEST_PATH, JSON.stringify({ version }));
|
|
285
|
-
}
|
|
286
|
-
export async function getLocalTvManifestVersion() {
|
|
287
|
-
if (existsSync(CONFIG.TV_LOCAL_MANIFEST_PATH)) {
|
|
217
|
+
export async function getLocalManifestVersion(manifestPath = CONFIG.LOCAL_MANIFEST_PATH) {
|
|
218
|
+
if (existsSync(manifestPath)) {
|
|
288
219
|
try {
|
|
289
|
-
const content = await fs.readFile(
|
|
220
|
+
const content = await fs.readFile(manifestPath, 'utf-8');
|
|
290
221
|
return JSON.parse(content).version || 0;
|
|
291
222
|
}
|
|
292
223
|
catch {
|
|
@@ -295,226 +226,106 @@ export async function getLocalTvManifestVersion() {
|
|
|
295
226
|
}
|
|
296
227
|
return 0;
|
|
297
228
|
}
|
|
298
|
-
export async function
|
|
299
|
-
await fs.writeFile(
|
|
300
|
-
}
|
|
301
|
-
export async function getLocalAsmrManifestVersion() {
|
|
302
|
-
if (existsSync(CONFIG.ASMR_LOCAL_MANIFEST_PATH)) {
|
|
303
|
-
try {
|
|
304
|
-
const content = await fs.readFile(CONFIG.ASMR_LOCAL_MANIFEST_PATH, 'utf-8');
|
|
305
|
-
return JSON.parse(content).version || 0;
|
|
306
|
-
}
|
|
307
|
-
catch {
|
|
308
|
-
return 0;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
return 0;
|
|
229
|
+
export async function setLocalManifestVersion(version, manifestPath = CONFIG.LOCAL_MANIFEST_PATH) {
|
|
230
|
+
await fs.writeFile(manifestPath, JSON.stringify({ version }));
|
|
312
231
|
}
|
|
313
|
-
export
|
|
314
|
-
|
|
232
|
+
export function getKindManifestVersion(kind) {
|
|
233
|
+
return getLocalManifestVersion(KIND_SYNC[kind].manifestPath);
|
|
315
234
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
if (activeProvider === 'github') {
|
|
319
|
-
if (!githubSyncService.isAuthenticated())
|
|
320
|
-
return { version: 0 };
|
|
321
|
-
return { version: await githubSyncService.getRemoteVersion() };
|
|
322
|
-
}
|
|
323
|
-
else if (activeProvider === 'google') {
|
|
324
|
-
if (!googleDriveService.isAuthenticated())
|
|
325
|
-
return { version: 0 };
|
|
326
|
-
return { version: await googleDriveService.getRemoteVersion() };
|
|
327
|
-
}
|
|
328
|
-
else if (activeProvider === 'rclone') {
|
|
329
|
-
return { version: await getRcloneRemotePayloadVersion(remoteFolder) };
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
catch (err) {
|
|
333
|
-
log.warn({ err }, 'Could not read remote manifest.');
|
|
334
|
-
}
|
|
335
|
-
return { version: 0 };
|
|
235
|
+
export function setKindManifestVersion(kind, version) {
|
|
236
|
+
return setLocalManifestVersion(version, KIND_SYNC[kind].manifestPath);
|
|
336
237
|
}
|
|
337
|
-
async function
|
|
238
|
+
async function getRemoteManifestVersion(remoteFolder, kind = 'anime') {
|
|
239
|
+
const def = KIND_SYNC[kind];
|
|
240
|
+
const label = kind === 'anime' ? 'manifest' : `${def.mid.trim()} manifest`;
|
|
338
241
|
try {
|
|
339
242
|
if (activeProvider === 'github') {
|
|
340
243
|
if (!githubSyncService.isAuthenticated())
|
|
341
244
|
return { version: 0 };
|
|
342
|
-
return { version: await
|
|
245
|
+
return { version: await def.githubVersion() };
|
|
343
246
|
}
|
|
344
247
|
else if (activeProvider === 'google') {
|
|
345
248
|
if (!googleDriveService.isAuthenticated())
|
|
346
249
|
return { version: 0 };
|
|
347
|
-
return { version: await
|
|
250
|
+
return { version: await def.googleVersion() };
|
|
348
251
|
}
|
|
349
252
|
else if (activeProvider === 'rclone') {
|
|
350
|
-
return {
|
|
351
|
-
version: await getRcloneRemotePayloadVersion(remoteFolder, CONFIG.MANGA_RCLONE_SYNC_FILENAME),
|
|
352
|
-
};
|
|
253
|
+
return { version: await getRcloneRemotePayloadVersion(remoteFolder, def.rcloneFile) };
|
|
353
254
|
}
|
|
354
255
|
}
|
|
355
256
|
catch (err) {
|
|
356
|
-
log.warn({ err },
|
|
257
|
+
log.warn({ err }, `Could not read remote ${label}.`);
|
|
357
258
|
}
|
|
358
259
|
return { version: 0 };
|
|
359
260
|
}
|
|
360
|
-
async function
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
if (!githubSyncService.isAuthenticated())
|
|
364
|
-
return { version: 0 };
|
|
365
|
-
return { version: await githubSyncService.getTvRemoteVersion() };
|
|
366
|
-
}
|
|
367
|
-
else if (activeProvider === 'google') {
|
|
368
|
-
if (!googleDriveService.isAuthenticated())
|
|
369
|
-
return { version: 0 };
|
|
370
|
-
return { version: await googleDriveService.getTvRemoteVersion() };
|
|
371
|
-
}
|
|
372
|
-
else if (activeProvider === 'rclone') {
|
|
373
|
-
return {
|
|
374
|
-
version: await getRcloneRemotePayloadVersion(remoteFolder, CONFIG.TV_RCLONE_SYNC_FILENAME),
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
catch (err) {
|
|
379
|
-
log.warn({ err }, 'Could not read remote TV manifest.');
|
|
380
|
-
}
|
|
381
|
-
return { version: 0 };
|
|
382
|
-
}
|
|
383
|
-
async function getAsmrRemoteManifestVersion(remoteFolder) {
|
|
384
|
-
try {
|
|
385
|
-
if (activeProvider === 'github') {
|
|
386
|
-
if (!githubSyncService.isAuthenticated())
|
|
387
|
-
return { version: 0 };
|
|
388
|
-
return { version: await githubSyncService.getAsmrRemoteVersion() };
|
|
389
|
-
}
|
|
390
|
-
else if (activeProvider === 'google') {
|
|
391
|
-
if (!googleDriveService.isAuthenticated())
|
|
392
|
-
return { version: 0 };
|
|
393
|
-
return { version: await googleDriveService.getAsmrRemoteVersion() };
|
|
394
|
-
}
|
|
395
|
-
else if (activeProvider === 'rclone') {
|
|
396
|
-
return {
|
|
397
|
-
version: await getRcloneRemotePayloadVersion(remoteFolder, CONFIG.ASMR_RCLONE_SYNC_FILENAME),
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
catch (err) {
|
|
402
|
-
log.warn({ err }, 'Could not read remote ASMR manifest.');
|
|
403
|
-
}
|
|
404
|
-
return { version: 0 };
|
|
405
|
-
}
|
|
406
|
-
export async function syncDownOnBoot(db, dbPath, remoteFolderName, closeMainDb) {
|
|
407
|
-
let localVersion = await getLocalManifestVersion();
|
|
261
|
+
export async function syncDownOnBoot(db, kind, remoteFolderName) {
|
|
262
|
+
const def = KIND_SYNC[kind];
|
|
263
|
+
let localVersion = await getKindManifestVersion(kind);
|
|
408
264
|
if (localVersion === 0 && db) {
|
|
409
265
|
const row = dbGet(db, "SELECT value FROM sync_metadata WHERE key = 'db_version'");
|
|
410
266
|
localVersion = row?.value ?? 0;
|
|
411
267
|
if (localVersion > 0) {
|
|
412
|
-
await
|
|
268
|
+
await setKindManifestVersion(kind, localVersion);
|
|
413
269
|
}
|
|
414
270
|
}
|
|
415
271
|
if (activeProvider === 'none')
|
|
416
|
-
return
|
|
272
|
+
return;
|
|
417
273
|
await syncMutex.lock();
|
|
418
274
|
if (isSyncing) {
|
|
419
275
|
syncMutex.unlock();
|
|
420
|
-
return
|
|
276
|
+
return;
|
|
421
277
|
}
|
|
422
278
|
isSyncing = true;
|
|
423
279
|
try {
|
|
424
|
-
notifySyncStart(`Initial sync check (${activeProvider})`);
|
|
425
|
-
const { version: remoteVersion } = await getRemoteManifestVersion(remoteFolderName);
|
|
280
|
+
notifySyncStart(`Initial ${def.mid}sync check (${activeProvider})`);
|
|
281
|
+
const { version: remoteVersion } = await getRemoteManifestVersion(remoteFolderName, kind);
|
|
426
282
|
notifySyncEnd();
|
|
427
|
-
log.info(
|
|
283
|
+
log.info(`${def.Head}Sync Check: Local v${localVersion} vs Remote v${remoteVersion}`);
|
|
428
284
|
if (remoteVersion > localVersion) {
|
|
429
285
|
if (activeProvider === 'github') {
|
|
430
286
|
if (!githubSyncService.isAuthenticated())
|
|
431
|
-
return
|
|
432
|
-
notifySyncStart(`Importing GitHub sync data (Remote v${remoteVersion})`);
|
|
433
|
-
const importedVersion = await
|
|
434
|
-
await
|
|
287
|
+
return;
|
|
288
|
+
notifySyncStart(`Importing GitHub ${def.mid}sync data (Remote v${remoteVersion})`);
|
|
289
|
+
const importedVersion = await def.githubDown(db);
|
|
290
|
+
await setKindManifestVersion(kind, importedVersion || remoteVersion);
|
|
435
291
|
notifySyncEnd();
|
|
436
|
-
log.info(
|
|
437
|
-
return
|
|
292
|
+
log.info(`GitHub ${def.mid}sync down complete.`);
|
|
293
|
+
return;
|
|
438
294
|
}
|
|
439
295
|
if (activeProvider === 'google') {
|
|
440
296
|
if (!googleDriveService.isAuthenticated())
|
|
441
|
-
return
|
|
442
|
-
notifySyncStart(`Importing Google sync data (Remote v${remoteVersion})`);
|
|
443
|
-
const importedVersion = await
|
|
444
|
-
await
|
|
297
|
+
return;
|
|
298
|
+
notifySyncStart(`Importing Google ${def.mid}sync data (Remote v${remoteVersion})`);
|
|
299
|
+
const importedVersion = await def.googleDown(db);
|
|
300
|
+
await setKindManifestVersion(kind, importedVersion || remoteVersion);
|
|
445
301
|
notifySyncEnd();
|
|
446
|
-
log.info(
|
|
447
|
-
return
|
|
302
|
+
log.info(`Google ${def.mid}sync down complete.`);
|
|
303
|
+
return;
|
|
448
304
|
}
|
|
449
305
|
if (activeProvider === 'rclone') {
|
|
450
|
-
notifySyncStart(`Importing Rclone sync data (Remote v${remoteVersion})`);
|
|
451
|
-
const importedVersion = await rcloneSyncDown(db, remoteFolderName);
|
|
452
|
-
await
|
|
306
|
+
notifySyncStart(`Importing Rclone ${def.mid}sync data (Remote v${remoteVersion})`);
|
|
307
|
+
const importedVersion = await rcloneSyncDown(db, kind, remoteFolderName);
|
|
308
|
+
await setKindManifestVersion(kind, importedVersion || remoteVersion);
|
|
453
309
|
notifySyncEnd();
|
|
454
|
-
log.info(
|
|
455
|
-
return
|
|
456
|
-
}
|
|
457
|
-
notifySyncStart(`Downloading remote database (Remote v${remoteVersion})`);
|
|
458
|
-
await closeMainDb();
|
|
459
|
-
const backupPath = `${dbPath}.bak`;
|
|
460
|
-
try {
|
|
461
|
-
if (existsSync(dbPath)) {
|
|
462
|
-
await fs.copyFile(dbPath, backupPath);
|
|
463
|
-
}
|
|
464
|
-
try {
|
|
465
|
-
await fs.unlink(`${dbPath}-wal`);
|
|
466
|
-
}
|
|
467
|
-
catch (e) {
|
|
468
|
-
void e;
|
|
469
|
-
}
|
|
470
|
-
try {
|
|
471
|
-
await fs.unlink(`${dbPath}-shm`);
|
|
472
|
-
}
|
|
473
|
-
catch (e) {
|
|
474
|
-
void e;
|
|
475
|
-
}
|
|
476
|
-
log.warn('Legacy raw-db sync path reached with JSON providers. No action taken.');
|
|
477
|
-
if (existsSync(backupPath)) {
|
|
478
|
-
await fs.unlink(backupPath);
|
|
479
|
-
}
|
|
480
|
-
notifySyncEnd();
|
|
481
|
-
log.info('Sync down complete.');
|
|
482
|
-
return true;
|
|
483
|
-
}
|
|
484
|
-
catch (err) {
|
|
485
|
-
notifySyncEnd();
|
|
486
|
-
log.error({ err }, 'Sync down failed. Restoring backup.');
|
|
487
|
-
if (existsSync(backupPath)) {
|
|
488
|
-
try {
|
|
489
|
-
await fs.copyFile(backupPath, dbPath);
|
|
490
|
-
log.info('Backup restored successfully after failed sync down.');
|
|
491
|
-
}
|
|
492
|
-
catch (restoreErr) {
|
|
493
|
-
log.error({ err: restoreErr }, 'Critical: restore from backup also failed.');
|
|
494
|
-
throw new Error('Sync down and restore both failed. Database may be corrupt.', {
|
|
495
|
-
cause: restoreErr,
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
return true;
|
|
310
|
+
log.info(`Rclone ${def.mid}sync down complete.`);
|
|
311
|
+
return;
|
|
500
312
|
}
|
|
501
313
|
}
|
|
502
314
|
else {
|
|
503
|
-
log.info(
|
|
504
|
-
return false;
|
|
315
|
+
log.info(`Local ${def.mid}DB is up to date.`);
|
|
505
316
|
}
|
|
506
317
|
}
|
|
507
318
|
catch (err) {
|
|
508
319
|
notifySyncEnd();
|
|
509
|
-
log.error({ err },
|
|
510
|
-
return false;
|
|
320
|
+
log.error({ err }, `${cap(`${def.mid}sync`)} boot error.`);
|
|
511
321
|
}
|
|
512
322
|
finally {
|
|
513
323
|
isSyncing = false;
|
|
514
324
|
syncMutex.unlock();
|
|
515
325
|
}
|
|
516
326
|
}
|
|
517
|
-
export async function syncUp(db,
|
|
327
|
+
export async function syncUp(db, kind, remoteFolderName) {
|
|
328
|
+
const def = KIND_SYNC[kind];
|
|
518
329
|
if (activeProvider === 'none')
|
|
519
330
|
return;
|
|
520
331
|
await syncMutex.lock();
|
|
@@ -524,219 +335,94 @@ export async function syncUp(db, dbPath, remoteFolderName) {
|
|
|
524
335
|
}
|
|
525
336
|
isSyncing = true;
|
|
526
337
|
try {
|
|
527
|
-
const localVersion = await
|
|
528
|
-
|
|
529
|
-
const { version: remoteVersion } = await getRemoteManifestVersion(remoteFolderName);
|
|
338
|
+
const localVersion = await getKindManifestVersion(kind);
|
|
339
|
+
const { version: remoteVersion } = await getRemoteManifestVersion(remoteFolderName, kind);
|
|
530
340
|
if (localVersion > remoteVersion) {
|
|
341
|
+
notifySyncStart(`Syncing ${def.mid}up (Local v${localVersion})`);
|
|
531
342
|
if (activeProvider === 'github') {
|
|
532
343
|
if (!githubSyncService.isAuthenticated())
|
|
533
344
|
return;
|
|
534
|
-
await
|
|
345
|
+
await def.githubUp(db);
|
|
535
346
|
}
|
|
536
347
|
else if (activeProvider === 'google') {
|
|
537
348
|
if (!googleDriveService.isAuthenticated())
|
|
538
349
|
return;
|
|
539
|
-
await
|
|
350
|
+
await def.googleUp(db);
|
|
540
351
|
}
|
|
541
352
|
else if (activeProvider === 'rclone') {
|
|
542
|
-
await rcloneSyncUp(db, remoteFolderName);
|
|
353
|
+
await rcloneSyncUp(db, kind, remoteFolderName);
|
|
543
354
|
}
|
|
544
355
|
notifySyncEnd();
|
|
545
|
-
log.info(
|
|
356
|
+
log.info(`${cap(`${def.mid}sync`)} up complete.`);
|
|
546
357
|
}
|
|
547
358
|
else {
|
|
548
|
-
|
|
549
|
-
log.info('No changes to sync up or remote is newer.');
|
|
359
|
+
log.info(`No ${def.mid}changes to sync up or remote is newer.`);
|
|
550
360
|
}
|
|
551
361
|
}
|
|
552
362
|
catch (err) {
|
|
553
363
|
notifySyncEnd();
|
|
554
|
-
log.error({ err },
|
|
364
|
+
log.error({ err }, `${cap(`${def.mid}sync`)} up failed.`);
|
|
555
365
|
}
|
|
556
366
|
finally {
|
|
557
367
|
isSyncing = false;
|
|
558
368
|
syncMutex.unlock();
|
|
559
369
|
}
|
|
560
370
|
}
|
|
561
|
-
export async function
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
371
|
+
export async function runFullSyncSequence(dbs, opts) {
|
|
372
|
+
await initSyncProvider(opts.preferredProvider);
|
|
373
|
+
if (getActiveProvider() === 'github' && githubSyncService.isAuthenticated()) {
|
|
374
|
+
try {
|
|
375
|
+
await githubSyncService.migrateFromAniWebSync();
|
|
376
|
+
}
|
|
377
|
+
catch (err) {
|
|
378
|
+
logger.error({ err }, 'GitHub sync migration from ani-web failed');
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
await syncDownOnBoot(dbs.db, 'anime', opts.remoteFolder);
|
|
382
|
+
try {
|
|
383
|
+
await syncUp(dbs.db, 'anime', opts.remoteFolder);
|
|
384
|
+
}
|
|
385
|
+
catch (err) {
|
|
386
|
+
logger.error({ err }, 'Sync up on boot failed');
|
|
387
|
+
}
|
|
388
|
+
const kinds = [
|
|
389
|
+
{ kind: 'manga', db: dbs.mangaDb },
|
|
390
|
+
{ kind: 'tv', db: dbs.tvDb },
|
|
391
|
+
{ kind: 'asmr', db: dbs.asmrDb },
|
|
392
|
+
];
|
|
393
|
+
for (const { kind, db } of kinds) {
|
|
394
|
+
const def = KIND_SYNC[kind];
|
|
395
|
+
try {
|
|
396
|
+
await syncDownOnBoot(db, kind, opts.remoteFolder);
|
|
397
|
+
}
|
|
398
|
+
catch (err) {
|
|
399
|
+
logger.error({ err }, `${def.Head}sync down on boot failed`);
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
await syncUp(db, kind, opts.remoteFolder);
|
|
403
|
+
}
|
|
404
|
+
catch (err) {
|
|
405
|
+
logger.error({ err }, `${def.Head}sync up on boot failed`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
569
408
|
}
|
|
570
|
-
export async function performWriteTransactionAsync(db, runnable) {
|
|
409
|
+
export async function performWriteTransactionAsync(db, runnable, setVersion = (v) => setLocalManifestVersion(v)) {
|
|
571
410
|
await runTx(db, async (tx) => {
|
|
572
411
|
await runnable(tx);
|
|
573
412
|
tx.run("UPDATE sync_metadata SET value = value + 1 WHERE key = 'db_version'");
|
|
574
413
|
});
|
|
575
414
|
const row = dbGet(db, "SELECT value FROM sync_metadata WHERE key = 'db_version'");
|
|
576
415
|
const newVersion = row?.value ?? 1;
|
|
577
|
-
await
|
|
578
|
-
}
|
|
579
|
-
export async function performMangaWriteTransaction(db, runnable) {
|
|
580
|
-
db.serialize(() => {
|
|
581
|
-
runnable(db);
|
|
582
|
-
db.run("UPDATE sync_metadata SET value = value + 1 WHERE key = 'db_version'");
|
|
583
|
-
});
|
|
584
|
-
const row = dbGet(db, "SELECT value FROM sync_metadata WHERE key = 'db_version'");
|
|
585
|
-
const newVersion = row?.value ?? 1;
|
|
586
|
-
await setLocalMangaManifestVersion(newVersion);
|
|
416
|
+
await setVersion(newVersion);
|
|
587
417
|
}
|
|
588
418
|
export async function performMangaWriteTransactionAsync(db, runnable) {
|
|
589
|
-
await
|
|
590
|
-
await runnable(tx);
|
|
591
|
-
tx.run("UPDATE sync_metadata SET value = value + 1 WHERE key = 'db_version'");
|
|
592
|
-
});
|
|
593
|
-
const row = dbGet(db, "SELECT value FROM sync_metadata WHERE key = 'db_version'");
|
|
594
|
-
const newVersion = row?.value ?? 1;
|
|
595
|
-
await setLocalMangaManifestVersion(newVersion);
|
|
596
|
-
}
|
|
597
|
-
export async function performTvWriteTransaction(db, runnable) {
|
|
598
|
-
db.serialize(() => {
|
|
599
|
-
runnable(db);
|
|
600
|
-
db.run("UPDATE sync_metadata SET value = value + 1 WHERE key = 'db_version'");
|
|
601
|
-
});
|
|
602
|
-
const row = dbGet(db, "SELECT value FROM sync_metadata WHERE key = 'db_version'");
|
|
603
|
-
const newVersion = row?.value ?? 1;
|
|
604
|
-
await setLocalTvManifestVersion(newVersion);
|
|
419
|
+
await performWriteTransactionAsync(db, runnable, (v) => setKindManifestVersion('manga', v));
|
|
605
420
|
}
|
|
606
421
|
export async function performTvWriteTransactionAsync(db, runnable) {
|
|
607
|
-
await
|
|
608
|
-
await runnable(tx);
|
|
609
|
-
tx.run("UPDATE sync_metadata SET value = value + 1 WHERE key = 'db_version'");
|
|
610
|
-
});
|
|
611
|
-
const row = dbGet(db, "SELECT value FROM sync_metadata WHERE key = 'db_version'");
|
|
612
|
-
const newVersion = row?.value ?? 1;
|
|
613
|
-
await setLocalTvManifestVersion(newVersion);
|
|
614
|
-
}
|
|
615
|
-
export async function performAsmrWriteTransaction(db, runnable) {
|
|
616
|
-
db.serialize(() => {
|
|
617
|
-
runnable(db);
|
|
618
|
-
db.run("UPDATE sync_metadata SET value = value + 1 WHERE key = 'db_version'");
|
|
619
|
-
});
|
|
620
|
-
const row = dbGet(db, "SELECT value FROM sync_metadata WHERE key = 'db_version'");
|
|
621
|
-
const newVersion = row?.value ?? 1;
|
|
622
|
-
await setLocalAsmrManifestVersion(newVersion);
|
|
422
|
+
await performWriteTransactionAsync(db, runnable, (v) => setKindManifestVersion('tv', v));
|
|
623
423
|
}
|
|
624
424
|
export async function performAsmrWriteTransactionAsync(db, runnable) {
|
|
625
|
-
await
|
|
626
|
-
await runnable(tx);
|
|
627
|
-
tx.run("UPDATE sync_metadata SET value = value + 1 WHERE key = 'db_version'");
|
|
628
|
-
});
|
|
629
|
-
const row = dbGet(db, "SELECT value FROM sync_metadata WHERE key = 'db_version'");
|
|
630
|
-
const newVersion = row?.value ?? 1;
|
|
631
|
-
await setLocalAsmrManifestVersion(newVersion);
|
|
632
|
-
}
|
|
633
|
-
export async function mangaSyncDownOnBoot(db, remoteFolderName) {
|
|
634
|
-
let localVersion = await getLocalMangaManifestVersion();
|
|
635
|
-
if (localVersion === 0 && db) {
|
|
636
|
-
const row = dbGet(db, "SELECT value FROM sync_metadata WHERE key = 'db_version'");
|
|
637
|
-
localVersion = row?.value ?? 0;
|
|
638
|
-
if (localVersion > 0) {
|
|
639
|
-
await setLocalMangaManifestVersion(localVersion);
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
if (activeProvider === 'none')
|
|
643
|
-
return;
|
|
644
|
-
await syncMutex.lock();
|
|
645
|
-
if (isSyncing) {
|
|
646
|
-
syncMutex.unlock();
|
|
647
|
-
return;
|
|
648
|
-
}
|
|
649
|
-
isSyncing = true;
|
|
650
|
-
try {
|
|
651
|
-
notifySyncStart(`Initial manga sync check (${activeProvider})`);
|
|
652
|
-
const { version: remoteVersion } = await getMangaRemoteManifestVersion(remoteFolderName);
|
|
653
|
-
notifySyncEnd();
|
|
654
|
-
log.info(`Manga Sync Check: Local v${localVersion} vs Remote v${remoteVersion}`);
|
|
655
|
-
if (remoteVersion > localVersion) {
|
|
656
|
-
if (activeProvider === 'github') {
|
|
657
|
-
if (!githubSyncService.isAuthenticated())
|
|
658
|
-
return;
|
|
659
|
-
notifySyncStart(`Importing GitHub manga sync data (Remote v${remoteVersion})`);
|
|
660
|
-
const importedVersion = await githubSyncService.syncMangaDown(db);
|
|
661
|
-
await setLocalMangaManifestVersion(importedVersion || remoteVersion);
|
|
662
|
-
notifySyncEnd();
|
|
663
|
-
log.info('GitHub manga sync down complete.');
|
|
664
|
-
return;
|
|
665
|
-
}
|
|
666
|
-
if (activeProvider === 'google') {
|
|
667
|
-
if (!googleDriveService.isAuthenticated())
|
|
668
|
-
return;
|
|
669
|
-
notifySyncStart(`Importing Google manga sync data (Remote v${remoteVersion})`);
|
|
670
|
-
const importedVersion = await googleDriveService.syncMangaDown(db);
|
|
671
|
-
await setLocalMangaManifestVersion(importedVersion || remoteVersion);
|
|
672
|
-
notifySyncEnd();
|
|
673
|
-
log.info('Google manga sync down complete.');
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
if (activeProvider === 'rclone') {
|
|
677
|
-
notifySyncStart(`Importing Rclone manga sync data (Remote v${remoteVersion})`);
|
|
678
|
-
const importedVersion = await rcloneMangaSyncDown(db, remoteFolderName);
|
|
679
|
-
await setLocalMangaManifestVersion(importedVersion || remoteVersion);
|
|
680
|
-
notifySyncEnd();
|
|
681
|
-
log.info('Rclone manga sync down complete.');
|
|
682
|
-
return;
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
else {
|
|
686
|
-
log.info('Local manga DB is up to date.');
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
catch (err) {
|
|
690
|
-
notifySyncEnd();
|
|
691
|
-
log.error({ err }, 'Manga sync boot error.');
|
|
692
|
-
}
|
|
693
|
-
finally {
|
|
694
|
-
isSyncing = false;
|
|
695
|
-
syncMutex.unlock();
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
export async function mangaSyncUp(db, remoteFolderName) {
|
|
699
|
-
if (activeProvider === 'none')
|
|
700
|
-
return;
|
|
701
|
-
await syncMutex.lock();
|
|
702
|
-
if (isSyncing) {
|
|
703
|
-
syncMutex.unlock();
|
|
704
|
-
return;
|
|
705
|
-
}
|
|
706
|
-
isSyncing = true;
|
|
707
|
-
try {
|
|
708
|
-
const localVersion = await getLocalMangaManifestVersion();
|
|
709
|
-
const { version: remoteVersion } = await getMangaRemoteManifestVersion(remoteFolderName);
|
|
710
|
-
if (localVersion > remoteVersion) {
|
|
711
|
-
notifySyncStart(`Syncing manga up (Local v${localVersion})`);
|
|
712
|
-
if (activeProvider === 'github') {
|
|
713
|
-
if (!githubSyncService.isAuthenticated())
|
|
714
|
-
return;
|
|
715
|
-
await githubSyncService.syncMangaUp(db);
|
|
716
|
-
}
|
|
717
|
-
else if (activeProvider === 'google') {
|
|
718
|
-
if (!googleDriveService.isAuthenticated())
|
|
719
|
-
return;
|
|
720
|
-
await googleDriveService.syncMangaUp(db);
|
|
721
|
-
}
|
|
722
|
-
else if (activeProvider === 'rclone') {
|
|
723
|
-
await rcloneMangaSyncUp(db, remoteFolderName);
|
|
724
|
-
}
|
|
725
|
-
notifySyncEnd();
|
|
726
|
-
log.info('Manga sync up complete.');
|
|
727
|
-
}
|
|
728
|
-
else {
|
|
729
|
-
log.info('No manga changes to sync up or remote is newer.');
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
catch (err) {
|
|
733
|
-
notifySyncEnd();
|
|
734
|
-
log.error({ err }, 'Manga sync up failed.');
|
|
735
|
-
}
|
|
736
|
-
finally {
|
|
737
|
-
isSyncing = false;
|
|
738
|
-
syncMutex.unlock();
|
|
739
|
-
}
|
|
425
|
+
await performWriteTransactionAsync(db, runnable, (v) => setKindManifestVersion('asmr', v));
|
|
740
426
|
}
|
|
741
427
|
export async function initializeMangaDatabase(dbPath) {
|
|
742
428
|
try {
|
|
@@ -789,222 +475,6 @@ export async function initializeMangaDatabase(dbPath) {
|
|
|
789
475
|
throw err;
|
|
790
476
|
}
|
|
791
477
|
}
|
|
792
|
-
export async function tvSyncDownOnBoot(db, remoteFolderName) {
|
|
793
|
-
let localVersion = await getLocalTvManifestVersion();
|
|
794
|
-
if (localVersion === 0 && db) {
|
|
795
|
-
const row = dbGet(db, "SELECT value FROM sync_metadata WHERE key = 'db_version'");
|
|
796
|
-
localVersion = row?.value ?? 0;
|
|
797
|
-
if (localVersion > 0) {
|
|
798
|
-
await setLocalTvManifestVersion(localVersion);
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
if (activeProvider === 'none')
|
|
802
|
-
return;
|
|
803
|
-
await syncMutex.lock();
|
|
804
|
-
if (isSyncing) {
|
|
805
|
-
syncMutex.unlock();
|
|
806
|
-
return;
|
|
807
|
-
}
|
|
808
|
-
isSyncing = true;
|
|
809
|
-
try {
|
|
810
|
-
notifySyncStart(`Initial TV sync check (${activeProvider})`);
|
|
811
|
-
const { version: remoteVersion } = await getTvRemoteManifestVersion(remoteFolderName);
|
|
812
|
-
notifySyncEnd();
|
|
813
|
-
log.info(`TV Sync Check: Local v${localVersion} vs Remote v${remoteVersion}`);
|
|
814
|
-
if (remoteVersion > localVersion) {
|
|
815
|
-
if (activeProvider === 'github') {
|
|
816
|
-
if (!githubSyncService.isAuthenticated())
|
|
817
|
-
return;
|
|
818
|
-
notifySyncStart(`Importing GitHub TV sync data (Remote v${remoteVersion})`);
|
|
819
|
-
const importedVersion = await githubSyncService.syncTvDown(db);
|
|
820
|
-
await setLocalTvManifestVersion(importedVersion || remoteVersion);
|
|
821
|
-
notifySyncEnd();
|
|
822
|
-
log.info('GitHub TV sync down complete.');
|
|
823
|
-
return;
|
|
824
|
-
}
|
|
825
|
-
if (activeProvider === 'google') {
|
|
826
|
-
if (!googleDriveService.isAuthenticated())
|
|
827
|
-
return;
|
|
828
|
-
notifySyncStart(`Importing Google TV sync data (Remote v${remoteVersion})`);
|
|
829
|
-
const importedVersion = await googleDriveService.syncTvDown(db);
|
|
830
|
-
await setLocalTvManifestVersion(importedVersion || remoteVersion);
|
|
831
|
-
notifySyncEnd();
|
|
832
|
-
log.info('Google TV sync down complete.');
|
|
833
|
-
return;
|
|
834
|
-
}
|
|
835
|
-
if (activeProvider === 'rclone') {
|
|
836
|
-
notifySyncStart(`Importing Rclone TV sync data (Remote v${remoteVersion})`);
|
|
837
|
-
const importedVersion = await rcloneTvSyncDown(db, remoteFolderName);
|
|
838
|
-
await setLocalTvManifestVersion(importedVersion || remoteVersion);
|
|
839
|
-
notifySyncEnd();
|
|
840
|
-
log.info('Rclone TV sync down complete.');
|
|
841
|
-
return;
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
else {
|
|
845
|
-
log.info('Local TV DB is up to date.');
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
catch (err) {
|
|
849
|
-
notifySyncEnd();
|
|
850
|
-
log.error({ err }, 'TV sync boot error.');
|
|
851
|
-
}
|
|
852
|
-
finally {
|
|
853
|
-
isSyncing = false;
|
|
854
|
-
syncMutex.unlock();
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
export async function tvSyncUp(db, remoteFolderName) {
|
|
858
|
-
if (activeProvider === 'none')
|
|
859
|
-
return;
|
|
860
|
-
await syncMutex.lock();
|
|
861
|
-
if (isSyncing) {
|
|
862
|
-
syncMutex.unlock();
|
|
863
|
-
return;
|
|
864
|
-
}
|
|
865
|
-
isSyncing = true;
|
|
866
|
-
try {
|
|
867
|
-
const localVersion = await getLocalTvManifestVersion();
|
|
868
|
-
const { version: remoteVersion } = await getTvRemoteManifestVersion(remoteFolderName);
|
|
869
|
-
if (localVersion > remoteVersion) {
|
|
870
|
-
notifySyncStart(`Syncing TV up (Local v${localVersion})`);
|
|
871
|
-
if (activeProvider === 'github') {
|
|
872
|
-
if (!githubSyncService.isAuthenticated())
|
|
873
|
-
return;
|
|
874
|
-
await githubSyncService.syncTvUp(db);
|
|
875
|
-
}
|
|
876
|
-
else if (activeProvider === 'google') {
|
|
877
|
-
if (!googleDriveService.isAuthenticated())
|
|
878
|
-
return;
|
|
879
|
-
await googleDriveService.syncTvUp(db);
|
|
880
|
-
}
|
|
881
|
-
else if (activeProvider === 'rclone') {
|
|
882
|
-
await rcloneTvSyncUp(db, remoteFolderName);
|
|
883
|
-
}
|
|
884
|
-
notifySyncEnd();
|
|
885
|
-
log.info('TV sync up complete.');
|
|
886
|
-
}
|
|
887
|
-
else {
|
|
888
|
-
log.info('No TV changes to sync up or remote is newer.');
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
catch (err) {
|
|
892
|
-
notifySyncEnd();
|
|
893
|
-
log.error({ err }, 'TV sync up failed.');
|
|
894
|
-
}
|
|
895
|
-
finally {
|
|
896
|
-
isSyncing = false;
|
|
897
|
-
syncMutex.unlock();
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
export async function asmrSyncDownOnBoot(db, remoteFolderName) {
|
|
901
|
-
let localVersion = await getLocalAsmrManifestVersion();
|
|
902
|
-
if (localVersion === 0 && db) {
|
|
903
|
-
const row = dbGet(db, "SELECT value FROM sync_metadata WHERE key = 'db_version'");
|
|
904
|
-
localVersion = row?.value ?? 0;
|
|
905
|
-
if (localVersion > 0) {
|
|
906
|
-
await setLocalAsmrManifestVersion(localVersion);
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
if (activeProvider === 'none')
|
|
910
|
-
return;
|
|
911
|
-
await syncMutex.lock();
|
|
912
|
-
if (isSyncing) {
|
|
913
|
-
syncMutex.unlock();
|
|
914
|
-
return;
|
|
915
|
-
}
|
|
916
|
-
isSyncing = true;
|
|
917
|
-
try {
|
|
918
|
-
notifySyncStart(`Initial ASMR sync check (${activeProvider})`);
|
|
919
|
-
const { version: remoteVersion } = await getAsmrRemoteManifestVersion(remoteFolderName);
|
|
920
|
-
notifySyncEnd();
|
|
921
|
-
log.info(`ASMR Sync Check: Local v${localVersion} vs Remote v${remoteVersion}`);
|
|
922
|
-
if (remoteVersion > localVersion) {
|
|
923
|
-
if (activeProvider === 'github') {
|
|
924
|
-
if (!githubSyncService.isAuthenticated())
|
|
925
|
-
return;
|
|
926
|
-
notifySyncStart(`Importing GitHub ASMR sync data (Remote v${remoteVersion})`);
|
|
927
|
-
const importedVersion = await githubSyncService.syncAsmrDown(db);
|
|
928
|
-
await setLocalAsmrManifestVersion(importedVersion || remoteVersion);
|
|
929
|
-
notifySyncEnd();
|
|
930
|
-
log.info('GitHub ASMR sync down complete.');
|
|
931
|
-
return;
|
|
932
|
-
}
|
|
933
|
-
if (activeProvider === 'google') {
|
|
934
|
-
if (!googleDriveService.isAuthenticated())
|
|
935
|
-
return;
|
|
936
|
-
notifySyncStart(`Importing Google ASMR sync data (Remote v${remoteVersion})`);
|
|
937
|
-
const importedVersion = await googleDriveService.syncAsmrDown(db);
|
|
938
|
-
await setLocalAsmrManifestVersion(importedVersion || remoteVersion);
|
|
939
|
-
notifySyncEnd();
|
|
940
|
-
log.info('Google ASMR sync down complete.');
|
|
941
|
-
return;
|
|
942
|
-
}
|
|
943
|
-
if (activeProvider === 'rclone') {
|
|
944
|
-
notifySyncStart(`Importing Rclone ASMR sync data (Remote v${remoteVersion})`);
|
|
945
|
-
const importedVersion = await rcloneAsmrSyncDown(db, remoteFolderName);
|
|
946
|
-
await setLocalAsmrManifestVersion(importedVersion || remoteVersion);
|
|
947
|
-
notifySyncEnd();
|
|
948
|
-
log.info('Rclone ASMR sync down complete.');
|
|
949
|
-
return;
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
else {
|
|
953
|
-
log.info('Local ASMR DB is up to date.');
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
catch (err) {
|
|
957
|
-
notifySyncEnd();
|
|
958
|
-
log.error({ err }, 'ASMR sync boot error.');
|
|
959
|
-
}
|
|
960
|
-
finally {
|
|
961
|
-
isSyncing = false;
|
|
962
|
-
syncMutex.unlock();
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
export async function asmrSyncUp(db, remoteFolderName) {
|
|
966
|
-
if (activeProvider === 'none')
|
|
967
|
-
return;
|
|
968
|
-
await syncMutex.lock();
|
|
969
|
-
if (isSyncing) {
|
|
970
|
-
syncMutex.unlock();
|
|
971
|
-
return;
|
|
972
|
-
}
|
|
973
|
-
isSyncing = true;
|
|
974
|
-
try {
|
|
975
|
-
const localVersion = await getLocalAsmrManifestVersion();
|
|
976
|
-
const { version: remoteVersion } = await getAsmrRemoteManifestVersion(remoteFolderName);
|
|
977
|
-
if (localVersion > remoteVersion) {
|
|
978
|
-
notifySyncStart(`Syncing ASMR up (Local v${localVersion})`);
|
|
979
|
-
if (activeProvider === 'github') {
|
|
980
|
-
if (!githubSyncService.isAuthenticated())
|
|
981
|
-
return;
|
|
982
|
-
await githubSyncService.syncAsmrUp(db);
|
|
983
|
-
}
|
|
984
|
-
else if (activeProvider === 'google') {
|
|
985
|
-
if (!googleDriveService.isAuthenticated())
|
|
986
|
-
return;
|
|
987
|
-
await googleDriveService.syncAsmrUp(db);
|
|
988
|
-
}
|
|
989
|
-
else if (activeProvider === 'rclone') {
|
|
990
|
-
await rcloneAsmrSyncUp(db, remoteFolderName);
|
|
991
|
-
}
|
|
992
|
-
notifySyncEnd();
|
|
993
|
-
log.info('ASMR sync up complete.');
|
|
994
|
-
}
|
|
995
|
-
else {
|
|
996
|
-
log.info('No ASMR changes to sync up or remote is newer.');
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
catch (err) {
|
|
1000
|
-
notifySyncEnd();
|
|
1001
|
-
log.error({ err }, 'ASMR sync up failed.');
|
|
1002
|
-
}
|
|
1003
|
-
finally {
|
|
1004
|
-
isSyncing = false;
|
|
1005
|
-
syncMutex.unlock();
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
478
|
export async function initializeTvDatabase(dbPath) {
|
|
1009
479
|
try {
|
|
1010
480
|
const db = await DatabaseWrapper.create(dbPath);
|
|
@@ -1180,7 +650,6 @@ export async function initializeDatabase(dbPath) {
|
|
|
1180
650
|
addCol('shows_meta', 'anilistId', 'INTEGER');
|
|
1181
651
|
addCol('shows_meta', 'isAdult', 'INTEGER');
|
|
1182
652
|
addCol('shows_meta', 'episodeDuration', 'INTEGER');
|
|
1183
|
-
await db.saveNow();
|
|
1184
653
|
return db;
|
|
1185
654
|
}
|
|
1186
655
|
catch (err) {
|