feat(network): Support for downloading releases from Sonarr

This commit is contained in:
2024-11-22 19:33:29 -07:00
parent cea4632a22
commit 896c50909a
8 changed files with 96 additions and 19 deletions
+2 -2
View File
@@ -12,7 +12,7 @@ use tokio::sync::Mutex;
use crate::app::App; use crate::app::App;
use crate::cli::CliCommandHandler; use crate::cli::CliCommandHandler;
use crate::models::radarr_models::{RadarrTaskName, ReleaseDownloadBody}; use crate::models::radarr_models::{RadarrReleaseDownloadBody, RadarrTaskName};
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
use crate::network::NetworkTrait; use crate::network::NetworkTrait;
use anyhow::Result; use anyhow::Result;
@@ -202,7 +202,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrCommand> for RadarrCliHandler<'a, '
indexer_id, indexer_id,
movie_id, movie_id,
} => { } => {
let params = ReleaseDownloadBody { let params = RadarrReleaseDownloadBody {
guid, guid,
indexer_id, indexer_id,
movie_id, movie_id,
+3 -3
View File
@@ -261,8 +261,8 @@ mod tests {
}, },
models::{ models::{
radarr_models::{ radarr_models::{
BlocklistItem, BlocklistResponse, IndexerSettings, RadarrSerdeable, RadarrTaskName, BlocklistItem, BlocklistResponse, IndexerSettings, RadarrReleaseDownloadBody,
ReleaseDownloadBody, RadarrSerdeable, RadarrTaskName,
}, },
Serdeable, Serdeable,
}, },
@@ -304,7 +304,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn test_download_release_command() { async fn test_download_release_command() {
let expected_release_download_body = ReleaseDownloadBody { let expected_release_download_body = RadarrReleaseDownloadBody {
guid: "guid".to_owned(), guid: "guid".to_owned(),
indexer_id: 1, indexer_id: 1,
movie_id: 1, movie_id: 1,
+1 -1
View File
@@ -414,7 +414,7 @@ pub struct RatingsList {
#[derive(Default, Serialize, Debug, PartialEq, Eq, Clone)] #[derive(Default, Serialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct ReleaseDownloadBody { pub struct RadarrReleaseDownloadBody {
pub guid: String, pub guid: String,
pub indexer_id: i64, pub indexer_id: i64,
pub movie_id: i64, pub movie_id: i64,
+13
View File
@@ -401,6 +401,19 @@ pub struct SonarrCommandBody {
pub episode_ids: Option<Vec<i64>>, pub episode_ids: Option<Vec<i64>>,
} }
#[derive(Default, Serialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct SonarrReleaseDownloadBody {
pub guid: String,
pub indexer_id: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub series_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub episode_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub season_number: Option<i64>,
}
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct SonarrTask { pub struct SonarrTask {
+11 -7
View File
@@ -10,7 +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, RadarrSerdeable, RadarrTask, RadarrTaskName, ReleaseDownloadBody, SystemStatus, MovieHistoryItem, RadarrReleaseDownloadBody, RadarrSerdeable, RadarrTask, 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::{
@@ -44,7 +45,7 @@ pub enum RadarrEvent {
DeleteMovie(Option<DeleteMovieParams>), DeleteMovie(Option<DeleteMovieParams>),
DeleteRootFolder(Option<i64>), DeleteRootFolder(Option<i64>),
DeleteTag(i64), DeleteTag(i64),
DownloadRelease(Option<ReleaseDownloadBody>), DownloadRelease(Option<RadarrReleaseDownloadBody>),
EditAllIndexerSettings(Option<IndexerSettings>), EditAllIndexerSettings(Option<IndexerSettings>),
EditCollection(Option<EditCollectionParams>), EditCollection(Option<EditCollectionParams>),
EditIndexer(Option<EditIndexerParams>), EditIndexer(Option<EditIndexerParams>),
@@ -176,7 +177,7 @@ impl<'a, 'b> Network<'a, 'b> {
.await .await
.map(RadarrSerdeable::from), .map(RadarrSerdeable::from),
RadarrEvent::DownloadRelease(params) => self RadarrEvent::DownloadRelease(params) => self
.download_release(params) .download_radarr_release(params)
.await .await
.map(RadarrSerdeable::from), .map(RadarrSerdeable::from),
RadarrEvent::EditAllIndexerSettings(params) => self RadarrEvent::EditAllIndexerSettings(params) => self
@@ -662,10 +663,13 @@ impl<'a, 'b> Network<'a, 'b> {
.await .await
} }
async fn download_release(&mut self, params: Option<ReleaseDownloadBody>) -> Result<Value> { async fn download_radarr_release(
&mut self,
params: Option<RadarrReleaseDownloadBody>,
) -> Result<Value> {
let event = RadarrEvent::DownloadRelease(None); let event = RadarrEvent::DownloadRelease(None);
let body = if let Some(release_download_body) = params { let body = if let Some(release_download_body) = params {
info!("Downloading release with params: {release_download_body:?}"); info!("Downloading Radarr release with params: {release_download_body:?}");
release_download_body release_download_body
} else { } else {
let (movie_id, _) = self.extract_movie_id(None).await; let (movie_id, _) = self.extract_movie_id(None).await;
@@ -690,7 +694,7 @@ impl<'a, 'b> Network<'a, 'b> {
info!("Downloading release: {title}"); info!("Downloading release: {title}");
ReleaseDownloadBody { RadarrReleaseDownloadBody {
guid, guid,
indexer_id, indexer_id,
movie_id, movie_id,
@@ -702,7 +706,7 @@ impl<'a, 'b> Network<'a, 'b> {
.await; .await;
self self
.handle_request::<ReleaseDownloadBody, Value>(request_props, |_, _| ()) .handle_request::<RadarrReleaseDownloadBody, Value>(request_props, |_, _| ())
.await .await
} }
+3 -3
View File
@@ -4805,7 +4805,7 @@ mod test {
} }
#[tokio::test] #[tokio::test]
async fn test_handle_download_release_event() { async fn test_handle_download_radarr_release_event() {
let (async_server, app_arc, _server) = mock_servarr_api( let (async_server, app_arc, _server) = mock_servarr_api(
RequestMethod::Post, RequestMethod::Post,
Some(json!({ Some(json!({
@@ -4843,7 +4843,7 @@ mod test {
} }
#[tokio::test] #[tokio::test]
async fn test_handle_download_release_event_uses_provided_params() { async fn test_handle_download_radarr_release_event_uses_provided_params() {
let (async_server, app_arc, _server) = mock_servarr_api( let (async_server, app_arc, _server) = mock_servarr_api(
RequestMethod::Post, RequestMethod::Post,
Some(json!({ Some(json!({
@@ -4859,7 +4859,7 @@ mod test {
) )
.await; .await;
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
let params = ReleaseDownloadBody { let params = RadarrReleaseDownloadBody {
guid: "1234".to_owned(), guid: "1234".to_owned(),
indexer_id: 2, indexer_id: 2,
movie_id: 1, movie_id: 1,
+30 -2
View File
@@ -19,8 +19,8 @@ use crate::{
}, },
sonarr_models::{ sonarr_models::{
BlocklistResponse, DownloadRecord, DownloadsResponse, Episode, IndexerSettings, Series, BlocklistResponse, DownloadRecord, DownloadsResponse, Episode, IndexerSettings, Series,
SonarrCommandBody, SonarrHistoryItem, SonarrHistoryWrapper, SonarrSerdeable, SonarrTask, SonarrCommandBody, SonarrHistoryItem, SonarrHistoryWrapper, SonarrReleaseDownloadBody,
SonarrTaskName, SystemStatus, SonarrSerdeable, SonarrTask, SonarrTaskName, SystemStatus,
}, },
stateful_table::StatefulTable, stateful_table::StatefulTable,
HorizontallyScrollableText, Route, Scrollable, ScrollableText, HorizontallyScrollableText, Route, Scrollable, ScrollableText,
@@ -44,6 +44,7 @@ pub enum SonarrEvent {
DeleteIndexer(Option<i64>), DeleteIndexer(Option<i64>),
DeleteRootFolder(Option<i64>), DeleteRootFolder(Option<i64>),
DeleteTag(i64), DeleteTag(i64),
DownloadRelease(SonarrReleaseDownloadBody),
GetAllIndexerSettings, GetAllIndexerSettings,
GetBlocklist, GetBlocklist,
GetDownloads, GetDownloads,
@@ -86,6 +87,7 @@ impl NetworkResource for SonarrEvent {
match &self { match &self {
SonarrEvent::AddTag(_) | SonarrEvent::DeleteTag(_) | SonarrEvent::GetTags => "/tag", SonarrEvent::AddTag(_) | SonarrEvent::DeleteTag(_) | SonarrEvent::GetTags => "/tag",
SonarrEvent::ClearBlocklist => "/blocklist/bulk", SonarrEvent::ClearBlocklist => "/blocklist/bulk",
SonarrEvent::DownloadRelease(_) => "/release",
SonarrEvent::DeleteBlocklistItem(_) => "/blocklist", SonarrEvent::DeleteBlocklistItem(_) => "/blocklist",
SonarrEvent::GetAllIndexerSettings => "/config/indexer", SonarrEvent::GetAllIndexerSettings => "/config/indexer",
SonarrEvent::GetBlocklist => "/blocklist?page=1&pageSize=10000", SonarrEvent::GetBlocklist => "/blocklist?page=1&pageSize=10000",
@@ -167,6 +169,10 @@ impl<'a, 'b> Network<'a, 'b> {
.delete_sonarr_tag(tag_id) .delete_sonarr_tag(tag_id)
.await .await
.map(SonarrSerdeable::from), .map(SonarrSerdeable::from),
SonarrEvent::DownloadRelease(sonarr_release_download_body) => self
.download_sonarr_release(sonarr_release_download_body)
.await
.map(SonarrSerdeable::from),
SonarrEvent::GetBlocklist => self.get_sonarr_blocklist().await.map(SonarrSerdeable::from), SonarrEvent::GetBlocklist => self.get_sonarr_blocklist().await.map(SonarrSerdeable::from),
SonarrEvent::GetDownloads => self.get_sonarr_downloads().await.map(SonarrSerdeable::from), SonarrEvent::GetDownloads => self.get_sonarr_downloads().await.map(SonarrSerdeable::from),
SonarrEvent::GetEpisodes(series_id) => self SonarrEvent::GetEpisodes(series_id) => self
@@ -511,6 +517,28 @@ impl<'a, 'b> Network<'a, 'b> {
.await .await
} }
async fn download_sonarr_release(
&mut self,
sonarr_release_download_body: SonarrReleaseDownloadBody,
) -> Result<Value> {
let event = SonarrEvent::DownloadRelease(sonarr_release_download_body.clone());
info!("Downloading Sonarr release with params: {sonarr_release_download_body:?}");
let request_props = self
.request_props_from(
event,
RequestMethod::Post,
Some(sonarr_release_download_body),
None,
None,
)
.await;
self
.handle_request::<SonarrReleaseDownloadBody, Value>(request_props, |_, _| ())
.await
}
async fn get_all_sonarr_indexer_settings(&mut self) -> Result<IndexerSettings> { async fn get_all_sonarr_indexer_settings(&mut self) -> Result<IndexerSettings> {
info!("Fetching Sonarr indexer settings"); info!("Fetching Sonarr indexer settings");
let event = SonarrEvent::GetAllIndexerSettings; let event = SonarrEvent::GetAllIndexerSettings;
+33 -1
View File
@@ -25,7 +25,7 @@ mod test {
}; };
use crate::models::sonarr_models::{ use crate::models::sonarr_models::{
BlocklistItem, DownloadRecord, DownloadsResponse, Episode, EpisodeFile, MediaInfo, BlocklistItem, DownloadRecord, DownloadsResponse, Episode, EpisodeFile, MediaInfo,
SonarrTaskName, SonarrReleaseDownloadBody, SonarrTaskName,
}; };
use crate::models::sonarr_models::{ use crate::models::sonarr_models::{
BlocklistResponse, SonarrHistoryData, SonarrHistoryItem, SonarrHistoryWrapper, BlocklistResponse, SonarrHistoryData, SonarrHistoryItem, SonarrHistoryWrapper,
@@ -599,6 +599,38 @@ mod test {
async_server.assert_async().await; async_server.assert_async().await;
} }
#[tokio::test]
async fn test_handle_download_sonarr_release_event_uses_provided_params() {
let params = SonarrReleaseDownloadBody {
guid: "1234".to_owned(),
indexer_id: 2,
series_id: Some(1),
..SonarrReleaseDownloadBody::default()
};
let (async_server, app_arc, _server) = mock_servarr_api(
RequestMethod::Post,
Some(json!({
"guid": "1234",
"indexerId": 2,
"seriesId": 1
})),
Some(json!({})),
None,
SonarrEvent::DownloadRelease(params.clone()),
None,
None,
)
.await;
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
assert!(network
.handle_sonarr_event(SonarrEvent::DownloadRelease(params))
.await
.is_ok());
async_server.assert_async().await;
}
#[rstest] #[rstest]
#[tokio::test] #[tokio::test]
async fn test_handle_get_sonarr_blocklist_event(#[values(true, false)] use_custom_sorting: bool) { async fn test_handle_get_sonarr_blocklist_event(#[values(true, false)] use_custom_sorting: bool) {