One more step closer, revision to jtui style

This commit is contained in:
2026-02-16 12:58:40 -06:00
parent 46d50de1a9
commit 6a6a491681

View File

@@ -2359,53 +2359,61 @@ fn is_downloaded(_it: &core::jellyfin::JfItem) -> bool {
} }
fn is_played(it: &core::jellyfin::JfItem) -> bool { fn is_played(it: &core::jellyfin::JfItem) -> bool {
// direct played flag
if it.user_data.as_ref().and_then(|u| u.played) == Some(true) { if it.user_data.as_ref().and_then(|u| u.played) == Some(true) {
return true; return true;
} }
// aggregate containers: Season/Series can have UnplayedItemCount // Jellyfin often provides this for Season/Series
if matches!(it.item_type.as_deref(), Some("Season" | "Series")) { if matches!(it.item_type.as_deref(), Some("Season" | "Series")) {
if it.user_data.as_ref().and_then(|u| u.unplayed_item_count) == Some(0) { if it.user_data.as_ref().and_then(|u| u.unplayed_item_count) == Some(0) {
return true; return true;
} }
} }
// percent-based fallback (works for many types)
if let Some(p) = it.user_data.as_ref().and_then(|u| u.played_percentage) { if let Some(p) = it.user_data.as_ref().and_then(|u| u.played_percentage) {
if p >= 99.9 { return p >= 99.9;
return true;
}
} }
false false
} }
fn playback_position_ticks(it: &core::jellyfin::JfItem) -> Option<u64> {
it.user_data
.as_ref()
.and_then(|u| u.playback_position_ticks)
.and_then(|v| if v > 0 { Some(v as u64) } else { None })
}
fn is_in_progress_fast(it: &core::jellyfin::JfItem) -> bool { fn is_in_progress_fast(it: &core::jellyfin::JfItem) -> bool {
if is_played(it) { if is_played(it) {
return false; return false;
} }
match it.item_type.as_deref() { match it.item_type.as_deref() {
// Movies/Episodes: only if resume ticks exist
Some("Movie" | "Episode") => playback_position_ticks(it).is_some(), Some("Movie" | "Episode") => playback_position_ticks(it).is_some(),
// Seasons: only percent-based // THIS is the season fix (jtui-style)
Some("Season") => { Some("Season") => it.user_data
if let Some(p) = it.user_data.as_ref().and_then(|u| u.played_percentage) { .as_ref()
p > 0.1 && p < 99.9 .and_then(|u| u.played_percentage)
} else { .is_some_and(|p| p > 0.1 && p < 99.9),
false
}
}
// Series: NO in-progress marker (by your requirement) // Series: you said “complete or nothing”
Some("Series") => false, Some("Series") => false,
_ => false, _ => false,
} }
} }
fn emoji_prefix_for(_app: &App, it: &core::jellyfin::JfItem) -> &'static str {
if is_music_item(it) { return ""; }
if is_played(it) { return ""; }
if is_in_progress_fast(it) { return "🟠 "; }
""
}
fn is_in_progress(it: &core::jellyfin::JfItem) -> bool { fn is_in_progress(it: &core::jellyfin::JfItem) -> bool {
if is_played(it) { if is_played(it) {
return false; return false;
@@ -2428,32 +2436,6 @@ fn is_in_progress(it: &core::jellyfin::JfItem) -> bool {
} }
} }
fn playback_position_ticks(it: &core::jellyfin::JfItem) -> Option<u64> {
it.user_data
.as_ref()
.and_then(|u| u.playback_position_ticks)
.and_then(|v| if v > 0 { Some(v as u64) } else { None })
}
fn emoji_prefix_for(_app: &App, it: &core::jellyfin::JfItem) -> &'static str {
// no emoji for music
if is_music_item(it) {
return "";
}
if is_played(it) {
return "";
}
if is_in_progress(it) {
return "🟠 ";
}
""
}
// --- transcoding + resume URL builder (Approach B) --- // --- transcoding + resume URL builder (Approach B) ---
fn clamp_mbps_to_bps(mbps: u32) -> u32 { fn clamp_mbps_to_bps(mbps: u32) -> u32 {
mbps.saturating_mul(1_000_000) // Mb/s -> bits/s mbps.saturating_mul(1_000_000) // Mb/s -> bits/s
@@ -2495,17 +2477,6 @@ fn mpv_cmd_reply(socket: &PathBuf, cmd: &str) -> Result<Value, String> {
Ok(v) Ok(v)
} }
struct Panes { struct Panes {
top: Rect, top: Rect,
left_main: Rect, left_main: Rect,