From 8dfa664a060feb8392f56562f6dfb6a3cbf0fc6a Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 15 Jan 2026 11:39:34 -0700 Subject: [PATCH] feat: CLI support for searching for discography releases in Lidarr --- src/cli/lidarr/lidarr_command_tests.rs | 31 +++++- .../lidarr/manual_search_command_handler.rs | 71 ++++++++++++++ .../manual_search_command_handler_tests.rs | 98 +++++++++++++++++++ src/cli/lidarr/mod.rs | 11 +++ src/models/lidarr_models.rs | 25 +++++ src/models/lidarr_models_tests.rs | 16 ++- src/models/servarr_data/lidarr/lidarr_data.rs | 14 ++- .../servarr_data/lidarr/lidarr_data_tests.rs | 7 +- .../artists/lidarr_artists_network_tests.rs | 85 +++++++++++++++- .../lidarr_network/library/artists/mod.rs | 35 ++++++- .../lidarr_network_test_utils.rs | 51 +++++++++- .../lidarr_network/lidarr_network_tests.rs | 5 + src/network/lidarr_network/mod.rs | 6 ++ 13 files changed, 445 insertions(+), 10 deletions(-) create mode 100644 src/cli/lidarr/manual_search_command_handler.rs create mode 100644 src/cli/lidarr/manual_search_command_handler_tests.rs diff --git a/src/cli/lidarr/lidarr_command_tests.rs b/src/cli/lidarr/lidarr_command_tests.rs index bf52288..b0cbe46 100644 --- a/src/cli/lidarr/lidarr_command_tests.rs +++ b/src/cli/lidarr/lidarr_command_tests.rs @@ -136,7 +136,7 @@ mod tests { #[test] fn test_start_task_requires_task_name() { - let result = Cli::command().try_get_matches_from(["managarr", "sonarr", "start-task"]); + let result = Cli::command().try_get_matches_from(["managarr", "lidarr", "start-task"]); assert_err!(&result); assert_eq!( @@ -232,6 +232,7 @@ mod tests { use crate::cli::lidarr::add_command_handler::LidarrAddCommand; use crate::cli::lidarr::edit_command_handler::LidarrEditCommand; use crate::cli::lidarr::get_command_handler::LidarrGetCommand; + use crate::cli::lidarr::manual_search_command_handler::LidarrManualSearchCommand; use crate::cli::lidarr::refresh_command_handler::LidarrRefreshCommand; use crate::cli::lidarr::trigger_automatic_search_command_handler::LidarrTriggerAutomaticSearchCommand; use crate::models::lidarr_models::LidarrTaskName; @@ -436,6 +437,34 @@ mod tests { assert_ok!(&result); } + #[tokio::test] + async fn test_lidarr_cli_handler_delegates_manual_search_commands_to_the_manual_search_command_handler() + { + let expected_artist_id = 1; + let mut mock_network = MockNetworkTrait::new(); + mock_network + .expect_handle_network_event() + .with(eq::( + LidarrEvent::GetDiscographyReleases(expected_artist_id).into(), + )) + .times(1) + .returning(|_| { + Ok(Serdeable::Lidarr(LidarrSerdeable::Value( + json!({"testResponse": "response"}), + ))) + }); + let app_arc = Arc::new(Mutex::new(App::test_default())); + let manual_episode_search_command = + LidarrCommand::ManualSearch(LidarrManualSearchCommand::Discography { artist_id: 1 }); + + let result = + LidarrCliHandler::with(&app_arc, manual_episode_search_command, &mut mock_network) + .handle() + .await; + + assert_ok!(&result); + } + #[tokio::test] async fn test_lidarr_cli_handler_delegates_trigger_automatic_search_commands_to_the_trigger_automatic_search_command_handler() { diff --git a/src/cli/lidarr/manual_search_command_handler.rs b/src/cli/lidarr/manual_search_command_handler.rs new file mode 100644 index 0000000..16e8dad --- /dev/null +++ b/src/cli/lidarr/manual_search_command_handler.rs @@ -0,0 +1,71 @@ +use crate::app::App; +use crate::cli::lidarr::LidarrCommand; +use crate::cli::{CliCommandHandler, Command}; +use crate::network::NetworkTrait; +use crate::network::lidarr_network::LidarrEvent; +use anyhow::Result; +use clap::Subcommand; +use std::sync::Arc; +use tokio::sync::Mutex; + +#[cfg(test)] +#[path = "manual_search_command_handler_tests.rs"] +mod manual_search_command_handler_tests; + +#[derive(Debug, Clone, PartialEq, Eq, Subcommand)] +pub enum LidarrManualSearchCommand { + #[command( + about = "Trigger a manual search of discography releases for the given artist corresponding to the artist with the given ID.\nNote that when downloading a discography release, ensure that the release includes 'discography: true', otherwise you'll run into issues" + )] + Discography { + #[arg( + long, + help = "The Lidarr ID of the artist whose discography releases you wish to fetch and list", + required = true + )] + artist_id: i64, + }, +} + +impl From for Command { + fn from(value: LidarrManualSearchCommand) -> Self { + Command::Lidarr(LidarrCommand::ManualSearch(value)) + } +} + +pub(super) struct LidarrManualSearchCommandHandler<'a, 'b> { + _app: &'a Arc>>, + command: LidarrManualSearchCommand, + network: &'a mut dyn NetworkTrait, +} + +impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrManualSearchCommand> + for LidarrManualSearchCommandHandler<'a, 'b> +{ + fn with( + _app: &'a Arc>>, + command: LidarrManualSearchCommand, + network: &'a mut dyn NetworkTrait, + ) -> Self { + LidarrManualSearchCommandHandler { + _app, + command, + network, + } + } + + async fn handle(self) -> Result { + let result = match self.command { + LidarrManualSearchCommand::Discography { artist_id } => { + println!("Searching for artist discography releases. This may take a minute..."); + let resp = self + .network + .handle_network_event(LidarrEvent::GetDiscographyReleases(artist_id).into()) + .await?; + serde_json::to_string_pretty(&resp)? + } + }; + + Ok(result) + } +} diff --git a/src/cli/lidarr/manual_search_command_handler_tests.rs b/src/cli/lidarr/manual_search_command_handler_tests.rs new file mode 100644 index 0000000..bfa29a3 --- /dev/null +++ b/src/cli/lidarr/manual_search_command_handler_tests.rs @@ -0,0 +1,98 @@ +#[cfg(test)] +mod tests { + use crate::cli::Command; + use crate::cli::lidarr::LidarrCommand; + use crate::cli::lidarr::manual_search_command_handler::LidarrManualSearchCommand; + use pretty_assertions::assert_eq; + + #[test] + fn test_lidarr_manual_search_command_from() { + let command = LidarrManualSearchCommand::Discography { artist_id: 1 }; + + let result = Command::from(command.clone()); + + assert_eq!( + result, + Command::Lidarr(LidarrCommand::ManualSearch(command)) + ); + } + + mod cli { + use crate::Cli; + use clap::CommandFactory; + use clap::error::ErrorKind; + use pretty_assertions::assert_eq; + + #[test] + fn test_manual_discography_search_requires_artist_id() { + let result = + Cli::command().try_get_matches_from(["managarr", "lidarr", "manual-search", "discography"]); + + assert_err!(&result); + assert_eq!( + result.unwrap_err().kind(), + ErrorKind::MissingRequiredArgument + ); + } + + #[test] + fn test_manual_discography_search_requirements_satisfied() { + let result = Cli::command().try_get_matches_from([ + "managarr", + "lidarr", + "manual-search", + "discography", + "--artist-id", + "1", + ]); + + assert_ok!(&result); + } + } + + mod handler { + use crate::app::App; + use crate::cli::CliCommandHandler; + use crate::cli::lidarr::manual_search_command_handler::{ + LidarrManualSearchCommand, LidarrManualSearchCommandHandler, + }; + use crate::models::Serdeable; + use crate::models::lidarr_models::LidarrSerdeable; + use crate::network::lidarr_network::LidarrEvent; + use crate::network::{MockNetworkTrait, NetworkEvent}; + use mockall::predicate::eq; + use serde_json::json; + use std::sync::Arc; + use tokio::sync::Mutex; + + #[tokio::test] + async fn test_manual_discography_search_command() { + let expected_artist_id = 1; + let mut mock_network = MockNetworkTrait::new(); + mock_network + .expect_handle_network_event() + .with(eq::( + LidarrEvent::GetDiscographyReleases(expected_artist_id).into(), + )) + .times(1) + .returning(|_| { + Ok(Serdeable::Lidarr(LidarrSerdeable::Value( + json!({"testResponse": "response"}), + ))) + }); + let app_arc = Arc::new(Mutex::new(App::test_default())); + let manual_discography_search_command = + LidarrManualSearchCommand::Discography { artist_id: 1 }; + + let result = LidarrManualSearchCommandHandler::with( + &app_arc, + manual_discography_search_command, + &mut mock_network, + ) + .handle() + .await; + + assert_ok!(&result); + } + } +} diff --git a/src/cli/lidarr/mod.rs b/src/cli/lidarr/mod.rs index 27a2e38..f7bf569 100644 --- a/src/cli/lidarr/mod.rs +++ b/src/cli/lidarr/mod.rs @@ -15,6 +15,9 @@ use trigger_automatic_search_command_handler::{ }; use super::{CliCommandHandler, Command}; +use crate::cli::lidarr::manual_search_command_handler::{ + LidarrManualSearchCommand, LidarrManualSearchCommandHandler, +}; use crate::models::lidarr_models::LidarrTaskName; use crate::network::lidarr_network::LidarrEvent; use crate::{app::App, network::NetworkTrait}; @@ -30,6 +33,7 @@ mod trigger_automatic_search_command_handler; #[cfg(test)] #[path = "lidarr_command_tests.rs"] mod lidarr_command_tests; +mod manual_search_command_handler; #[derive(Debug, Clone, PartialEq, Eq, Subcommand)] pub enum LidarrCommand { @@ -63,6 +67,8 @@ pub enum LidarrCommand { about = "Commands to refresh the data in your Lidarr instance" )] Refresh(LidarrRefreshCommand), + #[command(subcommand, about = "Commands to manually search for releases")] + ManualSearch(LidarrManualSearchCommand), #[command( subcommand, about = "Commands to trigger automatic searches for releases of different resources in your Lidarr instance" @@ -186,6 +192,11 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrCommand> for LidarrCliHandler<'a, ' .handle() .await? } + LidarrCommand::ManualSearch(manual_search_command) => { + LidarrManualSearchCommandHandler::with(self.app, manual_search_command, self.network) + .handle() + .await? + } LidarrCommand::TriggerAutomaticSearch(trigger_automatic_search_command) => { LidarrTriggerAutomaticSearchCommandHandler::with( self.app, diff --git a/src/models/lidarr_models.rs b/src/models/lidarr_models.rs index b23dbcd..1893266 100644 --- a/src/models/lidarr_models.rs +++ b/src/models/lidarr_models.rs @@ -464,6 +464,30 @@ impl Display for LidarrTaskName { } } +#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[serde(default)] +pub struct LidarrRelease { + pub guid: String, + pub protocol: String, + #[serde(deserialize_with = "super::from_i64")] + pub age: i64, + pub title: HorizontallyScrollableText, + pub discography: bool, + pub artist_name: Option, + pub album_title: Option, + pub indexer: String, + #[serde(deserialize_with = "super::from_i64")] + pub indexer_id: i64, + #[serde(deserialize_with = "super::from_i64")] + pub size: i64, + pub rejected: bool, + pub rejections: Option>, + pub seeders: Option, + pub leechers: Option, + pub quality: QualityWrapper, +} + impl From for Serdeable { fn from(value: LidarrSerdeable) -> Serdeable { Serdeable::Lidarr(value) @@ -489,6 +513,7 @@ serde_enum_from!( MetadataProfiles(Vec), QualityProfiles(Vec), QueueEvents(Vec), + Releases(Vec), RootFolders(Vec), SecurityConfig(SecurityConfig), SystemStatus(SystemStatus), diff --git a/src/models/lidarr_models_tests.rs b/src/models/lidarr_models_tests.rs index 32a2f24..00cef17 100644 --- a/src/models/lidarr_models_tests.rs +++ b/src/models/lidarr_models_tests.rs @@ -6,8 +6,8 @@ mod tests { use crate::models::lidarr_models::{ AddArtistSearchResult, Album, DownloadRecord, DownloadStatus, DownloadsResponse, - LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, LidarrTask, Member, - MetadataProfile, MonitorType, NewItemMonitorType, SystemStatus, + LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, LidarrRelease, LidarrTask, + Member, MetadataProfile, MonitorType, NewItemMonitorType, SystemStatus, }; use crate::models::servarr_models::{ DiskSpace, HostConfig, Indexer, IndexerSettings, IndexerTestResult, Log, LogResponse, @@ -460,6 +460,18 @@ mod tests { assert_eq!(lidarr_serdeable, LidarrSerdeable::RootFolders(root_folders)); } + #[test] + fn test_lidarr_serdeable_from_releases() { + let releases = vec![LidarrRelease { + guid: "test".to_owned(), + ..LidarrRelease::default() + }]; + + let lidarr_serdeable: LidarrSerdeable = releases.clone().into(); + + assert_eq!(lidarr_serdeable, LidarrSerdeable::Releases(releases)); + } + #[test] fn test_lidarr_serdeable_from_security_config() { let security_config = SecurityConfig { diff --git a/src/models/servarr_data/lidarr/lidarr_data.rs b/src/models/servarr_data/lidarr/lidarr_data.rs index 942b401..ed011cf 100644 --- a/src/models/servarr_data/lidarr/lidarr_data.rs +++ b/src/models/servarr_data/lidarr/lidarr_data.rs @@ -8,7 +8,7 @@ use crate::app::context_clues::{ use crate::app::lidarr::lidarr_context_clues::{ ARTIST_DETAILS_CONTEXT_CLUES, ARTIST_HISTORY_CONTEXT_CLUES, ARTISTS_CONTEXT_CLUES, }; -use crate::models::lidarr_models::LidarrTask; +use crate::models::lidarr_models::{LidarrRelease, LidarrTask}; use crate::models::servarr_data::modals::EditIndexerModal; use crate::models::servarr_models::{IndexerSettings, QueueEvent}; use crate::models::stateful_list::StatefulList; @@ -35,6 +35,9 @@ use { metadata_profile, metadata_profile_map, quality_profile, root_folder, tags_map, }, crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{log_line, task}, + crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{ + torrent_release, usenet_release, + }, crate::network::servarr_test_utils::diskspace, crate::network::servarr_test_utils::indexer_test_result, crate::network::servarr_test_utils::queued_event, @@ -58,6 +61,7 @@ pub struct LidarrData<'a> { pub artist_info_tabs: TabState, pub artists: StatefulTable, pub delete_files: bool, + pub discography_releases: StatefulTable, pub disk_space_vec: Vec, pub downloads: StatefulTable, pub edit_artist_modal: Option, @@ -92,6 +96,7 @@ impl LidarrData<'_> { pub fn reset_artist_info_tabs(&mut self) { self.albums = StatefulTable::default(); + self.discography_releases = StatefulTable::default(); self.artist_history = None; self.artist_info_tabs.index = 0; } @@ -140,6 +145,7 @@ impl<'a> Default for LidarrData<'a> { artist_history: None, artists: StatefulTable::default(), delete_files: false, + discography_releases: StatefulTable::default(), disk_space_vec: Vec::new(), downloads: StatefulTable::default(), edit_artist_modal: None, @@ -333,6 +339,12 @@ impl LidarrData<'_> { }]); lidarr_data.history.search = Some("test search".into()); lidarr_data.history.filter = Some("test filter".into()); + lidarr_data + .discography_releases + .set_items(vec![torrent_release(), usenet_release()]); + lidarr_data + .discography_releases + .sorting(vec![sort_option!(indexer_id)]); lidarr_data.root_folders.set_items(vec![root_folder()]); lidarr_data.indexers.set_items(vec![indexer()]); lidarr_data.queued_events.set_items(vec![queued_event()]); diff --git a/src/models/servarr_data/lidarr/lidarr_data_tests.rs b/src/models/servarr_data/lidarr/lidarr_data_tests.rs index 1f069a5..ca57ac2 100644 --- a/src/models/servarr_data/lidarr/lidarr_data_tests.rs +++ b/src/models/servarr_data/lidarr/lidarr_data_tests.rs @@ -7,7 +7,7 @@ mod tests { use crate::app::lidarr::lidarr_context_clues::{ ARTIST_DETAILS_CONTEXT_CLUES, ARTIST_HISTORY_CONTEXT_CLUES, ARTISTS_CONTEXT_CLUES, }; - use crate::models::lidarr_models::Album; + use crate::models::lidarr_models::{Album, LidarrRelease}; use crate::models::servarr_data::lidarr::lidarr_data::{ ADD_ARTIST_BLOCKS, ADD_ARTIST_SELECTION_BLOCKS, ADD_ROOT_FOLDER_BLOCKS, ARTIST_DETAILS_BLOCKS, DELETE_ALBUM_BLOCKS, DELETE_ALBUM_SELECTION_BLOCKS, DELETE_ARTIST_BLOCKS, @@ -60,12 +60,16 @@ mod tests { fn test_reset_artist_info_tabs() { let mut lidarr_data = LidarrData::default(); lidarr_data.albums.set_items(vec![Album::default()]); + lidarr_data + .discography_releases + .set_items(vec![LidarrRelease::default()]); lidarr_data.artist_history = Some(StatefulTable::default()); lidarr_data.artist_info_tabs.index = 1; lidarr_data.reset_artist_info_tabs(); assert_is_empty!(lidarr_data.albums); + assert_is_empty!(lidarr_data.discography_releases); assert_none!(lidarr_data.artist_history); assert_eq!(lidarr_data.artist_info_tabs.index, 0); } @@ -146,6 +150,7 @@ mod tests { assert_is_empty!(lidarr_data.downloads); assert_none!(lidarr_data.edit_artist_modal); assert_none!(lidarr_data.add_root_folder_modal); + assert_is_empty!(lidarr_data.discography_releases); assert_is_empty!(lidarr_data.history); assert_is_empty!(lidarr_data.logs); assert_is_empty!(lidarr_data.log_details); diff --git a/src/network/lidarr_network/library/artists/lidarr_artists_network_tests.rs b/src/network/lidarr_network/library/artists/lidarr_artists_network_tests.rs index 7952ebf..cf7f58b 100644 --- a/src/network/lidarr_network/library/artists/lidarr_artists_network_tests.rs +++ b/src/network/lidarr_network/library/artists/lidarr_artists_network_tests.rs @@ -2,14 +2,14 @@ mod tests { use crate::models::lidarr_models::{ AddArtistBody, AddArtistOptions, AddArtistSearchResult, Artist, DeleteParams, EditArtistParams, - LidarrHistoryItem, LidarrSerdeable, MonitorType, NewItemMonitorType, + LidarrHistoryItem, LidarrRelease, LidarrSerdeable, MonitorType, NewItemMonitorType, }; use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock; use crate::models::stateful_table::{SortOption, StatefulTable}; use crate::network::NetworkResource; use crate::network::lidarr_network::LidarrEvent; use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{ - ADD_ARTIST_SEARCH_RESULT_JSON, ARTIST_JSON, artist, lidarr_history_item, + ADD_ARTIST_SEARCH_RESULT_JSON, ARTIST_JSON, artist, lidarr_history_item, torrent_release, }; use crate::network::network_tests::test_utils::{MockServarrApi, test_network}; use bimap::BiMap; @@ -393,6 +393,87 @@ mod tests { assert_eq!(history_items, response); } + #[tokio::test] + async fn test_handle_get_artist_discography_releases_event() { + let release_json = json!([ + { + "guid": "1234", + "protocol": "torrent", + "age": 1, + "title": "Test Release", + "indexer": "kickass torrents", + "indexerId": 2, + "artistName": "Alex", + "albumTitle": "Something", + "size": 1234, + "rejected": true, + "rejections": [ "Unknown quality profile", "Release is already mapped" ], + "seeders": 2, + "leechers": 1, + "quality": { "quality": { "name": "Lossless" }}, + "discography": true + }, + { + "guid": "4567", + "protocol": "torrent", + "age": 1, + "title": "Test Release", + "indexer": "kickass torrents", + "indexerId": 2, + "artistName": "Alex", + "albumTitle": "Something", + "size": 1234, + "rejected": true, + "rejections": [ "Unknown quality profile", "Release is already mapped" ], + "seeders": 2, + "leechers": 1, + "quality": { "quality": { "name": "Lossless" }}, + } + ]); + let expected_filtered_lidarr_release = LidarrRelease { + discography: true, + ..torrent_release() + }; + let expected_raw_lidarr_releases = vec![ + LidarrRelease { + discography: true, + ..torrent_release() + }, + LidarrRelease { + guid: "4567".to_owned(), + ..torrent_release() + }, + ]; + let (mock, app, _server) = MockServarrApi::get() + .returns(release_json) + .query("artistId=1") + .build_for(LidarrEvent::GetDiscographyReleases(1)) + .await; + app + .lock() + .await + .data + .lidarr_data + .artists + .set_items(vec![artist()]); + app.lock().await.server_tabs.set_index(2); + let mut network = test_network(&app); + + let LidarrSerdeable::Releases(releases_vec) = network + .handle_lidarr_event(LidarrEvent::GetDiscographyReleases(1)) + .await + .unwrap() + else { + panic!("Expected Releases") + }; + mock.assert_async().await; + assert_eq!( + app.lock().await.data.lidarr_data.discography_releases.items, + vec![expected_filtered_lidarr_release] + ); + assert_eq!(releases_vec, expected_raw_lidarr_releases); + } + #[tokio::test] async fn test_handle_toggle_artist_monitoring_event() { let artist_json = json!({ diff --git a/src/network/lidarr_network/library/artists/mod.rs b/src/network/lidarr_network/library/artists/mod.rs index ccd8a40..8a362e0 100644 --- a/src/network/lidarr_network/library/artists/mod.rs +++ b/src/network/lidarr_network/library/artists/mod.rs @@ -5,7 +5,7 @@ use serde_json::{Value, json}; use crate::models::Route; use crate::models::lidarr_models::{ AddArtistBody, AddArtistSearchResult, Artist, DeleteParams, EditArtistParams, LidarrCommandBody, - LidarrHistoryItem, + LidarrHistoryItem, LidarrRelease, }; use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock; use crate::models::stateful_table::StatefulTable; @@ -317,6 +317,39 @@ impl Network<'_, '_> { .await } + pub(in crate::network::lidarr_network) async fn get_artist_discography_releases( + &mut self, + artist_id: i64, + ) -> Result> { + let event = LidarrEvent::GetDiscographyReleases(artist_id); + info!("Fetching discography releases for artist with ID: {artist_id}"); + + let request_props = self + .request_props_from( + event, + RequestMethod::Get, + None::<()>, + None, + Some(format!("artistId={artist_id}")), + ) + .await; + + self + .handle_request::<(), Vec>(request_props, |release_vec, mut app| { + let artist_releases_vec = release_vec + .into_iter() + .filter(|release| release.discography) + .collect(); + + app + .data + .lidarr_data + .discography_releases + .set_items(artist_releases_vec); + }) + .await + } + pub(in crate::network::lidarr_network) async fn edit_artist( &mut self, mut edit_artist_params: EditArtistParams, diff --git a/src/network/lidarr_network/lidarr_network_test_utils.rs b/src/network/lidarr_network/lidarr_network_test_utils.rs index 2024434..4f4c97f 100644 --- a/src/network/lidarr_network/lidarr_network_test_utils.rs +++ b/src/network/lidarr_network/lidarr_network_test_utils.rs @@ -4,8 +4,8 @@ pub mod test_utils { use crate::models::lidarr_models::{ AddArtistSearchResult, Album, AlbumStatistics, Artist, ArtistStatistics, ArtistStatus, DownloadRecord, DownloadStatus, DownloadsResponse, EditArtistParams, LidarrHistoryData, - LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, LidarrTask, LidarrTaskName, - Member, MetadataProfile, NewItemMonitorType, Ratings, SystemStatus, + LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, LidarrRelease, LidarrTask, + LidarrTaskName, Member, MetadataProfile, NewItemMonitorType, Ratings, SystemStatus, }; use crate::models::servarr_models::IndexerSettings; use crate::models::servarr_models::{ @@ -377,4 +377,51 @@ pub mod test_utils { * Fixed bug 2" )) } + + pub fn rejections() -> Vec { + vec![ + "Unknown quality profile".to_owned(), + "Release is already mapped".to_owned(), + ] + } + + pub fn torrent_release() -> LidarrRelease { + LidarrRelease { + guid: "1234".to_owned(), + protocol: "torrent".to_owned(), + age: 1, + title: HorizontallyScrollableText::from("Test Release"), + discography: false, + artist_name: Some("Alex".to_owned()), + album_title: Some("Something".to_owned()), + indexer: "kickass torrents".to_owned(), + indexer_id: 2, + size: 1234, + rejected: true, + rejections: Some(rejections()), + seeders: Some(Number::from(2)), + leechers: Some(Number::from(1)), + quality: quality_wrapper(), + } + } + + pub fn usenet_release() -> LidarrRelease { + LidarrRelease { + guid: "1234".to_owned(), + protocol: "usenet".to_owned(), + age: 1, + title: HorizontallyScrollableText::from("Test Release"), + discography: false, + artist_name: Some("Alex".to_owned()), + album_title: Some("Something".to_owned()), + indexer: "DrunkenSlug".to_owned(), + indexer_id: 1, + size: 1234, + rejected: true, + rejections: Some(rejections()), + seeders: None, + leechers: None, + quality: quality_wrapper(), + } + } } diff --git a/src/network/lidarr_network/lidarr_network_tests.rs b/src/network/lidarr_network/lidarr_network_tests.rs index bcd2e1f..3f24cb8 100644 --- a/src/network/lidarr_network/lidarr_network_tests.rs +++ b/src/network/lidarr_network/lidarr_network_tests.rs @@ -124,6 +124,11 @@ mod tests { assert_str_eq!(event.resource(), "/rootfolder"); } + #[rstest] + fn test_resource_release(#[values(LidarrEvent::GetDiscographyReleases(0))] event: LidarrEvent) { + assert_str_eq!(event.resource(), "/release"); + } + #[rstest] #[case(LidarrEvent::GetDiskSpace, "/diskspace")] #[case(LidarrEvent::GetMetadataProfiles, "/metadataprofile")] diff --git a/src/network/lidarr_network/mod.rs b/src/network/lidarr_network/mod.rs index 14af1e9..6c85bf9 100644 --- a/src/network/lidarr_network/mod.rs +++ b/src/network/lidarr_network/mod.rs @@ -43,6 +43,7 @@ pub enum LidarrEvent { GetArtistHistory(i64), GetAllIndexerSettings, GetArtistDetails(i64), + GetDiscographyReleases(i64), GetDiskSpace, GetDownloads(u64), GetHistory(u64), @@ -96,6 +97,7 @@ impl NetworkResource for LidarrEvent { LidarrEvent::GetDownloads(_) | LidarrEvent::DeleteDownload(_) => "/queue", LidarrEvent::GetHistory(_) => "/history", LidarrEvent::MarkHistoryItemAsFailed(_) => "/history/failed", + LidarrEvent::GetDiscographyReleases(_) => "/release", LidarrEvent::GetHostConfig | LidarrEvent::GetSecurityConfig => "/config/host", LidarrEvent::GetIndexers | LidarrEvent::DeleteIndexer(_) | LidarrEvent::EditIndexer(_) => { "/indexer" @@ -184,6 +186,10 @@ impl Network<'_, '_> { .get_album_details(album_id) .await .map(LidarrSerdeable::from), + LidarrEvent::GetDiscographyReleases(artist_id) => self + .get_artist_discography_releases(artist_id) + .await + .map(LidarrSerdeable::from), LidarrEvent::GetDiskSpace => self.get_lidarr_diskspace().await.map(LidarrSerdeable::from), LidarrEvent::GetDownloads(count) => self .get_lidarr_downloads(count)