Files
jellytau/src/lib/services/nextEpisodeService.ts
T
dtourolle d54d8cc7c4 refactor(logging): route frontend console calls through the logger
TRACES: | DR-204

484 ungated `console.*` calls across 63 non-test frontend files shipped to
end users with no way to turn them off. Mechanical substitution, no control
flow, error handling or message semantics changed:

  console.log / console.debug -> log.debug
  console.info                -> log.info
  console.warn                -> log.warn
  console.error               -> log.error

Hand-written `"[Scope] …"` prefixes are dropped where the logger's scope
now carries them; scope names that already existed are preserved verbatim
(`[Auth]`, `[VideoPlayer]`, `[PiP]`, …) and inferred from the filename
where a file had none. `src/routes/player/[id]/+page.svelte` keeps its
`NextEpisode` and `AutoPlay` sub-scopes as separate loggers rather than
flattening them into the page scope.

`grep -rn 'console\.' src/` now matches nothing outside the tests and the
facade itself.
2026-08-20 19:29:59 +02:00

79 lines
2.3 KiB
TypeScript

/**
* Next Episode Service
*
* Handles user interactions with the next episode popup.
* Backend manages countdown logic and autoplay decisions.
*
* Navigation uses goto() directly to load the next episode.
* The player page's $effect detects the URL param change and
* calls loadAndPlay for the new episode.
*
* TRACES: UR-023 | DR-047, DR-048
*/
import { goto } from "$app/navigation";
import { cancelAutoplayCountdown } from "$lib/api/autoplay";
import { nextEpisode } from "$lib/stores/nextEpisode";
import type { MediaItem } from "$lib/api/types";
import { createLogger } from "$lib/utils/logger";
const log = createLogger("NextEpisode");
/** Guard against double-navigation */
let isNavigating = false;
/**
* Cleanup next episode state (called on unmount/destroy)
*/
export function cleanup() {
nextEpisode.reset();
isNavigating = false;
}
/**
* Cancel the autoplay countdown
* Called when user clicks "Cancel" button on next episode popup
*/
export async function cancelAutoPlay() {
await cancelAutoplayCountdown();
nextEpisode.hidePopup();
}
/**
* Navigate to the next episode via goto().
* Uses replaceState to prevent history buildup when auto-advancing.
*
* Advancing to a next episode always starts that episode from the
* beginning, even if it was previously started or watched. The `restart`
* query param signals the player page to skip the resume-progress check.
*/
function navigateToEpisode(episode: MediaItem) {
if (isNavigating) {
log.warn("Already navigating, skipping duplicate navigation to", episode.id);
return;
}
isNavigating = true;
log.debug("Navigating to next episode:", episode.id, episode.name);
nextEpisode.hidePopup();
goto(`/player/${episode.id}?restart=true`, { replaceState: true }).finally(() => {
isNavigating = false;
});
}
/**
* Manually play the next episode
* Called when user clicks "Play Now" button on next episode popup
*/
export async function watchNextManually(nextEpisodeItem: MediaItem) {
await cancelAutoplayCountdown();
navigateToEpisode(nextEpisodeItem);
}
/**
* Auto-play the next episode when countdown reaches 0
* Called by playerEvents when countdown_tick event has remaining_seconds: 0
*/
export function autoPlayNext(nextEpisodeItem: MediaItem) {
navigateToEpisode(nextEpisodeItem);
}