@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.
- package/README.md +1 -1
- package/client/dist/assets/{AnimeCard-URCiiy5W.js → AnimeCard-CJy1SXH3.js} +1 -1
- package/client/dist/assets/{AnimeInfo-e3z3er9t.js → AnimeInfo-BoXlVvZ-.js} +1 -1
- package/client/dist/assets/{AnimeInfoPage-B7Djlmy_.js → AnimeInfoPage-BxbA_7AV.js} +2 -2
- package/client/dist/assets/Asmr--Zc8G9zv.js +2 -0
- package/client/dist/assets/AsmrResetProgressModal-CKYm-8cp.js +1 -0
- package/client/dist/assets/AvSyncCalibrator-3yjZS7s6.js +10 -0
- package/client/dist/assets/Home-rrhOGQcg.js +1 -0
- package/client/dist/assets/Insights-Bay90Yeg.js +1 -0
- package/client/dist/assets/{LibraryListPage-B0qWqhb_.js → LibraryListPage-HzuDavbb.js} +1 -1
- package/client/dist/assets/{ListeningList-CSqD8j1C.js → ListeningList-DYcQnnsG.js} +1 -1
- package/client/dist/assets/{Manga-Cg2YuObF.js → Manga-D8CT9aO0.js} +1 -1
- package/client/dist/assets/{MangaInfoPage-DcArhEsy.js → MangaInfoPage-DGK6CYp1.js} +1 -1
- package/client/dist/assets/MangaReadPage-B9XoDKF9.js +1 -0
- package/client/dist/assets/{MangaResetProgressModal-D_yT3d1v.js → MangaResetProgressModal-QjZyIAjl.js} +1 -1
- package/client/dist/assets/Mature-CHt_-Hz5.js +1 -0
- package/client/dist/assets/MediaCard-B5584Cq9.js +1 -0
- package/client/dist/assets/Music-HeLrEZ2d.js +1 -0
- package/client/dist/assets/OptionListMenu-CJ90CdwI.js +1 -0
- package/client/dist/assets/Player-BYtEax_z.js +56 -0
- package/client/dist/assets/PlayerSettings-4-cUAY4B.js +1 -0
- package/client/dist/assets/QueueOptionsButton-Ba0dg_82.js +1 -0
- package/client/dist/assets/{QueueRail-B7f1-e6i.js → QueueRail-O7f4RqjB.js} +1 -1
- package/client/dist/assets/Radio-DPZxQnyD.js +1 -0
- package/client/dist/assets/{ReadingList-BaIem8H1.js → ReadingList-n6rr8ygy.js} +1 -1
- package/client/dist/assets/{ResetProgressModal-DES5Y-Fl.js → ResetProgressModal-CcStlbhP.js} +1 -1
- package/client/dist/assets/Search-C2wrWOdy.js +1 -0
- package/client/dist/assets/Settings-CluUYs8U.js +21 -0
- package/client/dist/assets/{SkeletonGrid-BxfgjP4i.js → SkeletonGrid-CLEPY9nP.js} +1 -1
- package/client/dist/assets/Trackers-CTGPxs0V.js +2 -0
- package/client/dist/assets/Tv-BwHlDdi8.js +4 -0
- package/client/dist/assets/Tv-ZEA45gXK.css +1 -0
- package/client/dist/assets/TvInfo-CXnrLekr.js +1 -0
- package/client/dist/assets/TvPopup-BekfiNcX.js +1 -0
- package/client/dist/assets/{TvSearch-BWDwWnHe.js → TvSearch-DuqR-z3v.js} +1 -1
- package/client/dist/assets/{TvWatchlist-D3n5_4MI.js → TvWatchlist-CBvgFrA0.js} +1 -1
- package/client/dist/assets/Watchlist-mj8mTOam.js +1 -0
- package/client/dist/assets/{hls-BO_L1WoN.js → hls-Boje3IOE.js} +1 -1
- package/client/dist/assets/index-BXwhFWB_.js +178 -0
- package/client/dist/assets/useAnimeInfoData-DJ7A4B4T.js +1 -0
- package/client/dist/assets/useAsmr-DIwIsJXF.js +1 -0
- package/client/dist/assets/{useAsmrPopup-D_o9-QlR.js → useAsmrPopup-e4zNN9ig.js} +1 -1
- package/client/dist/assets/useMangaLibrary-C9Lci5N4.js +1 -0
- package/client/dist/assets/{useMangaPopup-TFzkC-tG.js → useMangaPopup-B8nnpvyi.js} +1 -1
- package/client/dist/assets/useProviders-B5Qr4I1y.js +1 -0
- package/client/dist/assets/useTv-Dm8MCYlL.js +1 -0
- package/client/dist/assets/useTvLibrary-B7jEk2Q7.js +1 -0
- package/client/dist/assets/{vendor-query-BXRECSI6.js → vendor-query-BUTye89G.js} +1 -1
- package/client/dist/index.html +2 -2
- package/orchestrator.js +1 -2
- package/package.json +2 -6
- package/server/dist/app-auth.js +9 -29
- package/server/dist/app-hono.js +621 -0
- package/server/dist/config.js +0 -1
- package/server/dist/db/drizzle.js +32 -0
- package/server/dist/db/schema-anime.js +64 -0
- package/server/dist/db/schema-asmr.js +25 -0
- package/server/dist/db/schema-manga.js +31 -0
- package/server/dist/db/schema-tv.js +33 -0
- package/server/dist/db.js +75 -4
- package/server/dist/discord-gateway.js +1 -1
- package/server/dist/google.js +0 -7
- package/server/dist/hono/asmr.js +1 -0
- package/server/dist/hono/auth.js +56 -0
- package/server/dist/hono/music.js +419 -0
- package/server/dist/{controllers/proxy.controller.js → hono/proxy.js} +390 -394
- package/server/dist/hono/tv.js +197 -0
- package/server/dist/hono/watchlist.js +733 -0
- package/server/dist/lib/anilist.js +3 -3
- package/server/dist/lib/insights.js +185 -0
- package/server/dist/lib/ipc.js +1 -2
- package/server/dist/lib/kitsu.js +1 -28
- package/server/dist/lib/mal-import.js +291 -0
- package/server/dist/lib/migration.js +20 -34
- package/server/dist/lib/offline-db.js +36 -36
- package/server/dist/lib/tracker/manga-sync.service.js +21 -21
- package/server/dist/lib/tracker/sync.service.js +20 -20
- package/server/dist/lib/translate.js +79 -0
- package/server/dist/lib/ytmusic.js +0 -2
- package/server/dist/providers/title-matching.js +0 -1
- package/server/dist/repositories/asmr.repository.js +90 -87
- package/server/dist/repositories/insights.repository.js +24 -13
- package/server/dist/repositories/library.repository.js +33 -93
- package/server/dist/repositories/manga.repository.js +108 -116
- package/server/dist/repositories/notifications.repository.js +50 -31
- package/server/dist/repositories/queue.repository.js +36 -25
- package/server/dist/repositories/settings.repository.js +18 -8
- package/server/dist/repositories/shows-meta.repository.js +54 -41
- package/server/dist/repositories/temp-show-ids.repository.js +43 -31
- package/server/dist/repositories/tv.repository.js +98 -115
- package/server/dist/repositories/watched-episodes.repository.js +52 -20
- package/server/dist/repositories/watchlist.repository.js +36 -48
- package/server/dist/server.js +59 -151
- package/server/dist/sync.js +41 -4
- package/server/dist/trpc/context.js +7 -0
- package/server/dist/trpc/errors.js +21 -0
- package/server/dist/trpc/index.js +19 -0
- package/server/dist/trpc/router.js +49 -0
- package/server/dist/trpc/routers/asmr-library.js +122 -0
- package/server/dist/trpc/routers/asmr-progress.js +133 -0
- package/server/dist/trpc/routers/asmr.js +87 -0
- package/server/dist/trpc/routers/auth.js +247 -0
- package/server/dist/trpc/routers/continue-watching.js +83 -0
- package/server/dist/trpc/routers/data.js +1039 -0
- package/server/dist/trpc/routers/discord.js +288 -0
- package/server/dist/trpc/routers/discovery.js +15 -0
- package/server/dist/trpc/routers/insights.js +45 -0
- package/server/dist/trpc/routers/manga-library.js +280 -0
- package/server/dist/trpc/routers/manga-progress.js +181 -0
- package/server/dist/trpc/routers/manga.js +201 -0
- package/server/dist/{controllers/music.controller.js → trpc/routers/music.js} +155 -288
- package/server/dist/trpc/routers/notifications.js +80 -0
- package/server/dist/trpc/routers/progress.js +15 -0
- package/server/dist/trpc/routers/queue.js +95 -0
- package/server/dist/{routes/radio.routes.js → trpc/routers/radio.js} +30 -23
- package/server/dist/trpc/routers/settings.js +105 -0
- package/server/dist/trpc/routers/tracker.js +219 -0
- package/server/dist/trpc/routers/tv-library.js +145 -0
- package/server/dist/trpc/routers/tv-progress.js +171 -0
- package/server/dist/trpc/routers/tv.js +565 -0
- package/server/dist/trpc/routers/watchlist.js +564 -0
- package/server/dist/trpc/validation.js +159 -0
- package/server/dist/utils/security.utils.js +0 -18
- package/server/package.json +5 -10
- package/client/dist/assets/Asmr-C28TfSvX.js +0 -2
- package/client/dist/assets/AsmrResetProgressModal-6Xh71AgR.js +0 -1
- package/client/dist/assets/AvSyncCalibrator-BwCtRW0P.js +0 -10
- package/client/dist/assets/Home-BJdbHYja.js +0 -1
- package/client/dist/assets/Insights-CjRy42YE.js +0 -1
- package/client/dist/assets/MangaReadPage-YCETZRte.js +0 -1
- package/client/dist/assets/Mature-BGxRWkV5.js +0 -1
- package/client/dist/assets/MediaCard-CAzoxsRS.js +0 -1
- package/client/dist/assets/Music-Cgo-yQ7E.js +0 -1
- package/client/dist/assets/OptionListMenu--Owe9Jle.js +0 -1
- package/client/dist/assets/Player-yRXG2BrP.js +0 -56
- package/client/dist/assets/PlayerSettings-DrZLa0CC.js +0 -1
- package/client/dist/assets/QueueOptionsButton-BVZyPyKf.js +0 -1
- package/client/dist/assets/Radio-BFjxlzuF.js +0 -1
- package/client/dist/assets/Search-BOBo0Ogh.js +0 -1
- package/client/dist/assets/Settings-DOvNQ0gX.js +0 -21
- package/client/dist/assets/Trackers-DZ9KqOWv.js +0 -2
- package/client/dist/assets/Tv-5mATEU4p.js +0 -4
- package/client/dist/assets/Tv-C2SWcVzN.css +0 -1
- package/client/dist/assets/TvInfo-CF-5C5Fs.js +0 -1
- package/client/dist/assets/TvPopup-Dfqifj3s.js +0 -1
- package/client/dist/assets/Watchlist-B8MHNsAU.js +0 -1
- package/client/dist/assets/index-D2RkUJIN.js +0 -178
- package/client/dist/assets/useAnimeInfoData-BWShDHqE.js +0 -1
- package/client/dist/assets/useAsmr-FyfD9G7b.js +0 -1
- package/client/dist/assets/useMangaLibrary-BJU5x8Vz.js +0 -1
- package/client/dist/assets/useProviders-DDRxa8vl.js +0 -1
- package/client/dist/assets/useTv-Bh6WmNnZ.js +0 -1
- package/client/dist/assets/useTvLibrary-_Pqm8re1.js +0 -1
- package/server/dist/controllers/asmr-library.controller.js +0 -288
- package/server/dist/controllers/auth.controller.js +0 -225
- package/server/dist/controllers/data.controller.js +0 -767
- package/server/dist/controllers/insights.controller.js +0 -203
- package/server/dist/controllers/manga-library.controller.js +0 -350
- package/server/dist/controllers/settings.controller.js +0 -609
- package/server/dist/controllers/tv-library.controller.js +0 -314
- package/server/dist/controllers/watchlist.controller.js +0 -1069
- package/server/dist/request-logger.js +0 -16
- package/server/dist/routes/asmr-library.routes.js +0 -23
- package/server/dist/routes/asmr.routes.js +0 -81
- package/server/dist/routes/auth.routes.js +0 -25
- package/server/dist/routes/data.routes.js +0 -47
- package/server/dist/routes/discord-gateway.routes.js +0 -51
- package/server/dist/routes/insights.routes.js +0 -35
- package/server/dist/routes/lan-auth.routes.js +0 -46
- package/server/dist/routes/manga-library.routes.js +0 -25
- package/server/dist/routes/manga.routes.js +0 -141
- package/server/dist/routes/music.routes.js +0 -20
- package/server/dist/routes/providers.routes.js +0 -17
- package/server/dist/routes/proxy.routes.js +0 -12
- package/server/dist/routes/settings.routes.js +0 -24
- package/server/dist/routes/tracker.routes.js +0 -287
- package/server/dist/routes/translate.routes.js +0 -88
- package/server/dist/routes/tv-library.routes.js +0 -23
- package/server/dist/routes/tv.routes.js +0 -629
- package/server/dist/routes/watchlist.routes.js +0 -341
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
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) =>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
20
|
-
const params = [];
|
|
24
|
+
const q = sql `SELECT * FROM manga_library`;
|
|
21
25
|
if (status && status !== 'All') {
|
|
22
|
-
|
|
23
|
-
params.push(status);
|
|
26
|
+
q.append(sql ` WHERE status = ${status}`);
|
|
24
27
|
}
|
|
25
|
-
|
|
28
|
+
q.append(sql ` ORDER BY updatedAt DESC`);
|
|
26
29
|
if (limit !== undefined && offset !== undefined) {
|
|
27
|
-
|
|
28
|
-
params.push(limit, offset);
|
|
30
|
+
q.append(sql ` LIMIT ${limit} OFFSET ${offset}`);
|
|
29
31
|
}
|
|
30
|
-
return
|
|
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
|
-
|
|
37
|
-
|
|
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
|
|
40
|
-
return
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
?
|
|
70
|
-
|
|
71
|
-
source
|
|
72
|
-
id,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
81
|
-
|
|
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) =>
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
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) =>
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
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) =>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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 {
|
|
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) =>
|
|
4
|
-
getDiscoveredByShow: (db, showId) =>
|
|
5
|
-
addDiscovered: (db, showId, episodeNumber) =>
|
|
6
|
-
|
|
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
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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 {
|
|
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) =>
|
|
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) =>
|
|
18
|
-
showId
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
getByShow: (db, 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
|
|
24
|
-
return
|
|
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) =>
|
|
27
|
-
|
|
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
|
|
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
|
-
|
|
48
|
-
|
|
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) =>
|
|
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
|
|
60
|
+
return getDrizzle(db)
|
|
61
|
+
.update(queue)
|
|
62
|
+
.set({ queueOrder: index })
|
|
63
|
+
.where(eq(queue.id, item.id));
|
|
57
64
|
}
|
|
58
|
-
return
|
|
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) =>
|
|
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 {
|
|
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) =>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
9
|
-
|
|
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 {
|
|
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) =>
|
|
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
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
};
|