@serifpersia/dango 3.3.4

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 (190) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +258 -0
  3. package/client/dist/assets/AnimeCard-B5A20jAI.js +1 -0
  4. package/client/dist/assets/AnimeInfo-0huPAq0O.js +1 -0
  5. package/client/dist/assets/AnimeInfo-BFWtHlPC.css +1 -0
  6. package/client/dist/assets/AnimeInfoPage-Dqr5QH6R.js +2 -0
  7. package/client/dist/assets/AnimeMetaDetails-BPvv5xIl.js +1 -0
  8. package/client/dist/assets/AnimeMetaDetails-BwNQnRCd.css +1 -0
  9. package/client/dist/assets/Asmr-B6ZiqkmU.js +2 -0
  10. package/client/dist/assets/Asmr-Cq2ua6Zk.css +1 -0
  11. package/client/dist/assets/Asmr.module-ChUXDZKx.js +1 -0
  12. package/client/dist/assets/AsmrResetProgressModal-Cqsoo3XC.js +1 -0
  13. package/client/dist/assets/AvSyncCalibrator-C1_iBSyL.css +1 -0
  14. package/client/dist/assets/AvSyncCalibrator-C8ZoIwMY.js +10 -0
  15. package/client/dist/assets/ErrorMessage-CXoRbrLN.css +1 -0
  16. package/client/dist/assets/ErrorMessage-z4f4WGgP.js +1 -0
  17. package/client/dist/assets/Home-DtRtTuI9.css +1 -0
  18. package/client/dist/assets/Home-thJhPfs1.js +1 -0
  19. package/client/dist/assets/Insights-DKtoVe6L.css +1 -0
  20. package/client/dist/assets/Insights-ZlgPrzGD.js +1 -0
  21. package/client/dist/assets/LibraryListPage-CCiee-Ot.js +1 -0
  22. package/client/dist/assets/ListeningList-ctAz_6Zi.js +1 -0
  23. package/client/dist/assets/Manga-BI9WMTx5.js +1 -0
  24. package/client/dist/assets/Manga-Dp_-l1Pw.css +1 -0
  25. package/client/dist/assets/Manga.module-BcfOdsNP.js +1 -0
  26. package/client/dist/assets/MangaInfoPage-DcrEr7Sk.js +1 -0
  27. package/client/dist/assets/MangaReadPage-SYg63m48.js +1 -0
  28. package/client/dist/assets/MangaResetProgressModal-DX-1PS4h.js +1 -0
  29. package/client/dist/assets/Map-CXFqXmsZ.css +1 -0
  30. package/client/dist/assets/Map-Dj51g5zL.js +1 -0
  31. package/client/dist/assets/Mature-DJmUUtlF.css +1 -0
  32. package/client/dist/assets/Mature-Dqh_9SA3.js +1 -0
  33. package/client/dist/assets/MediaCard-Be1ST9oj.js +1 -0
  34. package/client/dist/assets/MediaCard-DB6Yjgky.css +1 -0
  35. package/client/dist/assets/Music-BGH_0voi.css +1 -0
  36. package/client/dist/assets/Music-zsPwT8_5.js +1 -0
  37. package/client/dist/assets/OptionListMenu-Drc_vybW.js +1 -0
  38. package/client/dist/assets/Player-B036Ic3m.css +1 -0
  39. package/client/dist/assets/Player-BEtAYps2.js +56 -0
  40. package/client/dist/assets/PlayerRedirect-D70t8VUW.js +1 -0
  41. package/client/dist/assets/PlayerSettings-Bj6SsmQ2.css +1 -0
  42. package/client/dist/assets/PlayerSettings-Cwoji-P7.js +1 -0
  43. package/client/dist/assets/QueueOptionsButton-B6BMq66e.js +1 -0
  44. package/client/dist/assets/QueueOptionsButton-CM15uQn8.css +1 -0
  45. package/client/dist/assets/QueueRail-BUYEjdaq.css +1 -0
  46. package/client/dist/assets/QueueRail-VxpUDBSm.js +1 -0
  47. package/client/dist/assets/Radio-B2Q_Egj7.js +1 -0
  48. package/client/dist/assets/Radio-CCHNFN7R.css +1 -0
  49. package/client/dist/assets/Radio.module-DGSZLpKM.js +1 -0
  50. package/client/dist/assets/ReadingList-O0QPwXu7.js +1 -0
  51. package/client/dist/assets/ResetProgressModal-CR_ovLAu.js +1 -0
  52. package/client/dist/assets/Search-C8e2nlDD.js +1 -0
  53. package/client/dist/assets/Search-Cs45oAwI.css +1 -0
  54. package/client/dist/assets/Search.module-CMEai-B-.js +1 -0
  55. package/client/dist/assets/Settings-AuFxQcJF.css +1 -0
  56. package/client/dist/assets/Settings-BgPWBl2V.js +21 -0
  57. package/client/dist/assets/SkeletonGrid-CtHKKMLj.js +1 -0
  58. package/client/dist/assets/SkeletonGrid-DhPYefXr.css +1 -0
  59. package/client/dist/assets/SynopsisText-BcpwT5iw.js +1 -0
  60. package/client/dist/assets/SynopsisText-CYv2W3yR.css +1 -0
  61. package/client/dist/assets/Trackers-Bfoiyuzy.js +2 -0
  62. package/client/dist/assets/Trackers-D5XBOCDp.css +1 -0
  63. package/client/dist/assets/Tv-Ca0fCdko.js +3 -0
  64. package/client/dist/assets/Tv-CuEl3Iol.css +1 -0
  65. package/client/dist/assets/TvInfo-Bu36xJyO.css +1 -0
  66. package/client/dist/assets/TvInfo-D_scgw_h.js +1 -0
  67. package/client/dist/assets/TvPopup-YTI7dEbJ.js +1 -0
  68. package/client/dist/assets/TvSearch-CxU33UOf.js +1 -0
  69. package/client/dist/assets/TvWatchlist-B4X1fWEH.js +1 -0
  70. package/client/dist/assets/VirtualKeyboard-6ZNTym4R.js +1 -0
  71. package/client/dist/assets/VirtualKeyboard-KIPc5ZGH.css +1 -0
  72. package/client/dist/assets/Watchlist-BiDEU0iV.css +1 -0
  73. package/client/dist/assets/Watchlist-P6YMZUdF.js +1 -0
  74. package/client/dist/assets/Watchlist.module-XGtVQr1d.js +1 -0
  75. package/client/dist/assets/hls-BOLXTnjL.js +42 -0
  76. package/client/dist/assets/hls-C4cG9wQw.js +1 -0
  77. package/client/dist/assets/index-BOKlZEHq.js +30354 -0
  78. package/client/dist/assets/index-CUm1meqr.css +1 -0
  79. package/client/dist/assets/index-IZgh1beL.js +178 -0
  80. package/client/dist/assets/useAnimeInfoData-DeuwDSnI.js +1 -0
  81. package/client/dist/assets/useAsmr-CbMVRjmV.js +1 -0
  82. package/client/dist/assets/useAsmrPopup-D91Q3CBg.js +1 -0
  83. package/client/dist/assets/useIsMobile-Dm_b6Ob3.js +1 -0
  84. package/client/dist/assets/useMangaLibrary-BPvFb1lU.js +1 -0
  85. package/client/dist/assets/useMangaPopup-BOL2mQJs.js +1 -0
  86. package/client/dist/assets/useMatureConsent-CT7-KfzG.js +1 -0
  87. package/client/dist/assets/useProviders-BsRGONMA.js +1 -0
  88. package/client/dist/assets/useTv-D37MfTHA.js +1 -0
  89. package/client/dist/assets/useTvLibrary-Cwxgy_Bg.js +1 -0
  90. package/client/dist/assets/vendor-core-8R2fFbyN.js +1 -0
  91. package/client/dist/assets/vendor-icons-CDMuNDeL.js +1 -0
  92. package/client/dist/assets/vendor-query-BXRECSI6.js +1 -0
  93. package/client/dist/assets/vendor-ui-CUDCvRS_.js +1 -0
  94. package/client/dist/av-sync-test.mp4 +0 -0
  95. package/client/dist/favicon.ico +0 -0
  96. package/client/dist/index.html +46 -0
  97. package/client/dist/logo-mochi.png +0 -0
  98. package/client/dist/logo-sakura.png +0 -0
  99. package/client/dist/logo.png +0 -0
  100. package/client/dist/placeholder.svg +4 -0
  101. package/client/dist/robots.txt +3 -0
  102. package/orchestrator.js +534 -0
  103. package/package.json +85 -0
  104. package/server/dist/app-auth.js +177 -0
  105. package/server/dist/config.js +175 -0
  106. package/server/dist/controllers/asmr-library.controller.js +288 -0
  107. package/server/dist/controllers/auth.controller.js +225 -0
  108. package/server/dist/controllers/data.controller.js +766 -0
  109. package/server/dist/controllers/insights.controller.js +198 -0
  110. package/server/dist/controllers/manga-library.controller.js +296 -0
  111. package/server/dist/controllers/music.controller.js +698 -0
  112. package/server/dist/controllers/proxy.controller.js +820 -0
  113. package/server/dist/controllers/settings.controller.js +609 -0
  114. package/server/dist/controllers/tv-library.controller.js +310 -0
  115. package/server/dist/controllers/watchlist.controller.js +1015 -0
  116. package/server/dist/db.js +145 -0
  117. package/server/dist/discord-gateway.js +391 -0
  118. package/server/dist/discord-rpc.js +427 -0
  119. package/server/dist/github-sync.js +512 -0
  120. package/server/dist/google.js +748 -0
  121. package/server/dist/lib/anilist.js +1549 -0
  122. package/server/dist/lib/anischedule.js +194 -0
  123. package/server/dist/lib/discord-roles-sync.service.js +130 -0
  124. package/server/dist/lib/ipc.js +24 -0
  125. package/server/dist/lib/kitsu.js +544 -0
  126. package/server/dist/lib/mal.js +934 -0
  127. package/server/dist/lib/migration.js +329 -0
  128. package/server/dist/lib/offline-db.js +401 -0
  129. package/server/dist/lib/temp-ids.js +3 -0
  130. package/server/dist/lib/tmdb.js +72 -0
  131. package/server/dist/lib/tracker/anilist-tracker.js +245 -0
  132. package/server/dist/lib/tracker/sync.service.js +496 -0
  133. package/server/dist/lib/ytmusic.js +125 -0
  134. package/server/dist/logger.js +16 -0
  135. package/server/dist/providers/manga/manga.types.js +7 -0
  136. package/server/dist/providers/provider.interface.js +1 -0
  137. package/server/dist/providers/remote-loader.js +574 -0
  138. package/server/dist/providers/remote-types.js +117 -0
  139. package/server/dist/providers/title-matching.js +225 -0
  140. package/server/dist/providers/tv.types.js +1 -0
  141. package/server/dist/rclone.js +120 -0
  142. package/server/dist/repositories/asmr.repository.js +123 -0
  143. package/server/dist/repositories/insights.repository.js +85 -0
  144. package/server/dist/repositories/library.repository.js +108 -0
  145. package/server/dist/repositories/mal-cache.repository.js +55 -0
  146. package/server/dist/repositories/manga.repository.js +138 -0
  147. package/server/dist/repositories/notifications.repository.js +41 -0
  148. package/server/dist/repositories/queue.repository.js +61 -0
  149. package/server/dist/repositories/settings.repository.js +12 -0
  150. package/server/dist/repositories/shows-meta.repository.js +42 -0
  151. package/server/dist/repositories/temp-show-ids.repository.js +51 -0
  152. package/server/dist/repositories/tv.repository.js +151 -0
  153. package/server/dist/repositories/watched-episodes.repository.js +52 -0
  154. package/server/dist/repositories/watchlist.repository.js +71 -0
  155. package/server/dist/request-logger.js +16 -0
  156. package/server/dist/routes/asmr-library.routes.js +23 -0
  157. package/server/dist/routes/asmr.routes.js +81 -0
  158. package/server/dist/routes/auth.routes.js +25 -0
  159. package/server/dist/routes/data.routes.js +47 -0
  160. package/server/dist/routes/discord-gateway.routes.js +51 -0
  161. package/server/dist/routes/insights.routes.js +34 -0
  162. package/server/dist/routes/lan-auth.routes.js +46 -0
  163. package/server/dist/routes/manga-library.routes.js +23 -0
  164. package/server/dist/routes/manga.routes.js +141 -0
  165. package/server/dist/routes/music.routes.js +20 -0
  166. package/server/dist/routes/providers.routes.js +17 -0
  167. package/server/dist/routes/proxy.routes.js +12 -0
  168. package/server/dist/routes/radio.routes.js +138 -0
  169. package/server/dist/routes/settings.routes.js +24 -0
  170. package/server/dist/routes/tracker.routes.js +164 -0
  171. package/server/dist/routes/translate.routes.js +88 -0
  172. package/server/dist/routes/tv-library.routes.js +23 -0
  173. package/server/dist/routes/tv.routes.js +629 -0
  174. package/server/dist/routes/watchlist.routes.js +341 -0
  175. package/server/dist/scripts/backfill-episode-duration.js +42 -0
  176. package/server/dist/server.js +529 -0
  177. package/server/dist/shipped-defaults.js +7 -0
  178. package/server/dist/sync-config.js +23 -0
  179. package/server/dist/sync-payload.js +91 -0
  180. package/server/dist/sync.js +1128 -0
  181. package/server/dist/utils/cache.utils.js +25 -0
  182. package/server/dist/utils/cookie.utils.js +13 -0
  183. package/server/dist/utils/db-utils.js +9 -0
  184. package/server/dist/utils/env.utils.js +77 -0
  185. package/server/dist/utils/http.utils.js +70 -0
  186. package/server/dist/utils/machine-id.js +40 -0
  187. package/server/dist/utils/megaplay.utils.js +41 -0
  188. package/server/dist/utils/request-context.js +2 -0
  189. package/server/dist/utils/security.utils.js +94 -0
  190. package/server/package.json +44 -0
@@ -0,0 +1,245 @@
1
+ import logger from '../../logger.js';
2
+ import { waitForAnilistSlot, applyRateLimitHeaders } from '../anilist.js';
3
+ import { parseJsonBody } from '../../utils/http.utils.js';
4
+ const ANILIST_GRAPHQL_ENDPOINT = 'https://graphql.anilist.co';
5
+ export const ANILIST_TO_DANGO_STATUS = {
6
+ CURRENT: 'Watching',
7
+ COMPLETED: 'Completed',
8
+ PAUSED: 'On-Hold',
9
+ DROPPED: 'Dropped',
10
+ PLANNING: 'Planned',
11
+ };
12
+ export const DANGO_TO_ANILIST_STATUS = {
13
+ Watching: 'CURRENT',
14
+ Completed: 'COMPLETED',
15
+ 'On-Hold': 'PAUSED',
16
+ Dropped: 'DROPPED',
17
+ Planned: 'PLANNING',
18
+ };
19
+ export async function exchangeAuthorizationCode() {
20
+ throw new Error('Authorization Code flow removed — use Implicit Grant (response_type=token) instead.');
21
+ }
22
+ export class AniListTracker {
23
+ token;
24
+ constructor(token) {
25
+ this.token = token;
26
+ }
27
+ sleep(ms) {
28
+ return new Promise((resolve) => setTimeout(resolve, ms));
29
+ }
30
+ async request(query, variables = {}, options = {}) {
31
+ const retries = options.retries ?? 3;
32
+ await waitForAnilistSlot();
33
+ const headers = {
34
+ 'Content-Type': 'application/json',
35
+ Accept: 'application/json',
36
+ };
37
+ if (this.token)
38
+ headers.Authorization = `Bearer ${this.token}`;
39
+ let response;
40
+ try {
41
+ response = await fetch(ANILIST_GRAPHQL_ENDPOINT, {
42
+ method: 'POST',
43
+ headers,
44
+ body: JSON.stringify({ query, variables }),
45
+ });
46
+ }
47
+ catch (err) {
48
+ logger.error({ err }, '[AniList Tracker] Network error');
49
+ throw new Error('Could not reach AniList. Please try again later.', { cause: err });
50
+ }
51
+ applyRateLimitHeaders(response.headers);
52
+ if (response.status === 429) {
53
+ if (retries <= 0)
54
+ throw new Error('AniList rate limit reached, try again later');
55
+ const retryHeader = response.headers.get('retry-after');
56
+ const waitSeconds = retryHeader ? Number.parseInt(retryHeader, 10) || 60 : 60;
57
+ logger.warn({ waitSeconds }, '[AniList Tracker] Rate limited, waiting before retry');
58
+ await this.sleep(Math.min(waitSeconds, 60) * 1000);
59
+ return this.request(query, variables, { retries: retries - 1 });
60
+ }
61
+ if (!response.ok && response.status === 401) {
62
+ throw new Error('AniList token is invalid or expired. Please reconnect your account.');
63
+ }
64
+ if (!response.ok) {
65
+ throw new Error(`AniList request failed with status ${response.status}`);
66
+ }
67
+ const json = await parseJsonBody(response);
68
+ if (json.errors && !json.data) {
69
+ throw new Error(json.errors.map((e) => e.message).join(', '));
70
+ }
71
+ return json.data;
72
+ }
73
+ async getViewer() {
74
+ const query = `
75
+ query {
76
+ Viewer {
77
+ id
78
+ name
79
+ avatar { medium }
80
+ }
81
+ }
82
+ `;
83
+ const data = await this.request(query);
84
+ return {
85
+ id: data.Viewer.id,
86
+ name: data.Viewer.name,
87
+ avatar: data.Viewer.avatar?.medium,
88
+ };
89
+ }
90
+ async fetchUserAnimeList(userIdOrName) {
91
+ const query = `
92
+ query ($userId: Int, $userName: String) {
93
+ MediaListCollection(userId: $userId, userName: $userName, type: ANIME) {
94
+ lists {
95
+ entries {
96
+ id
97
+ mediaId
98
+ status
99
+ progress
100
+ score(format: POINT_10)
101
+ updatedAt
102
+ media {
103
+ id
104
+ idMal
105
+ episodes
106
+ duration
107
+ title { romaji english native }
108
+ coverImage { large }
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ `;
115
+ const variables = typeof userIdOrName === 'number'
116
+ ? { userId: userIdOrName }
117
+ : typeof userIdOrName === 'string'
118
+ ? { userName: userIdOrName }
119
+ : {};
120
+ const data = await this.request(query, variables);
121
+ const lists = data.MediaListCollection?.lists ?? [];
122
+ const entries = [];
123
+ for (const list of lists) {
124
+ for (const entry of list.entries ?? []) {
125
+ if (!entry.media)
126
+ continue;
127
+ if (entry.status === 'REPEATING')
128
+ continue;
129
+ entries.push({
130
+ mediaId: entry.mediaId,
131
+ entryId: entry.id,
132
+ idMal: entry.media.idMal ?? undefined,
133
+ status: ANILIST_TO_DANGO_STATUS[entry.status] ?? 'Planned',
134
+ progress: entry.progress ?? 0,
135
+ score: entry.score ?? undefined,
136
+ updatedAt: entry.updatedAt ?? 0,
137
+ title: entry.media.title ?? {},
138
+ coverImage: entry.media.coverImage?.large,
139
+ totalEpisodes: entry.media.episodes ?? undefined,
140
+ episodeDuration: entry.media.duration ?? undefined,
141
+ });
142
+ }
143
+ }
144
+ return entries;
145
+ }
146
+ async updateMediaEntry(params) {
147
+ const mutation = `
148
+ mutation ($mediaId: Int, $status: MediaListStatus, $progress: Int, $score: Float) {
149
+ SaveMediaListEntry(mediaId: $mediaId, status: $status, progress: $progress, score: $score) {
150
+ id
151
+ mediaId
152
+ status
153
+ progress
154
+ }
155
+ }
156
+ `;
157
+ const variables = { mediaId: params.mediaId };
158
+ const status = params.status ? DANGO_TO_ANILIST_STATUS[params.status] : undefined;
159
+ if (status)
160
+ variables.status = status;
161
+ if (params.progress !== undefined)
162
+ variables.progress = params.progress;
163
+ if (params.score !== undefined)
164
+ variables.score = params.score;
165
+ return this.request(mutation, variables);
166
+ }
167
+ async batchUpdateMediaEntries(entries) {
168
+ if (entries.length === 0)
169
+ return {};
170
+ if (entries.length === 1) {
171
+ return this.updateMediaEntry(entries[0]);
172
+ }
173
+ const BATCH_SIZE = 10;
174
+ const results = {};
175
+ for (let i = 0; i < entries.length; i += BATCH_SIZE) {
176
+ const batch = entries.slice(i, i + BATCH_SIZE);
177
+ const aliases = [];
178
+ const variables = {};
179
+ const varDecls = [];
180
+ batch.forEach((entry, idx) => {
181
+ const prefix = `a${i + idx}`;
182
+ const args = [`mediaId: $${prefix}mediaId`];
183
+ variables[`${prefix}mediaId`] = entry.mediaId;
184
+ varDecls.push(`$${prefix}mediaId: Int!`);
185
+ if (entry.status) {
186
+ const anilistStatus = DANGO_TO_ANILIST_STATUS[entry.status];
187
+ if (anilistStatus) {
188
+ args.push(`status: $${prefix}status`);
189
+ variables[`${prefix}status`] = anilistStatus;
190
+ varDecls.push(`$${prefix}status: MediaListStatus`);
191
+ }
192
+ }
193
+ if (entry.progress !== undefined) {
194
+ args.push(`progress: $${prefix}progress`);
195
+ variables[`${prefix}progress`] = entry.progress;
196
+ varDecls.push(`$${prefix}progress: Int`);
197
+ }
198
+ if (entry.score !== undefined) {
199
+ args.push(`score: $${prefix}score`);
200
+ variables[`${prefix}score`] = entry.score;
201
+ varDecls.push(`$${prefix}score: Float`);
202
+ }
203
+ aliases.push(`${prefix}: SaveMediaListEntry(${args.join(', ')}) { id mediaId status progress }`);
204
+ });
205
+ const mutation = `mutation (${varDecls.join(', ')}) { ${aliases.join('\n')} }`;
206
+ const data = await this.request(mutation, variables);
207
+ if (data)
208
+ Object.assign(results, data);
209
+ }
210
+ return results;
211
+ }
212
+ async deleteMediaEntry(entryId) {
213
+ const mutation = `
214
+ mutation ($id: Int) {
215
+ DeleteMediaListEntry(id: $id) { deleted }
216
+ }
217
+ `;
218
+ return this.request(mutation, { id: entryId });
219
+ }
220
+ async batchDeleteMediaEntries(entryIds) {
221
+ if (entryIds.length === 0)
222
+ return {};
223
+ if (entryIds.length === 1) {
224
+ return this.deleteMediaEntry(entryIds[0]);
225
+ }
226
+ const BATCH_SIZE = 10;
227
+ const results = {};
228
+ for (let i = 0; i < entryIds.length; i += BATCH_SIZE) {
229
+ const batch = entryIds.slice(i, i + BATCH_SIZE);
230
+ const aliases = [];
231
+ const variables = {};
232
+ batch.forEach((entryId, idx) => {
233
+ const prefix = `d${i + idx}`;
234
+ aliases.push(`${prefix}: DeleteMediaListEntry(id: $${prefix}id) { deleted }`);
235
+ variables[`${prefix}id`] = entryId;
236
+ });
237
+ const varDecls = batch.map((_, idx) => `$d${i + idx}id: Int`).join(', ');
238
+ const mutation = `mutation (${varDecls}) { ${aliases.join('\n')} }`;
239
+ const data = await this.request(mutation, variables);
240
+ if (data)
241
+ Object.assign(results, data);
242
+ }
243
+ return results;
244
+ }
245
+ }