@serifpersia/dango 3.4.5 → 3.4.6

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 (64) hide show
  1. package/README.md +1 -1
  2. package/client/dist/assets/{AnimeCard-I3S6qw9d.js → AnimeCard-BVdH0EeW.js} +1 -1
  3. package/client/dist/assets/{AnimeInfo-CLU2NSBR.js → AnimeInfo-Dw2Nop6Q.js} +1 -1
  4. package/client/dist/assets/{AnimeInfoPage-4Mw9ArDy.js → AnimeInfoPage-Cw-Tq3YX.js} +2 -2
  5. package/client/dist/assets/{Asmr-DN9XBc5Y.js → Asmr-BrjzzyOi.js} +1 -1
  6. package/client/dist/assets/{AsmrResetProgressModal-DyU1Z2Gw.js → AsmrResetProgressModal-CYjpcYpb.js} +1 -1
  7. package/client/dist/assets/{AvSyncCalibrator-DxL0mr_4.js → AvSyncCalibrator-BuHurVgo.js} +3 -3
  8. package/client/dist/assets/AvSyncCalibrator-D0FYSwiz.css +1 -0
  9. package/client/dist/assets/{Home-CeTeJ-gM.js → Home-KdnP7sNM.js} +1 -1
  10. package/client/dist/assets/{Insights-i34-qV4C.js → Insights-DDWry-t1.js} +1 -1
  11. package/client/dist/assets/{LibraryListPage-DE-ASOX2.js → LibraryListPage-CV8Z9JlO.js} +1 -1
  12. package/client/dist/assets/{ListeningList-DrNce5ll.js → ListeningList-DSMbMZT7.js} +1 -1
  13. package/client/dist/assets/{Manga-DOrf7Fk6.js → Manga-J6XoqEKq.js} +1 -1
  14. package/client/dist/assets/{MangaInfoPage-CAZMLMGw.js → MangaInfoPage-N7TxsN5u.js} +1 -1
  15. package/client/dist/assets/{MangaReadPage-DQVEo8_F.js → MangaReadPage-D_VzSNWq.js} +1 -1
  16. package/client/dist/assets/{MangaResetProgressModal-DOjpLAMp.js → MangaResetProgressModal-DyhQYBDy.js} +1 -1
  17. package/client/dist/assets/{Mature-D2xb9EGB.js → Mature-Db53R-LR.js} +1 -1
  18. package/client/dist/assets/{MediaCard-CKUEcFTd.js → MediaCard-BcLjlpfR.js} +1 -1
  19. package/client/dist/assets/{Music-w_a34r7r.js → Music-DjxQjJOl.js} +1 -1
  20. package/client/dist/assets/{OptionListMenu-DxZHBMIZ.js → OptionListMenu-DngTgHxH.js} +1 -1
  21. package/client/dist/assets/Player-Be_7Ne5p.js +56 -0
  22. package/client/dist/assets/{PlayerSettings-Ddlg-MvW.js → PlayerSettings-D47ItGDR.js} +1 -1
  23. package/client/dist/assets/{QueueOptionsButton-BMvAC6Ut.js → QueueOptionsButton-ClVTpH8J.js} +1 -1
  24. package/client/dist/assets/{QueueRail-CAoGJvHP.js → QueueRail-DeJKVRs5.js} +1 -1
  25. package/client/dist/assets/{Radio-6O7rAUv1.js → Radio-DU0ma3GA.js} +1 -1
  26. package/client/dist/assets/{ReadingList-k3g8oHvs.js → ReadingList-qXW7Dk5z.js} +1 -1
  27. package/client/dist/assets/{ResetProgressModal-vFC6N-Qw.js → ResetProgressModal-C6ukct_c.js} +1 -1
  28. package/client/dist/assets/{Search-Bh9Y6nIJ.js → Search-DjjKA_Wu.js} +1 -1
  29. package/client/dist/assets/{Settings-DRumujfh.js → Settings-DQt_S5Qw.js} +1 -1
  30. package/client/dist/assets/{SkeletonGrid-BtzUnQE0.js → SkeletonGrid-nx2HEi4P.js} +1 -1
  31. package/client/dist/assets/{Trackers-D0nqy8wu.js → Trackers-BcGzeqj2.js} +1 -1
  32. package/client/dist/assets/Tv-COvyHdeJ.js +4 -0
  33. package/client/dist/assets/{TvInfo-BkECbOKN.js → TvInfo-CUd1agFy.js} +1 -1
  34. package/client/dist/assets/{TvPopup-D8WWY6o4.js → TvPopup-CJeu3tgc.js} +1 -1
  35. package/client/dist/assets/{TvSearch-kWPep3gL.js → TvSearch-BIypjbU3.js} +1 -1
  36. package/client/dist/assets/{TvWatchlist-dD4emIHx.js → TvWatchlist-DZrBdiqZ.js} +1 -1
  37. package/client/dist/assets/{Watchlist-cWxw38nU.js → Watchlist-Dw8cPe0n.js} +1 -1
  38. package/client/dist/assets/{hls-uG_Y3i5s.js → hls-B3_1VuDP.js} +1 -1
  39. package/client/dist/assets/{index-BWIWE3Qy.js → index-gMxawd-E.js} +2 -2
  40. package/client/dist/assets/{useAnimeInfoData-DjPBg24u.js → useAnimeInfoData-_iOLEAa8.js} +1 -1
  41. package/client/dist/assets/{useAsmr-DQyobNQX.js → useAsmr-Blk4v41t.js} +1 -1
  42. package/client/dist/assets/{useAsmrPopup-CDoroMBD.js → useAsmrPopup-D9SQIgZa.js} +1 -1
  43. package/client/dist/assets/{useMangaLibrary-C6L8mrnS.js → useMangaLibrary-B60at6ni.js} +1 -1
  44. package/client/dist/assets/{useMangaPopup-BSuMa_vA.js → useMangaPopup-DEfGjWSu.js} +1 -1
  45. package/client/dist/assets/{useProviders-XIdvDof6.js → useProviders-Ca0bZWt7.js} +1 -1
  46. package/client/dist/assets/{useTv-DzSYasru.js → useTv-WJCDL5_5.js} +1 -1
  47. package/client/dist/assets/{useTvLibrary-BnbHS-gg.js → useTvLibrary-Fgn5i9rJ.js} +1 -1
  48. package/client/dist/index.html +1 -1
  49. package/package.json +2 -2
  50. package/server/dist/hono/proxy.js +102 -75
  51. package/server/dist/hono/tv.js +2 -2
  52. package/server/dist/hono/watchlist.js +2 -291
  53. package/server/dist/lib/anilist.js +0 -3
  54. package/server/dist/lib/mal.js +0 -38
  55. package/server/dist/lib/offline-db.js +0 -6
  56. package/server/dist/providers/remote-loader.js +15 -26
  57. package/server/dist/providers/title-matching.js +5 -5
  58. package/server/dist/repositories/manga.repository.js +0 -1
  59. package/server/dist/repositories/watched-episodes.repository.js +0 -2
  60. package/server/dist/utils/megaplay.utils.js +1 -2
  61. package/server/package.json +1 -1
  62. package/client/dist/assets/AvSyncCalibrator-C2wu1ydI.css +0 -1
  63. package/client/dist/assets/Player-B8YHeATv.js +0 -56
  64. package/client/dist/assets/Tv-DVebLMNS.js +0 -4
@@ -1,13 +1,11 @@
1
1
  import logger from '../logger.js';
2
2
  import { WatchlistRepository } from '../repositories/watchlist.repository.js';
3
- import { WatchedEpisodesRepository, } from '../repositories/watched-episodes.repository.js';
3
+ import { WatchedEpisodesRepository } from '../repositories/watched-episodes.repository.js';
4
4
  import { ShowsMetaRepository } from '../repositories/shows-meta.repository.js';
5
5
  import { NotificationsRepository } from '../repositories/notifications.repository.js';
6
- import { SettingsRepository } from '../repositories/settings.repository.js';
7
6
  import { dbAll } from '../utils/db-utils.js';
8
7
  import { AppCache } from '../utils/cache.utils.js';
9
- import { searchAnilist, getAiredEpisodesForShows, getAnilistEpisodes, getShowMetaById, isAnilistRateLimited, batchGetShowStatuses, } from '../lib/anilist.js';
10
- import { offlineDb } from '../lib/offline-db.js';
8
+ import { getAiredEpisodesForShows, isAnilistRateLimited, batchGetShowStatuses, } from '../lib/anilist.js';
11
9
  import { parseJsonBody } from '../utils/http.utils.js';
12
10
  import { pickBestMatch } from '../providers/title-matching.js';
13
11
  const BACKGROUND_DISCOVERY_INTERVAL_MS = 5 * 60 * 1000;
@@ -258,286 +256,6 @@ export function startWatchlistDiscovery(getDb) {
258
256
  runDiscovery(false);
259
257
  }, BACKGROUND_DISCOVERY_INTERVAL_MS);
260
258
  }
261
- async function showsMetaChanged(db, showId, candidate) {
262
- const existing = (await ShowsMetaRepository.getById(db, showId));
263
- if (!existing)
264
- return true;
265
- const differs = (incoming, stored) => {
266
- if (incoming === undefined || incoming === null || incoming === '')
267
- return false;
268
- return String(incoming) !== String(stored ?? '');
269
- };
270
- return (differs(candidate.name, existing.name) ||
271
- differs(candidate.thumbnail, existing.thumbnail) ||
272
- differs(candidate.nativeName, existing.nativeName) ||
273
- differs(candidate.englishName, existing.englishName) ||
274
- differs(candidate.genres, existing.genres) ||
275
- differs(candidate.popularityScore, existing.popularityScore) ||
276
- differs(candidate.status, existing.status) ||
277
- differs(candidate.episodeCount, existing.episodeCount) ||
278
- differs(candidate.type, existing.type) ||
279
- differs(candidate.anilistId, existing.anilistId) ||
280
- differs(candidate.isAdult, existing.isAdult) ||
281
- differs(candidate.episodeDuration, existing.episodeDuration));
282
- }
283
- function normalizeFilterValue(value) {
284
- if (typeof value !== 'string')
285
- return undefined;
286
- const trimmed = value.trim();
287
- return trimmed && trimmed !== 'ALL' ? trimmed : undefined;
288
- }
289
- function getWatchlistFilters(query) {
290
- return {
291
- query: normalizeFilterValue(query['query']),
292
- type: normalizeFilterValue(query['type']),
293
- season: normalizeFilterValue(query['season']),
294
- year: normalizeFilterValue(query['year']),
295
- genres: normalizeFilterValue(query['genres']),
296
- excludeGenres: normalizeFilterValue(query['excludeGenres']),
297
- sortBy: normalizeFilterValue(query['sortBy']),
298
- titlePreference: ['name', 'nativeName', 'englishName'].includes(String(query['titlePreference']))
299
- ? query['titlePreference']
300
- : 'name',
301
- };
302
- }
303
- function matchesLocalFilters(row, filters) {
304
- if (filters.query) {
305
- const queryWords = new Set(filters.query
306
- .toLowerCase()
307
- .split(/\s+/)
308
- .filter((w) => w.length >= 2));
309
- const rowTitle = (row.englishName || row.name || row.nativeName || '').toLowerCase();
310
- const titleWords = rowTitle.split(/\s+/);
311
- const overlap = titleWords.filter((w) => queryWords.has(w)).length;
312
- if (overlap < queryWords.size)
313
- return false;
314
- }
315
- return true;
316
- }
317
- function sortFilteredRows(rows, filters) {
318
- const getSortTitle = (row) => {
319
- const preferredTitle = filters.titlePreference ? row[filters.titlePreference] : undefined;
320
- return preferredTitle || row.name || '';
321
- };
322
- if (filters.sortBy === 'name_asc') {
323
- return [...rows].sort((a, b) => getSortTitle(a).localeCompare(getSortTitle(b)));
324
- }
325
- if (filters.sortBy === 'name_desc') {
326
- return [...rows].sort((a, b) => getSortTitle(b).localeCompare(getSortTitle(a)));
327
- }
328
- return rows;
329
- }
330
- async function getAnilistSeasonYearMatches(season, year) {
331
- const matched = new Map();
332
- if (!year || year === 'ALL') {
333
- return matched;
334
- }
335
- const seasonYear = parseInt(year);
336
- if (Number.isNaN(seasonYear))
337
- return matched;
338
- const perPage = 50;
339
- let page = 1;
340
- while (true) {
341
- const searchVars = {
342
- seasonYear,
343
- page,
344
- perPage,
345
- };
346
- if (season && season !== 'ALL') {
347
- searchVars.season = season.toUpperCase();
348
- }
349
- const results = await searchAnilist(searchVars);
350
- for (const show of results) {
351
- if (show.anilistId) {
352
- matched.set(show.anilistId, { title: show.names || {} });
353
- }
354
- }
355
- if (results.length < perPage)
356
- break;
357
- page++;
358
- if (page > 10)
359
- break;
360
- }
361
- return matched;
362
- }
363
- function rowMatchesAnilistSeasonYear(row, anilistMatches) {
364
- if (anilistMatches.size === 0)
365
- return true;
366
- if (/^\d+$/.test(row.id) && anilistMatches.has(parseInt(row.id))) {
367
- return true;
368
- }
369
- const rowTitle = (row.englishName || row.name || row.nativeName || '').toLowerCase();
370
- if (!rowTitle)
371
- return false;
372
- const rowWords = new Set(rowTitle.split(/\s+/).filter((w) => w.length >= 2));
373
- if (rowWords.size === 0)
374
- return false;
375
- for (const [, media] of anilistMatches) {
376
- const titles = [media.title?.romaji, media.title?.english, media.title?.native].filter(Boolean);
377
- for (const title of titles) {
378
- const titleWords = new Set(title
379
- .toLowerCase()
380
- .split(/\s+/)
381
- .filter((w) => w.length >= 2));
382
- const overlap = [...rowWords].filter((w) => titleWords.has(w)).length;
383
- const minLen = Math.min(rowWords.size, titleWords.size);
384
- if (minLen >= 2 && overlap / minLen >= 0.7)
385
- return true;
386
- }
387
- }
388
- return false;
389
- }
390
- async function filterWatchlistRows(rows, filters, db) {
391
- let filtered = rows.filter((row) => matchesLocalFilters(row, filters));
392
- if (filters.type === 'ADULT' && db && filtered.length > 0) {
393
- const ids = filtered.map((r) => r.id);
394
- const placeholders = ids.map(() => '?').join(',');
395
- const adultRows = await dbAll(db, `SELECT id FROM shows_meta WHERE id IN (${placeholders}) AND isAdult = 1`, ids);
396
- const adultSet = new Set(adultRows.map((r) => r.id));
397
- filtered = filtered.filter((row) => adultSet.has(row.id));
398
- }
399
- else if (filters.type && filters.type !== 'ALL' && filters.type !== 'ADULT') {
400
- filtered = filtered.filter((row) => row.type === filters.type);
401
- }
402
- if ((filters.genres || filters.excludeGenres) && db) {
403
- const ids = filtered.map((r) => r.id);
404
- const placeholders = ids.map(() => '?').join(',');
405
- const genreRows = await dbAll(db, `SELECT id, genres FROM shows_meta WHERE id IN (${placeholders})`, ids);
406
- const idToGenres = new Map(genreRows.map((r) => [r.id, r.genres ? JSON.parse(r.genres) : []]));
407
- const includeList = filters.genres
408
- ?.split(',')
409
- .map((g) => g.trim())
410
- .filter(Boolean) || [];
411
- const excludeList = filters.excludeGenres
412
- ?.split(',')
413
- .map((g) => g.trim())
414
- .filter(Boolean) || [];
415
- filtered = filtered.filter((row) => {
416
- const rowGenres = idToGenres.get(row.id) || [];
417
- if (includeList.length && !includeList.every((g) => rowGenres.includes(g)))
418
- return false;
419
- if (excludeList.length && excludeList.some((g) => rowGenres.includes(g)))
420
- return false;
421
- return true;
422
- });
423
- }
424
- if (((filters.season && filters.season !== 'ALL') || (filters.year && filters.year !== 'ALL')) &&
425
- filtered.length > 0) {
426
- const anilistMatches = await getAnilistSeasonYearMatches(filters.season, filters.year);
427
- if (anilistMatches.size > 0) {
428
- filtered = filtered.filter((row) => rowMatchesAnilistSeasonYear(row, anilistMatches));
429
- }
430
- }
431
- return sortFilteredRows(filtered, filters);
432
- }
433
- async function backfillMissingPosters(db, rows) {
434
- const missing = rows.filter((r) => !r.thumbnail || r.thumbnail.trim() === '');
435
- if (missing.length === 0)
436
- return;
437
- const slice = missing.slice(0, 10);
438
- await Promise.allSettled(slice.map(async (row) => {
439
- try {
440
- const meta = await getShowMetaById(row.id);
441
- const poster = meta?.thumbnail?.trim();
442
- if (!poster)
443
- return;
444
- row.thumbnail = poster;
445
- const dual = row;
446
- if (dual._id !== undefined)
447
- dual._id = row.id;
448
- try {
449
- await ShowsMetaRepository.upsert(db, {
450
- id: row.id,
451
- thumbnail: poster,
452
- popularityScore: meta?.score ?? (meta?.averageScore != null ? meta.averageScore / 10 : undefined),
453
- });
454
- }
455
- catch {
456
- // ignore
457
- }
458
- try {
459
- await WatchlistRepository.updateThumbnail(db, row.id, poster);
460
- }
461
- catch {
462
- // ignore
463
- }
464
- }
465
- catch {
466
- // ignore
467
- }
468
- }));
469
- await sweepOfflinePosters(db);
470
- }
471
- async function sweepOfflinePosters(db) {
472
- try {
473
- db.run(`UPDATE shows_meta SET name = (SELECT w.name FROM watchlist w WHERE w.id = shows_meta.id)
474
- WHERE (name IS NULL OR TRIM(name) = '')
475
- AND EXISTS (SELECT 1 FROM watchlist w WHERE w.id = shows_meta.id AND w.name IS NOT NULL AND TRIM(w.name) != '')`);
476
- }
477
- catch {
478
- // ignore
479
- }
480
- const [wlMissing, metaMissing] = await Promise.all([
481
- WatchlistRepository.getMissingThumbnails(db),
482
- ShowsMetaRepository.getShowIdsMissingMeta(db),
483
- ]);
484
- const ids = [...new Set([...wlMissing.map((r) => r.id), ...metaMissing.map((r) => r.id)])].slice(0, 500);
485
- if (ids.length === 0)
486
- return;
487
- for (const id of ids) {
488
- const poster = resolveOfflinePoster(id);
489
- if (poster)
490
- await applyPoster(db, id, poster);
491
- }
492
- }
493
- function resolveOfflinePoster(id) {
494
- if (/^\d+$/.test(id)) {
495
- return offlineDb.getByAnilistId(Number(id))?.thumbnail?.trim() || null;
496
- }
497
- const mal = /^mal-(\d+)$/i.exec(id)?.[1];
498
- if (mal)
499
- return offlineDb.getByMalId(Number(mal))?.thumbnail?.trim() || null;
500
- return null;
501
- }
502
- async function applyPoster(db, id, poster) {
503
- try {
504
- await ShowsMetaRepository.upsert(db, { id, thumbnail: poster });
505
- }
506
- catch {
507
- // ignore
508
- }
509
- try {
510
- await WatchlistRepository.updateThumbnail(db, id, poster);
511
- }
512
- catch {
513
- // ignore
514
- }
515
- }
516
- async function getContinueWatchingData(db, limit) {
517
- const rows = await WatchedEpisodesRepository.getContinueWatching(db, limit);
518
- const ignoreAdultRow = await SettingsRepository.getByKey(db, 'ignoreAdultContent');
519
- const ignoreAdult = ignoreAdultRow ? ignoreAdultRow.value !== 'false' : true;
520
- const watchlistOnlyRow = await SettingsRepository.getByKey(db, 'cwWatchlistOnly');
521
- const watchlistOnly = watchlistOnlyRow
522
- ? watchlistOnlyRow.value === 'true' || watchlistOnlyRow.value === '1'
523
- : false;
524
- const enrichedRows = rows
525
- .filter((show) => {
526
- if (ignoreAdult && show.isAdult)
527
- return false;
528
- if (watchlistOnly && show.watchlistStatus !== 'Watching')
529
- return false;
530
- return true;
531
- })
532
- .map((show) => ({
533
- ...show,
534
- episodeCount: show.episodeCount,
535
- type: show.type || show.smType,
536
- thumbnail: show.thumbnail ?? '',
537
- }));
538
- await backfillMissingPosters(db, enrichedRows);
539
- return enrichedRows;
540
- }
541
259
  export async function getAdultNonWatchlistShowIds(db) {
542
260
  const rows = await dbAll(db, `SELECT DISTINCT we.showId as showId
543
261
  FROM watched_episodes we
@@ -546,13 +264,6 @@ export async function getAdultNonWatchlistShowIds(db) {
546
264
  WHERE w.id IS NULL`);
547
265
  return rows.map((r) => r.showId);
548
266
  }
549
- async function resolveAvailableEpisodes(db, showId) {
550
- const episodeData = await getAnilistEpisodes(showId);
551
- const episodes = Array.isArray(episodeData) && episodeData.length
552
- ? [...episodeData].sort((a, b) => parseFloat(a) - parseFloat(b))
553
- : [];
554
- return episodes;
555
- }
556
267
  const dlsitePosterCache = new AppCache({ ttlSeconds: 3600, maxKeys: 2000 });
557
268
  const mangadexCoverCache = new AppCache({ ttlSeconds: 3600, maxKeys: 2000 });
558
269
  export async function getMangadexCover(title) {
@@ -21,9 +21,6 @@ export function anilistUnavailable() {
21
21
  }
22
22
  return false;
23
23
  }
24
- export function anilistIsDown() {
25
- return anilistDownUntil > Date.now();
26
- }
27
24
  export function isAnilistRateLimited() {
28
25
  return anilistRateLimited && anilistCooldownUntil > Date.now();
29
26
  }
@@ -153,44 +153,6 @@ function detailToSearchEntry(d) {
153
153
  score: d.score,
154
154
  };
155
155
  }
156
- export function toSearchEntry(e) {
157
- if (!e || !e.malId || !e.title)
158
- return null;
159
- return {
160
- id: -e.malId,
161
- idMal: e.malId,
162
- url: e.url ?? `https://myanimelist.net/anime/${e.malId}`,
163
- title: e.title,
164
- imageUrl: e.imageUrl ?? null,
165
- synopsis: e.synopsis ?? null,
166
- type: e.type ?? null,
167
- episodes: e.episodes ?? null,
168
- score: e.score ?? null,
169
- };
170
- }
171
- export function toDetail(e, malId) {
172
- if (!e || !e.title)
173
- return null;
174
- const genres = (Array.isArray(e.genres) ? e.genres : [])
175
- .map((g) => (typeof g === 'string' ? g : g?.name))
176
- .filter((g) => !!g);
177
- return {
178
- id: -malId,
179
- idMal: malId,
180
- url: e.url ?? `https://myanimelist.net/anime/${malId}`,
181
- title: e.title,
182
- titleEnglish: e.titleEnglish ?? null,
183
- titleJapanese: e.titleJapanese ?? null,
184
- imageUrl: e.imageUrl ?? e.images?.medium ?? null,
185
- synopsis: e.synopsis ?? null,
186
- type: e.type ?? null,
187
- episodes: e.episodes ?? null,
188
- status: e.status ?? null,
189
- score: e.score ?? null,
190
- rating: e.rating ?? null,
191
- genres,
192
- };
193
- }
194
156
  function mapMalStatus(status) {
195
157
  if (!status)
196
158
  return undefined;
@@ -208,12 +208,6 @@ class OfflineDb {
208
208
  getByAnilistId(anilistId) {
209
209
  return this.anilistToEntry.get(anilistId) ?? null;
210
210
  }
211
- getAnilistIdByMal(malId) {
212
- return this.malToEntry.get(malId)?.anilistId ?? null;
213
- }
214
- getMalIdByAnilist(anilistId) {
215
- return this.anilistToMal.get(anilistId) ?? null;
216
- }
217
211
  async getOfflineDbInfo(db) {
218
212
  const get = async (key) => (await SettingsRepository.getByKey(db, key))?.value ?? null;
219
213
  const autoUpdate = await get('offlineDbAutoUpdateEnabled');
@@ -9,6 +9,7 @@ import logger from '../logger.js';
9
9
  import { CONFIG } from '../config.js';
10
10
  import { requestContext } from '../utils/request-context.js';
11
11
  import { sanitizeCfClearance, buildCfClearanceCookie } from '../utils/cookie.utils.js';
12
+ import { base64UrlEncode } from '../utils/megaplay.utils.js';
12
13
  import { decodeMaybeGzip, parseJsonBody } from '../utils/http.utils.js';
13
14
  import { buildQueryVariants, pickBestMatch } from './title-matching.js';
14
15
  import { anilistRequest, parseMalId, searchAnilistByTitle } from '../lib/anilist.js';
@@ -71,13 +72,6 @@ function resolveEntryUrl(entry, registryUrl) {
71
72
  function sha256Hex(data) {
72
73
  return crypto.createHash('sha256').update(data).digest('hex');
73
74
  }
74
- function base64UrlEncode(input) {
75
- return Buffer.from(input)
76
- .toString('base64')
77
- .replace(/\+/g, '-')
78
- .replace(/\//g, '_')
79
- .replace(/=+$/, '');
80
- }
81
75
  function aes256CbcDecryptJson(b64url, keyUtf8, ivUtf8) {
82
76
  let encrypted;
83
77
  try {
@@ -272,31 +266,26 @@ function createCtx(cache) {
272
266
  },
273
267
  };
274
268
  }
275
- function isValidVideoProvider(p) {
269
+ function shapeOf(p) {
276
270
  if (!p || typeof p !== 'object')
277
- return false;
271
+ return null;
278
272
  const o = p;
279
- return (typeof o.name === 'string' &&
280
- typeof o.search === 'function' &&
281
- typeof o.getEpisodes === 'function' &&
282
- typeof o.getStreamUrls === 'function');
273
+ return typeof o.name === 'string' ? o : null;
274
+ }
275
+ function hasFns(o, fns) {
276
+ return fns.every((f) => typeof o[f] === 'function');
277
+ }
278
+ function isValidVideoProvider(p) {
279
+ const o = shapeOf(p);
280
+ return !!o && hasFns(o, ['search', 'getEpisodes', 'getStreamUrls']);
283
281
  }
284
282
  function isValidMangaProvider(p) {
285
- if (!p || typeof p !== 'object')
286
- return false;
287
- const o = p;
288
- return (typeof o.name === 'string' &&
289
- typeof o.search === 'function' &&
290
- typeof o.getDetail === 'function' &&
291
- typeof o.getChapters === 'function' &&
292
- typeof o.getPages === 'function');
283
+ const o = shapeOf(p);
284
+ return !!o && hasFns(o, ['search', 'getDetail', 'getChapters', 'getPages']);
293
285
  }
294
286
  function isValidTvProvider(p) {
295
- if (!p || typeof p !== 'object')
296
- return false;
297
- const o = p;
298
- return (typeof o.name === 'string' &&
299
- (typeof o.getSources === 'function' || typeof o.getEmbedUrl === 'function'));
287
+ const o = shapeOf(p);
288
+ return !!o && (typeof o.getSources === 'function' || typeof o.getEmbedUrl === 'function');
300
289
  }
301
290
  function makeEmbedProvider(id, base) {
302
291
  const root = base.trim().replace(/\/+$/, '');
@@ -1,14 +1,14 @@
1
- export function normalizeCompact(s) {
1
+ function normalizeCompact(s) {
2
2
  return s.toLowerCase().replace(/[^a-z0-9]/g, '');
3
3
  }
4
- export function tokenize(s) {
4
+ function tokenize(s) {
5
5
  return s
6
6
  .toLowerCase()
7
7
  .replace(/[^\p{L}\p{N}\s]/gu, ' ')
8
8
  .split(/\s+/)
9
9
  .filter((w) => w.length >= 2);
10
10
  }
11
- export function bigramDice(a, b) {
11
+ function bigramDice(a, b) {
12
12
  if (a.length < 2 || b.length < 2)
13
13
  return a === b ? 1 : 0;
14
14
  const grams = (s) => {
@@ -129,10 +129,10 @@ function detectSeasonMarker(s) {
129
129
  return trailingNum[1];
130
130
  return '';
131
131
  }
132
- export function distinctiveTokens(s) {
132
+ function distinctiveTokens(s) {
133
133
  return [...new Set(tokenize(s).filter((w) => !STOPWORDS.has(w)))];
134
134
  }
135
- export function titleSimilarity(query, candidate) {
135
+ function titleSimilarity(query, candidate) {
136
136
  const q = normalizeCompact(query);
137
137
  const c = normalizeCompact(candidate);
138
138
  if (!q || !c)
@@ -28,7 +28,6 @@ export const MangaLibraryRepository = {
28
28
  .set({ anilistId, anilistIdSource: source })
29
29
  .where(eq(mangaLibrary.id, id))
30
30
  : getDrizzle(db).update(mangaLibrary).set({ anilistId }).where(eq(mangaLibrary.id, id)),
31
- getByAnilistId: (db, anilistId) => getDrizzle(db).all(sql `SELECT * FROM manga_library WHERE anilistId = ${anilistId}`),
32
31
  touchProgress: (db, id, progress) => getDrizzle(db).run(sql `
33
32
  UPDATE manga_library SET lastChapterId = ${progress.chapterId}, lastChapterNumber = ${progress.chapterNumber}, lastPage = ${progress.page}, updatedAt = strftime('%s', 'now') WHERE id = ${id}`),
34
33
  setProgressPointer: (db, id, chapterNumber) => getDrizzle(db).run(sql `
@@ -79,6 +79,4 @@ export const WatchedEpisodesRepository = {
79
79
  ORDER BY we.watchedAt DESC
80
80
  ${sql.raw(limitClause)}`);
81
81
  },
82
- getEpisodesForShows: (db, showIds) => getDrizzle(db).all(sql `
83
- SELECT showId, episodeNumber, currentTime, duration, watchedAt FROM watched_episodes WHERE showId IN (${sql.join(showIds.map((id) => sql `${id}`), sql `, `)})`),
84
82
  };
@@ -2,8 +2,7 @@ import { createHmac } from 'node:crypto';
2
2
  const MEGAPLAY_CDN_TOKEN_KEY = 'MpCdnT0k3n!9f2K#xQ7vL5mR8wN1pY4s';
3
3
  export const MEGAPLAY_CDN_TOKEN_TTL_SECONDS = 120;
4
4
  export const MEGAPLAY_ORIGIN = 'https://megaplay.buzz';
5
- export const MEGAPLAY_REFERER = 'https://megaplay.buzz/';
6
- function base64UrlEncode(input) {
5
+ export function base64UrlEncode(input) {
7
6
  return Buffer.from(input)
8
7
  .toString('base64')
9
8
  .replace(/\+/g, '-')
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dango-server",
3
3
  "private": true,
4
- "version": "3.4.5",
4
+ "version": "3.4.6",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=22.12.0"
@@ -1 +0,0 @@
1
- ._controls_endtv_1{display:flex;align-items:stretch;gap:1.25rem;flex-wrap:wrap;width:100%}._titleContainer_endtv_9{display:flex;flex-direction:column;gap:1rem}._scheduleInfo_endtv_15{display:flex;gap:1rem;font-size:.95rem;color:var(--text-tertiary);align-items:center}._status_endtv_23{background:color-mix(in srgb,var(--accent) 10%,transparent);color:var(--accent-light);border:1px solid color-mix(in srgb,var(--accent) 20%,transparent);padding:.35rem .85rem;border-radius:var(--radius-full);font-weight:700;font-size:.75rem;text-transform:uppercase;letter-spacing:.05em}._nextEpisode_endtv_35{background:#ffffff0d;color:var(--text-secondary);padding:.35rem .85rem;border-radius:var(--radius-full);font-size:.8rem;font-weight:600;border:1px solid var(--glass-border)}._watchlistBtn_endtv_45{background:#ffffff0d;color:var(--text-primary);border:1px solid rgba(255,255,255,.1);padding:.65rem 1.25rem;border-radius:var(--radius-full);font-weight:600;cursor:pointer;transition:all var(--transition-normal);display:flex;align-items:center;gap:.6rem;font-size:.85rem;flex:0 1 auto;min-width:120px;justify-content:center;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}._watchlistBtn_endtv_45:hover{background:#ffffff1f;border-color:#ffffff40;transform:translateY(-2px);box-shadow:0 4px 12px #00000040}._watchlistBtn_endtv_45._inList_endtv_72{background:color-mix(in srgb,var(--color-success) 12%,transparent);border-color:color-mix(in srgb,var(--color-success) 35%,transparent);color:var(--color-success)}._watchlistBtn_endtv_45._inList_endtv_72:hover{background:color-mix(in srgb,var(--color-success) 18%,transparent);border-color:var(--color-success)}._queueBtn_endtv_83{background:color-mix(in srgb,var(--accent) 8%,transparent);border-color:color-mix(in srgb,var(--accent) 25%,transparent);color:var(--accent-light)}._queueBtn_endtv_83:hover{background:color-mix(in srgb,var(--accent) 14%,transparent);border-color:color-mix(in srgb,var(--accent) 45%,transparent);color:var(--accent-lighter)}._queueActive_endtv_95{background:color-mix(in srgb,var(--accent) 15%,transparent);border-color:color-mix(in srgb,var(--accent) 60%,transparent);color:var(--accent-lighter)}._modeToggleBtn_endtv_101{background:#ffffff0d;color:var(--text-primary);border-color:#ffffff1a;flex:0 1 auto}._modeToggleActive_endtv_108{background:color-mix(in srgb,var(--color-info) 12%,transparent);border-color:color-mix(in srgb,var(--color-info) 45%,transparent);color:#dbeafe}._modeToggleActive_endtv_108:hover{background:color-mix(in srgb,var(--color-info) 18%,transparent);border-color:color-mix(in srgb,var(--color-info) 65%,transparent)}._nativeToggleBtn_endtv_119{background:#ffffff0d;color:var(--text-primary);border-color:#ffffff1a;flex:0 1 auto}._nativeToggleBtn_endtv_119:hover{background:#ffffff1f;border-color:#ffffff40;transform:translateY(-2px)}._nativeToggleActive_endtv_132{background:color-mix(in srgb,var(--color-warning) 10%,transparent);border-color:color-mix(in srgb,var(--color-warning) 40%,transparent);color:var(--color-warning)}._nativeToggleActive_endtv_132:hover{background:color-mix(in srgb,var(--color-warning) 16%,transparent);border-color:color-mix(in srgb,var(--color-warning) 60%,transparent)}._watchlistBtn_endtv_45:disabled{opacity:.7;cursor:wait;transform:none}._watchlistBtn_endtv_45:disabled:hover{transform:none}._markWatchedBtn_endtv_153{background:color-mix(in srgb,var(--color-info) 8%,transparent);border-color:color-mix(in srgb,var(--color-info) 25%,transparent);color:#93c5fd;flex:0 1 auto;justify-content:center}._markWatchedBtn_endtv_153:hover{background:color-mix(in srgb,var(--color-info) 14%,transparent);border-color:color-mix(in srgb,var(--color-info) 45%,transparent);color:#dbeafe}._markWatchedDone_endtv_167{background:color-mix(in srgb,var(--color-success) 12%,transparent);border-color:color-mix(in srgb,var(--color-success) 35%,transparent);color:var(--color-success)}._markWatchedDone_endtv_167:hover{background:color-mix(in srgb,var(--color-success) 18%,transparent);border-color:var(--color-success)}._completeSeriesBtn_endtv_178{background:color-mix(in srgb,var(--color-success) 10%,transparent);border-color:color-mix(in srgb,var(--color-success) 30%,transparent);color:#d1fae5;flex:0 1 auto;justify-content:center}._completeSeriesBtn_endtv_178:hover{background:color-mix(in srgb,var(--color-success) 16%,transparent);border-color:color-mix(in srgb,var(--color-success) 45%,transparent);color:#ecfdf5}._providerAndEpisodeRow_endtv_192{display:flex;align-items:flex-start;gap:1rem;flex-wrap:wrap}._episodeActions_endtv_199{display:flex;flex-wrap:wrap;gap:.75rem;margin:.5rem 0 .25rem}._desktopEpisodeActions_endtv_206{flex:1 1 320px;justify-content:flex-end;align-self:flex-end;margin:0}._mobileEpisodeActions_endtv_213{margin:.5rem 0 .25rem}._episodeNavBtn_endtv_217{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;border:1px solid var(--glass-border);border-radius:var(--radius-lg);padding:.7rem 1rem;background:#ffffff0d;color:var(--text-primary);cursor:pointer;font-size:.9rem;font-weight:700;transition:background-color var(--transition-fast),border-color var(--transition-fast),color var(--transition-fast)}._episodeNavBtn_endtv_217:hover:not(:disabled){border-color:var(--text-secondary);background:#ffffff14}._episodeNavBtn_endtv_217:disabled{opacity:.45;cursor:not-allowed;transform:none}._episodeNavBtn_endtv_217._primary_endtv_247{background:linear-gradient(135deg,var(--accent) 0%,var(--accent-dark) 100%);border-color:transparent;color:#fff}._episodeNavBtn_endtv_217._primary_endtv_247:hover:not(:disabled){background:linear-gradient(135deg,var(--accent-light) 0%,var(--accent) 100%)}._episodeNavBtn_endtv_217._secondary_endtv_257{background:#ffffff0a}._episodePickerBtn_endtv_261{display:none}._queueCountdown_endtv_265{position:absolute;left:50%;bottom:1.5rem;transform:translate(-50%);z-index:75;padding:.7rem 1rem;border:1px solid var(--glass-border);border-radius:var(--radius-md);background:#121216eb;color:#fff;font-weight:700;box-shadow:var(--glass-shadow)}._speedBoostBadge_endtv_280{position:absolute;top:1.5rem;left:50%;transform:translate(-50%);z-index:75;display:flex;align-items:center;gap:.7rem;padding:.65rem 1rem;border:1px solid color-mix(in srgb,var(--accent) 45%,transparent);border-radius:var(--radius-md);background:#121216eb;color:#f5f3ff;font-weight:800;letter-spacing:.06em;text-transform:none;box-shadow:var(--glass-shadow);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px)}._speedBoostBadge_endtv_280 span{line-height:1}._speedBoostBadge_endtv_280 svg{display:block;flex-shrink:0}._videoContainer_endtv_311{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#000;display:flex;justify-content:center;align-items:center;container-type:inline-size}._loadingOverlay_endtv_324{position:absolute;top:0;left:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;z-index:10;background:#00000080;border-radius:inherit}._loadingDots_endtv_338{display:flex;gap:10px}._dot_endtv_343{width:12px;height:12px;border-radius:50%;background-color:var(--accent);animation:_bounce_endtv_1 1.4s infinite ease-in-out both;box-shadow:0 0 10px var(--accent-glow)}._dot_endtv_343:nth-child(1){animation-delay:-.32s}._dot_endtv_343:nth-child(2){animation-delay:-.16s}@keyframes _bounce_endtv_1{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}._bufferingOverlay_endtv_372{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);pointer-events:none;z-index:20}._bufferingSpinner_endtv_381{width:50px;height:50px;border:4px solid rgba(255,255,255,.3);border-radius:50%;border-top-color:var(--accent);animation:_spin_endtv_921 1s ease-in-out infinite}@keyframes _spin_endtv_921{to{transform:rotate(360deg)}}._videoIframe_endtv_396{width:100%;height:100%;border:none;position:absolute;top:0;left:0;object-fit:cover;border-radius:inherit}._videoContainer_endtv_311 video{position:absolute;top:0;left:0;width:100%;height:100%;z-index:1;object-fit:contain;background-color:#000;border-radius:inherit}._videoContainer_endtv_311 video._videoElementHidden_endtv_419{opacity:0;pointer-events:none}._upscalerCanvas_endtv_424{position:absolute;top:0;left:0;width:100%;height:100%;z-index:2;object-fit:contain;opacity:0;pointer-events:none;border-radius:inherit}._upscalerCanvas_endtv_424._upscalerActive_endtv_437{opacity:1}._upscalerStatusBadge_endtv_441{position:absolute;top:12px;right:12px;z-index:4;background:#000000a6;color:var(--text-primary);font-size:.75rem;font-weight:600;padding:.35em .8em;border-radius:999px;border:1px solid var(--glass-border);pointer-events:none}._subtitleOverlay_endtv_456{position:absolute;top:0;left:0;width:100%;height:100%;z-index:3;pointer-events:none;overflow:hidden;border-radius:inherit}._controlsOverlay_endtv_468{position:absolute;inset:0;opacity:1;transition:opacity .4s cubic-bezier(.4,0,.2,1);z-index:50;pointer-events:none;display:flex;flex-direction:column;justify-content:space-between;border-radius:inherit;background:linear-gradient(to bottom,rgba(0,0,0,.35) 0%,rgba(0,0,0,.2) 15%,transparent 40%,transparent 60%,rgba(0,0,0,.2) 85%,rgba(0,0,0,.35) 100%);will-change:opacity}._controlsOverlay_endtv_468>*{pointer-events:auto}._controlsOverlay_endtv_468._hidden_endtv_498{opacity:0;pointer-events:none;cursor:none}._centerPlayPause_endtv_504{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:flex;align-items:center;justify-content:center;background:#0f0f11d9;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid rgba(255,255,255,.15);border-radius:50%;color:#fff;width:90px;height:90px;font-size:2.8rem;cursor:pointer;padding-left:6px;transition:all .3s cubic-bezier(.34,1.56,.64,1);box-shadow:none;will-change:transform}._centerPlayPause_endtv_504:hover{background:var(--accent);color:var(--accent-text);transform:translate(-50%,-50%) scale(1.1);border-color:var(--accent);box-shadow:0 0 40px var(--accent-glow)}._bottomControls_endtv_536{position:absolute;bottom:24px;left:24px;right:24px;display:flex;flex-direction:column;gap:0;background:#0f0f11f2;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid rgba(255,255,255,.1);border-radius:20px;padding:16px 24px;box-shadow:none}._progressBarContainer_endtv_553{width:100%;padding:10px 0;cursor:pointer;position:relative;touch-action:none;display:flex;align-items:center}._progressBar_endtv_553{width:100%;height:4px;background-color:#ffffff26;border-radius:4px;position:relative;transition:height .2s,background-color .2s;overflow:visible}._progressBarContainer_endtv_553:hover ._progressBar_endtv_553,._progressBarContainer_endtv_553._scrubbing_endtv_576 ._progressBar_endtv_553{height:6px;background-color:#ffffff40}._watchedBar_endtv_581,._bufferedBar_endtv_582{position:absolute;top:0;left:0;height:100%;border-radius:4px}._watchedBar_endtv_581{background:var(--accent);background:linear-gradient(90deg,var(--accent) 0%,var(--accent-light) 100%);z-index:2;box-shadow:0 0 15px var(--accent-glow);transition:width .3s linear;will-change:width}._bufferedBar_endtv_582{background-color:#ffffff4d;z-index:1}._thumb_endtv_604{position:absolute;top:50%;transform:translate(-50%,-50%) scale(0);width:16px;height:16px;background-color:#fff;border-radius:50%;z-index:3;transition:transform .2s cubic-bezier(.175,.885,.32,1.275),left .3s linear;box-shadow:0 0 0 4px #fff3;will-change:left}._progressBarContainer_endtv_553:hover ._thumb_endtv_604,._progressBarContainer_endtv_553._scrubbing_endtv_576 ._thumb_endtv_604{transform:translate(-50%,-50%) scale(1)}._timeBubble_endtv_625{position:absolute;bottom:35px;transform:translate(-50%);background:#000000e6;border:1px solid rgba(255,255,255,.1);color:#fff;padding:6px 10px;border-radius:8px;font-size:.85rem;font-weight:600;opacity:0;transition:opacity .2s;pointer-events:none;white-space:nowrap;box-shadow:0 4px 12px #0000004d}._progressBarContainer_endtv_553:hover ._timeBubble_endtv_625,._progressBarContainer_endtv_553._scrubbing_endtv_576 ._timeBubble_endtv_625{opacity:1}._skipSegment_endtv_648{position:absolute;top:0;height:100%;z-index:10;opacity:.5;transition:opacity .2s;border-radius:4px}._skipSegment_endtv_648:hover{opacity:.8}._skipSegment_endtv_648._op_endtv_662{background-color:var(--success)}._skipSegment_endtv_648._ed_endtv_666{background-color:var(--accent-blue)}._skipSegment_endtv_648._recap_endtv_670{background-color:var(--warning)}._bottomControlsRow_endtv_674{display:flex;justify-content:space-between;align-items:center;width:100%;margin-top:4px}._controlBtn_endtv_682{background:transparent;border:none;color:#ffffffe6;font-size:1.2rem;cursor:pointer;transition:all .2s;padding:10px;border-radius:12px;display:flex;align-items:center;justify-content:center}._controlBtn_endtv_682:hover{background:#ffffff1a;color:#fff;transform:translateY(-2px)}._controlBtn_endtv_682:active{transform:translateY(0)}._timeDisplay_endtv_706{font-size:.9rem;font-weight:500;color:#ffffffe6;font-variant-numeric:tabular-nums;margin-left:.5rem;opacity:.9}._leftControls_endtv_715,._rightControls_endtv_716,._middleControls_endtv_717{display:flex;align-items:center;gap:1.5rem}._skipControls_endtv_723{display:flex;align-items:center;gap:.25rem}._skipBtn_endtv_729{background:transparent;border:none;color:#fff;cursor:pointer;font-size:1.4rem;display:flex;align-items:center;justify-content:center;opacity:.7;transition:all .2s;padding:6px}._skipBtn_endtv_729:hover{opacity:1;transform:scale(1.1);color:var(--accent)}._skipBtn_endtv_729:active{transform:scale(.9)}._volumeContainer_endtv_753{display:flex;align-items:center;position:relative;padding-right:10px}._volumeSlider_endtv_760{width:0;opacity:0;transform:scaleX(0);transform-origin:left;transition:all .3s var(--ease-smooth);cursor:pointer;margin-left:6px;-webkit-appearance:none;appearance:none;background:transparent;height:30px}._volumeContainer_endtv_753:hover ._volumeSlider_endtv_760,._volumeSlider_endtv_760:focus{width:80px;opacity:1;transform:scaleX(1)}._volumeSlider_endtv_760::-webkit-slider-runnable-track{width:100%;height:4px;background:linear-gradient(to right,var(--accent) 0%,var(--accent) var(--volume-percent, 100%),rgba(255,255,255,.25) var(--volume-percent, 100%),rgba(255,255,255,.25) 100%);border-radius:4px;border:none}._volumeSlider_endtv_760::-moz-range-track{width:100%;height:4px;background:#ffffff40;border-radius:4px;border:none}._volumeSlider_endtv_760::-moz-range-progress{height:4px;background:var(--accent);border-radius:4px}._volumeSlider_endtv_760::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:14px;height:14px;background-color:#fff;border:2px solid var(--accent);border-radius:50%;cursor:pointer;margin-top:-5px;box-shadow:0 0 10px #00000080;transition:transform .1s ease}._volumeSlider_endtv_760::-webkit-slider-thumb:hover{transform:scale(1.2);box-shadow:0 0 15px var(--accent-glow)}._volumeSlider_endtv_760::-moz-range-thumb{width:14px;height:14px;background-color:#fff;border:2px solid var(--accent);border-radius:50%;cursor:pointer;box-shadow:0 0 10px #00000080}._settingsMenu_endtv_838{position:absolute;bottom:50px;right:0;background:#0f0f0ffa;border:1px solid rgba(255,255,255,.1);color:#fff;padding:1rem;border-radius:12px;width:280px;z-index:var(--z-dropdown);box-shadow:0 10px 40px #00000080;animation:_slideUp_endtv_1 .2s cubic-bezier(.16,1,.3,1);transform-origin:bottom right}@keyframes _slideUp_endtv_1{0%{opacity:0;transform:translateY(10px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}._settingsMenu_endtv_838 h4{margin:0 0 1rem;font-size:.95rem;text-transform:uppercase;letter-spacing:.05em;color:#ffffff80;border-bottom:1px solid rgba(255,255,255,.1);padding-bottom:.5rem;font-weight:600}._qualityListInMenu_endtv_877,._ccOptionsContainer_endtv_878{display:flex;flex-direction:column;gap:.25rem;max-height:300px;overflow-y:auto}._qualityItemInMenu_endtv_886,._ccItem_endtv_887{background:transparent;color:#fffc;border:none;padding:.75rem 1rem;border-radius:8px;cursor:pointer;text-align:left;font-size:.95rem;transition:all .2s;display:flex;justify-content:space-between;align-items:center}._qualityItemInMenu_endtv_886:hover,._ccItem_endtv_887:hover{background:#ffffff1a;color:#fff}._qualityItemInMenu_endtv_886._active_endtv_908,._ccItem_endtv_887._active_endtv_908{background:var(--accent);color:var(--accent-text);font-weight:600}._sourceLoader_endtv_915{padding:2rem;display:flex;justify-content:center}._spinner_endtv_921{width:40px;height:40px;border:4px solid rgba(255,255,255,.1);border-left-color:var(--accent);border-radius:50%;animation:_spin_endtv_921 1s linear infinite}@media(max-width:768px){._bottomControls_endtv_536{padding:.75rem;bottom:8px;left:8px;right:8px;border-radius:12px}._leftControls_endtv_715,._rightControls_endtv_716{gap:.5rem}._controlBtn_endtv_682{padding:8px;font-size:1.1rem}._timeDisplay_endtv_706{font-size:.75rem;margin-left:.25rem}._centerPlayPause_endtv_504{width:64px;height:64px;font-size:2rem}._titleContainer_endtv_9 h1{font-size:1.25rem}._volumeSlider_endtv_760{width:0}._volumeContainer_endtv_753{display:flex}._skipBubble_endtv_972{width:80px;height:80px}._skipIcon_endtv_976{font-size:1.8rem}._skipText_endtv_979{font-size:.8rem}._episodeActions_endtv_199{margin-top:.25rem}._controls_endtv_1{gap:.5rem}._watchBtn_endtv_991,._watchlistBtn_endtv_45,._queueBtn_endtv_83,._markWatchedBtn_endtv_153,._completeSeriesBtn_endtv_178,._nativeToggleBtn_endtv_119,._modeToggleBtn_endtv_101{flex:1 1 calc(50% - .25rem);min-width:0;padding:.5rem .75rem;font-size:.78rem;gap:.4rem}._watchBtn_endtv_991{flex-basis:100%}._episodeNavBtn_endtv_217{flex:1 1 calc(50% - .25rem);min-width:0;padding:.45rem .75rem;font-size:.8rem;min-height:44px}._episodePickerBtn_endtv_261{display:inline-flex;flex-basis:100%;padding:.4rem .75rem;font-size:.78rem;min-height:44px}._desktopEpisodeActions_endtv_206{display:none}._mobileEpisodeActions_endtv_213{display:flex}._providerAndEpisodeRow_endtv_192{flex-direction:column}._errorOverlay_endtv_1037{padding:1rem;gap:.5rem;overflow-y:auto}._errorOverlay_endtv_1037 p{font-size:1rem}._retryButton_endtv_1047{padding:.5rem 1.25rem;font-size:.9rem;margin-top:0}}@media(min-width:769px){._mobileEpisodeActions_endtv_213{display:none}}@media(max-width:900px)and (orientation:landscape){._bottomControls_endtv_536{bottom:8px;left:max(8px,env(safe-area-inset-left));right:max(8px,env(safe-area-inset-right));padding:.5rem 1rem}._centerPlayPause_endtv_504{width:54px;height:54px;font-size:1.5rem}._skipIndicatorContainer_endtv_1074._leftSkip_endtv_1074{left:max(0px,env(safe-area-inset-left))}._skipIndicatorContainer_endtv_1074._rightSkip_endtv_1077{right:max(0px,env(safe-area-inset-right))}}._sourceSelectionContainer_endtv_1082{display:flex;flex-direction:column;gap:1.5rem;background:var(--glass-bg);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid var(--glass-border);padding:2rem;border-radius:var(--radius-xl);width:100%;box-shadow:var(--glass-shadow);animation:_slideUpIn_endtv_1 .6s var(--ease-smooth)}._providerSelectContainer_endtv_1097{display:flex;flex-direction:column;gap:.75rem;width:100%}._providerSelect_endtv_1097,._sourceSelect_endtv_1082{height:48px;padding:0 1.25rem;background:var(--bg-tertiary);border:1px solid var(--border-primary);border-radius:var(--radius-lg);color:var(--text-primary);font-size:.95rem;font-family:inherit;cursor:pointer;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%2371717a' stroke-width='2' viewBox='0 0 12 12'%3E%3Cpolyline points='3 5 6 8 9 5'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 1.25rem center;transition:all var(--transition-fast);width:100%;max-width:400px}._providerSelect_endtv_1097:hover,._sourceSelect_endtv_1082:hover{border-color:var(--border-secondary);background-color:var(--bg-elevated)}._providerSelect_endtv_1097:focus,._sourceSelect_endtv_1082:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 4px var(--accent-glow)}._providerSelect_endtv_1097 option,._sourceSelect_endtv_1082 option{background:var(--bg-tertiary);color:var(--text-primary)}._sourceSelectionContainer_endtv_1082 h4,._providerSelectContainer_endtv_1097 h4{margin:0;color:var(--text-tertiary);font-weight:700;text-transform:uppercase;font-size:.75rem;letter-spacing:.1em}._sourceButtons_endtv_1153{display:flex;flex-wrap:wrap;gap:.75rem}._sourceButton_endtv_1153{background:#ffffff0d;color:var(--text-secondary);border:1px solid var(--glass-border);padding:.75rem 1.5rem;border-radius:var(--radius-lg);cursor:pointer;font-size:.95rem;font-weight:600;transition:all var(--transition-normal)}._sourceButton_endtv_1153:hover{background:#ffffff1a;border-color:var(--text-primary);color:var(--text-primary);transform:translateY(-2px)}._sourceButton_endtv_1153._active_endtv_908{background:var(--accent);border-color:var(--accent);color:var(--accent-text);box-shadow:0 8px 20px var(--accent-glow)}._detailsBox_endtv_1185{background:var(--glass-bg);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid var(--glass-border);padding:2.5rem;border-radius:var(--radius-xl);display:flex;flex-direction:column;gap:1.5rem;box-shadow:var(--glass-shadow)}._detailsToggleBtn_endtv_1198{background:#ffffff0d;border:1px solid var(--glass-border);color:var(--text-secondary);padding:.75rem 1.5rem;border-radius:var(--radius-lg);cursor:pointer;font-size:.95rem;font-weight:600;transition:all var(--transition-normal);display:flex;align-items:center;gap:.75rem;margin-top:2rem;width:fit-content}._detailsToggleBtn_endtv_1198:hover{background:#ffffff1a;color:var(--text-primary);border-color:var(--text-primary);transform:translateY(-2px)}._detailsGridContainer_endtv_1222{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:2rem;margin-top:2.5rem;background:#ffffff05;padding:2.5rem;border-radius:var(--radius-xl);border:1px solid var(--glass-border);animation:_fadeIn_endtv_1 .6s var(--ease-smooth)}._detailItem_endtv_1234{display:flex;flex-direction:column;gap:.5rem}._detailItem_endtv_1234 strong{color:var(--text-tertiary);font-weight:700;font-size:.7rem;text-transform:uppercase;letter-spacing:.1em}._detailItem_endtv_1234 span{color:var(--text-primary);font-size:1rem;font-weight:500}._animeStatus_endtv_1254{display:inline-block;background:color-mix(in srgb,var(--color-success) 10%,transparent);color:var(--color-success);padding:.25rem .75rem;border-radius:var(--radius-sm);font-size:.85rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;border:1px solid color-mix(in srgb,var(--color-success) 20%,transparent)}._genresList_endtv_1267{display:flex;flex-wrap:wrap;gap:.6rem;margin-top:.25rem}._genreTag_endtv_1274{background:#ffffff0d;color:var(--text-secondary);padding:.4rem 1rem;border-radius:var(--radius-full);font-size:.85rem;font-weight:600;border:1px solid var(--glass-border);transition:all var(--transition-fast)}._genreTag_endtv_1274:hover{background:#ffffff1a;color:var(--text-primary);border-color:var(--text-secondary);transform:translateY(-1px)}._loadingDetails_endtv_1292{color:var(--text-muted);font-style:italic;text-align:center;padding:2rem}._descriptionSection_endtv_1299{margin-top:2.5rem;padding:2.5rem;background:#ffffff05;border-radius:var(--radius-xl);border:1px solid var(--glass-border)}._descriptionSection_endtv_1299 h3{font-size:1.25rem;font-weight:800;margin-bottom:1.5rem;color:var(--text-primary);text-transform:uppercase;letter-spacing:.05em;display:flex;align-items:center;gap:.75rem}._descriptionSection_endtv_1299 h3:before{content:"";width:4px;height:20px;background:var(--accent);border-radius:var(--radius-full);box-shadow:0 0 10px var(--accent-glow)}._description_endtv_1299{line-height:1.8;color:var(--text-secondary);font-size:1.05rem;margin:0}._epJumpControls_endtv_1335{margin-bottom:1rem;padding:1rem;background:#ffffff08;border-radius:var(--radius-md);display:flex;flex-direction:column;gap:.8rem;border:1px solid var(--border-primary)}._epRangeButtons_endtv_1346{display:flex;flex-wrap:wrap;gap:.5rem}._metaTags_endtv_1352{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:1rem}._epRangeBtn_endtv_1359{background:transparent;border:1px solid var(--border-primary);color:var(--text-secondary);padding:.3rem .6rem;border-radius:4px;cursor:pointer;font-size:.8rem}._epRangeBtn_endtv_1359:hover{border-color:var(--accent);color:var(--accent)}._epJumpInputGroup_endtv_1374{display:flex;gap:.5rem}._epJumpInputGroup_endtv_1374 input{background:#0000004d;border:1px solid var(--border-primary);color:#fff;padding:.4rem .8rem;border-radius:4px;flex:1}._epJumpInputGroup_endtv_1374 input:focus{outline:none;border-color:var(--accent)}._epJumpInputGroup_endtv_1374 button{background:var(--accent);color:var(--accent-text);border:none;padding:.4rem 1rem;border-radius:4px;cursor:pointer}._epJumpInputGroup_endtv_1374 button:hover{background:var(--accent-dark)}._videoContainer_endtv_311._fullscreenActive_endtv_1406{cursor:none;border-radius:0}._videoContainer_endtv_311:fullscreen,._videoContainer_endtv_311:-webkit-full-screen{width:100vw;height:100vh;border-radius:0}._videoContainer_endtv_311:fullscreen video,._videoContainer_endtv_311:fullscreen canvas,._videoContainer_endtv_311:-webkit-full-screen video,._videoContainer_endtv_311:-webkit-full-screen canvas{border-radius:0}._skipIndicatorContainer_endtv_1074{position:absolute;top:0;width:30%;height:100%;display:flex;align-items:center;justify-content:center;pointer-events:none;z-index:60;overflow:hidden}._leftSkip_endtv_1074{left:0}._rightSkip_endtv_1077{right:0}._skipBubble_endtv_972{background:#fff3;width:120px;height:120px;border-radius:50%;display:flex;flex-direction:column;align-items:center;justify-content:center;animation:_skipRipple_endtv_1 .6s ease-out forwards}._skipIcon_endtv_976{font-size:2.5rem;color:#fff;margin-bottom:.5rem}._skipText_endtv_979{color:#fff;font-weight:700;font-size:1rem}@keyframes _skipRipple_endtv_1{0%{transform:scale(.5);opacity:0}50%{transform:scale(1.1);opacity:1}to{transform:scale(1.3);opacity:0}}._errorOverlay_endtv_1037{position:absolute;top:0;left:0;width:100%;height:100%;display:flex;flex-direction:column;justify-content:center;align-items:center;background:#000c;color:#fff;z-index:30;padding:2rem;text-align:center;gap:1rem;border-radius:inherit}._errorOverlay_endtv_1037 p{margin:0;font-size:1.2rem;font-weight:500}._errorSubtext_endtv_1512{color:var(--text-tertiary)!important;font-size:.9rem!important}._retryButton_endtv_1047{background:var(--accent);color:var(--accent-text);border:none;padding:.6rem 2rem;border-radius:var(--radius-md);font-weight:600;cursor:pointer;transition:all .2s;margin-top:.5rem;box-shadow:0 4px 12px var(--accent-glow)}._retryButton_endtv_1047:hover{background:var(--accent-light);transform:translateY(-2px)}._playerPageLayout_xfhzl_1{display:grid;grid-template-columns:320px 1fr;gap:2.5rem;padding:2rem;width:100%;max-width:1800px;margin:0 auto;align-items:start;min-height:calc(100vh - var(--header-height));background-color:transparent;animation:_fadeIn_xfhzl_1 .8s var(--ease-smooth)}._episodeSidebar_xfhzl_15{background:var(--glass-bg);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid var(--glass-border);border-radius:var(--radius-lg);overflow:visible;display:flex;flex-direction:column;box-shadow:var(--glass-shadow);animation:_slideUpIn_xfhzl_1 .8s var(--ease-smooth)}._playerMain_xfhzl_28{min-width:0;display:flex;flex-direction:column;gap:1.25rem;animation:_slideUpIn_xfhzl_1 .8s .1s var(--ease-smooth) both}._videoPlayerWrapper_xfhzl_36{width:100%;padding-top:56.25%;background-color:#000;border-radius:0;overflow:clip;isolation:isolate;position:relative;box-shadow:0 20px 50px #00000080;border:1px solid var(--glass-border)}._playerInfoHeader_xfhzl_52{display:flex;gap:2.5rem;align-items:flex-start}._playerAnimeCard_xfhzl_58{width:140px;flex-shrink:0;border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow-xl);border:1px solid var(--glass-border);transform:translateZ(0);aspect-ratio:2/3;height:auto}._playerInfoContainer_xfhzl_70{background:var(--glass-bg);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid var(--glass-border);padding:3rem;border-radius:var(--radius-2xl);box-shadow:var(--glass-shadow);display:flex;flex-direction:column}._playerAnimeCard_xfhzl_58 img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .6s cubic-bezier(.2,.8,.2,1)}._playerAnimeCard_xfhzl_58:hover img{transform:scale(1.1)}._videoTitleSection_xfhzl_94{flex:1;display:flex;flex-direction:column;justify-content:flex-start;gap:1.5rem}._videoTitleSection_xfhzl_94 h1{margin:0;font-size:2.5rem;line-height:1.1;font-weight:800;color:var(--text-primary);letter-spacing:-.02em}._videoDescription_xfhzl_111{color:var(--text-secondary);line-height:1.7;font-size:1.05rem}@keyframes _fadeIn_xfhzl_1{0%{opacity:0}to{opacity:1}}@keyframes _slideUpIn_xfhzl_1{0%{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)}}@media(max-width:1280px){._playerPageLayout_xfhzl_1{grid-template-columns:280px 1fr;padding:1.5rem;gap:1.5rem}}@media(max-width:1024px){._playerPageLayout_xfhzl_1{display:flex;flex-direction:column;gap:2rem}._episodeSidebar_xfhzl_15{width:100%;height:auto;max-height:500px;position:static;order:2;overflow-y:auto;-webkit-overflow-scrolling:touch}._playerMain_xfhzl_28{order:1;width:100%}._playerInfoHeader_xfhzl_52{padding:0}}@media(max-width:768px){._playerPageLayout_xfhzl_1{padding:1rem;gap:1.5rem}._episodeSidebar_xfhzl_15{display:none}._playerMain_xfhzl_28{gap:1.5rem}._videoPlayerWrapper_xfhzl_36{border-radius:0}._playerInfoContainer_xfhzl_70{border-radius:var(--radius-xl);padding:1.5rem}._playerInfoHeader_xfhzl_52{flex-direction:row;align-items:flex-start;text-align:left;gap:1.5rem}._videoTitleSection_xfhzl_94 h1{font-size:1.75rem}._playerAnimeCard_xfhzl_58{width:100px}._episodeSidebar_xfhzl_15{border-radius:var(--radius-lg)}}@media(max-width:480px){._playerInfoHeader_xfhzl_52{flex-direction:column;align-items:center;text-align:center}._playerAnimeCard_xfhzl_58{width:120px}}@media(max-width:480px)and (orientation:portrait){._videoPlayerWrapper_xfhzl_36{padding-top:100%;position:relative}}@media(min-width:481px)and (max-width:768px)and (orientation:portrait){._videoPlayerWrapper_xfhzl_36{padding-top:75%;position:relative}}@media(max-width:900px)and (orientation:landscape){._playerPageLayout_xfhzl_1{padding:0;gap:0}._videoPlayerWrapper_xfhzl_36{border-radius:0;height:100vh;padding-top:0}._playerInfoContainer_xfhzl_70{padding:1.5rem}}body.low-end ._playerPageLayout_xfhzl_1,body.low-end ._episodeSidebar_xfhzl_15,body.low-end ._playerInfoContainer_xfhzl_70{animation:none!important;backdrop-filter:none!important;-webkit-backdrop-filter:none!important;box-shadow:none!important;background:var(--bg-secondary)!important}body.low-end ._videoPlayerWrapper_xfhzl_36{box-shadow:none!important;border-color:var(--border-primary)!important}body.low-end ._episodeSidebar_xfhzl_15{background:var(--bg-secondary)!important}._theaterMode_xfhzl_276{display:flex!important;justify-content:center!important;align-items:center!important;position:fixed!important;top:0!important;left:0!important;width:100vw!important;height:100vh!important;min-height:100vh!important;max-width:none!important;padding:2.5rem!important;margin:0!important;background-color:var(--bg-main)!important;backdrop-filter:blur(24px)!important;-webkit-backdrop-filter:blur(24px)!important;animation:_fadeIn_xfhzl_1 .4s var(--ease-smooth)!important;overflow:hidden!important;z-index:var(--z-overlay)!important}._theaterMode_xfhzl_276 ._playerMain_xfhzl_28{width:90%!important;max-width:1200px!important;height:auto!important;aspect-ratio:16/9!important;gap:0!important;margin:auto!important;padding:0!important;animation:_scaleUpIn_xfhzl_1 .4s cubic-bezier(.25,1,.5,1)!important}._theaterMode_xfhzl_276 ._videoPlayerWrapper_xfhzl_36{width:100%!important;height:100%!important;padding-top:0!important;border-radius:0!important;border-color:#ffffff26!important;box-shadow:0 20px 60px color-mix(in srgb,var(--accent) 15%,transparent),0 0 100px #000000e6!important;overflow:hidden!important;position:relative!important}@keyframes _scaleUpIn_xfhzl_1{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}body.theater-mode header,body.theater-mode footer{display:none!important}body.theater-mode main{padding:0!important;max-width:none!important;margin:0!important;width:100vw!important;height:100vh!important}body.theater-mode{overflow:hidden!important;background:#000!important}@media(max-width:768px){._theaterMode_xfhzl_276{padding:1rem!important}._theaterMode_xfhzl_276 ._playerMain_xfhzl_28{width:100%!important;max-width:none!important}}._shell_41rxp_1{position:relative;width:100%;aspect-ratio:16 / 9;background:#000;overflow:clip;isolation:isolate;border-radius:0;container-type:inline-size;touch-action:manipulation;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none}@media(max-width:480px)and (orientation:portrait){._shell_41rxp_1{aspect-ratio:1 / 1}}@media(min-width:481px)and (max-width:768px)and (orientation:portrait){._shell_41rxp_1{aspect-ratio:4 / 3}}._shell_41rxp_1:fullscreen,._shell_41rxp_1:-webkit-full-screen{width:100vw;height:100vh;aspect-ratio:auto;border-radius:0}._shell_41rxp_1:fullscreen,._shell_41rxp_1:fullscreen ._stage_41rxp_37,._shell_41rxp_1:fullscreen ._stage_41rxp_37>video,._shell_41rxp_1:fullscreen ._stage_41rxp_37>canvas,._shell_41rxp_1:fullscreen ._stage_41rxp_37>iframe,._shell_41rxp_1:-webkit-full-screen,._shell_41rxp_1:-webkit-full-screen ._stage_41rxp_37,._shell_41rxp_1:-webkit-full-screen ._stage_41rxp_37>video,._shell_41rxp_1:-webkit-full-screen ._stage_41rxp_37>canvas,._shell_41rxp_1:-webkit-full-screen ._stage_41rxp_37>iframe{border-radius:0!important}@media(max-width:900px)and (orientation:landscape){._shell_41rxp_1{aspect-ratio:auto;height:100vh;border-radius:0}}._stage_41rxp_37{position:absolute;inset:0;z-index:1;touch-action:manipulation;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none}._stage_41rxp_37>video,._stage_41rxp_37>canvas,._stage_41rxp_37>iframe{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:contain;display:block;background:#000;border:none;border-radius:inherit;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none}._shellFill_41rxp_85{position:absolute;inset:0;width:100%;height:100%;aspect-ratio:auto}._shellFullscreen_41rxp_93,._shellFullscreen_41rxp_93 ._stage_41rxp_37,._shellFullscreen_41rxp_93 ._stage_41rxp_37>video,._shellFullscreen_41rxp_93 ._stage_41rxp_37>canvas,._shellFullscreen_41rxp_93 ._stage_41rxp_37>iframe{border-radius:0!important}._shellFullscreen_41rxp_93{width:100vw;height:100vh;aspect-ratio:auto}._uiLayer_41rxp_107{position:absolute;inset:0;z-index:50;pointer-events:none}._uiLayer_41rxp_107>*{pointer-events:auto}._volumeWrap_41rxp_118{display:flex;align-items:center;position:relative}._volumeSlider_41rxp_124{width:0;opacity:0;transform:scaleX(0);transform-origin:left center;transition:width .25s ease,opacity .25s ease,transform .25s ease;cursor:pointer;margin-left:8px;-webkit-appearance:none;appearance:none;background:transparent;height:24px;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none}._volumeWrap_41rxp_118[data-expanded=true] ._volumeSlider_41rxp_124{width:84px;opacity:1;transform:scaleX(1)}@media(hover:hover)and (pointer:fine){._volumeWrap_41rxp_118:hover ._volumeSlider_41rxp_124,._volumeSlider_41rxp_124:focus-visible{width:84px;opacity:1;transform:scaleX(1)}}._volumeSlider_41rxp_124::-webkit-slider-runnable-track{width:100%;height:4px;background:linear-gradient(to right,var(--accent) 0%,var(--accent) var(--volume-percent, 0%),rgba(255,255,255,.2) var(--volume-percent, 0%),rgba(255,255,255,.2) 100%);border-radius:4px}._volumeSlider_41rxp_124::-moz-range-track{width:100%;height:4px;background:#fff3;border-radius:4px}._volumeSlider_41rxp_124::-moz-range-progress{height:4px;background:var(--accent);border-radius:4px}._volumeSlider_41rxp_124::-webkit-slider-thumb{-webkit-appearance:none;height:12px;width:12px;border-radius:50%;background:#fff;margin-top:-4px;box-shadow:0 0 5px #00000080}@media(pointer:coarse){._volumeWrap_41rxp_118[data-expanded=true] ._volumeSlider_41rxp_124{width:96px;height:32px}}._centerControls_61n7k_1{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%) scale(1);display:flex;align-items:center;gap:3rem;gap:clamp(1rem,5cqw,3rem);z-index:10;pointer-events:auto;transition:transform .4s cubic-bezier(.2,.8,.2,1)}._centerSkipBtn_61n7k_15{background:transparent;border:none;border-radius:50%;color:#ffffffe6;width:56px;height:56px;width:clamp(2.5rem,6.5cqw,3.5rem);height:clamp(2.5rem,6.5cqw,3.5rem);font-size:1.8rem;font-size:clamp(1.1rem,2.8cqw,1.8rem);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .35s cubic-bezier(.34,1.56,.64,1);filter:drop-shadow(0 2px 8px rgba(0,0,0,.85))}._centerSkipBtn_61n7k_15:hover{color:#fff;transform:scale(1.15);filter:drop-shadow(0 2px 8px rgba(0,0,0,.85)) drop-shadow(0 0 14px var(--accent-glow))}._centerPlayPause_61n7k_40{display:flex;align-items:center;justify-content:center;background:transparent;border:none;border-radius:50%;color:#fff;width:76px;height:76px;width:clamp(3rem,8cqw,5rem);height:clamp(3rem,8cqw,5rem);font-size:2.2rem;font-size:clamp(1.4rem,3.4cqw,2.3rem);cursor:pointer;transition:all .35s cubic-bezier(.34,1.56,.64,1);filter:drop-shadow(0 2px 8px rgba(0,0,0,.85))}@media(pointer:coarse){._centerSkipBtn_61n7k_15{width:clamp(2.75rem,7cqw,3.75rem);height:clamp(2.75rem,7cqw,3.75rem)}}._centerPlayPause_61n7k_40:hover{transform:scale(1.08);filter:drop-shadow(0 2px 8px rgba(0,0,0,.85)) drop-shadow(0 0 16px var(--accent-glow))}._playIconOffset_61n7k_71{transform:translate(3px)}._statusArea_1xccu_1{display:flex;flex-direction:column;align-items:flex-start;gap:.6rem;background:var(--bg-secondary);border:1px solid var(--border-primary);padding:.75rem 1.25rem;border-radius:var(--radius-lg);margin-top:.5rem;margin-bottom:0;color:var(--text-secondary);box-shadow:var(--shadow-lg);width:fit-content;max-width:100%;-webkit-user-select:none;user-select:none;flex-wrap:wrap}._toggleBtn_1xccu_20{display:inline-flex;align-items:center;gap:.5rem;background:transparent;border:none;color:var(--text-secondary);font-size:.8rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;cursor:pointer;padding:.15rem .25rem;border-radius:var(--radius-sm);transition:color var(--transition-fast);pointer-events:auto}._toggleBtn_1xccu_20:hover{color:var(--text-primary)}._toggleBtn_1xccu_20 svg{color:var(--accent)}._shortcuts_1xccu_46{display:flex;gap:1.5rem;align-items:center;flex-wrap:wrap;padding-top:.6rem;border-top:1px solid var(--border-primary);width:100%}._shortcutGroup_1xccu_56{display:flex;gap:1rem;align-items:center;flex-wrap:nowrap}._shortcut_1xccu_46{display:flex;align-items:center;gap:.3rem;white-space:nowrap}._key_1xccu_70{background:#ffffff14;padding:.15rem .4rem;border-radius:5px;border:1px solid rgba(255,255,255,.1);font-weight:800;color:var(--text-primary);font-size:.75rem;min-width:1.4rem;height:1.4rem;display:flex;align-items:center;justify-content:center;font-family:var(--font-mono);box-shadow:0 1.5px #0000004d}._key_1xccu_70+._key_1xccu_70{margin-left:-.15rem}._label_1xccu_91{color:var(--text-tertiary);font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.03em}@media(max-width:768px){._statusArea_1xccu_1{display:none}}._episodeListContainer_9tyin_1{display:flex;flex-direction:column;height:100%;min-height:0;background:transparent}._drawerContainer_9tyin_9{height:auto;min-height:100%}._episodeListHeader_9tyin_14{background:transparent;padding:var(--space-6);border-bottom:1px solid var(--glass-border)}._drawerHeader_9tyin_20{position:sticky;top:0;z-index:5;background:#0c0c0ffa;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}._episodeListTitle_9tyin_29{margin:0 0 var(--space-5) 0;font-size:.85rem;font-weight:800;color:var(--text-tertiary);display:flex;align-items:center;gap:var(--space-3);letter-spacing:.1em;text-transform:uppercase}._rangeSelector_9tyin_41{display:flex;flex-wrap:wrap;gap:var(--space-2)}._rangeButton_9tyin_47{background:#ffffff0d;border:1px solid var(--glass-border);color:var(--text-secondary);padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);font-size:.8rem;font-weight:600;transition:all var(--transition-fast);cursor:pointer}._rangeButton_9tyin_47:hover{border-color:var(--text-secondary);color:var(--text-primary);background:#ffffff1a}._rangeButton_9tyin_47:focus-visible{outline:none;box-shadow:var(--shadow-focus)}._rangeButton_9tyin_47._active_9tyin_70{background:var(--accent);color:var(--accent-text);border-color:var(--accent)}._episodeList_9tyin_1{flex:1;display:flex;flex-direction:column;padding:var(--space-4);overflow-y:auto;-webkit-overflow-scrolling:touch;min-height:0;touch-action:pan-y;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.14) transparent}._drawerList_9tyin_89{overflow:visible;-webkit-overflow-scrolling:auto;touch-action:pan-y}._episodeList_9tyin_1::-webkit-scrollbar{width:6px}._episodeList_9tyin_1::-webkit-scrollbar-track{background:transparent}._episodeList_9tyin_1::-webkit-scrollbar-thumb{background:#ffffff24;background-clip:padding-box;border-radius:var(--radius-full);border:2px solid transparent}._episodeList_9tyin_1:hover::-webkit-scrollbar-thumb{background-color:#ffffff47}._episodeList_9tyin_1::-webkit-scrollbar-thumb:hover{background-color:var(--accent)}._episodeItem_9tyin_118{padding:var(--space-4) var(--space-5);margin-bottom:var(--space-2);border-radius:var(--radius-lg);cursor:pointer;transition:all var(--transition-normal);color:var(--text-secondary);font-weight:600;display:flex;justify-content:space-between;align-items:center;font-size:.9rem;border:1px solid transparent}._episodeItem_9tyin_118:hover{background:#ffffff0d;color:var(--text-primary);transform:translate(6px);border-color:var(--glass-border)}._episodeItem_9tyin_118:focus-visible{outline:none;box-shadow:var(--shadow-focus)}._episodeItem_9tyin_118._watched_9tyin_145{color:var(--text-muted);opacity:.6}._episodeItem_9tyin_118._active_9tyin_70{background:color-mix(in srgb,var(--accent) 10%,transparent);color:var(--accent-light);border:1px solid color-mix(in srgb,var(--accent) 30%,transparent)}._episodeItem_9tyin_118._active_9tyin_70:hover{transform:none;background:color-mix(in srgb,var(--accent) 15%,transparent)}._episodeItem_9tyin_118 span{display:flex;align-items:center;gap:var(--space-3)}._episodeThumb_9tyin_167{width:72px;aspect-ratio:16 / 9;object-fit:cover;border-radius:var(--radius-md);flex-shrink:0;background:#ffffff0f}._episodeText_9tyin_176{flex-direction:column!important;align-items:flex-start!important;gap:2px!important;min-width:0;flex:1}._episodeLabel_9tyin_184{font-weight:700;color:inherit}._episodeSub_9tyin_189{font-size:.78rem;font-weight:500;color:var(--text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}._shell_cys6s_1{position:fixed;inset:0;z-index:var(--z-drawer);display:flex;align-items:flex-end;justify-content:center}._overlay_cys6s_10{position:absolute;inset:0;border:0;padding:0;background:#0000008f;backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);animation:_fadeIn_cys6s_1 .2s ease-out}._drawer_cys6s_21{position:relative;z-index:1;width:min(100vw,100%);max-height:86vh;background:#0c0c0ffa;border-radius:var(--radius-lg) var(--radius-lg) 0 0;border:1px solid var(--glass-border);border-bottom:none;display:flex;flex-direction:column;overflow:hidden;animation:_slideUpIn_cys6s_1 .25s ease-out}@keyframes _fadeIn_cys6s_1{0%{opacity:0}to{opacity:1}}@keyframes _slideUpIn_cys6s_1{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}._handle_cys6s_56{width:52px;height:5px;border-radius:999px;background:#ffffff2e;margin:.7rem auto 0}._header_cys6s_64{display:flex;justify-content:space-between;align-items:flex-start;gap:1rem;padding:1rem 1rem .9rem;border-bottom:1px solid var(--glass-border)}._headerCopy_cys6s_73{display:flex;flex-direction:column;gap:.25rem}._kicker_cys6s_79{color:var(--text-tertiary);font-size:.72rem;font-weight:700;text-transform:uppercase;letter-spacing:.14em}._title_cys6s_87{margin:0;font-size:1.1rem;font-weight:800;color:var(--text-primary)}._closeBtn_cys6s_94{width:38px;height:38px;border-radius:999px;border:1px solid var(--glass-border);background:#ffffff0d;color:var(--text-primary);display:inline-flex;align-items:center;justify-content:center;cursor:pointer}._closeBtn_cys6s_94:hover{background:#ffffff1a}._closeBtn_cys6s_94:focus-visible{outline:none;box-shadow:var(--shadow-focus)}._body_cys6s_116{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;touch-action:pan-y}._row_1fq55_1{display:flex;flex-direction:column;gap:.3rem;padding:.5rem .75rem .6rem;min-width:0}._rowHeader_1fq55_9{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem;min-width:0}._rowLabel_1fq55_17{font-size:.68rem;font-weight:700;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}._rowValue_1fq55_28{font-size:.72rem;font-weight:600;color:var(--text-secondary);font-variant-numeric:tabular-nums;white-space:nowrap}._slider_1fq55_36{width:100%;min-width:8rem;cursor:pointer;-webkit-appearance:none;appearance:none;background:transparent;height:1.375rem;margin:0;touch-action:pan-y}._slider_1fq55_36::-webkit-slider-runnable-track{width:100%;height:4px;background:linear-gradient(to right,var(--accent) 0%,var(--accent) var(--slider-percent, 0%),rgba(255,255,255,.25) var(--slider-percent, 0%),rgba(255,255,255,.25) 100%);border-radius:4px;border:none}._slider_1fq55_36::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:12px;height:12px;background-color:#fff;border:2px solid var(--accent);border-radius:50%;cursor:pointer;margin-top:-4px;box-shadow:0 0 8px #00000080}._slider_1fq55_36::-moz-range-track{width:100%;height:4px;background:#ffffff40;border-radius:4px;border:none}._slider_1fq55_36::-moz-range-progress{height:4px;background:var(--accent);border-radius:4px}._slider_1fq55_36::-moz-range-thumb{width:10px;height:10px;background-color:#fff;border:2px solid var(--accent);border-radius:50%;cursor:pointer;box-shadow:0 0 8px #00000080}._swatches_1fq55_99{display:flex;gap:.375rem;align-items:center}._swatch_1fq55_99{width:1.25rem;height:1.25rem;padding:0;border-radius:50%;cursor:pointer;flex:0 0 auto}._swatchPicked_1fq55_114{box-shadow:0 0 0 2px var(--panel-bg, #141416),0 0 0 3.5px var(--accent)}._colorInput_1fq55_120{width:1.75rem;height:1.25rem;padding:0;border:none;border-radius:.3rem;background:transparent;cursor:pointer;flex:0 0 auto}._segmented_1fq55_131{display:flex;gap:.25rem;background:#ffffff0d;border:1px solid var(--glass-border);border-radius:var(--radius-sm);padding:.2rem}._segmented_1fq55_131 button{flex:1 1 0;min-width:0;background:transparent;border:none;border-radius:calc(var(--radius-sm) - 2px);color:var(--text-secondary);font-size:.72rem;font-weight:700;text-transform:capitalize;padding:.3rem .25rem;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:.3rem;white-space:nowrap}._segmented_1fq55_131 button:hover{color:var(--text-primary)}._segmented_1fq55_131 button[aria-pressed=true]{background:color-mix(in srgb,var(--accent) 25%,transparent);color:var(--accent-light)}@media(pointer:coarse){._row_1fq55_1{gap:.4rem;padding:.6rem .85rem .7rem}._slider_1fq55_36{height:1.875rem}._slider_1fq55_36::-webkit-slider-thumb{width:16px;height:16px;margin-top:-6px}._slider_1fq55_36::-moz-range-thumb{width:14px;height:14px}._swatch_1fq55_99{width:1.5rem;height:1.5rem}._segmented_1fq55_131 button{padding:.5rem .25rem;min-height:2.5rem}}