Add episode queueing picker and configurable mpv video output

Activating an episode now opens a scope picker (episode / season / whole
show) and plays the chosen queue back-to-back in one mpv instance, with
only the first item honoring its resume position. A new mpv `vo` driver
setting (CLI flag, env var, config file, and settings TUI) lets users
opt into terminal-based vos like sixel/kitty/tct, which auto-enable
mpv's terminal input so playback keys still work.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
janisslsmandClaude Opus 4.7 committed 2026-05-22 15:46:17 +03:00
1 parent f0ebb4b9a4
commit 093b13f03e
9 files changed
+661 -72

No files matched your search

+18 -2
View File
@@ -20,14 +20,16 @@ enum Field {
Password,
AudioLang,
SubtitleLang,
VideoOutput,
}
const FIELDS: [Field; 5] = [
const FIELDS: [Field; 6] = [
Field::Server,
Field::User,
Field::Password,
Field::AudioLang,
Field::SubtitleLang,
Field::VideoOutput,
];
impl Field {
@@ -47,6 +49,7 @@ struct Form {
password: String,
audio_lang: String,
subtitle_lang: String,
video_output: String,
focus: Field,
error: Option<String>,
}
@@ -59,6 +62,7 @@ impl Form {
password: cfg.password.clone(),
audio_lang: cfg.audio_lang.clone().unwrap_or_default(),
subtitle_lang: cfg.subtitle_lang.clone().unwrap_or_default(),
video_output: cfg.video_output.clone().unwrap_or_default(),
focus: Field::Server,
error: None,
}
@@ -71,6 +75,7 @@ impl Form {
Field::Password => &mut self.password,
Field::AudioLang => &mut self.audio_lang,
Field::SubtitleLang => &mut self.subtitle_lang,
Field::VideoOutput => &mut self.video_output,
}
}
}
@@ -139,6 +144,7 @@ async fn apply(form: &mut Form, app: &mut App) -> Result<()> {
password: form.password.clone(),
audio_lang: trim_to_opt(&form.audio_lang),
subtitle_lang: trim_to_opt(&form.subtitle_lang),
video_output: trim_to_opt(&form.video_output),
};
let creds_changed = new_cfg.server != app.config.server
@@ -208,6 +214,7 @@ fn draw(f: &mut ratatui::Frame, form: &Form) {
Constraint::Length(3), // password
Constraint::Length(3), // audio lang
Constraint::Length(3), // subtitle lang
Constraint::Length(3), // video output
Constraint::Min(1), // error / help
])
.split(inner);
@@ -253,6 +260,15 @@ fn draw(f: &mut ratatui::Frame, form: &Form) {
),
rows[5],
);
f.render_widget(
field_widget(
"Video output (gpu/sixel/kitty/drm/tct, blank=mpv default)",
&form.video_output,
form.focus == Field::VideoOutput,
false,
),
rows[6],
);
let footer = if let Some(err) = &form.error {
Line::from(Span::styled(
@@ -265,5 +281,5 @@ fn draw(f: &mut ratatui::Frame, form: &Form) {
Style::default().fg(Color::DarkGray),
))
};
f.render_widget(Paragraph::new(footer), rows[6]);
f.render_widget(Paragraph::new(footer), rows[7]);
}