local playback v1

This commit is contained in:
2026-02-16 17:24:54 -06:00
parent 60decb4499
commit 8f4be282ee
2 changed files with 64 additions and 2 deletions

View File

@@ -942,3 +942,57 @@ fn aria2_front_status(state: &Arc<Mutex<DownloadState>>) -> Option<String> {
let res = aria2_rpc_call("aria2.tellStatus", json!([gid, ["status"]])).ok()?; let res = aria2_rpc_call("aria2.tellStatus", json!([gid, ["status"]])).ok()?;
res.get("status").and_then(|v| v.as_str()).map(|s| s.to_string()) res.get("status").and_then(|v| v.as_str()).map(|s| s.to_string())
} }
/// If this item_id has a local download marker, return the local media file path.
/// Marker files are written alongside the media file as:
/// <media_filename>.<DOWNLOAD_MARKER_EXT>
/// where DOWNLOAD_MARKER_EXT == "cj-jftui.json"
pub fn local_media_path_for_item_id(item_id: &str) -> Option<PathBuf> {
fn walk(dir: &Path, item_id: &str) -> Option<PathBuf> {
if !dir.exists() {
return None;
}
let rd = fs::read_dir(dir).ok()?;
for ent in rd {
let p = ent.ok()?.path();
if p.is_dir() {
if let Some(found) = walk(&p, item_id) {
return Some(found);
}
continue;
}
let name = p.file_name().and_then(|s| s.to_str()).unwrap_or("");
if !name.ends_with(DOWNLOAD_MARKER_EXT) {
continue;
}
let raw = fs::read_to_string(&p).ok()?;
let v: Value = serde_json::from_str(raw.trim()).ok()?;
let id = v.get("item_id").and_then(|x| x.as_str())?;
if id == item_id {
// Marker file is "<media_filename>.<DOWNLOAD_MARKER_EXT>"
// So the media file path is the same path with that suffix stripped.
let suffix = format!(".{DOWNLOAD_MARKER_EXT}");
if !name.ends_with(&suffix) {
continue;
}
let media_name = &name[..name.len() - suffix.len()];
let media_path = p.with_file_name(media_name);
if media_path.exists() {
return Some(media_path);
}
}
}
None
}
let root = downloads_root().ok()?;
walk(&root, item_id)
}

View File

@@ -694,7 +694,11 @@ fn handle_key(app: &mut App, code: KeyCode) {
app.mpv_now_playing = Some(item_name); app.mpv_now_playing = Some(item_name);
// If mpv running -> replace, else spawn // If mpv running -> replace, else spawn
let url = build_jellyfin_play_url(&cfg, &item_id); let url = if let Some(p) = crate::core::downloads::local_media_path_for_item_id(&item_id) {
p.to_string_lossy().to_string()
} else {
build_jellyfin_play_url(&cfg, &item_id)
};
if let Some(mpv) = &app.mpv { if let Some(mpv) = &app.mpv {
match mpv_load_url(&cfg, mpv, &url) { match mpv_load_url(&cfg, mpv, &url) {
@@ -733,7 +737,11 @@ fn handle_key(app: &mut App, code: KeyCode) {
app.autoplay_next_index = if selected + 1 < len { Some(selected + 1) } else { None }; app.autoplay_next_index = if selected + 1 < len { Some(selected + 1) } else { None };
// load/replace or spawn // load/replace or spawn
let url = build_jellyfin_play_url(&cfg, &item_id); let url = if let Some(p) = crate::core::downloads::local_media_path_for_item_id(&item_id) {
p.to_string_lossy().to_string()
} else {
build_jellyfin_play_url(&cfg, &item_id)
};
if let Some(mpv) = &app.mpv { if let Some(mpv) = &app.mpv {
match mpv_load_url(&cfg, mpv, &url) { match mpv_load_url(&cfg, mpv, &url) {