@serifpersia/dango 3.3.8 → 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-D4qo4289.js → AnimeCard-CJy1SXH3.js} +1 -1
  3. package/client/dist/assets/{AnimeInfo-BdRRm4So.js → AnimeInfo-BoXlVvZ-.js} +1 -1
  4. package/client/dist/assets/{AnimeInfoPage-DeJNAKMR.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-D3hTqTRU.js → LibraryListPage-HzuDavbb.js} +1 -1
  11. package/client/dist/assets/{ListeningList-BgWsfu40.js → ListeningList-DYcQnnsG.js} +1 -1
  12. package/client/dist/assets/{Manga-Dn-CYins.js → Manga-D8CT9aO0.js} +1 -1
  13. package/client/dist/assets/{MangaInfoPage-EmQoK7Bv.js → MangaInfoPage-DGK6CYp1.js} +1 -1
  14. package/client/dist/assets/MangaReadPage-B9XoDKF9.js +1 -0
  15. package/client/dist/assets/{MangaResetProgressModal-Dian37PQ.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-B7DeSFoA.js → QueueRail-O7f4RqjB.js} +1 -1
  24. package/client/dist/assets/Radio-DPZxQnyD.js +1 -0
  25. package/client/dist/assets/{ReadingList-BT8kUjCB.js → ReadingList-n6rr8ygy.js} +1 -1
  26. package/client/dist/assets/{ResetProgressModal-DWeiO-hs.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-B5loz7ii.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-BYwCf5kO.js → TvSearch-DuqR-z3v.js} +1 -1
  36. package/client/dist/assets/{TvWatchlist-BiJU6pKT.js → TvWatchlist-CBvgFrA0.js} +1 -1
  37. package/client/dist/assets/Watchlist-mj8mTOam.js +1 -0
  38. package/client/dist/assets/{hls-C11FOWHj.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-cT_Uu41e.js → useAsmrPopup-e4zNN9ig.js} +1 -1
  43. package/client/dist/assets/useMangaLibrary-C9Lci5N4.js +1 -0
  44. package/client/dist/assets/{useMangaPopup-PnmSctHu.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 +15 -4
  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} +235 -295
  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-D0EGRYsv.js +0 -2
  126. package/client/dist/assets/AsmrResetProgressModal-Gs0GmVaT.js +0 -1
  127. package/client/dist/assets/AvSyncCalibrator-D8hqarVB.js +0 -10
  128. package/client/dist/assets/Home-BnBzjFkW.js +0 -1
  129. package/client/dist/assets/Insights-i9QgY3nE.js +0 -1
  130. package/client/dist/assets/MangaReadPage-xJB8K2YU.js +0 -1
  131. package/client/dist/assets/Mature-DQtXKOXc.js +0 -1
  132. package/client/dist/assets/MediaCard-DpKoY-1j.js +0 -1
  133. package/client/dist/assets/Music-BP43Zqmq.js +0 -1
  134. package/client/dist/assets/OptionListMenu-Bj9ebsW3.js +0 -1
  135. package/client/dist/assets/Player-O2Cy2GF1.js +0 -56
  136. package/client/dist/assets/PlayerSettings-DYZ272Yp.js +0 -1
  137. package/client/dist/assets/QueueOptionsButton-lv26-6jp.js +0 -1
  138. package/client/dist/assets/Radio-qQBYnVfY.js +0 -1
  139. package/client/dist/assets/Search-BkvPYN33.js +0 -1
  140. package/client/dist/assets/Settings-CyZEBF6S.js +0 -21
  141. package/client/dist/assets/Trackers-JK-aXBYC.js +0 -2
  142. package/client/dist/assets/Tv-BwXWJxMy.js +0 -4
  143. package/client/dist/assets/Tv-C2SWcVzN.css +0 -1
  144. package/client/dist/assets/TvInfo-Dzyq6ucH.js +0 -1
  145. package/client/dist/assets/TvPopup-PlCU30FO.js +0 -1
  146. package/client/dist/assets/Watchlist-BOEeGXH9.js +0 -1
  147. package/client/dist/assets/index-CtoKRNLc.js +0 -178
  148. package/client/dist/assets/useAnimeInfoData-kr3WOIWH.js +0 -1
  149. package/client/dist/assets/useAsmr-CB9THf0m.js +0 -1
  150. package/client/dist/assets/useMangaLibrary-rM6z3_0E.js +0 -1
  151. package/client/dist/assets/useProviders-B3VB7A9M.js +0 -1
  152. package/client/dist/assets/useTv-BTUKMH7Q.js +0 -1
  153. package/client/dist/assets/useTvLibrary-C0sX-ilZ.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,51 +1,63 @@
1
- import { dbGet, dbRun } from '../utils/db-utils.js';
1
+ import { sql } from 'drizzle-orm';
2
+ import { getDrizzle } from '../db/drizzle.js';
3
+ import { tempShowIds } from '../db/schema-anime.js';
2
4
  import { TEMP_SHOW_ID_PREFIX } from '../lib/temp-ids.js';
3
5
  export const TempShowIdsRepository = {
4
- getById: (db, id) => dbGet(db, 'SELECT * FROM temp_show_ids WHERE id = ?', [id]),
5
- getByProviderNative: (db, provider, nativeId) => dbGet(db, 'SELECT * FROM temp_show_ids WHERE provider = ? AND nativeId = ?', [
6
- provider,
7
- nativeId,
8
- ]),
9
- count: (db) => {
10
- const row = dbGet(db, 'SELECT COUNT(*) as total FROM temp_show_ids');
11
- return row?.total || 0;
6
+ getById: async (db, id) => {
7
+ const rows = await getDrizzle(db).all(sql `SELECT * FROM temp_show_ids WHERE id = ${id}`);
8
+ return rows[0];
12
9
  },
13
- allocate: (db, data) => {
14
- const existing = TempShowIdsRepository.getByProviderNative(db, data.provider, data.nativeId);
10
+ getByProviderNative: async (db, provider, nativeId) => {
11
+ const rows = await getDrizzle(db).all(sql `SELECT * FROM temp_show_ids WHERE provider = ${provider} AND nativeId = ${nativeId}`);
12
+ return rows[0];
13
+ },
14
+ count: async (db) => {
15
+ const rows = await getDrizzle(db).all(sql `SELECT COUNT(*) as total FROM temp_show_ids`);
16
+ return rows[0]?.total || 0;
17
+ },
18
+ allocate: async (db, data) => {
19
+ const existing = await TempShowIdsRepository.getByProviderNative(db, data.provider, data.nativeId);
15
20
  if (existing)
16
21
  return existing;
17
22
  const prefixLen = TEMP_SHOW_ID_PREFIX.length + 1;
18
- const maxRow = dbGet(db, `SELECT MAX(CAST(SUBSTR(id, ?) AS INTEGER)) as maxId FROM temp_show_ids WHERE id LIKE '${TEMP_SHOW_ID_PREFIX}%'`, [prefixLen]);
19
- const next = (maxRow?.maxId || 0) + 1;
23
+ const maxRows = await getDrizzle(db).all(sql `
24
+ SELECT MAX(CAST(SUBSTR(id, ${prefixLen}) AS INTEGER)) as maxId FROM temp_show_ids WHERE id LIKE ${`${TEMP_SHOW_ID_PREFIX}%`}`);
25
+ const next = (maxRows[0]?.maxId || 0) + 1;
20
26
  const id = `${TEMP_SHOW_ID_PREFIX}${next}`;
21
27
  try {
22
- dbRun(db, 'INSERT INTO temp_show_ids (id, provider, nativeId, title, thumbnail, createdAt) VALUES (?, ?, ?, ?, ?, ?)', [id, data.provider, data.nativeId, data.title, data.thumbnail || '', Date.now()]);
28
+ await getDrizzle(db)
29
+ .insert(tempShowIds)
30
+ .values({
31
+ id,
32
+ provider: data.provider,
33
+ nativeId: data.nativeId,
34
+ title: data.title,
35
+ thumbnail: data.thumbnail || '',
36
+ createdAt: Date.now(),
37
+ });
23
38
  }
24
39
  catch {
25
- // Lost a race with a concurrent allocate — return whatever won.
26
- const winner = TempShowIdsRepository.getByProviderNative(db, data.provider, data.nativeId);
40
+ const winner = await TempShowIdsRepository.getByProviderNative(db, data.provider, data.nativeId);
27
41
  if (winner)
28
42
  return winner;
29
43
  throw new Error('temp id allocation failed');
30
44
  }
31
- return TempShowIdsRepository.getById(db, id);
45
+ return (await TempShowIdsRepository.getById(db, id));
32
46
  },
33
- purge: (db) => {
34
- const total = TempShowIdsRepository.count(db);
47
+ purge: async (db) => {
48
+ const total = await TempShowIdsRepository.count(db);
35
49
  if (total === 0)
36
50
  return 0;
37
- const like = `${TEMP_SHOW_ID_PREFIX}%`;
38
- dbRun(db, 'DELETE FROM watchlist WHERE id LIKE ?', [like]);
39
- dbRun(db, 'DELETE FROM watched_episodes WHERE showId LIKE ?', [like]);
40
- dbRun(db, 'DELETE FROM queue WHERE showId LIKE ?', [like]);
41
- dbRun(db, 'DELETE FROM shows_meta WHERE id LIKE ?', [like]);
42
- dbRun(db, 'DELETE FROM dismissed_notifications WHERE showId LIKE ?', [like]);
43
- dbRun(db, 'DELETE FROM discovered_notifications WHERE showId LIKE ?', [like]);
44
- dbRun(db, 'DELETE FROM legacy_id_mapping WHERE legacyId LIKE ? OR numericId LIKE ?', [
45
- like,
46
- like,
47
- ]);
48
- dbRun(db, 'DELETE FROM temp_show_ids');
51
+ const likePattern = `${TEMP_SHOW_ID_PREFIX}%`;
52
+ await getDrizzle(db).run(sql `DELETE FROM watchlist WHERE id LIKE ${likePattern}`);
53
+ await getDrizzle(db).run(sql `DELETE FROM watched_episodes WHERE showId LIKE ${likePattern}`);
54
+ await getDrizzle(db).run(sql `DELETE FROM queue WHERE showId LIKE ${likePattern}`);
55
+ await getDrizzle(db).run(sql `DELETE FROM shows_meta WHERE id LIKE ${likePattern}`);
56
+ await getDrizzle(db).run(sql `DELETE FROM dismissed_notifications WHERE showId LIKE ${likePattern}`);
57
+ await getDrizzle(db).run(sql `DELETE FROM discovered_notifications WHERE showId LIKE ${likePattern}`);
58
+ await getDrizzle(db).run(sql `
59
+ DELETE FROM legacy_id_mapping WHERE legacyId LIKE ${likePattern} OR numericId LIKE ${likePattern}`);
60
+ await getDrizzle(db).delete(tempShowIds);
49
61
  return total;
50
62
  },
51
63
  };
@@ -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 { tvLibrary, tvProgress } from '../db/schema-tv.js';
2
4
  export const TV_STATUSES = ['Watching', 'Completed', 'On-Hold', 'Dropped', 'Planned'];
3
5
  export function buildTvId(mediaType, tmdbId) {
4
6
  const t = String(mediaType).toLowerCase() === 'movie' ? 'movie' : 'tv';
@@ -12,142 +14,123 @@ export function normalizeTvMediaId(raw) {
12
14
  return s;
13
15
  }
14
16
  export const TvLibraryRepository = {
15
- getById: (db, id) => dbGet(db, 'SELECT * FROM tv_library WHERE id = ?', [id]),
16
- exists: (db, id) => {
17
- const row = dbGet(db, 'SELECT EXISTS(SELECT 1 FROM tv_library WHERE id = ?) as inLibrary', [id]);
18
- return !!(row && row.inLibrary);
17
+ getById: async (db, id) => {
18
+ const rows = await getDrizzle(db).all(sql `SELECT * FROM tv_library WHERE id = ${id}`);
19
+ return rows[0];
20
+ },
21
+ exists: async (db, id) => {
22
+ const rows = await getDrizzle(db).all(sql `SELECT EXISTS(SELECT 1 FROM tv_library WHERE id = ${id}) as inLibrary`);
23
+ return !!(rows[0] && rows[0].inLibrary);
19
24
  },
20
25
  getAll: (db, status, limit, offset) => {
21
- let query = 'SELECT * FROM tv_library';
22
- const params = [];
26
+ const q = sql `SELECT * FROM tv_library`;
23
27
  if (status && status !== 'All') {
24
- query += ' WHERE status = ?';
25
- params.push(status);
28
+ q.append(sql ` WHERE status = ${status}`);
26
29
  }
27
- query += ' ORDER BY updatedAt DESC';
30
+ q.append(sql ` ORDER BY updatedAt DESC`);
28
31
  if (limit !== undefined && offset !== undefined) {
29
- query += ' LIMIT ? OFFSET ?';
30
- params.push(limit, offset);
32
+ q.append(sql ` LIMIT ${limit} OFFSET ${offset}`);
31
33
  }
32
- return dbAll(db, query, params);
34
+ return getDrizzle(db).all(q);
33
35
  },
34
- getCount: (db, status) => {
35
- let query = 'SELECT COUNT(*) as total FROM tv_library';
36
- const params = [];
36
+ getCount: async (db, status) => {
37
37
  if (status && status !== 'All') {
38
- query += ' WHERE status = ?';
39
- params.push(status);
38
+ const rows = await getDrizzle(db).all(sql `SELECT COUNT(*) as total FROM tv_library WHERE status = ${status}`);
39
+ return rows[0]?.total || 0;
40
40
  }
41
- const row = dbGet(db, query, params);
42
- return row?.total || 0;
41
+ const rows = await getDrizzle(db).all(sql `SELECT COUNT(*) as total FROM tv_library`);
42
+ return rows[0]?.total || 0;
43
+ },
44
+ getIds: async (db) => {
45
+ const rows = await getDrizzle(db).all(sql `SELECT id FROM tv_library`);
46
+ return rows.map((r) => r.id);
43
47
  },
44
- getIds: (db) => dbAll(db, 'SELECT id FROM tv_library').map((r) => r.id),
45
- upsert: (db, data) => dbRun(db, `INSERT INTO tv_library (id, tmdbId, mediaType, title, poster, backdrop, year, overview, status, adult, updatedAt)
46
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, strftime('%s', 'now'))
47
- ON CONFLICT(id) DO UPDATE SET
48
- tmdbId = EXCLUDED.tmdbId,
49
- mediaType = EXCLUDED.mediaType,
50
- title = COALESCE(NULLIF(EXCLUDED.title, ''), tv_library.title),
51
- poster = COALESCE(NULLIF(EXCLUDED.poster, ''), tv_library.poster),
52
- backdrop = COALESCE(NULLIF(EXCLUDED.backdrop, ''), tv_library.backdrop),
53
- year = COALESCE(NULLIF(EXCLUDED.year, ''), tv_library.year),
54
- overview = COALESCE(NULLIF(EXCLUDED.overview, ''), tv_library.overview),
55
- status = COALESCE(NULLIF(EXCLUDED.status, ''), tv_library.status),
56
- adult = COALESCE(EXCLUDED.adult, tv_library.adult),
57
- updatedAt = strftime('%s', 'now')`, [
58
- data.id,
59
- data.tmdbId,
60
- data.mediaType,
61
- data.title,
62
- data.poster,
63
- data.backdrop || null,
64
- data.year || null,
65
- data.overview || null,
66
- data.status,
67
- data.adult ? 1 : 0,
68
- ]),
69
- updateStatus: (db, id, status) => dbRun(db, "UPDATE tv_library SET status = ?, updatedAt = strftime('%s', 'now') WHERE id = ?", [
70
- status,
71
- id,
72
- ]),
48
+ upsert: (db, data) => getDrizzle(db).run(sql `
49
+ INSERT INTO tv_library (id, tmdbId, mediaType, title, poster, backdrop, year, overview, status, adult, updatedAt)
50
+ VALUES (${data.id}, ${data.tmdbId}, ${data.mediaType}, ${data.title}, ${data.poster}, ${data.backdrop || null}, ${data.year || null}, ${data.overview || null}, ${data.status}, ${data.adult ? 1 : 0}, strftime('%s', 'now'))
51
+ ON CONFLICT(id) DO UPDATE SET
52
+ tmdbId = EXCLUDED.tmdbId,
53
+ mediaType = EXCLUDED.mediaType,
54
+ title = COALESCE(NULLIF(EXCLUDED.title, ''), tv_library.title),
55
+ poster = COALESCE(NULLIF(EXCLUDED.poster, ''), tv_library.poster),
56
+ backdrop = COALESCE(NULLIF(EXCLUDED.backdrop, ''), tv_library.backdrop),
57
+ year = COALESCE(NULLIF(EXCLUDED.year, ''), tv_library.year),
58
+ overview = COALESCE(NULLIF(EXCLUDED.overview, ''), tv_library.overview),
59
+ status = COALESCE(NULLIF(EXCLUDED.status, ''), tv_library.status),
60
+ adult = COALESCE(EXCLUDED.adult, tv_library.adult),
61
+ updatedAt = strftime('%s', 'now')`),
62
+ updateStatus: (db, id, status) => getDrizzle(db).run(sql `
63
+ UPDATE tv_library SET status = ${status}, updatedAt = strftime('%s', 'now') WHERE id = ${id}`),
73
64
  updateStatusMany: (db, ids, status) => {
74
65
  if (ids.length === 0)
75
- return;
76
- const placeholders = ids.map(() => '?').join(', ');
77
- dbRun(db, `UPDATE tv_library SET status = ?, updatedAt = strftime('%s', 'now') WHERE id IN (${placeholders})`, [status, ...ids]);
66
+ return Promise.resolve();
67
+ return getDrizzle(db).run(sql `
68
+ UPDATE tv_library SET status = ${status}, updatedAt = strftime('%s', 'now') WHERE id IN (${sql.join(ids.map((id) => sql `${id}`), sql `, `)})`);
78
69
  },
79
- touchProgress: (db, id, progress) => dbRun(db, `UPDATE tv_library SET lastSeason = ?, lastEpisode = ?, updatedAt = strftime('%s', 'now') WHERE id = ?`, [progress.season, progress.episode, id]),
80
- delete: (db, id) => dbRun(db, 'DELETE FROM tv_library WHERE id = ?', [id]),
70
+ touchProgress: (db, id, progress) => getDrizzle(db).run(sql `
71
+ UPDATE tv_library SET lastSeason = ${progress.season}, lastEpisode = ${progress.episode}, updatedAt = strftime('%s', 'now') WHERE id = ${id}`),
72
+ delete: (db, id) => getDrizzle(db).delete(tvLibrary).where(eq(tvLibrary.id, id)),
81
73
  deleteMany: (db, ids) => {
82
74
  if (ids.length === 0)
83
- return;
84
- const placeholders = ids.map(() => '?').join(', ');
85
- dbRun(db, `DELETE FROM tv_library WHERE id IN (${placeholders})`, ids);
75
+ return Promise.resolve();
76
+ return getDrizzle(db).delete(tvLibrary).where(inArray(tvLibrary.id, ids));
86
77
  },
87
78
  };
88
79
  export const TvProgressRepository = {
89
- getByMedia: (db, mediaId) => dbAll(db, 'SELECT mediaId, season, episode, currentTime, duration, completed, updatedAt FROM tv_progress WHERE mediaId = ? ORDER BY updatedAt DESC, rowid DESC', [mediaId]),
90
- getEpisode: (db, mediaId, season, episode) => dbGet(db, 'SELECT mediaId, season, episode, currentTime, duration, completed, updatedAt FROM tv_progress WHERE mediaId = ? AND season = ? AND episode = ?', [mediaId, season, episode]),
91
- getLatest: (db, mediaId) => dbGet(db, 'SELECT mediaId, season, episode, currentTime, duration, completed, updatedAt FROM tv_progress WHERE mediaId = ? ORDER BY updatedAt DESC, rowid DESC LIMIT 1', [mediaId]),
92
- upsert: (db, data) => dbRun(db, `INSERT INTO tv_progress (mediaId, season, episode, currentTime, duration, completed, title, poster, backdrop, year, overview, tmdbId, mediaType, adult, updatedAt)
93
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, strftime('%s', 'now'))
94
- ON CONFLICT(mediaId, season, episode) DO UPDATE SET
95
- currentTime = EXCLUDED.currentTime,
96
- duration = EXCLUDED.duration,
97
- completed = CASE WHEN tv_progress.completed = 1 OR EXCLUDED.completed = 1 THEN 1 ELSE 0 END,
98
- title = COALESCE(EXCLUDED.title, tv_progress.title),
99
- poster = COALESCE(EXCLUDED.poster, tv_progress.poster),
100
- backdrop = COALESCE(EXCLUDED.backdrop, tv_progress.backdrop),
101
- year = COALESCE(EXCLUDED.year, tv_progress.year),
102
- overview = COALESCE(EXCLUDED.overview, tv_progress.overview),
103
- tmdbId = COALESCE(EXCLUDED.tmdbId, tv_progress.tmdbId),
104
- mediaType = COALESCE(EXCLUDED.mediaType, tv_progress.mediaType),
105
- adult = COALESCE(EXCLUDED.adult, tv_progress.adult),
106
- updatedAt = strftime('%s', 'now')`, [
107
- data.mediaId,
108
- data.season,
109
- data.episode,
110
- data.currentTime,
111
- data.duration,
112
- data.completed,
113
- data.title ?? null,
114
- data.poster ?? null,
115
- data.backdrop ?? null,
116
- data.year ?? null,
117
- data.overview ?? null,
118
- data.tmdbId ?? null,
119
- data.mediaType ?? null,
120
- data.adult ?? null,
121
- ]),
122
- deleteByMedia: (db, mediaId) => dbRun(db, 'DELETE FROM tv_progress WHERE mediaId = ?', [mediaId]),
80
+ getByMedia: (db, mediaId) => getDrizzle(db).all(sql `
81
+ SELECT mediaId, season, episode, currentTime, duration, completed, updatedAt FROM tv_progress WHERE mediaId = ${mediaId} ORDER BY updatedAt DESC, rowid DESC`),
82
+ getEpisode: async (db, mediaId, season, episode) => {
83
+ const rows = await getDrizzle(db).all(sql `
84
+ SELECT mediaId, season, episode, currentTime, duration, completed, updatedAt FROM tv_progress WHERE mediaId = ${mediaId} AND season = ${season} AND episode = ${episode}`);
85
+ return rows[0];
86
+ },
87
+ getLatest: async (db, mediaId) => {
88
+ const rows = await getDrizzle(db).all(sql `
89
+ SELECT mediaId, season, episode, currentTime, duration, completed, updatedAt FROM tv_progress WHERE mediaId = ${mediaId} ORDER BY updatedAt DESC, rowid DESC LIMIT 1`);
90
+ return rows[0];
91
+ },
92
+ upsert: (db, data) => getDrizzle(db).run(sql `
93
+ INSERT INTO tv_progress (mediaId, season, episode, currentTime, duration, completed, title, poster, backdrop, year, overview, tmdbId, mediaType, adult, updatedAt)
94
+ VALUES (${data.mediaId}, ${data.season}, ${data.episode}, ${data.currentTime}, ${data.duration}, ${data.completed}, ${data.title ?? null}, ${data.poster ?? null}, ${data.backdrop ?? null}, ${data.year ?? null}, ${data.overview ?? null}, ${data.tmdbId ?? null}, ${data.mediaType ?? null}, ${data.adult ?? null}, strftime('%s', 'now'))
95
+ ON CONFLICT(mediaId, season, episode) DO UPDATE SET
96
+ currentTime = EXCLUDED.currentTime,
97
+ duration = EXCLUDED.duration,
98
+ completed = EXCLUDED.completed,
99
+ title = COALESCE(EXCLUDED.title, tv_progress.title),
100
+ poster = COALESCE(EXCLUDED.poster, tv_progress.poster),
101
+ backdrop = COALESCE(EXCLUDED.backdrop, tv_progress.backdrop),
102
+ year = COALESCE(EXCLUDED.year, tv_progress.year),
103
+ overview = COALESCE(EXCLUDED.overview, tv_progress.overview),
104
+ tmdbId = COALESCE(EXCLUDED.tmdbId, tv_progress.tmdbId),
105
+ mediaType = COALESCE(EXCLUDED.mediaType, tv_progress.mediaType),
106
+ adult = COALESCE(EXCLUDED.adult, tv_progress.adult),
107
+ updatedAt = strftime('%s', 'now')`),
108
+ deleteByMedia: (db, mediaId) => getDrizzle(db).delete(tvProgress).where(eq(tvProgress.mediaId, mediaId)),
123
109
  deleteMany: (db, ids) => {
124
110
  if (ids.length === 0)
125
- return;
126
- const placeholders = ids.map(() => '?').join(', ');
127
- dbRun(db, `DELETE FROM tv_progress WHERE mediaId IN (${placeholders})`, ids);
111
+ return Promise.resolve();
112
+ return getDrizzle(db).delete(tvProgress).where(inArray(tvProgress.mediaId, ids));
128
113
  },
129
- deleteEpisode: (db, mediaId, season, episode) => dbRun(db, 'DELETE FROM tv_progress WHERE mediaId = ? AND season = ? AND episode = ?', [
130
- mediaId,
131
- season,
132
- episode,
133
- ]),
114
+ deleteEpisode: (db, mediaId, season, episode) => getDrizzle(db).run(sql `
115
+ DELETE FROM tv_progress WHERE mediaId = ${mediaId} AND season = ${season} AND episode = ${episode}`),
134
116
  getContinueWatching: (db, limit) => {
135
117
  const limitClause = typeof limit === 'number' ? `LIMIT ${limit}` : '';
136
- return dbAll(db, `SELECT p.mediaId as id,
137
- COALESCE(l.tmdbId, p.tmdbId, CAST(SUBSTR(REPLACE(p.mediaId, '-', ':'), INSTR(REPLACE(p.mediaId, '-', ':'), ':') + 1) AS INTEGER)) as tmdbId,
138
- COALESCE(l.mediaType, p.mediaType, SUBSTR(REPLACE(p.mediaId, '-', ':'), 1, INSTR(REPLACE(p.mediaId, '-', ':'), ':') - 1)) as mediaType,
139
- COALESCE(l.title, p.title) as title, COALESCE(l.poster, p.poster) as poster, COALESCE(l.backdrop, p.backdrop) as backdrop,
140
- COALESCE(l.year, p.year) as year, COALESCE(l.overview, p.overview) as overview,
141
- l.status as watchlistStatus, COALESCE(l.adult, p.adult) as adult,
142
- p.season, p.episode, p.currentTime, p.duration, p.completed, p.updatedAt as progressAt
143
- FROM (
144
- SELECT *, rowid AS progressRowid, ROW_NUMBER() OVER (PARTITION BY mediaId ORDER BY updatedAt DESC, rowid DESC) as rn
145
- FROM tv_progress
146
- ) p
147
- LEFT JOIN tv_library l ON p.mediaId = l.id
148
- WHERE p.rn = 1
149
- AND (l.status IS NULL OR l.status = 'Watching')
150
- ORDER BY p.updatedAt DESC, p.progressRowid DESC
151
- ${limitClause}`);
118
+ return getDrizzle(db).all(sql `
119
+ SELECT p.mediaId as id,
120
+ COALESCE(l.tmdbId, p.tmdbId, CAST(SUBSTR(REPLACE(p.mediaId, '-', ':'), INSTR(REPLACE(p.mediaId, '-', ':'), ':') + 1) AS INTEGER)) as tmdbId,
121
+ COALESCE(l.mediaType, p.mediaType, SUBSTR(REPLACE(p.mediaId, '-', ':'), 1, INSTR(REPLACE(p.mediaId, '-', ':'), ':') - 1)) as mediaType,
122
+ COALESCE(l.title, p.title) as title, COALESCE(l.poster, p.poster) as poster, COALESCE(l.backdrop, p.backdrop) as backdrop,
123
+ COALESCE(l.year, p.year) as year, COALESCE(l.overview, p.overview) as overview,
124
+ l.status as watchlistStatus, COALESCE(l.adult, p.adult) as adult,
125
+ p.season, p.episode, p.currentTime, p.duration, p.completed, p.updatedAt as progressAt
126
+ FROM (
127
+ SELECT *, rowid AS progressRowid, ROW_NUMBER() OVER (PARTITION BY mediaId ORDER BY updatedAt DESC, rowid DESC) as rn
128
+ FROM tv_progress
129
+ ) p
130
+ LEFT JOIN tv_library l ON p.mediaId = l.id
131
+ WHERE p.rn = 1
132
+ AND (l.status IS NULL OR l.status = 'Watching')
133
+ ORDER BY p.updatedAt DESC, p.progressRowid DESC
134
+ ${sql.raw(limitClause)}`);
152
135
  },
153
136
  };
@@ -1,22 +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 { watchedEpisodes } from '../db/schema-anime.js';
2
4
  export const WatchedEpisodesRepository = {
3
- getByShowAndEpisode: (db, showId, episodeNumber) => dbGet(db, 'SELECT currentTime, duration FROM watched_episodes WHERE showId = ? AND episodeNumber = ?', [showId, episodeNumber]),
5
+ getByShowAndEpisode: async (db, showId, episodeNumber) => {
6
+ const rows = await getDrizzle(db).all(sql `
7
+ SELECT currentTime, duration FROM watched_episodes WHERE showId = ${showId} AND episodeNumber = ${episodeNumber}`);
8
+ return rows[0];
9
+ },
4
10
  getWatchedEpisodeNumbers: async (db, showId) => {
5
- const rows = await dbAll(db, 'SELECT episodeNumber FROM watched_episodes WHERE showId = ?', [showId]);
11
+ const rows = await getDrizzle(db).all(sql `SELECT episodeNumber FROM watched_episodes WHERE showId = ${showId}`);
6
12
  return rows.map((r) => r.episodeNumber);
7
13
  },
8
- getByShow: (db, showId) => dbAll(db, 'SELECT showId, episodeNumber, currentTime, duration, watchedAt FROM watched_episodes WHERE showId = ? ORDER BY CAST(episodeNumber AS REAL) ASC', [showId]),
9
- getLatestResumeProgress: (db, showId) => dbGet(db, `SELECT showId, episodeNumber, currentTime, duration, watchedAt
10
- FROM watched_episodes
11
- WHERE showId = ? AND currentTime > 5 AND (duration <= 0 OR currentTime < duration * 0.8)
12
- ORDER BY watchedAt DESC
13
- LIMIT 1`, [showId]),
14
- upsert: (db, data) => dbRun(db, 'INSERT OR REPLACE INTO watched_episodes (showId, episodeNumber, watchedAt, currentTime, duration) VALUES (?, ?, CURRENT_TIMESTAMP, ?, ?)', [data.showId, data.episodeNumber, data.currentTime, data.duration]),
15
- insertIfMissing: (db, data) => dbRun(db, 'INSERT OR IGNORE INTO watched_episodes (showId, episodeNumber, watchedAt, currentTime, duration) VALUES (?, ?, COALESCE(?, CURRENT_TIMESTAMP), 0, 0)', [data.showId, data.episodeNumber, data.watchedAt ?? null]),
16
- deleteByShow: (db, showId) => dbRun(db, 'DELETE FROM watched_episodes WHERE showId = ?', [showId]),
14
+ getByShow: (db, showId) => getDrizzle(db).all(sql `
15
+ SELECT showId, episodeNumber, currentTime, duration, watchedAt FROM watched_episodes WHERE showId = ${showId} ORDER BY CAST(episodeNumber AS REAL) ASC`),
16
+ getLatestResumeProgress: async (db, showId) => {
17
+ const rows = await getDrizzle(db).all(sql `
18
+ SELECT showId, episodeNumber, currentTime, duration, watchedAt
19
+ FROM watched_episodes
20
+ WHERE showId = ${showId} AND currentTime > 5 AND (duration <= 0 OR currentTime < duration * 0.8)
21
+ ORDER BY watchedAt DESC
22
+ LIMIT 1`);
23
+ return rows[0];
24
+ },
25
+ upsert: (db, data) => getDrizzle(db)
26
+ .insert(watchedEpisodes)
27
+ .values({
28
+ showId: data.showId,
29
+ episodeNumber: data.episodeNumber,
30
+ watchedAt: sql `CURRENT_TIMESTAMP`,
31
+ currentTime: data.currentTime,
32
+ duration: data.duration,
33
+ })
34
+ .onConflictDoUpdate({
35
+ target: [watchedEpisodes.showId, watchedEpisodes.episodeNumber],
36
+ set: {
37
+ watchedAt: sql `CURRENT_TIMESTAMP`,
38
+ currentTime: data.currentTime,
39
+ duration: data.duration,
40
+ },
41
+ }),
42
+ insertIfMissing: (db, data) => getDrizzle(db)
43
+ .insert(watchedEpisodes)
44
+ .values({
45
+ showId: data.showId,
46
+ episodeNumber: data.episodeNumber,
47
+ watchedAt: data.watchedAt ? sql `${data.watchedAt}` : sql `CURRENT_TIMESTAMP`,
48
+ currentTime: 0,
49
+ duration: 0,
50
+ })
51
+ .onConflictDoNothing(),
52
+ deleteByShow: (db, showId) => getDrizzle(db).delete(watchedEpisodes).where(eq(watchedEpisodes.showId, showId)),
17
53
  getContinueWatching: (db, limit) => {
18
54
  const limitClause = typeof limit === 'number' ? `LIMIT ${limit}` : '';
19
- const query = `
55
+ return getDrizzle(db).all(sql `
20
56
  SELECT
21
57
  we.showId as _id,
22
58
  we.showId as id,
@@ -41,12 +77,8 @@ export const WatchedEpisodesRepository = {
41
77
  AND (w.status IS NULL OR w.status = 'Watching')
42
78
  AND (w.id IS NOT NULL OR sm.id IS NOT NULL)
43
79
  ORDER BY we.watchedAt DESC
44
- ${limitClause}
45
- `;
46
- return dbAll(db, query);
47
- },
48
- getEpisodesForShows: (db, showIds) => {
49
- const placeholders = showIds.map(() => '?').join(',');
50
- return dbAll(db, `SELECT showId, episodeNumber, currentTime, duration, watchedAt FROM watched_episodes WHERE showId IN (${placeholders})`, showIds);
80
+ ${sql.raw(limitClause)}`);
51
81
  },
82
+ getEpisodesForShows: (db, showIds) => getDrizzle(db).all(sql `
83
+ SELECT showId, episodeNumber, currentTime, duration, watchedAt FROM watched_episodes WHERE showId IN (${sql.join(showIds.map((id) => sql `${id}`), sql `, `)})`),
52
84
  };
@@ -1,72 +1,60 @@
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 { watchlist } from '../db/schema-anime.js';
2
4
  export const WatchlistRepository = {
3
- getById: (db, id) => dbGet(db, 'SELECT * FROM watchlist WHERE id = ?', [id]),
4
- exists: (db, id) => {
5
- const row = dbGet(db, 'SELECT EXISTS(SELECT 1 FROM watchlist WHERE id = ?) as inWatchlist', [id]);
6
- return !!(row && row.inWatchlist);
5
+ getById: async (db, id) => {
6
+ const rows = await getDrizzle(db).all(sql `SELECT * FROM watchlist WHERE id = ${id}`);
7
+ return rows[0];
8
+ },
9
+ exists: async (db, id) => {
10
+ const rows = await getDrizzle(db).all(sql `SELECT EXISTS(SELECT 1 FROM watchlist WHERE id = ${id}) as inWatchlist`);
11
+ return !!(rows[0] && rows[0].inWatchlist);
7
12
  },
8
13
  getAll: (db, status, limit, offset) => {
9
- let query = 'SELECT * FROM watchlist';
10
- const params = [];
14
+ const q = sql `SELECT * FROM watchlist`;
11
15
  if (status && status !== 'All') {
12
- query += ' WHERE status = ?';
13
- params.push(status);
16
+ q.append(sql ` WHERE status = ${status}`);
14
17
  }
15
- query += ' ORDER BY rowid DESC';
18
+ q.append(sql ` ORDER BY rowid DESC`);
16
19
  if (limit !== undefined && offset !== undefined) {
17
- query += ' LIMIT ? OFFSET ?';
18
- params.push(limit, offset);
20
+ q.append(sql ` LIMIT ${limit} OFFSET ${offset}`);
19
21
  }
20
- return dbAll(db, query, params);
22
+ return getDrizzle(db).all(q);
21
23
  },
22
- getCount: (db, status) => {
23
- let query = 'SELECT COUNT(*) as total FROM watchlist';
24
- const params = [];
24
+ getCount: async (db, status) => {
25
+ const q = sql `SELECT COUNT(*) as total FROM watchlist`;
25
26
  if (status && status !== 'All') {
26
- query += ' WHERE status = ?';
27
- params.push(status);
27
+ q.append(sql ` WHERE status = ${status}`);
28
28
  }
29
- const row = dbGet(db, query, params);
30
- return row?.total || 0;
29
+ const rows = await getDrizzle(db).all(q);
30
+ return rows[0]?.total || 0;
31
31
  },
32
- upsert: (db, data) => dbRun(db, `INSERT INTO watchlist (id, name, thumbnail, status, nativeName, englishName, type) VALUES (?, ?, ?, ?, ?, ?, ?)
33
- ON CONFLICT(id) DO UPDATE SET
34
- name = COALESCE(NULLIF(EXCLUDED.name, ''), watchlist.name),
35
- thumbnail = COALESCE(NULLIF(EXCLUDED.thumbnail, ''), watchlist.thumbnail),
36
- status = COALESCE(NULLIF(EXCLUDED.status, ''), watchlist.status),
37
- nativeName = COALESCE(NULLIF(EXCLUDED.nativeName, ''), watchlist.nativeName),
38
- englishName = COALESCE(NULLIF(EXCLUDED.englishName, ''), watchlist.englishName),
39
- type = COALESCE(NULLIF(EXCLUDED.type, ''), watchlist.type)`, [
40
- data.id,
41
- data.name,
42
- data.thumbnail,
43
- data.status,
44
- data.nativeName,
45
- data.englishName,
46
- data.type,
47
- ]),
48
- updateStatus: (db, id, status) => dbRun(db, 'UPDATE watchlist SET status = ? WHERE id = ?', [status, id]),
32
+ upsert: (db, data) => getDrizzle(db).run(sql `
33
+ INSERT INTO watchlist (id, name, thumbnail, status, nativeName, englishName, type) VALUES (${data.id}, ${data.name}, ${data.thumbnail}, ${data.status}, ${data.nativeName}, ${data.englishName}, ${data.type})
34
+ ON CONFLICT(id) DO UPDATE SET
35
+ name = COALESCE(NULLIF(EXCLUDED.name, ''), watchlist.name),
36
+ thumbnail = COALESCE(NULLIF(EXCLUDED.thumbnail, ''), watchlist.thumbnail),
37
+ status = COALESCE(NULLIF(EXCLUDED.status, ''), watchlist.status),
38
+ nativeName = COALESCE(NULLIF(EXCLUDED.nativeName, ''), watchlist.nativeName),
39
+ englishName = COALESCE(NULLIF(EXCLUDED.englishName, ''), watchlist.englishName),
40
+ type = COALESCE(NULLIF(EXCLUDED.type, ''), watchlist.type)`),
41
+ updateStatus: (db, id, status) => getDrizzle(db).update(watchlist).set({ status }).where(eq(watchlist.id, id)),
49
42
  updateThumbnail: (db, id, thumbnail) => {
50
43
  if (!thumbnail || thumbnail.trim() === '')
51
44
  return Promise.resolve();
52
- return dbRun(db, 'UPDATE watchlist SET thumbnail = ? WHERE id = ?', [thumbnail, id]);
45
+ return getDrizzle(db).update(watchlist).set({ thumbnail }).where(eq(watchlist.id, id));
53
46
  },
54
- delete: (db, id) => dbRun(db, 'DELETE FROM watchlist WHERE id = ?', [id]),
47
+ delete: (db, id) => getDrizzle(db).delete(watchlist).where(eq(watchlist.id, id)),
55
48
  deleteMany: (db, ids) => {
56
49
  if (ids.length === 0)
57
50
  return Promise.resolve();
58
- const placeholders = ids.map(() => '?').join(', ');
59
- return dbRun(db, `DELETE FROM watchlist WHERE id IN (${placeholders})`, ids);
51
+ return getDrizzle(db).delete(watchlist).where(inArray(watchlist.id, ids));
60
52
  },
61
53
  updateStatusMany: (db, ids, status) => {
62
54
  if (ids.length === 0)
63
55
  return Promise.resolve();
64
- const placeholders = ids.map(() => '?').join(', ');
65
- return dbRun(db, `UPDATE watchlist SET status = ? WHERE id IN (${placeholders})`, [
66
- status,
67
- ...ids,
68
- ]);
56
+ return getDrizzle(db).update(watchlist).set({ status }).where(inArray(watchlist.id, ids));
69
57
  },
70
- getWatchingShows: (db) => dbAll(db, "SELECT id, name, thumbnail, nativeName, englishName FROM watchlist WHERE status = 'Watching'"),
71
- getMissingThumbnails: (db) => dbAll(db, "SELECT id, name FROM watchlist WHERE thumbnail IS NULL OR TRIM(thumbnail) = ''"),
58
+ getWatchingShows: (db) => getDrizzle(db).all(sql `SELECT id, name, thumbnail, nativeName, englishName FROM watchlist WHERE status = 'Watching'`),
59
+ getMissingThumbnails: (db) => getDrizzle(db).all(sql `SELECT id, name FROM watchlist WHERE thumbnail IS NULL OR TRIM(thumbnail) = ''`),
72
60
  };