diff --git a/src/core/jellyfin.rs b/src/core/jellyfin.rs index 200c9c1..d4e3878 100644 --- a/src/core/jellyfin.rs +++ b/src/core/jellyfin.rs @@ -173,7 +173,7 @@ impl JellyfinClient { ("Recursive", recursive), ("StartIndex", &start_index.to_string()), ("Limit", &limit.to_string()), - ("Fields", "PrimaryImageAspectRatio,SortName,ProductionYear,IndexNumber,ParentIndexNumber,RunTimeTicks,Genres,Studios,Overview,SeriesName"), + ("Fields", "PrimaryImageAspectRatio,SortName,ProductionYear,IndexNumber,ParentIndexNumber,RunTimeTicks,Genres,Studios,Overview,SeriesName,Album,AlbumArtist,Artists"), ("SortBy", sort_by), ("SortOrder", "Ascending"), ]); @@ -226,7 +226,7 @@ impl JellyfinClient { ("UserId", user_id), ("StartIndex", &start_index.to_string()), ("Limit", &limit.to_string()), - ("Fields", "PrimaryImageAspectRatio,SortName,ProductionYear,IndexNumber,ParentIndexNumber,RunTimeTicks,Genres,Studios,Overview,SeriesName"), + ("Fields", "PrimaryImageAspectRatio,SortName,ProductionYear,IndexNumber,ParentIndexNumber,RunTimeTicks,Genres,Studios,Overview,SeriesName,Album,AlbumArtist,Artists"), ]) .send() .map_err(|e| format!("request failed: {e}"))?; @@ -353,6 +353,15 @@ pub struct JfItem { #[serde(rename = "SeriesName")] pub series_name: Option, + + #[serde(rename = "Album")] + pub album: Option, + + #[serde(rename = "AlbumArtist")] + pub album_artist: Option, + + #[serde(rename = "Artists", default)] + pub artists: Vec, } #[derive(Deserialize)] diff --git a/src/main.rs b/src/main.rs index 5e5d23b..89bca23 100644 --- a/src/main.rs +++ b/src/main.rs @@ -976,8 +976,6 @@ fn ensure_loaded_to_end(app: &mut App) { } } - - fn ticks_to_runtime_str(ticks: Option) -> Option { // Jellyfin ticks are 10,000,000 per second let t = ticks?; @@ -996,6 +994,22 @@ fn ticks_to_runtime_str(ticks: Option) -> Option { } } +fn join_artists(artists: &[String]) -> String { + let names: Vec = artists + .iter() + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect(); + + if names.is_empty() { "-".into() } else { names.join(", ") } +} + +fn first_nonempty(opt: &Option) -> Option { + opt.as_deref() + .map(|s| s.trim()) + .filter(|s| !s.is_empty()) + .map(|s| s.to_string()) +} fn join_genres(genres: &[String]) -> String { if genres.is_empty() { "-".into() } else { genres.join(", ") } @@ -1046,7 +1060,6 @@ fn format_details(it: &core::jellyfin::JfItem) -> String { overview_str(&it.overview), ), - // Episodes: show SeriesName if present "Episode" => { let series = it.series_name.clone().unwrap_or_else(|| "-".into()); let se = match (it.parent_index_number, it.index_number) { @@ -1064,14 +1077,42 @@ fn format_details(it: &core::jellyfin::JfItem) -> String { ) } - // Music: you asked only Name + Runtime - "Audio" => format!( - "Name: {}\nRuntime: {}", - it.name, - ticks_to_runtime_str(it.run_time_ticks).unwrap_or_else(|| "-".into()), - ), + // --- Songs --- + "Audio" => { + let artist = first_nonempty(&it.album_artist) + .or_else(|| it.artists.get(0).cloned()) + .unwrap_or_else(|| "-".into()); + + let album = first_nonempty(&it.album).unwrap_or_else(|| "-".into()); + + format!( + "Name: {}\nRuntime: {}\nArtist: {}\nAlbum: {}\nYear: {}", + it.name, + ticks_to_runtime_str(it.run_time_ticks).unwrap_or_else(|| "-".into()), + artist, + album, + year_str(it.production_year), + ) + } + + // --- Albums --- + "MusicAlbum" => { + let artist = first_nonempty(&it.album_artist) + .or_else(|| it.artists.get(0).cloned()) + .unwrap_or_else(|| "-".into()); + + format!( + "Name: {}\nRuntime: {}\nArtist: {}\nYear: {}", + it.name, + ticks_to_runtime_str(it.run_time_ticks).unwrap_or_else(|| "-".into()), + artist, + year_str(it.production_year), + ) + } + + // --- Artists --- + "MusicArtist" => format!("Name: {}", it.name), - // fallback _ => format!( "Name: {}\nId: {}\nType: {:?}\nYear: {:?}", it.name, it.id, it.item_type, it.production_year @@ -1079,16 +1120,6 @@ fn format_details(it: &core::jellyfin::JfItem) -> String { } } - - - - - - - - - - fn ui(frame: &mut Frame, app: &App) { let area = frame.area(); let panes = layout(area);