diff --git a/src/app/radarr/radarr_tests.rs b/src/app/radarr/radarr_tests.rs index 45648d7..5745f52 100644 --- a/src/app/radarr/radarr_tests.rs +++ b/src/app/radarr/radarr_tests.rs @@ -6,7 +6,9 @@ mod tests { use crate::app::radarr::ActiveRadarrBlock; use crate::app::App; - use crate::models::radarr_models::{Collection, CollectionMovie, Credit, RadarrRelease}; + use crate::models::radarr_models::{ + AddMovieBody, AddMovieOptions, Collection, CollectionMovie, Credit, RadarrRelease, + }; use crate::models::servarr_data::radarr::modals::MovieDetailsModal; use crate::network::radarr_network::RadarrEvent; @@ -83,8 +85,23 @@ mod tests { #[tokio::test] async fn test_dispatch_by_collection_details_block_with_add_movie() { + let add_movie_body = AddMovieBody { + tmdb_id: 1234, + title: "Test".to_owned(), + root_folder_path: "/nfs2".to_owned(), + minimum_availability: "announced".to_owned(), + monitored: true, + quality_profile_id: 2222, + tags: vec![1, 2], + tag_input_string: String::new(), + add_options: AddMovieOptions { + monitor: "movieOnly".to_owned(), + search_for_movie: true, + }, + }; let (mut app, mut sync_network_rx) = construct_app_unit(); - app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie(None)); + app.data.radarr_data.prompt_confirm_action = + Some(RadarrEvent::AddMovie(add_movie_body.clone())); app.data.radarr_data.collections.set_items(vec![Collection { movies: Some(vec![CollectionMovie::default()]), @@ -106,7 +123,7 @@ mod tests { ); assert_eq!( sync_network_rx.recv().await.unwrap(), - RadarrEvent::AddMovie(None).into() + RadarrEvent::AddMovie(add_movie_body).into() ); assert!(!app.data.radarr_data.collection_movies.items.is_empty()); assert_eq!(app.tick_count, 0); diff --git a/src/cli/radarr/add_command_handler.rs b/src/cli/radarr/add_command_handler.rs index 0c35a7e..098a532 100644 --- a/src/cli/radarr/add_command_handler.rs +++ b/src/cli/radarr/add_command_handler.rs @@ -125,6 +125,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrAddCommand> for RadarrAddCommandHan minimum_availability: minimum_availability.to_string(), monitored: !disable_monitoring, tags, + tag_input_string: String::new(), add_options: AddMovieOptions { monitor: monitor.to_string(), search_for_movie: !no_search_for_movie, @@ -132,7 +133,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrAddCommand> for RadarrAddCommandHan }; let resp = self .network - .handle_network_event(RadarrEvent::AddMovie(Some(body)).into()) + .handle_network_event(RadarrEvent::AddMovie(body).into()) .await?; serde_json::to_string_pretty(&resp)? } diff --git a/src/cli/radarr/add_command_handler_tests.rs b/src/cli/radarr/add_command_handler_tests.rs index 754abd2..83202b5 100644 --- a/src/cli/radarr/add_command_handler_tests.rs +++ b/src/cli/radarr/add_command_handler_tests.rs @@ -381,6 +381,7 @@ mod tests { minimum_availability: "released".to_owned(), monitored: false, tags: vec![1, 2], + tag_input_string: String::new(), add_options: AddMovieOptions { monitor: "movieAndCollection".to_owned(), search_for_movie: false, @@ -390,7 +391,7 @@ mod tests { mock_network .expect_handle_network_event() .with(eq::( - RadarrEvent::AddMovie(Some(expected_add_movie_body)).into(), + RadarrEvent::AddMovie(expected_add_movie_body).into(), )) .times(1) .returning(|_| { diff --git a/src/handlers/radarr_handlers/library/add_movie_handler.rs b/src/handlers/radarr_handlers/library/add_movie_handler.rs index c6c11d5..a30e641 100644 --- a/src/handlers/radarr_handlers/library/add_movie_handler.rs +++ b/src/handlers/radarr_handlers/library/add_movie_handler.rs @@ -1,7 +1,8 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::handlers::table_handler::TableHandlingConfig; use crate::handlers::{handle_prompt_toggle, KeyEventHandler}; -use crate::models::radarr_models::AddMovieSearchResult; +use crate::models::radarr_models::{AddMovieBody, AddMovieOptions, AddMovieSearchResult, CollectionMovie}; +use crate::models::servarr_data::radarr::modals::AddMovieModal; use crate::models::servarr_data::radarr::radarr_data::{ ActiveRadarrBlock, ADD_MOVIE_BLOCKS, ADD_MOVIE_SELECTION_BLOCKS, }; @@ -33,6 +34,89 @@ impl<'a, 'b> AddMovieHandler<'a, 'b> { .unwrap(), AddMovieSearchResult ); + + fn build_add_movie_body(&mut self) -> AddMovieBody { + let tags = self + .app + .data + .radarr_data + .add_movie_modal + .as_ref() + .unwrap() + .tags + .text + .clone(); + let AddMovieModal { + root_folder_list, + monitor_list, + minimum_availability_list, + quality_profile_list, + .. + } = self.app.data.radarr_data.add_movie_modal.as_ref().unwrap(); + let (tmdb_id, title) = if let Some(context) = self.context + { + if context == ActiveRadarrBlock::CollectionDetails { + let CollectionMovie { tmdb_id, title, .. } = self.app + .data + .radarr_data + .collection_movies + .current_selection() + .clone(); + (tmdb_id, title.text) + } else { + let AddMovieSearchResult { tmdb_id, title, .. } = self.app + .data + .radarr_data + .add_searched_movies + .as_ref() + .unwrap() + .current_selection() + .clone(); + (tmdb_id, title.text) + } + } else { + let AddMovieSearchResult { tmdb_id, title, .. } = self.app + .data + .radarr_data + .add_searched_movies + .as_ref() + .unwrap() + .current_selection() + .clone(); + (tmdb_id, title.text) + }; + let quality_profile = quality_profile_list.current_selection(); + let quality_profile_id = *self.app + .data + .radarr_data + .quality_profile_map + .iter() + .filter(|(_, value)| *value == quality_profile) + .map(|(key, _)| key) + .next() + .unwrap(); + + let path = root_folder_list.current_selection().path.clone(); + let monitor = monitor_list.current_selection().to_string(); + let minimum_availability = minimum_availability_list.current_selection().to_string(); + + self.app.data.radarr_data.add_movie_modal = None; + + AddMovieBody { + tmdb_id, + title, + root_folder_path: path, + minimum_availability, + monitored: true, + quality_profile_id, + tags: Vec::new(), + tag_input_string: tags, + add_options: AddMovieOptions { + monitor, + search_for_movie: true, + }, + } + } } impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, 'b> { @@ -361,7 +445,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, match self.app.data.radarr_data.selected_block.get_active_block() { ActiveRadarrBlock::AddMovieConfirmPrompt => { if self.app.data.radarr_data.prompt_confirm { - self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie(None)); + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie(self.build_add_movie_body())); } self.app.pop_navigation_stack(); @@ -461,7 +545,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, && key == DEFAULT_KEYBINDINGS.confirm.key { self.app.data.radarr_data.prompt_confirm = true; - self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie(None)); + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie(self.build_add_movie_body())); self.app.pop_navigation_stack(); } } diff --git a/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs b/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs index ebd98b5..1590e91 100644 --- a/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs @@ -758,6 +758,9 @@ mod tests { use pretty_assertions::{assert_eq, assert_str_eq}; use rstest::rstest; + use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::{ + add_movie_body, add_movie_search_result, collection_movie, + }; use crate::models::radarr_models::Movie; use crate::models::servarr_data::radarr::modals::AddMovieModal; use crate::models::servarr_data::radarr::radarr_data::ADD_MOVIE_SELECTION_BLOCKS; @@ -969,8 +972,10 @@ mod tests { assert_eq!(app.data.radarr_data.prompt_confirm_action, None); } - #[test] - fn test_add_movie_confirm_prompt_prompt_confirmation_submit() { + #[rstest] + fn test_add_movie_confirm_prompt_prompt_confirmation_submit( + #[values(true, false)] movie_details_context: bool, + ) { let mut app = App::default(); app.data.radarr_data.add_movie_modal = Some(AddMovieModal::default()); app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); @@ -982,21 +987,67 @@ mod tests { .radarr_data .selected_block .set_index(0, ADD_MOVIE_SELECTION_BLOCKS.len() - 1); + let mut add_movie_modal = AddMovieModal { + tags: "usenet, testing".into(), + ..AddMovieModal::default() + }; + add_movie_modal.root_folder_list.set_items(vec![ + RootFolder { + id: 1, + path: "/nfs".to_owned(), + accessible: true, + free_space: 219902325555200, + unmapped_folders: None, + }, + RootFolder { + id: 2, + path: "/nfs2".to_owned(), + accessible: true, + free_space: 21990232555520, + unmapped_folders: None, + }, + ]); + add_movie_modal.root_folder_list.state.select(Some(1)); + add_movie_modal + .quality_profile_list + .set_items(vec!["HD - 1080p".to_owned()]); + add_movie_modal + .monitor_list + .set_items(Vec::from_iter(MovieMonitor::iter())); + add_movie_modal + .minimum_availability_list + .set_items(Vec::from_iter(MinimumAvailability::iter())); + app.data.radarr_data.add_movie_modal = Some(add_movie_modal); + app.data.radarr_data.quality_profile_map = + BiMap::from_iter([(2222, "HD - 1080p".to_owned())]); + let context = if movie_details_context { + app + .data + .radarr_data + .collection_movies + .set_items(vec![collection_movie()]); + Some(ActiveRadarrBlock::CollectionDetails) + } else { + let mut add_searched_movies = StatefulTable::default(); + add_searched_movies.set_items(vec![add_movie_search_result()]); + app.data.radarr_data.add_searched_movies = Some(add_searched_movies); + None + }; AddMovieHandler::with( SUBMIT_KEY, &mut app, ActiveRadarrBlock::AddMoviePrompt, - None, + context, ) .handle(); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into()); assert_eq!( app.data.radarr_data.prompt_confirm_action, - Some(RadarrEvent::AddMovie(None)) + Some(RadarrEvent::AddMovie(add_movie_body())) ); - assert!(app.data.radarr_data.add_movie_modal.is_some()); + assert!(app.data.radarr_data.add_movie_modal.is_none()); } #[rstest] @@ -1266,10 +1317,18 @@ mod tests { } mod test_handle_key_char { + use bimap::BiMap; + use pretty_assertions::assert_eq; + use rstest::rstest; + use super::*; use crate::{ + handlers::radarr_handlers::radarr_handler_test_utils::utils::{ + add_movie_body, add_movie_search_result, collection_movie, + }, models::{ servarr_data::radarr::{modals::AddMovieModal, radarr_data::ADD_MOVIE_SELECTION_BLOCKS}, + stateful_table::StatefulTable, BlockSelectionState, }, network::radarr_network::RadarrEvent, @@ -1368,8 +1427,10 @@ mod tests { ); } - #[test] - fn test_add_movie_confirm_prompt_prompt_confirmation_confirm() { + #[rstest] + fn test_add_movie_confirm_prompt_prompt_confirmation_confirm( + #[values(true, false)] movie_details_context: bool, + ) { let mut app = App::default(); app.data.radarr_data.add_movie_modal = Some(AddMovieModal::default()); app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); @@ -1380,21 +1441,67 @@ mod tests { .radarr_data .selected_block .set_index(0, ADD_MOVIE_SELECTION_BLOCKS.len() - 1); + let mut add_movie_modal = AddMovieModal { + tags: "usenet, testing".into(), + ..AddMovieModal::default() + }; + add_movie_modal.root_folder_list.set_items(vec![ + RootFolder { + id: 1, + path: "/nfs".to_owned(), + accessible: true, + free_space: 219902325555200, + unmapped_folders: None, + }, + RootFolder { + id: 2, + path: "/nfs2".to_owned(), + accessible: true, + free_space: 21990232555520, + unmapped_folders: None, + }, + ]); + add_movie_modal.root_folder_list.state.select(Some(1)); + add_movie_modal + .quality_profile_list + .set_items(vec!["HD - 1080p".to_owned()]); + add_movie_modal + .monitor_list + .set_items(Vec::from_iter(MovieMonitor::iter())); + add_movie_modal + .minimum_availability_list + .set_items(Vec::from_iter(MinimumAvailability::iter())); + app.data.radarr_data.add_movie_modal = Some(add_movie_modal); + app.data.radarr_data.quality_profile_map = + BiMap::from_iter([(2222, "HD - 1080p".to_owned())]); + let context = if movie_details_context { + app + .data + .radarr_data + .collection_movies + .set_items(vec![collection_movie()]); + Some(ActiveRadarrBlock::CollectionDetails) + } else { + let mut add_searched_movies = StatefulTable::default(); + add_searched_movies.set_items(vec![add_movie_search_result()]); + app.data.radarr_data.add_searched_movies = Some(add_searched_movies); + None + }; AddMovieHandler::with( DEFAULT_KEYBINDINGS.confirm.key, &mut app, ActiveRadarrBlock::AddMoviePrompt, - None, + context, ) .handle(); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into()); assert_eq!( app.data.radarr_data.prompt_confirm_action, - Some(RadarrEvent::AddMovie(None)) + Some(RadarrEvent::AddMovie(add_movie_body())) ); - assert!(app.data.radarr_data.add_movie_modal.is_some()); + assert!(app.data.radarr_data.add_movie_modal.is_none()); } } diff --git a/src/handlers/radarr_handlers/radarr_handler_test_utils.rs b/src/handlers/radarr_handlers/radarr_handler_test_utils.rs index b56730e..9f3fbde 100644 --- a/src/handlers/radarr_handlers/radarr_handler_test_utils.rs +++ b/src/handlers/radarr_handlers/radarr_handler_test_utils.rs @@ -1,6 +1,19 @@ #[cfg(test)] #[macro_use] -mod utils { +pub(in crate::handlers::radarr_handlers) mod utils { + use crate::models::radarr_models::{ + AddMovieBody, AddMovieOptions, AddMovieSearchResult, BlocklistItem, BlocklistItemMovie, + Collection, CollectionMovie, Credit, CreditType, DownloadRecord, DownloadsResponse, + IndexerSettings, MediaInfo, MinimumAvailability, Movie, MovieCollection, MovieFile, + MovieHistoryItem, RadarrRelease, Rating, RatingsList, + }; + use crate::models::servarr_models::{ + Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder, + }; + use crate::models::HorizontallyScrollableText; + use chrono::DateTime; + use serde_json::{json, Number}; + #[macro_export] macro_rules! test_edit_movie_key { ($handler:ident, $block:expr, $context:expr) => { @@ -228,4 +241,303 @@ mod utils { ); }; } + + pub fn language() -> Language { + Language { + id: 1, + name: "English".to_owned(), + } + } + + pub fn genres() -> Vec { + vec!["cool".to_owned(), "family".to_owned(), "fun".to_owned()] + } + + pub fn rating() -> Rating { + Rating { + value: Number::from_f64(9.9).unwrap(), + } + } + + pub fn ratings_list() -> RatingsList { + RatingsList { + imdb: Some(rating()), + tmdb: Some(rating()), + rotten_tomatoes: Some(rating()), + } + } + + pub fn media_info() -> MediaInfo { + MediaInfo { + audio_bitrate: 0, + audio_channels: Number::from_f64(7.1).unwrap(), + audio_codec: Some("AAC".to_owned()), + audio_languages: Some("eng".to_owned()), + audio_stream_count: 1, + video_bit_depth: 10, + video_bitrate: 0, + video_codec: "x265".to_owned(), + video_fps: Number::from_f64(23.976).unwrap(), + resolution: "1920x804".to_owned(), + run_time: "2:00:00".to_owned(), + scan_type: "Progressive".to_owned(), + } + } + + pub fn movie_file() -> MovieFile { + MovieFile { + relative_path: "Test.mkv".to_owned(), + path: "/nfs/movies/Test.mkv".to_owned(), + date_added: DateTime::from(DateTime::parse_from_rfc3339("2022-12-30T07:37:56Z").unwrap()), + media_info: Some(media_info()), + } + } + + pub fn collection_movie() -> CollectionMovie { + CollectionMovie { + title: "Test".to_owned().into(), + overview: "Collection blah blah blah".to_owned(), + year: 2023, + runtime: 120, + tmdb_id: 1234, + genres: genres(), + ratings: ratings_list(), + } + } + + pub fn blocklist_item() -> BlocklistItem { + BlocklistItem { + id: 1, + movie_id: 1, + source_title: "z movie".to_owned(), + languages: vec![language()], + quality: quality_wrapper(), + custom_formats: Some(vec![language()]), + date: DateTime::from(DateTime::parse_from_rfc3339("2024-02-10T07:28:45Z").unwrap()), + protocol: "usenet".to_owned(), + indexer: "DrunkenSlug (Prowlarr)".to_owned(), + message: "test message".to_owned(), + movie: blocklist_item_movie(), + } + } + + pub fn blocklist_item_movie() -> BlocklistItemMovie { + BlocklistItemMovie { + title: "Test".into(), + } + } + + pub fn collection() -> Collection { + Collection { + id: 123, + title: "Test Collection".to_owned().into(), + root_folder_path: Some("/nfs/movies".to_owned()), + search_on_add: true, + monitored: true, + minimum_availability: MinimumAvailability::Released, + overview: Some("Collection blah blah blah".to_owned()), + quality_profile_id: 2222, + movies: Some(vec![collection_movie()]), + } + } + + pub fn movie() -> Movie { + Movie { + id: 1, + title: "Test".to_owned().into(), + original_language: language(), + size_on_disk: 3543348019, + status: "Downloaded".to_owned(), + overview: "Blah blah blah".to_owned(), + path: "/nfs/movies".to_owned(), + studio: "21st Century Alex".to_owned(), + genres: genres(), + year: 2023, + monitored: true, + has_file: true, + runtime: 120, + tmdb_id: 1234, + quality_profile_id: 2222, + minimum_availability: MinimumAvailability::Announced, + certification: Some("R".to_owned()), + tags: vec![Number::from(1)], + ratings: ratings_list(), + movie_file: Some(movie_file()), + collection: Some(movie_collection()), + } + } + + pub fn movie_collection() -> MovieCollection { + MovieCollection { + title: Some("Test Collection".to_owned()), + } + } + + pub fn rejections() -> Vec { + vec![ + "Unknown quality profile".to_owned(), + "Release is already mapped".to_owned(), + ] + } + + pub fn quality() -> Quality { + Quality { + name: "HD - 1080p".to_owned(), + } + } + + pub fn quality_wrapper() -> QualityWrapper { + QualityWrapper { quality: quality() } + } + + pub fn release() -> RadarrRelease { + RadarrRelease { + guid: "1234".to_owned(), + protocol: "torrent".to_owned(), + age: 1, + title: HorizontallyScrollableText::from("Test Release"), + 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)), + languages: Some(vec![language()]), + quality: quality_wrapper(), + } + } + + pub fn add_movie_search_result() -> AddMovieSearchResult { + AddMovieSearchResult { + tmdb_id: 1234, + title: HorizontallyScrollableText::from("Test"), + original_language: language(), + status: "released".to_owned(), + overview: "New movie blah blah blah".to_owned(), + genres: genres(), + year: 2023, + runtime: 120, + ratings: ratings_list(), + } + } + + pub fn movie_history_item() -> MovieHistoryItem { + MovieHistoryItem { + source_title: HorizontallyScrollableText::from("Test"), + quality: quality_wrapper(), + languages: vec![language()], + date: DateTime::from(DateTime::parse_from_rfc3339("2022-12-30T07:37:56Z").unwrap()), + event_type: "grabbed".to_owned(), + } + } + + pub fn download_record() -> DownloadRecord { + DownloadRecord { + title: "Test Download Title".to_owned(), + status: "downloading".to_owned(), + id: 1, + movie_id: 1, + size: 3543348019, + sizeleft: 1771674009, + output_path: Some(HorizontallyScrollableText::from("/nfs/movies/Test")), + indexer: "kickass torrents".to_owned(), + download_client: "transmission".to_owned(), + } + } + + pub fn downloads_response() -> DownloadsResponse { + DownloadsResponse { + records: vec![download_record()], + } + } + + pub fn root_folder() -> RootFolder { + RootFolder { + id: 1, + path: "/nfs".to_owned(), + accessible: true, + free_space: 219902325555200, + unmapped_folders: None, + } + } + + pub fn cast_credit() -> Credit { + Credit { + person_name: "Madison Clarke".to_owned(), + character: Some("Johnny Blaze".to_owned()), + department: None, + job: None, + credit_type: CreditType::Cast, + } + } + + pub fn crew_credit() -> Credit { + Credit { + person_name: "Alex Clarke".to_owned(), + character: None, + department: Some("Music".to_owned()), + job: Some("Composition".to_owned()), + credit_type: CreditType::Crew, + } + } + + pub fn indexer() -> Indexer { + Indexer { + enable_rss: true, + enable_automatic_search: true, + enable_interactive_search: true, + supports_rss: true, + supports_search: true, + protocol: "torrent".to_owned(), + priority: 25, + download_client_id: 0, + name: Some("Test Indexer".to_owned()), + implementation_name: Some("Torznab".to_owned()), + implementation: Some("Torznab".to_owned()), + config_contract: Some("TorznabSettings".to_owned()), + tags: vec![Number::from(1)], + id: 1, + fields: Some(vec![ + IndexerField { + name: Some("baseUrl".to_owned()), + value: Some(json!("https://test.com")), + }, + IndexerField { + name: Some("apiKey".to_owned()), + value: Some(json!("")), + }, + IndexerField { + name: Some("seedCriteria.seedRatio".to_owned()), + value: Some(json!("1.2")), + }, + ]), + } + } + + pub fn indexer_settings() -> IndexerSettings { + IndexerSettings { + rss_sync_interval: 60, + allow_hardcoded_subs: true, + id: 1, + ..IndexerSettings::default() + } + } + + pub fn add_movie_body() -> AddMovieBody { + AddMovieBody { + tmdb_id: 1234, + title: "Test".to_owned(), + root_folder_path: "/nfs2".to_owned(), + minimum_availability: "announced".to_owned(), + monitored: true, + quality_profile_id: 2222, + tags: Vec::new(), + tag_input_string: "usenet, testing".into(), + add_options: AddMovieOptions { + monitor: "movieOnly".to_owned(), + search_for_movie: true, + }, + } + } } diff --git a/src/models/radarr_models.rs b/src/models/radarr_models.rs index a99974e..e25d8ab 100644 --- a/src/models/radarr_models.rs +++ b/src/models/radarr_models.rs @@ -29,6 +29,8 @@ pub struct AddMovieBody { pub minimum_availability: String, pub monitored: bool, pub tags: Vec, + #[serde(skip_serializing, skip_deserializing)] + pub tag_input_string: String, pub add_options: AddMovieOptions, } diff --git a/src/network/radarr_network.rs b/src/network/radarr_network.rs index 60497af..88b651b 100644 --- a/src/network/radarr_network.rs +++ b/src/network/radarr_network.rs @@ -7,15 +7,15 @@ use serde_json::{json, Value}; use urlencoding::encode; use crate::models::radarr_models::{ - AddMovieBody, AddMovieOptions, AddMovieSearchResult, BlocklistResponse, Collection, - CollectionMovie, Credit, CreditType, DeleteMovieParams, DownloadRecord, DownloadsResponse, + AddMovieBody, AddMovieSearchResult, BlocklistResponse, Collection, + Credit, CreditType, DeleteMovieParams, DownloadRecord, DownloadsResponse, EditCollectionParams, EditMovieParams, IndexerSettings, IndexerTestResult, Movie, MovieCommandBody, MovieHistoryItem, RadarrRelease, RadarrReleaseDownloadBody, RadarrSerdeable, RadarrTask, RadarrTaskName, SystemStatus, }; use crate::models::servarr_data::modals::{EditIndexerModal, IndexerTestResultModalItem}; use crate::models::servarr_data::radarr::modals::{ - AddMovieModal, EditCollectionModal, EditMovieModal, MovieDetailsModal, + EditCollectionModal, EditMovieModal, MovieDetailsModal, }; use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock; use crate::models::servarr_models::{ @@ -35,7 +35,7 @@ mod radarr_network_tests; #[derive(Debug, Eq, PartialEq, Clone)] pub enum RadarrEvent { - AddMovie(Option), + AddMovie(AddMovieBody), AddRootFolder(Option), AddTag(String), ClearBlocklist, @@ -282,101 +282,18 @@ impl<'a, 'b> Network<'a, 'b> { } } - async fn add_movie(&mut self, add_movie_body_option: Option) -> Result { + async fn add_movie(&mut self, mut add_movie_body: AddMovieBody) -> Result { info!("Adding new movie to Radarr"); - let event = RadarrEvent::AddMovie(None); - let body = if let Some(add_movie_body) = add_movie_body_option { - add_movie_body - } else { - let tags = self - .app - .lock() - .await - .data - .radarr_data - .add_movie_modal - .as_ref() - .unwrap() - .tags - .text - .clone(); - let tag_ids_vec = self.extract_and_add_radarr_tag_ids_vec(tags).await; - let mut app = self.app.lock().await; - let AddMovieModal { - root_folder_list, - monitor_list, - minimum_availability_list, - quality_profile_list, - .. - } = app.data.radarr_data.add_movie_modal.as_ref().unwrap(); - let (tmdb_id, title) = if let Route::Radarr(active_radarr_block, _) = app.get_current_route() - { - if active_radarr_block == ActiveRadarrBlock::CollectionDetails { - let CollectionMovie { tmdb_id, title, .. } = app - .data - .radarr_data - .collection_movies - .current_selection() - .clone(); - (tmdb_id, title.text) - } else { - let AddMovieSearchResult { tmdb_id, title, .. } = app - .data - .radarr_data - .add_searched_movies - .as_ref() - .unwrap() - .current_selection() - .clone(); - (tmdb_id, title.text) - } - } else { - let AddMovieSearchResult { tmdb_id, title, .. } = app - .data - .radarr_data - .add_searched_movies - .as_ref() - .unwrap() - .current_selection() - .clone(); - (tmdb_id, title.text) - }; - let quality_profile = quality_profile_list.current_selection(); - let quality_profile_id = *app - .data - .radarr_data - .quality_profile_map - .iter() - .filter(|(_, value)| *value == quality_profile) - .map(|(key, _)| key) - .next() - .unwrap(); + let event = RadarrEvent::AddMovie(add_movie_body.clone()); + let tag_ids_vec = self + .extract_and_add_radarr_tag_ids_vec(add_movie_body.tag_input_string.clone()) + .await; + add_movie_body.tags = tag_ids_vec; - let path = root_folder_list.current_selection().path.clone(); - let monitor = monitor_list.current_selection().to_string(); - let minimum_availability = minimum_availability_list.current_selection().to_string(); - - app.data.radarr_data.add_movie_modal = None; - - AddMovieBody { - tmdb_id, - title, - root_folder_path: path, - minimum_availability, - monitored: true, - quality_profile_id, - tags: tag_ids_vec, - add_options: AddMovieOptions { - monitor, - search_for_movie: true, - }, - } - }; - - debug!("Add movie body: {body:?}"); + debug!("Add movie body: {add_movie_body:?}"); let request_props = self - .request_props_from(event, RequestMethod::Post, Some(body), None, None) + .request_props_from(event, RequestMethod::Post, Some(add_movie_body), None, None) .await; self @@ -782,7 +699,7 @@ impl<'a, 'b> Network<'a, 'b> { info!("Constructing edit collection body"); - let mut detailed_collection_body: Value = serde_json::from_str(&response).unwrap(); + let mut detailed_collection_body: Value = serde_json::from_str(&response)?; let (monitored, minimum_availability, quality_profile_id, root_folder_path, search_on_add) = if let Some(params) = edit_collection_params { let monitored = params.monitored.unwrap_or_else(|| { @@ -926,7 +843,7 @@ impl<'a, 'b> Network<'a, 'b> { info!("Constructing edit indexer body"); - let mut detailed_indexer_body: Value = serde_json::from_str(&response).unwrap(); + let mut detailed_indexer_body: Value = serde_json::from_str(&response)?; let ( name, @@ -1171,7 +1088,7 @@ impl<'a, 'b> Network<'a, 'b> { info!("Constructing edit movie body"); - let mut detailed_movie_body: Value = serde_json::from_str(&response).unwrap(); + let mut detailed_movie_body: Value = serde_json::from_str(&response)?; let (monitored, minimum_availability, quality_profile_id, root_folder_path, tags) = if let Some(params) = edit_movie_params { let monitored = params.monitored.unwrap_or( diff --git a/src/network/radarr_network_tests.rs b/src/network/radarr_network_tests.rs index fa2b5a0..4a508bd 100644 --- a/src/network/radarr_network_tests.rs +++ b/src/network/radarr_network_tests.rs @@ -15,8 +15,8 @@ mod test { use crate::app::ServarrConfig; use crate::models::radarr_models::{ - BlocklistItem, BlocklistItemMovie, CollectionMovie, MediaInfo, MinimumAvailability, - MovieCollection, MovieFile, MovieMonitor, Rating, RatingsList, + AddMovieOptions, BlocklistItem, BlocklistItemMovie, CollectionMovie, MediaInfo, + MinimumAvailability, MovieCollection, MovieFile, Rating, RatingsList, }; use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock; use crate::models::servarr_models::{ @@ -117,7 +117,7 @@ mod test { #[rstest] fn test_resource_movie( #[values( - RadarrEvent::AddMovie(None), + RadarrEvent::AddMovie(AddMovieBody::default()), RadarrEvent::EditMovie(None), RadarrEvent::GetMovies, RadarrEvent::GetMovieDetails(None), @@ -3396,9 +3396,8 @@ mod test { async_server.assert_async().await; } - #[rstest] #[tokio::test] - async fn test_handle_add_movie_event(#[values(true, false)] movie_details_context: bool) { + async fn test_handle_add_movie_event() { let (async_server, app_arc, _server) = mock_servarr_api( RequestMethod::Post, Some(json!({ @@ -3416,104 +3415,14 @@ mod test { })), Some(json!({})), None, - RadarrEvent::AddMovie(None), + RadarrEvent::AddMovie(AddMovieBody::default()), None, None, ) .await; - - { - let mut app = app_arc.lock().await; - let mut add_movie_modal = AddMovieModal { - tags: "usenet, testing".into(), - ..AddMovieModal::default() - }; - add_movie_modal.root_folder_list.set_items(vec![ - RootFolder { - id: 1, - path: "/nfs".to_owned(), - accessible: true, - free_space: 219902325555200, - unmapped_folders: None, - }, - RootFolder { - id: 2, - path: "/nfs2".to_owned(), - accessible: true, - free_space: 21990232555520, - unmapped_folders: None, - }, - ]); - add_movie_modal.root_folder_list.state.select(Some(1)); - add_movie_modal - .quality_profile_list - .set_items(vec!["HD - 1080p".to_owned()]); - add_movie_modal - .monitor_list - .set_items(Vec::from_iter(MovieMonitor::iter())); - add_movie_modal - .minimum_availability_list - .set_items(Vec::from_iter(MinimumAvailability::iter())); - app.data.radarr_data.add_movie_modal = Some(add_movie_modal); - app.data.radarr_data.quality_profile_map = - BiMap::from_iter([(2222, "HD - 1080p".to_owned())]); - app.data.radarr_data.tags_map = - BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]); - if movie_details_context { - app - .data - .radarr_data - .collection_movies - .set_items(vec![collection_movie()]); - app.push_navigation_stack(ActiveRadarrBlock::CollectionDetails.into()); - } else { - let mut add_searched_movies = StatefulTable::default(); - add_searched_movies.set_items(vec![add_movie_search_result()]); - app.data.radarr_data.add_searched_movies = Some(add_searched_movies); - } - } - let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); - - assert!(network - .handle_radarr_event(RadarrEvent::AddMovie(None)) - .await - .is_ok()); - - async_server.assert_async().await; - assert!(app_arc - .lock() - .await - .data - .radarr_data - .add_movie_modal - .is_none()); - } - - #[tokio::test] - async fn test_handle_add_movie_event_uses_provided_body() { - let (async_server, app_arc, _server) = mock_servarr_api( - RequestMethod::Post, - Some(json!({ - "tmdbId": 1234, - "title": "Test", - "rootFolderPath": "/nfs2", - "minimumAvailability": "announced", - "monitored": true, - "qualityProfileId": 2222, - "tags": [1, 2], - "addOptions": { - "monitor": "movieOnly", - "searchForMovie": true - } - })), - Some(json!({})), - None, - RadarrEvent::AddMovie(None), - None, - None, - ) - .await; - let body = AddMovieBody { + app_arc.lock().await.data.radarr_data.tags_map = + BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]); + let add_movie_body = AddMovieBody { tmdb_id: 1234, title: "Test".to_owned(), root_folder_path: "/nfs2".to_owned(), @@ -3521,6 +3430,7 @@ mod test { monitored: true, quality_profile_id: 2222, tags: vec![1, 2], + tag_input_string: "usenet, testing".into(), add_options: AddMovieOptions { monitor: "movieOnly".to_owned(), search_for_movie: true, @@ -3530,119 +3440,11 @@ mod test { let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); assert!(network - .handle_radarr_event(RadarrEvent::AddMovie(Some(body))) + .handle_radarr_event(RadarrEvent::AddMovie(add_movie_body)) .await .is_ok()); async_server.assert_async().await; - assert!(app_arc - .lock() - .await - .data - .radarr_data - .add_movie_modal - .is_none()); - } - - #[tokio::test] - async fn test_handle_add_movie_event_reuse_existing_table_if_search_already_performed() { - let (async_server, app_arc, _server) = mock_servarr_api( - RequestMethod::Post, - Some(json!({ - "tmdbId": 5678, - "title": "Test", - "rootFolderPath": "/nfs2", - "minimumAvailability": "announced", - "monitored": true, - "qualityProfileId": 2222, - "tags": [1, 2], - "addOptions": { - "monitor": "movieOnly", - "searchForMovie": true - } - })), - Some(json!({})), - None, - RadarrEvent::AddMovie(None), - None, - None, - ) - .await; - - { - let mut app = app_arc.lock().await; - let mut add_movie_modal = AddMovieModal { - tags: "usenet, testing".into(), - ..AddMovieModal::default() - }; - add_movie_modal.root_folder_list.set_items(vec![ - RootFolder { - id: 1, - path: "/nfs".to_owned(), - accessible: true, - free_space: 219902325555200, - unmapped_folders: None, - }, - RootFolder { - id: 2, - path: "/nfs2".to_owned(), - accessible: true, - free_space: 21990232555520, - unmapped_folders: None, - }, - ]); - add_movie_modal.root_folder_list.state.select(Some(1)); - add_movie_modal - .quality_profile_list - .set_items(vec!["HD - 1080p".to_owned()]); - add_movie_modal - .monitor_list - .set_items(Vec::from_iter(MovieMonitor::iter())); - add_movie_modal - .minimum_availability_list - .set_items(Vec::from_iter(MinimumAvailability::iter())); - app.data.radarr_data.add_movie_modal = Some(add_movie_modal); - app.data.radarr_data.quality_profile_map = - BiMap::from_iter([(2222, "HD - 1080p".to_owned())]); - app.data.radarr_data.tags_map = - BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]); - let secondary_search_result = AddMovieSearchResult { - tmdb_id: 5678, - ..add_movie_search_result() - }; - let mut add_searched_movies = StatefulTable::default(); - add_searched_movies.set_items(vec![add_movie_search_result(), secondary_search_result]); - add_searched_movies.scroll_to_bottom(); - app.data.radarr_data.add_searched_movies = Some(add_searched_movies); - } - let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); - - assert!(network - .handle_radarr_event(RadarrEvent::AddMovie(None)) - .await - .is_ok()); - - async_server.assert_async().await; - assert!(app_arc - .lock() - .await - .data - .radarr_data - .add_movie_modal - .is_none()); - assert_eq!( - app_arc - .lock() - .await - .data - .radarr_data - .add_searched_movies - .as_ref() - .unwrap() - .current_selection() - .tmdb_id, - 5678 - ); } #[tokio::test]