@serifpersia/dango 3.3.9 → 3.4.1

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-B0oMgXBm.js} +1 -1
  3. package/client/dist/assets/{AnimeInfo-e3z3er9t.js → AnimeInfo-o0Udf0og.js} +1 -1
  4. package/client/dist/assets/{AnimeInfoPage-B7Djlmy_.js → AnimeInfoPage-CojMIEuf.js} +2 -2
  5. package/client/dist/assets/Asmr-2V3yCCiK.js +2 -0
  6. package/client/dist/assets/AsmrResetProgressModal-C9R7HlNa.js +1 -0
  7. package/client/dist/assets/AvSyncCalibrator-Bo3820j3.js +10 -0
  8. package/client/dist/assets/Home-C8uXTzIM.js +1 -0
  9. package/client/dist/assets/Insights-CRqBfZuK.js +1 -0
  10. package/client/dist/assets/{LibraryListPage-B0qWqhb_.js → LibraryListPage-B_r51cRi.js} +1 -1
  11. package/client/dist/assets/{ListeningList-CSqD8j1C.js → ListeningList-BWnwvJTt.js} +1 -1
  12. package/client/dist/assets/{Manga-Cg2YuObF.js → Manga-CI5cZg3h.js} +1 -1
  13. package/client/dist/assets/{MangaInfoPage-DcArhEsy.js → MangaInfoPage-BtkOrO34.js} +1 -1
  14. package/client/dist/assets/MangaReadPage-Uv72AYdY.js +1 -0
  15. package/client/dist/assets/{MangaResetProgressModal-D_yT3d1v.js → MangaResetProgressModal-CPMEqZyi.js} +1 -1
  16. package/client/dist/assets/Mature-DXHWvoY5.js +1 -0
  17. package/client/dist/assets/MediaCard-DzA336CA.js +1 -0
  18. package/client/dist/assets/Music-C4dM0BQx.js +1 -0
  19. package/client/dist/assets/OptionListMenu-CpwxAC3J.js +1 -0
  20. package/client/dist/assets/Player-CpMvWTkH.js +56 -0
  21. package/client/dist/assets/PlayerSettings-pMU85k7p.js +1 -0
  22. package/client/dist/assets/QueueOptionsButton-1M7MdKzM.js +1 -0
  23. package/client/dist/assets/{QueueRail-B7f1-e6i.js → QueueRail-Bpla2xD8.js} +1 -1
  24. package/client/dist/assets/Radio-DlmTC-qG.js +1 -0
  25. package/client/dist/assets/{ReadingList-BaIem8H1.js → ReadingList-wJMDUybm.js} +1 -1
  26. package/client/dist/assets/{ResetProgressModal-DES5Y-Fl.js → ResetProgressModal-CByRM1Fh.js} +1 -1
  27. package/client/dist/assets/Search-B94D0tRM.js +1 -0
  28. package/client/dist/assets/Settings-CcU3oNY8.js +21 -0
  29. package/client/dist/assets/{SkeletonGrid-BxfgjP4i.js → SkeletonGrid-BJyZkg_K.js} +1 -1
  30. package/client/dist/assets/Trackers-vz-gqxtm.js +2 -0
  31. package/client/dist/assets/Tv-Rji_lvQo.js +4 -0
  32. package/client/dist/assets/Tv-ZEA45gXK.css +1 -0
  33. package/client/dist/assets/TvInfo-Ciujd6os.js +1 -0
  34. package/client/dist/assets/TvPopup-CqlVRF-Y.js +1 -0
  35. package/client/dist/assets/{TvSearch-BWDwWnHe.js → TvSearch-BcKXQue9.js} +1 -1
  36. package/client/dist/assets/{TvWatchlist-D3n5_4MI.js → TvWatchlist-sjsiup2h.js} +1 -1
  37. package/client/dist/assets/Watchlist-DAMdvt7Z.js +1 -0
  38. package/client/dist/assets/{hls-BO_L1WoN.js → hls-Do_n10_Y.js} +1 -1
  39. package/client/dist/assets/index-B5SLDCAi.js +178 -0
  40. package/client/dist/assets/useAnimeInfoData-DU9X90no.js +1 -0
  41. package/client/dist/assets/useAsmr-D6cSjbvt.js +1 -0
  42. package/client/dist/assets/{useAsmrPopup-D_o9-QlR.js → useAsmrPopup-DK1Vx-HV.js} +1 -1
  43. package/client/dist/assets/useMangaLibrary-DcxPn0RM.js +1 -0
  44. package/client/dist/assets/{useMangaPopup-TFzkC-tG.js → useMangaPopup-BLwvRZn0.js} +1 -1
  45. package/client/dist/assets/useProviders-D3L1Chqc.js +1 -0
  46. package/client/dist/assets/useTv-B546u-Rx.js +1 -0
  47. package/client/dist/assets/useTvLibrary-C2rPeCVT.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 +6 -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 { asmrLibrary, asmrProgress } from '../db/schema-asmr.js';
2
4
  export const ASMR_STATUSES = [
3
5
  'Listening',
4
6
  'Completed',
@@ -10,114 +12,115 @@ export function buildAsmrId(rjCode) {
10
12
  return String(rjCode).trim().toUpperCase();
11
13
  }
12
14
  export const AsmrLibraryRepository = {
13
- getById: (db, id) => dbGet(db, 'SELECT * FROM asmr_library WHERE id = ?', [id]),
14
- exists: (db, id) => {
15
- const row = dbGet(db, 'SELECT EXISTS(SELECT 1 FROM asmr_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 asmr_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 asmr_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 asmr_library';
20
- const params = [];
24
+ const q = sql `SELECT * FROM asmr_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 asmr_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 asmr_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 asmr_library`);
40
+ return rows[0]?.total || 0;
41
+ },
42
+ getIds: async (db) => {
43
+ const rows = await getDrizzle(db).all(sql `SELECT id FROM asmr_library`);
44
+ return rows.map((r) => r.id);
41
45
  },
42
- getIds: (db) => dbAll(db, 'SELECT id FROM asmr_library').map((r) => r.id),
43
- upsert: (db, data) => dbRun(db, `INSERT INTO asmr_library (id, rjCode, title, thumbnail, status, isAdult, updatedAt)
44
- VALUES (?, ?, ?, ?, ?, ?, strftime('%s', 'now'))
45
- ON CONFLICT(id) DO UPDATE SET
46
- rjCode = COALESCE(NULLIF(EXCLUDED.rjCode, ''), asmr_library.rjCode),
47
- title = COALESCE(NULLIF(EXCLUDED.title, ''), asmr_library.title),
48
- thumbnail = COALESCE(NULLIF(EXCLUDED.thumbnail, ''), asmr_library.thumbnail),
49
- status = COALESCE(NULLIF(EXCLUDED.status, ''), asmr_library.status),
50
- isAdult = COALESCE(EXCLUDED.isAdult, asmr_library.isAdult),
51
- updatedAt = strftime('%s', 'now')`, [data.id, data.rjCode, data.title, data.thumbnail, data.status, data.isAdult ? 1 : 0]),
52
- updateStatus: (db, id, status) => dbRun(db, "UPDATE asmr_library SET status = ?, updatedAt = strftime('%s', 'now') WHERE id = ?", [status, id]),
46
+ upsert: (db, data) => getDrizzle(db).run(sql `
47
+ INSERT INTO asmr_library (id, rjCode, title, thumbnail, status, isAdult, updatedAt)
48
+ VALUES (${data.id}, ${data.rjCode}, ${data.title}, ${data.thumbnail}, ${data.status}, ${data.isAdult ? 1 : 0}, strftime('%s', 'now'))
49
+ ON CONFLICT(id) DO UPDATE SET
50
+ rjCode = COALESCE(NULLIF(EXCLUDED.rjCode, ''), asmr_library.rjCode),
51
+ title = COALESCE(NULLIF(EXCLUDED.title, ''), asmr_library.title),
52
+ thumbnail = COALESCE(NULLIF(EXCLUDED.thumbnail, ''), asmr_library.thumbnail),
53
+ status = COALESCE(NULLIF(EXCLUDED.status, ''), asmr_library.status),
54
+ isAdult = COALESCE(EXCLUDED.isAdult, asmr_library.isAdult),
55
+ updatedAt = strftime('%s', 'now')`),
56
+ updateStatus: (db, id, status) => getDrizzle(db).run(sql `
57
+ UPDATE asmr_library SET status = ${status}, updatedAt = strftime('%s', 'now') WHERE id = ${id}`),
53
58
  updateStatusMany: (db, ids, status) => {
54
59
  if (ids.length === 0)
55
- return;
56
- const placeholders = ids.map(() => '?').join(', ');
57
- dbRun(db, `UPDATE asmr_library SET status = ?, updatedAt = strftime('%s', 'now') WHERE id IN (${placeholders})`, [status, ...ids]);
60
+ return Promise.resolve();
61
+ return getDrizzle(db).run(sql `
62
+ UPDATE asmr_library SET status = ${status}, updatedAt = strftime('%s', 'now') WHERE id IN (${sql.join(ids.map((id) => sql `${id}`), sql `, `)})`);
58
63
  },
59
- touchProgress: (db, id, progress) => dbRun(db, `UPDATE asmr_library SET lastTrackIndex = ?, lastTrackLabel = ?, lastPosition = ?, updatedAt = strftime('%s', 'now') WHERE id = ?`, [progress.trackIndex, progress.trackLabel, progress.position, id]),
60
- delete: (db, id) => dbRun(db, 'DELETE FROM asmr_library WHERE id = ?', [id]),
64
+ touchProgress: (db, id, progress) => getDrizzle(db).run(sql `
65
+ UPDATE asmr_library SET lastTrackIndex = ${progress.trackIndex}, lastTrackLabel = ${progress.trackLabel}, lastPosition = ${progress.position}, updatedAt = strftime('%s', 'now') WHERE id = ${id}`),
66
+ delete: (db, id) => getDrizzle(db).delete(asmrLibrary).where(eq(asmrLibrary.id, id)),
61
67
  deleteMany: (db, ids) => {
62
68
  if (ids.length === 0)
63
- return;
64
- const placeholders = ids.map(() => '?').join(', ');
65
- dbRun(db, `DELETE FROM asmr_library WHERE id IN (${placeholders})`, ids);
69
+ return Promise.resolve();
70
+ return getDrizzle(db).delete(asmrLibrary).where(inArray(asmrLibrary.id, ids));
66
71
  },
67
72
  };
68
73
  export const AsmrProgressRepository = {
69
- getByWork: (db, workId) => dbAll(db, 'SELECT workId, trackIndex, trackLabel, currentTime, duration, updatedAt FROM asmr_progress WHERE workId = ? ORDER BY updatedAt DESC', [workId]),
70
- getTrack: (db, workId, trackIndex) => dbGet(db, 'SELECT workId, trackIndex, trackLabel, currentTime, duration, updatedAt FROM asmr_progress WHERE workId = ? AND trackIndex = ?', [workId, trackIndex]),
71
- getLatest: (db, workId) => dbGet(db, 'SELECT workId, trackIndex, trackLabel, currentTime, duration, updatedAt FROM asmr_progress WHERE workId = ? ORDER BY updatedAt DESC LIMIT 1', [workId]),
72
- upsert: (db, data) => dbRun(db, `INSERT INTO asmr_progress (workId, trackIndex, trackLabel, currentTime, duration, title, thumbnail, rjCode, isAdult, updatedAt)
73
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, strftime('%s', 'now'))
74
- ON CONFLICT(workId, trackIndex) DO UPDATE SET
75
- trackLabel = COALESCE(NULLIF(EXCLUDED.trackLabel, ''), asmr_progress.trackLabel),
76
- currentTime = EXCLUDED.currentTime,
77
- duration = EXCLUDED.duration,
78
- title = COALESCE(EXCLUDED.title, asmr_progress.title),
79
- thumbnail = COALESCE(EXCLUDED.thumbnail, asmr_progress.thumbnail),
80
- rjCode = COALESCE(EXCLUDED.rjCode, asmr_progress.rjCode),
81
- isAdult = COALESCE(EXCLUDED.isAdult, asmr_progress.isAdult),
82
- updatedAt = strftime('%s', 'now')`, [
83
- data.workId,
84
- data.trackIndex,
85
- data.trackLabel,
86
- data.currentTime,
87
- data.duration,
88
- data.title ?? null,
89
- data.thumbnail ?? null,
90
- data.rjCode ?? null,
91
- data.isAdult ?? null,
92
- ]),
93
- deleteByWork: (db, workId) => dbRun(db, 'DELETE FROM asmr_progress WHERE workId = ?', [workId]),
74
+ getByWork: (db, workId) => getDrizzle(db).all(sql `
75
+ SELECT workId, trackIndex, trackLabel, currentTime, duration, updatedAt FROM asmr_progress WHERE workId = ${workId} ORDER BY updatedAt DESC`),
76
+ getTrack: async (db, workId, trackIndex) => {
77
+ const rows = await getDrizzle(db).all(sql `
78
+ SELECT workId, trackIndex, trackLabel, currentTime, duration, updatedAt FROM asmr_progress WHERE workId = ${workId} AND trackIndex = ${trackIndex}`);
79
+ return rows[0];
80
+ },
81
+ getLatest: async (db, workId) => {
82
+ const rows = await getDrizzle(db).all(sql `
83
+ SELECT workId, trackIndex, trackLabel, currentTime, duration, updatedAt FROM asmr_progress WHERE workId = ${workId} ORDER BY updatedAt DESC LIMIT 1`);
84
+ return rows[0];
85
+ },
86
+ upsert: (db, data) => getDrizzle(db).run(sql `
87
+ INSERT INTO asmr_progress (workId, trackIndex, trackLabel, currentTime, duration, title, thumbnail, rjCode, isAdult, updatedAt)
88
+ VALUES (${data.workId}, ${data.trackIndex}, ${data.trackLabel}, ${data.currentTime}, ${data.duration}, ${data.title ?? null}, ${data.thumbnail ?? null}, ${data.rjCode ?? null}, ${data.isAdult ?? null}, strftime('%s', 'now'))
89
+ ON CONFLICT(workId, trackIndex) DO UPDATE SET
90
+ trackLabel = COALESCE(NULLIF(EXCLUDED.trackLabel, ''), asmr_progress.trackLabel),
91
+ currentTime = EXCLUDED.currentTime,
92
+ duration = EXCLUDED.duration,
93
+ title = COALESCE(EXCLUDED.title, asmr_progress.title),
94
+ thumbnail = COALESCE(EXCLUDED.thumbnail, asmr_progress.thumbnail),
95
+ rjCode = COALESCE(EXCLUDED.rjCode, asmr_progress.rjCode),
96
+ isAdult = COALESCE(EXCLUDED.isAdult, asmr_progress.isAdult),
97
+ updatedAt = strftime('%s', 'now')`),
98
+ deleteByWork: (db, workId) => getDrizzle(db).delete(asmrProgress).where(eq(asmrProgress.workId, workId)),
94
99
  deleteMany: (db, ids) => {
95
100
  if (ids.length === 0)
96
- return;
97
- const placeholders = ids.map(() => '?').join(', ');
98
- dbRun(db, `DELETE FROM asmr_progress WHERE workId IN (${placeholders})`, ids);
101
+ return Promise.resolve();
102
+ return getDrizzle(db).delete(asmrProgress).where(inArray(asmrProgress.workId, ids));
99
103
  },
100
- deleteTrack: (db, workId, trackIndex) => dbRun(db, 'DELETE FROM asmr_progress WHERE workId = ? AND trackIndex = ?', [
101
- workId,
102
- trackIndex,
103
- ]),
104
+ deleteTrack: (db, workId, trackIndex) => getDrizzle(db).run(sql `
105
+ DELETE FROM asmr_progress WHERE workId = ${workId} AND trackIndex = ${trackIndex}`),
104
106
  getContinueListening: (db, limit) => {
105
107
  const limitClause = typeof limit === 'number' ? `LIMIT ${limit}` : '';
106
- return dbAll(db, `SELECT p.workId as id,
107
- COALESCE(l.rjCode, p.rjCode, p.workId) as rjCode,
108
- COALESCE(l.title, p.title) as title,
109
- COALESCE(l.thumbnail, p.thumbnail) as thumbnail,
110
- COALESCE(l.isAdult, p.isAdult) as isAdult,
111
- l.status as watchlistStatus,
112
- p.trackIndex, p.trackLabel, p.currentTime, p.duration, p.updatedAt as progressAt
113
- FROM (
114
- SELECT *, ROW_NUMBER() OVER (PARTITION BY workId ORDER BY updatedAt DESC) as rn
115
- FROM asmr_progress
116
- ) p
117
- LEFT JOIN asmr_library l ON p.workId = l.id
118
- WHERE p.rn = 1
119
- AND (l.status IS NULL OR l.status = 'Listening')
120
- ORDER BY p.updatedAt DESC
121
- ${limitClause}`);
108
+ return getDrizzle(db).all(sql `
109
+ SELECT p.workId as id,
110
+ COALESCE(l.rjCode, p.rjCode, p.workId) as rjCode,
111
+ COALESCE(l.title, p.title) as title,
112
+ COALESCE(l.thumbnail, p.thumbnail) as thumbnail,
113
+ COALESCE(l.isAdult, p.isAdult) as isAdult,
114
+ l.status as watchlistStatus,
115
+ p.trackIndex, p.trackLabel, p.currentTime, p.duration, p.updatedAt as progressAt
116
+ FROM (
117
+ SELECT *, ROW_NUMBER() OVER (PARTITION BY workId ORDER BY updatedAt DESC) as rn
118
+ FROM asmr_progress
119
+ ) p
120
+ LEFT JOIN asmr_library l ON p.workId = l.id
121
+ WHERE p.rn = 1
122
+ AND (l.status IS NULL OR l.status = 'Listening')
123
+ ORDER BY p.updatedAt DESC
124
+ ${sql.raw(limitClause)}`);
122
125
  },
123
126
  };
@@ -1,6 +1,8 @@
1
- import { dbAll, dbGet } from '../utils/db-utils.js';
1
+ import { sql } from 'drizzle-orm';
2
+ import { getDrizzle } from '../db/drizzle.js';
2
3
  export const InsightsRepository = {
3
- getCoreStats: (db) => dbGet(db, `SELECT
4
+ getCoreStats: async (db) => (await getDrizzle(db).all(sql `
5
+ SELECT
4
6
  (SELECT SUM(
5
7
  CASE WHEN we.currentTime > 0 THEN we.currentTime
6
8
  WHEN we.currentTime = 0 AND we.duration = 0 THEN
@@ -16,10 +18,13 @@ export const InsightsRepository = {
16
18
  (SELECT COUNT(*) FROM watched_episodes) as totalEpisodes,
17
19
  (SELECT COUNT(DISTINCT showId) FROM watched_episodes) as totalAnime,
18
20
  (SELECT COUNT(*) FROM watchlist WHERE status = 'Completed') as completedCount,
19
- (SELECT COUNT(*) FROM watchlist) as totalWatchlist`),
20
- getActivityGrid: (db) => dbAll(db, `SELECT date(watchedAt) as day, COUNT(*) as count FROM watched_episodes WHERE NOT (currentTime = 0 AND duration = 0) GROUP BY day`),
21
- getHourlyDist: (db) => dbAll(db, `SELECT strftime('%H', watchedAt) as hour, COUNT(*) as count FROM watched_episodes WHERE NOT (currentTime = 0 AND duration = 0) GROUP BY hour`),
22
- getSeasonality: (db) => dbAll(db, `SELECT strftime('%m', we.watchedAt) as month, SUM(
21
+ (SELECT COUNT(*) FROM watchlist) as totalWatchlist`))[0],
22
+ getActivityGrid: (db) => getDrizzle(db).all(sql `
23
+ SELECT date(watchedAt) as day, COUNT(*) as count FROM watched_episodes WHERE NOT (currentTime = 0 AND duration = 0) GROUP BY day`),
24
+ getHourlyDist: (db) => getDrizzle(db).all(sql `
25
+ SELECT strftime('%H', watchedAt) as hour, COUNT(*) as count FROM watched_episodes WHERE NOT (currentTime = 0 AND duration = 0) GROUP BY hour`),
26
+ getSeasonality: (db) => getDrizzle(db).all(sql `
27
+ SELECT strftime('%m', we.watchedAt) as month, SUM(
23
28
  CASE WHEN we.currentTime > 0 THEN we.currentTime
24
29
  WHEN we.currentTime = 0 AND we.duration = 0 THEN
25
30
  COALESCE(sm.episodeDuration * 60,
@@ -32,7 +37,8 @@ export const InsightsRepository = {
32
37
  FROM watched_episodes we
33
38
  LEFT JOIN shows_meta sm ON sm.id = we.showId
34
39
  GROUP BY month`),
35
- getAllWatches: (db) => dbAll(db, `SELECT we.watchedAt, we.currentTime,
40
+ getAllWatches: (db) => getDrizzle(db).all(sql `
41
+ SELECT we.watchedAt, we.currentTime,
36
42
  CASE WHEN we.currentTime > 0 THEN we.currentTime
37
43
  WHEN we.currentTime = 0 AND we.duration = 0 THEN
38
44
  COALESCE(sm.episodeDuration * 60,
@@ -45,23 +51,27 @@ export const InsightsRepository = {
45
51
  FROM watched_episodes we
46
52
  LEFT JOIN shows_meta sm ON sm.id = we.showId
47
53
  ORDER BY we.watchedAt ASC`),
48
- getWatchedShowsMeta: (db) => dbAll(db, `SELECT DISTINCT sm.id, sm.genres, sm.popularityScore
54
+ getWatchedShowsMeta: (db) => getDrizzle(db).all(sql `
55
+ SELECT DISTINCT sm.id, sm.genres, sm.popularityScore
49
56
  FROM shows_meta sm
50
57
  JOIN watched_episodes we ON sm.id = we.showId`),
51
- getDroppedShows: (db) => dbAll(db, `SELECT w.id, w.name, MAX(we.watchedAt) as lastActivity
58
+ getDroppedShows: (db) => getDrizzle(db).all(sql `
59
+ SELECT w.id, w.name, MAX(we.watchedAt) as lastActivity
52
60
  FROM watchlist w
53
61
  JOIN watched_episodes we ON w.id = we.showId
54
62
  WHERE w.status = 'Watching' AND NOT (we.currentTime = 0 AND we.duration = 0)
55
63
  GROUP BY w.id
56
64
  HAVING lastActivity < date('now', '-90 days')`),
57
- getCompletionVelocities: (db) => dbAll(db, `SELECT
65
+ getCompletionVelocities: (db) => getDrizzle(db).all(sql `
66
+ SELECT
58
67
  (julianday(MAX(we.watchedAt)) - julianday(MIN(we.watchedAt))) as daysToFinish
59
68
  FROM watchlist w
60
69
  JOIN watched_episodes we ON w.id = we.showId
61
70
  WHERE w.status = 'Completed'
62
71
  GROUP BY w.id
63
72
  HAVING SUM(we.currentTime) > 0`),
64
- getWatchedEpisodesWithMeta: (db) => dbAll(db, `SELECT
73
+ getWatchedEpisodesWithMeta: (db) => getDrizzle(db).all(sql `
74
+ SELECT
65
75
  we.showId,
66
76
  we.currentTime,
67
77
  we.duration,
@@ -82,7 +92,8 @@ export const InsightsRepository = {
82
92
  sm.thumbnail
83
93
  FROM watched_episodes we
84
94
  JOIN shows_meta sm ON we.showId = sm.id`),
85
- getLibraryShowsWithGenres: (db) => dbAll(db, `SELECT
95
+ getLibraryShowsWithGenres: (db) => getDrizzle(db).all(sql `
96
+ SELECT
86
97
  w.id,
87
98
  w.status,
88
99
  COALESCE(NULLIF(sm.englishName, ''), sm.name, w.name) as title,
@@ -91,5 +102,5 @@ export const InsightsRepository = {
91
102
  COALESCE(sm.popularityScore, 0) as popularityScore
92
103
  FROM watchlist w
93
104
  LEFT JOIN shows_meta sm ON sm.id = w.id`),
94
- getAllAnilistIds: (db) => dbAll(db, 'SELECT anilistId FROM shows_meta WHERE anilistId IS NOT NULL'),
105
+ getAllAnilistIds: (db) => getDrizzle(db).all(sql `SELECT anilistId FROM shows_meta WHERE anilistId IS NOT NULL`),
95
106
  };
@@ -1,4 +1,5 @@
1
- import { dbGet, dbRun } from '../utils/db-utils.js';
1
+ import { sql } from 'drizzle-orm';
2
+ import { getDrizzle } from '../db/drizzle.js';
2
3
  const LIBRARY_TABLES = [
3
4
  'watchlist',
4
5
  'watched_episodes',
@@ -12,97 +13,36 @@ const LIBRARY_TABLES = [
12
13
  const MANGA_LIBRARY_TABLES = ['manga_library', 'manga_progress'];
13
14
  const TV_LIBRARY_TABLES = ['tv_library', 'tv_progress'];
14
15
  const ASMR_LIBRARY_TABLES = ['asmr_library', 'asmr_progress'];
16
+ async function countTables(db, tables) {
17
+ const counts = {};
18
+ for (const table of tables) {
19
+ try {
20
+ const rows = await getDrizzle(db).all(sql `SELECT COUNT(*) AS rows FROM ${sql.raw(`"${table}"`)}`);
21
+ counts[table] = rows[0]?.rows ?? 0;
22
+ }
23
+ catch {
24
+ counts[table] = 0;
25
+ }
26
+ }
27
+ return counts;
28
+ }
29
+ async function clearTables(db, tables) {
30
+ for (const table of tables) {
31
+ try {
32
+ await getDrizzle(db).run(sql `DELETE FROM ${sql.raw(`"${table}"`)}`);
33
+ }
34
+ catch {
35
+ // ignore
36
+ }
37
+ }
38
+ }
15
39
  export const LibraryRepository = {
16
- countAll: (db) => {
17
- const counts = {};
18
- for (const table of LIBRARY_TABLES) {
19
- try {
20
- counts[table] =
21
- dbGet(db, `SELECT COUNT(*) AS rows FROM "${table}"`)?.rows ?? 0;
22
- }
23
- catch {
24
- counts[table] = 0;
25
- }
26
- }
27
- return counts;
28
- },
29
- clearAll: (db) => {
30
- for (const table of LIBRARY_TABLES) {
31
- try {
32
- dbRun(db, `DELETE FROM "${table}"`);
33
- }
34
- catch {
35
- // ignore
36
- }
37
- }
38
- },
39
- countManga: (db) => {
40
- const counts = {};
41
- for (const table of MANGA_LIBRARY_TABLES) {
42
- try {
43
- counts[table] =
44
- dbGet(db, `SELECT COUNT(*) AS rows FROM "${table}"`)?.rows ?? 0;
45
- }
46
- catch {
47
- counts[table] = 0;
48
- }
49
- }
50
- return counts;
51
- },
52
- clearManga: (db) => {
53
- for (const table of MANGA_LIBRARY_TABLES) {
54
- try {
55
- dbRun(db, `DELETE FROM "${table}"`);
56
- }
57
- catch {
58
- // ignore
59
- }
60
- }
61
- },
62
- countTv: (db) => {
63
- const counts = {};
64
- for (const table of TV_LIBRARY_TABLES) {
65
- try {
66
- counts[table] =
67
- dbGet(db, `SELECT COUNT(*) AS rows FROM "${table}"`)?.rows ?? 0;
68
- }
69
- catch {
70
- counts[table] = 0;
71
- }
72
- }
73
- return counts;
74
- },
75
- clearTv: (db) => {
76
- for (const table of TV_LIBRARY_TABLES) {
77
- try {
78
- dbRun(db, `DELETE FROM "${table}"`);
79
- }
80
- catch {
81
- // ignore
82
- }
83
- }
84
- },
85
- countAsmr: (db) => {
86
- const counts = {};
87
- for (const table of ASMR_LIBRARY_TABLES) {
88
- try {
89
- counts[table] =
90
- dbGet(db, `SELECT COUNT(*) AS rows FROM "${table}"`)?.rows ?? 0;
91
- }
92
- catch {
93
- counts[table] = 0;
94
- }
95
- }
96
- return counts;
97
- },
98
- clearAsmr: (db) => {
99
- for (const table of ASMR_LIBRARY_TABLES) {
100
- try {
101
- dbRun(db, `DELETE FROM "${table}"`);
102
- }
103
- catch {
104
- // ignore
105
- }
106
- }
107
- },
40
+ countAll: (db) => countTables(db, LIBRARY_TABLES),
41
+ clearAll: (db) => clearTables(db, LIBRARY_TABLES),
42
+ countManga: (db) => countTables(db, MANGA_LIBRARY_TABLES),
43
+ clearManga: (db) => clearTables(db, MANGA_LIBRARY_TABLES),
44
+ countTv: (db) => countTables(db, TV_LIBRARY_TABLES),
45
+ clearTv: (db) => clearTables(db, TV_LIBRARY_TABLES),
46
+ countAsmr: (db) => countTables(db, ASMR_LIBRARY_TABLES),
47
+ clearAsmr: (db) => clearTables(db, ASMR_LIBRARY_TABLES),
108
48
  };