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

+12
View File
@@ -28,6 +28,11 @@ pub struct CliArgs {
#[arg(long, env = "EMBY_SUBTITLE_LANG")]
pub subtitle_lang: Option<String>,
/// mpv video output driver (e.g. `gpu`, `gpu-next`, `sixel`, `kitty`,
/// `drm`, `tct`). Forwarded to mpv's `vo`. Leave unset for mpv's default.
#[arg(long, env = "EMBY_VIDEO_OUTPUT")]
pub video_output: Option<String>,
/// Path to config file. Defaults: ./config.toml then $XDG_CONFIG_HOME/emby-client/config.toml
#[arg(short, long)]
pub config: Option<PathBuf>,
@@ -44,6 +49,7 @@ pub struct FileConfig {
pub password: Option<String>,
pub audio_lang: Option<String>,
pub subtitle_lang: Option<String>,
pub video_output: Option<String>,
}
#[derive(Debug, Clone)]
@@ -53,6 +59,9 @@ pub struct Config {
pub password: String,
pub audio_lang: Option<String>,
pub subtitle_lang: Option<String>,
/// mpv `vo` driver, e.g. `gpu`, `sixel`, `kitty`, `drm`, `tct`. `None`
/// means we don't touch the property and mpv picks its default.
pub video_output: Option<String>,
}
pub fn load(args: &CliArgs) -> Result<(Option<Config>, Option<PathBuf>)> {
@@ -63,6 +72,7 @@ pub fn load(args: &CliArgs) -> Result<(Option<Config>, Option<PathBuf>)> {
let password = args.password.clone().or(file_cfg.password);
let audio_lang = args.audio_lang.clone().or(file_cfg.audio_lang);
let subtitle_lang = args.subtitle_lang.clone().or(file_cfg.subtitle_lang);
let video_output = args.video_output.clone().or(file_cfg.video_output);
let cfg = match (server, user, password) {
(Some(server), Some(user), Some(password)) => Some(Config {
@@ -71,6 +81,7 @@ pub fn load(args: &CliArgs) -> Result<(Option<Config>, Option<PathBuf>)> {
password,
audio_lang,
subtitle_lang,
video_output,
}),
_ => None,
};
@@ -124,6 +135,7 @@ pub fn save(path: &Path, cfg: &Config) -> Result<()> {
password: Some(cfg.password.clone()),
audio_lang: cfg.audio_lang.clone(),
subtitle_lang: cfg.subtitle_lang.clone(),
video_output: cfg.video_output.clone(),
};
let text = toml::to_string_pretty(&file_cfg).context("serializing config")?;
std::fs::write(path, text).with_context(|| format!("writing {}", path.display()))?;