@serifpersia/dango 3.6.7 → 3.6.9

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 (156) hide show
  1. package/README.md +1 -1
  2. package/client/dist/assets/{AnimeCard-C3P9zWi9.js → AnimeCard-DKDJK8Mp.js} +1 -1
  3. package/client/dist/assets/{AnimeInfo-Et6687Tl.js → AnimeInfo-CTcvfjva.js} +1 -1
  4. package/client/dist/assets/{AnimeInfoPage-DX4eNnlH.js → AnimeInfoPage-B_UiacIN.js} +2 -2
  5. package/client/dist/assets/{Asmr-CCdEWrFi.js → Asmr-DMSN2vxl.js} +2 -2
  6. package/client/dist/assets/{AsmrResetProgressModal-DgEWBJOw.js → AsmrResetProgressModal-CRwfOatX.js} +1 -1
  7. package/client/dist/assets/{AvSyncCalibrator-CQ2fjf2E.js → AvSyncCalibrator-CRJsi-vz.js} +1 -1
  8. package/client/dist/assets/{Home-DUkUTTTy.js → Home-pwmK9_Eh.js} +1 -1
  9. package/client/dist/assets/{Insights-QCh4xW12.js → Insights-m7kFzg2A.js} +1 -1
  10. package/client/dist/assets/{LibraryListPage-BoTxRqHz.js → LibraryListPage-C1wpPvu6.js} +1 -1
  11. package/client/dist/assets/ListeningList-OzfEI1Ga.js +1 -0
  12. package/client/dist/assets/{Manga-HydR-L7o.js → Manga-ChRCExhK.js} +1 -1
  13. package/client/dist/assets/{MangaInfoPage-CBakEI9a.js → MangaInfoPage-DSHJPo2A.js} +1 -1
  14. package/client/dist/assets/{MangaReadPage-DwAR3iPy.js → MangaReadPage-CzOVY1wA.js} +1 -1
  15. package/client/dist/assets/{MangaResetProgressModal-DGzl8vsO.js → MangaResetProgressModal-CJfzdUBb.js} +1 -1
  16. package/client/dist/assets/{Mature-Pi0TxKuF.js → Mature-CifQMfhs.js} +1 -1
  17. package/client/dist/assets/{MediaCard--cuFxOCS.js → MediaCard-CM10yB9V.js} +1 -1
  18. package/client/dist/assets/{OptionListMenu-B3D39dGS.js → OptionListMenu-6Kovbsqw.js} +1 -1
  19. package/client/dist/assets/{Player-CsH4x_I_.js → Player-CK0hT-u4.js} +2 -2
  20. package/client/dist/assets/{PlayerSettings-DGxBsi3F.js → PlayerSettings-Bb66uqT9.js} +1 -1
  21. package/client/dist/assets/{QueueOptionsButton-Djq4Cpex.js → QueueOptionsButton-C_nTj8_q.js} +1 -1
  22. package/client/dist/assets/{QueueRail-DOrYgNVX.js → QueueRail-DUK7URAE.js} +1 -1
  23. package/client/dist/assets/{ReadingList-DwYoBrNk.js → ReadingList-Ck9LJThU.js} +1 -1
  24. package/client/dist/assets/{ResetProgressModal-CMF86mX6.js → ResetProgressModal-BUphIv0k.js} +1 -1
  25. package/client/dist/assets/{Search-DFkhuk-x.js → Search-zJyP15h6.js} +1 -1
  26. package/client/dist/assets/{Settings-DBB2r0VQ.js → Settings-BYdoSEUh.js} +1 -1
  27. package/client/dist/assets/{SkeletonGrid-DJelJgAv.js → SkeletonGrid-CqgbOhjX.js} +1 -1
  28. package/client/dist/assets/{Trackers-DGYXFhYZ.js → Trackers-BVjyv6CM.js} +1 -1
  29. package/client/dist/assets/{Tv-C5MDGD2Z.js → Tv-L13ylswR.js} +1 -1
  30. package/client/dist/assets/{TvInfo-DrKuK13u.js → TvInfo-Cvcm5U2D.js} +1 -1
  31. package/client/dist/assets/{TvPopup-DtHilRDT.js → TvPopup-DhxfKvYm.js} +1 -1
  32. package/client/dist/assets/{TvSearch-CzNxdu_w.js → TvSearch-P8bFE6_X.js} +1 -1
  33. package/client/dist/assets/{TvWatchlist-D7p4br6S.js → TvWatchlist-D5gVPjtb.js} +1 -1
  34. package/client/dist/assets/{Watchlist-DrOzswl2.js → Watchlist-Cjq8l3rU.js} +1 -1
  35. package/client/dist/assets/{hls-BGCX7mJy.js → hls-BkzuANAg.js} +1 -1
  36. package/client/dist/assets/{index-CnfZwsds.js → index-CLVgzKLk.js} +19 -19
  37. package/client/dist/assets/{useAnimeInfoData-DYXWBPF-.js → useAnimeInfoData-DZJZu-so.js} +1 -1
  38. package/client/dist/assets/{useAsmr-BbhQ59bk.js → useAsmr-CvrMGHuo.js} +1 -1
  39. package/client/dist/assets/useAsmrPopup-Bimy8Ouw.js +1 -0
  40. package/client/dist/assets/{useMangaLibrary-DuesWQaE.js → useMangaLibrary--MiN11uR.js} +1 -1
  41. package/client/dist/assets/{useMangaPopup-D57OVGUr.js → useMangaPopup-CR5XhKKS.js} +1 -1
  42. package/client/dist/assets/{useProviders-mpQb_dix.js → useProviders-DqdT4fA5.js} +1 -1
  43. package/client/dist/assets/{useTv-DGflCsSI.js → useTv-BL0HMDyP.js} +1 -1
  44. package/client/dist/assets/{useTvLibrary-BqeSmyc4.js → useTvLibrary-D4U4iqN8.js} +1 -1
  45. package/client/dist/index.html +1 -1
  46. package/package.json +3 -5
  47. package/server/dist/config.js +1 -174
  48. package/server/dist/server.js +1068 -289
  49. package/server/dist/shipped-defaults.js +1 -6
  50. package/server/package.json +4 -5
  51. package/client/dist/assets/Asmr.module-CdWlioTY.js +0 -1
  52. package/client/dist/assets/ListeningList-DguKjI_t.js +0 -1
  53. package/client/dist/assets/Music-BGH_0voi.css +0 -1
  54. package/client/dist/assets/Music-CQpyXTLH.js +0 -1
  55. package/client/dist/assets/Radio-CCHNFN7R.css +0 -1
  56. package/client/dist/assets/Radio-DF6g6odd.js +0 -1
  57. package/client/dist/assets/Radio.module-DGSZLpKM.js +0 -1
  58. package/client/dist/assets/useAsmrPopup-SymV1J-b.js +0 -1
  59. package/server/dist/app-auth.js +0 -157
  60. package/server/dist/app-hono.js +0 -598
  61. package/server/dist/db/drizzle.js +0 -16
  62. package/server/dist/db/schema-anime.js +0 -61
  63. package/server/dist/db/schema-asmr.js +0 -13
  64. package/server/dist/db/schema-manga.js +0 -31
  65. package/server/dist/db/schema-tv.js +0 -18
  66. package/server/dist/db.js +0 -217
  67. package/server/dist/discord-gateway.js +0 -385
  68. package/server/dist/discord-rpc.js +0 -413
  69. package/server/dist/github-sync.js +0 -508
  70. package/server/dist/google.js +0 -741
  71. package/server/dist/hono/asmr.js +0 -10
  72. package/server/dist/hono/auth.js +0 -49
  73. package/server/dist/hono/music.js +0 -209
  74. package/server/dist/hono/proxy.js +0 -827
  75. package/server/dist/hono/tv.js +0 -200
  76. package/server/dist/hono/watchlist.js +0 -341
  77. package/server/dist/lib/anilist.js +0 -1583
  78. package/server/dist/lib/anischedule.js +0 -191
  79. package/server/dist/lib/insights.js +0 -185
  80. package/server/dist/lib/ipc.js +0 -23
  81. package/server/dist/lib/kitsu.js +0 -517
  82. package/server/dist/lib/mal-import.js +0 -291
  83. package/server/dist/lib/mal.js +0 -957
  84. package/server/dist/lib/migration.js +0 -310
  85. package/server/dist/lib/offline-db.js +0 -395
  86. package/server/dist/lib/potoken.js +0 -135
  87. package/server/dist/lib/recommendations.service.js +0 -216
  88. package/server/dist/lib/temp-ids.js +0 -3
  89. package/server/dist/lib/tmdb.js +0 -72
  90. package/server/dist/lib/tracker/anilist-tracker.js +0 -308
  91. package/server/dist/lib/tracker/manga-sync.service.js +0 -773
  92. package/server/dist/lib/tracker/sync-state.js +0 -21
  93. package/server/dist/lib/tracker/sync.service.js +0 -476
  94. package/server/dist/lib/translate.js +0 -79
  95. package/server/dist/lib/ump.js +0 -106
  96. package/server/dist/lib/ytmusic.js +0 -162
  97. package/server/dist/logger.js +0 -16
  98. package/server/dist/providers/manga/manga.types.js +0 -1
  99. package/server/dist/providers/provider.interface.js +0 -1
  100. package/server/dist/providers/remote-loader.js +0 -614
  101. package/server/dist/providers/remote-types.js +0 -159
  102. package/server/dist/providers/title-matching.js +0 -226
  103. package/server/dist/providers/tv.types.js +0 -1
  104. package/server/dist/rclone.js +0 -120
  105. package/server/dist/repositories/asmr.repository.js +0 -77
  106. package/server/dist/repositories/insights.repository.js +0 -106
  107. package/server/dist/repositories/library.repository.js +0 -94
  108. package/server/dist/repositories/mal-cache.repository.js +0 -52
  109. package/server/dist/repositories/manga.repository.js +0 -95
  110. package/server/dist/repositories/notifications.repository.js +0 -60
  111. package/server/dist/repositories/queue.repository.js +0 -72
  112. package/server/dist/repositories/settings.repository.js +0 -30
  113. package/server/dist/repositories/shows-meta.repository.js +0 -58
  114. package/server/dist/repositories/temp-show-ids.repository.js +0 -63
  115. package/server/dist/repositories/tv.repository.js +0 -93
  116. package/server/dist/repositories/watched-episodes.repository.js +0 -82
  117. package/server/dist/repositories/watchlist.repository.js +0 -60
  118. package/server/dist/scripts/backfill-episode-duration.js +0 -42
  119. package/server/dist/sync-payload.js +0 -91
  120. package/server/dist/sync.js +0 -657
  121. package/server/dist/trpc/context.js +0 -7
  122. package/server/dist/trpc/errors.js +0 -15
  123. package/server/dist/trpc/index.js +0 -18
  124. package/server/dist/trpc/router.js +0 -49
  125. package/server/dist/trpc/routers/asmr-library.js +0 -112
  126. package/server/dist/trpc/routers/asmr-progress.js +0 -121
  127. package/server/dist/trpc/routers/asmr.js +0 -87
  128. package/server/dist/trpc/routers/auth.js +0 -194
  129. package/server/dist/trpc/routers/continue-watching.js +0 -81
  130. package/server/dist/trpc/routers/data.js +0 -1026
  131. package/server/dist/trpc/routers/discord.js +0 -285
  132. package/server/dist/trpc/routers/discovery.js +0 -11
  133. package/server/dist/trpc/routers/insights.js +0 -14
  134. package/server/dist/trpc/routers/manga-library.js +0 -263
  135. package/server/dist/trpc/routers/manga-progress.js +0 -169
  136. package/server/dist/trpc/routers/manga.js +0 -201
  137. package/server/dist/trpc/routers/music.js +0 -655
  138. package/server/dist/trpc/routers/notifications.js +0 -80
  139. package/server/dist/trpc/routers/progress.js +0 -109
  140. package/server/dist/trpc/routers/queue.js +0 -93
  141. package/server/dist/trpc/routers/radio.js +0 -145
  142. package/server/dist/trpc/routers/settings.js +0 -96
  143. package/server/dist/trpc/routers/tracker.js +0 -213
  144. package/server/dist/trpc/routers/tv-library.js +0 -135
  145. package/server/dist/trpc/routers/tv-progress.js +0 -159
  146. package/server/dist/trpc/routers/tv.js +0 -551
  147. package/server/dist/trpc/routers/watchlist.js +0 -550
  148. package/server/dist/trpc/validation.js +0 -258
  149. package/server/dist/utils/cache.utils.js +0 -25
  150. package/server/dist/utils/cookie.utils.js +0 -13
  151. package/server/dist/utils/db-utils.js +0 -17
  152. package/server/dist/utils/env.utils.js +0 -77
  153. package/server/dist/utils/http.utils.js +0 -82
  154. package/server/dist/utils/request-context.js +0 -2
  155. package/server/dist/utils/security.utils.js +0 -76
  156. /package/client/dist/assets/{Asmr-D1KuQwQy.css → useAsmrPopup-D1KuQwQy.css} +0 -0
@@ -1,18 +0,0 @@
1
- import { initTRPC } from '@trpc/server';
2
- import { lanAuthRequiredError } from './errors.js';
3
- const t = initTRPC.context().create({
4
- errorFormatter: ({ shape, error }) => ({
5
- ...shape,
6
- data: {
7
- ...shape.data,
8
- cause: error.cause ?? null,
9
- },
10
- }),
11
- });
12
- export const router = t.router;
13
- const lanGuard = t.middleware(({ ctx, next }) => {
14
- if (!ctx.lanAuthed)
15
- throw lanAuthRequiredError();
16
- return next({ ctx });
17
- });
18
- export const protectedProcedure = t.procedure.use(lanGuard);
@@ -1,49 +0,0 @@
1
- import { router } from './index.js';
2
- import { settingsRouter } from './routers/settings.js';
3
- import { queueRouter } from './routers/queue.js';
4
- import { watchlistRouter } from './routers/watchlist.js';
5
- import { progressRouter } from './routers/progress.js';
6
- import { notificationsRouter } from './routers/notifications.js';
7
- import { continueWatchingRouter } from './routers/continue-watching.js';
8
- import { discoveryRouter } from './routers/discovery.js';
9
- import { mangaLibraryRouter } from './routers/manga-library.js';
10
- import { mangaProgressRouter } from './routers/manga-progress.js';
11
- import { asmrLibraryRouter } from './routers/asmr-library.js';
12
- import { asmrProgressRouter } from './routers/asmr-progress.js';
13
- import { tvLibraryRouter } from './routers/tv-library.js';
14
- import { tvProgressRouter } from './routers/tv-progress.js';
15
- import { tvRouter } from './routers/tv.js';
16
- import { mangaRouter } from './routers/manga.js';
17
- import { asmrRouter } from './routers/asmr.js';
18
- import { musicRouter } from './routers/music.js';
19
- import { radioRouter } from './routers/radio.js';
20
- import { trackerRouter } from './routers/tracker.js';
21
- import { insightsRouter } from './routers/insights.js';
22
- import { authRouter } from './routers/auth.js';
23
- import { dataRouter } from './routers/data.js';
24
- import { discordRouter } from './routers/discord.js';
25
- export const appRouter = router({
26
- settings: settingsRouter,
27
- queue: queueRouter,
28
- watchlist: watchlistRouter,
29
- progress: progressRouter,
30
- notifications: notificationsRouter,
31
- continueWatching: continueWatchingRouter,
32
- discovery: discoveryRouter,
33
- mangaLibrary: mangaLibraryRouter,
34
- mangaProgress: mangaProgressRouter,
35
- asmrLibrary: asmrLibraryRouter,
36
- asmrProgress: asmrProgressRouter,
37
- tvLibrary: tvLibraryRouter,
38
- tvProgress: tvProgressRouter,
39
- tv: tvRouter,
40
- manga: mangaRouter,
41
- asmr: asmrRouter,
42
- music: musicRouter,
43
- radio: radioRouter,
44
- tracker: trackerRouter,
45
- insights: insightsRouter,
46
- auth: authRouter,
47
- data: dataRouter,
48
- discord: discordRouter,
49
- });
@@ -1,112 +0,0 @@
1
- import { TRPCError } from '@trpc/server';
2
- import { protectedProcedure, router } from '../index.js';
3
- import { performAsmrWriteTransactionAsync } from '../../sync.js';
4
- import { buildAsmrId, ASMR_STATUSES, AsmrLibraryRepository, AsmrProgressRepository, } from '../../repositories/asmr.repository.js';
5
- import { defineSchema, reqObj, reqStr, optStr, reqStrArray, readPageLimit } from '../validation.js';
6
- const asmrLibraryListInput = () => defineSchema((value) => {
7
- const obj = reqObj(value);
8
- const out = { ...readPageLimit(obj) };
9
- const status = optStr(obj, 'status');
10
- if (status !== undefined)
11
- out.status = status;
12
- return out;
13
- });
14
- const asmrLibraryIdInput = () => defineSchema((value) => ({
15
- id: reqStr(reqObj(value), 'id'),
16
- }));
17
- const asmrLibraryAddInput = () => defineSchema((value) => {
18
- const obj = reqObj(value);
19
- return {
20
- rjCode: optStr(obj, 'rjCode'),
21
- id: optStr(obj, 'id'),
22
- title: optStr(obj, 'title'),
23
- thumbnail: optStr(obj, 'thumbnail'),
24
- status: optStr(obj, 'status'),
25
- isAdult: obj.isAdult,
26
- };
27
- });
28
- const asmrLibraryStatusInput = () => defineSchema((value) => {
29
- const obj = reqObj(value);
30
- return { id: reqStr(obj, 'id'), status: reqStr(obj, 'status') };
31
- });
32
- const asmrLibraryBatchStatusInput = () => defineSchema((value) => {
33
- const obj = reqObj(value);
34
- return { ids: reqStrArray(obj, 'ids', true), status: reqStr(obj, 'status') };
35
- });
36
- const asmrLibraryIdsInput = () => defineSchema((value) => ({
37
- ids: reqStrArray(reqObj(value), 'ids', true),
38
- }));
39
- export const asmrLibraryRouter = router({
40
- list: protectedProcedure.input(asmrLibraryListInput()).query(async ({ ctx, input }) => {
41
- const page = Math.max(parseInt(String(input.page ?? 1)) || 1, 1);
42
- const limit = Math.min(Math.max(parseInt(String(input.limit ?? 24)) || 24, 1), 100);
43
- const offset = (page - 1) * limit;
44
- const rows = await AsmrLibraryRepository.getAll(ctx.asmrDb, input.status, limit, offset);
45
- const total = await AsmrLibraryRepository.getCount(ctx.asmrDb, input.status);
46
- return { data: rows, total, page, limit };
47
- }),
48
- ids: protectedProcedure.query(async ({ ctx }) => {
49
- return { ids: await AsmrLibraryRepository.getIds(ctx.asmrDb) };
50
- }),
51
- check: protectedProcedure.input(asmrLibraryIdInput()).query(async ({ ctx, input }) => {
52
- const item = await AsmrLibraryRepository.getById(ctx.asmrDb, input.id);
53
- return { inLibrary: !!item, status: item?.status ?? null };
54
- }),
55
- add: protectedProcedure.input(asmrLibraryAddInput()).mutation(async ({ ctx, input }) => {
56
- const rawCode = String(input.rjCode || input.id || '').trim();
57
- if (!rawCode) {
58
- throw new TRPCError({ code: 'BAD_REQUEST', message: 'rjCode is required' });
59
- }
60
- const id = input.id && input.id.trim() ? buildAsmrId(input.id) : buildAsmrId(rawCode);
61
- const cleanStatus = typeof input.status === 'string' && ASMR_STATUSES.includes(input.status)
62
- ? input.status
63
- : 'Listening';
64
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
65
- await AsmrLibraryRepository.upsert(tx, {
66
- id,
67
- rjCode: buildAsmrId(rawCode),
68
- title: String(input.title || ''),
69
- thumbnail: String(input.thumbnail || ''),
70
- status: cleanStatus,
71
- isAdult: input.isAdult === true || input.isAdult === 1,
72
- });
73
- });
74
- return { success: true, id };
75
- }),
76
- remove: protectedProcedure.input(asmrLibraryIdInput()).mutation(async ({ ctx, input }) => {
77
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
78
- await AsmrLibraryRepository.delete(tx, String(input.id));
79
- await AsmrProgressRepository.deleteByWork(tx, String(input.id));
80
- });
81
- return { success: true };
82
- }),
83
- setStatus: protectedProcedure.input(asmrLibraryStatusInput()).mutation(async ({ ctx, input }) => {
84
- if (!input.id || !ASMR_STATUSES.includes(input.status)) {
85
- throw new TRPCError({ code: 'BAD_REQUEST', message: 'id and a valid status are required' });
86
- }
87
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
88
- await AsmrLibraryRepository.updateStatus(tx, String(input.id), String(input.status));
89
- });
90
- return { success: true };
91
- }),
92
- batchStatus: protectedProcedure
93
- .input(asmrLibraryBatchStatusInput())
94
- .mutation(async ({ ctx, input }) => {
95
- if (!input.status || !ASMR_STATUSES.includes(input.status)) {
96
- throw new TRPCError({ code: 'BAD_REQUEST', message: 'a valid status is required' });
97
- }
98
- const ids = input.ids.map((id) => String(id));
99
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
100
- await AsmrLibraryRepository.updateStatusMany(tx, ids, String(input.status));
101
- });
102
- return { success: true, updated: ids.length };
103
- }),
104
- removeMany: protectedProcedure.input(asmrLibraryIdsInput()).mutation(async ({ ctx, input }) => {
105
- const ids = input.ids.map((id) => String(id));
106
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
107
- await AsmrLibraryRepository.deleteMany(tx, ids);
108
- await AsmrProgressRepository.deleteMany(tx, ids);
109
- });
110
- return { success: true, removed: ids.length };
111
- }),
112
- });
@@ -1,121 +0,0 @@
1
- import { protectedProcedure, router } from '../index.js';
2
- import { performAsmrWriteTransactionAsync } from '../../sync.js';
3
- import { adultNonListIds } from '../../utils/db-utils.js';
4
- import { SettingsRepository } from '../../repositories/settings.repository.js';
5
- import { AsmrLibraryRepository, AsmrProgressRepository, } from '../../repositories/asmr.repository.js';
6
- import { defineSchema, reqObj, reqStr, reqStrArray, strOrNull } from '../validation.js';
7
- const getAdultNonListWorkIds = (db) => adultNonListIds(db, 'asmr_progress', 'asmr_library', 'workId', 'COALESCE(l.isAdult, p.isAdult) = 1');
8
- const asmrProgressIdsInput = () => defineSchema((value) => ({
9
- ids: reqStrArray(reqObj(value), 'ids', true),
10
- }));
11
- const asmrProgressWorkInput = () => defineSchema((value) => ({
12
- workId: reqStr(reqObj(value), 'workId'),
13
- }));
14
- const asmrProgressSaveInput = () => defineSchema((value) => {
15
- const obj = reqObj(value);
16
- return {
17
- workId: reqStr(obj, 'workId'),
18
- trackIndex: obj.trackIndex,
19
- trackLabel: obj.trackLabel,
20
- currentTime: obj.currentTime,
21
- duration: obj.duration,
22
- title: strOrNull(obj, 'title'),
23
- thumbnail: strOrNull(obj, 'thumbnail'),
24
- rjCode: strOrNull(obj, 'rjCode'),
25
- isAdult: obj.isAdult ?? null,
26
- };
27
- });
28
- const asmrProgressRemoveInput = () => defineSchema((value) => {
29
- const obj = reqObj(value);
30
- return { workId: reqStr(obj, 'workId'), trackIndex: obj.trackIndex ?? undefined };
31
- });
32
- const asmrContinueListeningInput = () => defineSchema((value) => {
33
- const obj = reqObj(value);
34
- const out = {};
35
- if (obj.limit !== undefined && obj.limit !== null && obj.limit !== '') {
36
- const limit = Number(obj.limit);
37
- if (Number.isFinite(limit))
38
- out.limit = limit;
39
- }
40
- return out;
41
- });
42
- export const asmrProgressRouter = router({
43
- getByWork: protectedProcedure.input(asmrProgressWorkInput()).query(async ({ ctx, input }) => {
44
- const rows = await AsmrProgressRepository.getByWork(ctx.asmrDb, input.workId);
45
- return { progress: rows };
46
- }),
47
- latest: protectedProcedure.input(asmrProgressWorkInput()).query(async ({ ctx, input }) => {
48
- const row = await AsmrProgressRepository.getLatest(ctx.asmrDb, input.workId);
49
- return row || { currentTime: 0, duration: 0 };
50
- }),
51
- save: protectedProcedure.input(asmrProgressSaveInput()).mutation(async ({ ctx, input }) => {
52
- const trackIndexNum = Math.max(Math.round(Number(input.trackIndex)) || 0, 0);
53
- const timeNum = Math.max(Number(input.currentTime) || 0, 0);
54
- const durationNum = Math.max(Number(input.duration) || 0, 0);
55
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
56
- await AsmrProgressRepository.upsert(tx, {
57
- workId: String(input.workId),
58
- trackIndex: trackIndexNum,
59
- trackLabel: String(input.trackLabel || ''),
60
- currentTime: timeNum,
61
- duration: durationNum,
62
- title: input.title ?? null,
63
- thumbnail: input.thumbnail ?? null,
64
- rjCode: input.rjCode ?? null,
65
- isAdult: input.isAdult != null ? Number(input.isAdult) : null,
66
- });
67
- await AsmrLibraryRepository.touchProgress(tx, String(input.workId), {
68
- trackIndex: trackIndexNum,
69
- trackLabel: String(input.trackLabel || ''),
70
- position: timeNum,
71
- });
72
- });
73
- return { success: true };
74
- }),
75
- remove: protectedProcedure.input(asmrProgressRemoveInput()).mutation(async ({ ctx, input }) => {
76
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
77
- if (input.trackIndex !== undefined && input.trackIndex !== null) {
78
- await AsmrProgressRepository.deleteTrack(tx, String(input.workId), Number(input.trackIndex));
79
- }
80
- else {
81
- await AsmrProgressRepository.deleteByWork(tx, String(input.workId));
82
- }
83
- });
84
- return { success: true };
85
- }),
86
- removeMany: protectedProcedure.input(asmrProgressIdsInput()).mutation(async ({ ctx, input }) => {
87
- const ids = input.ids.map((id) => String(id));
88
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
89
- await AsmrProgressRepository.deleteMany(tx, ids);
90
- });
91
- return { success: true, removed: ids.length };
92
- }),
93
- continueListening: protectedProcedure
94
- .input(asmrContinueListeningInput())
95
- .query(async ({ ctx, input }) => {
96
- const limit = Math.min(Math.max(parseInt(String(input.limit ?? 24)) || 24, 1), 100);
97
- const { ignoreAdult, listOnly } = await SettingsRepository.readContinueFlags(ctx.db, 'asmrIgnoreAdultContent', 'asmrCwWatchlistOnly');
98
- const rows = (await AsmrProgressRepository.getContinueListening(ctx.asmrDb, limit)).filter((row) => {
99
- if (ignoreAdult && row.isAdult === 1)
100
- return false;
101
- if (listOnly && row.watchlistStatus !== 'Listening')
102
- return false;
103
- return true;
104
- });
105
- return { data: rows, total: rows.length };
106
- }),
107
- adultCount: protectedProcedure.query(async ({ ctx }) => {
108
- const ids = await getAdultNonListWorkIds(ctx.asmrDb);
109
- return { count: ids.length };
110
- }),
111
- purgeAdult: protectedProcedure.mutation(async ({ ctx }) => {
112
- const ids = await getAdultNonListWorkIds(ctx.asmrDb);
113
- if (ids.length > 0) {
114
- await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
115
- for (const id of ids)
116
- await AsmrProgressRepository.deleteByWork(tx, id);
117
- });
118
- }
119
- return { success: true, removed: ids.length };
120
- }),
121
- });
@@ -1,87 +0,0 @@
1
- import { protectedProcedure, router } from '../index.js';
2
- import { defineSchema, optStr, reqObj, reqStr } from '../validation.js';
3
- import { authRequiredError } from '../errors.js';
4
- import logger from '../../logger.js';
5
- async function cached(ctx, key, ttl, produce) {
6
- const hit = ctx.apiCache.get(key);
7
- if (hit)
8
- return hit;
9
- const body = await produce();
10
- if (ttl !== undefined)
11
- ctx.apiCache.set(key, body, ttl);
12
- else
13
- ctx.apiCache.set(key, body);
14
- return body;
15
- }
16
- const asmrBrowseInput = () => defineSchema((value) => {
17
- if (value === undefined || value === null)
18
- return {};
19
- const obj = reqObj(value);
20
- const out = {};
21
- const q = optStr(obj, 'q');
22
- if (q !== undefined)
23
- out.q = q;
24
- if (obj.page !== undefined)
25
- out.page = parseInt(String(obj.page), 10) || 1;
26
- const sort = optStr(obj, 'sort');
27
- if (sort !== undefined)
28
- out.sort = sort;
29
- const rating = optStr(obj, 'rating');
30
- if (rating !== undefined)
31
- out.rating = rating;
32
- return out;
33
- });
34
- const asmrWorkInput = () => defineSchema((value) => ({ rj: reqStr(reqObj(value), 'rj') }));
35
- export const asmrRouter = router({
36
- browse: protectedProcedure.input(asmrBrowseInput()).query(async ({ ctx, input }) => {
37
- const q = input.q || '';
38
- const page = input.page || 1;
39
- const sort = input.sort || '';
40
- const rating = input.rating || '';
41
- return cached(ctx, `route-asmr-browse-${q}-${page}-${sort}-${rating}-${sort === 'random' ? Date.now() : ''}`, 300, async () => {
42
- try {
43
- const provider = ctx.getJasmr();
44
- if (!provider)
45
- return { shows: [], hasNext: false };
46
- return await provider.browse({ query: q, page, sort, rating });
47
- }
48
- catch (err) {
49
- if (err.message === 'AUTH_REQUIRED')
50
- throw authRequiredError('jasmr');
51
- logger.error({ err }, '[Asmr] browse failed');
52
- return { shows: [], hasNext: false };
53
- }
54
- });
55
- }),
56
- work: protectedProcedure.input(asmrWorkInput()).query(async ({ ctx, input }) => {
57
- const rj = input.rj;
58
- return cached(ctx, `route-asmr-work-${rj}`, 1800, async () => {
59
- try {
60
- const provider = ctx.getJasmr();
61
- if (!provider) {
62
- return { rjCode: rj, description: '', tracks: [], images: [], chapters: [] };
63
- }
64
- const rjCode = String(rj).trim().toUpperCase();
65
- const episodes = await provider.getEpisodes(rjCode);
66
- const [streams, images, chapters] = await Promise.all([
67
- provider.getStreamUrls(rjCode, '1'),
68
- provider.getImages(rjCode),
69
- provider.getChapters(rjCode),
70
- ]);
71
- return {
72
- rjCode,
73
- description: episodes?.description || '',
74
- tracks: streams?.[0]?.links || [],
75
- images,
76
- chapters,
77
- };
78
- }
79
- catch (err) {
80
- if (err.message === 'AUTH_REQUIRED')
81
- throw authRequiredError('jasmr');
82
- logger.error({ err, rj }, '[Asmr] work fetch failed');
83
- return { rjCode: rj, description: '', tracks: [], images: [], chapters: [] };
84
- }
85
- });
86
- }),
87
- });
@@ -1,194 +0,0 @@
1
- import logger from '../../logger.js';
2
- import { TRPCError } from '@trpc/server';
3
- import { googleDriveService } from '../../google.js';
4
- import { githubSyncService } from '../../github-sync.js';
5
- import { initSyncProvider, getActiveProvider, runFullSyncSequence } from '../../sync.js';
6
- import { CONFIG } from '../../config.js';
7
- import { rcloneService } from '../../rclone.js';
8
- import { updateEnvFile } from '../../utils/env.utils.js';
9
- import { protectedProcedure, router } from '../index.js';
10
- import { defineSchema, reqObj } from '../validation.js';
11
- async function runSyncFromCtx(ctx, database, preferredProvider) {
12
- const remoteFolder = CONFIG.IS_DEV ? CONFIG.REMOTE_FOLDER_DEV : CONFIG.REMOTE_FOLDER_PROD;
13
- await runFullSyncSequence({ db: database, mangaDb: ctx.mangaDb, tvDb: ctx.tvDb, asmrDb: ctx.asmrDb }, { remoteFolder, preferredProvider });
14
- }
15
- const googleAuthSaveInput = () => defineSchema((value) => {
16
- const obj = reqObj(value);
17
- return { clientId: obj.clientId, clientSecret: obj.clientSecret, workerUrl: obj.workerUrl };
18
- });
19
- const githubAuthSaveInput = () => defineSchema((value) => {
20
- const obj = reqObj(value);
21
- if (typeof obj.clientId !== 'string')
22
- throw new Error('clientId required');
23
- return { clientId: obj.clientId };
24
- });
25
- const syncProviderInput = () => defineSchema((value) => ({
26
- provider: reqObj(value).provider,
27
- }));
28
- const rcloneSaveInput = () => defineSchema((value) => ({
29
- remote: reqObj(value).remote,
30
- }));
31
- const serverPortInput = () => defineSchema((value) => ({
32
- port: reqObj(value).port,
33
- }));
34
- export const authRouter = router({
35
- configStatus: protectedProcedure.query(async () => {
36
- const useWorker = !!CONFIG.GOOGLE_AUTH_WORKER_URL;
37
- const hasLegacyConfig = !!process.env.GOOGLE_CLIENT_ID && !!process.env.GOOGLE_CLIENT_SECRET;
38
- return { hasConfig: useWorker || hasLegacyConfig, useWorker };
39
- }),
40
- googleAuth: protectedProcedure.query(async () => {
41
- return {
42
- useWorker: !!CONFIG.GOOGLE_AUTH_WORKER_URL,
43
- hasCustomWorkerUrl: !!process.env.GOOGLE_AUTH_WORKER_URL,
44
- hasCustomClientId: !!process.env.GOOGLE_CLIENT_ID,
45
- hasClientSecret: !!process.env.GOOGLE_CLIENT_SECRET,
46
- };
47
- }),
48
- saveGoogleAuth: protectedProcedure.input(googleAuthSaveInput()).mutation(async ({ input }) => {
49
- const { clientId, clientSecret, workerUrl } = input;
50
- const updates = {};
51
- if (typeof clientId === 'string') {
52
- updates.GOOGLE_CLIENT_ID = clientId;
53
- }
54
- if (typeof clientSecret === 'string') {
55
- updates.GOOGLE_CLIENT_SECRET = clientSecret;
56
- }
57
- if (typeof workerUrl === 'string') {
58
- updates.GOOGLE_AUTH_WORKER_URL = workerUrl;
59
- }
60
- await updateEnvFile(updates);
61
- if (typeof workerUrl === 'string') {
62
- ;
63
- CONFIG.GOOGLE_AUTH_WORKER_URL = workerUrl;
64
- }
65
- if (typeof clientId === 'string') {
66
- ;
67
- CONFIG.GOOGLE_CLIENT_ID = clientId || undefined;
68
- }
69
- if (typeof clientSecret === 'string') {
70
- ;
71
- CONFIG.GOOGLE_CLIENT_SECRET =
72
- clientSecret || undefined;
73
- }
74
- await initSyncProvider();
75
- return { success: true };
76
- }),
77
- githubAuth: protectedProcedure.query(async () => {
78
- return { hasCustomClientId: !!process.env.GITHUB_CLIENT_ID };
79
- }),
80
- saveGithubAuth: protectedProcedure.input(githubAuthSaveInput()).mutation(async ({ input }) => {
81
- await updateEnvFile({ GITHUB_CLIENT_ID: input.clientId });
82
- return { success: true };
83
- }),
84
- rcloneSettings: protectedProcedure.query(async () => {
85
- const remotes = await rcloneService.listRemotes();
86
- return {
87
- remote: CONFIG.RCLONE_REMOTE || '',
88
- availableRemotes: remotes,
89
- activeRemote: rcloneService.isActive() ? rcloneService.getRemoteName() : null,
90
- };
91
- }),
92
- syncSettings: protectedProcedure.query(async () => {
93
- return {
94
- activeProvider: process.env.SYNC_PROVIDER || 'default',
95
- actualActiveProvider: getActiveProvider(),
96
- authenticatedProviders: {
97
- github: githubSyncService.isAuthenticated(),
98
- google: googleDriveService.isAuthenticated(),
99
- rclone: rcloneService.isActive(),
100
- },
101
- };
102
- }),
103
- saveSyncProvider: protectedProcedure.input(syncProviderInput()).mutation(async ({ input }) => {
104
- const { provider } = input;
105
- const value = provider === 'default' ? '' : provider;
106
- await updateEnvFile({ SYNC_PROVIDER: value });
107
- await initSyncProvider();
108
- return { success: true, activeProvider: process.env.SYNC_PROVIDER || 'default' };
109
- }),
110
- githubStatus: protectedProcedure.query(async () => {
111
- try {
112
- const user = await githubSyncService.getUserProfile();
113
- return {
114
- authenticated: !!user,
115
- user,
116
- device: githubSyncService.getDeviceState(),
117
- hasCustomClientId: !!process.env.GITHUB_CLIENT_ID,
118
- };
119
- }
120
- catch (error) {
121
- logger.error({ err: error }, 'Failed to fetch GitHub auth status');
122
- return {
123
- authenticated: false,
124
- user: null,
125
- device: githubSyncService.getDeviceState(),
126
- hasCustomClientId: !!process.env.GITHUB_CLIENT_ID,
127
- };
128
- }
129
- }),
130
- githubStart: protectedProcedure.mutation(async ({ ctx }) => {
131
- const state = await githubSyncService.startDeviceAuth(ctx.db, (db, provider) => runSyncFromCtx(ctx, db, provider));
132
- return state;
133
- }),
134
- githubPoll: protectedProcedure.query(async () => {
135
- return githubSyncService.getDeviceState();
136
- }),
137
- githubLogout: protectedProcedure.mutation(async () => {
138
- await githubSyncService.logout();
139
- await updateEnvFile({ SYNC_PROVIDER: '' });
140
- await initSyncProvider();
141
- return { success: true };
142
- }),
143
- saveRclone: protectedProcedure.input(rcloneSaveInput()).mutation(async ({ ctx, input }) => {
144
- const { remote } = input;
145
- await updateEnvFile({
146
- RCLONE_REMOTE: remote,
147
- SYNC_PROVIDER: 'rclone',
148
- });
149
- await runSyncFromCtx(ctx, ctx.db, 'rclone');
150
- return { success: true };
151
- }),
152
- setServerPort: protectedProcedure.input(serverPortInput()).mutation(async ({ input }) => {
153
- const port = Number(input.port);
154
- if (!Number.isInteger(port) || port < 1 || port > 65535) {
155
- throw new TRPCError({
156
- code: 'BAD_REQUEST',
157
- message: 'Port must be an integer between 1 and 65535',
158
- });
159
- }
160
- await updateEnvFile({ PORT: String(port) });
161
- logger.info({ port }, 'Server port saved. Restart required to apply.');
162
- return { success: true, port };
163
- }),
164
- googleAuthUrl: protectedProcedure.query(async () => {
165
- const url = await googleDriveService.getAuthUrl();
166
- return { url };
167
- }),
168
- googleLogin: protectedProcedure.mutation(async ({ ctx }) => {
169
- if (googleDriveService.isAuthenticated()) {
170
- const user = await googleDriveService.getUserProfile();
171
- if (user) {
172
- await updateEnvFile({ SYNC_PROVIDER: 'google' });
173
- await runSyncFromCtx(ctx, ctx.db, 'google');
174
- return { url: null, authenticated: true };
175
- }
176
- else {
177
- logger.warn('Google tokens found but invalid. Clearing and requesting new auth.');
178
- await googleDriveService.logout();
179
- }
180
- }
181
- const url = await googleDriveService.getAuthUrl();
182
- return { url, authenticated: false };
183
- }),
184
- user: protectedProcedure.query(async () => {
185
- const user = await googleDriveService.getUserProfile();
186
- return user;
187
- }),
188
- logout: protectedProcedure.mutation(async () => {
189
- await googleDriveService.logout();
190
- await updateEnvFile({ SYNC_PROVIDER: '' });
191
- await initSyncProvider();
192
- return { success: true };
193
- }),
194
- });
@@ -1,81 +0,0 @@
1
- import { protectedProcedure, router } from '../index.js';
2
- import { performWriteTransactionAsync } from '../../sync.js';
3
- import { WatchedEpisodesRepository } from '../../repositories/watched-episodes.repository.js';
4
- import { NotificationsRepository } from '../../repositories/notifications.repository.js';
5
- import { getMigratedId } from '../../lib/migration.js';
6
- import { getAdultNonWatchlistShowIds } from '../../hono/watchlist.js';
7
- import { dbAll } from '../../utils/db-utils.js';
8
- import { idsInput, showIdInput } from '../validation.js';
9
- export const continueWatchingRouter = router({
10
- remove: protectedProcedure.input(showIdInput()).mutation(async ({ ctx, input }) => {
11
- const showId = await getMigratedId(ctx.db, input.showId);
12
- await performWriteTransactionAsync(ctx.db, async (tx) => {
13
- await WatchedEpisodesRepository.deleteByShow(tx, showId);
14
- await NotificationsRepository.deleteByShow(tx, showId);
15
- });
16
- return { success: true };
17
- }),
18
- removeMany: protectedProcedure.input(idsInput()).mutation(async ({ ctx, input }) => {
19
- const ids = await Promise.all(input.ids.map((id) => getMigratedId(ctx.db, id)));
20
- await performWriteTransactionAsync(ctx.db, async (tx) => {
21
- for (const id of ids) {
22
- await WatchedEpisodesRepository.deleteByShow(tx, id);
23
- await NotificationsRepository.deleteByShow(tx, id);
24
- }
25
- });
26
- return { success: true, removed: ids.length };
27
- }),
28
- adultCount: protectedProcedure.query(async ({ ctx }) => {
29
- const ids = await getAdultNonWatchlistShowIds(ctx.db);
30
- return { count: ids.length };
31
- }),
32
- purgeAdult: protectedProcedure.mutation(async ({ ctx }) => {
33
- const ids = await getAdultNonWatchlistShowIds(ctx.db);
34
- if (ids.length > 0) {
35
- await performWriteTransactionAsync(ctx.db, async (tx) => {
36
- for (const id of ids) {
37
- await WatchedEpisodesRepository.deleteByShow(tx, id);
38
- await NotificationsRepository.deleteByShow(tx, id);
39
- }
40
- });
41
- }
42
- return { success: true, removed: ids.length };
43
- }),
44
- thisWeek: protectedProcedure.query(async ({ ctx }) => {
45
- const rows = await dbAll(ctx.db, `SELECT
46
- w.id, w.name, w.thumbnail, w.nativeName, w.englishName, w.type,
47
- dn.episodeNumber, dn.discoveredAt
48
- FROM discovered_notifications dn
49
- JOIN watchlist w ON dn.showId = w.id
50
- WHERE w.status = 'Watching'
51
- AND EXISTS (
52
- SELECT 1 FROM discovered_notifications dn_recent
53
- WHERE dn_recent.showId = dn.showId
54
- AND dn_recent.discoveredAt >= datetime('now', '-7 days')
55
- )
56
- AND CAST(dn.episodeNumber AS INTEGER) = (
57
- SELECT MIN(CAST(dn2.episodeNumber AS INTEGER))
58
- FROM discovered_notifications dn2
59
- WHERE dn2.showId = dn.showId
60
- AND NOT EXISTS (
61
- SELECT 1 FROM watched_episodes we2
62
- WHERE we2.showId = dn2.showId AND we2.episodeNumber = dn2.episodeNumber
63
- )
64
- )
65
- AND NOT EXISTS (
66
- SELECT 1 FROM watched_episodes we
67
- WHERE we.showId = dn.showId AND we.episodeNumber = dn.episodeNumber
68
- )
69
- ORDER BY CAST(dn.episodeNumber AS INTEGER) ASC`);
70
- return rows.map((row) => ({
71
- _id: row.id,
72
- id: row.id,
73
- name: row.name,
74
- thumbnail: row.thumbnail || '',
75
- nativeName: row.nativeName,
76
- englishName: row.englishName,
77
- type: row.type,
78
- episodeNumber: parseInt(row.episodeNumber) || row.episodeNumber,
79
- }));
80
- }),
81
- });