@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,10 +1 @@
1
- export function isJasmrApi(mod) {
2
- if (!mod || typeof mod !== 'object')
3
- return false;
4
- const m = mod;
5
- return (typeof m.browse === 'function' &&
6
- typeof m.getEpisodes === 'function' &&
7
- typeof m.getStreamUrls === 'function' &&
8
- typeof m.getImages === 'function' &&
9
- typeof m.getChapters === 'function');
10
- }
1
+ function t(e){if(!e||typeof e!="object")return!1;const n=e;return typeof n.browse=="function"&&typeof n.getEpisodes=="function"&&typeof n.getStreamUrls=="function"&&typeof n.getImages=="function"&&typeof n.getChapters=="function"}export{t as isJasmrApi};
@@ -1,24 +1,4 @@
1
- import logger from '../logger.js';
2
- import { googleDriveService } from '../google.js';
3
- import { updateEnvFile } from '../utils/env.utils.js';
4
- export function registerAuth(app, getDbs, runSync) {
5
- app.get('/api/auth/google/callback', async (c) => {
6
- const code = c.req.query('code');
7
- if (!code) {
8
- return c.text('No code provided', 400);
9
- }
10
- await googleDriveService.handleCallback(code);
11
- const user = await googleDriveService.getUserProfile();
12
- await updateEnvFile({ SYNC_PROVIDER: 'google' });
13
- logger.info('User logged in. Syncing database (please wait)...');
14
- const dbs = getDbs();
15
- try {
16
- await runSync(dbs.db, dbs.mangaDb, 'google');
17
- }
18
- catch (err) {
19
- logger.error({ err }, 'Post-login sync failed');
20
- }
21
- const responseHtml = `
1
+ import a from"../logger.js";import{googleDriveService as n}from"../google.js";import{updateEnvFile as l}from"../utils/env.utils.js";function w(r,i,s){r.get("/api/auth/google/callback",async e=>{const o=e.req.query("code");if(!o)return e.text("No code provided",400);await n.handleCallback(o);const c=await n.getUserProfile();await l({SYNC_PROVIDER:"google"}),a.info("User logged in. Syncing database (please wait)...");const t=i();try{await s(t.db,t.mangaDb,"google")}catch(d){a.error({err:d},"Post-login sync failed")}const p=`
22
2
  <html>
23
3
  <head><meta name="viewport" content="width=device-width, initial-scale=1" /></head>
24
4
  <body>
@@ -27,7 +7,7 @@ export function registerAuth(app, getDbs, runSync) {
27
7
  <p><a href="/?google_auth=success">Tap here if you are not redirected</a></p>
28
8
  <script>
29
9
  (function () {
30
- var payload = { type: 'GOOGLE_AUTH_SUCCESS', user: ${JSON.stringify(user)} };
10
+ var payload = { type: 'GOOGLE_AUTH_SUCCESS', user: ${JSON.stringify(c)} };
31
11
  try {
32
12
  if (window.opener && !window.opener.closed) {
33
13
  window.opener.postMessage(payload, window.location.origin);
@@ -43,7 +23,4 @@ export function registerAuth(app, getDbs, runSync) {
43
23
  </script>
44
24
  </body>
45
25
  </html>
46
- `;
47
- return c.html(responseHtml);
48
- });
49
- }
26
+ `;return e.html(p)})}export{w as registerAuth};
@@ -1,209 +1 @@
1
- import logger from '../logger.js';
2
- import { parseUmpResponse } from '../lib/ump.js';
3
- import { mintPoToken, resetPoTokens } from '../lib/potoken.js';
4
- import { getInnertube, getAuthedInnertube, getMusicCookie, refreshAuthedInnertube, } from '../lib/ytmusic.js';
5
- const UPSTREAM_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:156.0) Gecko/20100101 Firefox/156.0';
6
- const audioCache = new Map();
7
- const AUDIO_CACHE_MS = 5 * 60 * 1000;
8
- const FIRST_CHUNK = 16384;
9
- const AUDIO_CHUNK = 262144;
10
- const UMP_CVER = '1.20260920.16.00';
11
- const audioTotalCache = new Map();
12
- const umpSessions = new Map();
13
- function randomCpn() {
14
- const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
15
- let out = '';
16
- for (let i = 0; i < 16; i++)
17
- out += chars[Math.floor(Math.random() * chars.length)];
18
- return out;
19
- }
20
- function getUmpSession(videoId) {
21
- let session = umpSessions.get(videoId);
22
- if (!session) {
23
- session = { cpn: randomCpn(), rn: 0, carry: null };
24
- umpSessions.set(videoId, session);
25
- }
26
- return session;
27
- }
28
- function boundWindow(clientRange, videoId) {
29
- const total = audioTotalCache.get(videoId);
30
- const clamp = (start, end) => {
31
- if (total !== undefined)
32
- end = Math.min(end, total - 1);
33
- return [start, Math.max(end, start)];
34
- };
35
- const m = /^bytes=(\d+)-(\d*)$/.exec((clientRange ?? '').trim());
36
- if (m && m[2] !== '') {
37
- const s = Number(m[1]);
38
- const e = Number(m[2]);
39
- if (e - s + 1 <= AUDIO_CHUNK)
40
- return [s, e];
41
- return clamp(s, s + AUDIO_CHUNK - 1);
42
- }
43
- const start = m ? Number(m[1]) : 0;
44
- return clamp(start, start + (start === 0 ? FIRST_CHUNK : AUDIO_CHUNK) - 1);
45
- }
46
- async function decipherWith(yt, videoId) {
47
- const info = await yt.music.getInfo(videoId);
48
- const streaming = info.streaming_data;
49
- const formats = streaming?.adaptive_formats ?? [];
50
- const sorted = [...formats]
51
- .filter((f) => f.has_audio && !f.has_video)
52
- .sort((a, b) => (b.bitrate ?? 0) - (a.bitrate ?? 0));
53
- const player = yt.session.player;
54
- for (const fmt of sorted) {
55
- try {
56
- const url = await fmt.decipher(player);
57
- if (url && url.startsWith('http')) {
58
- const entry = { url, mimeType: fmt.mime_type, fetchedAt: Date.now() };
59
- audioCache.set(videoId, entry);
60
- return entry;
61
- }
62
- }
63
- catch {
64
- // ignore
65
- }
66
- }
67
- throw new Error('No stream available');
68
- }
69
- async function decipherAudio(videoId) {
70
- const cached = audioCache.get(videoId);
71
- if (cached && Date.now() - cached.fetchedAt < AUDIO_CACHE_MS)
72
- return cached;
73
- const authed = await getAuthedInnertube().catch(() => null);
74
- if (authed) {
75
- try {
76
- return await decipherWith(authed, videoId);
77
- }
78
- catch (err) {
79
- logger.warn({ err }, '[music] authed stream lookup failed, retrying with a fresh saved-cookie session');
80
- const fresh = await refreshAuthedInnertube();
81
- if (fresh) {
82
- try {
83
- return await decipherWith(fresh, videoId);
84
- }
85
- catch (retryErr) {
86
- logger.warn({ err: retryErr }, '[music] refreshed stream lookup failed, falling back to public session');
87
- }
88
- }
89
- }
90
- }
91
- return decipherWith(await getInnertube(), videoId);
92
- }
93
- export function registerMusic(app) {
94
- app.get('/api/music/stream', async (c) => {
95
- const videoId = String(c.req.query('videoId') || c.req.query('id') || '').trim();
96
- if (!videoId)
97
- return c.json({ error: 'videoId is required' }, 400);
98
- try {
99
- const entry = await decipherAudio(videoId);
100
- return c.json({ url: entry.url, mimeType: entry.mimeType });
101
- }
102
- catch (err) {
103
- logger.error({ err }, '[music] stream failed');
104
- return c.json({ error: 'Stream lookup failed' }, 502);
105
- }
106
- });
107
- app.get('/api/music/audio', async (c) => {
108
- const videoId = String(c.req.query('videoId') || c.req.query('id') || '').trim();
109
- if (!videoId)
110
- return c.json({ error: 'videoId is required' }, 400);
111
- const signal = c.req.raw.signal.aborted
112
- ? c.req.raw.signal
113
- : AbortSignal.any([c.req.raw.signal, AbortSignal.timeout(30000)]);
114
- try {
115
- for (let attempt = 0; attempt < 3; attempt++) {
116
- if (attempt === 1)
117
- umpSessions.delete(videoId);
118
- if (attempt === 2) {
119
- audioCache.delete(videoId);
120
- umpSessions.delete(videoId);
121
- resetPoTokens(videoId);
122
- }
123
- let entry;
124
- try {
125
- entry = await decipherAudio(videoId);
126
- }
127
- catch {
128
- return c.json({ error: 'No stream available' }, 502);
129
- }
130
- const clen = /[?&]clen=(\d+)/.exec(entry.url)?.[1];
131
- if (clen && !audioTotalCache.has(videoId))
132
- audioTotalCache.set(videoId, Number(clen));
133
- const [rangeStart, rangeEnd] = boundWindow(c.req.header('range') ?? null, videoId);
134
- const session = getUmpSession(videoId);
135
- let pot = '';
136
- try {
137
- pot = await mintPoToken(videoId);
138
- }
139
- catch (err) {
140
- logger.warn({ err }, '[music] PoToken mint failed, trying without attestation');
141
- }
142
- const upstreamUrl = `${entry.url}&cpn=${session.cpn}&cver=${UMP_CVER}` +
143
- `&range=${rangeStart}-${rangeEnd}&rn=${session.rn}&rbuf=0` +
144
- (pot ? `&pot=${encodeURIComponent(pot)}` : '') +
145
- `&ump=1&srfvp=1`;
146
- session.rn += 1;
147
- let raw;
148
- try {
149
- const cookie = getMusicCookie();
150
- const upstream = await fetch(upstreamUrl, {
151
- method: 'POST',
152
- headers: {
153
- 'User-Agent': UPSTREAM_UA,
154
- Referer: 'https://music.youtube.com/',
155
- Origin: 'https://music.youtube.com/',
156
- ...(cookie ? { Cookie: cookie } : {}),
157
- },
158
- body: 'x\x00',
159
- signal,
160
- });
161
- if (upstream.status !== 200) {
162
- try {
163
- await upstream.body?.cancel();
164
- }
165
- catch {
166
- // ignore
167
- }
168
- continue;
169
- }
170
- raw = Buffer.from(await upstream.arrayBuffer());
171
- }
172
- catch (err) {
173
- if (c.req.raw.signal.aborted)
174
- return new Response(null, { status: 499 });
175
- logger.error({ err }, '[music] audio upstream failed');
176
- continue;
177
- }
178
- const parsed = parseUmpResponse(raw, session.carry);
179
- session.carry = parsed.carry;
180
- if (parsed.status === 3) {
181
- logger.warn({ videoId }, '[music] stream protection blocked, refreshing token');
182
- resetPoTokens(videoId);
183
- umpSessions.delete(videoId);
184
- continue;
185
- }
186
- if (parsed.media.length === 0)
187
- continue;
188
- const total = audioTotalCache.get(videoId);
189
- const headers = new Headers();
190
- headers.set('Content-Type', entry.mimeType || 'audio/webm');
191
- headers.set('Content-Length', String(parsed.media.length));
192
- if (total) {
193
- headers.set('Content-Range', `bytes ${rangeStart}-${rangeStart + parsed.media.length - 1}/${total}`);
194
- }
195
- headers.set('Accept-Ranges', 'bytes');
196
- headers.set('Access-Control-Allow-Origin', '*');
197
- return new Response(parsed.media, { status: 206, headers });
198
- }
199
- logger.warn({ videoId }, '[music] audio upstream gave no media after retries');
200
- return c.json({ error: 'Upstream error' }, 502);
201
- }
202
- catch (err) {
203
- if (c.req.raw.signal.aborted)
204
- return new Response(null, { status: 499 });
205
- logger.error({ err }, '[music] audio failed');
206
- return c.json({ error: 'Stream lookup failed' }, 502);
207
- }
208
- });
209
- }
1
+ import c from"../logger.js";import{parseUmpResponse as T}from"../lib/ump.js";import{mintPoToken as M,resetPoTokens as C}from"../lib/potoken.js";import{getInnertube as D,getAuthedInnertube as R,getMusicCookie as I,refreshAuthedInnertube as v}from"../lib/ytmusic.js";const P="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:156.0) Gecko/20100101 Firefox/156.0",w=new Map,_=300*1e3,$=16384,b=262144,N="1.20260920.16.00",g=new Map,p=new Map;function x(){const r="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";let e="";for(let t=0;t<16;t++)e+=r[Math.floor(Math.random()*r.length)];return e}function j(r){let e=p.get(r);return e||(e={cpn:x(),rn:0,carry:null},p.set(r,e)),e}function E(r,e){const t=g.get(e),i=(a,n)=>(t!==void 0&&(n=Math.min(n,t-1)),[a,Math.max(n,a)]),o=/^bytes=(\d+)-(\d*)$/.exec((r??"").trim());if(o&&o[2]!==""){const a=Number(o[1]),n=Number(o[2]);return n-a+1<=b?[a,n]:i(a,a+b-1)}const s=o?Number(o[1]):0;return i(s,s+(s===0?$:b)-1)}async function A(r,e){const s=[...(await r.music.getInfo(e)).streaming_data?.adaptive_formats??[]].filter(n=>n.has_audio&&!n.has_video).sort((n,m)=>(m.bitrate??0)-(n.bitrate??0)),a=r.session.player;for(const n of s)try{const m=await n.decipher(a);if(m&&m.startsWith("http")){const u={url:m,mimeType:n.mime_type,fetchedAt:Date.now()};return w.set(e,u),u}}catch{}throw new Error("No stream available")}async function q(r){const e=w.get(r);if(e&&Date.now()-e.fetchedAt<_)return e;const t=await R().catch(()=>null);if(t)try{return await A(t,r)}catch(i){c.warn({err:i},"[music] authed stream lookup failed, retrying with a fresh saved-cookie session");const o=await v();if(o)try{return await A(o,r)}catch(s){c.warn({err:s},"[music] refreshed stream lookup failed, falling back to public session")}}return A(await D(),r)}function Y(r){r.get("/api/music/stream",async e=>{const t=String(e.req.query("videoId")||e.req.query("id")||"").trim();if(!t)return e.json({error:"videoId is required"},400);try{const i=await q(t);return e.json({url:i.url,mimeType:i.mimeType})}catch(i){return c.error({err:i},"[music] stream failed"),e.json({error:"Stream lookup failed"},502)}}),r.get("/api/music/audio",async e=>{const t=String(e.req.query("videoId")||e.req.query("id")||"").trim();if(!t)return e.json({error:"videoId is required"},400);const i=e.req.raw.signal.aborted?e.req.raw.signal:AbortSignal.any([e.req.raw.signal,AbortSignal.timeout(3e4)]);try{for(let o=0;o<3;o++){o===1&&p.delete(t),o===2&&(w.delete(t),p.delete(t),C(t));let s;try{s=await q(t)}catch{return e.json({error:"No stream available"},502)}const a=/[?&]clen=(\d+)/.exec(s.url)?.[1];a&&!g.has(t)&&g.set(t,Number(a));const[n,m]=E(e.req.header("range")??null,t),u=j(t);let h="";try{h=await M(t)}catch(f){c.warn({err:f},"[music] PoToken mint failed, trying without attestation")}const k=`${s.url}&cpn=${u.cpn}&cver=${N}&range=${n}-${m}&rn=${u.rn}&rbuf=0`+(h?`&pot=${encodeURIComponent(h)}`:"")+"&ump=1&srfvp=1";u.rn+=1;let S;try{const f=I(),y=await fetch(k,{method:"POST",headers:{"User-Agent":P,Referer:"https://music.youtube.com/",Origin:"https://music.youtube.com/",...f?{Cookie:f}:{}},body:"x\0",signal:i});if(y.status!==200){try{await y.body?.cancel()}catch{}continue}S=Buffer.from(await y.arrayBuffer())}catch(f){if(e.req.raw.signal.aborted)return new Response(null,{status:499});c.error({err:f},"[music] audio upstream failed");continue}const d=T(S,u.carry);if(u.carry=d.carry,d.status===3){c.warn({videoId:t},"[music] stream protection blocked, refreshing token"),C(t),p.delete(t);continue}if(d.media.length===0)continue;const U=g.get(t),l=new Headers;return l.set("Content-Type",s.mimeType||"audio/webm"),l.set("Content-Length",String(d.media.length)),U&&l.set("Content-Range",`bytes ${n}-${n+d.media.length-1}/${U}`),l.set("Accept-Ranges","bytes"),l.set("Access-Control-Allow-Origin","*"),new Response(d.media,{status:206,headers:l})}return c.warn({videoId:t},"[music] audio upstream gave no media after retries"),e.json({error:"Upstream error"},502)}catch(o){return e.req.raw.signal.aborted?new Response(null,{status:499}):(c.error({err:o},"[music] audio failed"),e.json({error:"Stream lookup failed"},502))}})}export{Y as registerMusic};