/** * 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); }