refactor: Network module is now broken out into similar directory structures for each servarr to mimic the rest of the project to make it easier to develop and maintain
This commit is contained in:
@@ -0,0 +1,503 @@
|
||||
use anyhow::Result;
|
||||
use log::info;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use super::{Network, NetworkEvent, NetworkResource};
|
||||
use crate::{
|
||||
models::{
|
||||
servarr_models::{AddRootFolderBody, EditIndexerParams, Language, QualityProfile, Tag},
|
||||
sonarr_models::{
|
||||
AddSeriesBody, DeleteSeriesParams, EditSeriesParams, IndexerSettings,
|
||||
SonarrReleaseDownloadBody, SonarrSerdeable, SonarrTaskName,
|
||||
},
|
||||
},
|
||||
network::RequestMethod,
|
||||
};
|
||||
#[cfg(test)]
|
||||
#[path = "sonarr_network_tests.rs"]
|
||||
mod sonarr_network_tests;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "sonarr_network_test_utils.rs"]
|
||||
mod sonarr_network_test_utils;
|
||||
|
||||
mod blocklist;
|
||||
mod downloads;
|
||||
mod history;
|
||||
mod indexers;
|
||||
mod library;
|
||||
mod root_folders;
|
||||
mod system;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub enum SonarrEvent {
|
||||
AddRootFolder(AddRootFolderBody),
|
||||
AddSeries(AddSeriesBody),
|
||||
AddTag(String),
|
||||
ClearBlocklist,
|
||||
DeleteBlocklistItem(i64),
|
||||
DeleteDownload(i64),
|
||||
DeleteEpisodeFile(i64),
|
||||
DeleteIndexer(i64),
|
||||
DeleteRootFolder(i64),
|
||||
DeleteSeries(DeleteSeriesParams),
|
||||
DeleteTag(i64),
|
||||
DownloadRelease(SonarrReleaseDownloadBody),
|
||||
EditAllIndexerSettings(IndexerSettings),
|
||||
EditIndexer(EditIndexerParams),
|
||||
EditSeries(EditSeriesParams),
|
||||
GetAllIndexerSettings,
|
||||
GetBlocklist,
|
||||
GetDownloads(u64),
|
||||
GetHistory(u64),
|
||||
GetHostConfig,
|
||||
GetIndexers,
|
||||
GetEpisodeDetails(i64),
|
||||
GetEpisodes(i64),
|
||||
GetEpisodeFiles(i64),
|
||||
GetEpisodeHistory(i64),
|
||||
GetLanguageProfiles,
|
||||
GetLogs(u64),
|
||||
GetDiskSpace,
|
||||
GetQualityProfiles,
|
||||
GetQueuedEvents,
|
||||
GetRootFolders,
|
||||
GetEpisodeReleases(i64),
|
||||
GetSeasonHistory((i64, i64)),
|
||||
GetSeasonReleases((i64, i64)),
|
||||
GetSecurityConfig,
|
||||
GetSeriesDetails(i64),
|
||||
GetSeriesHistory(i64),
|
||||
GetStatus,
|
||||
GetUpdates,
|
||||
GetTags,
|
||||
GetTasks,
|
||||
HealthCheck,
|
||||
ListSeries,
|
||||
MarkHistoryItemAsFailed(i64),
|
||||
SearchNewSeries(String),
|
||||
StartTask(SonarrTaskName),
|
||||
TestIndexer(i64),
|
||||
TestAllIndexers,
|
||||
ToggleSeasonMonitoring((i64, i64)),
|
||||
ToggleSeriesMonitoring(i64),
|
||||
ToggleEpisodeMonitoring(i64),
|
||||
TriggerAutomaticEpisodeSearch(i64),
|
||||
TriggerAutomaticSeasonSearch((i64, i64)),
|
||||
TriggerAutomaticSeriesSearch(i64),
|
||||
UpdateAllSeries,
|
||||
UpdateAndScanSeries(i64),
|
||||
UpdateDownloads,
|
||||
}
|
||||
|
||||
impl NetworkResource for SonarrEvent {
|
||||
fn resource(&self) -> &'static str {
|
||||
match &self {
|
||||
SonarrEvent::AddTag(_) | SonarrEvent::DeleteTag(_) | SonarrEvent::GetTags => "/tag",
|
||||
SonarrEvent::ClearBlocklist => "/blocklist/bulk",
|
||||
SonarrEvent::DownloadRelease(_) => "/release",
|
||||
SonarrEvent::DeleteBlocklistItem(_) => "/blocklist",
|
||||
SonarrEvent::GetAllIndexerSettings | SonarrEvent::EditAllIndexerSettings(_) => {
|
||||
"/config/indexer"
|
||||
}
|
||||
SonarrEvent::GetEpisodeFiles(_) | SonarrEvent::DeleteEpisodeFile(_) => "/episodefile",
|
||||
SonarrEvent::GetBlocklist => "/blocklist?page=1&pageSize=10000",
|
||||
SonarrEvent::GetDownloads(_) | SonarrEvent::DeleteDownload(_) => "/queue",
|
||||
SonarrEvent::GetEpisodes(_) | SonarrEvent::GetEpisodeDetails(_) => "/episode",
|
||||
SonarrEvent::GetHistory(_) | SonarrEvent::GetEpisodeHistory(_) => "/history",
|
||||
SonarrEvent::GetHostConfig | SonarrEvent::GetSecurityConfig => "/config/host",
|
||||
SonarrEvent::GetIndexers | SonarrEvent::DeleteIndexer(_) | SonarrEvent::EditIndexer(_) => {
|
||||
"/indexer"
|
||||
}
|
||||
SonarrEvent::GetLanguageProfiles => "/language",
|
||||
SonarrEvent::GetLogs(_) => "/log",
|
||||
SonarrEvent::GetDiskSpace => "/diskspace",
|
||||
SonarrEvent::GetQualityProfiles => "/qualityprofile",
|
||||
SonarrEvent::GetQueuedEvents
|
||||
| SonarrEvent::StartTask(_)
|
||||
| SonarrEvent::TriggerAutomaticSeriesSearch(_)
|
||||
| SonarrEvent::TriggerAutomaticSeasonSearch(_)
|
||||
| SonarrEvent::TriggerAutomaticEpisodeSearch(_)
|
||||
| SonarrEvent::UpdateAllSeries
|
||||
| SonarrEvent::UpdateAndScanSeries(_)
|
||||
| SonarrEvent::UpdateDownloads => "/command",
|
||||
SonarrEvent::GetRootFolders
|
||||
| SonarrEvent::DeleteRootFolder(_)
|
||||
| SonarrEvent::AddRootFolder(_) => "/rootfolder",
|
||||
SonarrEvent::GetSeasonReleases(_) | SonarrEvent::GetEpisodeReleases(_) => "/release",
|
||||
SonarrEvent::GetSeriesHistory(_) | SonarrEvent::GetSeasonHistory(_) => "/history/series",
|
||||
SonarrEvent::GetStatus => "/system/status",
|
||||
SonarrEvent::GetTasks => "/system/task",
|
||||
SonarrEvent::GetUpdates => "/update",
|
||||
SonarrEvent::HealthCheck => "/health",
|
||||
SonarrEvent::AddSeries(_)
|
||||
| SonarrEvent::ListSeries
|
||||
| SonarrEvent::GetSeriesDetails(_)
|
||||
| SonarrEvent::DeleteSeries(_)
|
||||
| SonarrEvent::EditSeries(_)
|
||||
| SonarrEvent::ToggleSeasonMonitoring(_)
|
||||
| SonarrEvent::ToggleSeriesMonitoring(_) => "/series",
|
||||
SonarrEvent::SearchNewSeries(_) => "/series/lookup",
|
||||
SonarrEvent::MarkHistoryItemAsFailed(_) => "/history/failed",
|
||||
SonarrEvent::TestIndexer(_) => "/indexer/test",
|
||||
SonarrEvent::TestAllIndexers => "/indexer/testall",
|
||||
SonarrEvent::ToggleEpisodeMonitoring(_) => "/episode/monitor",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SonarrEvent> for NetworkEvent {
|
||||
fn from(sonarr_event: SonarrEvent) -> Self {
|
||||
NetworkEvent::Sonarr(sonarr_event)
|
||||
}
|
||||
}
|
||||
|
||||
impl Network<'_, '_> {
|
||||
pub async fn handle_sonarr_event(
|
||||
&mut self,
|
||||
sonarr_event: SonarrEvent,
|
||||
) -> Result<SonarrSerdeable> {
|
||||
match sonarr_event {
|
||||
SonarrEvent::AddRootFolder(path) => self
|
||||
.add_sonarr_root_folder(path)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::AddSeries(body) => self
|
||||
.add_sonarr_series(body)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::AddTag(tag) => self.add_sonarr_tag(tag).await.map(SonarrSerdeable::from),
|
||||
SonarrEvent::ClearBlocklist => self
|
||||
.clear_sonarr_blocklist()
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetAllIndexerSettings => self
|
||||
.get_all_sonarr_indexer_settings()
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::DeleteBlocklistItem(blocklist_item_id) => self
|
||||
.delete_sonarr_blocklist_item(blocklist_item_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::DeleteDownload(download_id) => self
|
||||
.delete_sonarr_download(download_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::DeleteEpisodeFile(episode_file_id) => self
|
||||
.delete_sonarr_episode_file(episode_file_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::DeleteIndexer(indexer_id) => self
|
||||
.delete_sonarr_indexer(indexer_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::DeleteRootFolder(root_folder_id) => self
|
||||
.delete_sonarr_root_folder(root_folder_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::DeleteSeries(params) => {
|
||||
self.delete_series(params).await.map(SonarrSerdeable::from)
|
||||
}
|
||||
SonarrEvent::DeleteTag(tag_id) => self
|
||||
.delete_sonarr_tag(tag_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::DownloadRelease(sonarr_release_download_body) => self
|
||||
.download_sonarr_release(sonarr_release_download_body)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::EditAllIndexerSettings(params) => self
|
||||
.edit_all_sonarr_indexer_settings(params)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::EditIndexer(params) => self
|
||||
.edit_sonarr_indexer(params)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::EditSeries(params) => self
|
||||
.edit_sonarr_series(params)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetBlocklist => self.get_sonarr_blocklist().await.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetDownloads(count) => self
|
||||
.get_sonarr_downloads(count)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetEpisodes(series_id) => self
|
||||
.get_episodes(series_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetEpisodeFiles(series_id) => self
|
||||
.get_episode_files(series_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetEpisodeDetails(episode_id) => self
|
||||
.get_episode_details(episode_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetEpisodeHistory(episode_id) => self
|
||||
.get_sonarr_episode_history(episode_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetHistory(events) => self
|
||||
.get_sonarr_history(events)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetHostConfig => self
|
||||
.get_sonarr_host_config()
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetIndexers => self.get_sonarr_indexers().await.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetLanguageProfiles => self
|
||||
.get_sonarr_language_profiles()
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetLogs(events) => self
|
||||
.get_sonarr_logs(events)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetDiskSpace => self.get_sonarr_diskspace().await.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetQualityProfiles => self
|
||||
.get_sonarr_quality_profiles()
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetQueuedEvents => self
|
||||
.get_queued_sonarr_events()
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetRootFolders => self
|
||||
.get_sonarr_root_folders()
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetEpisodeReleases(params) => self
|
||||
.get_episode_releases(params)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetSeasonHistory(params) => self
|
||||
.get_sonarr_season_history(params)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetSeasonReleases(params) => self
|
||||
.get_season_releases(params)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetSecurityConfig => self
|
||||
.get_sonarr_security_config()
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetSeriesDetails(series_id) => self
|
||||
.get_series_details(series_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetSeriesHistory(series_id) => self
|
||||
.get_sonarr_series_history(series_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetStatus => self.get_sonarr_status().await.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetTags => self.get_sonarr_tags().await.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetTasks => self.get_sonarr_tasks().await.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetUpdates => self.get_sonarr_updates().await.map(SonarrSerdeable::from),
|
||||
SonarrEvent::HealthCheck => self
|
||||
.get_sonarr_healthcheck()
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::ListSeries => self.list_series().await.map(SonarrSerdeable::from),
|
||||
SonarrEvent::MarkHistoryItemAsFailed(history_item_id) => self
|
||||
.mark_sonarr_history_item_as_failed(history_item_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::SearchNewSeries(query) => self
|
||||
.search_sonarr_series(query)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::StartTask(task_name) => self
|
||||
.start_sonarr_task(task_name)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::TestIndexer(indexer_id) => self
|
||||
.test_sonarr_indexer(indexer_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::TestAllIndexers => self
|
||||
.test_all_sonarr_indexers()
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::ToggleEpisodeMonitoring(episode_id) => self
|
||||
.toggle_sonarr_episode_monitoring(episode_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::ToggleSeasonMonitoring(params) => self
|
||||
.toggle_sonarr_season_monitoring(params)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::ToggleSeriesMonitoring(series_id) => self
|
||||
.toggle_sonarr_series_monitoring(series_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::TriggerAutomaticSeasonSearch(params) => self
|
||||
.trigger_automatic_season_search(params)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::TriggerAutomaticSeriesSearch(series_id) => self
|
||||
.trigger_automatic_series_search(series_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::TriggerAutomaticEpisodeSearch(episode_id) => self
|
||||
.trigger_automatic_episode_search(episode_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::UpdateAllSeries => self.update_all_series().await.map(SonarrSerdeable::from),
|
||||
SonarrEvent::UpdateAndScanSeries(series_id) => self
|
||||
.update_and_scan_series(series_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::UpdateDownloads => self
|
||||
.update_sonarr_downloads()
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_sonarr_tag(&mut self, tag: String) -> Result<Tag> {
|
||||
info!("Adding a new Sonarr tag");
|
||||
let event = SonarrEvent::AddTag(String::new());
|
||||
|
||||
let request_props = self
|
||||
.request_props_from(
|
||||
event,
|
||||
RequestMethod::Post,
|
||||
Some(json!({ "label": tag })),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<Value, Tag>(request_props, |tag, mut app| {
|
||||
app.data.sonarr_data.tags_map.insert(tag.id, tag.label);
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn delete_sonarr_tag(&mut self, id: i64) -> Result<()> {
|
||||
info!("Deleting Sonarr tag with id: {id}");
|
||||
let event = SonarrEvent::DeleteTag(id);
|
||||
|
||||
let request_props = self
|
||||
.request_props_from(
|
||||
event,
|
||||
RequestMethod::Delete,
|
||||
None::<()>,
|
||||
Some(format!("/{id}")),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), ()>(request_props, |_, _| ())
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_sonarr_healthcheck(&mut self) -> Result<()> {
|
||||
info!("Performing Sonarr health check");
|
||||
let event = SonarrEvent::HealthCheck;
|
||||
|
||||
let request_props = self
|
||||
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), ()>(request_props, |_, _| ())
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_sonarr_language_profiles(&mut self) -> Result<Vec<Language>> {
|
||||
info!("Fetching Sonarr language profiles");
|
||||
let event = SonarrEvent::GetLanguageProfiles;
|
||||
|
||||
let request_props = self
|
||||
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), Vec<Language>>(request_props, |language_profiles_vec, mut app| {
|
||||
app.data.sonarr_data.language_profiles_map = language_profiles_vec
|
||||
.into_iter()
|
||||
.map(|language| (language.id, language.name))
|
||||
.collect();
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_sonarr_quality_profiles(&mut self) -> Result<Vec<QualityProfile>> {
|
||||
info!("Fetching Sonarr quality profiles");
|
||||
let event = SonarrEvent::GetQualityProfiles;
|
||||
|
||||
let request_props = self
|
||||
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), Vec<QualityProfile>>(request_props, |quality_profiles, mut app| {
|
||||
app.data.sonarr_data.quality_profile_map = quality_profiles
|
||||
.into_iter()
|
||||
.map(|profile| (profile.id, profile.name))
|
||||
.collect();
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_sonarr_tags(&mut self) -> Result<Vec<Tag>> {
|
||||
info!("Fetching Sonarr tags");
|
||||
let event = SonarrEvent::GetTags;
|
||||
|
||||
let request_props = self
|
||||
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), Vec<Tag>>(request_props, |tags_vec, mut app| {
|
||||
app.data.sonarr_data.tags_map = tags_vec
|
||||
.into_iter()
|
||||
.map(|tag| (tag.id, tag.label))
|
||||
.collect();
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub(in crate::network::sonarr_network) async fn extract_and_add_sonarr_tag_ids_vec(
|
||||
&mut self,
|
||||
edit_tags: &str,
|
||||
) -> Vec<i64> {
|
||||
let missing_tags_vec = {
|
||||
let tags_map = &self.app.lock().await.data.sonarr_data.tags_map;
|
||||
edit_tags
|
||||
.split(',')
|
||||
.filter(|&tag| {
|
||||
!tag.is_empty() && tags_map.get_by_right(tag.to_lowercase().trim()).is_none()
|
||||
})
|
||||
.collect::<Vec<&str>>()
|
||||
};
|
||||
|
||||
for tag in missing_tags_vec {
|
||||
self
|
||||
.add_sonarr_tag(tag.trim().to_owned())
|
||||
.await
|
||||
.expect("Unable to add tag");
|
||||
}
|
||||
|
||||
let app = self.app.lock().await;
|
||||
edit_tags
|
||||
.split(',')
|
||||
.filter(|tag| !tag.is_empty())
|
||||
.map(|tag| {
|
||||
*app
|
||||
.data
|
||||
.sonarr_data
|
||||
.tags_map
|
||||
.get_by_right(tag.to_lowercase().trim())
|
||||
.unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user