fix(ios,android): offer the right episode as up next while the next one opens

After "next episode" during the credits, the previous file keeps
playing until the new stream is open. Its chapters and position were
read against the already-advanced index, so the up-next card offered
the episode after the next one for those seconds. The card, the skip
offer and auto-skip now only act on readings of the open episode.
This commit is contained in:
nvrl committed 2026-10-12 18:36:33 +02:00
1 parent 73973841c2
commit 0b9a9dab50
3 files changed
+16 -5

No files matched your search

@@ -333,7 +333,12 @@ fun PlayerScreen(
val problem by nativeHost.problem.collectAsState() val problem by nativeHost.problem.collectAsState()
LaunchedEffect(problem) { problem?.let { playbackError = it } } LaunchedEffect(problem) { problem?.let { playbackError = it } }
val offer = remember(chapters, key, (state.position * 4).roundToInt()) { // Whether mpv's readings are this episode's. Not while the next one opens:
// the last one's credits play on until it is loaded, and read against the
// new index they offered the episode after the next as up next.
val playingThis = state.media == key
val offer = remember(chapters, key, playingThis, (state.position * 4).roundToInt()) {
if (!playingThis) return@remember null
runCatching { skipOffer(chapters, state.position) }.getOrNull() runCatching { skipOffer(chapters, state.position) }.getOrNull()
} }
// Auto-skip is offered once per chapter: seeking back into an opening on // Auto-skip is offered once per chapter: seeking back into an opening on
@@ -475,7 +480,7 @@ fun PlayerScreen(
UpNextCard( UpNextCard(
next = next, next = next,
visible = autoplay && !state.paused && visible = autoplay && !state.paused && playingThis &&
chapters.creditsStart?.let { state.position >= it } == true, chapters.creditsStart?.let { state.position >= it } == true,
onPlayNow = { advance() }, onPlayNow = { advance() },
modifier = Modifier modifier = Modifier
+7 -2
View File
@@ -111,6 +111,11 @@ final class PlayerHost {
var episode: EpisodeRecord? { episodes.indices.contains(index) ? episodes[index] : nil } var episode: EpisodeRecord? { episodes.indices.contains(index) ? episodes[index] : nil }
var key: String? { episode.map(mediaKey) } var key: String? { episode.map(mediaKey) }
var next: EpisodeRecord? { episodes.indices.contains(index + 1) ? episodes[index + 1] : nil } var next: EpisodeRecord? { episodes.indices.contains(index + 1) ? episodes[index + 1] : nil }
/// Whether mpv's readings — position, chapters — are the open episode's.
/// Not while the next one opens: the last one's credits go on playing
/// until it is loaded, and read against the new index they offered the
/// episode *after* the next as up next.
var playingOpenEpisode: Bool { player?.media != nil && player?.media == key }
var isOpen: Bool { title != nil } var isOpen: Bool { title != nil }
// MARK: - Opening // MARK: - Opening
@@ -329,7 +334,7 @@ final class PlayerHost {
} }
private func tick() { private func tick() {
guard let player else { return } guard let player, playingOpenEpisode else { return }
replanChapters(player) replanChapters(player)
// Auto-skip, once per chapter. // Auto-skip, once per chapter.
if autoSkip, !player.paused, let offer = skipOffer(plan: chapters, position: player.position), if autoSkip, !player.paused, let offer = skipOffer(plan: chapters, position: player.position),
@@ -352,7 +357,7 @@ final class PlayerHost {
/// The skip button's offer at the current position, if any. /// The skip button's offer at the current position, if any.
var offer: SkipOffer? { var offer: SkipOffer? {
guard let player else { return nil } guard let player, playingOpenEpisode else { return nil }
return skipOffer(plan: chapters, position: player.position) return skipOffer(plan: chapters, position: player.position)
} }
+2 -1
View File
@@ -63,7 +63,8 @@ struct PlayerView: View {
} }
UpNextCard( UpNextCard(
next: host.next, next: host.next,
visible: host.autoplay && !player.paused && host.chapters.creditsStart.map { player.position >= $0 } == true, visible: host.autoplay && !player.paused && host.playingOpenEpisode
&& host.chapters.creditsStart.map { player.position >= $0 } == true,
onPlayNow: host.advance onPlayNow: host.advance
) )
} }