diff --git a/README.md b/README.md index 61b1c65..0dde566 100644 --- a/README.md +++ b/README.md @@ -380,6 +380,13 @@ lidarr: - host: 192.168.0.86 port: 8686 api_token: ${MY_LIDARR_API_TOKEN} # Example of configuring using environment variables + monitored_storage_paths: # Filter which Root Folders or Disk Storage you want displayed in the UI's 'Stats' block + # Note: Setting these values does not affect what shows up in the 'Root Folders' tab of the UI. + - /nfs # An example disk (i.e. ' list disk-space' command) you want displayed in the UI under 'Storage:' + - /media # An example root folder you want displayed in the UI + # Root folders collapse up to the super-directory to reduce duplication in the UI. For example: + # if you have root folders '/media/tv', '/media/cartoons' and '/media/reality', and you set this + # monitored path, the UI will show '/media/[tv,cartoons,reality]' under Root Folders whisparr: - host: 192.168.0.69 port: 6969 diff --git a/src/ui/lidarr_ui/mod.rs b/src/ui/lidarr_ui/mod.rs index 22a4575..dbf277d 100644 --- a/src/ui/lidarr_ui/mod.rs +++ b/src/ui/lidarr_ui/mod.rs @@ -1,5 +1,3 @@ -use std::{cmp, iter}; - #[cfg(test)] use crate::ui::ui_test_utils::test_utils::Utc; use chrono::Duration; @@ -14,6 +12,7 @@ use ratatui::{ text::Text, widgets::Paragraph, }; +use std::{cmp, iter}; use super::{ DrawUi, draw_tabs, @@ -101,6 +100,8 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { start_time, .. } = &app.data.lidarr_data; + let monitored_disk_space_vec = extract_monitored_disk_space_vec(app, disk_space_vec.clone()); + let monitored_root_folders = extract_monitored_root_folders(app, root_folders.items.clone()); let mut constraints = vec![ Constraint::Length(1), @@ -111,7 +112,7 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { constraints.append( &mut iter::repeat_n( Constraint::Length(1), - disk_space_vec.len() + root_folders.items.len() + 1, + monitored_disk_space_vec.len() + monitored_root_folders.len() + 1, ) .collect(), ); @@ -147,7 +148,6 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { f.render_widget(uptime_paragraph, stat_item_areas[1]); f.render_widget(storage, stat_item_areas[2]); - let monitored_disk_space_vec = extract_monitored_disk_space_vec(app, disk_space_vec.clone()); for i in 0..monitored_disk_space_vec.len() { let DiskSpace { path, @@ -172,7 +172,6 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { f.render_widget(folders, stat_item_areas[monitored_disk_space_vec.len() + 3]); - let monitored_root_folders = extract_monitored_root_folders(app, root_folders.items.clone()); for i in 0..monitored_root_folders.len() { let RootFolder { path, free_space, .. diff --git a/src/ui/radarr_ui/mod.rs b/src/ui/radarr_ui/mod.rs index 9199a66..79561e9 100644 --- a/src/ui/radarr_ui/mod.rs +++ b/src/ui/radarr_ui/mod.rs @@ -93,6 +93,8 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { start_time, .. } = &app.data.radarr_data; + let monitored_disk_space_vec = extract_monitored_disk_space_vec(app, disk_space_vec.clone()); + let monitored_root_folders = extract_monitored_root_folders(app, root_folders.items.clone()); let mut constraints = vec![ Constraint::Length(1), @@ -103,7 +105,7 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { constraints.append( &mut iter::repeat_n( Constraint::Length(1), - disk_space_vec.len() + root_folders.items.len() + 1, + monitored_disk_space_vec.len() + monitored_root_folders.len() + 1, ) .collect(), ); @@ -139,7 +141,6 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { f.render_widget(uptime_paragraph, stat_item_areas[1]); f.render_widget(storage, stat_item_areas[2]); - let monitored_disk_space_vec = extract_monitored_disk_space_vec(app, disk_space_vec.clone()); for i in 0..monitored_disk_space_vec.len() { let DiskSpace { path, diff --git a/src/ui/sonarr_ui/mod.rs b/src/ui/sonarr_ui/mod.rs index 2a76585..bb5e45c 100644 --- a/src/ui/sonarr_ui/mod.rs +++ b/src/ui/sonarr_ui/mod.rs @@ -100,6 +100,8 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { start_time, .. } = &app.data.sonarr_data; + let monitored_disk_space_vec = extract_monitored_disk_space_vec(app, disk_space_vec.clone()); + let monitored_root_folders = extract_monitored_root_folders(app, root_folders.items.clone()); let mut constraints = vec![ Constraint::Length(1), @@ -110,7 +112,7 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { constraints.append( &mut iter::repeat_n( Constraint::Length(1), - disk_space_vec.len() + root_folders.items.len() + 1, + monitored_disk_space_vec.len() + monitored_root_folders.len() + 1, ) .collect(), ); @@ -146,7 +148,6 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { f.render_widget(uptime_paragraph, stat_item_areas[1]); f.render_widget(storage, stat_item_areas[2]); - let monitored_disk_space_vec = extract_monitored_disk_space_vec(app, disk_space_vec.clone()); for i in 0..monitored_disk_space_vec.len() { let DiskSpace { path, @@ -172,7 +173,6 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { f.render_widget(folders, stat_item_areas[monitored_disk_space_vec.len() + 3]); - let monitored_root_folders = extract_monitored_root_folders(app, root_folders.items.clone()); for i in 0..monitored_root_folders.len() { let RootFolder { path, free_space, .. diff --git a/src/ui/utils.rs b/src/ui/utils.rs index 3f4f791..c349314 100644 --- a/src/ui/utils.rs +++ b/src/ui/utils.rs @@ -9,7 +9,7 @@ use ratatui::layout::{Alignment, Constraint, Layout, Rect}; use ratatui::style::{Style, Stylize}; use ratatui::text::{Line, Span, Text}; use ratatui::widgets::{Block, BorderType, Borders, LineGauge, ListItem, Paragraph, Wrap}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; #[cfg(test)] @@ -280,17 +280,22 @@ pub(super) fn extract_monitored_disk_space_vec( if let Some(monitored_paths) = monitored_paths && !monitored_paths.is_empty() { - let paths: Vec = monitored_paths.iter().map(PathBuf::from).collect(); - disk_space_vec - .into_iter() - .filter(|it| { - if let Some(path) = it.path.as_ref() { - paths.iter().any(|p| path == p) - } else { - true + let monitored: HashSet<&str> = monitored_paths.iter().map(|s| s.as_str()).collect(); + let mut seen_paths = HashSet::new(); + let mut filtered_disk_space_vec = Vec::with_capacity(disk_space_vec.len()); + + for ds in disk_space_vec { + match ds.path.as_deref() { + None => filtered_disk_space_vec.push(ds), + Some(p) => { + if monitored.contains(p) && seen_paths.insert(p.to_owned()) { + filtered_disk_space_vec.push(ds) + } } - }) - .collect() + } + } + + filtered_disk_space_vec } else { disk_space_vec } diff --git a/src/ui/utils_tests.rs b/src/ui/utils_tests.rs index f5a401d..d8f073e 100644 --- a/src/ui/utils_tests.rs +++ b/src/ui/utils_tests.rs @@ -469,14 +469,19 @@ mod test { fn test_extract_monitored_disk_space_vec() { let mut app = App::test_default(); app.server_tabs.tabs[0].config = Some(ServarrConfig { - monitored_storage_paths: Some(vec!["/nfs".to_owned()]), + monitored_storage_paths: Some(vec!["/data".to_owned(), "/downloads".to_owned()]), ..ServarrConfig::default() }); let disk_space = DiskSpace { - path: Some("/nfs".to_string()), + path: Some("/data".to_string()), free_space: 10, total_space: 1000, }; + let disk_space_2 = DiskSpace { + path: Some("/downloads".to_string()), + free_space: 100, + total_space: 10000, + }; let disk_space_with_empty_path = DiskSpace { path: None, free_space: 10, @@ -486,17 +491,18 @@ mod test { disk_space.clone(), disk_space_with_empty_path.clone(), DiskSpace { - path: Some("/nfs/some/subpath".to_string()), - free_space: 10, - total_space: 1000, + path: Some("/downloads/".to_string()), + free_space: 100, + total_space: 10000, }, + disk_space_2.clone(), ]; let monitored_disk_space = extract_monitored_disk_space_vec(&app, disk_spaces); assert_eq!( monitored_disk_space, - vec![disk_space, disk_space_with_empty_path] + vec![disk_space, disk_space_with_empty_path, disk_space_2] ); }