@serifpersia/dango 3.6.5 → 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 (151) hide show
  1. package/README.md +1 -1
  2. package/client/dist/assets/{AnimeCard-JkXG40Es.js → AnimeCard-DraA_TF9.js} +1 -1
  3. package/client/dist/assets/{AnimeInfo-2L0UJkbB.js → AnimeInfo-BJ7pzs-P.js} +1 -1
  4. package/client/dist/assets/{AnimeInfoPage-BDs03vKk.js → AnimeInfoPage-B_esUr5C.js} +2 -2
  5. package/client/dist/assets/{Asmr-D2mVKEl9.js → Asmr-Ckuh2xfW.js} +1 -1
  6. package/client/dist/assets/{AsmrResetProgressModal-MQA_bNQM.js → AsmrResetProgressModal-BS6UFJN4.js} +1 -1
  7. package/client/dist/assets/AvSyncCalibrator-DwHCd9yB.js +10 -0
  8. package/client/dist/assets/{Home-DxMBHFmc.js → Home-DuM1EuvN.js} +1 -1
  9. package/client/dist/assets/{Insights-hmLWsSXY.js → Insights-D3Pd9c9V.js} +1 -1
  10. package/client/dist/assets/{LibraryListPage-CWI7hgUh.js → LibraryListPage-Dm2yVn0-.js} +1 -1
  11. package/client/dist/assets/{ListeningList-NOc28KAV.js → ListeningList-D-zt6EXZ.js} +1 -1
  12. package/client/dist/assets/{Manga-B5qNhI7v.js → Manga-JwprMKBk.js} +1 -1
  13. package/client/dist/assets/{MangaInfoPage-D4wIyb-V.js → MangaInfoPage-DaeyJl2p.js} +1 -1
  14. package/client/dist/assets/{MangaReadPage-Ce1VX7-0.js → MangaReadPage-COg0fHI9.js} +1 -1
  15. package/client/dist/assets/{MangaResetProgressModal-P3oY4_14.js → MangaResetProgressModal-kguXv0_M.js} +1 -1
  16. package/client/dist/assets/{Mature-D8EwGYQq.js → Mature-IHDaasrs.js} +1 -1
  17. package/client/dist/assets/{MediaCard-Dp8DjyXB.js → MediaCard-CpwsrXo5.js} +1 -1
  18. package/client/dist/assets/{Music-DULtVsAM.js → Music-Dks4tPeK.js} +1 -1
  19. package/client/dist/assets/{OptionListMenu-fIHdE-nq.js → OptionListMenu-11CcAsy8.js} +1 -1
  20. package/client/dist/assets/{Player-B8XeOmso.js → Player-DQoEQkHL.js} +6 -6
  21. package/client/dist/assets/{PlayerSettings-BycNCiCs.js → PlayerSettings-D4Ud4kv4.js} +1 -1
  22. package/client/dist/assets/{QueueOptionsButton-T1J37IZT.js → QueueOptionsButton-B8PlPKjC.js} +1 -1
  23. package/client/dist/assets/{QueueRail-BAwI-apz.js → QueueRail-B4Dok7U3.js} +1 -1
  24. package/client/dist/assets/{Radio-Bv9DyKtZ.js → Radio-BHK6LSk4.js} +1 -1
  25. package/client/dist/assets/{ReadingList-Cl0CcVp3.js → ReadingList-BCsxIz57.js} +1 -1
  26. package/client/dist/assets/{ResetProgressModal--N60WWCB.js → ResetProgressModal-BXHxSfy-.js} +1 -1
  27. package/client/dist/assets/{Search-DvgVDlUR.js → Search-PK1ds07O.js} +1 -1
  28. package/client/dist/assets/{Settings-dCz_-bJn.js → Settings-Bxk_x55l.js} +1 -1
  29. package/client/dist/assets/{SkeletonGrid-BZHKk1I6.js → SkeletonGrid-CpJCZSRN.js} +1 -1
  30. package/client/dist/assets/{Trackers-C1T5VcPu.js → Trackers-RieCh_84.js} +1 -1
  31. package/client/dist/assets/{Tv-Dhv_cg4y.js → Tv-DLytPJUj.js} +4 -4
  32. package/client/dist/assets/{TvInfo-DgaLK2H6.js → TvInfo-C8djKebR.js} +1 -1
  33. package/client/dist/assets/{TvPopup-DyX7f8kC.js → TvPopup-40JGGJDk.js} +1 -1
  34. package/client/dist/assets/{TvSearch-Bp0H6kue.js → TvSearch-A3hbospf.js} +1 -1
  35. package/client/dist/assets/{TvWatchlist-ohMtMeKa.js → TvWatchlist-DuXjqmZM.js} +1 -1
  36. package/client/dist/assets/{Watchlist-BbQtzsER.js → Watchlist-B9wGBh0u.js} +1 -1
  37. package/client/dist/assets/{hls-Bi1cjpLZ.js → hls-CZ5X3Ya-.js} +1 -1
  38. package/client/dist/assets/{index-BTnhzofk.js → index-dRhc9-ol.js} +2 -2
  39. package/client/dist/assets/{useAnimeInfoData-Cvmso3IK.js → useAnimeInfoData-ZzDtLfvD.js} +1 -1
  40. package/client/dist/assets/{useAsmr-CbzGCzmv.js → useAsmr-CKOdHgol.js} +1 -1
  41. package/client/dist/assets/{useAsmrPopup-DaREZ9Ie.js → useAsmrPopup-SFHPhpcl.js} +1 -1
  42. package/client/dist/assets/{useMangaLibrary-BVQRhsFh.js → useMangaLibrary-CKATe31P.js} +1 -1
  43. package/client/dist/assets/{useMangaPopup-CzsCbndG.js → useMangaPopup-C75QuYgY.js} +1 -1
  44. package/client/dist/assets/{useProviders-UuUPzft-.js → useProviders-CMtvZbEi.js} +1 -1
  45. package/client/dist/assets/{useTv-CSsbWKH9.js → useTv-DUX4zYMr.js} +1 -1
  46. package/client/dist/assets/{useTvLibrary-a_Y0c2FS.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 -905
  66. package/server/dist/hono/tv.js +5 -169
  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 -605
  92. package/server/dist/providers/remote-types.js +1 -124
  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 -307
  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
  150. package/client/dist/assets/AvSyncCalibrator-DmoH5eRA.js +0 -10
  151. package/server/dist/utils/megaplay.utils.js +0 -40
@@ -1,258 +1 @@
1
- import { TRPCError } from '@trpc/server';
2
- export function defineSchema(parse) {
3
- return {
4
- '~standard': {
5
- version: 1,
6
- vendor: 'dango',
7
- types: undefined,
8
- validate(value) {
9
- try {
10
- return { value: parse(value) };
11
- }
12
- catch (err) {
13
- return { issues: [{ message: err instanceof Error ? err.message : 'Invalid input' }] };
14
- }
15
- },
16
- },
17
- };
18
- }
19
- function readKey(value) {
20
- if (typeof value !== 'object' || value === null)
21
- throw new Error('Expected an object');
22
- const key = value.key;
23
- if (typeof key !== 'string' || key.length === 0)
24
- throw new Error('key must be a string');
25
- return key;
26
- }
27
- export function reqStr(obj, name) {
28
- const value = obj[name];
29
- if (typeof value !== 'string' || value.length === 0) {
30
- throw new Error(`${name} must be a string`);
31
- }
32
- return value;
33
- }
34
- export function optStr(obj, name) {
35
- const value = obj[name];
36
- if (value === undefined || value === null)
37
- return undefined;
38
- return String(value);
39
- }
40
- export function optNum(obj, name) {
41
- const value = obj[name];
42
- if (value === undefined || value === null || value === '')
43
- return undefined;
44
- const num = Number(value);
45
- return Number.isFinite(num) ? num : undefined;
46
- }
47
- export function optBool(obj, name) {
48
- const value = obj[name];
49
- if (typeof value !== 'boolean')
50
- return undefined;
51
- return value;
52
- }
53
- export function badRequest(message) {
54
- return new TRPCError({ code: 'BAD_REQUEST', message });
55
- }
56
- export function failed(message) {
57
- return new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message });
58
- }
59
- export function reqId(obj, name) {
60
- const value = obj[name];
61
- if ((typeof value !== 'string' && typeof value !== 'number') || String(value).length === 0) {
62
- throw new Error(`${name} must be a string`);
63
- }
64
- return String(value);
65
- }
66
- export function readCount(obj, name, fallback) {
67
- const raw = obj[name];
68
- if (raw === undefined || raw === null || raw === '')
69
- return fallback;
70
- const n = Math.floor(Number(raw));
71
- return Number.isFinite(n) ? n : fallback;
72
- }
73
- export function strOrNull(obj, name) {
74
- const value = obj[name];
75
- return typeof value === 'string' ? value : null;
76
- }
77
- export function readPageLimit(obj) {
78
- const out = {};
79
- const page = optNum(obj, 'page');
80
- if (page !== undefined)
81
- out.page = page;
82
- const limit = optNum(obj, 'limit');
83
- if (limit !== undefined)
84
- out.limit = limit;
85
- return out;
86
- }
87
- function reqEpNum(obj, name) {
88
- const value = obj[name];
89
- if ((typeof value !== 'string' && typeof value !== 'number') || String(value).length === 0) {
90
- throw new Error(`${name} must be a string`);
91
- }
92
- return String(value);
93
- }
94
- export function reqStrArray(obj, name, nonEmpty) {
95
- const value = obj[name];
96
- if (!Array.isArray(value) || (nonEmpty && value.length === 0)) {
97
- throw new Error(`${name} must be ${nonEmpty ? 'a non-empty ' : 'an '}array`);
98
- }
99
- return value.map((ep) => String(ep));
100
- }
101
- export function reqObj(value) {
102
- if (typeof value !== 'object' || value === null)
103
- throw new Error('Expected an object');
104
- return value;
105
- }
106
- export const keyInput = () => defineSchema((value) => ({ key: readKey(value) }));
107
- export const idInput = () => defineSchema((value) => ({ id: reqStr(reqObj(value), 'id') }));
108
- export const showIdInput = () => defineSchema((value) => ({
109
- showId: reqStr(reqObj(value), 'showId'),
110
- }));
111
- export const episodeProgressInput = () => defineSchema((value) => {
112
- const obj = reqObj(value);
113
- return { showId: reqStr(obj, 'showId'), episodeNumber: reqEpNum(obj, 'episodeNumber') };
114
- });
115
- export const idsInput = () => defineSchema((value) => ({
116
- ids: [...new Set(reqStrArray(reqObj(value), 'ids', true))],
117
- }));
118
- export const watchlistAddInput = () => defineSchema((value) => {
119
- const obj = reqObj(value);
120
- const out = { id: reqStr(obj, 'id'), name: reqStr(obj, 'name') };
121
- const status = optStr(obj, 'status');
122
- if (status !== undefined)
123
- out.status = status;
124
- const thumbnail = optStr(obj, 'thumbnail');
125
- if (thumbnail !== undefined)
126
- out.thumbnail = thumbnail;
127
- const nativeName = optStr(obj, 'nativeName');
128
- if (nativeName !== undefined)
129
- out.nativeName = nativeName;
130
- const englishName = optStr(obj, 'englishName');
131
- if (englishName !== undefined)
132
- out.englishName = englishName;
133
- const type = optStr(obj, 'type');
134
- if (type !== undefined)
135
- out.type = type;
136
- if (typeof obj.isAdult === 'boolean')
137
- out.isAdult = obj.isAdult;
138
- return out;
139
- });
140
- export const watchlistStatusInput = () => defineSchema((value) => {
141
- const obj = reqObj(value);
142
- return { id: reqStr(obj, 'id'), status: reqStr(obj, 'status') };
143
- });
144
- export const watchlistBatchStatusInput = () => defineSchema((value) => {
145
- const obj = reqObj(value);
146
- return { ids: [...new Set(reqStrArray(obj, 'ids', true))], status: reqStr(obj, 'status') };
147
- });
148
- function readMeta(obj) {
149
- return {
150
- showName: optStr(obj, 'showName'),
151
- showThumbnail: optStr(obj, 'showThumbnail'),
152
- nativeName: optStr(obj, 'nativeName'),
153
- englishName: optStr(obj, 'englishName'),
154
- type: optStr(obj, 'type'),
155
- };
156
- }
157
- export const queueAddInput = () => defineSchema((value) => {
158
- const obj = reqObj(value);
159
- return {
160
- ...readMeta(obj),
161
- showId: reqStr(obj, 'showId'),
162
- episodeNumber: reqEpNum(obj, 'episodeNumber'),
163
- };
164
- });
165
- export const queueBatchInput = () => defineSchema((value) => {
166
- const obj = reqObj(value);
167
- return {
168
- ...readMeta(obj),
169
- showId: reqStr(obj, 'showId'),
170
- episodeNumbers: reqStrArray(obj, 'episodeNumbers', true),
171
- };
172
- });
173
- export const queueRemoveInput = () => defineSchema((value) => {
174
- const obj = reqObj(value);
175
- return { showId: reqStr(obj, 'showId'), episodeNumber: reqEpNum(obj, 'episodeNumber') };
176
- });
177
- export const queueRemoveManyInput = () => defineSchema((value) => {
178
- const obj = reqObj(value);
179
- const out = { showId: reqStr(obj, 'showId') };
180
- if (obj.episodeNumbers !== undefined)
181
- out.episodeNumbers = reqStrArray(obj, 'episodeNumbers', false);
182
- return out;
183
- });
184
- export const queueReorderInput = () => defineSchema((value) => {
185
- if (!Array.isArray(value))
186
- throw new Error('items must be an array');
187
- return value.map((item) => {
188
- const entry = reqObj(item);
189
- const out = {};
190
- if (typeof entry.id === 'number')
191
- out.id = entry.id;
192
- if (entry.showId !== undefined)
193
- out.showId = String(entry.showId);
194
- if (entry.episodeNumber !== undefined)
195
- out.episodeNumber = String(entry.episodeNumber);
196
- return out;
197
- });
198
- });
199
- export const setSettingInput = () => defineSchema((value) => {
200
- if (typeof value !== 'object' || value === null)
201
- throw new Error('Expected an object');
202
- return { key: readKey(value), value: value.value };
203
- });
204
- export const progressUpdateInput = () => defineSchema((value) => {
205
- const obj = reqObj(value);
206
- const out = {
207
- showId: reqStr(obj, 'showId'),
208
- episodeNumber: reqEpNum(obj, 'episodeNumber'),
209
- };
210
- const currentTime = optNum(obj, 'currentTime');
211
- if (currentTime !== undefined)
212
- out.currentTime = currentTime;
213
- const duration = optNum(obj, 'duration');
214
- if (duration !== undefined)
215
- out.duration = duration;
216
- const showName = optStr(obj, 'showName');
217
- if (showName !== undefined)
218
- out.showName = showName;
219
- const showThumbnail = optStr(obj, 'showThumbnail');
220
- if (showThumbnail !== undefined)
221
- out.showThumbnail = showThumbnail;
222
- const nativeName = optStr(obj, 'nativeName');
223
- if (nativeName !== undefined)
224
- out.nativeName = nativeName;
225
- const englishName = optStr(obj, 'englishName');
226
- if (englishName !== undefined)
227
- out.englishName = englishName;
228
- const genres = obj['genres'];
229
- if (typeof genres === 'string' || Array.isArray(genres)) {
230
- out.genres = (Array.isArray(genres) ? genres : [genres]).map(String);
231
- }
232
- const popularityScore = optNum(obj, 'popularityScore');
233
- if (popularityScore !== undefined)
234
- out.popularityScore = popularityScore;
235
- const type = optStr(obj, 'type');
236
- if (type !== undefined)
237
- out.type = type;
238
- const status = optStr(obj, 'status');
239
- if (status !== undefined)
240
- out.status = status;
241
- const episodeCount = optNum(obj, 'episodeCount');
242
- if (episodeCount !== undefined)
243
- out.episodeCount = episodeCount;
244
- const isPlaying = optBool(obj, 'isPlaying');
245
- if (isPlaying !== undefined)
246
- out.isPlaying = isPlaying;
247
- const sessionId = optStr(obj, 'sessionId');
248
- if (sessionId !== undefined)
249
- out.sessionId = sessionId;
250
- const isAdult = optBool(obj, 'isAdult');
251
- if (isAdult !== undefined)
252
- out.isAdult = isAdult;
253
- return out;
254
- });
255
- export const notificationDismissInput = () => defineSchema((value) => {
256
- const obj = reqObj(value);
257
- return { showId: reqStr(obj, 'showId'), episodeNumber: reqEpNum(obj, 'episodeNumber') };
258
- });
1
+ import{TRPCError as S}from"@trpc/server";function i(t){return{"~standard":{version:1,vendor:"dango",types:void 0,validate(e){try{return{value:t(e)}}catch(n){return{issues:[{message:n instanceof Error?n.message:"Invalid input"}]}}}}}}function v(t){if(typeof t!="object"||t===null)throw new Error("Expected an object");const e=t.key;if(typeof e!="string"||e.length===0)throw new Error("key must be a string");return e}function o(t,e){const n=t[e];if(typeof n!="string"||n.length===0)throw new Error(`${e} must be a string`);return n}function s(t,e){const n=t[e];if(n!=null)return String(n)}function a(t,e){const n=t[e];if(n==null||n==="")return;const r=Number(n);return Number.isFinite(r)?r:void 0}function k(t,e){const n=t[e];if(typeof n=="boolean")return n}function Q(t){return new S({code:"BAD_REQUEST",message:t})}function T(t){return new S({code:"INTERNAL_SERVER_ERROR",message:t})}function P(t,e){const n=t[e];if(typeof n!="string"&&typeof n!="number"||String(n).length===0)throw new Error(`${e} must be a string`);return String(n)}function j(t,e,n){const r=t[e];if(r==null||r==="")return n;const d=Math.floor(Number(r));return Number.isFinite(d)?d:n}function q(t,e){const n=t[e];return typeof n=="string"?n:null}function M(t){const e={},n=a(t,"page");n!==void 0&&(e.page=n);const r=a(t,"limit");return r!==void 0&&(e.limit=r),e}function f(t,e){const n=t[e];if(typeof n!="string"&&typeof n!="number"||String(n).length===0)throw new Error(`${e} must be a string`);return String(n)}function l(t,e,n){const r=t[e];if(!Array.isArray(r)||n&&r.length===0)throw new Error(`${e} must be ${n?"a non-empty ":"an "}array`);return r.map(d=>String(d))}function u(t){if(typeof t!="object"||t===null)throw new Error("Expected an object");return t}const O=()=>i(t=>({key:v(t)})),B=()=>i(t=>({id:o(u(t),"id")})),W=()=>i(t=>({showId:o(u(t),"showId")})),C=()=>i(t=>{const e=u(t);return{showId:o(e,"showId"),episodeNumber:f(e,"episodeNumber")}}),U=()=>i(t=>({ids:[...new Set(l(u(t),"ids",!0))]})),D=()=>i(t=>{const e=u(t),n={id:o(e,"id"),name:o(e,"name")},r=s(e,"status");r!==void 0&&(n.status=r);const d=s(e,"thumbnail");d!==void 0&&(n.thumbnail=d);const p=s(e,"nativeName");p!==void 0&&(n.nativeName=p);const g=s(e,"englishName");g!==void 0&&(n.englishName=g);const c=s(e,"type");return c!==void 0&&(n.type=c),typeof e.isAdult=="boolean"&&(n.isAdult=e.isAdult),n}),$=()=>i(t=>{const e=u(t);return{id:o(e,"id"),status:o(e,"status")}}),_=()=>i(t=>{const e=u(t);return{ids:[...new Set(l(e,"ids",!0))],status:o(e,"status")}});function A(t){return{showName:s(t,"showName"),showThumbnail:s(t,"showThumbnail"),nativeName:s(t,"nativeName"),englishName:s(t,"englishName"),type:s(t,"type")}}const F=()=>i(t=>{const e=u(t);return{...A(e),showId:o(e,"showId"),episodeNumber:f(e,"episodeNumber")}}),L=()=>i(t=>{const e=u(t);return{...A(e),showId:o(e,"showId"),episodeNumbers:l(e,"episodeNumbers",!0)}}),K=()=>i(t=>{const e=u(t);return{showId:o(e,"showId"),episodeNumber:f(e,"episodeNumber")}}),V=()=>i(t=>{const e=u(t),n={showId:o(e,"showId")};return e.episodeNumbers!==void 0&&(n.episodeNumbers=l(e,"episodeNumbers",!1)),n}),z=()=>i(t=>{if(!Array.isArray(t))throw new Error("items must be an array");return t.map(e=>{const n=u(e),r={};return typeof n.id=="number"&&(r.id=n.id),n.showId!==void 0&&(r.showId=String(n.showId)),n.episodeNumber!==void 0&&(r.episodeNumber=String(n.episodeNumber)),r})}),G=()=>i(t=>{if(typeof t!="object"||t===null)throw new Error("Expected an object");return{key:v(t),value:t.value}}),H=()=>i(t=>{const e=u(t),n={showId:o(e,"showId"),episodeNumber:f(e,"episodeNumber")},r=a(e,"currentTime");r!==void 0&&(n.currentTime=r);const d=a(e,"duration");d!==void 0&&(n.duration=d);const p=s(e,"showName");p!==void 0&&(n.showName=p);const g=s(e,"showThumbnail");g!==void 0&&(n.showThumbnail=g);const c=s(e,"nativeName");c!==void 0&&(n.nativeName=c);const h=s(e,"englishName");h!==void 0&&(n.englishName=h);const m=e.genres;(typeof m=="string"||Array.isArray(m))&&(n.genres=(Array.isArray(m)?m:[m]).map(String));const I=a(e,"popularityScore");I!==void 0&&(n.popularityScore=I);const w=s(e,"type");w!==void 0&&(n.type=w);const y=s(e,"status");y!==void 0&&(n.status=y);const b=a(e,"episodeCount");b!==void 0&&(n.episodeCount=b);const N=k(e,"isPlaying");N!==void 0&&(n.isPlaying=N);const x=s(e,"sessionId");x!==void 0&&(n.sessionId=x);const R=k(e,"isAdult");return R!==void 0&&(n.isAdult=R),n}),J=()=>i(t=>{const e=u(t);return{showId:o(e,"showId"),episodeNumber:f(e,"episodeNumber")}});export{Q as badRequest,i as defineSchema,C as episodeProgressInput,T as failed,B as idInput,U as idsInput,O as keyInput,J as notificationDismissInput,k as optBool,a as optNum,s as optStr,H as progressUpdateInput,F as queueAddInput,L as queueBatchInput,K as queueRemoveInput,V as queueRemoveManyInput,z as queueReorderInput,j as readCount,M as readPageLimit,P as reqId,u as reqObj,o as reqStr,l as reqStrArray,G as setSettingInput,W as showIdInput,q as strOrNull,D as watchlistAddInput,_ as watchlistBatchStatusInput,$ as watchlistStatusInput};
@@ -1,25 +1 @@
1
- import { LRUCache } from 'lru-cache';
2
- export class AppCache {
3
- cache;
4
- constructor(options = {}) {
5
- this.cache = new LRUCache({
6
- max: options.maxKeys ?? 5000,
7
- ttl: (options.ttlSeconds ?? 3600) * 1000,
8
- });
9
- }
10
- get(key) {
11
- return this.cache.get(key);
12
- }
13
- set(key, value, ttlSeconds) {
14
- const entry = value;
15
- if (ttlSeconds === undefined) {
16
- this.cache.set(key, entry);
17
- }
18
- else {
19
- this.cache.set(key, entry, { ttl: ttlSeconds * 1000 });
20
- }
21
- }
22
- delete(key) {
23
- this.cache.delete(key);
24
- }
25
- }
1
+ import{LRUCache as s}from"lru-cache";class r{cache;constructor(e={}){this.cache=new s({max:e.maxKeys??5e3,ttl:(e.ttlSeconds??3600)*1e3})}get(e){return this.cache.get(e)}set(e,n,t){const c=n;t===void 0?this.cache.set(e,c):this.cache.set(e,c,{ttl:t*1e3})}delete(e){this.cache.delete(e)}}export{r as AppCache};
@@ -1,13 +1 @@
1
- export function sanitizeCfClearance(raw) {
2
- if (!raw)
3
- return '';
4
- let s = String(raw).trim();
5
- s = s.replace(/^cf_clearance/i, '');
6
- s = s.replace(/^[:=]\s*/, '');
7
- s = s.replace(/["']/g, '').trim();
8
- return s;
9
- }
10
- export function buildCfClearanceCookie(raw) {
11
- const c = sanitizeCfClearance(raw);
12
- return c ? `cf_clearance=${c}` : '';
13
- }
1
+ function r(n){if(!n)return"";let e=String(n).trim();return e=e.replace(/^cf_clearance/i,""),e=e.replace(/^[:=]\s*/,""),e=e.replace(/["']/g,"").trim(),e}function i(n){const e=r(n);return e?`cf_clearance=${e}`:""}export{i as buildCfClearanceCookie,r as sanitizeCfClearance};
@@ -1,17 +1,5 @@
1
- export const dbAll = (db, sql, params = []) => {
2
- return db.all(sql, params);
3
- };
4
- export const dbGet = (db, sql, params = []) => {
5
- return db.get(sql, params);
6
- };
7
- export const dbRun = (db, sql, params = []) => {
8
- db.run(sql, params);
9
- };
10
- export async function adultNonListIds(db, progressTable, libraryTable, keyCol, adultPred) {
11
- const rows = await dbAll(db, `SELECT DISTINCT p."${keyCol}" as id
12
- FROM "${progressTable}" p
13
- LEFT JOIN "${libraryTable}" l ON l.id = p."${keyCol}"
14
- WHERE ${adultPred}
15
- AND l.id IS NULL`);
16
- return rows.map((r) => r.id);
17
- }
1
+ const e=(r,n,a=[])=>r.all(n,a),u=(r,n,a=[])=>r.get(n,a),b=(r,n,a=[])=>{r.run(n,a)};async function d(r,n,a,t,s){return(await e(r,`SELECT DISTINCT p."${t}" as id
2
+ FROM "${n}" p
3
+ LEFT JOIN "${a}" l ON l.id = p."${t}"
4
+ WHERE ${s}
5
+ AND l.id IS NULL`)).map(i=>i.id)}export{d as adultNonListIds,e as dbAll,u as dbGet,b as dbRun};
@@ -1,77 +1,5 @@
1
- import fs from 'fs';
2
- import { CONFIG } from '../config.js';
3
- const lockPath = `${CONFIG.ENV_PATH}.lock`;
4
- async function acquireLock() {
5
- let handle;
6
- let attempts = 0;
7
- while (!handle) {
8
- try {
9
- handle = await fs.promises.open(lockPath, 'wx');
10
- }
11
- catch {
12
- if (++attempts >= 100) {
13
- fs.promises.unlink(lockPath).catch(() => { });
14
- throw new Error('Failed to acquire lock on .env file - another process may be holding it');
15
- }
16
- await new Promise((r) => setTimeout(r, 100));
17
- }
18
- }
19
- return handle;
20
- }
21
- function releaseLock(handle) {
22
- handle.close().catch(() => { });
23
- fs.promises.unlink(lockPath).catch(() => { });
24
- }
25
- export async function updateEnvFile(updates) {
26
- const lockHandle = await acquireLock();
27
- try {
28
- const envPath = CONFIG.ENV_PATH;
29
- let envContent = '';
30
- if (fs.existsSync(envPath)) {
31
- envContent = fs.readFileSync(envPath, 'utf8');
32
- }
33
- const lines = envContent.split('\n');
34
- const newLines = [...lines];
35
- Object.entries(updates).forEach(([rawKey, rawValue]) => {
36
- const key = rawKey.trim();
37
- if (!/^[A-Z0-9_]+$/i.test(key)) {
38
- throw new Error(`Invalid environment variable key: ${key}`);
39
- }
40
- const value = String(rawValue ?? '').replace(/[\r\n]/g, '');
41
- let found = false;
42
- for (let i = 0; i < newLines.length; i++) {
43
- const line = newLines[i];
44
- if (line && line.startsWith(`${key}=`)) {
45
- if (value === '') {
46
- newLines.splice(i, 1);
47
- continue;
48
- }
49
- else {
50
- newLines[i] = `${key}=${value}`;
51
- }
52
- found = true;
53
- break;
54
- }
55
- }
56
- if (!found && value !== '') {
57
- newLines.push(`${key}=${value}`);
58
- }
59
- });
60
- const finalContent = newLines
61
- .join('\n')
62
- .replace(/\n{2,}/g, '\n')
63
- .trim() + '\n';
64
- fs.writeFileSync(envPath, finalContent);
65
- Object.entries(updates).forEach(([key, value]) => {
66
- if (value === '') {
67
- delete process.env[key];
68
- }
69
- else {
70
- process.env[key] = value;
71
- }
72
- });
73
- }
74
- finally {
75
- releaseLock(lockHandle);
76
- }
77
- }
1
+ import t from"fs";import{CONFIG as u}from"../config.js";const f=`${u.ENV_PATH}.lock`;async function w(){let e,a=0;for(;!e;)try{e=await t.promises.open(f,"wx")}catch{if(++a>=100)throw t.promises.unlink(f).catch(()=>{}),new Error("Failed to acquire lock on .env file - another process may be holding it");await new Promise(i=>setTimeout(i,100))}return e}function k(e){e.close().catch(()=>{}),t.promises.unlink(f).catch(()=>{})}async function $(e){const a=await w();try{const i=u.ENV_PATH;let p="";t.existsSync(i)&&(p=t.readFileSync(i,"utf8"));const n=[...p.split(`
2
+ `)];Object.entries(e).forEach(([r,c])=>{const s=r.trim();if(!/^[A-Z0-9_]+$/i.test(s))throw new Error(`Invalid environment variable key: ${s}`);const l=String(c??"").replace(/[\r\n]/g,"");let m=!1;for(let o=0;o<n.length;o++){const h=n[o];if(h&&h.startsWith(`${s}=`)){if(l===""){n.splice(o,1);continue}else n[o]=`${s}=${l}`;m=!0;break}}!m&&l!==""&&n.push(`${s}=${l}`)});const d=n.join(`
3
+ `).replace(/\n{2,}/g,`
4
+ `).trim()+`
5
+ `;t.writeFileSync(i,d),Object.entries(e).forEach(([r,c])=>{c===""?delete process.env[r]:process.env[r]=c})}finally{k(a)}}export{$ as updateEnvFile};
@@ -1,82 +1 @@
1
- import { brotliDecompressSync, gunzipSync, inflateSync } from 'node:zlib';
2
- export class HttpError extends Error {
3
- status;
4
- constructor(status, message) {
5
- super(message || `Upstream request failed with status ${status}`);
6
- this.name = 'HttpError';
7
- this.status = status;
8
- }
9
- }
10
- export function decodeMaybeGzip(bytes) {
11
- if (bytes.length > 2 && bytes[0] === 0x1f && bytes[1] === 0x8b)
12
- return gunzipSync(bytes);
13
- return bytes;
14
- }
15
- export async function parseJsonBody(res) {
16
- const buf = Buffer.from(await res.arrayBuffer());
17
- const text = decodeMaybeGzip(buf).toString('utf8');
18
- try {
19
- return JSON.parse(text);
20
- }
21
- catch (err) {
22
- const attempts = [() => brotliDecompressSync(buf), () => inflateSync(buf)];
23
- for (const attempt of attempts) {
24
- try {
25
- return JSON.parse(attempt().toString('utf8'));
26
- }
27
- catch {
28
- // ignore
29
- }
30
- }
31
- throw err;
32
- }
33
- }
34
- export function linkAbort(signal, abort) {
35
- signal.addEventListener('abort', () => abort.abort(), { once: true });
36
- }
37
- export function setProxyHeaders(out, get) {
38
- for (const name of ['content-length', 'content-range', 'accept-ranges']) {
39
- const raw = get(name);
40
- const value = Array.isArray(raw) ? raw[0] : raw;
41
- if (value)
42
- out.set(name, value);
43
- }
44
- out.set('Access-Control-Allow-Origin', '*');
45
- }
46
- export function isAbortError(err) {
47
- if (!err || typeof err !== 'object')
48
- return false;
49
- const name = err.name;
50
- return name === 'AbortError' || name === 'TimeoutError';
51
- }
52
- const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
53
- function isRetryableStatus(status) {
54
- return status === 408 || status === 429 || status >= 500;
55
- }
56
- export async function fetchWithRetry(input, init = {}, options = {}) {
57
- const { retries = 0, baseDelayMs = 100, timeoutMs = 30000, signal } = options;
58
- let attempt = 0;
59
- for (;;) {
60
- const timeoutSignal = AbortSignal.timeout(timeoutMs);
61
- const combined = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
62
- try {
63
- const response = await fetch(input, { ...init, signal: combined });
64
- if (!isRetryableStatus(response.status) || attempt >= retries)
65
- return response;
66
- try {
67
- await response.body?.cancel();
68
- }
69
- catch {
70
- // ignore
71
- }
72
- }
73
- catch (err) {
74
- if (signal?.aborted || attempt >= retries)
75
- throw err;
76
- if (!isAbortError(err) && !(err instanceof TypeError))
77
- throw err;
78
- }
79
- attempt += 1;
80
- await sleep(Math.min(baseDelayMs * 2 ** (attempt - 1), 3000));
81
- }
82
- }
1
+ import{brotliDecompressSync as l,gunzipSync as m,inflateSync as p}from"node:zlib";class w extends Error{status;constructor(r,e){super(e||`Upstream request failed with status ${r}`),this.name="HttpError",this.status=r}}function b(t){return t.length>2&&t[0]===31&&t[1]===139?m(t):t}async function x(t){const r=Buffer.from(await t.arrayBuffer()),e=b(r).toString("utf8");try{return JSON.parse(e)}catch(n){const s=[()=>l(r),()=>p(r)];for(const i of s)try{return JSON.parse(i().toString("utf8"))}catch{}throw n}}function A(t,r){t.addEventListener("abort",()=>r.abort(),{once:!0})}function S(t,r){for(const e of["content-length","content-range","accept-ranges"]){const n=r(e),s=Array.isArray(n)?n[0]:n;s&&t.set(e,s)}t.set("Access-Control-Allow-Origin","*")}function g(t){if(!t||typeof t!="object")return!1;const r=t.name;return r==="AbortError"||r==="TimeoutError"}const y=t=>new Promise(r=>setTimeout(r,t));function h(t){return t===408||t===429||t>=500}async function E(t,r={},e={}){const{retries:n=0,baseDelayMs:s=100,timeoutMs:i=3e4,signal:c}=e;let a=0;for(;;){const u=AbortSignal.timeout(i),f=c?AbortSignal.any([c,u]):u;try{const o=await fetch(t,{...r,signal:f});if(!h(o.status)||a>=n)return o;try{await o.body?.cancel()}catch{}}catch(o){if(c?.aborted||a>=n||!g(o)&&!(o instanceof TypeError))throw o}a+=1,await y(Math.min(s*2**(a-1),3e3))}}export{w as HttpError,b as decodeMaybeGzip,E as fetchWithRetry,g as isAbortError,A as linkAbort,x as parseJsonBody,S as setProxyHeaders};
@@ -1,2 +1 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
2
- export const requestContext = new AsyncLocalStorage();
1
+ import{AsyncLocalStorage as t}from"node:async_hooks";const r=new t;export{r as requestContext};
@@ -1,76 +1 @@
1
- import { CONFIG } from '../config.js';
2
- export function isSafeExternalUrl(rawUrl) {
3
- if (typeof rawUrl !== 'string' || !rawUrl.trim()) {
4
- return { safe: false, error: 'URL required' };
5
- }
6
- let parsed;
7
- try {
8
- parsed = new URL(rawUrl.trim());
9
- }
10
- catch {
11
- return { safe: false, error: 'Invalid URL format' };
12
- }
13
- if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
14
- return { safe: false, error: 'Only http and https protocols are supported' };
15
- }
16
- const hostname = parsed.hostname.toLowerCase();
17
- if (hostname === 'localhost' ||
18
- hostname === '127.0.0.1' ||
19
- hostname === '::1' ||
20
- hostname === '0.0.0.0' ||
21
- hostname.endsWith('.localhost') ||
22
- hostname.endsWith('.local') ||
23
- hostname.endsWith('.internal')) {
24
- return { safe: false, error: 'Access to loopback/local addresses is forbidden' };
25
- }
26
- const ipv4Match = hostname.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
27
- if (ipv4Match) {
28
- const o1 = Number(ipv4Match[1]);
29
- const o2 = Number(ipv4Match[2]);
30
- const o3 = Number(ipv4Match[3]);
31
- const o4 = Number(ipv4Match[4]);
32
- if ([o1, o2, o3, o4].some((o) => o < 0 || o > 255)) {
33
- return { safe: false, error: 'Invalid IP address format' };
34
- }
35
- if (o1 === 0 ||
36
- o1 === 10 ||
37
- o1 === 127 ||
38
- (o1 === 172 && o2 >= 16 && o2 <= 31) ||
39
- (o1 === 192 && o2 === 168) ||
40
- (o1 === 169 && o2 === 254)) {
41
- return { safe: false, error: 'Access to private or link-local IP addresses is forbidden' };
42
- }
43
- }
44
- if (hostname.startsWith('[') || hostname.includes(':')) {
45
- const cleanIpv6 = hostname.replace(/^\[|\]$/g, '').toLowerCase();
46
- if (cleanIpv6 === '::1' ||
47
- cleanIpv6 === '::' ||
48
- cleanIpv6.startsWith('fe80:') ||
49
- cleanIpv6.startsWith('fc00:') ||
50
- cleanIpv6.startsWith('fd00:') ||
51
- cleanIpv6.startsWith('::ffff:127.') ||
52
- cleanIpv6.startsWith('::ffff:192.168.') ||
53
- cleanIpv6.startsWith('::ffff:10.') ||
54
- cleanIpv6.startsWith('::ffff:172.') ||
55
- cleanIpv6.startsWith('::ffff:169.254.')) {
56
- return { safe: false, error: 'Access to private or link-local IPv6 addresses is forbidden' };
57
- }
58
- }
59
- return { safe: true, url: parsed };
60
- }
61
- export function isAllowedOrigin(origin) {
62
- if (!origin)
63
- return true; // Non-browser clients, same-origin, or mobile webview
64
- try {
65
- const parsed = new URL(origin);
66
- const host = parsed.hostname.toLowerCase();
67
- const isLocal = host === 'localhost' || host === '127.0.0.1' || host === '::1';
68
- if (CONFIG.HOST === '0.0.0.0') {
69
- return true;
70
- }
71
- return isLocal;
72
- }
73
- catch {
74
- return false;
75
- }
76
- }
1
+ import{CONFIG as l}from"../config.js";function d(s){if(typeof s!="string"||!s.trim())return{safe:!1,error:"URL required"};let o;try{o=new URL(s.trim())}catch{return{safe:!1,error:"Invalid URL format"}}if(o.protocol!=="http:"&&o.protocol!=="https:")return{safe:!1,error:"Only http and https protocols are supported"};const r=o.hostname.toLowerCase();if(r==="localhost"||r==="127.0.0.1"||r==="::1"||r==="0.0.0.0"||r.endsWith(".localhost")||r.endsWith(".local")||r.endsWith(".internal"))return{safe:!1,error:"Access to loopback/local addresses is forbidden"};const e=r.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);if(e){const t=Number(e[1]),a=Number(e[2]),n=Number(e[3]),i=Number(e[4]);if([t,a,n,i].some(f=>f<0||f>255))return{safe:!1,error:"Invalid IP address format"};if(t===0||t===10||t===127||t===172&&a>=16&&a<=31||t===192&&a===168||t===169&&a===254)return{safe:!1,error:"Access to private or link-local IP addresses is forbidden"}}if(r.startsWith("[")||r.includes(":")){const t=r.replace(/^\[|\]$/g,"").toLowerCase();if(t==="::1"||t==="::"||t.startsWith("fe80:")||t.startsWith("fc00:")||t.startsWith("fd00:")||t.startsWith("::ffff:127.")||t.startsWith("::ffff:192.168.")||t.startsWith("::ffff:10.")||t.startsWith("::ffff:172.")||t.startsWith("::ffff:169.254."))return{safe:!1,error:"Access to private or link-local IPv6 addresses is forbidden"}}return{safe:!0,url:o}}function u(s){if(!s)return!0;try{const r=new URL(s).hostname.toLowerCase(),e=r==="localhost"||r==="127.0.0.1"||r==="::1";return l.HOST==="0.0.0.0"?!0:e}catch{return!1}}export{u as isAllowedOrigin,d as isSafeExternalUrl};
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dango-server",
3
3
  "private": true,
4
- "version": "3.6.5",
4
+ "version": "3.6.8",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=22.12.0"
@@ -9,7 +9,7 @@
9
9
  "scripts": {
10
10
  "start": "node --max-old-space-size=256 dist/server.js",
11
11
  "dev": "tsx watch src/server.ts --dev",
12
- "build": "tsc",
12
+ "build": "tsc --noEmit && node build.mjs",
13
13
  "format": "prettier --write .",
14
14
  "lint": "eslint ."
15
15
  },
@@ -36,6 +36,7 @@
36
36
  "@types/jsdom": "^30.0.0",
37
37
  "@types/node": "^22.5.0",
38
38
  "@types/ws": "^8.5.10",
39
+ "esbuild": "0.28.2",
39
40
  "pino-pretty": "^13.1.1",
40
41
  "tsx": "^4.23.13"
41
42
  }