diff --git a/src/app/radarr/radarr_tests.rs b/src/app/radarr/radarr_tests.rs index e4be8f1..901ca12 100644 --- a/src/app/radarr/radarr_tests.rs +++ b/src/app/radarr/radarr_tests.rs @@ -6,10 +6,9 @@ mod tests { use crate::app::radarr::ActiveRadarrBlock; use crate::app::App; - use crate::models::radarr_models::{Collection, CollectionMovie, Credit}; + use crate::models::radarr_models::{Collection, CollectionMovie, Credit, RadarrRelease}; use crate::models::servarr_data::radarr::modals::MovieDetailsModal; - use crate::models::servarr_models::Release; use crate::network::radarr_network::RadarrEvent; use crate::network::NetworkEvent; @@ -431,7 +430,7 @@ mod tests { let mut movie_details_modal = MovieDetailsModal::default(); movie_details_modal .movie_releases - .set_items(vec![Release::default()]); + .set_items(vec![RadarrRelease::default()]); app.data.radarr_data.movie_details_modal = Some(movie_details_modal); app diff --git a/src/cli/sonarr/manual_search_command_handler.rs b/src/cli/sonarr/manual_search_command_handler.rs index 5164526..e8e1ca3 100644 --- a/src/cli/sonarr/manual_search_command_handler.rs +++ b/src/cli/sonarr/manual_search_command_handler.rs @@ -28,7 +28,7 @@ pub enum SonarrManualSearchCommand { episode_id: i64, }, #[command( - about = "Trigger a manual search of releases for the given season corresponding to the series with the given ID" + about = "Trigger a manual search of releases for the given season corresponding to the series with the given ID.\nNote that when downloading a season release, ensure that the release includes 'fullSeason: true', otherwise you'll run into issues" )] Season { #[arg( diff --git a/src/handlers/radarr_handlers/library/movie_details_handler.rs b/src/handlers/radarr_handlers/library/movie_details_handler.rs index fce5b46..501f304 100644 --- a/src/handlers/radarr_handlers/library/movie_details_handler.rs +++ b/src/handlers/radarr_handlers/library/movie_details_handler.rs @@ -4,10 +4,11 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::App; use crate::event::Key; use crate::handlers::{handle_prompt_toggle, KeyEventHandler}; +use crate::models::radarr_models::RadarrRelease; use crate::models::servarr_data::radarr::radarr_data::{ ActiveRadarrBlock, EDIT_MOVIE_SELECTION_BLOCKS, MOVIE_DETAILS_BLOCKS, }; -use crate::models::servarr_models::{Language, Release}; +use crate::models::servarr_models::Language; use crate::models::stateful_table::SortOption; use crate::models::{BlockSelectionState, Scrollable}; use crate::network::radarr_network::RadarrEvent; @@ -505,7 +506,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler< } } -fn releases_sorting_options() -> Vec> { +fn releases_sorting_options() -> Vec> { vec![ SortOption { name: "Source", diff --git a/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs b/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs index 174232b..fef7f73 100644 --- a/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs @@ -14,10 +14,11 @@ mod tests { releases_sorting_options, MovieDetailsHandler, }; use crate::handlers::KeyEventHandler; + use crate::models::radarr_models::RadarrRelease; use crate::models::radarr_models::{Credit, MovieHistoryItem}; use crate::models::servarr_data::radarr::modals::MovieDetailsModal; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS}; - use crate::models::servarr_models::{Language, Quality, QualityWrapper, Release}; + use crate::models::servarr_models::{Language, Quality, QualityWrapper}; use crate::models::stateful_table::SortOption; use crate::models::{HorizontallyScrollableText, ScrollableText}; @@ -405,7 +406,7 @@ mod tests { movie_details_modal .movie_releases .set_items(simple_stateful_iterable_vec!( - Release, + RadarrRelease, HorizontallyScrollableText )); app.data.radarr_data.movie_details_modal = Some(movie_details_modal); @@ -453,7 +454,7 @@ mod tests { movie_details_modal .movie_releases .set_items(simple_stateful_iterable_vec!( - Release, + RadarrRelease, HorizontallyScrollableText )); app.data.radarr_data.movie_details_modal = Some(movie_details_modal); @@ -996,7 +997,7 @@ mod tests { movie_details_modal .movie_releases .set_items(extended_stateful_iterable_vec!( - Release, + RadarrRelease, HorizontallyScrollableText )); app.data.radarr_data.movie_details_modal = Some(movie_details_modal); @@ -1054,7 +1055,7 @@ mod tests { movie_details_modal .movie_releases .set_items(extended_stateful_iterable_vec!( - Release, + RadarrRelease, HorizontallyScrollableText )); app.data.radarr_data.movie_details_modal = Some(movie_details_modal); @@ -1249,7 +1250,9 @@ mod tests { movie_details: ScrollableText::with_string("test".to_owned()), ..MovieDetailsModal::default() }; - modal.movie_releases.set_items(vec![Release::default()]); + modal + .movie_releases + .set_items(vec![RadarrRelease::default()]); app.data.radarr_data.movie_details_modal = Some(modal); app.push_navigation_stack(ActiveRadarrBlock::ManualSearch.into()); @@ -1487,6 +1490,8 @@ mod tests { )] active_radarr_block: ActiveRadarrBlock, ) { + use crate::models::radarr_models::RadarrRelease; + let mut app = App::default(); let mut modal = MovieDetailsModal { movie_details: ScrollableText::with_string("Test".to_owned()), @@ -1497,7 +1502,9 @@ mod tests { .set_items(vec![MovieHistoryItem::default()]); modal.movie_cast.set_items(vec![Credit::default()]); modal.movie_crew.set_items(vec![Credit::default()]); - modal.movie_releases.set_items(vec![Release::default()]); + modal + .movie_releases + .set_items(vec![RadarrRelease::default()]); app.data.radarr_data.movie_details_modal = Some(modal); MovieDetailsHandler::with( @@ -1687,7 +1694,9 @@ mod tests { .set_items(vec![MovieHistoryItem::default()]); modal.movie_cast.set_items(vec![Credit::default()]); modal.movie_crew.set_items(vec![Credit::default()]); - modal.movie_releases.set_items(vec![Release::default()]); + modal + .movie_releases + .set_items(vec![RadarrRelease::default()]); app.data.radarr_data.movie_details_modal = Some(modal); MovieDetailsHandler::with( @@ -1757,7 +1766,9 @@ mod tests { .set_items(vec![MovieHistoryItem::default()]); modal.movie_cast.set_items(vec![Credit::default()]); modal.movie_crew.set_items(vec![Credit::default()]); - modal.movie_releases.set_items(vec![Release::default()]); + modal + .movie_releases + .set_items(vec![RadarrRelease::default()]); app.data.radarr_data.movie_details_modal = Some(modal); MovieDetailsHandler::with( @@ -1851,7 +1862,8 @@ mod tests { #[test] fn test_releases_sorting_options_source() { - let expected_cmp_fn: fn(&Release, &Release) -> Ordering = |a, b| a.protocol.cmp(&b.protocol); + let expected_cmp_fn: fn(&RadarrRelease, &RadarrRelease) -> Ordering = + |a, b| a.protocol.cmp(&b.protocol); let mut expected_releases_vec = release_vec(); expected_releases_vec.sort_by(expected_cmp_fn); @@ -1865,7 +1877,7 @@ mod tests { #[test] fn test_releases_sorting_options_age() { - let expected_cmp_fn: fn(&Release, &Release) -> Ordering = |a, b| a.age.cmp(&b.age); + let expected_cmp_fn: fn(&RadarrRelease, &RadarrRelease) -> Ordering = |a, b| a.age.cmp(&b.age); let mut expected_releases_vec = release_vec(); expected_releases_vec.sort_by(expected_cmp_fn); @@ -1879,7 +1891,8 @@ mod tests { #[test] fn test_releases_sorting_options_rejected() { - let expected_cmp_fn: fn(&Release, &Release) -> Ordering = |a, b| a.rejected.cmp(&b.rejected); + let expected_cmp_fn: fn(&RadarrRelease, &RadarrRelease) -> Ordering = + |a, b| a.rejected.cmp(&b.rejected); let mut expected_releases_vec = release_vec(); expected_releases_vec.sort_by(expected_cmp_fn); @@ -1893,7 +1906,7 @@ mod tests { #[test] fn test_releases_sorting_options_title() { - let expected_cmp_fn: fn(&Release, &Release) -> Ordering = |a, b| { + let expected_cmp_fn: fn(&RadarrRelease, &RadarrRelease) -> Ordering = |a, b| { a.title .text .to_lowercase() @@ -1912,7 +1925,7 @@ mod tests { #[test] fn test_releases_sorting_options_indexer() { - let expected_cmp_fn: fn(&Release, &Release) -> Ordering = + let expected_cmp_fn: fn(&RadarrRelease, &RadarrRelease) -> Ordering = |a, b| a.indexer.to_lowercase().cmp(&b.indexer.to_lowercase()); let mut expected_releases_vec = release_vec(); expected_releases_vec.sort_by(expected_cmp_fn); @@ -1927,7 +1940,8 @@ mod tests { #[test] fn test_releases_sorting_options_size() { - let expected_cmp_fn: fn(&Release, &Release) -> Ordering = |a, b| a.size.cmp(&b.size); + let expected_cmp_fn: fn(&RadarrRelease, &RadarrRelease) -> Ordering = + |a, b| a.size.cmp(&b.size); let mut expected_releases_vec = release_vec(); expected_releases_vec.sort_by(expected_cmp_fn); @@ -1941,7 +1955,7 @@ mod tests { #[test] fn test_releases_sorting_options_peers() { - let expected_cmp_fn: fn(&Release, &Release) -> Ordering = |a, b| { + let expected_cmp_fn: fn(&RadarrRelease, &RadarrRelease) -> Ordering = |a, b| { let default_number = Number::from(i64::MAX); let seeder_a = a .seeders @@ -1971,7 +1985,7 @@ mod tests { #[test] fn test_releases_sorting_options_language() { - let expected_cmp_fn: fn(&Release, &Release) -> Ordering = |a, b| { + let expected_cmp_fn: fn(&RadarrRelease, &RadarrRelease) -> Ordering = |a, b| { let default_language_vec = vec![Language { name: "_".to_owned(), }]; @@ -1993,7 +2007,8 @@ mod tests { #[test] fn test_releases_sorting_options_quality() { - let expected_cmp_fn: fn(&Release, &Release) -> Ordering = |a, b| a.quality.cmp(&b.quality); + let expected_cmp_fn: fn(&RadarrRelease, &RadarrRelease) -> Ordering = + |a, b| a.quality.cmp(&b.quality); let mut expected_releases_vec = release_vec(); expected_releases_vec.sort_by(expected_cmp_fn); @@ -2040,7 +2055,9 @@ mod tests { .set_items(vec![MovieHistoryItem::default()]); modal.movie_cast.set_items(vec![Credit::default()]); modal.movie_crew.set_items(vec![Credit::default()]); - modal.movie_releases.set_items(vec![Release::default()]); + modal + .movie_releases + .set_items(vec![RadarrRelease::default()]); app.data.radarr_data.movie_details_modal = Some(modal); let handler = MovieDetailsHandler::with( @@ -2149,7 +2166,9 @@ mod tests { let mut app = App::default(); app.is_loading = false; let mut modal = MovieDetailsModal::default(); - modal.movie_releases.set_items(vec![Release::default()]); + modal + .movie_releases + .set_items(vec![RadarrRelease::default()]); app.data.radarr_data.movie_details_modal = Some(modal); let handler = MovieDetailsHandler::with( @@ -2162,8 +2181,8 @@ mod tests { assert!(handler.is_ready()); } - fn release_vec() -> Vec { - let release_a = Release { + fn release_vec() -> Vec { + let release_a = RadarrRelease { protocol: "Protocol A".to_owned(), age: 1, title: HorizontallyScrollableText::from("Title A"), @@ -2179,9 +2198,9 @@ mod tests { name: "Quality A".to_owned(), }, }, - ..Release::default() + ..RadarrRelease::default() }; - let release_b = Release { + let release_b = RadarrRelease { protocol: "Protocol B".to_owned(), age: 2, title: HorizontallyScrollableText::from("title B"), @@ -2197,9 +2216,9 @@ mod tests { name: "Quality B".to_owned(), }, }, - ..Release::default() + ..RadarrRelease::default() }; - let release_c = Release { + let release_c = RadarrRelease { protocol: "Protocol C".to_owned(), age: 3, title: HorizontallyScrollableText::from("Title C"), @@ -2213,13 +2232,13 @@ mod tests { name: "Quality C".to_owned(), }, }, - ..Release::default() + ..RadarrRelease::default() }; vec![release_a, release_b, release_c] } - fn sort_options() -> Vec> { + fn sort_options() -> Vec> { vec![SortOption { name: "Test 1", cmp_fn: Some(|a, b| a.age.cmp(&b.age)), diff --git a/src/models/radarr_models.rs b/src/models/radarr_models.rs index 5675502..cf37f7c 100644 --- a/src/models/radarr_models.rs +++ b/src/models/radarr_models.rs @@ -11,7 +11,7 @@ use crate::{models::HorizontallyScrollableText, serde_enum_from}; use super::servarr_models::{ DiskSpace, HostConfig, Indexer, Language, LogResponse, QualityProfile, QualityWrapper, - QueueEvent, Release, RootFolder, SecurityConfig, Tag, Update, + QueueEvent, RootFolder, SecurityConfig, Tag, Update, }; use super::{EnumDisplayStyle, Serdeable}; @@ -412,6 +412,28 @@ pub struct RatingsList { pub rotten_tomatoes: Option, } +#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[serde(default)] +pub struct RadarrRelease { + pub guid: String, + pub protocol: String, + #[serde(deserialize_with = "super::from_i64")] + pub age: i64, + pub title: HorizontallyScrollableText, + 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 languages: Option>, + pub quality: QualityWrapper, +} + #[derive(Default, Serialize, Debug, PartialEq, Eq, Clone)] #[serde(rename_all = "camelCase")] pub struct RadarrReleaseDownloadBody { @@ -485,7 +507,7 @@ pub enum RadarrSerdeable { Movies(Vec), QualityProfiles(Vec), QueueEvents(Vec), - Releases(Vec), + Releases(Vec), RootFolders(Vec), SecurityConfig(SecurityConfig), SystemStatus(SystemStatus), @@ -526,7 +548,7 @@ serde_enum_from!( Movies(Vec), QualityProfiles(Vec), QueueEvents(Vec), - Releases(Vec), + Releases(Vec), RootFolders(Vec), SecurityConfig(SecurityConfig), SystemStatus(SystemStatus), diff --git a/src/models/radarr_models_tests.rs b/src/models/radarr_models_tests.rs index 4afe5a1..d49c3cd 100644 --- a/src/models/radarr_models_tests.rs +++ b/src/models/radarr_models_tests.rs @@ -7,8 +7,8 @@ mod tests { radarr_models::{ AddMovieSearchResult, BlocklistItem, BlocklistResponse, Collection, Credit, DiskSpace, DownloadRecord, DownloadsResponse, Indexer, IndexerSettings, IndexerTestResult, - MinimumAvailability, Monitor, Movie, MovieHistoryItem, QualityProfile, RadarrSerdeable, - RadarrTask, RadarrTaskName, Release, SystemStatus, Tag, Update, + MinimumAvailability, Monitor, Movie, MovieHistoryItem, QualityProfile, RadarrRelease, + RadarrSerdeable, RadarrTask, RadarrTaskName, SystemStatus, Tag, Update, }, servarr_models::{HostConfig, Log, LogResponse, QueueEvent, RootFolder, SecurityConfig}, EnumDisplayStyle, Serdeable, @@ -317,9 +317,9 @@ mod tests { #[test] fn test_radarr_serdeable_from_releases() { - let releases = vec![Release { + let releases = vec![RadarrRelease { size: 1, - ..Release::default() + ..RadarrRelease::default() }]; let radarr_serdeable: RadarrSerdeable = releases.clone().into(); diff --git a/src/models/servarr_data/radarr/modals.rs b/src/models/servarr_data/radarr/modals.rs index 2d7c893..9a98c4e 100644 --- a/src/models/servarr_data/radarr/modals.rs +++ b/src/models/servarr_data/radarr/modals.rs @@ -1,10 +1,10 @@ use strum::IntoEnumIterator; use crate::models::radarr_models::{ - Collection, Credit, MinimumAvailability, Monitor, Movie, MovieHistoryItem, + Collection, Credit, MinimumAvailability, Monitor, Movie, MovieHistoryItem, RadarrRelease, }; use crate::models::servarr_data::radarr::radarr_data::RadarrData; -use crate::models::servarr_models::{Indexer, Release, RootFolder}; +use crate::models::servarr_models::{Indexer, RootFolder}; use crate::models::stateful_list::StatefulList; use crate::models::stateful_table::StatefulTable; use crate::models::{HorizontallyScrollableText, ScrollableText}; @@ -22,7 +22,7 @@ pub struct MovieDetailsModal { pub movie_history: StatefulTable, pub movie_cast: StatefulTable, pub movie_crew: StatefulTable, - pub movie_releases: StatefulTable, + pub movie_releases: StatefulTable, } #[derive(Default, Debug, PartialEq, Eq)] diff --git a/src/models/servarr_data/radarr/radarr_test_utils.rs b/src/models/servarr_data/radarr/radarr_test_utils.rs index b437060..a3c5469 100644 --- a/src/models/servarr_data/radarr/radarr_test_utils.rs +++ b/src/models/servarr_data/radarr/radarr_test_utils.rs @@ -1,11 +1,10 @@ #[cfg(test)] pub mod utils { use crate::models::radarr_models::{ - AddMovieSearchResult, CollectionMovie, Credit, MovieHistoryItem, + AddMovieSearchResult, CollectionMovie, Credit, MovieHistoryItem, RadarrRelease, }; use crate::models::servarr_data::radarr::modals::MovieDetailsModal; use crate::models::servarr_data::radarr::radarr_data::RadarrData; - use crate::models::servarr_models::Release; use crate::models::stateful_table::StatefulTable; use crate::models::{HorizontallyScrollableText, ScrollableText}; @@ -25,7 +24,7 @@ pub mod utils { .set_items(vec![Credit::default()]); movie_details_modal .movie_releases - .set_items(vec![Release::default()]); + .set_items(vec![RadarrRelease::default()]); let mut radarr_data = RadarrData { delete_movie_files: true, diff --git a/src/models/servarr_data/sonarr/modals.rs b/src/models/servarr_data/sonarr/modals.rs index 71dbb96..4abf22c 100644 --- a/src/models/servarr_data/sonarr/modals.rs +++ b/src/models/servarr_data/sonarr/modals.rs @@ -1,6 +1,5 @@ use crate::models::{ - servarr_models::Release, - sonarr_models::{Episode, SonarrHistoryItem}, + sonarr_models::{Episode, SonarrHistoryItem, SonarrRelease}, stateful_table::StatefulTable, ScrollableText, }; @@ -12,12 +11,12 @@ pub struct EpisodeDetailsModal { pub audio_details: String, pub video_details: String, pub episode_history: StatefulTable, - pub episode_releases: StatefulTable, + pub episode_releases: StatefulTable, } #[derive(Default)] pub struct SeasonDetailsModal { pub episodes: StatefulTable, pub episode_details_modal: Option, - pub season_releases: StatefulTable, + pub season_releases: StatefulTable, } diff --git a/src/models/servarr_models.rs b/src/models/servarr_models.rs index 3c2b5c1..0089f5f 100644 --- a/src/models/servarr_models.rs +++ b/src/models/servarr_models.rs @@ -195,28 +195,6 @@ pub struct QueueEvent { pub duration: Option, } -#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq)] -#[serde(rename_all = "camelCase")] -#[serde(default)] -pub struct Release { - pub guid: String, - pub protocol: String, - #[serde(deserialize_with = "super::from_i64")] - pub age: i64, - pub title: HorizontallyScrollableText, - 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 languages: Option>, - pub quality: QualityWrapper, -} - #[derive(Default, Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] #[serde(rename_all = "camelCase")] pub struct RootFolder { diff --git a/src/models/sonarr_models.rs b/src/models/sonarr_models.rs index 92cd9e8..2f72ab7 100644 --- a/src/models/sonarr_models.rs +++ b/src/models/sonarr_models.rs @@ -13,7 +13,7 @@ use super::{ radarr_models::IndexerTestResult, servarr_models::{ DiskSpace, HostConfig, Indexer, Language, LogResponse, QualityProfile, QualityWrapper, - QueueEvent, Release, RootFolder, SecurityConfig, Tag, Update, + QueueEvent, RootFolder, SecurityConfig, Tag, Update, }, EnumDisplayStyle, HorizontallyScrollableText, Serdeable, }; @@ -401,6 +401,28 @@ pub struct SonarrCommandBody { pub episode_ids: Option>, } +#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[serde(default)] +pub struct SonarrRelease { + pub guid: String, + pub protocol: String, + #[serde(deserialize_with = "super::from_i64")] + pub age: i64, + pub title: HorizontallyScrollableText, + 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 languages: Option>, + pub quality: QualityWrapper, + pub full_season: bool, +} #[derive(Default, Serialize, Debug, PartialEq, Eq, Clone)] #[serde(rename_all = "camelCase")] pub struct SonarrReleaseDownloadBody { @@ -467,7 +489,7 @@ pub enum SonarrSerdeable { LogResponse(LogResponse), QualityProfiles(Vec), QueueEvents(Vec), - Releases(Vec), + Releases(Vec), RootFolders(Vec), SecurityConfig(SecurityConfig), SeriesVec(Vec), @@ -508,7 +530,7 @@ serde_enum_from!( LogResponse(LogResponse), QualityProfiles(Vec), QueueEvents(Vec), - Releases(Vec), + Releases(Vec), RootFolders(Vec), SecurityConfig(SecurityConfig), SeriesVec(Vec), diff --git a/src/models/sonarr_models_tests.rs b/src/models/sonarr_models_tests.rs index a56af3d..0a6aff3 100644 --- a/src/models/sonarr_models_tests.rs +++ b/src/models/sonarr_models_tests.rs @@ -6,13 +6,13 @@ mod tests { use crate::models::{ radarr_models::IndexerTestResult, servarr_models::{ - DiskSpace, HostConfig, Indexer, Log, LogResponse, QualityProfile, QueueEvent, Release, - RootFolder, SecurityConfig, Tag, Update, + DiskSpace, HostConfig, Indexer, Log, LogResponse, QualityProfile, QueueEvent, RootFolder, + SecurityConfig, Tag, Update, }, sonarr_models::{ BlocklistItem, BlocklistResponse, DownloadRecord, DownloadsResponse, Episode, IndexerSettings, Series, SeriesStatus, SeriesType, SonarrHistoryEventType, SonarrHistoryItem, - SonarrSerdeable, SonarrTask, SonarrTaskName, SystemStatus, + SonarrRelease, SonarrSerdeable, SonarrTask, SonarrTaskName, SystemStatus, }, EnumDisplayStyle, Serdeable, }; @@ -355,9 +355,9 @@ mod tests { #[test] fn test_sonarr_serdeable_from_releases() { - let releases = vec![Release { + let releases = vec![SonarrRelease { size: 1, - ..Release::default() + ..SonarrRelease::default() }]; let sonarr_serdeable: SonarrSerdeable = releases.clone().into(); diff --git a/src/network/radarr_network.rs b/src/network/radarr_network.rs index e195118..82b2b1b 100644 --- a/src/network/radarr_network.rs +++ b/src/network/radarr_network.rs @@ -10,8 +10,8 @@ use crate::models::radarr_models::{ AddMovieBody, AddMovieSearchResult, AddOptions, BlocklistResponse, Collection, CollectionMovie, Credit, CreditType, DeleteMovieParams, DownloadRecord, DownloadsResponse, EditCollectionParams, EditIndexerParams, EditMovieParams, IndexerSettings, IndexerTestResult, Movie, MovieCommandBody, - MovieHistoryItem, RadarrReleaseDownloadBody, RadarrSerdeable, RadarrTask, RadarrTaskName, - SystemStatus, + MovieHistoryItem, RadarrRelease, RadarrReleaseDownloadBody, RadarrSerdeable, RadarrTask, + RadarrTaskName, SystemStatus, }; use crate::models::servarr_data::modals::IndexerTestResultModalItem; use crate::models::servarr_data::radarr::modals::{ @@ -20,7 +20,7 @@ use crate::models::servarr_data::radarr::modals::{ use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock; use crate::models::servarr_models::{ AddRootFolderBody, CommandBody, DiskSpace, HostConfig, Indexer, LogResponse, QualityProfile, - QueueEvent, Release, RootFolder, SecurityConfig, Tag, Update, + QueueEvent, RootFolder, SecurityConfig, Tag, Update, }; use crate::models::stateful_table::StatefulTable; use crate::models::{HorizontallyScrollableText, Route, Scrollable, ScrollableText}; @@ -675,7 +675,7 @@ impl<'a, 'b> Network<'a, 'b> { let (movie_id, _) = self.extract_movie_id(None).await; let (guid, title, indexer_id) = { let app = self.app.lock().await; - let Release { + let RadarrRelease { guid, title, indexer_id, @@ -1770,7 +1770,7 @@ impl<'a, 'b> Network<'a, 'b> { .await } - async fn get_movie_releases(&mut self, movie_id: Option) -> Result> { + async fn get_movie_releases(&mut self, movie_id: Option) -> Result> { let (id, movie_id_param) = self.extract_movie_id(movie_id).await; info!("Fetching releases for movie with ID: {id}"); let event = RadarrEvent::GetReleases(None); @@ -1786,7 +1786,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; self - .handle_request::<(), Vec>(request_props, |release_vec, mut app| { + .handle_request::<(), Vec>(request_props, |release_vec, mut app| { if app.data.radarr_data.movie_details_modal.is_none() { app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal::default()); } diff --git a/src/network/radarr_network_tests.rs b/src/network/radarr_network_tests.rs index 75703eb..f695f1a 100644 --- a/src/network/radarr_network_tests.rs +++ b/src/network/radarr_network_tests.rs @@ -5221,8 +5221,8 @@ mod test { QualityWrapper { quality: quality() } } - fn release() -> Release { - Release { + fn release() -> RadarrRelease { + RadarrRelease { guid: "1234".to_owned(), protocol: "torrent".to_owned(), age: 1, diff --git a/src/network/sonarr_network.rs b/src/network/sonarr_network.rs index 1facb66..ae96b8f 100644 --- a/src/network/sonarr_network.rs +++ b/src/network/sonarr_network.rs @@ -15,12 +15,12 @@ use crate::{ }, servarr_models::{ AddRootFolderBody, CommandBody, DiskSpace, HostConfig, Indexer, LogResponse, QualityProfile, - QueueEvent, Release, RootFolder, SecurityConfig, Tag, Update, + QueueEvent, RootFolder, SecurityConfig, Tag, Update, }, sonarr_models::{ BlocklistResponse, DownloadRecord, DownloadsResponse, Episode, IndexerSettings, Series, - SonarrCommandBody, SonarrHistoryItem, SonarrHistoryWrapper, SonarrReleaseDownloadBody, - SonarrSerdeable, SonarrTask, SonarrTaskName, SystemStatus, + SonarrCommandBody, SonarrHistoryItem, SonarrHistoryWrapper, SonarrRelease, + SonarrReleaseDownloadBody, SonarrSerdeable, SonarrTask, SonarrTaskName, SystemStatus, }, stateful_table::StatefulTable, HorizontallyScrollableText, Route, Scrollable, ScrollableText, @@ -1009,7 +1009,7 @@ impl<'a, 'b> Network<'a, 'b> { .await } - async fn get_episode_releases(&mut self, episode_id: Option) -> Result> { + async fn get_episode_releases(&mut self, episode_id: Option) -> Result> { let event = SonarrEvent::GetEpisodeReleases(None); let id = self.extract_episode_id(episode_id).await; @@ -1026,7 +1026,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; self - .handle_request::<(), Vec>(request_props, |release_vec, mut app| { + .handle_request::<(), Vec>(request_props, |release_vec, mut app| { if app.data.sonarr_data.season_details_modal.is_none() { app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default()); } @@ -1067,7 +1067,7 @@ impl<'a, 'b> Network<'a, 'b> { async fn get_season_releases( &mut self, series_season_id_tuple: Option<(i64, i64)>, - ) -> Result> { + ) -> Result> { let event = SonarrEvent::GetSeasonReleases(None); let (series_id, season_number) = if let Some((series_id, season_number)) = series_season_id_tuple { @@ -1092,11 +1092,16 @@ impl<'a, 'b> Network<'a, 'b> { .await; self - .handle_request::<(), Vec>(request_props, |release_vec, mut app| { + .handle_request::<(), Vec>(request_props, |release_vec, mut app| { if app.data.sonarr_data.season_details_modal.is_none() { app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default()); } + let season_releases_vec = release_vec + .into_iter() + .filter(|release| release.full_season) + .collect(); + app .data .sonarr_data @@ -1104,7 +1109,7 @@ impl<'a, 'b> Network<'a, 'b> { .as_mut() .unwrap() .season_releases - .set_items(release_vec); + .set_items(season_releases_vec); }) .await } diff --git a/src/network/sonarr_network_tests.rs b/src/network/sonarr_network_tests.rs index 260d44a..a3b86cc 100644 --- a/src/network/sonarr_network_tests.rs +++ b/src/network/sonarr_network_tests.rs @@ -21,11 +21,11 @@ mod test { use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock; use crate::models::servarr_models::{ DiskSpace, HostConfig, Indexer, IndexerField, Language, LogResponse, Quality, QualityProfile, - QualityWrapper, QueueEvent, Release, RootFolder, SecurityConfig, Tag, Update, + QualityWrapper, QueueEvent, RootFolder, SecurityConfig, Tag, Update, }; use crate::models::sonarr_models::{ BlocklistItem, DownloadRecord, DownloadsResponse, Episode, EpisodeFile, MediaInfo, - SonarrReleaseDownloadBody, SonarrTaskName, + SonarrRelease, SonarrReleaseDownloadBody, SonarrTaskName, }; use crate::models::sonarr_models::{ BlocklistResponse, SonarrHistoryData, SonarrHistoryItem, SonarrHistoryWrapper, @@ -612,7 +612,7 @@ mod test { Some(json!({ "guid": "1234", "indexerId": 2, - "seriesId": 1 + "seriesId": 1, })), Some(json!({})), None, @@ -2712,21 +2712,53 @@ mod test { #[tokio::test] async fn test_handle_get_season_releases_event() { - let release_json = json!([{ - "guid": "1234", - "protocol": "torrent", - "age": 1, - "title": "Test Release", - "indexer": "kickass torrents", - "indexerId": 2, - "size": 1234, - "rejected": true, - "rejections": [ "Unknown quality profile", "Release is already mapped" ], - "seeders": 2, - "leechers": 1, - "languages": [ { "name": "English" } ], - "quality": { "quality": { "name": "Bluray-1080p" }} - }]); + let release_json = json!([ + { + "guid": "1234", + "protocol": "torrent", + "age": 1, + "title": "Test Release", + "indexer": "kickass torrents", + "indexerId": 2, + "size": 1234, + "rejected": true, + "rejections": [ "Unknown quality profile", "Release is already mapped" ], + "seeders": 2, + "leechers": 1, + "languages": [ { "name": "English" } ], + "quality": { "quality": { "name": "Bluray-1080p" }}, + "fullSeason": true + }, + { + "guid": "4567", + "protocol": "torrent", + "age": 1, + "title": "Test Release", + "indexer": "kickass torrents", + "indexerId": 2, + "size": 1234, + "rejected": true, + "rejections": [ "Unknown quality profile", "Release is already mapped" ], + "seeders": 2, + "leechers": 1, + "languages": [ { "name": "English" } ], + "quality": { "quality": { "name": "Bluray-1080p" }}, + } + ]); + let expected_filtered_sonarr_release = SonarrRelease { + full_season: true, + ..release() + }; + let expected_raw_sonarr_releases = vec![ + SonarrRelease { + full_season: true, + ..release() + }, + SonarrRelease { + guid: "4567".to_owned(), + ..release() + }, + ]; let (async_server, app_arc, _server) = mock_servarr_api( RequestMethod::Get, None, @@ -2772,29 +2804,51 @@ mod test { .unwrap() .season_releases .items, - vec![release()] + vec![expected_filtered_sonarr_release] ); - assert_eq!(releases_vec, vec![release()]); + assert_eq!(releases_vec, expected_raw_sonarr_releases); } } #[tokio::test] async fn test_handle_get_season_releases_event_empty_season_details_modal() { - let release_json = json!([{ - "guid": "1234", - "protocol": "torrent", - "age": 1, - "title": "Test Release", - "indexer": "kickass torrents", - "indexerId": 2, - "size": 1234, - "rejected": true, - "rejections": [ "Unknown quality profile", "Release is already mapped" ], - "seeders": 2, - "leechers": 1, - "languages": [ { "name": "English" } ], - "quality": { "quality": { "name": "Bluray-1080p" }} - }]); + let release_json = json!([ + { + "guid": "1234", + "protocol": "torrent", + "age": 1, + "title": "Test Release", + "indexer": "kickass torrents", + "indexerId": 2, + "size": 1234, + "rejected": true, + "rejections": [ "Unknown quality profile", "Release is already mapped" ], + "seeders": 2, + "leechers": 1, + "languages": [ { "name": "English" } ], + "quality": { "quality": { "name": "Bluray-1080p" }}, + "fullSeason": true + }, + { + "guid": "4567", + "protocol": "usenet", + "age": 1, + "title": "Test Release", + "indexer": "kickass torrents", + "indexerId": 2, + "size": 1234, + "rejected": true, + "rejections": [ "Unknown quality profile", "Release is already mapped" ], + "seeders": 2, + "leechers": 1, + "languages": [ { "name": "English" } ], + "quality": { "quality": { "name": "Bluray-1080p" }}, + } + ]); + let expected_sonarr_release = SonarrRelease { + full_season: true, + ..release() + }; let (async_server, app_arc, _server) = mock_servarr_api( RequestMethod::Get, None, @@ -2838,27 +2892,59 @@ mod test { .unwrap() .season_releases .items, - vec![release()] + vec![expected_sonarr_release] ); } #[tokio::test] async fn test_handle_get_season_releases_event_uses_provided_series_id_and_season_number() { - let release_json = json!([{ - "guid": "1234", - "protocol": "torrent", - "age": 1, - "title": "Test Release", - "indexer": "kickass torrents", - "indexerId": 2, - "size": 1234, - "rejected": true, - "rejections": [ "Unknown quality profile", "Release is already mapped" ], - "seeders": 2, - "leechers": 1, - "languages": [ { "name": "English" } ], - "quality": { "quality": { "name": "Bluray-1080p" }} - }]); + let release_json = json!([ + { + "guid": "1234", + "protocol": "torrent", + "age": 1, + "title": "Test Release", + "indexer": "kickass torrents", + "indexerId": 2, + "size": 1234, + "rejected": true, + "rejections": [ "Unknown quality profile", "Release is already mapped" ], + "seeders": 2, + "leechers": 1, + "languages": [ { "name": "English" } ], + "quality": { "quality": { "name": "Bluray-1080p" }}, + "fullSeason": true + }, + { + "guid": "4567", + "protocol": "torrent", + "age": 1, + "title": "Test Release", + "indexer": "kickass torrents", + "indexerId": 2, + "size": 1234, + "rejected": true, + "rejections": [ "Unknown quality profile", "Release is already mapped" ], + "seeders": 2, + "leechers": 1, + "languages": [ { "name": "English" } ], + "quality": { "quality": { "name": "Bluray-1080p" }}, + } + ]); + let expected_filtered_sonarr_release = SonarrRelease { + full_season: true, + ..release() + }; + let expected_raw_sonarr_releases = vec![ + SonarrRelease { + full_season: true, + ..release() + }, + SonarrRelease { + guid: "4567".to_owned(), + ..release() + }, + ]; let (async_server, app_arc, _server) = mock_servarr_api( RequestMethod::Get, None, @@ -2904,29 +2990,61 @@ mod test { .unwrap() .season_releases .items, - vec![release()] + vec![expected_filtered_sonarr_release] ); - assert_eq!(releases_vec, vec![release()]); + assert_eq!(releases_vec, expected_raw_sonarr_releases); } } #[tokio::test] async fn test_handle_get_season_releases_event_filtered_series_and_filtered_seasons() { - let release_json = json!([{ - "guid": "1234", - "protocol": "torrent", - "age": 1, - "title": "Test Release", - "indexer": "kickass torrents", - "indexerId": 2, - "size": 1234, - "rejected": true, - "rejections": [ "Unknown quality profile", "Release is already mapped" ], - "seeders": 2, - "leechers": 1, - "languages": [ { "name": "English" } ], - "quality": { "quality": { "name": "Bluray-1080p" }} - }]); + let release_json = json!([ + { + "guid": "1234", + "protocol": "torrent", + "age": 1, + "title": "Test Release", + "indexer": "kickass torrents", + "indexerId": 2, + "size": 1234, + "rejected": true, + "rejections": [ "Unknown quality profile", "Release is already mapped" ], + "seeders": 2, + "leechers": 1, + "languages": [ { "name": "English" } ], + "quality": { "quality": { "name": "Bluray-1080p" }}, + "fullSeason": true + }, + { + "guid": "4567", + "protocol": "torrent", + "age": 1, + "title": "Test Release", + "indexer": "kickass torrents", + "indexerId": 2, + "size": 1234, + "rejected": true, + "rejections": [ "Unknown quality profile", "Release is already mapped" ], + "seeders": 2, + "leechers": 1, + "languages": [ { "name": "English" } ], + "quality": { "quality": { "name": "Bluray-1080p" }}, + } + ]); + let expected_filtered_sonarr_release = SonarrRelease { + full_season: true, + ..release() + }; + let expected_raw_sonarr_releases = vec![ + SonarrRelease { + full_season: true, + ..release() + }, + SonarrRelease { + guid: "4567".to_owned(), + ..release() + }, + ]; let (async_server, app_arc, _server) = mock_servarr_api( RequestMethod::Get, None, @@ -2970,9 +3088,9 @@ mod test { .unwrap() .season_releases .items, - vec![release()] + vec![expected_filtered_sonarr_release] ); - assert_eq!(releases_vec, vec![release()]); + assert_eq!(releases_vec, expected_raw_sonarr_releases); } } @@ -5097,8 +5215,8 @@ mod test { ] } - fn release() -> Release { - Release { + fn release() -> SonarrRelease { + SonarrRelease { guid: "1234".to_owned(), protocol: "torrent".to_owned(), age: 1, @@ -5112,6 +5230,7 @@ mod test { leechers: Some(Number::from(1)), languages: Some(vec![language()]), quality: quality_wrapper(), + full_season: false, } } diff --git a/src/ui/radarr_ui/library/movie_details_ui.rs b/src/ui/radarr_ui/library/movie_details_ui.rs index 36699a6..ff7dd77 100644 --- a/src/ui/radarr_ui/library/movie_details_ui.rs +++ b/src/ui/radarr_ui/library/movie_details_ui.rs @@ -7,10 +7,9 @@ use ratatui::widgets::{Cell, Paragraph, Row, Wrap}; use ratatui::Frame; use crate::app::App; -use crate::models::radarr_models::{Credit, MovieHistoryItem}; +use crate::models::radarr_models::{Credit, MovieHistoryItem, RadarrRelease}; use crate::models::servarr_data::radarr::modals::MovieDetailsModal; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS}; -use crate::models::servarr_models::Release; use crate::models::Route; use crate::ui::radarr_ui::library::draw_library; use crate::ui::styles::ManagarrStyle; @@ -381,7 +380,7 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .clone(), movie_details_modal.movie_releases.items.is_empty(), ), - _ => (Release::default(), true), + _ => (RadarrRelease::default(), true), }; let current_route = *app.get_current_route(); let mut default_movie_details_modal = MovieDetailsModal::default(); @@ -399,8 +398,8 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .unwrap_or(&mut default_movie_details_modal) .movie_releases, ); - let releases_row_mapping = |release: &Release| { - let Release { + let releases_row_mapping = |release: &RadarrRelease| { + let RadarrRelease { protocol, age, title,