feat(network): Support for deleting a series from Sonarr

This commit is contained in:
2024-11-23 12:42:11 -07:00
parent 374819b4f3
commit cac54c5447
5 changed files with 164 additions and 7 deletions
@@ -20,7 +20,9 @@ use super::modals::SeasonDetailsModal;
mod sonarr_data_tests; mod sonarr_data_tests;
pub struct SonarrData { pub struct SonarrData {
pub add_list_exclusion: bool,
pub blocklist: StatefulTable<BlocklistItem>, pub blocklist: StatefulTable<BlocklistItem>,
pub delete_series_files: bool,
pub downloads: StatefulTable<DownloadRecord>, pub downloads: StatefulTable<DownloadRecord>,
pub disk_space_vec: Vec<DiskSpace>, pub disk_space_vec: Vec<DiskSpace>,
pub edit_root_folder: Option<HorizontallyScrollableText>, pub edit_root_folder: Option<HorizontallyScrollableText>,
@@ -44,11 +46,20 @@ pub struct SonarrData {
pub version: String, pub version: String,
} }
impl SonarrData {
pub fn reset_delete_series_preferences(&mut self) {
self.delete_series_files = false;
self.add_list_exclusion = false;
}
}
impl Default for SonarrData { impl Default for SonarrData {
fn default() -> SonarrData { fn default() -> SonarrData {
SonarrData { SonarrData {
add_list_exclusion: false,
blocklist: StatefulTable::default(), blocklist: StatefulTable::default(),
downloads: StatefulTable::default(), downloads: StatefulTable::default(),
delete_series_files: false,
disk_space_vec: Vec::new(), disk_space_vec: Vec::new(),
edit_root_folder: None, edit_root_folder: None,
history: StatefulTable::default(), history: StatefulTable::default(),
@@ -97,6 +108,8 @@ pub enum ActiveSonarrBlock {
DeleteRootFolderPrompt, DeleteRootFolderPrompt,
DeleteSeriesConfirmPrompt, DeleteSeriesConfirmPrompt,
DeleteSeriesPrompt, DeleteSeriesPrompt,
DeleteSeriesToggleAddListExclusion,
DeleteSeriesToggleDeleteFile,
Downloads, Downloads,
EditEpisodePrompt, EditEpisodePrompt,
EditIndexerPrompt, EditIndexerPrompt,
@@ -30,11 +30,27 @@ mod tests {
); );
} }
#[test]
fn test_reset_delete_series_preferences() {
let mut sonarr_data = SonarrData {
add_list_exclusion: true,
delete_series_files: true,
..SonarrData::default()
};
sonarr_data.reset_delete_series_preferences();
assert!(!sonarr_data.delete_series_files);
assert!(!sonarr_data.add_list_exclusion);
}
#[test] #[test]
fn test_sonarr_data_defaults() { fn test_sonarr_data_defaults() {
let sonarr_data = SonarrData::default(); let sonarr_data = SonarrData::default();
assert!(!sonarr_data.add_list_exclusion);
assert!(sonarr_data.blocklist.is_empty()); assert!(sonarr_data.blocklist.is_empty());
assert!(!sonarr_data.delete_series_files);
assert!(sonarr_data.downloads.is_empty()); assert!(sonarr_data.downloads.is_empty());
assert!(sonarr_data.disk_space_vec.is_empty()); assert!(sonarr_data.disk_space_vec.is_empty());
assert!(sonarr_data.edit_root_folder.is_none()); assert!(sonarr_data.edit_root_folder.is_none());
+8
View File
@@ -44,6 +44,14 @@ pub struct BlocklistResponse {
pub records: Vec<BlocklistItem>, pub records: Vec<BlocklistItem>,
} }
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
#[serde(rename_all = "lowercase")]
pub struct DeleteSeriesParams {
pub id: i64,
pub delete_series_files: bool,
pub add_list_exclusion: bool,
}
#[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq)] #[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct DownloadRecord { pub struct DownloadRecord {
+60 -4
View File
@@ -18,9 +18,10 @@ use crate::{
QueueEvent, RootFolder, SecurityConfig, Tag, Update, QueueEvent, RootFolder, SecurityConfig, Tag, Update,
}, },
sonarr_models::{ sonarr_models::{
BlocklistResponse, DownloadRecord, DownloadsResponse, Episode, IndexerSettings, Series, BlocklistResponse, DeleteSeriesParams, DownloadRecord, DownloadsResponse, Episode,
SonarrCommandBody, SonarrHistoryItem, SonarrHistoryWrapper, SonarrRelease, IndexerSettings, Series, SonarrCommandBody, SonarrHistoryItem, SonarrHistoryWrapper,
SonarrReleaseDownloadBody, SonarrSerdeable, SonarrTask, SonarrTaskName, SystemStatus, SonarrRelease, SonarrReleaseDownloadBody, SonarrSerdeable, SonarrTask, SonarrTaskName,
SystemStatus,
}, },
stateful_table::StatefulTable, stateful_table::StatefulTable,
HorizontallyScrollableText, Route, Scrollable, ScrollableText, HorizontallyScrollableText, Route, Scrollable, ScrollableText,
@@ -43,6 +44,7 @@ pub enum SonarrEvent {
DeleteDownload(Option<i64>), DeleteDownload(Option<i64>),
DeleteIndexer(Option<i64>), DeleteIndexer(Option<i64>),
DeleteRootFolder(Option<i64>), DeleteRootFolder(Option<i64>),
DeleteSeries(Option<DeleteSeriesParams>),
DeleteTag(i64), DeleteTag(i64),
DownloadRelease(SonarrReleaseDownloadBody), DownloadRelease(SonarrReleaseDownloadBody),
GetAllIndexerSettings, GetAllIndexerSettings,
@@ -116,7 +118,9 @@ impl NetworkResource for SonarrEvent {
SonarrEvent::GetTasks => "/system/task", SonarrEvent::GetTasks => "/system/task",
SonarrEvent::GetUpdates => "/update", SonarrEvent::GetUpdates => "/update",
SonarrEvent::HealthCheck => "/health", SonarrEvent::HealthCheck => "/health",
SonarrEvent::ListSeries | SonarrEvent::GetSeriesDetails(_) => "/series", SonarrEvent::ListSeries | SonarrEvent::GetSeriesDetails(_) | SonarrEvent::DeleteSeries(_) => {
"/series"
}
SonarrEvent::MarkHistoryItemAsFailed(_) => "/history/failed", SonarrEvent::MarkHistoryItemAsFailed(_) => "/history/failed",
SonarrEvent::TestIndexer(_) => "/indexer/test", SonarrEvent::TestIndexer(_) => "/indexer/test",
SonarrEvent::TestAllIndexers => "/indexer/testall", SonarrEvent::TestAllIndexers => "/indexer/testall",
@@ -165,6 +169,9 @@ impl<'a, 'b> Network<'a, 'b> {
.delete_sonarr_root_folder(root_folder_id) .delete_sonarr_root_folder(root_folder_id)
.await .await
.map(SonarrSerdeable::from), .map(SonarrSerdeable::from),
SonarrEvent::DeleteSeries(params) => {
self.delete_series(params).await.map(SonarrSerdeable::from)
}
SonarrEvent::DeleteTag(tag_id) => self SonarrEvent::DeleteTag(tag_id) => self
.delete_sonarr_tag(tag_id) .delete_sonarr_tag(tag_id)
.await .await
@@ -498,6 +505,55 @@ impl<'a, 'b> Network<'a, 'b> {
.await .await
} }
async fn delete_series(
&mut self,
delete_series_params: Option<DeleteSeriesParams>,
) -> Result<()> {
let event = SonarrEvent::DeleteSeries(None);
let (series_id, delete_files, add_import_exclusion) = if let Some(params) = delete_series_params
{
(
params.id,
params.delete_series_files,
params.add_list_exclusion,
)
} else {
let (series_id, _) = self.extract_series_id(None).await;
let delete_files = self.app.lock().await.data.sonarr_data.delete_series_files;
let add_import_exclusion = self.app.lock().await.data.sonarr_data.add_list_exclusion;
(series_id, delete_files, add_import_exclusion)
};
info!("Deleting Sonarr series with ID: {series_id} with deleteFiles={delete_files} and addImportExclusion={add_import_exclusion}");
let request_props = self
.request_props_from(
event,
RequestMethod::Delete,
None::<()>,
Some(format!("/{series_id}")),
Some(format!(
"deleteFiles={delete_files}&addImportExclusion={add_import_exclusion}"
)),
)
.await;
let resp = self
.handle_request::<(), ()>(request_props, |_, _| ())
.await;
self
.app
.lock()
.await
.data
.sonarr_data
.reset_delete_series_preferences();
resp
}
async fn delete_sonarr_tag(&mut self, id: i64) -> Result<()> { async fn delete_sonarr_tag(&mut self, id: i64) -> Result<()> {
info!("Deleting Sonarr tag with id: {id}"); info!("Deleting Sonarr tag with id: {id}");
let event = SonarrEvent::DeleteTag(id); let event = SonarrEvent::DeleteTag(id);
+67 -3
View File
@@ -24,8 +24,8 @@ mod test {
QualityWrapper, QueueEvent, 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, DeleteSeriesParams, DownloadRecord, DownloadsResponse, Episode, EpisodeFile,
SonarrRelease, SonarrReleaseDownloadBody, SonarrTaskName, MediaInfo, SonarrRelease, SonarrReleaseDownloadBody, SonarrTaskName,
}; };
use crate::models::sonarr_models::{ use crate::models::sonarr_models::{
BlocklistResponse, SonarrHistoryData, SonarrHistoryItem, SonarrHistoryWrapper, BlocklistResponse, SonarrHistoryData, SonarrHistoryItem, SonarrHistoryWrapper,
@@ -137,7 +137,12 @@ mod test {
#[rstest] #[rstest]
fn test_resource_series( fn test_resource_series(
#[values(SonarrEvent::ListSeries, SonarrEvent::GetSeriesDetails(None))] event: SonarrEvent, #[values(
SonarrEvent::ListSeries,
SonarrEvent::GetSeriesDetails(None),
SonarrEvent::DeleteSeries(None)
)]
event: SonarrEvent,
) { ) {
assert_str_eq!(event.resource(), "/series"); assert_str_eq!(event.resource(), "/series");
} }
@@ -577,6 +582,65 @@ mod test {
async_server.assert_async().await; async_server.assert_async().await;
} }
#[tokio::test]
async fn test_handle_delete_series_event() {
let (async_server, app_arc, _server) = mock_servarr_api(
RequestMethod::Delete,
None,
None,
None,
SonarrEvent::DeleteSeries(None),
Some("/1"),
Some("deleteFiles=true&addImportExclusion=true"),
)
.await;
{
let mut app = app_arc.lock().await;
app.data.sonarr_data.series.set_items(vec![series()]);
app.data.sonarr_data.delete_series_files = true;
app.data.sonarr_data.add_list_exclusion = true;
}
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
assert!(network
.handle_sonarr_event(SonarrEvent::DeleteSeries(None))
.await
.is_ok());
async_server.assert_async().await;
assert!(!app_arc.lock().await.data.sonarr_data.delete_series_files);
assert!(!app_arc.lock().await.data.sonarr_data.add_list_exclusion);
}
#[tokio::test]
async fn test_handle_delete_series_event_use_provided_params() {
let (async_server, app_arc, _server) = mock_servarr_api(
RequestMethod::Delete,
None,
None,
None,
SonarrEvent::DeleteSeries(None),
Some("/1"),
Some("deleteFiles=true&addImportExclusion=true"),
)
.await;
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
let delete_series_params = DeleteSeriesParams {
id: 1,
delete_series_files: true,
add_list_exclusion: true,
};
assert!(network
.handle_sonarr_event(SonarrEvent::DeleteSeries(Some(delete_series_params)))
.await
.is_ok());
async_server.assert_async().await;
assert!(!app_arc.lock().await.data.sonarr_data.delete_series_files);
assert!(!app_arc.lock().await.data.sonarr_data.add_list_exclusion);
}
#[tokio::test] #[tokio::test]
async fn test_handle_delete_sonarr_tag_event() { async fn test_handle_delete_sonarr_tag_event() {
let (async_server, app_arc, _server) = mock_servarr_api( let (async_server, app_arc, _server) = mock_servarr_api(