fix(network): Added filtering for full seasons specifically in the UI when performing a manual full season search and added a message to the CLI that noes to only try to download a full season if that release includes 'fullSeason: true'

This commit is contained in:
2024-11-23 12:15:41 -07:00
parent 3be9321df6
commit 4d92c350de
17 changed files with 336 additions and 174 deletions
+2 -3
View File
@@ -6,10 +6,9 @@ mod tests {
use crate::app::radarr::ActiveRadarrBlock; use crate::app::radarr::ActiveRadarrBlock;
use crate::app::App; 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_data::radarr::modals::MovieDetailsModal;
use crate::models::servarr_models::Release;
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
use crate::network::NetworkEvent; use crate::network::NetworkEvent;
@@ -431,7 +430,7 @@ mod tests {
let mut movie_details_modal = MovieDetailsModal::default(); let mut movie_details_modal = MovieDetailsModal::default();
movie_details_modal movie_details_modal
.movie_releases .movie_releases
.set_items(vec![Release::default()]); .set_items(vec![RadarrRelease::default()]);
app.data.radarr_data.movie_details_modal = Some(movie_details_modal); app.data.radarr_data.movie_details_modal = Some(movie_details_modal);
app app
@@ -28,7 +28,7 @@ pub enum SonarrManualSearchCommand {
episode_id: i64, episode_id: i64,
}, },
#[command( #[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 { Season {
#[arg( #[arg(
@@ -4,10 +4,11 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler}; use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::RadarrRelease;
use crate::models::servarr_data::radarr::radarr_data::{ use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, EDIT_MOVIE_SELECTION_BLOCKS, MOVIE_DETAILS_BLOCKS, 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::stateful_table::SortOption;
use crate::models::{BlockSelectionState, Scrollable}; use crate::models::{BlockSelectionState, Scrollable};
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
@@ -505,7 +506,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
} }
} }
fn releases_sorting_options() -> Vec<SortOption<Release>> { fn releases_sorting_options() -> Vec<SortOption<RadarrRelease>> {
vec![ vec![
SortOption { SortOption {
name: "Source", name: "Source",
@@ -14,10 +14,11 @@ mod tests {
releases_sorting_options, MovieDetailsHandler, releases_sorting_options, MovieDetailsHandler,
}; };
use crate::handlers::KeyEventHandler; use crate::handlers::KeyEventHandler;
use crate::models::radarr_models::RadarrRelease;
use crate::models::radarr_models::{Credit, MovieHistoryItem}; use crate::models::radarr_models::{Credit, MovieHistoryItem};
use crate::models::servarr_data::radarr::modals::MovieDetailsModal; use crate::models::servarr_data::radarr::modals::MovieDetailsModal;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS}; 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::stateful_table::SortOption;
use crate::models::{HorizontallyScrollableText, ScrollableText}; use crate::models::{HorizontallyScrollableText, ScrollableText};
@@ -405,7 +406,7 @@ mod tests {
movie_details_modal movie_details_modal
.movie_releases .movie_releases
.set_items(simple_stateful_iterable_vec!( .set_items(simple_stateful_iterable_vec!(
Release, RadarrRelease,
HorizontallyScrollableText HorizontallyScrollableText
)); ));
app.data.radarr_data.movie_details_modal = Some(movie_details_modal); app.data.radarr_data.movie_details_modal = Some(movie_details_modal);
@@ -453,7 +454,7 @@ mod tests {
movie_details_modal movie_details_modal
.movie_releases .movie_releases
.set_items(simple_stateful_iterable_vec!( .set_items(simple_stateful_iterable_vec!(
Release, RadarrRelease,
HorizontallyScrollableText HorizontallyScrollableText
)); ));
app.data.radarr_data.movie_details_modal = Some(movie_details_modal); app.data.radarr_data.movie_details_modal = Some(movie_details_modal);
@@ -996,7 +997,7 @@ mod tests {
movie_details_modal movie_details_modal
.movie_releases .movie_releases
.set_items(extended_stateful_iterable_vec!( .set_items(extended_stateful_iterable_vec!(
Release, RadarrRelease,
HorizontallyScrollableText HorizontallyScrollableText
)); ));
app.data.radarr_data.movie_details_modal = Some(movie_details_modal); app.data.radarr_data.movie_details_modal = Some(movie_details_modal);
@@ -1054,7 +1055,7 @@ mod tests {
movie_details_modal movie_details_modal
.movie_releases .movie_releases
.set_items(extended_stateful_iterable_vec!( .set_items(extended_stateful_iterable_vec!(
Release, RadarrRelease,
HorizontallyScrollableText HorizontallyScrollableText
)); ));
app.data.radarr_data.movie_details_modal = Some(movie_details_modal); 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()), movie_details: ScrollableText::with_string("test".to_owned()),
..MovieDetailsModal::default() ..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.data.radarr_data.movie_details_modal = Some(modal);
app.push_navigation_stack(ActiveRadarrBlock::ManualSearch.into()); app.push_navigation_stack(ActiveRadarrBlock::ManualSearch.into());
@@ -1487,6 +1490,8 @@ mod tests {
)] )]
active_radarr_block: ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
) { ) {
use crate::models::radarr_models::RadarrRelease;
let mut app = App::default(); let mut app = App::default();
let mut modal = MovieDetailsModal { let mut modal = MovieDetailsModal {
movie_details: ScrollableText::with_string("Test".to_owned()), movie_details: ScrollableText::with_string("Test".to_owned()),
@@ -1497,7 +1502,9 @@ mod tests {
.set_items(vec![MovieHistoryItem::default()]); .set_items(vec![MovieHistoryItem::default()]);
modal.movie_cast.set_items(vec![Credit::default()]); modal.movie_cast.set_items(vec![Credit::default()]);
modal.movie_crew.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); app.data.radarr_data.movie_details_modal = Some(modal);
MovieDetailsHandler::with( MovieDetailsHandler::with(
@@ -1687,7 +1694,9 @@ mod tests {
.set_items(vec![MovieHistoryItem::default()]); .set_items(vec![MovieHistoryItem::default()]);
modal.movie_cast.set_items(vec![Credit::default()]); modal.movie_cast.set_items(vec![Credit::default()]);
modal.movie_crew.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); app.data.radarr_data.movie_details_modal = Some(modal);
MovieDetailsHandler::with( MovieDetailsHandler::with(
@@ -1757,7 +1766,9 @@ mod tests {
.set_items(vec![MovieHistoryItem::default()]); .set_items(vec![MovieHistoryItem::default()]);
modal.movie_cast.set_items(vec![Credit::default()]); modal.movie_cast.set_items(vec![Credit::default()]);
modal.movie_crew.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); app.data.radarr_data.movie_details_modal = Some(modal);
MovieDetailsHandler::with( MovieDetailsHandler::with(
@@ -1851,7 +1862,8 @@ mod tests {
#[test] #[test]
fn test_releases_sorting_options_source() { 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(); let mut expected_releases_vec = release_vec();
expected_releases_vec.sort_by(expected_cmp_fn); expected_releases_vec.sort_by(expected_cmp_fn);
@@ -1865,7 +1877,7 @@ mod tests {
#[test] #[test]
fn test_releases_sorting_options_age() { 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(); let mut expected_releases_vec = release_vec();
expected_releases_vec.sort_by(expected_cmp_fn); expected_releases_vec.sort_by(expected_cmp_fn);
@@ -1879,7 +1891,8 @@ mod tests {
#[test] #[test]
fn test_releases_sorting_options_rejected() { 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(); let mut expected_releases_vec = release_vec();
expected_releases_vec.sort_by(expected_cmp_fn); expected_releases_vec.sort_by(expected_cmp_fn);
@@ -1893,7 +1906,7 @@ mod tests {
#[test] #[test]
fn test_releases_sorting_options_title() { 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 a.title
.text .text
.to_lowercase() .to_lowercase()
@@ -1912,7 +1925,7 @@ mod tests {
#[test] #[test]
fn test_releases_sorting_options_indexer() { 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()); |a, b| a.indexer.to_lowercase().cmp(&b.indexer.to_lowercase());
let mut expected_releases_vec = release_vec(); let mut expected_releases_vec = release_vec();
expected_releases_vec.sort_by(expected_cmp_fn); expected_releases_vec.sort_by(expected_cmp_fn);
@@ -1927,7 +1940,8 @@ mod tests {
#[test] #[test]
fn test_releases_sorting_options_size() { 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(); let mut expected_releases_vec = release_vec();
expected_releases_vec.sort_by(expected_cmp_fn); expected_releases_vec.sort_by(expected_cmp_fn);
@@ -1941,7 +1955,7 @@ mod tests {
#[test] #[test]
fn test_releases_sorting_options_peers() { 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 default_number = Number::from(i64::MAX);
let seeder_a = a let seeder_a = a
.seeders .seeders
@@ -1971,7 +1985,7 @@ mod tests {
#[test] #[test]
fn test_releases_sorting_options_language() { 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 { let default_language_vec = vec![Language {
name: "_".to_owned(), name: "_".to_owned(),
}]; }];
@@ -1993,7 +2007,8 @@ mod tests {
#[test] #[test]
fn test_releases_sorting_options_quality() { 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(); let mut expected_releases_vec = release_vec();
expected_releases_vec.sort_by(expected_cmp_fn); expected_releases_vec.sort_by(expected_cmp_fn);
@@ -2040,7 +2055,9 @@ mod tests {
.set_items(vec![MovieHistoryItem::default()]); .set_items(vec![MovieHistoryItem::default()]);
modal.movie_cast.set_items(vec![Credit::default()]); modal.movie_cast.set_items(vec![Credit::default()]);
modal.movie_crew.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); app.data.radarr_data.movie_details_modal = Some(modal);
let handler = MovieDetailsHandler::with( let handler = MovieDetailsHandler::with(
@@ -2149,7 +2166,9 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
app.is_loading = false; app.is_loading = false;
let mut modal = MovieDetailsModal::default(); 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); app.data.radarr_data.movie_details_modal = Some(modal);
let handler = MovieDetailsHandler::with( let handler = MovieDetailsHandler::with(
@@ -2162,8 +2181,8 @@ mod tests {
assert!(handler.is_ready()); assert!(handler.is_ready());
} }
fn release_vec() -> Vec<Release> { fn release_vec() -> Vec<RadarrRelease> {
let release_a = Release { let release_a = RadarrRelease {
protocol: "Protocol A".to_owned(), protocol: "Protocol A".to_owned(),
age: 1, age: 1,
title: HorizontallyScrollableText::from("Title A"), title: HorizontallyScrollableText::from("Title A"),
@@ -2179,9 +2198,9 @@ mod tests {
name: "Quality A".to_owned(), name: "Quality A".to_owned(),
}, },
}, },
..Release::default() ..RadarrRelease::default()
}; };
let release_b = Release { let release_b = RadarrRelease {
protocol: "Protocol B".to_owned(), protocol: "Protocol B".to_owned(),
age: 2, age: 2,
title: HorizontallyScrollableText::from("title B"), title: HorizontallyScrollableText::from("title B"),
@@ -2197,9 +2216,9 @@ mod tests {
name: "Quality B".to_owned(), name: "Quality B".to_owned(),
}, },
}, },
..Release::default() ..RadarrRelease::default()
}; };
let release_c = Release { let release_c = RadarrRelease {
protocol: "Protocol C".to_owned(), protocol: "Protocol C".to_owned(),
age: 3, age: 3,
title: HorizontallyScrollableText::from("Title C"), title: HorizontallyScrollableText::from("Title C"),
@@ -2213,13 +2232,13 @@ mod tests {
name: "Quality C".to_owned(), name: "Quality C".to_owned(),
}, },
}, },
..Release::default() ..RadarrRelease::default()
}; };
vec![release_a, release_b, release_c] vec![release_a, release_b, release_c]
} }
fn sort_options() -> Vec<SortOption<Release>> { fn sort_options() -> Vec<SortOption<RadarrRelease>> {
vec![SortOption { vec![SortOption {
name: "Test 1", name: "Test 1",
cmp_fn: Some(|a, b| a.age.cmp(&b.age)), cmp_fn: Some(|a, b| a.age.cmp(&b.age)),
+25 -3
View File
@@ -11,7 +11,7 @@ use crate::{models::HorizontallyScrollableText, serde_enum_from};
use super::servarr_models::{ use super::servarr_models::{
DiskSpace, HostConfig, Indexer, Language, LogResponse, QualityProfile, QualityWrapper, DiskSpace, HostConfig, Indexer, Language, LogResponse, QualityProfile, QualityWrapper,
QueueEvent, Release, RootFolder, SecurityConfig, Tag, Update, QueueEvent, RootFolder, SecurityConfig, Tag, Update,
}; };
use super::{EnumDisplayStyle, Serdeable}; use super::{EnumDisplayStyle, Serdeable};
@@ -412,6 +412,28 @@ pub struct RatingsList {
pub rotten_tomatoes: Option<Rating>, pub rotten_tomatoes: Option<Rating>,
} }
#[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<Vec<String>>,
pub seeders: Option<Number>,
pub leechers: Option<Number>,
pub languages: Option<Vec<Language>>,
pub quality: QualityWrapper,
}
#[derive(Default, Serialize, Debug, PartialEq, Eq, Clone)] #[derive(Default, Serialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct RadarrReleaseDownloadBody { pub struct RadarrReleaseDownloadBody {
@@ -485,7 +507,7 @@ pub enum RadarrSerdeable {
Movies(Vec<Movie>), Movies(Vec<Movie>),
QualityProfiles(Vec<QualityProfile>), QualityProfiles(Vec<QualityProfile>),
QueueEvents(Vec<QueueEvent>), QueueEvents(Vec<QueueEvent>),
Releases(Vec<Release>), Releases(Vec<RadarrRelease>),
RootFolders(Vec<RootFolder>), RootFolders(Vec<RootFolder>),
SecurityConfig(SecurityConfig), SecurityConfig(SecurityConfig),
SystemStatus(SystemStatus), SystemStatus(SystemStatus),
@@ -526,7 +548,7 @@ serde_enum_from!(
Movies(Vec<Movie>), Movies(Vec<Movie>),
QualityProfiles(Vec<QualityProfile>), QualityProfiles(Vec<QualityProfile>),
QueueEvents(Vec<QueueEvent>), QueueEvents(Vec<QueueEvent>),
Releases(Vec<Release>), Releases(Vec<RadarrRelease>),
RootFolders(Vec<RootFolder>), RootFolders(Vec<RootFolder>),
SecurityConfig(SecurityConfig), SecurityConfig(SecurityConfig),
SystemStatus(SystemStatus), SystemStatus(SystemStatus),
+4 -4
View File
@@ -7,8 +7,8 @@ mod tests {
radarr_models::{ radarr_models::{
AddMovieSearchResult, BlocklistItem, BlocklistResponse, Collection, Credit, DiskSpace, AddMovieSearchResult, BlocklistItem, BlocklistResponse, Collection, Credit, DiskSpace,
DownloadRecord, DownloadsResponse, Indexer, IndexerSettings, IndexerTestResult, DownloadRecord, DownloadsResponse, Indexer, IndexerSettings, IndexerTestResult,
MinimumAvailability, Monitor, Movie, MovieHistoryItem, QualityProfile, RadarrSerdeable, MinimumAvailability, Monitor, Movie, MovieHistoryItem, QualityProfile, RadarrRelease,
RadarrTask, RadarrTaskName, Release, SystemStatus, Tag, Update, RadarrSerdeable, RadarrTask, RadarrTaskName, SystemStatus, Tag, Update,
}, },
servarr_models::{HostConfig, Log, LogResponse, QueueEvent, RootFolder, SecurityConfig}, servarr_models::{HostConfig, Log, LogResponse, QueueEvent, RootFolder, SecurityConfig},
EnumDisplayStyle, Serdeable, EnumDisplayStyle, Serdeable,
@@ -317,9 +317,9 @@ mod tests {
#[test] #[test]
fn test_radarr_serdeable_from_releases() { fn test_radarr_serdeable_from_releases() {
let releases = vec![Release { let releases = vec![RadarrRelease {
size: 1, size: 1,
..Release::default() ..RadarrRelease::default()
}]; }];
let radarr_serdeable: RadarrSerdeable = releases.clone().into(); let radarr_serdeable: RadarrSerdeable = releases.clone().into();
+3 -3
View File
@@ -1,10 +1,10 @@
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use crate::models::radarr_models::{ 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_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_list::StatefulList;
use crate::models::stateful_table::StatefulTable; use crate::models::stateful_table::StatefulTable;
use crate::models::{HorizontallyScrollableText, ScrollableText}; use crate::models::{HorizontallyScrollableText, ScrollableText};
@@ -22,7 +22,7 @@ pub struct MovieDetailsModal {
pub movie_history: StatefulTable<MovieHistoryItem>, pub movie_history: StatefulTable<MovieHistoryItem>,
pub movie_cast: StatefulTable<Credit>, pub movie_cast: StatefulTable<Credit>,
pub movie_crew: StatefulTable<Credit>, pub movie_crew: StatefulTable<Credit>,
pub movie_releases: StatefulTable<Release>, pub movie_releases: StatefulTable<RadarrRelease>,
} }
#[derive(Default, Debug, PartialEq, Eq)] #[derive(Default, Debug, PartialEq, Eq)]
@@ -1,11 +1,10 @@
#[cfg(test)] #[cfg(test)]
pub mod utils { pub mod utils {
use crate::models::radarr_models::{ 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::modals::MovieDetailsModal;
use crate::models::servarr_data::radarr::radarr_data::RadarrData; use crate::models::servarr_data::radarr::radarr_data::RadarrData;
use crate::models::servarr_models::Release;
use crate::models::stateful_table::StatefulTable; use crate::models::stateful_table::StatefulTable;
use crate::models::{HorizontallyScrollableText, ScrollableText}; use crate::models::{HorizontallyScrollableText, ScrollableText};
@@ -25,7 +24,7 @@ pub mod utils {
.set_items(vec![Credit::default()]); .set_items(vec![Credit::default()]);
movie_details_modal movie_details_modal
.movie_releases .movie_releases
.set_items(vec![Release::default()]); .set_items(vec![RadarrRelease::default()]);
let mut radarr_data = RadarrData { let mut radarr_data = RadarrData {
delete_movie_files: true, delete_movie_files: true,
+3 -4
View File
@@ -1,6 +1,5 @@
use crate::models::{ use crate::models::{
servarr_models::Release, sonarr_models::{Episode, SonarrHistoryItem, SonarrRelease},
sonarr_models::{Episode, SonarrHistoryItem},
stateful_table::StatefulTable, stateful_table::StatefulTable,
ScrollableText, ScrollableText,
}; };
@@ -12,12 +11,12 @@ pub struct EpisodeDetailsModal {
pub audio_details: String, pub audio_details: String,
pub video_details: String, pub video_details: String,
pub episode_history: StatefulTable<SonarrHistoryItem>, pub episode_history: StatefulTable<SonarrHistoryItem>,
pub episode_releases: StatefulTable<Release>, pub episode_releases: StatefulTable<SonarrRelease>,
} }
#[derive(Default)] #[derive(Default)]
pub struct SeasonDetailsModal { pub struct SeasonDetailsModal {
pub episodes: StatefulTable<Episode>, pub episodes: StatefulTable<Episode>,
pub episode_details_modal: Option<EpisodeDetailsModal>, pub episode_details_modal: Option<EpisodeDetailsModal>,
pub season_releases: StatefulTable<Release>, pub season_releases: StatefulTable<SonarrRelease>,
} }
-22
View File
@@ -195,28 +195,6 @@ pub struct QueueEvent {
pub duration: Option<String>, pub duration: Option<String>,
} }
#[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<Vec<String>>,
pub seeders: Option<Number>,
pub leechers: Option<Number>,
pub languages: Option<Vec<Language>>,
pub quality: QualityWrapper,
}
#[derive(Default, Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] #[derive(Default, Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct RootFolder { pub struct RootFolder {
+25 -3
View File
@@ -13,7 +13,7 @@ use super::{
radarr_models::IndexerTestResult, radarr_models::IndexerTestResult,
servarr_models::{ servarr_models::{
DiskSpace, HostConfig, Indexer, Language, LogResponse, QualityProfile, QualityWrapper, DiskSpace, HostConfig, Indexer, Language, LogResponse, QualityProfile, QualityWrapper,
QueueEvent, Release, RootFolder, SecurityConfig, Tag, Update, QueueEvent, RootFolder, SecurityConfig, Tag, Update,
}, },
EnumDisplayStyle, HorizontallyScrollableText, Serdeable, EnumDisplayStyle, HorizontallyScrollableText, Serdeable,
}; };
@@ -401,6 +401,28 @@ pub struct SonarrCommandBody {
pub episode_ids: Option<Vec<i64>>, pub episode_ids: Option<Vec<i64>>,
} }
#[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<Vec<String>>,
pub seeders: Option<Number>,
pub leechers: Option<Number>,
pub languages: Option<Vec<Language>>,
pub quality: QualityWrapper,
pub full_season: bool,
}
#[derive(Default, Serialize, Debug, PartialEq, Eq, Clone)] #[derive(Default, Serialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct SonarrReleaseDownloadBody { pub struct SonarrReleaseDownloadBody {
@@ -467,7 +489,7 @@ pub enum SonarrSerdeable {
LogResponse(LogResponse), LogResponse(LogResponse),
QualityProfiles(Vec<QualityProfile>), QualityProfiles(Vec<QualityProfile>),
QueueEvents(Vec<QueueEvent>), QueueEvents(Vec<QueueEvent>),
Releases(Vec<Release>), Releases(Vec<SonarrRelease>),
RootFolders(Vec<RootFolder>), RootFolders(Vec<RootFolder>),
SecurityConfig(SecurityConfig), SecurityConfig(SecurityConfig),
SeriesVec(Vec<Series>), SeriesVec(Vec<Series>),
@@ -508,7 +530,7 @@ serde_enum_from!(
LogResponse(LogResponse), LogResponse(LogResponse),
QualityProfiles(Vec<QualityProfile>), QualityProfiles(Vec<QualityProfile>),
QueueEvents(Vec<QueueEvent>), QueueEvents(Vec<QueueEvent>),
Releases(Vec<Release>), Releases(Vec<SonarrRelease>),
RootFolders(Vec<RootFolder>), RootFolders(Vec<RootFolder>),
SecurityConfig(SecurityConfig), SecurityConfig(SecurityConfig),
SeriesVec(Vec<Series>), SeriesVec(Vec<Series>),
+5 -5
View File
@@ -6,13 +6,13 @@ mod tests {
use crate::models::{ use crate::models::{
radarr_models::IndexerTestResult, radarr_models::IndexerTestResult,
servarr_models::{ servarr_models::{
DiskSpace, HostConfig, Indexer, Log, LogResponse, QualityProfile, QueueEvent, Release, DiskSpace, HostConfig, Indexer, Log, LogResponse, QualityProfile, QueueEvent, RootFolder,
RootFolder, SecurityConfig, Tag, Update, SecurityConfig, Tag, Update,
}, },
sonarr_models::{ sonarr_models::{
BlocklistItem, BlocklistResponse, DownloadRecord, DownloadsResponse, Episode, BlocklistItem, BlocklistResponse, DownloadRecord, DownloadsResponse, Episode,
IndexerSettings, Series, SeriesStatus, SeriesType, SonarrHistoryEventType, SonarrHistoryItem, IndexerSettings, Series, SeriesStatus, SeriesType, SonarrHistoryEventType, SonarrHistoryItem,
SonarrSerdeable, SonarrTask, SonarrTaskName, SystemStatus, SonarrRelease, SonarrSerdeable, SonarrTask, SonarrTaskName, SystemStatus,
}, },
EnumDisplayStyle, Serdeable, EnumDisplayStyle, Serdeable,
}; };
@@ -355,9 +355,9 @@ mod tests {
#[test] #[test]
fn test_sonarr_serdeable_from_releases() { fn test_sonarr_serdeable_from_releases() {
let releases = vec![Release { let releases = vec![SonarrRelease {
size: 1, size: 1,
..Release::default() ..SonarrRelease::default()
}]; }];
let sonarr_serdeable: SonarrSerdeable = releases.clone().into(); let sonarr_serdeable: SonarrSerdeable = releases.clone().into();
+6 -6
View File
@@ -10,8 +10,8 @@ use crate::models::radarr_models::{
AddMovieBody, AddMovieSearchResult, AddOptions, BlocklistResponse, Collection, CollectionMovie, AddMovieBody, AddMovieSearchResult, AddOptions, BlocklistResponse, Collection, CollectionMovie,
Credit, CreditType, DeleteMovieParams, DownloadRecord, DownloadsResponse, EditCollectionParams, Credit, CreditType, DeleteMovieParams, DownloadRecord, DownloadsResponse, EditCollectionParams,
EditIndexerParams, EditMovieParams, IndexerSettings, IndexerTestResult, Movie, MovieCommandBody, EditIndexerParams, EditMovieParams, IndexerSettings, IndexerTestResult, Movie, MovieCommandBody,
MovieHistoryItem, RadarrReleaseDownloadBody, RadarrSerdeable, RadarrTask, RadarrTaskName, MovieHistoryItem, RadarrRelease, RadarrReleaseDownloadBody, RadarrSerdeable, RadarrTask,
SystemStatus, RadarrTaskName, SystemStatus,
}; };
use crate::models::servarr_data::modals::IndexerTestResultModalItem; use crate::models::servarr_data::modals::IndexerTestResultModalItem;
use crate::models::servarr_data::radarr::modals::{ 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_data::radarr::radarr_data::ActiveRadarrBlock;
use crate::models::servarr_models::{ use crate::models::servarr_models::{
AddRootFolderBody, CommandBody, DiskSpace, HostConfig, Indexer, LogResponse, QualityProfile, 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::stateful_table::StatefulTable;
use crate::models::{HorizontallyScrollableText, Route, Scrollable, ScrollableText}; 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 (movie_id, _) = self.extract_movie_id(None).await;
let (guid, title, indexer_id) = { let (guid, title, indexer_id) = {
let app = self.app.lock().await; let app = self.app.lock().await;
let Release { let RadarrRelease {
guid, guid,
title, title,
indexer_id, indexer_id,
@@ -1770,7 +1770,7 @@ impl<'a, 'b> Network<'a, 'b> {
.await .await
} }
async fn get_movie_releases(&mut self, movie_id: Option<i64>) -> Result<Vec<Release>> { async fn get_movie_releases(&mut self, movie_id: Option<i64>) -> Result<Vec<RadarrRelease>> {
let (id, movie_id_param) = self.extract_movie_id(movie_id).await; let (id, movie_id_param) = self.extract_movie_id(movie_id).await;
info!("Fetching releases for movie with ID: {id}"); info!("Fetching releases for movie with ID: {id}");
let event = RadarrEvent::GetReleases(None); let event = RadarrEvent::GetReleases(None);
@@ -1786,7 +1786,7 @@ impl<'a, 'b> Network<'a, 'b> {
.await; .await;
self self
.handle_request::<(), Vec<Release>>(request_props, |release_vec, mut app| { .handle_request::<(), Vec<RadarrRelease>>(request_props, |release_vec, mut app| {
if app.data.radarr_data.movie_details_modal.is_none() { if app.data.radarr_data.movie_details_modal.is_none() {
app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal::default()); app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal::default());
} }
+2 -2
View File
@@ -5221,8 +5221,8 @@ mod test {
QualityWrapper { quality: quality() } QualityWrapper { quality: quality() }
} }
fn release() -> Release { fn release() -> RadarrRelease {
Release { RadarrRelease {
guid: "1234".to_owned(), guid: "1234".to_owned(),
protocol: "torrent".to_owned(), protocol: "torrent".to_owned(),
age: 1, age: 1,
+13 -8
View File
@@ -15,12 +15,12 @@ use crate::{
}, },
servarr_models::{ servarr_models::{
AddRootFolderBody, CommandBody, DiskSpace, HostConfig, Indexer, LogResponse, QualityProfile, AddRootFolderBody, CommandBody, DiskSpace, HostConfig, Indexer, LogResponse, QualityProfile,
QueueEvent, Release, RootFolder, SecurityConfig, Tag, Update, QueueEvent, RootFolder, SecurityConfig, Tag, Update,
}, },
sonarr_models::{ sonarr_models::{
BlocklistResponse, DownloadRecord, DownloadsResponse, Episode, IndexerSettings, Series, BlocklistResponse, DownloadRecord, DownloadsResponse, Episode, IndexerSettings, Series,
SonarrCommandBody, SonarrHistoryItem, SonarrHistoryWrapper, SonarrReleaseDownloadBody, SonarrCommandBody, SonarrHistoryItem, SonarrHistoryWrapper, SonarrRelease,
SonarrSerdeable, SonarrTask, SonarrTaskName, SystemStatus, SonarrReleaseDownloadBody, SonarrSerdeable, SonarrTask, SonarrTaskName, SystemStatus,
}, },
stateful_table::StatefulTable, stateful_table::StatefulTable,
HorizontallyScrollableText, Route, Scrollable, ScrollableText, HorizontallyScrollableText, Route, Scrollable, ScrollableText,
@@ -1009,7 +1009,7 @@ impl<'a, 'b> Network<'a, 'b> {
.await .await
} }
async fn get_episode_releases(&mut self, episode_id: Option<i64>) -> Result<Vec<Release>> { async fn get_episode_releases(&mut self, episode_id: Option<i64>) -> Result<Vec<SonarrRelease>> {
let event = SonarrEvent::GetEpisodeReleases(None); let event = SonarrEvent::GetEpisodeReleases(None);
let id = self.extract_episode_id(episode_id).await; let id = self.extract_episode_id(episode_id).await;
@@ -1026,7 +1026,7 @@ impl<'a, 'b> Network<'a, 'b> {
.await; .await;
self self
.handle_request::<(), Vec<Release>>(request_props, |release_vec, mut app| { .handle_request::<(), Vec<SonarrRelease>>(request_props, |release_vec, mut app| {
if app.data.sonarr_data.season_details_modal.is_none() { if app.data.sonarr_data.season_details_modal.is_none() {
app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default()); 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( async fn get_season_releases(
&mut self, &mut self,
series_season_id_tuple: Option<(i64, i64)>, series_season_id_tuple: Option<(i64, i64)>,
) -> Result<Vec<Release>> { ) -> Result<Vec<SonarrRelease>> {
let event = SonarrEvent::GetSeasonReleases(None); let event = SonarrEvent::GetSeasonReleases(None);
let (series_id, season_number) = let (series_id, season_number) =
if let Some((series_id, season_number)) = series_season_id_tuple { if let Some((series_id, season_number)) = series_season_id_tuple {
@@ -1092,11 +1092,16 @@ impl<'a, 'b> Network<'a, 'b> {
.await; .await;
self self
.handle_request::<(), Vec<Release>>(request_props, |release_vec, mut app| { .handle_request::<(), Vec<SonarrRelease>>(request_props, |release_vec, mut app| {
if app.data.sonarr_data.season_details_modal.is_none() { if app.data.sonarr_data.season_details_modal.is_none() {
app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default()); 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 app
.data .data
.sonarr_data .sonarr_data
@@ -1104,7 +1109,7 @@ impl<'a, 'b> Network<'a, 'b> {
.as_mut() .as_mut()
.unwrap() .unwrap()
.season_releases .season_releases
.set_items(release_vec); .set_items(season_releases_vec);
}) })
.await .await
} }
+191 -72
View File
@@ -21,11 +21,11 @@ mod test {
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock; use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
use crate::models::servarr_models::{ use crate::models::servarr_models::{
DiskSpace, HostConfig, Indexer, IndexerField, Language, LogResponse, Quality, QualityProfile, 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::{ use crate::models::sonarr_models::{
BlocklistItem, DownloadRecord, DownloadsResponse, Episode, EpisodeFile, MediaInfo, BlocklistItem, DownloadRecord, DownloadsResponse, Episode, EpisodeFile, MediaInfo,
SonarrReleaseDownloadBody, SonarrTaskName, SonarrRelease, SonarrReleaseDownloadBody, SonarrTaskName,
}; };
use crate::models::sonarr_models::{ use crate::models::sonarr_models::{
BlocklistResponse, SonarrHistoryData, SonarrHistoryItem, SonarrHistoryWrapper, BlocklistResponse, SonarrHistoryData, SonarrHistoryItem, SonarrHistoryWrapper,
@@ -612,7 +612,7 @@ mod test {
Some(json!({ Some(json!({
"guid": "1234", "guid": "1234",
"indexerId": 2, "indexerId": 2,
"seriesId": 1 "seriesId": 1,
})), })),
Some(json!({})), Some(json!({})),
None, None,
@@ -2712,21 +2712,53 @@ mod test {
#[tokio::test] #[tokio::test]
async fn test_handle_get_season_releases_event() { async fn test_handle_get_season_releases_event() {
let release_json = json!([{ let release_json = json!([
"guid": "1234", {
"protocol": "torrent", "guid": "1234",
"age": 1, "protocol": "torrent",
"title": "Test Release", "age": 1,
"indexer": "kickass torrents", "title": "Test Release",
"indexerId": 2, "indexer": "kickass torrents",
"size": 1234, "indexerId": 2,
"rejected": true, "size": 1234,
"rejections": [ "Unknown quality profile", "Release is already mapped" ], "rejected": true,
"seeders": 2, "rejections": [ "Unknown quality profile", "Release is already mapped" ],
"leechers": 1, "seeders": 2,
"languages": [ { "name": "English" } ], "leechers": 1,
"quality": { "quality": { "name": "Bluray-1080p" }} "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( let (async_server, app_arc, _server) = mock_servarr_api(
RequestMethod::Get, RequestMethod::Get,
None, None,
@@ -2772,29 +2804,51 @@ mod test {
.unwrap() .unwrap()
.season_releases .season_releases
.items, .items,
vec![release()] vec![expected_filtered_sonarr_release]
); );
assert_eq!(releases_vec, vec![release()]); assert_eq!(releases_vec, expected_raw_sonarr_releases);
} }
} }
#[tokio::test] #[tokio::test]
async fn test_handle_get_season_releases_event_empty_season_details_modal() { async fn test_handle_get_season_releases_event_empty_season_details_modal() {
let release_json = json!([{ let release_json = json!([
"guid": "1234", {
"protocol": "torrent", "guid": "1234",
"age": 1, "protocol": "torrent",
"title": "Test Release", "age": 1,
"indexer": "kickass torrents", "title": "Test Release",
"indexerId": 2, "indexer": "kickass torrents",
"size": 1234, "indexerId": 2,
"rejected": true, "size": 1234,
"rejections": [ "Unknown quality profile", "Release is already mapped" ], "rejected": true,
"seeders": 2, "rejections": [ "Unknown quality profile", "Release is already mapped" ],
"leechers": 1, "seeders": 2,
"languages": [ { "name": "English" } ], "leechers": 1,
"quality": { "quality": { "name": "Bluray-1080p" }} "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( let (async_server, app_arc, _server) = mock_servarr_api(
RequestMethod::Get, RequestMethod::Get,
None, None,
@@ -2838,27 +2892,59 @@ mod test {
.unwrap() .unwrap()
.season_releases .season_releases
.items, .items,
vec![release()] vec![expected_sonarr_release]
); );
} }
#[tokio::test] #[tokio::test]
async fn test_handle_get_season_releases_event_uses_provided_series_id_and_season_number() { async fn test_handle_get_season_releases_event_uses_provided_series_id_and_season_number() {
let release_json = json!([{ let release_json = json!([
"guid": "1234", {
"protocol": "torrent", "guid": "1234",
"age": 1, "protocol": "torrent",
"title": "Test Release", "age": 1,
"indexer": "kickass torrents", "title": "Test Release",
"indexerId": 2, "indexer": "kickass torrents",
"size": 1234, "indexerId": 2,
"rejected": true, "size": 1234,
"rejections": [ "Unknown quality profile", "Release is already mapped" ], "rejected": true,
"seeders": 2, "rejections": [ "Unknown quality profile", "Release is already mapped" ],
"leechers": 1, "seeders": 2,
"languages": [ { "name": "English" } ], "leechers": 1,
"quality": { "quality": { "name": "Bluray-1080p" }} "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( let (async_server, app_arc, _server) = mock_servarr_api(
RequestMethod::Get, RequestMethod::Get,
None, None,
@@ -2904,29 +2990,61 @@ mod test {
.unwrap() .unwrap()
.season_releases .season_releases
.items, .items,
vec![release()] vec![expected_filtered_sonarr_release]
); );
assert_eq!(releases_vec, vec![release()]); assert_eq!(releases_vec, expected_raw_sonarr_releases);
} }
} }
#[tokio::test] #[tokio::test]
async fn test_handle_get_season_releases_event_filtered_series_and_filtered_seasons() { async fn test_handle_get_season_releases_event_filtered_series_and_filtered_seasons() {
let release_json = json!([{ let release_json = json!([
"guid": "1234", {
"protocol": "torrent", "guid": "1234",
"age": 1, "protocol": "torrent",
"title": "Test Release", "age": 1,
"indexer": "kickass torrents", "title": "Test Release",
"indexerId": 2, "indexer": "kickass torrents",
"size": 1234, "indexerId": 2,
"rejected": true, "size": 1234,
"rejections": [ "Unknown quality profile", "Release is already mapped" ], "rejected": true,
"seeders": 2, "rejections": [ "Unknown quality profile", "Release is already mapped" ],
"leechers": 1, "seeders": 2,
"languages": [ { "name": "English" } ], "leechers": 1,
"quality": { "quality": { "name": "Bluray-1080p" }} "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( let (async_server, app_arc, _server) = mock_servarr_api(
RequestMethod::Get, RequestMethod::Get,
None, None,
@@ -2970,9 +3088,9 @@ mod test {
.unwrap() .unwrap()
.season_releases .season_releases
.items, .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 { fn release() -> SonarrRelease {
Release { SonarrRelease {
guid: "1234".to_owned(), guid: "1234".to_owned(),
protocol: "torrent".to_owned(), protocol: "torrent".to_owned(),
age: 1, age: 1,
@@ -5112,6 +5230,7 @@ mod test {
leechers: Some(Number::from(1)), leechers: Some(Number::from(1)),
languages: Some(vec![language()]), languages: Some(vec![language()]),
quality: quality_wrapper(), quality: quality_wrapper(),
full_season: false,
} }
} }
+4 -5
View File
@@ -7,10 +7,9 @@ use ratatui::widgets::{Cell, Paragraph, Row, Wrap};
use ratatui::Frame; use ratatui::Frame;
use crate::app::App; 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::modals::MovieDetailsModal;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS}; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS};
use crate::models::servarr_models::Release;
use crate::models::Route; use crate::models::Route;
use crate::ui::radarr_ui::library::draw_library; use crate::ui::radarr_ui::library::draw_library;
use crate::ui::styles::ManagarrStyle; use crate::ui::styles::ManagarrStyle;
@@ -381,7 +380,7 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
.clone(), .clone(),
movie_details_modal.movie_releases.items.is_empty(), movie_details_modal.movie_releases.items.is_empty(),
), ),
_ => (Release::default(), true), _ => (RadarrRelease::default(), true),
}; };
let current_route = *app.get_current_route(); let current_route = *app.get_current_route();
let mut default_movie_details_modal = MovieDetailsModal::default(); 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) .unwrap_or(&mut default_movie_details_modal)
.movie_releases, .movie_releases,
); );
let releases_row_mapping = |release: &Release| { let releases_row_mapping = |release: &RadarrRelease| {
let Release { let RadarrRelease {
protocol, protocol,
age, age,
title, title,