@serifpersia/dango 3.6.7 → 3.6.8

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 (149) hide show
  1. package/README.md +1 -1
  2. package/client/dist/assets/{AnimeCard-C3P9zWi9.js → AnimeCard-DraA_TF9.js} +1 -1
  3. package/client/dist/assets/{AnimeInfo-Et6687Tl.js → AnimeInfo-BJ7pzs-P.js} +1 -1
  4. package/client/dist/assets/{AnimeInfoPage-DX4eNnlH.js → AnimeInfoPage-B_esUr5C.js} +2 -2
  5. package/client/dist/assets/{Asmr-CCdEWrFi.js → Asmr-Ckuh2xfW.js} +1 -1
  6. package/client/dist/assets/{AsmrResetProgressModal-DgEWBJOw.js → AsmrResetProgressModal-BS6UFJN4.js} +1 -1
  7. package/client/dist/assets/{AvSyncCalibrator-CQ2fjf2E.js → AvSyncCalibrator-DwHCd9yB.js} +1 -1
  8. package/client/dist/assets/{Home-DUkUTTTy.js → Home-DuM1EuvN.js} +1 -1
  9. package/client/dist/assets/{Insights-QCh4xW12.js → Insights-D3Pd9c9V.js} +1 -1
  10. package/client/dist/assets/{LibraryListPage-BoTxRqHz.js → LibraryListPage-Dm2yVn0-.js} +1 -1
  11. package/client/dist/assets/{ListeningList-DguKjI_t.js → ListeningList-D-zt6EXZ.js} +1 -1
  12. package/client/dist/assets/{Manga-HydR-L7o.js → Manga-JwprMKBk.js} +1 -1
  13. package/client/dist/assets/{MangaInfoPage-CBakEI9a.js → MangaInfoPage-DaeyJl2p.js} +1 -1
  14. package/client/dist/assets/{MangaReadPage-DwAR3iPy.js → MangaReadPage-COg0fHI9.js} +1 -1
  15. package/client/dist/assets/{MangaResetProgressModal-DGzl8vsO.js → MangaResetProgressModal-kguXv0_M.js} +1 -1
  16. package/client/dist/assets/{Mature-Pi0TxKuF.js → Mature-IHDaasrs.js} +1 -1
  17. package/client/dist/assets/{MediaCard--cuFxOCS.js → MediaCard-CpwsrXo5.js} +1 -1
  18. package/client/dist/assets/{Music-CQpyXTLH.js → Music-Dks4tPeK.js} +1 -1
  19. package/client/dist/assets/{OptionListMenu-B3D39dGS.js → OptionListMenu-11CcAsy8.js} +1 -1
  20. package/client/dist/assets/{Player-CsH4x_I_.js → Player-DQoEQkHL.js} +2 -2
  21. package/client/dist/assets/{PlayerSettings-DGxBsi3F.js → PlayerSettings-D4Ud4kv4.js} +1 -1
  22. package/client/dist/assets/{QueueOptionsButton-Djq4Cpex.js → QueueOptionsButton-B8PlPKjC.js} +1 -1
  23. package/client/dist/assets/{QueueRail-DOrYgNVX.js → QueueRail-B4Dok7U3.js} +1 -1
  24. package/client/dist/assets/{Radio-DF6g6odd.js → Radio-BHK6LSk4.js} +1 -1
  25. package/client/dist/assets/{ReadingList-DwYoBrNk.js → ReadingList-BCsxIz57.js} +1 -1
  26. package/client/dist/assets/{ResetProgressModal-CMF86mX6.js → ResetProgressModal-BXHxSfy-.js} +1 -1
  27. package/client/dist/assets/{Search-DFkhuk-x.js → Search-PK1ds07O.js} +1 -1
  28. package/client/dist/assets/{Settings-DBB2r0VQ.js → Settings-Bxk_x55l.js} +1 -1
  29. package/client/dist/assets/{SkeletonGrid-DJelJgAv.js → SkeletonGrid-CpJCZSRN.js} +1 -1
  30. package/client/dist/assets/{Trackers-DGYXFhYZ.js → Trackers-RieCh_84.js} +1 -1
  31. package/client/dist/assets/{Tv-C5MDGD2Z.js → Tv-DLytPJUj.js} +1 -1
  32. package/client/dist/assets/{TvInfo-DrKuK13u.js → TvInfo-C8djKebR.js} +1 -1
  33. package/client/dist/assets/{TvPopup-DtHilRDT.js → TvPopup-40JGGJDk.js} +1 -1
  34. package/client/dist/assets/{TvSearch-CzNxdu_w.js → TvSearch-A3hbospf.js} +1 -1
  35. package/client/dist/assets/{TvWatchlist-D7p4br6S.js → TvWatchlist-DuXjqmZM.js} +1 -1
  36. package/client/dist/assets/{Watchlist-DrOzswl2.js → Watchlist-B9wGBh0u.js} +1 -1
  37. package/client/dist/assets/{hls-BGCX7mJy.js → hls-CZ5X3Ya-.js} +1 -1
  38. package/client/dist/assets/{index-CnfZwsds.js → index-dRhc9-ol.js} +2 -2
  39. package/client/dist/assets/{useAnimeInfoData-DYXWBPF-.js → useAnimeInfoData-ZzDtLfvD.js} +1 -1
  40. package/client/dist/assets/{useAsmr-BbhQ59bk.js → useAsmr-CKOdHgol.js} +1 -1
  41. package/client/dist/assets/{useAsmrPopup-SymV1J-b.js → useAsmrPopup-SFHPhpcl.js} +1 -1
  42. package/client/dist/assets/{useMangaLibrary-DuesWQaE.js → useMangaLibrary-CKATe31P.js} +1 -1
  43. package/client/dist/assets/{useMangaPopup-D57OVGUr.js → useMangaPopup-C75QuYgY.js} +1 -1
  44. package/client/dist/assets/{useProviders-mpQb_dix.js → useProviders-CMtvZbEi.js} +1 -1
  45. package/client/dist/assets/{useTv-DGflCsSI.js → useTv-DUX4zYMr.js} +1 -1
  46. package/client/dist/assets/{useTvLibrary-BqeSmyc4.js → useTvLibrary-By4Gt-R_.js} +1 -1
  47. package/client/dist/index.html +1 -1
  48. package/package.json +2 -2
  49. package/server/dist/app-auth.js +1 -157
  50. package/server/dist/app-hono.js +7 -557
  51. package/server/dist/config.js +1 -174
  52. package/server/dist/db/drizzle.js +1 -16
  53. package/server/dist/db/schema-anime.js +1 -61
  54. package/server/dist/db/schema-asmr.js +1 -13
  55. package/server/dist/db/schema-manga.js +1 -31
  56. package/server/dist/db/schema-tv.js +1 -18
  57. package/server/dist/db.js +1 -217
  58. package/server/dist/discord-gateway.js +1 -385
  59. package/server/dist/discord-rpc.js +1 -413
  60. package/server/dist/github-sync.js +1 -508
  61. package/server/dist/google.js +1 -741
  62. package/server/dist/hono/asmr.js +1 -10
  63. package/server/dist/hono/auth.js +3 -26
  64. package/server/dist/hono/music.js +1 -209
  65. package/server/dist/hono/proxy.js +36 -783
  66. package/server/dist/hono/tv.js +5 -200
  67. package/server/dist/hono/watchlist.js +2 -338
  68. package/server/dist/lib/anilist.js +26 -1458
  69. package/server/dist/lib/anischedule.js +1 -191
  70. package/server/dist/lib/insights.js +1 -185
  71. package/server/dist/lib/ipc.js +1 -23
  72. package/server/dist/lib/kitsu.js +1 -517
  73. package/server/dist/lib/mal-import.js +1 -291
  74. package/server/dist/lib/mal.js +1 -957
  75. package/server/dist/lib/migration.js +1 -310
  76. package/server/dist/lib/offline-db.js +4 -384
  77. package/server/dist/lib/potoken.js +1 -135
  78. package/server/dist/lib/recommendations.service.js +1 -216
  79. package/server/dist/lib/temp-ids.js +1 -3
  80. package/server/dist/lib/tmdb.js +1 -72
  81. package/server/dist/lib/tracker/anilist-tracker.js +8 -245
  82. package/server/dist/lib/tracker/manga-sync.service.js +1 -773
  83. package/server/dist/lib/tracker/sync-state.js +1 -21
  84. package/server/dist/lib/tracker/sync.service.js +1 -476
  85. package/server/dist/lib/translate.js +3 -79
  86. package/server/dist/lib/ump.js +1 -106
  87. package/server/dist/lib/ytmusic.js +1 -162
  88. package/server/dist/logger.js +1 -16
  89. package/server/dist/providers/manga/manga.types.js +0 -1
  90. package/server/dist/providers/provider.interface.js +0 -1
  91. package/server/dist/providers/remote-loader.js +2 -614
  92. package/server/dist/providers/remote-types.js +1 -159
  93. package/server/dist/providers/title-matching.js +1 -226
  94. package/server/dist/providers/tv.types.js +0 -1
  95. package/server/dist/rclone.js +2 -120
  96. package/server/dist/repositories/asmr.repository.js +11 -46
  97. package/server/dist/repositories/insights.repository.js +11 -25
  98. package/server/dist/repositories/library.repository.js +3 -94
  99. package/server/dist/repositories/mal-cache.repository.js +1 -52
  100. package/server/dist/repositories/manga.repository.js +14 -58
  101. package/server/dist/repositories/notifications.repository.js +6 -52
  102. package/server/dist/repositories/queue.repository.js +5 -58
  103. package/server/dist/repositories/settings.repository.js +2 -29
  104. package/server/dist/repositories/shows-meta.repository.js +3 -56
  105. package/server/dist/repositories/temp-show-ids.repository.js +3 -63
  106. package/server/dist/repositories/tv.repository.js +11 -54
  107. package/server/dist/repositories/watched-episodes.repository.js +6 -55
  108. package/server/dist/repositories/watchlist.repository.js +3 -54
  109. package/server/dist/scripts/backfill-episode-duration.js +1 -42
  110. package/server/dist/server.js +1 -310
  111. package/server/dist/shipped-defaults.js +1 -6
  112. package/server/dist/sync-payload.js +1 -91
  113. package/server/dist/sync.js +3 -647
  114. package/server/dist/trpc/context.js +1 -7
  115. package/server/dist/trpc/errors.js +1 -15
  116. package/server/dist/trpc/index.js +1 -18
  117. package/server/dist/trpc/router.js +1 -49
  118. package/server/dist/trpc/routers/asmr-library.js +1 -112
  119. package/server/dist/trpc/routers/asmr-progress.js +1 -121
  120. package/server/dist/trpc/routers/asmr.js +1 -87
  121. package/server/dist/trpc/routers/auth.js +1 -194
  122. package/server/dist/trpc/routers/continue-watching.js +2 -58
  123. package/server/dist/trpc/routers/data.js +1 -1026
  124. package/server/dist/trpc/routers/discord.js +1 -285
  125. package/server/dist/trpc/routers/discovery.js +1 -11
  126. package/server/dist/trpc/routers/insights.js +1 -14
  127. package/server/dist/trpc/routers/manga-library.js +1 -263
  128. package/server/dist/trpc/routers/manga-progress.js +1 -169
  129. package/server/dist/trpc/routers/manga.js +1 -201
  130. package/server/dist/trpc/routers/music.js +1 -655
  131. package/server/dist/trpc/routers/notifications.js +1 -80
  132. package/server/dist/trpc/routers/progress.js +1 -109
  133. package/server/dist/trpc/routers/queue.js +1 -93
  134. package/server/dist/trpc/routers/radio.js +1 -145
  135. package/server/dist/trpc/routers/settings.js +1 -96
  136. package/server/dist/trpc/routers/tracker.js +1 -213
  137. package/server/dist/trpc/routers/tv-library.js +1 -135
  138. package/server/dist/trpc/routers/tv-progress.js +1 -159
  139. package/server/dist/trpc/routers/tv.js +1 -551
  140. package/server/dist/trpc/routers/watchlist.js +2 -549
  141. package/server/dist/trpc/validation.js +1 -258
  142. package/server/dist/utils/cache.utils.js +1 -25
  143. package/server/dist/utils/cookie.utils.js +1 -13
  144. package/server/dist/utils/db-utils.js +5 -17
  145. package/server/dist/utils/env.utils.js +5 -77
  146. package/server/dist/utils/http.utils.js +1 -82
  147. package/server/dist/utils/request-context.js +1 -2
  148. package/server/dist/utils/security.utils.js +1 -76
  149. package/server/package.json +3 -2
@@ -1,169 +1 @@
1
- import { protectedProcedure, router } from '../index.js';
2
- import { defineSchema, optStr, reqObj, reqStr, reqStrArray } from '../validation.js';
3
- import { performMangaWriteTransactionAsync } from '../../sync.js';
4
- import { adultNonListIds } from '../../utils/db-utils.js';
5
- import { SettingsRepository } from '../../repositories/settings.repository.js';
6
- import { MangaLibraryRepository, MangaProgressRepository, } from '../../repositories/manga.repository.js';
7
- const MANGA_ADULT_RATINGS = ['erotica', 'pornographic'];
8
- const isMangaContinueAdult = (row) => !!row.contentRating && MANGA_ADULT_RATINGS.includes(row.contentRating);
9
- const getAdultNonListMangaIds = (db) => adultNonListIds(db, 'manga_progress', 'manga_library', 'mangaId', "COALESCE(l.contentRating, p.contentRating) IN ('erotica', 'pornographic')");
10
- const mangaIdInput = () => defineSchema((value) => ({
11
- mangaId: reqStr(reqObj(value), 'mangaId'),
12
- }));
13
- const mangaProgressSaveInput = () => defineSchema((value) => {
14
- const obj = reqObj(value);
15
- const out = {
16
- mangaId: reqStr(obj, 'mangaId'),
17
- chapterId: reqStr(obj, 'chapterId'),
18
- };
19
- if (obj.chapterNumber !== undefined && obj.chapterNumber !== null)
20
- out.chapterNumber = String(obj.chapterNumber);
21
- if (obj.page !== undefined && obj.page !== null) {
22
- const page = Number(obj.page);
23
- if (!Number.isNaN(page))
24
- out.page = page;
25
- }
26
- if (obj.pageCount !== undefined && obj.pageCount !== null) {
27
- const pageCount = Number(obj.pageCount);
28
- if (!Number.isNaN(pageCount))
29
- out.pageCount = pageCount;
30
- }
31
- const title = optStr(obj, 'title');
32
- if (title !== undefined)
33
- out.title = title;
34
- const cover = optStr(obj, 'cover');
35
- if (cover !== undefined)
36
- out.cover = cover;
37
- const provider = optStr(obj, 'provider');
38
- if (provider !== undefined)
39
- out.provider = provider;
40
- const altTitle = optStr(obj, 'altTitle');
41
- if (altTitle !== undefined)
42
- out.altTitle = altTitle;
43
- const contentRating = optStr(obj, 'contentRating');
44
- if (contentRating !== undefined)
45
- out.contentRating = contentRating;
46
- return out;
47
- });
48
- const mangaProgressRemoveInput = () => defineSchema((value) => {
49
- const obj = reqObj(value);
50
- const out = { mangaId: reqStr(obj, 'mangaId') };
51
- const chapterId = optStr(obj, 'chapterId');
52
- if (chapterId !== undefined)
53
- out.chapterId = chapterId;
54
- return out;
55
- });
56
- const mangaProgressRemoveManyInput = () => defineSchema((value) => ({
57
- ids: reqStrArray(reqObj(value), 'ids', true),
58
- }));
59
- const mangaContinueReadingInput = () => defineSchema((value) => {
60
- const obj = reqObj(value);
61
- const out = {};
62
- if (obj.limit !== undefined && obj.limit !== null) {
63
- const limit = Number(obj.limit);
64
- if (!Number.isNaN(limit))
65
- out.limit = limit;
66
- }
67
- return out;
68
- });
69
- export const mangaProgressRouter = router({
70
- byManga: protectedProcedure.input(mangaIdInput()).query(async ({ ctx, input }) => {
71
- try {
72
- const rows = await MangaProgressRepository.getByManga(ctx.mangaDb, input.mangaId);
73
- return { progress: rows };
74
- }
75
- catch {
76
- return { progress: [] };
77
- }
78
- }),
79
- latest: protectedProcedure.input(mangaIdInput()).query(async ({ ctx, input }) => {
80
- try {
81
- const row = await MangaProgressRepository.getLatest(ctx.mangaDb, input.mangaId);
82
- return row || { page: 0, pageCount: 0 };
83
- }
84
- catch {
85
- return { page: 0, pageCount: 0 };
86
- }
87
- }),
88
- save: protectedProcedure.input(mangaProgressSaveInput()).mutation(async ({ ctx, input }) => {
89
- const pageNum = Math.max(Number(input.page) || 0, 0);
90
- const pageCountNum = Math.max(Number(input.pageCount) || 0, 0);
91
- await performMangaWriteTransactionAsync(ctx.mangaDb, async (tx) => {
92
- await MangaProgressRepository.upsert(tx, {
93
- mangaId: String(input.mangaId),
94
- chapterId: String(input.chapterId),
95
- chapterNumber: String(input.chapterNumber || ''),
96
- page: pageNum,
97
- pageCount: pageCountNum,
98
- title: input.title ?? null,
99
- cover: input.cover ?? null,
100
- provider: input.provider ?? null,
101
- altTitle: input.altTitle ?? null,
102
- contentRating: input.contentRating ?? null,
103
- });
104
- await MangaLibraryRepository.touchProgress(tx, String(input.mangaId), {
105
- chapterId: String(input.chapterId),
106
- chapterNumber: String(input.chapterNumber || ''),
107
- page: pageNum,
108
- });
109
- });
110
- return { success: true };
111
- }),
112
- remove: protectedProcedure.input(mangaProgressRemoveInput()).mutation(async ({ ctx, input }) => {
113
- await performMangaWriteTransactionAsync(ctx.mangaDb, async (tx) => {
114
- if (input.chapterId) {
115
- await MangaProgressRepository.deleteChapter(tx, String(input.mangaId), String(input.chapterId));
116
- }
117
- else {
118
- await MangaProgressRepository.deleteByManga(tx, String(input.mangaId));
119
- }
120
- });
121
- return { success: true };
122
- }),
123
- removeMany: protectedProcedure
124
- .input(mangaProgressRemoveManyInput())
125
- .mutation(async ({ ctx, input }) => {
126
- const ids = input.ids.map((id) => String(id));
127
- await performMangaWriteTransactionAsync(ctx.mangaDb, async (tx) => {
128
- await MangaProgressRepository.deleteMany(tx, ids);
129
- });
130
- return { success: true, removed: ids.length };
131
- }),
132
- continueReading: protectedProcedure
133
- .input(mangaContinueReadingInput())
134
- .query(async ({ ctx, input }) => {
135
- try {
136
- const limit = Math.min(Math.max(parseInt(String(input.limit)) || 24, 1), 100);
137
- const { ignoreAdult, listOnly } = await SettingsRepository.readContinueFlags(ctx.db, 'mangaIgnoreAdultContent', 'mangaCwWatchlistOnly');
138
- const rows = (await MangaProgressRepository.getContinueReading(ctx.mangaDb, limit)).filter((row) => {
139
- if (ignoreAdult && isMangaContinueAdult(row))
140
- return false;
141
- if (listOnly && row.watchlistStatus !== 'Reading')
142
- return false;
143
- return true;
144
- });
145
- return { data: rows, total: rows.length };
146
- }
147
- catch {
148
- return { data: [], total: 0 };
149
- }
150
- }),
151
- adultCount: protectedProcedure.query(async ({ ctx }) => {
152
- try {
153
- const ids = await getAdultNonListMangaIds(ctx.mangaDb);
154
- return { count: ids.length };
155
- }
156
- catch {
157
- return { count: 0 };
158
- }
159
- }),
160
- purgeAdult: protectedProcedure.mutation(async ({ ctx }) => {
161
- const ids = await getAdultNonListMangaIds(ctx.mangaDb);
162
- if (ids.length > 0) {
163
- await performMangaWriteTransactionAsync(ctx.mangaDb, async (tx) => {
164
- await MangaProgressRepository.deleteMany(tx, ids);
165
- });
166
- }
167
- return { success: true, removed: ids.length };
168
- }),
169
- });
1
+ import{protectedProcedure as r,router as b}from"../index.js";import{defineSchema as p,optStr as g,reqObj as d,reqStr as m,reqStrArray as f}from"../validation.js";import{performMangaWriteTransactionAsync as l}from"../../sync.js";import{adultNonListIds as M}from"../../utils/db-utils.js";import{SettingsRepository as N}from"../../repositories/settings.repository.js";import{MangaLibraryRepository as R,MangaProgressRepository as i}from"../../repositories/manga.repository.js";const v=["erotica","pornographic"],C=n=>!!n.contentRating&&v.includes(n.contentRating),h=n=>M(n,"manga_progress","manga_library","mangaId","COALESCE(l.contentRating, p.contentRating) IN ('erotica', 'pornographic')"),y=()=>p(n=>({mangaId:m(d(n),"mangaId")})),S=()=>p(n=>{const t=d(n),a={mangaId:m(t,"mangaId"),chapterId:m(t,"chapterId")};if(t.chapterNumber!==void 0&&t.chapterNumber!==null&&(a.chapterNumber=String(t.chapterNumber)),t.page!==void 0&&t.page!==null){const c=Number(t.page);Number.isNaN(c)||(a.page=c)}if(t.pageCount!==void 0&&t.pageCount!==null){const c=Number(t.pageCount);Number.isNaN(c)||(a.pageCount=c)}const e=g(t,"title");e!==void 0&&(a.title=e);const o=g(t,"cover");o!==void 0&&(a.cover=o);const s=g(t,"provider");s!==void 0&&(a.provider=s);const u=g(t,"altTitle");u!==void 0&&(a.altTitle=u);const I=g(t,"contentRating");return I!==void 0&&(a.contentRating=I),a}),w=()=>p(n=>{const t=d(n),a={mangaId:m(t,"mangaId")},e=g(t,"chapterId");return e!==void 0&&(a.chapterId=e),a}),P=()=>p(n=>({ids:f(d(n),"ids",!0)})),A=()=>p(n=>{const t=d(n),a={};if(t.limit!==void 0&&t.limit!==null){const e=Number(t.limit);Number.isNaN(e)||(a.limit=e)}return a}),O=b({byManga:r.input(y()).query(async({ctx:n,input:t})=>{try{return{progress:await i.getByManga(n.mangaDb,t.mangaId)}}catch{return{progress:[]}}}),latest:r.input(y()).query(async({ctx:n,input:t})=>{try{return await i.getLatest(n.mangaDb,t.mangaId)||{page:0,pageCount:0}}catch{return{page:0,pageCount:0}}}),save:r.input(S()).mutation(async({ctx:n,input:t})=>{const a=Math.max(Number(t.page)||0,0),e=Math.max(Number(t.pageCount)||0,0);return await l(n.mangaDb,async o=>{await i.upsert(o,{mangaId:String(t.mangaId),chapterId:String(t.chapterId),chapterNumber:String(t.chapterNumber||""),page:a,pageCount:e,title:t.title??null,cover:t.cover??null,provider:t.provider??null,altTitle:t.altTitle??null,contentRating:t.contentRating??null}),await R.touchProgress(o,String(t.mangaId),{chapterId:String(t.chapterId),chapterNumber:String(t.chapterNumber||""),page:a})}),{success:!0}}),remove:r.input(w()).mutation(async({ctx:n,input:t})=>(await l(n.mangaDb,async a=>{t.chapterId?await i.deleteChapter(a,String(t.mangaId),String(t.chapterId)):await i.deleteByManga(a,String(t.mangaId))}),{success:!0})),removeMany:r.input(P()).mutation(async({ctx:n,input:t})=>{const a=t.ids.map(e=>String(e));return await l(n.mangaDb,async e=>{await i.deleteMany(e,a)}),{success:!0,removed:a.length}}),continueReading:r.input(A()).query(async({ctx:n,input:t})=>{try{const a=Math.min(Math.max(parseInt(String(t.limit))||24,1),100),{ignoreAdult:e,listOnly:o}=await N.readContinueFlags(n.db,"mangaIgnoreAdultContent","mangaCwWatchlistOnly"),s=(await i.getContinueReading(n.mangaDb,a)).filter(u=>!(e&&C(u)||o&&u.watchlistStatus!=="Reading"));return{data:s,total:s.length}}catch{return{data:[],total:0}}}),adultCount:r.query(async({ctx:n})=>{try{return{count:(await h(n.mangaDb)).length}}catch{return{count:0}}}),purgeAdult:r.mutation(async({ctx:n})=>{const t=await h(n.mangaDb);return t.length>0&&await l(n.mangaDb,async a=>{await i.deleteMany(a,t)}),{success:!0,removed:t.length}})});export{O as mangaProgressRouter};
@@ -1,201 +1 @@
1
- import { TRPCError } from '@trpc/server';
2
- import { protectedProcedure, router } from '../index.js';
3
- import { parseJsonBody } from '../../utils/http.utils.js';
4
- import { defineSchema, optStr, reqObj } from '../validation.js';
5
- import logger from '../../logger.js';
6
- const SAFE = ['safe'];
7
- function parseRatings(query) {
8
- const mature = query['mature'] === '1';
9
- const raw = String(query['rating'] || 'safe')
10
- .split(',')
11
- .map((s) => s.trim().toLowerCase())
12
- .filter((s) => ['safe', 'suggestive', 'erotica', 'pornographic'].includes(s));
13
- if (!mature)
14
- return SAFE;
15
- const exact = [...new Set(raw)];
16
- return exact.length > 0 ? exact : SAFE;
17
- }
18
- function pick(ctx, name) {
19
- return ctx.getMangaProvider(name.toLowerCase());
20
- }
21
- async function cached(ctx, key, ttl, produce) {
22
- const hit = ctx.apiCache.get(key);
23
- if (hit)
24
- return hit;
25
- const result = await produce();
26
- if ((result.status ?? 200) >= 200 && (result.status ?? 200) < 400) {
27
- if (ttl !== undefined)
28
- ctx.apiCache.set(key, result.body, ttl);
29
- else
30
- ctx.apiCache.set(key, result.body);
31
- }
32
- if (result.status !== undefined && result.status !== 200) {
33
- throw new TRPCError({
34
- code: result.status === 400
35
- ? 'BAD_REQUEST'
36
- : result.status === 404
37
- ? 'NOT_FOUND'
38
- : 'INTERNAL_SERVER_ERROR',
39
- message: result.body?.error || 'Request failed',
40
- });
41
- }
42
- return result.body;
43
- }
44
- const mangaSearchInput = () => defineSchema((value) => {
45
- const obj = reqObj(value);
46
- const out = {};
47
- for (const k of ['provider', 'q', 'sort', 'status', 'type', 'rating', 'mature']) {
48
- const v = optStr(obj, k);
49
- if (v !== undefined)
50
- out[k] = v;
51
- }
52
- if (obj.page !== undefined)
53
- out.page = parseInt(String(obj.page), 10) || 1;
54
- if (obj.limit !== undefined)
55
- out.limit = Math.min(parseInt(String(obj.limit), 10) || 24, 50);
56
- return out;
57
- });
58
- const mangaInfoInput = () => defineSchema((value) => {
59
- const obj = reqObj(value);
60
- const out = {};
61
- for (const k of ['provider', 'id', 'rating', 'mature']) {
62
- const v = optStr(obj, k);
63
- if (v !== undefined)
64
- out[k] = v;
65
- }
66
- return out;
67
- });
68
- const mangaPagesInput = () => defineSchema((value) => {
69
- const obj = reqObj(value);
70
- const out = {};
71
- const provider = optStr(obj, 'provider');
72
- if (provider !== undefined)
73
- out.provider = provider;
74
- const id = optStr(obj, 'id');
75
- if (id !== undefined)
76
- out.id = id;
77
- return out;
78
- });
79
- export const mangaRouter = router({
80
- trending: protectedProcedure.query(async ({ ctx }) => {
81
- const cacheKey = 'route-manga-trending';
82
- const cached = ctx.apiCache.get(cacheKey);
83
- if (cached)
84
- return cached;
85
- try {
86
- const url = 'https://api.mangadex.org/manga?limit=6&order%5BfollowedCount%5D=desc&includes%5B%5D=cover_art&contentRating%5B%5D=safe&contentRating%5B%5D=suggestive';
87
- const upstream = await fetch(url, {
88
- headers: { 'User-Agent': 'dango/3.1.9' },
89
- });
90
- if (!upstream.ok)
91
- return [];
92
- const data = await parseJsonBody(upstream);
93
- const results = (data.data || []).map((m) => {
94
- const title = m.attributes?.title?.en || Object.values(m.attributes?.title || {})[0] || '';
95
- const desc = m.attributes?.description?.en || '';
96
- const tags = (m.attributes?.tags || [])
97
- .map((t) => t.attributes?.name?.en)
98
- .filter(Boolean);
99
- const coverArt = m.relationships?.find((r) => r.type === 'cover_art');
100
- const fileName = coverArt?.attributes?.fileName;
101
- const cover = fileName
102
- ? `https://uploads.mangadex.org/covers/${m.id}/${fileName}.512.jpg`
103
- : '';
104
- return {
105
- id: m.id,
106
- title,
107
- description: desc,
108
- tags,
109
- year: m.attributes?.year || null,
110
- status: m.attributes?.status || null,
111
- cover,
112
- };
113
- });
114
- ctx.apiCache.set(cacheKey, results, 600);
115
- return results;
116
- }
117
- catch {
118
- return [];
119
- }
120
- }),
121
- search: protectedProcedure.input(mangaSearchInput()).query(async ({ ctx, input }) => {
122
- const provider = input.provider || '';
123
- const q = input.q || '';
124
- const page = input.page || 1;
125
- return cached(ctx, `route-manga-search-${provider}-${q}-${page}-${input.sort || ''}-${input.status || ''}-${input.type || ''}-${input.rating || ''}-${input.mature || ''}`, 300, async () => {
126
- try {
127
- const ratings = parseRatings({ rating: input.rating, mature: input.mature });
128
- const rawType = input.type || '';
129
- const type = rawType === 'doujinshi' && input.mature !== '1' ? '' : rawType;
130
- const result = await pick(ctx, provider)?.search({
131
- query: q,
132
- page,
133
- limit: input.limit || 24,
134
- sort: input.sort || 'popular',
135
- status: input.status || '',
136
- type,
137
- ratings,
138
- });
139
- return { body: result ?? { items: [], hasNext: false } };
140
- }
141
- catch (err) {
142
- logger.error({ err }, '[Manga] search failed');
143
- return { body: { items: [], hasNext: false } };
144
- }
145
- });
146
- }),
147
- info: protectedProcedure.input(mangaInfoInput()).query(async ({ ctx, input }) => {
148
- const provider = String(input.provider || '');
149
- const id = String(input.id || '');
150
- const rating = String(input.rating || '');
151
- const mature = String(input.mature || '');
152
- return cached(ctx, `route-manga-info-${provider}-${id}-${rating}-${mature}`, 600, async () => {
153
- try {
154
- if (!id)
155
- return { status: 400, body: { error: 'Missing id' } };
156
- const detail = await pick(ctx, provider)?.getDetail(id, parseRatings({ rating, mature }));
157
- if (!detail)
158
- return { status: 404, body: { error: 'Not found' } };
159
- return { body: detail };
160
- }
161
- catch (err) {
162
- logger.error({ err }, '[Manga] info failed');
163
- return { status: 500, body: { error: 'Failed to load manga' } };
164
- }
165
- });
166
- }),
167
- chapters: protectedProcedure.input(mangaInfoInput()).query(async ({ ctx, input }) => {
168
- const provider = String(input.provider || '');
169
- const id = String(input.id || '');
170
- const rating = String(input.rating || '');
171
- const mature = String(input.mature || '');
172
- return cached(ctx, `route-manga-chapters-${provider}-${id}-${rating}-${mature}`, 600, async () => {
173
- try {
174
- if (!id)
175
- return { status: 400, body: { error: 'Missing id' } };
176
- const chapters = (await pick(ctx, provider)?.getChapters(id, parseRatings({ rating, mature }))) ?? [];
177
- return { body: { chapters } };
178
- }
179
- catch (err) {
180
- logger.error({ err }, '[Manga] chapters failed');
181
- return { body: { chapters: [] } };
182
- }
183
- });
184
- }),
185
- pages: protectedProcedure.input(mangaPagesInput()).query(async ({ ctx, input }) => {
186
- const provider = String(input.provider || '');
187
- const id = String(input.id || '');
188
- return cached(ctx, `route-manga-pages-${provider}-${id}`, 300, async () => {
189
- try {
190
- if (!id)
191
- return { status: 400, body: { error: 'Missing id' } };
192
- const pages = (await pick(ctx, provider)?.getPages(id)) ?? [];
193
- return { body: { pages } };
194
- }
195
- catch (err) {
196
- logger.error({ err }, '[Manga] pages failed');
197
- return { body: { pages: [] } };
198
- }
199
- });
200
- }),
201
- });
1
+ import{TRPCError as $}from"@trpc/server";import{protectedProcedure as g,router as w}from"../index.js";import{parseJsonBody as P}from"../../utils/http.utils.js";import{defineSchema as f,optStr as u,reqObj as y}from"../validation.js";import c from"../../logger.js";const v=["safe"];function l(a){const t=a.mature==="1",r=String(a.rating||"safe").split(",").map(e=>e.trim().toLowerCase()).filter(e=>["safe","suggestive","erotica","pornographic"].includes(e));if(!t)return v;const n=[...new Set(r)];return n.length>0?n:v}function d(a,t){return a.getMangaProvider(t.toLowerCase())}async function p(a,t,r,n){const e=a.apiCache.get(t);if(e)return e;const s=await n();if((s.status??200)>=200&&(s.status??200)<400&&(r!==void 0?a.apiCache.set(t,s.body,r):a.apiCache.set(t,s.body)),s.status!==void 0&&s.status!==200)throw new $({code:s.status===400?"BAD_REQUEST":s.status===404?"NOT_FOUND":"INTERNAL_SERVER_ERROR",message:s.body?.error||"Request failed"});return s.body}const C=()=>f(a=>{const t=y(a),r={};for(const n of["provider","q","sort","status","type","rating","mature"]){const e=u(t,n);e!==void 0&&(r[n]=e)}return t.page!==void 0&&(r.page=parseInt(String(t.page),10)||1),t.limit!==void 0&&(r.limit=Math.min(parseInt(String(t.limit),10)||24,50)),r}),M=()=>f(a=>{const t=y(a),r={};for(const n of["provider","id","rating","mature"]){const e=u(t,n);e!==void 0&&(r[n]=e)}return r}),N=()=>f(a=>{const t=y(a),r={},n=u(t,"provider");n!==void 0&&(r.provider=n);const e=u(t,"id");return e!==void 0&&(r.id=e),r}),E=w({trending:g.query(async({ctx:a})=>{const t="route-manga-trending",r=a.apiCache.get(t);if(r)return r;try{const e=await fetch("https://api.mangadex.org/manga?limit=6&order%5BfollowedCount%5D=desc&includes%5B%5D=cover_art&contentRating%5B%5D=safe&contentRating%5B%5D=suggestive",{headers:{"User-Agent":"dango/3.1.9"}});if(!e.ok)return[];const i=((await P(e)).data||[]).map(o=>{const h=o.attributes?.title?.en||Object.values(o.attributes?.title||{})[0]||"",I=o.attributes?.description?.en||"",S=(o.attributes?.tags||[]).map(m=>m.attributes?.name?.en).filter(Boolean),b=o.relationships?.find(m=>m.type==="cover_art")?.attributes?.fileName,R=b?`https://uploads.mangadex.org/covers/${o.id}/${b}.512.jpg`:"";return{id:o.id,title:h,description:I,tags:S,year:o.attributes?.year||null,status:o.attributes?.status||null,cover:R}});return a.apiCache.set(t,i,600),i}catch{return[]}}),search:g.input(C()).query(async({ctx:a,input:t})=>{const r=t.provider||"",n=t.q||"",e=t.page||1;return p(a,`route-manga-search-${r}-${n}-${e}-${t.sort||""}-${t.status||""}-${t.type||""}-${t.rating||""}-${t.mature||""}`,300,async()=>{try{const s=l({rating:t.rating,mature:t.mature}),i=t.type||"",o=i==="doujinshi"&&t.mature!=="1"?"":i;return{body:await d(a,r)?.search({query:n,page:e,limit:t.limit||24,sort:t.sort||"popular",status:t.status||"",type:o,ratings:s})??{items:[],hasNext:!1}}}catch(s){return c.error({err:s},"[Manga] search failed"),{body:{items:[],hasNext:!1}}}})}),info:g.input(M()).query(async({ctx:a,input:t})=>{const r=String(t.provider||""),n=String(t.id||""),e=String(t.rating||""),s=String(t.mature||"");return p(a,`route-manga-info-${r}-${n}-${e}-${s}`,600,async()=>{try{if(!n)return{status:400,body:{error:"Missing id"}};const i=await d(a,r)?.getDetail(n,l({rating:e,mature:s}));return i?{body:i}:{status:404,body:{error:"Not found"}}}catch(i){return c.error({err:i},"[Manga] info failed"),{status:500,body:{error:"Failed to load manga"}}}})}),chapters:g.input(M()).query(async({ctx:a,input:t})=>{const r=String(t.provider||""),n=String(t.id||""),e=String(t.rating||""),s=String(t.mature||"");return p(a,`route-manga-chapters-${r}-${n}-${e}-${s}`,600,async()=>{try{return n?{body:{chapters:await d(a,r)?.getChapters(n,l({rating:e,mature:s}))??[]}}:{status:400,body:{error:"Missing id"}}}catch(i){return c.error({err:i},"[Manga] chapters failed"),{body:{chapters:[]}}}})}),pages:g.input(N()).query(async({ctx:a,input:t})=>{const r=String(t.provider||""),n=String(t.id||"");return p(a,`route-manga-pages-${r}-${n}`,300,async()=>{try{return n?{body:{pages:await d(a,r)?.getPages(n)??[]}}:{status:400,body:{error:"Missing id"}}}catch(e){return c.error({err:e},"[Manga] pages failed"),{body:{pages:[]}}}})})});export{E as mangaRouter};