// Pure logic for the "More Episodes" strip in EpisodeFocusView. // // Extracted from the component so it can be unit-tested: the strip must never // collapse to just the current episode while real siblings exist, and it must // not mistake number-less episodes for the current one. // // TRACES: UR-048 | DR-062 import type { MediaItem } from "$lib/api/types"; /** * Does `ep` refer to the same episode as `current`? * * Matches by id first. Falls back to season+episode number, but ONLY when both * numbers are known on both sides — otherwise `undefined === undefined` would * mark every number-less episode as the current one (the bug that made the * whole strip look like the current episode). */ export function isCurrentEpisode(ep: MediaItem, current: MediaItem): boolean { if (ep.id === current.id) return true; if ( ep.indexNumber == null || current.indexNumber == null || ep.parentIndexNumber == null || current.parentIndexNumber == null ) { return false; } return ( ep.parentIndexNumber === current.parentIndexNumber && ep.indexNumber === current.indexNumber ); } /** * The window of episodes shown under the hero: up to 3 before and 6 after the * current episode. Degrades gracefully: * - prefers the current season, falling back to the full list when the season * is unknown (e.g. the episode was fetched directly on an API-ID mismatch); * - splices the current episode into the pool at its numeric position when it * isn't present, so it still anchors the window; * - returns just `[current]` only when there genuinely are no other episodes. */ export function adjacentEpisodes(current: MediaItem, allEpisodes: MediaItem[]): MediaItem[] { const seasonMatches = allEpisodes.filter( (e) => current.parentIndexNumber != null && e.parentIndexNumber === current.parentIndexNumber ); const pool = (seasonMatches.length > 0 ? seasonMatches : allEpisodes) .slice() .sort((a, b) => (a.indexNumber ?? 0) - (b.indexNumber ?? 0)); let idx = pool.findIndex((e) => isCurrentEpisode(e, current)); if (idx === -1) { const epNum = current.indexNumber ?? 0; const insertAt = pool.findIndex((e) => (e.indexNumber ?? 0) > epNum); idx = insertAt === -1 ? pool.length : insertAt; pool.splice(idx, 0, current); } const start = Math.max(0, idx - 3); const end = Math.min(pool.length, idx + 7); return pool.slice(start, end); }