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::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
@@ -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(
@@ -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<SortOption<Release>> {
fn releases_sorting_options() -> Vec<SortOption<RadarrRelease>> {
vec![
SortOption {
name: "Source",
@@ -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<Release> {
let release_a = Release {
fn release_vec() -> Vec<RadarrRelease> {
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<SortOption<Release>> {
fn sort_options() -> Vec<SortOption<RadarrRelease>> {
vec![SortOption {
name: "Test 1",
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::{
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<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)]
#[serde(rename_all = "camelCase")]
pub struct RadarrReleaseDownloadBody {
@@ -485,7 +507,7 @@ pub enum RadarrSerdeable {
Movies(Vec<Movie>),
QualityProfiles(Vec<QualityProfile>),
QueueEvents(Vec<QueueEvent>),
Releases(Vec<Release>),
Releases(Vec<RadarrRelease>),
RootFolders(Vec<RootFolder>),
SecurityConfig(SecurityConfig),
SystemStatus(SystemStatus),
@@ -526,7 +548,7 @@ serde_enum_from!(
Movies(Vec<Movie>),
QualityProfiles(Vec<QualityProfile>),
QueueEvents(Vec<QueueEvent>),
Releases(Vec<Release>),
Releases(Vec<RadarrRelease>),
RootFolders(Vec<RootFolder>),
SecurityConfig(SecurityConfig),
SystemStatus(SystemStatus),
+4 -4
View File
@@ -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();
+3 -3
View File
@@ -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<MovieHistoryItem>,
pub movie_cast: StatefulTable<Credit>,
pub movie_crew: StatefulTable<Credit>,
pub movie_releases: StatefulTable<Release>,
pub movie_releases: StatefulTable<RadarrRelease>,
}
#[derive(Default, Debug, PartialEq, Eq)]
@@ -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,
+3 -4
View File
@@ -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<SonarrHistoryItem>,
pub episode_releases: StatefulTable<Release>,
pub episode_releases: StatefulTable<SonarrRelease>,
}
#[derive(Default)]
pub struct SeasonDetailsModal {
pub episodes: StatefulTable<Episode>,
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>,
}
#[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)]
#[serde(rename_all = "camelCase")]
pub struct RootFolder {
+25 -3
View File
@@ -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<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)]
#[serde(rename_all = "camelCase")]
pub struct SonarrReleaseDownloadBody {
@@ -467,7 +489,7 @@ pub enum SonarrSerdeable {
LogResponse(LogResponse),
QualityProfiles(Vec<QualityProfile>),
QueueEvents(Vec<QueueEvent>),
Releases(Vec<Release>),
Releases(Vec<SonarrRelease>),
RootFolders(Vec<RootFolder>),
SecurityConfig(SecurityConfig),
SeriesVec(Vec<Series>),
@@ -508,7 +530,7 @@ serde_enum_from!(
LogResponse(LogResponse),
QualityProfiles(Vec<QualityProfile>),
QueueEvents(Vec<QueueEvent>),
Releases(Vec<Release>),
Releases(Vec<SonarrRelease>),
RootFolders(Vec<RootFolder>),
SecurityConfig(SecurityConfig),
SeriesVec(Vec<Series>),
+5 -5
View File
@@ -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();
+6 -6
View File
@@ -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<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;
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<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() {
app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal::default());
}
+2 -2
View File
@@ -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,
+13 -8
View File
@@ -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<i64>) -> Result<Vec<Release>> {
async fn get_episode_releases(&mut self, episode_id: Option<i64>) -> Result<Vec<SonarrRelease>> {
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<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() {
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<Vec<Release>> {
) -> Result<Vec<SonarrRelease>> {
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<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() {
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
}
+191 -72
View File
@@ -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,
}
}
+4 -5
View File
@@ -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,