@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.
Files changed (93) hide show
  1. package/README.md +1 -1
  2. package/client/dist/assets/{AnimeCard-CIwTZI3D.js → AnimeCard-4CEz8Buu.js} +1 -1
  3. package/client/dist/assets/AnimeInfo-DyX9qTwg.js +1 -0
  4. package/client/dist/assets/AnimeInfoPage-Dvp_c8vK.js +2 -0
  5. package/client/dist/assets/Asmr-qg3lMAJn.js +2 -0
  6. package/client/dist/assets/{AsmrResetProgressModal-BDpA07Fa.js → AsmrResetProgressModal-BpPlg_Vv.js} +1 -1
  7. package/client/dist/assets/AvSyncCalibrator-C2wu1ydI.css +1 -0
  8. package/client/dist/assets/AvSyncCalibrator-FJllOgOH.js +10 -0
  9. package/client/dist/assets/Home-A7gCeEST.js +1 -0
  10. package/client/dist/assets/Insights-G9IXepHJ.js +1 -0
  11. package/client/dist/assets/{LibraryListPage-DWbcek1i.js → LibraryListPage-Bh2qCJPh.js} +1 -1
  12. package/client/dist/assets/{ListeningList-CcuxBzy1.js → ListeningList-Czv4cTRl.js} +1 -1
  13. package/client/dist/assets/{Manga-DpLaW39C.js → Manga-qdAAS1z7.js} +1 -1
  14. package/client/dist/assets/MangaInfoPage-1rTgZzf2.js +1 -0
  15. package/client/dist/assets/MangaReadPage-CeyxPG6w.js +1 -0
  16. package/client/dist/assets/{MangaResetProgressModal-CYguBG2f.js → MangaResetProgressModal-C_ppotmh.js} +1 -1
  17. package/client/dist/assets/Mature-Bkhxnr3K.js +1 -0
  18. package/client/dist/assets/{MediaCard-p29DqyKX.js → MediaCard-BQZsutEo.js} +1 -1
  19. package/client/dist/assets/Music-C0rGzSbi.js +1 -0
  20. package/client/dist/assets/{OptionListMenu-C1pvpu8X.js → OptionListMenu-CyfpCBEy.js} +1 -1
  21. package/client/dist/assets/Player-yhiTlpYU.js +56 -0
  22. package/client/dist/assets/PlayerSettings-icy4SfQa.js +1 -0
  23. package/client/dist/assets/{QueueOptionsButton-CWdoe1_I.js → QueueOptionsButton-5eloKF5J.js} +1 -1
  24. package/client/dist/assets/{QueueRail-BBpn9CII.js → QueueRail-DaMjlKL4.js} +1 -1
  25. package/client/dist/assets/Radio-FxGqCxR4.js +1 -0
  26. package/client/dist/assets/{ReadingList-D4xQ9tlw.js → ReadingList-CCm3IAbt.js} +1 -1
  27. package/client/dist/assets/{ResetProgressModal-JFlbXy7W.js → ResetProgressModal-CeI4V3Y5.js} +1 -1
  28. package/client/dist/assets/{Search-D-ocogP-.js → Search-qpILaAiQ.js} +1 -1
  29. package/client/dist/assets/Settings-Bw9M3NGz.js +21 -0
  30. package/client/dist/assets/{SkeletonGrid-N67HqYXx.js → SkeletonGrid-0dY_xMq3.js} +1 -1
  31. package/client/dist/assets/{Trackers-C_SPMGKx.js → Trackers-Zy5GOs0P.js} +2 -2
  32. package/client/dist/assets/Tv-Bx7tGS57.css +1 -0
  33. package/client/dist/assets/Tv-DREwc7BX.js +4 -0
  34. package/client/dist/assets/TvInfo-CHIsVzZ0.js +1 -0
  35. package/client/dist/assets/{TvPopup-DEB6Ufq4.js → TvPopup-D_tVTyZ2.js} +1 -1
  36. package/client/dist/assets/{TvSearch-grOUL0Bc.js → TvSearch-BVxedBW-.js} +1 -1
  37. package/client/dist/assets/{TvWatchlist-CBI21Vge.js → TvWatchlist-BEIwq9R9.js} +1 -1
  38. package/client/dist/assets/{Watchlist-5cDHUoC8.js → Watchlist-CSngjqlz.js} +1 -1
  39. package/client/dist/assets/{hls-DySVERd_.js → hls-D48GVTkk.js} +1 -1
  40. package/client/dist/assets/index-CVB5dYhT.js +178 -0
  41. package/client/dist/assets/{useAnimeInfoData-BMwVSGhZ.js → useAnimeInfoData-DxdOg_B7.js} +1 -1
  42. package/client/dist/assets/useAsmr-ClckxnPe.js +1 -0
  43. package/client/dist/assets/{useAsmrPopup-CY-6b88q.js → useAsmrPopup-Bbgxefy_.js} +1 -1
  44. package/client/dist/assets/useMangaLibrary-B3_BUKVk.js +1 -0
  45. package/client/dist/assets/{useMangaPopup-_pNXvRYJ.js → useMangaPopup-Celk4lzf.js} +1 -1
  46. package/client/dist/assets/{useProviders-CKSedfqb.js → useProviders-pwxPZPHh.js} +1 -1
  47. package/client/dist/assets/useTv-D9sQrDop.js +1 -0
  48. package/client/dist/assets/useTvLibrary-UsQpgBTB.js +1 -0
  49. package/client/dist/index.html +1 -1
  50. package/package.json +3 -3
  51. package/server/dist/app-hono.js +15 -37
  52. package/server/dist/db/drizzle.js +2 -18
  53. package/server/dist/db.js +3 -2
  54. package/server/dist/hono/tv.js +9 -5
  55. package/server/dist/hono/watchlist.js +10 -108
  56. package/server/dist/lib/migration.js +0 -5
  57. package/server/dist/providers/remote-loader.js +40 -1
  58. package/server/dist/providers/remote-types.js +9 -2
  59. package/server/dist/server.js +50 -182
  60. package/server/dist/sync.js +173 -704
  61. package/server/dist/trpc/routers/auth.js +2 -71
  62. package/server/dist/trpc/routers/continue-watching.js +0 -2
  63. package/server/dist/trpc/routers/data.js +0 -1
  64. package/server/dist/trpc/routers/discord.js +8 -0
  65. package/server/dist/trpc/routers/progress.js +95 -1
  66. package/server/dist/trpc/routers/queue.js +0 -2
  67. package/server/dist/trpc/routers/watchlist.js +0 -7
  68. package/server/dist/trpc/validation.js +64 -0
  69. package/server/package.json +2 -2
  70. package/client/dist/assets/AnimeInfo-DlfvDaR8.js +0 -1
  71. package/client/dist/assets/AnimeInfoPage-B3EPwtzx.js +0 -2
  72. package/client/dist/assets/Asmr-DCirAWM6.js +0 -2
  73. package/client/dist/assets/AvSyncCalibrator-BPa6KaGC.css +0 -1
  74. package/client/dist/assets/AvSyncCalibrator-BroSW86T.js +0 -10
  75. package/client/dist/assets/Home-DEjYtHbs.js +0 -1
  76. package/client/dist/assets/Insights-B3b6u8RW.js +0 -1
  77. package/client/dist/assets/MangaInfoPage-B7FPC4a_.js +0 -1
  78. package/client/dist/assets/MangaReadPage-Dx0qAAFe.js +0 -1
  79. package/client/dist/assets/Mature-AScDPmWW.js +0 -1
  80. package/client/dist/assets/Music-DFInMskb.js +0 -1
  81. package/client/dist/assets/Player-D-8zY3cx.js +0 -56
  82. package/client/dist/assets/PlayerSettings-CfJjs_0Q.js +0 -1
  83. package/client/dist/assets/Radio-DCgO5hAS.js +0 -1
  84. package/client/dist/assets/Settings-BsFbgkdy.js +0 -21
  85. package/client/dist/assets/Tv-D7s0Kqqx.js +0 -4
  86. package/client/dist/assets/Tv-ZEA45gXK.css +0 -1
  87. package/client/dist/assets/TvInfo-CrmCSFDF.js +0 -1
  88. package/client/dist/assets/index-HZNJp28e.js +0 -178
  89. package/client/dist/assets/useAsmr-BLc7FwPm.js +0 -1
  90. package/client/dist/assets/useMangaLibrary-XFRTq5Qg.js +0 -1
  91. package/client/dist/assets/useMatureConsent-Cra09TA0.js +0 -1
  92. package/client/dist/assets/useTv-Dizj6G4R.js +0 -1
  93. package/client/dist/assets/useTvLibrary-twjHQ89n.js +0 -1
@@ -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
- async function exportSyncPayload(db) {
20
- return exportTables(db, SYNC_TABLES);
21
- }
22
- function importSyncPayload(db, payload) {
23
- importTables(db, SYNC_TABLES, payload, {
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
- async function exportMangaSyncPayload(db) {
29
- return exportTables(db, MANGA_SYNC_TABLES);
30
- }
31
- async function exportTvSyncPayload(db) {
32
- return exportTables(db, TV_SYNC_TABLES);
33
- }
34
- async function exportAsmrSyncPayload(db) {
35
- return exportTables(db, ASMR_SYNC_TABLES);
36
- }
37
- function importMangaSyncPayload(db, payload) {
38
- importTables(db, MANGA_SYNC_TABLES, payload, {
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
- function importTvSyncPayload(db, payload) {
44
- importTables(db, TV_SYNC_TABLES, payload, {
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
- function importAsmrSyncPayload(db, payload) {
50
- importTables(db, ASMR_SYNC_TABLES, payload, {
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, remoteFolder, fileName = CONFIG.RCLONE_SYNC_FILENAME) {
75
- const payload = await exportSyncPayload(db);
76
- const tempPath = path.join(CONFIG.ROOT, `temp_${Date.now()}_rclone_up.json`);
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, fileName);
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 rcloneTvSyncDown(db, remoteFolder) {
151
- const tempPath = path.join(CONFIG.ROOT, `temp_${Date.now()}_rclone_tv_down.json`);
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, CONFIG.TV_RCLONE_SYNC_FILENAME, tempPath);
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), TV_SYNC_TABLES, 'rclone tv');
156
- importTvSyncPayload(db, payload);
157
- return readPayloadVersion(payload);
158
- }
159
- finally {
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(CONFIG.LOCAL_MANIFEST_PATH)) {
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(CONFIG.TV_LOCAL_MANIFEST_PATH, 'utf-8');
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 setLocalTvManifestVersion(version) {
299
- await fs.writeFile(CONFIG.TV_LOCAL_MANIFEST_PATH, JSON.stringify({ version }));
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 async function setLocalAsmrManifestVersion(version) {
314
- await fs.writeFile(CONFIG.ASMR_LOCAL_MANIFEST_PATH, JSON.stringify({ version }));
232
+ export function getKindManifestVersion(kind) {
233
+ return getLocalManifestVersion(KIND_SYNC[kind].manifestPath);
315
234
  }
316
- async function getRemoteManifestVersion(remoteFolder) {
317
- try {
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 getMangaRemoteManifestVersion(remoteFolder) {
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 githubSyncService.getMangaRemoteVersion() };
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 googleDriveService.getMangaRemoteVersion() };
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 }, 'Could not read remote manga manifest.');
257
+ log.warn({ err }, `Could not read remote ${label}.`);
357
258
  }
358
259
  return { version: 0 };
359
260
  }
360
- async function getTvRemoteManifestVersion(remoteFolder) {
361
- try {
362
- if (activeProvider === 'github') {
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 setLocalManifestVersion(localVersion);
268
+ await setKindManifestVersion(kind, localVersion);
413
269
  }
414
270
  }
415
271
  if (activeProvider === 'none')
416
- return false;
272
+ return;
417
273
  await syncMutex.lock();
418
274
  if (isSyncing) {
419
275
  syncMutex.unlock();
420
- return false;
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(`Sync Check: Local v${localVersion} vs Remote v${remoteVersion}`);
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 false;
432
- notifySyncStart(`Importing GitHub sync data (Remote v${remoteVersion})`);
433
- const importedVersion = await githubSyncService.syncDown(db);
434
- await setLocalManifestVersion(importedVersion || remoteVersion);
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('GitHub sync down complete.');
437
- return false;
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 false;
442
- notifySyncStart(`Importing Google sync data (Remote v${remoteVersion})`);
443
- const importedVersion = await googleDriveService.syncDown(db);
444
- await setLocalManifestVersion(importedVersion || remoteVersion);
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('Google sync down complete.');
447
- return false;
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 setLocalManifestVersion(importedVersion || remoteVersion);
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('Rclone sync down complete.');
455
- return false;
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('Local DB is up to date.');
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 }, 'Sync boot error.');
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, dbPath, remoteFolderName) {
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 getLocalManifestVersion();
528
- notifySyncStart(`Syncing up (Local v${localVersion})`);
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 githubSyncService.syncUp(db);
345
+ await def.githubUp(db);
535
346
  }
536
347
  else if (activeProvider === 'google') {
537
348
  if (!googleDriveService.isAuthenticated())
538
349
  return;
539
- await googleDriveService.syncUp(db);
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('Sync up complete.');
356
+ log.info(`${cap(`${def.mid}sync`)} up complete.`);
546
357
  }
547
358
  else {
548
- notifySyncEnd();
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 }, 'Sync up failed.');
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 performWriteTransaction(db, runnable) {
562
- db.serialize(() => {
563
- runnable(db);
564
- db.run("UPDATE sync_metadata SET value = value + 1 WHERE key = 'db_version'");
565
- });
566
- const row = dbGet(db, "SELECT value FROM sync_metadata WHERE key = 'db_version'");
567
- const newVersion = row?.value ?? 1;
568
- await setLocalManifestVersion(newVersion);
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 setLocalManifestVersion(newVersion);
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 runTx(db, async (tx) => {
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 runTx(db, async (tx) => {
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 runTx(db, async (tx) => {
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) {