Fix stale preview reels when switching between series
Episode tiles were keyed by season/episode index only, so switching series reused the DOM: text bindings updated but <video> elements were kept, and browsers ignore patched <source> children without load() — the previous series' reels kept playing. - Key SeriesFullView by item id so a series switch fully remounts it. - Include a seriesIdentity (id, else root_id+title) in episode tile keys so tiles/videos remount even on an in-place series swap. - Reset episode cursor, audio owner and preview startup timers when the series identity changes, and re-gate video mounting.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
<!-- Full screen view for series -->
|
||||
<SeriesFullView
|
||||
v-if="item.type === 'series'"
|
||||
:key="item.id"
|
||||
:series="item.data as Series"
|
||||
:all-movies="allMovies"
|
||||
:focus-episode="focusEpisode"
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
<div class="episodes-grid">
|
||||
<div
|
||||
v-for="(episode, eIndex) in selectedSeason?.episodes || []"
|
||||
:key="`${selectedSeasonIndex}-${episode.episode_number}`"
|
||||
:key="`${seriesIdentity}-${selectedSeasonIndex}-${episode.episode_number}`"
|
||||
class="episode-tile"
|
||||
:class="{ 'episode-tile--ahead': isEpisodeAhead(eIndex) }"
|
||||
v-bind="getEpisodeNavAttrs(selectedSeasonIndex, eIndex)"
|
||||
@@ -258,7 +258,7 @@ import EpisodeReleaseMenu from "./EpisodeReleaseMenu.vue"
|
||||
import { sortTorrentsByPreference } from "../composables/useSettings"
|
||||
|
||||
const props = defineProps<{
|
||||
series: Series & { root_id?: string | null }
|
||||
series: Series & { id?: string; root_id?: string | null }
|
||||
allMovies: MovieUi[]
|
||||
focusEpisode?: { seasonNumber: number; episodeNumber: number } | null
|
||||
resumePoint?: SeriesResumePoint | null
|
||||
@@ -297,6 +297,15 @@ const selectedSeason = computed<Season | null>(
|
||||
() => props.series.seasons[selectedSeasonIndex.value] || null,
|
||||
)
|
||||
|
||||
// Stable identity of the displayed series. Episode tiles carry it in their
|
||||
// :key so swapping to another series remounts the tiles (and their <video>
|
||||
// elements) instead of patching <source> children — which browsers ignore,
|
||||
// leaving the previous series' preview reels playing.
|
||||
const seriesIdentity = computed(() => {
|
||||
const s = props.series
|
||||
return s.id ?? `${s.root_id ?? ""}:${s.title ?? ""}`
|
||||
})
|
||||
|
||||
function selectSeason(index: number) {
|
||||
if (index < 0 || index >= props.series.seasons.length) return
|
||||
if (selectedSeasonIndex.value === index) return
|
||||
@@ -1409,6 +1418,17 @@ watch(
|
||||
},
|
||||
)
|
||||
|
||||
// Series swapped under a reused component instance: tiles remount via the
|
||||
// :key, but per-episode state keyed by episode index alone (audio owner,
|
||||
// cursor, startup timers) survives — reset it so the old series' playback
|
||||
// and audio don't bleed into the new one.
|
||||
watch(seriesIdentity, () => {
|
||||
episodeCursorIndex.value = null
|
||||
setAudioOwner(null)
|
||||
stopEpisodePreviews()
|
||||
scheduleEpisodeMediaReady()
|
||||
})
|
||||
|
||||
// Episodes past the spoiler threshold (cursor or continue point) are faded;
|
||||
// stop their playback too.
|
||||
watch([episodeCursorIndex, resumePointGlobalIndex], () => {
|
||||
|
||||
Reference in New Issue
Block a user