@serifpersia/dango 3.3.9 → 3.4.0

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 (180) hide show
  1. package/README.md +1 -1
  2. package/client/dist/assets/{AnimeCard-URCiiy5W.js → AnimeCard-CJy1SXH3.js} +1 -1
  3. package/client/dist/assets/{AnimeInfo-e3z3er9t.js → AnimeInfo-BoXlVvZ-.js} +1 -1
  4. package/client/dist/assets/{AnimeInfoPage-B7Djlmy_.js → AnimeInfoPage-BxbA_7AV.js} +2 -2
  5. package/client/dist/assets/Asmr--Zc8G9zv.js +2 -0
  6. package/client/dist/assets/AsmrResetProgressModal-CKYm-8cp.js +1 -0
  7. package/client/dist/assets/AvSyncCalibrator-3yjZS7s6.js +10 -0
  8. package/client/dist/assets/Home-rrhOGQcg.js +1 -0
  9. package/client/dist/assets/Insights-Bay90Yeg.js +1 -0
  10. package/client/dist/assets/{LibraryListPage-B0qWqhb_.js → LibraryListPage-HzuDavbb.js} +1 -1
  11. package/client/dist/assets/{ListeningList-CSqD8j1C.js → ListeningList-DYcQnnsG.js} +1 -1
  12. package/client/dist/assets/{Manga-Cg2YuObF.js → Manga-D8CT9aO0.js} +1 -1
  13. package/client/dist/assets/{MangaInfoPage-DcArhEsy.js → MangaInfoPage-DGK6CYp1.js} +1 -1
  14. package/client/dist/assets/MangaReadPage-B9XoDKF9.js +1 -0
  15. package/client/dist/assets/{MangaResetProgressModal-D_yT3d1v.js → MangaResetProgressModal-QjZyIAjl.js} +1 -1
  16. package/client/dist/assets/Mature-CHt_-Hz5.js +1 -0
  17. package/client/dist/assets/MediaCard-B5584Cq9.js +1 -0
  18. package/client/dist/assets/Music-HeLrEZ2d.js +1 -0
  19. package/client/dist/assets/OptionListMenu-CJ90CdwI.js +1 -0
  20. package/client/dist/assets/Player-BYtEax_z.js +56 -0
  21. package/client/dist/assets/PlayerSettings-4-cUAY4B.js +1 -0
  22. package/client/dist/assets/QueueOptionsButton-Ba0dg_82.js +1 -0
  23. package/client/dist/assets/{QueueRail-B7f1-e6i.js → QueueRail-O7f4RqjB.js} +1 -1
  24. package/client/dist/assets/Radio-DPZxQnyD.js +1 -0
  25. package/client/dist/assets/{ReadingList-BaIem8H1.js → ReadingList-n6rr8ygy.js} +1 -1
  26. package/client/dist/assets/{ResetProgressModal-DES5Y-Fl.js → ResetProgressModal-CcStlbhP.js} +1 -1
  27. package/client/dist/assets/Search-C2wrWOdy.js +1 -0
  28. package/client/dist/assets/Settings-CluUYs8U.js +21 -0
  29. package/client/dist/assets/{SkeletonGrid-BxfgjP4i.js → SkeletonGrid-CLEPY9nP.js} +1 -1
  30. package/client/dist/assets/Trackers-CTGPxs0V.js +2 -0
  31. package/client/dist/assets/Tv-BwHlDdi8.js +4 -0
  32. package/client/dist/assets/Tv-ZEA45gXK.css +1 -0
  33. package/client/dist/assets/TvInfo-CXnrLekr.js +1 -0
  34. package/client/dist/assets/TvPopup-BekfiNcX.js +1 -0
  35. package/client/dist/assets/{TvSearch-BWDwWnHe.js → TvSearch-DuqR-z3v.js} +1 -1
  36. package/client/dist/assets/{TvWatchlist-D3n5_4MI.js → TvWatchlist-CBvgFrA0.js} +1 -1
  37. package/client/dist/assets/Watchlist-mj8mTOam.js +1 -0
  38. package/client/dist/assets/{hls-BO_L1WoN.js → hls-Boje3IOE.js} +1 -1
  39. package/client/dist/assets/index-BXwhFWB_.js +178 -0
  40. package/client/dist/assets/useAnimeInfoData-DJ7A4B4T.js +1 -0
  41. package/client/dist/assets/useAsmr-DIwIsJXF.js +1 -0
  42. package/client/dist/assets/{useAsmrPopup-D_o9-QlR.js → useAsmrPopup-e4zNN9ig.js} +1 -1
  43. package/client/dist/assets/useMangaLibrary-C9Lci5N4.js +1 -0
  44. package/client/dist/assets/{useMangaPopup-TFzkC-tG.js → useMangaPopup-B8nnpvyi.js} +1 -1
  45. package/client/dist/assets/useProviders-B5Qr4I1y.js +1 -0
  46. package/client/dist/assets/useTv-Dm8MCYlL.js +1 -0
  47. package/client/dist/assets/useTvLibrary-B7jEk2Q7.js +1 -0
  48. package/client/dist/assets/{vendor-query-BXRECSI6.js → vendor-query-BUTye89G.js} +1 -1
  49. package/client/dist/index.html +2 -2
  50. package/orchestrator.js +1 -2
  51. package/package.json +2 -6
  52. package/server/dist/app-auth.js +9 -29
  53. package/server/dist/app-hono.js +621 -0
  54. package/server/dist/config.js +0 -1
  55. package/server/dist/db/drizzle.js +32 -0
  56. package/server/dist/db/schema-anime.js +64 -0
  57. package/server/dist/db/schema-asmr.js +25 -0
  58. package/server/dist/db/schema-manga.js +31 -0
  59. package/server/dist/db/schema-tv.js +33 -0
  60. package/server/dist/db.js +75 -4
  61. package/server/dist/discord-gateway.js +1 -1
  62. package/server/dist/google.js +0 -7
  63. package/server/dist/hono/asmr.js +1 -0
  64. package/server/dist/hono/auth.js +56 -0
  65. package/server/dist/hono/music.js +419 -0
  66. package/server/dist/{controllers/proxy.controller.js → hono/proxy.js} +390 -394
  67. package/server/dist/hono/tv.js +197 -0
  68. package/server/dist/hono/watchlist.js +733 -0
  69. package/server/dist/lib/anilist.js +3 -3
  70. package/server/dist/lib/insights.js +185 -0
  71. package/server/dist/lib/ipc.js +1 -2
  72. package/server/dist/lib/kitsu.js +1 -28
  73. package/server/dist/lib/mal-import.js +291 -0
  74. package/server/dist/lib/migration.js +20 -34
  75. package/server/dist/lib/offline-db.js +36 -36
  76. package/server/dist/lib/tracker/manga-sync.service.js +21 -21
  77. package/server/dist/lib/tracker/sync.service.js +20 -20
  78. package/server/dist/lib/translate.js +79 -0
  79. package/server/dist/lib/ytmusic.js +0 -2
  80. package/server/dist/providers/title-matching.js +0 -1
  81. package/server/dist/repositories/asmr.repository.js +90 -87
  82. package/server/dist/repositories/insights.repository.js +24 -13
  83. package/server/dist/repositories/library.repository.js +33 -93
  84. package/server/dist/repositories/manga.repository.js +108 -116
  85. package/server/dist/repositories/notifications.repository.js +50 -31
  86. package/server/dist/repositories/queue.repository.js +36 -25
  87. package/server/dist/repositories/settings.repository.js +18 -8
  88. package/server/dist/repositories/shows-meta.repository.js +54 -41
  89. package/server/dist/repositories/temp-show-ids.repository.js +43 -31
  90. package/server/dist/repositories/tv.repository.js +98 -115
  91. package/server/dist/repositories/watched-episodes.repository.js +52 -20
  92. package/server/dist/repositories/watchlist.repository.js +36 -48
  93. package/server/dist/server.js +59 -151
  94. package/server/dist/sync.js +41 -4
  95. package/server/dist/trpc/context.js +7 -0
  96. package/server/dist/trpc/errors.js +21 -0
  97. package/server/dist/trpc/index.js +19 -0
  98. package/server/dist/trpc/router.js +49 -0
  99. package/server/dist/trpc/routers/asmr-library.js +122 -0
  100. package/server/dist/trpc/routers/asmr-progress.js +133 -0
  101. package/server/dist/trpc/routers/asmr.js +87 -0
  102. package/server/dist/trpc/routers/auth.js +247 -0
  103. package/server/dist/trpc/routers/continue-watching.js +83 -0
  104. package/server/dist/trpc/routers/data.js +1039 -0
  105. package/server/dist/trpc/routers/discord.js +288 -0
  106. package/server/dist/trpc/routers/discovery.js +15 -0
  107. package/server/dist/trpc/routers/insights.js +45 -0
  108. package/server/dist/trpc/routers/manga-library.js +280 -0
  109. package/server/dist/trpc/routers/manga-progress.js +181 -0
  110. package/server/dist/trpc/routers/manga.js +201 -0
  111. package/server/dist/{controllers/music.controller.js → trpc/routers/music.js} +155 -288
  112. package/server/dist/trpc/routers/notifications.js +80 -0
  113. package/server/dist/trpc/routers/progress.js +15 -0
  114. package/server/dist/trpc/routers/queue.js +95 -0
  115. package/server/dist/{routes/radio.routes.js → trpc/routers/radio.js} +30 -23
  116. package/server/dist/trpc/routers/settings.js +105 -0
  117. package/server/dist/trpc/routers/tracker.js +219 -0
  118. package/server/dist/trpc/routers/tv-library.js +145 -0
  119. package/server/dist/trpc/routers/tv-progress.js +171 -0
  120. package/server/dist/trpc/routers/tv.js +565 -0
  121. package/server/dist/trpc/routers/watchlist.js +564 -0
  122. package/server/dist/trpc/validation.js +159 -0
  123. package/server/dist/utils/security.utils.js +0 -18
  124. package/server/package.json +5 -10
  125. package/client/dist/assets/Asmr-C28TfSvX.js +0 -2
  126. package/client/dist/assets/AsmrResetProgressModal-6Xh71AgR.js +0 -1
  127. package/client/dist/assets/AvSyncCalibrator-BwCtRW0P.js +0 -10
  128. package/client/dist/assets/Home-BJdbHYja.js +0 -1
  129. package/client/dist/assets/Insights-CjRy42YE.js +0 -1
  130. package/client/dist/assets/MangaReadPage-YCETZRte.js +0 -1
  131. package/client/dist/assets/Mature-BGxRWkV5.js +0 -1
  132. package/client/dist/assets/MediaCard-CAzoxsRS.js +0 -1
  133. package/client/dist/assets/Music-Cgo-yQ7E.js +0 -1
  134. package/client/dist/assets/OptionListMenu--Owe9Jle.js +0 -1
  135. package/client/dist/assets/Player-yRXG2BrP.js +0 -56
  136. package/client/dist/assets/PlayerSettings-DrZLa0CC.js +0 -1
  137. package/client/dist/assets/QueueOptionsButton-BVZyPyKf.js +0 -1
  138. package/client/dist/assets/Radio-BFjxlzuF.js +0 -1
  139. package/client/dist/assets/Search-BOBo0Ogh.js +0 -1
  140. package/client/dist/assets/Settings-DOvNQ0gX.js +0 -21
  141. package/client/dist/assets/Trackers-DZ9KqOWv.js +0 -2
  142. package/client/dist/assets/Tv-5mATEU4p.js +0 -4
  143. package/client/dist/assets/Tv-C2SWcVzN.css +0 -1
  144. package/client/dist/assets/TvInfo-CF-5C5Fs.js +0 -1
  145. package/client/dist/assets/TvPopup-Dfqifj3s.js +0 -1
  146. package/client/dist/assets/Watchlist-B8MHNsAU.js +0 -1
  147. package/client/dist/assets/index-D2RkUJIN.js +0 -178
  148. package/client/dist/assets/useAnimeInfoData-BWShDHqE.js +0 -1
  149. package/client/dist/assets/useAsmr-FyfD9G7b.js +0 -1
  150. package/client/dist/assets/useMangaLibrary-BJU5x8Vz.js +0 -1
  151. package/client/dist/assets/useProviders-DDRxa8vl.js +0 -1
  152. package/client/dist/assets/useTv-Bh6WmNnZ.js +0 -1
  153. package/client/dist/assets/useTvLibrary-_Pqm8re1.js +0 -1
  154. package/server/dist/controllers/asmr-library.controller.js +0 -288
  155. package/server/dist/controllers/auth.controller.js +0 -225
  156. package/server/dist/controllers/data.controller.js +0 -767
  157. package/server/dist/controllers/insights.controller.js +0 -203
  158. package/server/dist/controllers/manga-library.controller.js +0 -350
  159. package/server/dist/controllers/settings.controller.js +0 -609
  160. package/server/dist/controllers/tv-library.controller.js +0 -314
  161. package/server/dist/controllers/watchlist.controller.js +0 -1069
  162. package/server/dist/request-logger.js +0 -16
  163. package/server/dist/routes/asmr-library.routes.js +0 -23
  164. package/server/dist/routes/asmr.routes.js +0 -81
  165. package/server/dist/routes/auth.routes.js +0 -25
  166. package/server/dist/routes/data.routes.js +0 -47
  167. package/server/dist/routes/discord-gateway.routes.js +0 -51
  168. package/server/dist/routes/insights.routes.js +0 -35
  169. package/server/dist/routes/lan-auth.routes.js +0 -46
  170. package/server/dist/routes/manga-library.routes.js +0 -25
  171. package/server/dist/routes/manga.routes.js +0 -141
  172. package/server/dist/routes/music.routes.js +0 -20
  173. package/server/dist/routes/providers.routes.js +0 -17
  174. package/server/dist/routes/proxy.routes.js +0 -12
  175. package/server/dist/routes/settings.routes.js +0 -24
  176. package/server/dist/routes/tracker.routes.js +0 -287
  177. package/server/dist/routes/translate.routes.js +0 -88
  178. package/server/dist/routes/tv-library.routes.js +0 -23
  179. package/server/dist/routes/tv.routes.js +0 -629
  180. package/server/dist/routes/watchlist.routes.js +0 -341
@@ -1,4 +1,6 @@
1
- import { dbAll, dbGet, dbRun } from '../utils/db-utils.js';
1
+ import { eq, inArray, sql } from 'drizzle-orm';
2
+ import { getDrizzle } from '../db/drizzle.js';
3
+ import { mangaLibrary, mangaProgress } from '../db/schema-manga.js';
2
4
  export const MANGA_STATUSES = [
3
5
  'Reading',
4
6
  'Completed',
@@ -10,144 +12,134 @@ export function buildMangaId(provider, mangaId) {
10
12
  return `${provider.toLowerCase()}:${mangaId}`;
11
13
  }
12
14
  export const MangaLibraryRepository = {
13
- getById: (db, id) => dbGet(db, 'SELECT * FROM manga_library WHERE id = ?', [id]),
14
- exists: (db, id) => {
15
- const row = dbGet(db, 'SELECT EXISTS(SELECT 1 FROM manga_library WHERE id = ?) as inLibrary', [id]);
16
- return !!(row && row.inLibrary);
15
+ getById: async (db, id) => {
16
+ const rows = await getDrizzle(db).all(sql `SELECT * FROM manga_library WHERE id = ${id}`);
17
+ return rows[0];
18
+ },
19
+ exists: async (db, id) => {
20
+ const rows = await getDrizzle(db).all(sql `SELECT EXISTS(SELECT 1 FROM manga_library WHERE id = ${id}) as inLibrary`);
21
+ return !!(rows[0] && rows[0].inLibrary);
17
22
  },
18
23
  getAll: (db, status, limit, offset) => {
19
- let query = 'SELECT * FROM manga_library';
20
- const params = [];
24
+ const q = sql `SELECT * FROM manga_library`;
21
25
  if (status && status !== 'All') {
22
- query += ' WHERE status = ?';
23
- params.push(status);
26
+ q.append(sql ` WHERE status = ${status}`);
24
27
  }
25
- query += ' ORDER BY updatedAt DESC';
28
+ q.append(sql ` ORDER BY updatedAt DESC`);
26
29
  if (limit !== undefined && offset !== undefined) {
27
- query += ' LIMIT ? OFFSET ?';
28
- params.push(limit, offset);
30
+ q.append(sql ` LIMIT ${limit} OFFSET ${offset}`);
29
31
  }
30
- return dbAll(db, query, params);
32
+ return getDrizzle(db).all(q);
31
33
  },
32
- getCount: (db, status) => {
33
- let query = 'SELECT COUNT(*) as total FROM manga_library';
34
- const params = [];
34
+ getCount: async (db, status) => {
35
35
  if (status && status !== 'All') {
36
- query += ' WHERE status = ?';
37
- params.push(status);
36
+ const rows = await getDrizzle(db).all(sql `SELECT COUNT(*) as total FROM manga_library WHERE status = ${status}`);
37
+ return rows[0]?.total || 0;
38
38
  }
39
- const row = dbGet(db, query, params);
40
- return row?.total || 0;
39
+ const rows = await getDrizzle(db).all(sql `SELECT COUNT(*) as total FROM manga_library`);
40
+ return rows[0]?.total || 0;
41
+ },
42
+ getIds: async (db) => {
43
+ const rows = await getDrizzle(db).all(sql `SELECT id FROM manga_library`);
44
+ return rows.map((r) => r.id);
41
45
  },
42
- getIds: (db) => dbAll(db, 'SELECT id FROM manga_library').map((r) => r.id),
43
- upsert: (db, data) => dbRun(db, `INSERT INTO manga_library (id, provider, mangaId, title, cover, status, author, altTitle, contentRating, anilistId, anilistIdSource, updatedAt)
44
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, strftime('%s', 'now'))
45
- ON CONFLICT(id) DO UPDATE SET
46
- provider = COALESCE(NULLIF(EXCLUDED.provider, ''), manga_library.provider),
47
- title = COALESCE(NULLIF(EXCLUDED.title, ''), manga_library.title),
48
- cover = COALESCE(NULLIF(EXCLUDED.cover, ''), manga_library.cover),
49
- status = COALESCE(NULLIF(EXCLUDED.status, ''), manga_library.status),
50
- author = COALESCE(NULLIF(EXCLUDED.author, ''), manga_library.author),
51
- altTitle = COALESCE(NULLIF(EXCLUDED.altTitle, ''), manga_library.altTitle),
52
- contentRating = COALESCE(EXCLUDED.contentRating, manga_library.contentRating),
53
- anilistId = COALESCE(EXCLUDED.anilistId, manga_library.anilistId),
54
- anilistIdSource = COALESCE(EXCLUDED.anilistIdSource, manga_library.anilistIdSource),
55
- updatedAt = strftime('%s', 'now')`, [
56
- data.id,
57
- data.provider,
58
- data.mangaId,
59
- data.title,
60
- data.cover,
61
- data.status,
62
- data.author || null,
63
- data.altTitle || null,
64
- data.contentRating || null,
65
- data.anilistId ?? null,
66
- data.anilistIdSource ?? null,
67
- ]),
46
+ upsert: (db, data) => getDrizzle(db).run(sql `
47
+ INSERT INTO manga_library (id, provider, mangaId, title, cover, status, author, altTitle, contentRating, anilistId, anilistIdSource, updatedAt)
48
+ VALUES (${data.id}, ${data.provider}, ${data.mangaId}, ${data.title}, ${data.cover}, ${data.status}, ${data.author || null}, ${data.altTitle || null}, ${data.contentRating || null}, ${data.anilistId ?? null}, ${data.anilistIdSource ?? null}, strftime('%s', 'now'))
49
+ ON CONFLICT(id) DO UPDATE SET
50
+ provider = COALESCE(NULLIF(EXCLUDED.provider, ''), manga_library.provider),
51
+ title = COALESCE(NULLIF(EXCLUDED.title, ''), manga_library.title),
52
+ cover = COALESCE(NULLIF(EXCLUDED.cover, ''), manga_library.cover),
53
+ status = COALESCE(NULLIF(EXCLUDED.status, ''), manga_library.status),
54
+ author = COALESCE(NULLIF(EXCLUDED.author, ''), manga_library.author),
55
+ altTitle = COALESCE(NULLIF(EXCLUDED.altTitle, ''), manga_library.altTitle),
56
+ contentRating = COALESCE(EXCLUDED.contentRating, manga_library.contentRating),
57
+ anilistId = COALESCE(EXCLUDED.anilistId, manga_library.anilistId),
58
+ anilistIdSource = COALESCE(EXCLUDED.anilistIdSource, manga_library.anilistIdSource),
59
+ updatedAt = strftime('%s', 'now')`),
68
60
  setAnilistId: (db, id, anilistId, source) => source
69
- ? dbRun(db, 'UPDATE manga_library SET anilistId = ?, anilistIdSource = ? WHERE id = ?', [
70
- anilistId,
71
- source,
72
- id,
73
- ])
74
- : dbRun(db, 'UPDATE manga_library SET anilistId = ? WHERE id = ?', [anilistId, id]),
75
- getByAnilistId: (db, anilistId) => dbAll(db, 'SELECT * FROM manga_library WHERE anilistId = ?', [anilistId]),
76
- updateStatus: (db, id, status) => dbRun(db, "UPDATE manga_library SET status = ?, updatedAt = strftime('%s', 'now') WHERE id = ?", [status, id]),
61
+ ? getDrizzle(db)
62
+ .update(mangaLibrary)
63
+ .set({ anilistId, anilistIdSource: source })
64
+ .where(eq(mangaLibrary.id, id))
65
+ : getDrizzle(db).update(mangaLibrary).set({ anilistId }).where(eq(mangaLibrary.id, id)),
66
+ getByAnilistId: (db, anilistId) => getDrizzle(db).all(sql `SELECT * FROM manga_library WHERE anilistId = ${anilistId}`),
67
+ updateStatus: (db, id, status) => getDrizzle(db).run(sql `
68
+ UPDATE manga_library SET status = ${status}, updatedAt = strftime('%s', 'now') WHERE id = ${id}`),
77
69
  updateStatusMany: (db, ids, status) => {
78
70
  if (ids.length === 0)
79
- return;
80
- const placeholders = ids.map(() => '?').join(', ');
81
- dbRun(db, `UPDATE manga_library SET status = ?, updatedAt = strftime('%s', 'now') WHERE id IN (${placeholders})`, [status, ...ids]);
71
+ return Promise.resolve();
72
+ return getDrizzle(db).run(sql `
73
+ UPDATE manga_library SET status = ${status}, updatedAt = strftime('%s', 'now') WHERE id IN (${sql.join(ids.map((id) => sql `${id}`), sql `, `)})`);
82
74
  },
83
- touchProgress: (db, id, progress) => dbRun(db, `UPDATE manga_library SET lastChapterId = ?, lastChapterNumber = ?, lastPage = ?, updatedAt = strftime('%s', 'now') WHERE id = ?`, [progress.chapterId, progress.chapterNumber, progress.page, id]),
84
- setProgressPointer: (db, id, chapterNumber) => dbRun(db, `UPDATE manga_library SET lastChapterNumber = ?, updatedAt = strftime('%s', 'now') WHERE id = ?`, [chapterNumber, id]),
85
- delete: (db, id) => dbRun(db, 'DELETE FROM manga_library WHERE id = ?', [id]),
75
+ touchProgress: (db, id, progress) => getDrizzle(db).run(sql `
76
+ UPDATE manga_library SET lastChapterId = ${progress.chapterId}, lastChapterNumber = ${progress.chapterNumber}, lastPage = ${progress.page}, updatedAt = strftime('%s', 'now') WHERE id = ${id}`),
77
+ setProgressPointer: (db, id, chapterNumber) => getDrizzle(db).run(sql `
78
+ UPDATE manga_library SET lastChapterNumber = ${chapterNumber}, updatedAt = strftime('%s', 'now') WHERE id = ${id}`),
79
+ delete: (db, id) => getDrizzle(db).delete(mangaLibrary).where(eq(mangaLibrary.id, id)),
86
80
  deleteMany: (db, ids) => {
87
81
  if (ids.length === 0)
88
- return;
89
- const placeholders = ids.map(() => '?').join(', ');
90
- dbRun(db, `DELETE FROM manga_library WHERE id IN (${placeholders})`, ids);
82
+ return Promise.resolve();
83
+ return getDrizzle(db).delete(mangaLibrary).where(inArray(mangaLibrary.id, ids));
91
84
  },
92
85
  };
93
86
  export const MangaProgressRepository = {
94
- getByManga: (db, mangaId) => dbAll(db, 'SELECT mangaId, chapterId, chapterNumber, page, pageCount, updatedAt FROM manga_progress WHERE mangaId = ? ORDER BY updatedAt DESC', [mangaId]),
95
- getChapter: (db, mangaId, chapterId) => dbGet(db, 'SELECT mangaId, chapterId, chapterNumber, page, pageCount, updatedAt FROM manga_progress WHERE mangaId = ? AND chapterId = ?', [mangaId, chapterId]),
96
- getLatest: (db, mangaId) => dbGet(db, 'SELECT mangaId, chapterId, chapterNumber, page, pageCount, updatedAt FROM manga_progress WHERE mangaId = ? ORDER BY updatedAt DESC LIMIT 1', [mangaId]),
97
- upsert: (db, data) => dbRun(db, `INSERT INTO manga_progress (mangaId, chapterId, chapterNumber, page, pageCount, title, cover, provider, altTitle, contentRating, updatedAt)
98
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, strftime('%s', 'now'))
99
- ON CONFLICT(mangaId, chapterId) DO UPDATE SET
100
- chapterNumber = COALESCE(NULLIF(EXCLUDED.chapterNumber, ''), manga_progress.chapterNumber),
101
- page = EXCLUDED.page,
102
- pageCount = EXCLUDED.pageCount,
103
- title = COALESCE(EXCLUDED.title, manga_progress.title),
104
- cover = COALESCE(EXCLUDED.cover, manga_progress.cover),
105
- provider = COALESCE(EXCLUDED.provider, manga_progress.provider),
106
- altTitle = COALESCE(EXCLUDED.altTitle, manga_progress.altTitle),
107
- contentRating = COALESCE(EXCLUDED.contentRating, manga_progress.contentRating),
108
- updatedAt = strftime('%s', 'now')`, [
109
- data.mangaId,
110
- data.chapterId,
111
- data.chapterNumber,
112
- data.page,
113
- data.pageCount,
114
- data.title ?? null,
115
- data.cover ?? null,
116
- data.provider ?? null,
117
- data.altTitle ?? null,
118
- data.contentRating ?? null,
119
- ]),
120
- deleteByManga: (db, mangaId) => dbRun(db, 'DELETE FROM manga_progress WHERE mangaId = ?', [mangaId]),
87
+ getByManga: (db, mangaId) => getDrizzle(db).all(sql `
88
+ SELECT mangaId, chapterId, chapterNumber, page, pageCount, updatedAt FROM manga_progress WHERE mangaId = ${mangaId} ORDER BY updatedAt DESC`),
89
+ getChapter: async (db, mangaId, chapterId) => {
90
+ const rows = await getDrizzle(db).all(sql `
91
+ SELECT mangaId, chapterId, chapterNumber, page, pageCount, updatedAt FROM manga_progress WHERE mangaId = ${mangaId} AND chapterId = ${chapterId}`);
92
+ return rows[0];
93
+ },
94
+ getLatest: async (db, mangaId) => {
95
+ const rows = await getDrizzle(db).all(sql `
96
+ SELECT mangaId, chapterId, chapterNumber, page, pageCount, updatedAt FROM manga_progress WHERE mangaId = ${mangaId} ORDER BY updatedAt DESC LIMIT 1`);
97
+ return rows[0];
98
+ },
99
+ upsert: (db, data) => getDrizzle(db).run(sql `
100
+ INSERT INTO manga_progress (mangaId, chapterId, chapterNumber, page, pageCount, title, cover, provider, altTitle, contentRating, updatedAt)
101
+ VALUES (${data.mangaId}, ${data.chapterId}, ${data.chapterNumber}, ${data.page}, ${data.pageCount}, ${data.title ?? null}, ${data.cover ?? null}, ${data.provider ?? null}, ${data.altTitle ?? null}, ${data.contentRating ?? null}, strftime('%s', 'now'))
102
+ ON CONFLICT(mangaId, chapterId) DO UPDATE SET
103
+ chapterNumber = COALESCE(NULLIF(EXCLUDED.chapterNumber, ''), manga_progress.chapterNumber),
104
+ page = EXCLUDED.page,
105
+ pageCount = EXCLUDED.pageCount,
106
+ title = COALESCE(EXCLUDED.title, manga_progress.title),
107
+ cover = COALESCE(EXCLUDED.cover, manga_progress.cover),
108
+ provider = COALESCE(EXCLUDED.provider, manga_progress.provider),
109
+ altTitle = COALESCE(EXCLUDED.altTitle, manga_progress.altTitle),
110
+ contentRating = COALESCE(EXCLUDED.contentRating, manga_progress.contentRating),
111
+ updatedAt = strftime('%s', 'now')`),
112
+ deleteByManga: (db, mangaId) => getDrizzle(db).delete(mangaProgress).where(eq(mangaProgress.mangaId, mangaId)),
121
113
  deleteMany: (db, ids) => {
122
114
  if (ids.length === 0)
123
- return;
124
- const placeholders = ids.map(() => '?').join(', ');
125
- dbRun(db, `DELETE FROM manga_progress WHERE mangaId IN (${placeholders})`, ids);
115
+ return Promise.resolve();
116
+ return getDrizzle(db).delete(mangaProgress).where(inArray(mangaProgress.mangaId, ids));
126
117
  },
127
- deleteChapter: (db, mangaId, chapterId) => dbRun(db, 'DELETE FROM manga_progress WHERE mangaId = ? AND chapterId = ?', [
128
- mangaId,
129
- chapterId,
130
- ]),
131
- deleteSyntheticChapters: (db, mangaId, keepChapterId) => dbRun(db, `DELETE FROM manga_progress WHERE mangaId = ? AND chapterId LIKE 'anilist:ch:%' AND chapterId != ?`, [mangaId, keepChapterId]),
132
- moveSyntheticChapters: (db, fromMangaId, toMangaId) => dbRun(db, `UPDATE manga_progress SET mangaId = ?, updatedAt = strftime('%s', 'now') WHERE mangaId = ? AND chapterId LIKE 'anilist:ch:%'`, [toMangaId, fromMangaId]),
118
+ deleteChapter: (db, mangaId, chapterId) => getDrizzle(db).run(sql `
119
+ DELETE FROM manga_progress WHERE mangaId = ${mangaId} AND chapterId = ${chapterId}`),
120
+ deleteSyntheticChapters: (db, mangaId, keepChapterId) => getDrizzle(db).run(sql `
121
+ DELETE FROM manga_progress WHERE mangaId = ${mangaId} AND chapterId LIKE 'anilist:ch:%' AND chapterId != ${keepChapterId}`),
122
+ moveSyntheticChapters: (db, fromMangaId, toMangaId) => getDrizzle(db).run(sql `
123
+ UPDATE manga_progress SET mangaId = ${toMangaId}, updatedAt = strftime('%s', 'now') WHERE mangaId = ${fromMangaId} AND chapterId LIKE 'anilist:ch:%'`),
133
124
  getContinueReading: (db, limit) => {
134
125
  const limitClause = typeof limit === 'number' ? `LIMIT ${limit}` : '';
135
- return dbAll(db, `SELECT p.mangaId as id,
136
- COALESCE(l.mangaId, SUBSTR(p.mangaId, INSTR(p.mangaId, ':') + 1)) as mangaId,
137
- COALESCE(l.provider, p.provider) as provider,
138
- COALESCE(l.title, p.title) as title, COALESCE(l.cover, p.cover) as cover,
139
- COALESCE(l.author, '') as author, COALESCE(l.altTitle, p.altTitle) as altTitle,
140
- COALESCE(l.contentRating, p.contentRating) as contentRating,
141
- l.status as watchlistStatus,
142
- p.chapterId, p.chapterNumber, p.page, p.pageCount, p.updatedAt as progressAt
143
- FROM (
144
- SELECT *, ROW_NUMBER() OVER (PARTITION BY mangaId ORDER BY updatedAt DESC) as rn
145
- FROM manga_progress
146
- ) p
147
- LEFT JOIN manga_library l ON p.mangaId = l.id
148
- WHERE p.rn = 1
149
- AND (l.status IS NULL OR l.status = 'Reading')
150
- ORDER BY p.updatedAt DESC
151
- ${limitClause}`);
126
+ return getDrizzle(db).all(sql `
127
+ SELECT p.mangaId as id,
128
+ COALESCE(l.mangaId, SUBSTR(p.mangaId, INSTR(p.mangaId, ':') + 1)) as mangaId,
129
+ COALESCE(l.provider, p.provider) as provider,
130
+ COALESCE(l.title, p.title) as title, COALESCE(l.cover, p.cover) as cover,
131
+ COALESCE(l.author, '') as author, COALESCE(l.altTitle, p.altTitle) as altTitle,
132
+ COALESCE(l.contentRating, p.contentRating) as contentRating,
133
+ l.status as watchlistStatus,
134
+ p.chapterId, p.chapterNumber, p.page, p.pageCount, p.updatedAt as progressAt
135
+ FROM (
136
+ SELECT *, ROW_NUMBER() OVER (PARTITION BY mangaId ORDER BY updatedAt DESC) as rn
137
+ FROM manga_progress
138
+ ) p
139
+ LEFT JOIN manga_library l ON p.mangaId = l.id
140
+ WHERE p.rn = 1
141
+ AND (l.status IS NULL OR l.status = 'Reading')
142
+ ORDER BY p.updatedAt DESC
143
+ ${sql.raw(limitClause)}`);
152
144
  },
153
145
  };
@@ -1,41 +1,60 @@
1
- import { dbAll, dbGet, dbRun } from '../utils/db-utils.js';
1
+ import { and, eq, sql } from 'drizzle-orm';
2
+ import { getDrizzle } from '../db/drizzle.js';
3
+ import { discoveredNotifications, dismissedNotifications } from '../db/schema-anime.js';
2
4
  export const NotificationsRepository = {
3
- getDismissedByShow: (db, showId) => dbAll(db, 'SELECT episodeNumber FROM dismissed_notifications WHERE showId = ?', [showId]),
4
- getDiscoveredByShow: (db, showId) => dbAll(db, 'SELECT episodeNumber FROM discovered_notifications WHERE showId = ?', [showId]),
5
- addDiscovered: (db, showId, episodeNumber) => dbRun(db, 'INSERT OR IGNORE INTO discovered_notifications (showId, episodeNumber) VALUES (?, ?)', [showId, episodeNumber]),
6
- addDismissed: (db, showId, episodeNumber) => dbRun(db, 'INSERT OR IGNORE INTO dismissed_notifications (showId, episodeNumber) VALUES (?, ?)', [showId, episodeNumber]),
5
+ getDismissedByShow: (db, showId) => getDrizzle(db).all(sql `SELECT episodeNumber FROM dismissed_notifications WHERE showId = ${showId}`),
6
+ getDiscoveredByShow: (db, showId) => getDrizzle(db).all(sql `SELECT episodeNumber FROM discovered_notifications WHERE showId = ${showId}`),
7
+ addDiscovered: (db, showId, episodeNumber) => getDrizzle(db)
8
+ .insert(discoveredNotifications)
9
+ .values({ showId, episodeNumber })
10
+ .onConflictDoNothing(),
11
+ addDismissed: (db, showId, episodeNumber) => getDrizzle(db)
12
+ .insert(dismissedNotifications)
13
+ .values({ showId, episodeNumber })
14
+ .onConflictDoNothing(),
7
15
  dismissFromDiscovered: (db, showId) => {
8
16
  if (showId) {
9
- return dbRun(db, 'INSERT OR IGNORE INTO dismissed_notifications (showId, episodeNumber) SELECT showId, episodeNumber FROM discovered_notifications WHERE showId = ?', [showId]);
10
- }
11
- else {
12
- return dbRun(db, 'INSERT OR IGNORE INTO dismissed_notifications (showId, episodeNumber) SELECT showId, episodeNumber FROM discovered_notifications');
17
+ return getDrizzle(db).run(sql `
18
+ INSERT OR IGNORE INTO dismissed_notifications (showId, episodeNumber) SELECT showId, episodeNumber FROM discovered_notifications WHERE showId = ${showId}`);
13
19
  }
20
+ return getDrizzle(db).run(sql `
21
+ INSERT OR IGNORE INTO dismissed_notifications (showId, episodeNumber) SELECT showId, episodeNumber FROM discovered_notifications`);
14
22
  },
15
23
  deleteByShow: (db, showId) => Promise.all([
16
- dbRun(db, 'DELETE FROM dismissed_notifications WHERE showId = ?', [showId]),
17
- dbRun(db, 'DELETE FROM discovered_notifications WHERE showId = ?', [showId]),
18
- ]),
19
- deleteSpecificDismissed: (db, showId, episodeNumber) => dbRun(db, 'DELETE FROM dismissed_notifications WHERE showId = ? AND episodeNumber = ?', [
20
- showId,
21
- episodeNumber,
22
- ]),
23
- deleteDiscovered: (db, showId, episodeNumber) => dbRun(db, 'DELETE FROM discovered_notifications WHERE showId = ? AND episodeNumber = ?', [
24
- showId,
25
- episodeNumber,
24
+ getDrizzle(db)
25
+ .delete(dismissedNotifications)
26
+ .where(eq(dismissedNotifications.showId, showId)),
27
+ getDrizzle(db)
28
+ .delete(discoveredNotifications)
29
+ .where(eq(discoveredNotifications.showId, showId)),
26
30
  ]),
31
+ deleteSpecificDismissed: (db, showId, episodeNumber) => getDrizzle(db)
32
+ .delete(dismissedNotifications)
33
+ .where(and(eq(dismissedNotifications.showId, showId), eq(dismissedNotifications.episodeNumber, episodeNumber))),
34
+ deleteDiscovered: (db, showId, episodeNumber) => getDrizzle(db)
35
+ .delete(discoveredNotifications)
36
+ .where(and(eq(discoveredNotifications.showId, showId), eq(discoveredNotifications.episodeNumber, episodeNumber))),
27
37
  cleanupWatchedNotifications: (db) => Promise.all([
28
- dbRun(db, 'DELETE FROM dismissed_notifications WHERE EXISTS (SELECT 1 FROM watched_episodes we WHERE we.showId = dismissed_notifications.showId AND we.episodeNumber = dismissed_notifications.episodeNumber)'),
29
- dbRun(db, 'DELETE FROM discovered_notifications WHERE EXISTS (SELECT 1 FROM watched_episodes we WHERE we.showId = discovered_notifications.showId AND we.episodeNumber = discovered_notifications.episodeNumber)'),
38
+ getDrizzle(db).run(sql `
39
+ DELETE FROM dismissed_notifications WHERE EXISTS (SELECT 1 FROM watched_episodes we WHERE we.showId = dismissed_notifications.showId AND we.episodeNumber = dismissed_notifications.episodeNumber)`),
40
+ getDrizzle(db).run(sql `
41
+ DELETE FROM discovered_notifications WHERE EXISTS (SELECT 1 FROM watched_episodes we WHERE we.showId = discovered_notifications.showId AND we.episodeNumber = discovered_notifications.episodeNumber)`),
30
42
  ]),
31
- hasAnyDiscovered: (db) => Promise.resolve(dbGet(db, 'SELECT COUNT(*) as count FROM discovered_notifications')).then((row) => (row?.count || 0) > 0),
32
- hasActiveNotifications: (db) => Promise.resolve(dbGet(db, `SELECT COUNT(*) as count FROM discovered_notifications dn
33
- WHERE NOT EXISTS (
34
- SELECT 1 FROM dismissed_notifications dn2
35
- WHERE dn2.showId = dn.showId AND dn2.episodeNumber = dn.episodeNumber
36
- )
37
- AND NOT EXISTS (
38
- SELECT 1 FROM watched_episodes we
39
- WHERE we.showId = dn.showId AND we.episodeNumber = dn.episodeNumber
40
- )`)).then((row) => (row?.count || 0) > 0),
43
+ hasAnyDiscovered: async (db) => {
44
+ const rows = await getDrizzle(db).all(sql `SELECT COUNT(*) as count FROM discovered_notifications`);
45
+ return (rows[0]?.count || 0) > 0;
46
+ },
47
+ hasActiveNotifications: async (db) => {
48
+ const rows = await getDrizzle(db).all(sql `
49
+ SELECT COUNT(*) as count FROM discovered_notifications dn
50
+ WHERE NOT EXISTS (
51
+ SELECT 1 FROM dismissed_notifications dn2
52
+ WHERE dn2.showId = dn.showId AND dn2.episodeNumber = dn.episodeNumber
53
+ )
54
+ AND NOT EXISTS (
55
+ SELECT 1 FROM watched_episodes we
56
+ WHERE we.showId = dn.showId AND we.episodeNumber = dn.episodeNumber
57
+ )`);
58
+ return (rows[0]?.count || 0) > 0;
59
+ },
41
60
  };
@@ -1,6 +1,9 @@
1
- import { dbAll, dbGet, dbRun } from '../utils/db-utils.js';
1
+ import { and, eq, sql } from 'drizzle-orm';
2
+ import { getDrizzle } from '../db/drizzle.js';
3
+ import { queue } from '../db/schema-anime.js';
2
4
  export const QueueRepository = {
3
- getAll: (db) => dbAll(db, `SELECT
5
+ getAll: (db) => getDrizzle(db).all(sql `
6
+ SELECT
4
7
  q.id,
5
8
  q.showId,
6
9
  q.episodeNumber,
@@ -14,17 +17,21 @@ export const QueueRepository = {
14
17
  LEFT JOIN shows_meta sm ON q.showId = sm.id
15
18
  LEFT JOIN watchlist w ON q.showId = w.id
16
19
  ORDER BY q.queue_order ASC, q.id ASC`),
17
- getByEpisode: (db, showId, episodeNumber) => dbGet(db, 'SELECT * FROM queue WHERE showId = ? AND episodeNumber = ?', [
18
- showId,
19
- episodeNumber,
20
- ]),
21
- getByShow: (db, showId) => dbAll(db, 'SELECT * FROM queue WHERE showId = ? ORDER BY queue_order ASC', [showId]),
20
+ getByEpisode: async (db, showId, episodeNumber) => {
21
+ const rows = await getDrizzle(db).all(sql `SELECT * FROM queue WHERE showId = ${showId} AND episodeNumber = ${episodeNumber}`);
22
+ return rows[0];
23
+ },
24
+ getByShow: (db, showId) => getDrizzle(db).all(sql `SELECT * FROM queue WHERE showId = ${showId} ORDER BY queue_order ASC`),
22
25
  getMaxOrder: async (db) => {
23
- const row = await dbGet(db, 'SELECT COALESCE(MAX(queue_order), -1) as maxOrder FROM queue');
24
- return row?.maxOrder ?? -1;
26
+ const rows = await getDrizzle(db).all(sql `SELECT COALESCE(MAX(queue_order), -1) as maxOrder FROM queue`);
27
+ return rows[0]?.maxOrder ?? -1;
25
28
  },
26
- addToEnd: (db, showId, episodeNumber) => dbRun(db, 'INSERT INTO queue (showId, episodeNumber, queue_order) VALUES (?, ?, (SELECT COALESCE(MAX(queue_order), -1) + 1 FROM queue))', [showId, episodeNumber]),
27
- removeEpisode: (db, showId, episodeNumber) => dbRun(db, 'DELETE FROM queue WHERE showId = ? AND episodeNumber = ?', [showId, episodeNumber]),
29
+ addToEnd: (db, showId, episodeNumber) => getDrizzle(db).run(sql `
30
+ INSERT INTO queue (showId, episodeNumber, queue_order) VALUES
31
+ (${showId}, ${episodeNumber}, (SELECT COALESCE(MAX(queue_order), -1) + 1 FROM queue))`),
32
+ removeEpisode: (db, showId, episodeNumber) => getDrizzle(db)
33
+ .delete(queue)
34
+ .where(and(eq(queue.showId, showId), eq(queue.episodeNumber, episodeNumber))),
28
35
  addManyToEnd: async (db, episodes) => {
29
36
  if (episodes.length === 0)
30
37
  return;
@@ -33,29 +40,33 @@ export const QueueRepository = {
33
40
  const existing = await QueueRepository.getByEpisode(db, episode.showId, episode.episodeNumber);
34
41
  if (existing)
35
42
  continue;
36
- await dbRun(db, 'INSERT INTO queue (showId, episodeNumber, queue_order) VALUES (?, ?, ?)', [
37
- episode.showId,
38
- episode.episodeNumber,
39
- nextOrder,
40
- ]);
43
+ await getDrizzle(db).insert(queue).values({
44
+ showId: episode.showId,
45
+ episodeNumber: episode.episodeNumber,
46
+ queueOrder: nextOrder,
47
+ });
41
48
  nextOrder += 1;
42
49
  }
43
50
  },
44
51
  removeMany: (db, showId, episodeNumbers) => {
45
52
  if (episodeNumbers.length === 0)
46
53
  return Promise.resolve();
47
- const placeholders = episodeNumbers.map(() => '?').join(', ');
48
- return dbRun(db, `DELETE FROM queue WHERE showId = ? AND episodeNumber IN (${placeholders})`, [
49
- showId,
50
- ...episodeNumbers,
51
- ]);
54
+ return getDrizzle(db).run(sql `
55
+ DELETE FROM queue WHERE showId = ${showId} AND episodeNumber IN (${sql.join(episodeNumbers.map((e) => sql `${e}`), sql `, `)})`);
52
56
  },
53
- clear: (db) => dbRun(db, 'DELETE FROM queue'),
57
+ clear: (db) => getDrizzle(db).delete(queue),
54
58
  reorder: (db, items) => Promise.all(items.map((item, index) => {
55
59
  if (item.id !== undefined) {
56
- return dbRun(db, 'UPDATE queue SET queue_order = ? WHERE id = ?', [index, item.id]);
60
+ return getDrizzle(db)
61
+ .update(queue)
62
+ .set({ queueOrder: index })
63
+ .where(eq(queue.id, item.id));
57
64
  }
58
- return dbRun(db, 'UPDATE queue SET queue_order = ? WHERE showId = ? AND episodeNumber = ?', [index, item.showId, item.episodeNumber]);
65
+ return getDrizzle(db)
66
+ .update(queue)
67
+ .set({ queueOrder: index })
68
+ .where(and(eq(queue.showId, item.showId ?? ''), eq(queue.episodeNumber, item.episodeNumber ?? '')));
59
69
  })),
60
- cleanupOrphanedShowsMeta: (db) => dbRun(db, 'DELETE FROM shows_meta WHERE id NOT IN (SELECT id FROM watchlist) AND id NOT IN (SELECT showId FROM queue)'),
70
+ cleanupOrphanedShowsMeta: (db) => getDrizzle(db).run(sql `
71
+ DELETE FROM shows_meta WHERE id NOT IN (SELECT id FROM watchlist) AND id NOT IN (SELECT showId FROM queue)`),
61
72
  };
@@ -1,12 +1,22 @@
1
- import { dbGet, dbRun } from '../utils/db-utils.js';
1
+ import { eq, sql } from 'drizzle-orm';
2
+ import { getDrizzle } from '../db/drizzle.js';
3
+ import { settings, watchlist } from '../db/schema-anime.js';
2
4
  export const SettingsRepository = {
3
- getByKey: (db, key) => dbGet(db, 'SELECT value FROM settings WHERE key = ?', [key]),
4
- upsert: (db, key, value) => dbRun(db, 'INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)', [key, value]),
5
- deleteByKey: (db, key) => dbRun(db, 'DELETE FROM settings WHERE key = ?', [key]),
6
- clearWatchlist: (db) => dbRun(db, 'DELETE FROM watchlist'),
5
+ getByKey: async (db, key) => {
6
+ const rows = await getDrizzle(db).all(sql `SELECT value FROM settings WHERE key = ${key}`);
7
+ return rows[0];
8
+ },
9
+ upsert: (db, key, value) => getDrizzle(db)
10
+ .insert(settings)
11
+ .values({ key, value })
12
+ .onConflictDoUpdate({ target: settings.key, set: { value } }),
13
+ deleteByKey: (db, key) => getDrizzle(db).delete(settings).where(eq(settings.key, key)),
14
+ clearWatchlist: (db) => getDrizzle(db).delete(watchlist),
7
15
  upsertWatchlistBatch: (db, shows) => {
8
- for (const show of shows) {
9
- dbRun(db, 'INSERT OR REPLACE INTO watchlist (id, name, thumbnail, status) VALUES (?, ?, ?, ?)', [show.id, show.name, show.thumbnail ?? null, show.status]);
10
- }
16
+ if (shows.length === 0)
17
+ return Promise.resolve();
18
+ return getDrizzle(db).run(sql `
19
+ INSERT OR REPLACE INTO watchlist (id, name, thumbnail, status) VALUES
20
+ ${sql.join(shows.map((s) => sql `(${s.id}, ${s.name}, ${s.thumbnail ?? null}, ${s.status})`), sql `, `)}`);
11
21
  },
12
22
  };
@@ -1,45 +1,58 @@
1
- import { dbAll, dbGet, dbRun } from '../utils/db-utils.js';
1
+ import { eq, sql } from 'drizzle-orm';
2
+ import { getDrizzle } from '../db/drizzle.js';
3
+ import { showsMeta } from '../db/schema-anime.js';
2
4
  export const ShowsMetaRepository = {
3
- getById: (db, id) => dbGet(db, 'SELECT * FROM shows_meta WHERE id = ?', [id]),
5
+ getById: async (db, id) => {
6
+ const rows = await getDrizzle(db).all(sql `SELECT * FROM shows_meta WHERE id = ${id}`);
7
+ return rows[0];
8
+ },
4
9
  getStatus: async (db, id) => {
5
- const row = await dbGet(db, 'SELECT status FROM shows_meta WHERE id = ?', [
6
- id,
7
- ]);
8
- return row?.status;
10
+ const rows = await getDrizzle(db).all(sql `SELECT status FROM shows_meta WHERE id = ${id}`);
11
+ return rows[0]?.status;
12
+ },
13
+ upsert: (db, data) => {
14
+ const row = {
15
+ id: data.id,
16
+ name: data.name ?? null,
17
+ thumbnail: data.thumbnail ?? null,
18
+ nativeName: data.nativeName ?? null,
19
+ englishName: data.englishName ?? null,
20
+ genres: data.genres ?? null,
21
+ popularityScore: data.popularityScore ?? null,
22
+ status: data.status ?? null,
23
+ episodeCount: data.episodeCount ?? null,
24
+ type: data.type ?? null,
25
+ anilistId: data.anilistId ?? null,
26
+ isAdult: data.isAdult ?? null,
27
+ episodeDuration: data.episodeDuration ?? null,
28
+ };
29
+ return getDrizzle(db)
30
+ .insert(showsMeta)
31
+ .values(row)
32
+ .onConflictDoUpdate({
33
+ target: showsMeta.id,
34
+ set: {
35
+ name: sql `COALESCE(NULLIF(${row.name}, ''), ${showsMeta.name})`,
36
+ thumbnail: sql `COALESCE(NULLIF(${row.thumbnail}, ''), ${showsMeta.thumbnail})`,
37
+ nativeName: sql `COALESCE(NULLIF(${row.nativeName}, ''), ${showsMeta.nativeName})`,
38
+ englishName: sql `COALESCE(NULLIF(${row.englishName}, ''), ${showsMeta.englishName})`,
39
+ genres: sql `COALESCE(NULLIF(${row.genres}, ''), ${showsMeta.genres})`,
40
+ popularityScore: sql `COALESCE(${row.popularityScore}, ${showsMeta.popularityScore})`,
41
+ status: sql `COALESCE(NULLIF(${row.status}, ''), ${showsMeta.status})`,
42
+ episodeCount: sql `COALESCE(${row.episodeCount}, ${showsMeta.episodeCount})`,
43
+ type: sql `COALESCE(NULLIF(${row.type}, ''), ${showsMeta.type})`,
44
+ anilistId: sql `COALESCE(${row.anilistId}, ${showsMeta.anilistId})`,
45
+ isAdult: sql `COALESCE(${row.isAdult}, ${showsMeta.isAdult})`,
46
+ episodeDuration: sql `COALESCE(NULLIF(${row.episodeDuration}, 0), ${showsMeta.episodeDuration})`,
47
+ },
48
+ });
9
49
  },
10
- upsert: (db, data) => dbRun(db, `INSERT INTO shows_meta (id, name, thumbnail, nativeName, englishName, genres, popularityScore, status, episodeCount, type, anilistId, isAdult, episodeDuration)
11
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
12
- ON CONFLICT(id) DO UPDATE SET
13
- name = COALESCE(NULLIF(EXCLUDED.name, ''), shows_meta.name),
14
- thumbnail = COALESCE(NULLIF(EXCLUDED.thumbnail, ''), shows_meta.thumbnail),
15
- nativeName = COALESCE(NULLIF(EXCLUDED.nativeName, ''), shows_meta.nativeName),
16
- englishName = COALESCE(NULLIF(EXCLUDED.englishName, ''), shows_meta.englishName),
17
- genres = COALESCE(NULLIF(EXCLUDED.genres, ''), shows_meta.genres),
18
- popularityScore = COALESCE(EXCLUDED.popularityScore, shows_meta.popularityScore),
19
- status = COALESCE(NULLIF(EXCLUDED.status, ''), shows_meta.status),
20
- episodeCount = COALESCE(EXCLUDED.episodeCount, shows_meta.episodeCount),
21
- type = COALESCE(NULLIF(EXCLUDED.type, ''), shows_meta.type),
22
- anilistId = COALESCE(EXCLUDED.anilistId, shows_meta.anilistId),
23
- isAdult = COALESCE(EXCLUDED.isAdult, shows_meta.isAdult),
24
- episodeDuration = COALESCE(NULLIF(EXCLUDED.episodeDuration, 0), shows_meta.episodeDuration)`, [
25
- data.id,
26
- data.name ?? null,
27
- data.thumbnail ?? null,
28
- data.nativeName ?? null,
29
- data.englishName ?? null,
30
- data.genres ?? null,
31
- data.popularityScore ?? null,
32
- data.status ?? null,
33
- data.episodeCount ?? null,
34
- data.type ?? null,
35
- data.anilistId ?? null,
36
- data.isAdult ?? null,
37
- data.episodeDuration ?? null,
38
- ]),
39
- updateEpisodeCount: (db, id, episodeCount) => dbRun(db, 'UPDATE shows_meta SET episodeCount = ? WHERE id = ?', [episodeCount, id]),
40
- updateStatus: (db, id, status) => dbRun(db, 'UPDATE shows_meta SET status = ? WHERE id = ?', [status, id]),
41
- cleanupOrphanedMeta: (db) => dbRun(db, 'DELETE FROM shows_meta WHERE id NOT IN (SELECT id FROM watchlist) AND id NOT IN (SELECT showId FROM queue)'),
42
- getShowIdsMissingMeta: (db) => dbAll(db, `SELECT DISTINCT we.showId as id FROM watched_episodes we
43
- LEFT JOIN shows_meta sm ON sm.id = we.showId
44
- WHERE sm.thumbnail IS NULL OR TRIM(sm.thumbnail) = ''`),
50
+ updateEpisodeCount: (db, id, episodeCount) => getDrizzle(db).update(showsMeta).set({ episodeCount }).where(eq(showsMeta.id, id)),
51
+ updateStatus: (db, id, status) => getDrizzle(db).update(showsMeta).set({ status }).where(eq(showsMeta.id, id)),
52
+ cleanupOrphanedMeta: (db) => getDrizzle(db).run(sql `
53
+ DELETE FROM shows_meta WHERE id NOT IN (SELECT id FROM watchlist) AND id NOT IN (SELECT showId FROM queue)`),
54
+ getShowIdsMissingMeta: (db) => getDrizzle(db).all(sql `
55
+ SELECT DISTINCT we.showId as id FROM watched_episodes we
56
+ LEFT JOIN shows_meta sm ON sm.id = we.showId
57
+ WHERE sm.thumbnail IS NULL OR TRIM(sm.thumbnail) = ''`),
45
58
  };