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

+33
View File
@@ -27,6 +27,7 @@ use pstr_core::proton_drive_rs::ThumbnailType;
use pstr_core::proton_sdk::api::HumanVerificationCredential;
use pstr_core::proton_sdk::ids::{LinkId, VolumeId};
use pstr_core::shares::AccountFolder;
use pstr_core::subtitles::SubtitleTrack;
use pstr_core::sync::WatchSync;
use pstr_core::{
Account, AccountStore, SecretStore, Share, ShareClient, ShareStore, SharedLibrary,
@@ -739,6 +740,38 @@ fn chapter_kind(role: ChapterRole) -> ChapterKind {
}
}
/// One subtitle track as the player read it out of mpv's `track-list`.
#[derive(Debug, Clone, uniffi::Record)]
pub struct SubtitleTrackRecord {
pub id: i64,
pub language: Option<String>,
pub title: Option<String>,
pub default: bool,
pub forced: bool,
pub selected: bool,
}
/// The dialogue track to play in place of the subtitle track mpv chose, when
/// mpv chose the signs and songs (`pstr_core::subtitles`). Asked once per file,
/// as soon as mpv has loaded it and made its own pick.
#[uniffi::export]
pub fn full_subtitles_instead(tracks: Vec<SubtitleTrackRecord>) -> Option<i64> {
let tracks: Vec<SubtitleTrack> = tracks
.into_iter()
.map(|track| SubtitleTrack {
id: track.id,
language: track
.language
.filter(|language| !language.trim().is_empty()),
title: track.title.filter(|title| !title.trim().is_empty()),
default: track.default,
forced: track.forced,
selected: track.selected,
})
.collect();
pstr_core::subtitles::full_instead(&tracks)
}
/// What the app is holding on disk. Offline episodes are the viewer's, the
/// block cache is not: only the latter may be reclaimed without asking.
#[derive(Debug, Clone, uniffi::Record)]