feat(core,app,android,ios): play the dialogue, not the signs and songs

Anime releases often carry two subtitle tracks in one language, the
dialogue and a "Signs & Songs" track, and the second is as often as not
the one marked default or forced. mpv, picking by language, played it.

pstr_core::subtitles reads a track's name and forced flag for whether it
is the dialogue, and every client asks it once per file, after mpv has
made its own pick, whether to switch to a track in the same language
that is. A subtitle track the viewer chose by name stays, and subtitles
that are off stay off.
This commit is contained in:
nvrl committed 2026-10-14 18:18:40 +02:00
1 parent 8d615579e2
commit b1ea076ea3
10 files changed
+353 -7

No files matched your search

+13
View File
@@ -256,6 +256,7 @@ final class PlayerHost {
player?.onEnded = { [weak self] reason, media in self?.ended(reason, media) }
player?.onTick = { [weak self] in self?.tick() }
player?.onStateChanged = { [weak self] in self?.publishNowPlaying() }
player?.onLoaded = { [weak self] in self?.preferFullSubtitles() }
}
guard let player else {
playbackError = "Playback is unavailable: libmpv did not start."
@@ -433,6 +434,18 @@ final class PlayerHost {
chapters = chapterPlan(chapters: player.chapters, duration: player.duration > 0 ? player.duration : nil)
}
/// Play the dialogue rather than the signs and songs mpv picked by
/// language (`pstr_core::subtitles`). Not remembered: it is the choice mpv
/// should have made, not the viewer's.
private func preferFullSubtitles() {
guard let player, playingOpenEpisode else { return }
let subtitles = player.tracks.filter { $0.type == "sub" }.map {
SubtitleTrackRecord(id: $0.id, language: $0.language, title: $0.title, default: $0.isDefault, forced: $0.forced, selected: $0.selected)
}
guard let id = fullSubtitlesInstead(tracks: subtitles), let track = player.tracks.first(where: { $0.type == "sub" && $0.id == id }) else { return }
player.select(type: "sub", track: track)
}
/// The skip button's offer at the current position, if any.
var offer: SkipOffer? {
guard let player, playingOpenEpisode else { return nil }