mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-03 16:41:26 +01:00
feat(app,player): choose audio and subtitle languages in desktop Settings
The phones have had this, and the desktop could only set languages from the player, after a file had already opened in the wrong one. Settings now has an audio language and a subtitle choice: off, the file's default, or a language. The names come from the list pstr-player already names tracks with, now exported as LANGUAGES.
This commit is contained in:
1 parent
eb67df0aa0
commit
5c15f45ace
4 files changed
+115
-8
No files matched your search
@@ -223,6 +223,13 @@ pub enum Action {
|
||||
PlayAdjacent(Adjacent),
|
||||
/// Start or stop the next episode playing on its own at the end of one.
|
||||
SetAutoplay(bool),
|
||||
/// The audio and subtitle languages a file is opened with, for titles
|
||||
/// without a choice of their own. `subtitles` false is "off".
|
||||
SetLanguages {
|
||||
audio: Option<String>,
|
||||
subtitles: bool,
|
||||
subtitle: Option<String>,
|
||||
},
|
||||
/// Play faster or slower, and keep doing so for the next file.
|
||||
SetSpeed(f64),
|
||||
/// Seek past openings and credits without being asked.
|
||||
@@ -1877,6 +1884,17 @@ impl App {
|
||||
prefs.autoplay_next = autoplay;
|
||||
self.engine.set_playback_prefs(prefs, true);
|
||||
}
|
||||
Action::SetLanguages {
|
||||
audio,
|
||||
subtitles,
|
||||
subtitle,
|
||||
} => {
|
||||
let mut prefs = self.engine.playback_prefs();
|
||||
prefs.audio_language = audio;
|
||||
prefs.subtitles = subtitles;
|
||||
prefs.subtitle_language = subtitle;
|
||||
self.engine.set_playback_prefs(prefs, true);
|
||||
}
|
||||
Action::SetAppearance(appearance) => {
|
||||
self.engine.set_appearance(appearance);
|
||||
theme::apply(ctx, appearance);
|
||||
@@ -2218,6 +2236,7 @@ impl eframe::App for App {
|
||||
let prefs = ui::settings::Prefs {
|
||||
autoplay: playback_prefs.autoplay_next,
|
||||
auto_skip: playback_prefs.auto_skip,
|
||||
playback: &playback_prefs,
|
||||
appearance: self.engine.appearance(),
|
||||
desktop: self.engine.desktop_prefs(),
|
||||
cache_used: (self.page == Page::Settings)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
use pstr_core::appearance::{Accent, Appearance, Flavor, Tiles};
|
||||
use pstr_core::metadata::{MetadataConfig, ProviderId, TitleNames};
|
||||
use pstr_core::prefs::PlaybackPrefs;
|
||||
use pstr_player::LANGUAGES;
|
||||
|
||||
use crate::app::Action;
|
||||
use crate::desktop_prefs::{CACHE_CHOICES, DesktopPrefs};
|
||||
@@ -19,9 +21,11 @@ use crate::ui;
|
||||
/// they are read-only, and passing them one by one is what pushed this
|
||||
/// function past the argument count anybody can read.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Prefs {
|
||||
pub struct Prefs<'a> {
|
||||
pub autoplay: bool,
|
||||
pub auto_skip: bool,
|
||||
/// The languages, read from here.
|
||||
pub playback: &'a PlaybackPrefs,
|
||||
pub appearance: Appearance,
|
||||
pub desktop: DesktopPrefs,
|
||||
/// What the streaming cache holds, once the shares are open.
|
||||
@@ -35,7 +39,7 @@ const COLUMN: f32 = 760.0;
|
||||
pub fn show(
|
||||
ui: &mut egui::Ui,
|
||||
settings: &mut MetadataConfig,
|
||||
prefs: Prefs,
|
||||
prefs: Prefs<'_>,
|
||||
api_key: &mut String,
|
||||
actions: &mut Vec<Action>,
|
||||
) {
|
||||
@@ -271,16 +275,18 @@ fn swatch(ui: &mut egui::Ui, appearance: Appearance, accent: Accent) -> egui::Re
|
||||
|
||||
/// The playback settings that are not per-file.
|
||||
///
|
||||
/// Volume, audio language, subtitle language and speed are all set from the
|
||||
/// player itself, where a viewer can hear the result.
|
||||
fn playback_form(ui: &mut egui::Ui, prefs: Prefs, actions: &mut Vec<Action>) {
|
||||
/// Volume and speed are set from the player itself, where a viewer can hear
|
||||
/// the result. The languages are here as well as there: what a first episode
|
||||
/// opens with is decided before there is a player to choose from.
|
||||
fn playback_form(ui: &mut egui::Ui, prefs: Prefs<'_>, actions: &mut Vec<Action>) {
|
||||
ui::widgets::settings_group(
|
||||
ui,
|
||||
"Playback",
|
||||
Some(
|
||||
"Volume, audio, subtitles and speed are set from the player, and remembered from there.",
|
||||
"Volume and speed are set from the player. A language chosen in the player for one show is kept for that show.",
|
||||
),
|
||||
|ui| {
|
||||
languages(ui, prefs.playback, actions);
|
||||
let mut autoplay = prefs.autoplay;
|
||||
ui::widgets::settings_row(
|
||||
ui,
|
||||
@@ -322,8 +328,80 @@ fn playback_form(ui: &mut egui::Ui, prefs: Prefs, actions: &mut Vec<Action>) {
|
||||
);
|
||||
}
|
||||
|
||||
/// The audio and subtitle languages every file opens with, until a show has a
|
||||
/// choice of its own.
|
||||
fn languages(ui: &mut egui::Ui, playback: &PlaybackPrefs, actions: &mut Vec<Action>) {
|
||||
let name = |tag: &Option<String>| match tag {
|
||||
Some(tag) => pstr_player::language_name(tag),
|
||||
None => "The file's default".to_owned(),
|
||||
};
|
||||
let audio = playback.audio_language.clone();
|
||||
ui::widgets::settings_row(
|
||||
ui,
|
||||
"Audio language",
|
||||
Some("Played when a file has it."),
|
||||
|ui| {
|
||||
egui::ComboBox::from_id_salt("audio-language")
|
||||
.selected_text(name(&audio))
|
||||
.show_ui(ui, |ui| {
|
||||
for choice in
|
||||
std::iter::once(None).chain(LANGUAGES.map(|tag| Some(tag.to_owned())))
|
||||
{
|
||||
if ui
|
||||
.selectable_label(audio == choice, name(&choice))
|
||||
.clicked()
|
||||
&& audio != choice
|
||||
{
|
||||
actions.push(Action::SetLanguages {
|
||||
audio: choice,
|
||||
subtitles: playback.subtitles,
|
||||
subtitle: playback.subtitle_language.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
// Off, the file's default, or a language: "off" is a decision of its
|
||||
// own, which no language expresses.
|
||||
let subtitle = playback
|
||||
.subtitles
|
||||
.then(|| playback.subtitle_language.clone());
|
||||
let subtitle_name = |choice: &Option<Option<String>>| match choice {
|
||||
None => "Off".to_owned(),
|
||||
Some(tag) => name(tag),
|
||||
};
|
||||
ui::widgets::settings_row(
|
||||
ui,
|
||||
"Subtitles",
|
||||
Some("Shown when a file has them in this language."),
|
||||
|ui| {
|
||||
egui::ComboBox::from_id_salt("subtitle-language")
|
||||
.selected_text(subtitle_name(&subtitle))
|
||||
.show_ui(ui, |ui| {
|
||||
let choices = [None, Some(None)]
|
||||
.into_iter()
|
||||
.chain(LANGUAGES.map(|tag| Some(Some(tag.to_owned()))));
|
||||
for choice in choices {
|
||||
if ui
|
||||
.selectable_label(subtitle == choice, subtitle_name(&choice))
|
||||
.clicked()
|
||||
&& subtitle != choice
|
||||
{
|
||||
actions.push(Action::SetLanguages {
|
||||
audio: playback.audio_language.clone(),
|
||||
subtitles: choice.is_some(),
|
||||
subtitle: choice.flatten(),
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// The streaming cache: how big it may get, and a way to empty it.
|
||||
fn storage_form(ui: &mut egui::Ui, prefs: Prefs, actions: &mut Vec<Action>) {
|
||||
fn storage_form(ui: &mut egui::Ui, prefs: Prefs<'_>, actions: &mut Vec<Action>) {
|
||||
ui::widgets::settings_group(
|
||||
ui,
|
||||
"Storage",
|
||||
|
||||
@@ -52,4 +52,4 @@ pub use player::{
|
||||
};
|
||||
pub use registry::{PROTOCOL, StreamHandle, StreamRegistry};
|
||||
pub use render::VideoRenderer;
|
||||
pub use tracks::{Track, TrackKind, language_name, pick_track};
|
||||
pub use tracks::{LANGUAGES, Track, TrackKind, language_name, pick_track};
|
||||
@@ -190,6 +190,16 @@ fn flag(mpv: &Mpv, index: i64, field: &str) -> bool {
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// The languages offered by name where a preference is chosen ahead of any
|
||||
/// file, as the three-letter tag a Matroska file carries. The same list
|
||||
/// [`language_name`] knows, and the phones' `LANGUAGES`; mpv treats the two-
|
||||
/// and three-letter forms as one.
|
||||
pub const LANGUAGES: [&str; 29] = [
|
||||
"jpn", "eng", "ger", "fre", "spa", "ita", "por", "dut", "rus", "pol", "swe", "dan", "nor",
|
||||
"fin", "cze", "hun", "tur", "ara", "heb", "hin", "kor", "chi", "tha", "vie", "ukr", "rum",
|
||||
"gre", "ind", "may",
|
||||
];
|
||||
|
||||
/// A readable name for a language tag, falling back to the tag itself.
|
||||
///
|
||||
/// Deliberately a short list rather than a full ISO 639 table: it covers what
|
||||
|
||||
Reference in new issue
Block a user