From cb4cd93bcda95b9ae08baaa7fb51324b2990d263 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Sun, 13 Jul 2025 14:48:15 -0600 Subject: [PATCH] feat: Fetch more than 10 downloads when listing Sonarr downloads, and add a --count flag to the CLI to specify how many downloads to fetch --- src/app/sonarr/mod.rs | 6 +-- src/app/sonarr/sonarr_tests.rs | 16 +++---- src/cli/sonarr/list_command_handler.rs | 9 ++-- src/cli/sonarr/list_command_handler_tests.rs | 50 +++++++++++++++++++- src/network/sonarr_network.rs | 21 +++++--- src/network/sonarr_network_tests.rs | 8 ++-- 6 files changed, 84 insertions(+), 26 deletions(-) diff --git a/src/app/sonarr/mod.rs b/src/app/sonarr/mod.rs index a0fc76a..620784d 100644 --- a/src/app/sonarr/mod.rs +++ b/src/app/sonarr/mod.rs @@ -53,7 +53,7 @@ impl App<'_> { ) .await; self - .dispatch_network_event(SonarrEvent::GetDownloads.into()) + .dispatch_network_event(SonarrEvent::GetDownloads(500).into()) .await; } ActiveSonarrBlock::SeasonHistory => { @@ -108,7 +108,7 @@ impl App<'_> { } ActiveSonarrBlock::Downloads => { self - .dispatch_network_event(SonarrEvent::GetDownloads.into()) + .dispatch_network_event(SonarrEvent::GetDownloads(500).into()) .await; } ActiveSonarrBlock::Blocklist => { @@ -234,7 +234,7 @@ impl App<'_> { .dispatch_network_event(SonarrEvent::GetRootFolders.into()) .await; self - .dispatch_network_event(SonarrEvent::GetDownloads.into()) + .dispatch_network_event(SonarrEvent::GetDownloads(500).into()) .await; self .dispatch_network_event(SonarrEvent::GetDiskSpace.into()) diff --git a/src/app/sonarr/sonarr_tests.rs b/src/app/sonarr/sonarr_tests.rs index 46c1382..904379f 100644 --- a/src/app/sonarr/sonarr_tests.rs +++ b/src/app/sonarr/sonarr_tests.rs @@ -107,7 +107,7 @@ mod tests { ); assert_eq!( sync_network_rx.recv().await.unwrap(), - SonarrEvent::GetDownloads.into() + SonarrEvent::GetDownloads(500).into() ); assert!(!app.data.sonarr_data.prompt_confirm); assert_eq!(app.tick_count, 0); @@ -366,7 +366,7 @@ mod tests { assert!(app.is_loading); assert_eq!( sync_network_rx.recv().await.unwrap(), - SonarrEvent::GetDownloads.into() + SonarrEvent::GetDownloads(500).into() ); assert!(!app.data.sonarr_data.prompt_confirm); assert_eq!(app.tick_count, 0); @@ -604,7 +604,7 @@ mod tests { ); assert_eq!( sync_network_rx.recv().await.unwrap(), - SonarrEvent::GetDownloads.into() + SonarrEvent::GetDownloads(500).into() ); assert_eq!( sync_network_rx.recv().await.unwrap(), @@ -642,7 +642,7 @@ mod tests { ); assert_eq!( sync_network_rx.recv().await.unwrap(), - SonarrEvent::GetDownloads.into() + SonarrEvent::GetDownloads(500).into() ); assert_eq!( sync_network_rx.recv().await.unwrap(), @@ -667,7 +667,7 @@ mod tests { assert_eq!( sync_network_rx.recv().await.unwrap(), - SonarrEvent::GetDownloads.into() + SonarrEvent::GetDownloads(500).into() ); assert!(!app.data.sonarr_data.prompt_confirm); } @@ -692,7 +692,7 @@ mod tests { assert_eq!( sync_network_rx.recv().await.unwrap(), - SonarrEvent::GetDownloads.into() + SonarrEvent::GetDownloads(500).into() ); assert!(app.should_refresh); assert!(!app.data.sonarr_data.prompt_confirm); @@ -709,7 +709,7 @@ mod tests { assert_eq!( sync_network_rx.recv().await.unwrap(), - SonarrEvent::GetDownloads.into() + SonarrEvent::GetDownloads(500).into() ); assert!(app.is_loading); assert!(app.should_refresh); @@ -743,7 +743,7 @@ mod tests { ); assert_eq!( sync_network_rx.recv().await.unwrap(), - SonarrEvent::GetDownloads.into() + SonarrEvent::GetDownloads(500).into() ); assert!(app.is_loading); } diff --git a/src/cli/sonarr/list_command_handler.rs b/src/cli/sonarr/list_command_handler.rs index ca839fd..5a9ecda 100644 --- a/src/cli/sonarr/list_command_handler.rs +++ b/src/cli/sonarr/list_command_handler.rs @@ -21,7 +21,10 @@ pub enum SonarrListCommand { #[command(about = "List all items in the Sonarr blocklist")] Blocklist, #[command(about = "List all active downloads in Sonarr")] - Downloads, + Downloads { + #[arg(long, help = "How many downloads to fetch", default_value_t = 500)] + count: u64, + }, #[command(about = "List disk space details for all provisioned root folders in Sonarr")] DiskSpace, #[command(about = "List the episodes for the series with the given ID")] @@ -146,10 +149,10 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrListCommand> for SonarrListCommandH .await?; serde_json::to_string_pretty(&resp)? } - SonarrListCommand::Downloads => { + SonarrListCommand::Downloads { count } => { let resp = self .network - .handle_network_event(SonarrEvent::GetDownloads.into()) + .handle_network_event(SonarrEvent::GetDownloads(count).into()) .await?; serde_json::to_string_pretty(&resp)? } diff --git a/src/cli/sonarr/list_command_handler_tests.rs b/src/cli/sonarr/list_command_handler_tests.rs index 4c10a77..f2b015b 100644 --- a/src/cli/sonarr/list_command_handler_tests.rs +++ b/src/cli/sonarr/list_command_handler_tests.rs @@ -28,7 +28,6 @@ mod tests { #[values( "blocklist", "series", - "downloads", "disk-space", "quality-profiles", "indexers", @@ -102,6 +101,28 @@ mod tests { } } + #[test] + fn test_list_downloads_events_flag_requires_arguments() { + let result = + Cli::command().try_get_matches_from(["managarr", "sonarr", "list", "downloads", "--count"]); + + assert!(result.is_err()); + assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidValue); + } + + #[test] + fn test_list_downloads_default_values() { + let expected_args = SonarrListCommand::Downloads { count: 500 }; + let result = Cli::try_parse_from(["managarr", "sonarr", "list", "downloads"]); + + assert!(result.is_ok()); + + if let Some(Command::Sonarr(SonarrCommand::List(downloads_command))) = result.unwrap().command + { + assert_eq!(downloads_command, expected_args); + } + } + #[test] fn test_list_history_events_flag_requires_arguments() { let result = @@ -287,7 +308,6 @@ mod tests { #[rstest] #[case(SonarrListCommand::Blocklist, SonarrEvent::GetBlocklist)] - #[case(SonarrListCommand::Downloads, SonarrEvent::GetDownloads)] #[case(SonarrListCommand::DiskSpace, SonarrEvent::GetDiskSpace)] #[case(SonarrListCommand::Indexers, SonarrEvent::GetIndexers)] #[case(SonarrListCommand::QualityProfiles, SonarrEvent::GetQualityProfiles)] @@ -374,6 +394,32 @@ mod tests { assert!(result.is_ok()); } + #[tokio::test] + async fn test_handle_list_downloads_command() { + let expected_count = 1000; + let mut mock_network = MockNetworkTrait::new(); + mock_network + .expect_handle_network_event() + .with(eq::( + SonarrEvent::GetDownloads(expected_count).into(), + )) + .times(1) + .returning(|_| { + Ok(Serdeable::Sonarr(SonarrSerdeable::Value( + json!({"testResponse": "response"}), + ))) + }); + let app_arc = Arc::new(Mutex::new(App::test_default())); + let list_downloads_command = SonarrListCommand::Downloads { count: 1000 }; + + let result = + SonarrListCommandHandler::with(&app_arc, list_downloads_command, &mut mock_network) + .handle() + .await; + + assert!(result.is_ok()); + } + #[tokio::test] async fn test_handle_list_history_command() { let expected_events = 1000; diff --git a/src/network/sonarr_network.rs b/src/network/sonarr_network.rs index 51d3a27..854b8cd 100644 --- a/src/network/sonarr_network.rs +++ b/src/network/sonarr_network.rs @@ -55,7 +55,7 @@ pub enum SonarrEvent { EditSeries(EditSeriesParams), GetAllIndexerSettings, GetBlocklist, - GetDownloads, + GetDownloads(u64), GetHistory(u64), GetHostConfig, GetIndexers, @@ -108,7 +108,7 @@ impl NetworkResource for SonarrEvent { } SonarrEvent::GetEpisodeFiles(_) | SonarrEvent::DeleteEpisodeFile(_) => "/episodefile", SonarrEvent::GetBlocklist => "/blocklist?page=1&pageSize=10000", - SonarrEvent::GetDownloads | SonarrEvent::DeleteDownload(_) => "/queue", + SonarrEvent::GetDownloads(_) | SonarrEvent::DeleteDownload(_) => "/queue", SonarrEvent::GetEpisodes(_) | SonarrEvent::GetEpisodeDetails(_) => "/episode", SonarrEvent::GetHistory(_) | SonarrEvent::GetEpisodeHistory(_) => "/history", SonarrEvent::GetHostConfig | SonarrEvent::GetSecurityConfig => "/config/host", @@ -224,7 +224,10 @@ impl Network<'_, '_> { .await .map(SonarrSerdeable::from), SonarrEvent::GetBlocklist => self.get_sonarr_blocklist().await.map(SonarrSerdeable::from), - SonarrEvent::GetDownloads => self.get_sonarr_downloads().await.map(SonarrSerdeable::from), + SonarrEvent::GetDownloads(count) => self + .get_sonarr_downloads(count) + .await + .map(SonarrSerdeable::from), SonarrEvent::GetEpisodes(series_id) => self .get_episodes(series_id) .await @@ -1114,12 +1117,18 @@ impl Network<'_, '_> { .await } - async fn get_sonarr_downloads(&mut self) -> Result { + async fn get_sonarr_downloads(&mut self, count: u64) -> Result { info!("Fetching Sonarr downloads"); - let event = SonarrEvent::GetDownloads; + let event = SonarrEvent::GetDownloads(count); let request_props = self - .request_props_from(event, RequestMethod::Get, None::<()>, None, None) + .request_props_from( + event, + RequestMethod::Get, + None::<()>, + None, + Some(format!("pageSize={count}")), + ) .await; self diff --git a/src/network/sonarr_network_tests.rs b/src/network/sonarr_network_tests.rs index 3fa56bf..895c475 100644 --- a/src/network/sonarr_network_tests.rs +++ b/src/network/sonarr_network_tests.rs @@ -237,7 +237,7 @@ mod test { #[rstest] fn test_resource_queue( - #[values(SonarrEvent::GetDownloads, SonarrEvent::DeleteDownload(0))] event: SonarrEvent, + #[values(SonarrEvent::GetDownloads(0), SonarrEvent::DeleteDownload(0))] event: SonarrEvent, ) { assert_str_eq!(event.resource(), "/queue"); } @@ -1740,16 +1740,16 @@ mod test { None, Some(downloads_response_json), None, - SonarrEvent::GetDownloads, - None, + SonarrEvent::GetDownloads(500), None, + Some("pageSize=500"), ) .await; app_arc.lock().await.server_tabs.next(); let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); if let SonarrSerdeable::DownloadsResponse(downloads) = network - .handle_sonarr_event(SonarrEvent::GetDownloads) + .handle_sonarr_event(SonarrEvent::GetDownloads(500)) .await .unwrap() {