Every size cell converted bytes to gigabytes and formatted the float in place, so anything below a gigabyte rendered as 0.00 GB. Books and tracks are almost always that small, which made the Size columns in Readarr and Lidarr useless. Replace convert_to_gb and convert_f64_to_gb with one format_size helper that renders gigabytes at or above 1 GiB and megabytes below it, at the precision each site already used. Every render site across the four servarr trees now calls it, including the two movie and episode detail strings built on the network side. Values at or above a gigabyte render byte for byte as before. Give the Readarr book statistics and book file fixtures and the Lidarr album statistics fixture realistic sub-gigabyte sizes so the megabyte path is pinned by the committed snapshots.
224 lines
6.7 KiB
Rust
224 lines
6.7 KiB
Rust
use add_artist_ui::AddArtistUi;
|
|
use artist_details_ui::ArtistDetailsUi;
|
|
use delete_artist_ui::DeleteArtistUi;
|
|
use edit_artist_ui::EditArtistUi;
|
|
use ratatui::{
|
|
Frame,
|
|
layout::{Constraint, Rect},
|
|
widgets::{Cell, Row},
|
|
};
|
|
|
|
use crate::ui::widgets::managarr_table::ManagarrTable;
|
|
use crate::ui::widgets::{
|
|
confirmation_prompt::ConfirmationPrompt,
|
|
popup::{Popup, Size},
|
|
};
|
|
use crate::utils::format_size;
|
|
use crate::{
|
|
app::App,
|
|
models::{
|
|
Route,
|
|
lidarr_models::{Artist, ArtistStatus},
|
|
servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LIBRARY_BLOCKS},
|
|
},
|
|
ui::{
|
|
DrawUi,
|
|
styles::ManagarrStyle,
|
|
utils::{get_width_from_percentage, layout_block_top_border},
|
|
},
|
|
};
|
|
|
|
mod add_artist_ui;
|
|
mod album_details_ui;
|
|
mod artist_details_ui;
|
|
mod artist_overview_ui;
|
|
mod delete_album_ui;
|
|
mod delete_artist_ui;
|
|
mod edit_artist_ui;
|
|
mod track_details_ui;
|
|
|
|
#[cfg(test)]
|
|
#[path = "library_ui_tests.rs"]
|
|
mod library_ui_tests;
|
|
|
|
pub(super) struct LibraryUi;
|
|
|
|
impl DrawUi for LibraryUi {
|
|
fn accepts(route: Route) -> bool {
|
|
if let Route::Lidarr(active_lidarr_block, _) = route {
|
|
return AddArtistUi::accepts(route)
|
|
|| DeleteArtistUi::accepts(route)
|
|
|| EditArtistUi::accepts(route)
|
|
|| ArtistDetailsUi::accepts(route)
|
|
|| LIBRARY_BLOCKS.contains(&active_lidarr_block);
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|
let route = app.get_current_route();
|
|
draw_library(f, app, area);
|
|
|
|
match route {
|
|
_ if AddArtistUi::accepts(route) => AddArtistUi::draw(f, app, area),
|
|
_ if DeleteArtistUi::accepts(route) => DeleteArtistUi::draw(f, app, area),
|
|
_ if ArtistDetailsUi::accepts(route) => ArtistDetailsUi::draw(f, app, area),
|
|
_ if EditArtistUi::accepts(route) => EditArtistUi::draw(f, app, area),
|
|
Route::Lidarr(ActiveLidarrBlock::UpdateAllArtistsPrompt, _) => {
|
|
let confirmation_prompt = ConfirmationPrompt::new()
|
|
.title("Update All Artists")
|
|
.prompt("Do you want to update info and scan your disks for all of your artists?")
|
|
.yes_no_value(app.data.lidarr_data.prompt_confirm);
|
|
|
|
f.render_widget(
|
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
|
f.area(),
|
|
);
|
|
}
|
|
_ => (),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|
if let Route::Lidarr(active_lidarr_block, _) = app.get_current_route() {
|
|
let current_selection = if !app.data.lidarr_data.artists.items.is_empty() {
|
|
app.data.lidarr_data.artists.current_selection().clone()
|
|
} else {
|
|
Artist::default()
|
|
};
|
|
let quality_profile_map = &app.data.lidarr_data.quality_profile_map;
|
|
let metadata_profile_map = &app.data.lidarr_data.metadata_profile_map;
|
|
let tags_map = &app.data.lidarr_data.tags_map;
|
|
let content = Some(&mut app.data.lidarr_data.artists);
|
|
|
|
let artists_table_row_mapping = |artist: &Artist| {
|
|
artist.artist_name.scroll_left_or_reset(
|
|
get_width_from_percentage(area, 25),
|
|
*artist == current_selection,
|
|
app.should_text_scroll,
|
|
);
|
|
let monitored = if artist.monitored { "🏷" } else { "" };
|
|
let artist_type = artist.artist_type.clone().unwrap_or_default();
|
|
let size = artist
|
|
.statistics
|
|
.as_ref()
|
|
.map_or(0, |stats| stats.size_on_disk);
|
|
let quality_profile = quality_profile_map
|
|
.get_by_left(&artist.quality_profile_id)
|
|
.cloned()
|
|
.unwrap_or_default();
|
|
let metadata_profile = metadata_profile_map
|
|
.get_by_left(&artist.metadata_profile_id)
|
|
.cloned()
|
|
.unwrap_or_default();
|
|
let albums = artist
|
|
.statistics
|
|
.as_ref()
|
|
.map_or(0, |stats| stats.album_count);
|
|
let tracks = artist.statistics.as_ref().map_or(String::new(), |stats| {
|
|
format!("{}/{}", stats.track_file_count, stats.total_track_count)
|
|
});
|
|
let tags = artist
|
|
.tags
|
|
.iter()
|
|
.filter_map(|tag_id| {
|
|
let id = tag_id.as_i64()?;
|
|
tags_map.get_by_left(&id).cloned()
|
|
})
|
|
.collect::<Vec<_>>()
|
|
.join(", ");
|
|
|
|
decorate_artist_row_with_style(
|
|
artist,
|
|
Row::new(vec![
|
|
Cell::from(artist.artist_name.to_string()),
|
|
Cell::from(artist_type),
|
|
Cell::from(artist.status.to_display_str()),
|
|
Cell::from(quality_profile),
|
|
Cell::from(metadata_profile),
|
|
Cell::from(albums.to_string()),
|
|
Cell::from(tracks),
|
|
Cell::from(format_size(size, 2)),
|
|
Cell::from(monitored.to_owned()),
|
|
Cell::from(tags),
|
|
]),
|
|
)
|
|
};
|
|
let artists_table = ManagarrTable::new(content, artists_table_row_mapping)
|
|
.block(layout_block_top_border())
|
|
.loading(app.is_loading)
|
|
.sorting(active_lidarr_block == ActiveLidarrBlock::ArtistsSortPrompt)
|
|
.searching(active_lidarr_block == ActiveLidarrBlock::SearchArtists)
|
|
.filtering(active_lidarr_block == ActiveLidarrBlock::FilterArtists)
|
|
.search_produced_empty_results(active_lidarr_block == ActiveLidarrBlock::SearchArtistsError)
|
|
.filter_produced_empty_results(active_lidarr_block == ActiveLidarrBlock::FilterArtistsError)
|
|
.headers([
|
|
"Name",
|
|
"Type",
|
|
"Status",
|
|
"Quality Profile",
|
|
"Metadata Profile",
|
|
"Albums",
|
|
"Tracks",
|
|
"Size",
|
|
"Monitored",
|
|
"Tags",
|
|
])
|
|
.constraints([
|
|
Constraint::Percentage(22),
|
|
Constraint::Percentage(8),
|
|
Constraint::Percentage(8),
|
|
Constraint::Percentage(12),
|
|
Constraint::Percentage(12),
|
|
Constraint::Percentage(6),
|
|
Constraint::Percentage(8),
|
|
Constraint::Percentage(7),
|
|
Constraint::Percentage(6),
|
|
Constraint::Percentage(11),
|
|
]);
|
|
|
|
if [
|
|
ActiveLidarrBlock::SearchArtists,
|
|
ActiveLidarrBlock::FilterArtists,
|
|
]
|
|
.contains(&active_lidarr_block)
|
|
{
|
|
artists_table.show_cursor(f, area);
|
|
}
|
|
|
|
f.render_widget(artists_table, area);
|
|
}
|
|
}
|
|
|
|
fn decorate_artist_row_with_style<'a>(artist: &Artist, row: Row<'a>) -> Row<'a> {
|
|
if !artist.monitored {
|
|
return row.unmonitored();
|
|
}
|
|
|
|
match artist.status {
|
|
ArtistStatus::Ended => {
|
|
if let Some(ref stats) = artist.statistics {
|
|
return if stats.track_file_count == stats.total_track_count && stats.total_track_count > 0 {
|
|
row.downloaded()
|
|
} else {
|
|
row.missing()
|
|
};
|
|
}
|
|
row.indeterminate()
|
|
}
|
|
ArtistStatus::Continuing => {
|
|
if let Some(ref stats) = artist.statistics {
|
|
return if stats.track_file_count == stats.total_track_count && stats.total_track_count > 0 {
|
|
row.unreleased()
|
|
} else {
|
|
row.missing()
|
|
};
|
|
}
|
|
row.indeterminate()
|
|
}
|
|
_ => row.indeterminate(),
|
|
}
|
|
}
|