@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.
- package/README.md +1 -1
- package/client/dist/assets/{AnimeCard-I3S6qw9d.js → AnimeCard-BVdH0EeW.js} +1 -1
- package/client/dist/assets/{AnimeInfo-CLU2NSBR.js → AnimeInfo-Dw2Nop6Q.js} +1 -1
- package/client/dist/assets/{AnimeInfoPage-4Mw9ArDy.js → AnimeInfoPage-Cw-Tq3YX.js} +2 -2
- package/client/dist/assets/{Asmr-DN9XBc5Y.js → Asmr-BrjzzyOi.js} +1 -1
- package/client/dist/assets/{AsmrResetProgressModal-DyU1Z2Gw.js → AsmrResetProgressModal-CYjpcYpb.js} +1 -1
- package/client/dist/assets/{AvSyncCalibrator-DxL0mr_4.js → AvSyncCalibrator-BuHurVgo.js} +3 -3
- package/client/dist/assets/AvSyncCalibrator-D0FYSwiz.css +1 -0
- package/client/dist/assets/{Home-CeTeJ-gM.js → Home-KdnP7sNM.js} +1 -1
- package/client/dist/assets/{Insights-i34-qV4C.js → Insights-DDWry-t1.js} +1 -1
- package/client/dist/assets/{LibraryListPage-DE-ASOX2.js → LibraryListPage-CV8Z9JlO.js} +1 -1
- package/client/dist/assets/{ListeningList-DrNce5ll.js → ListeningList-DSMbMZT7.js} +1 -1
- package/client/dist/assets/{Manga-DOrf7Fk6.js → Manga-J6XoqEKq.js} +1 -1
- package/client/dist/assets/{MangaInfoPage-CAZMLMGw.js → MangaInfoPage-N7TxsN5u.js} +1 -1
- package/client/dist/assets/{MangaReadPage-DQVEo8_F.js → MangaReadPage-D_VzSNWq.js} +1 -1
- package/client/dist/assets/{MangaResetProgressModal-DOjpLAMp.js → MangaResetProgressModal-DyhQYBDy.js} +1 -1
- package/client/dist/assets/{Mature-D2xb9EGB.js → Mature-Db53R-LR.js} +1 -1
- package/client/dist/assets/{MediaCard-CKUEcFTd.js → MediaCard-BcLjlpfR.js} +1 -1
- package/client/dist/assets/{Music-w_a34r7r.js → Music-DjxQjJOl.js} +1 -1
- package/client/dist/assets/{OptionListMenu-DxZHBMIZ.js → OptionListMenu-DngTgHxH.js} +1 -1
- package/client/dist/assets/Player-Be_7Ne5p.js +56 -0
- package/client/dist/assets/{PlayerSettings-Ddlg-MvW.js → PlayerSettings-D47ItGDR.js} +1 -1
- package/client/dist/assets/{QueueOptionsButton-BMvAC6Ut.js → QueueOptionsButton-ClVTpH8J.js} +1 -1
- package/client/dist/assets/{QueueRail-CAoGJvHP.js → QueueRail-DeJKVRs5.js} +1 -1
- package/client/dist/assets/{Radio-6O7rAUv1.js → Radio-DU0ma3GA.js} +1 -1
- package/client/dist/assets/{ReadingList-k3g8oHvs.js → ReadingList-qXW7Dk5z.js} +1 -1
- package/client/dist/assets/{ResetProgressModal-vFC6N-Qw.js → ResetProgressModal-C6ukct_c.js} +1 -1
- package/client/dist/assets/{Search-Bh9Y6nIJ.js → Search-DjjKA_Wu.js} +1 -1
- package/client/dist/assets/{Settings-DRumujfh.js → Settings-DQt_S5Qw.js} +1 -1
- package/client/dist/assets/{SkeletonGrid-BtzUnQE0.js → SkeletonGrid-nx2HEi4P.js} +1 -1
- package/client/dist/assets/{Trackers-D0nqy8wu.js → Trackers-BcGzeqj2.js} +1 -1
- package/client/dist/assets/Tv-COvyHdeJ.js +4 -0
- package/client/dist/assets/{TvInfo-BkECbOKN.js → TvInfo-CUd1agFy.js} +1 -1
- package/client/dist/assets/{TvPopup-D8WWY6o4.js → TvPopup-CJeu3tgc.js} +1 -1
- package/client/dist/assets/{TvSearch-kWPep3gL.js → TvSearch-BIypjbU3.js} +1 -1
- package/client/dist/assets/{TvWatchlist-dD4emIHx.js → TvWatchlist-DZrBdiqZ.js} +1 -1
- package/client/dist/assets/{Watchlist-cWxw38nU.js → Watchlist-Dw8cPe0n.js} +1 -1
- package/client/dist/assets/{hls-uG_Y3i5s.js → hls-B3_1VuDP.js} +1 -1
- package/client/dist/assets/{index-BWIWE3Qy.js → index-gMxawd-E.js} +2 -2
- package/client/dist/assets/{useAnimeInfoData-DjPBg24u.js → useAnimeInfoData-_iOLEAa8.js} +1 -1
- package/client/dist/assets/{useAsmr-DQyobNQX.js → useAsmr-Blk4v41t.js} +1 -1
- package/client/dist/assets/{useAsmrPopup-CDoroMBD.js → useAsmrPopup-D9SQIgZa.js} +1 -1
- package/client/dist/assets/{useMangaLibrary-C6L8mrnS.js → useMangaLibrary-B60at6ni.js} +1 -1
- package/client/dist/assets/{useMangaPopup-BSuMa_vA.js → useMangaPopup-DEfGjWSu.js} +1 -1
- package/client/dist/assets/{useProviders-XIdvDof6.js → useProviders-Ca0bZWt7.js} +1 -1
- package/client/dist/assets/{useTv-DzSYasru.js → useTv-WJCDL5_5.js} +1 -1
- package/client/dist/assets/{useTvLibrary-BnbHS-gg.js → useTvLibrary-Fgn5i9rJ.js} +1 -1
- package/client/dist/index.html +1 -1
- package/package.json +2 -2
- package/server/dist/hono/proxy.js +102 -75
- package/server/dist/hono/tv.js +2 -2
- package/server/dist/hono/watchlist.js +2 -291
- package/server/dist/lib/anilist.js +0 -3
- package/server/dist/lib/mal.js +0 -38
- package/server/dist/lib/offline-db.js +0 -6
- package/server/dist/providers/remote-loader.js +15 -26
- package/server/dist/providers/title-matching.js +5 -5
- package/server/dist/repositories/manga.repository.js +0 -1
- package/server/dist/repositories/watched-episodes.repository.js +0 -2
- package/server/dist/utils/megaplay.utils.js +1 -2
- package/server/package.json +1 -1
- package/client/dist/assets/AvSyncCalibrator-C2wu1ydI.css +0 -1
- package/client/dist/assets/Player-B8YHeATv.js +0 -56
- 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
|
|
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 {
|
|
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
|
}
|
package/server/dist/lib/mal.js
CHANGED
|
@@ -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
|
|
269
|
+
function shapeOf(p) {
|
|
276
270
|
if (!p || typeof p !== 'object')
|
|
277
|
-
return
|
|
271
|
+
return null;
|
|
278
272
|
const o = p;
|
|
279
|
-
return
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
-
|
|
286
|
-
|
|
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
|
-
|
|
296
|
-
|
|
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
|
-
|
|
1
|
+
function normalizeCompact(s) {
|
|
2
2
|
return s.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
3
3
|
}
|
|
4
|
-
|
|
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
|
-
|
|
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
|
-
|
|
132
|
+
function distinctiveTokens(s) {
|
|
133
133
|
return [...new Set(tokenize(s).filter((w) => !STOPWORDS.has(w)))];
|
|
134
134
|
}
|
|
135
|
-
|
|
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
|
|
6
|
-
function base64UrlEncode(input) {
|
|
5
|
+
export function base64UrlEncode(input) {
|
|
7
6
|
return Buffer.from(input)
|
|
8
7
|
.toString('base64')
|
|
9
8
|
.replace(/\+/g, '-')
|
package/server/package.json
CHANGED
|
@@ -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}}
|