Finalized season in progress info

This commit is contained in:
2026-02-16 13:26:42 -06:00
parent 6a6a491681
commit 2c386df706
2 changed files with 47 additions and 12 deletions

View File

@@ -173,7 +173,7 @@ impl JellyfinClient {
("Recursive", recursive), ("Recursive", recursive),
("StartIndex", &start_index.to_string()), ("StartIndex", &start_index.to_string()),
("Limit", &limit.to_string()), ("Limit", &limit.to_string()),
("Fields", "PrimaryImageAspectRatio,SortName,ProductionYear,IndexNumber,ParentIndexNumber,RunTimeTicks,Genres,Studios,Overview,SeriesName,Album,AlbumArtist,Artists,UserData"), ("Fields", "PrimaryImageAspectRatio,SortName,ProductionYear,IndexNumber,ParentIndexNumber,RunTimeTicks,Genres,Studios,Overview,SeriesName,Album,AlbumArtist,Artists,UserData,ChildCount,RecursiveItemCount"),
("SortBy", sort_by), ("SortBy", sort_by),
("SortOrder", "Ascending"), ("SortOrder", "Ascending"),
]); ]);
@@ -250,7 +250,7 @@ pub fn playlist_items(
("UserId", user_id), ("UserId", user_id),
("StartIndex", &start_index.to_string()), ("StartIndex", &start_index.to_string()),
("Limit", &limit.to_string()), ("Limit", &limit.to_string()),
("Fields", "PrimaryImageAspectRatio,SortName,ProductionYear,IndexNumber,ParentIndexNumber,RunTimeTicks,Genres,Studios,Overview,SeriesName,Album,AlbumArtist,Artists,UserData"), ("Fields", "PrimaryImageAspectRatio,SortName,ProductionYear,IndexNumber,ParentIndexNumber,RunTimeTicks,Genres,Studios,Overview,SeriesName,Album,AlbumArtist,Artists,UserData,ChildCount,RecursiveItemCount"),
]) ])
.send() .send()
.map_err(|e| format!("request failed: {e}"))?; .map_err(|e| format!("request failed: {e}"))?;
@@ -383,6 +383,11 @@ pub struct JfItem {
#[serde(rename = "ProductionYear")] #[serde(rename = "ProductionYear")]
pub production_year: Option<i32>, pub production_year: Option<i32>,
#[serde(rename = "ChildCount")]
pub child_count: Option<i64>,
#[serde(rename = "RecursiveItemCount")]
pub recursive_item_count: Option<i64>,
#[serde(rename = "IndexNumber")] #[serde(rename = "IndexNumber")]
pub index_number: Option<i32>, pub index_number: Option<i32>,
#[serde(rename = "ParentIndexNumber")] #[serde(rename = "ParentIndexNumber")]

View File

@@ -1186,7 +1186,7 @@ fn format_details(it: &core::jellyfin::JfItem) -> String {
join_studios(&it.studios), join_studios(&it.studios),
); );
if is_in_progress(it) { if is_in_progress_fast(it) {
let pos = playback_position_ticks(it).unwrap_or(0); let pos = playback_position_ticks(it).unwrap_or(0);
let pos_secs = (pos / 10_000_000) as f64; let pos_secs = (pos / 10_000_000) as f64;
header.push_str(&format!( header.push_str(&format!(
@@ -1217,7 +1217,7 @@ fn format_details(it: &core::jellyfin::JfItem) -> String {
year_str(it.production_year), year_str(it.production_year),
); );
if is_in_progress(it) { if is_in_progress_fast(it) {
let pos = playback_position_ticks(it).unwrap_or(0); let pos = playback_position_ticks(it).unwrap_or(0);
let pos_secs = (pos / 10_000_000) as f64; let pos_secs = (pos / 10_000_000) as f64;
header.push_str(&format!( header.push_str(&format!(
@@ -2385,26 +2385,55 @@ fn playback_position_ticks(it: &core::jellyfin::JfItem) -> Option<u64> {
} }
fn is_in_progress_fast(it: &core::jellyfin::JfItem) -> bool { fn is_in_progress_fast(it: &core::jellyfin::JfItem) -> bool {
// Already fully played? Never in-progress.
if is_played(it) { if is_played(it) {
return false; return false;
} }
match it.item_type.as_deref() { match it.item_type.as_deref() {
Some("Movie" | "Episode") => playback_position_ticks(it).is_some(), // Movies / Episodes → resume ticks mean in-progress
Some("Movie" | "Episode") => {
playback_position_ticks(it).is_some()
}
// THIS is the season fix (jtui-style) // Seasons → jtui-style fast logic
Some("Season") => it.user_data Some("Season") => {
.as_ref() // 1) Prefer server percentage if present
.and_then(|u| u.played_percentage) if let Some(p) = it.user_data
.is_some_and(|p| p > 0.1 && p < 99.9), .as_ref()
.and_then(|u| u.played_percentage)
{
if p > 0.1 && p < 99.9 {
return true;
}
}
// Series: you said “complete or nothing” // 2) Fallback to count-based detection:
// 0 < UnplayedItemCount < ChildCount
let total = it.child_count
.and_then(|v| if v > 0 { Some(v as u64) } else { None });
let unplayed = it.user_data
.as_ref()
.and_then(|u| u.unplayed_item_count)
.and_then(|v| if v >= 0 { Some(v as u64) } else { None });
match (total, unplayed) {
(Some(t), Some(u)) if t > 0 && u > 0 && u < t => true,
_ => false,
}
}
// Series → complete or nothing (as you requested)
Some("Series") => false, Some("Series") => false,
_ => false, _ => false,
} }
} }
fn emoji_prefix_for(_app: &App, it: &core::jellyfin::JfItem) -> &'static str { fn emoji_prefix_for(_app: &App, it: &core::jellyfin::JfItem) -> &'static str {
if is_music_item(it) { return ""; } if is_music_item(it) { return ""; }
@@ -2413,7 +2442,7 @@ fn emoji_prefix_for(_app: &App, it: &core::jellyfin::JfItem) -> &'static str {
"" ""
} }
/*
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;
@@ -2435,6 +2464,7 @@ fn is_in_progress(it: &core::jellyfin::JfItem) -> bool {
_ => false, _ => false,
} }
} }
*/
// --- 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 {