Files
managarr/src/network/readarr_network/mod.rs
T

393 lines
12 KiB
Rust

use anyhow::Result;
use log::info;
use serde_json::{Value, json};
use super::{NetworkEvent, NetworkResource};
use crate::models::readarr_models::{
AddAuthorBody, AddReadarrRootFolderBody, DeleteParams, EditAuthorParams, ReadarrSerdeable,
ReadarrTaskName,
};
use crate::models::servarr_models::{MetadataProfile, QualityProfile, Tag};
use crate::network::{Network, RequestMethod};
mod library;
mod root_folders;
mod system;
#[cfg(test)]
#[path = "readarr_network_tests.rs"]
mod readarr_network_tests;
#[cfg(test)]
#[path = "readarr_network_test_utils.rs"]
pub mod readarr_network_test_utils;
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum ReadarrEvent {
AddAuthor(AddAuthorBody),
AddRootFolder(AddReadarrRootFolderBody),
AddTag(String),
DeleteAuthor(DeleteParams),
DeleteBook(DeleteParams),
DeleteBookFile(i64),
DeleteRootFolder(i64),
DeleteTag(i64),
EditAuthor(EditAuthorParams),
GetAuthorDetails(i64),
GetAuthorHistory(i64),
GetBookDetails(i64),
GetBookEditions(i64),
GetBookFiles(i64),
GetBookHistory(i64, i64),
GetBooks(i64),
GetDiskSpace,
GetHostConfig,
GetLogs(u64),
GetMetadataProfiles,
GetQualityProfiles,
GetQueuedEvents,
GetRootFolders,
GetSecurityConfig,
GetStatus,
GetTags,
GetTasks,
GetUpdates,
HealthCheck,
ListAuthors,
SearchNewAuthor(String),
StartTask(ReadarrTaskName),
ToggleAuthorMonitoring(i64),
ToggleBookMonitoring(i64),
TriggerAutomaticAuthorSearch(i64),
TriggerAutomaticBookSearch(i64),
UpdateAllAuthors,
UpdateAndScanAuthor(i64),
}
impl NetworkResource for ReadarrEvent {
fn resource(&self) -> &'static str {
match &self {
ReadarrEvent::AddAuthor(_)
| ReadarrEvent::DeleteAuthor(_)
| ReadarrEvent::EditAuthor(_)
| ReadarrEvent::GetAuthorDetails(_)
| ReadarrEvent::ListAuthors
| ReadarrEvent::ToggleAuthorMonitoring(_) => "/author",
ReadarrEvent::SearchNewAuthor(_) => "/author/lookup",
ReadarrEvent::DeleteBook(_)
| ReadarrEvent::GetBookDetails(_)
| ReadarrEvent::GetBooks(_)
| ReadarrEvent::ToggleBookMonitoring(_) => "/book",
ReadarrEvent::DeleteBookFile(_) | ReadarrEvent::GetBookFiles(_) => "/bookfile",
ReadarrEvent::GetQueuedEvents
| ReadarrEvent::StartTask(_)
| ReadarrEvent::TriggerAutomaticAuthorSearch(_)
| ReadarrEvent::TriggerAutomaticBookSearch(_)
| ReadarrEvent::UpdateAllAuthors
| ReadarrEvent::UpdateAndScanAuthor(_) => "/command",
ReadarrEvent::GetHostConfig | ReadarrEvent::GetSecurityConfig => "/config/host",
ReadarrEvent::GetDiskSpace => "/diskspace",
ReadarrEvent::GetBookEditions(_) => "/edition",
ReadarrEvent::HealthCheck => "/health",
ReadarrEvent::GetAuthorHistory(_) | ReadarrEvent::GetBookHistory(_, _) => "/history/author",
ReadarrEvent::GetLogs(_) => "/log",
ReadarrEvent::GetMetadataProfiles => "/metadataprofile",
ReadarrEvent::GetQualityProfiles => "/qualityprofile",
ReadarrEvent::AddRootFolder(_)
| ReadarrEvent::DeleteRootFolder(_)
| ReadarrEvent::GetRootFolders => "/rootfolder",
ReadarrEvent::GetStatus => "/system/status",
ReadarrEvent::GetTasks => "/system/task",
ReadarrEvent::AddTag(_) | ReadarrEvent::DeleteTag(_) | ReadarrEvent::GetTags => "/tag",
ReadarrEvent::GetUpdates => "/update",
}
}
}
impl From<ReadarrEvent> for NetworkEvent {
fn from(readarr_event: ReadarrEvent) -> Self {
NetworkEvent::Readarr(readarr_event)
}
}
impl Network<'_, '_> {
pub async fn handle_readarr_event(
&mut self,
readarr_event: ReadarrEvent,
) -> Result<ReadarrSerdeable> {
match readarr_event {
ReadarrEvent::AddAuthor(body) => self.add_author(body).await.map(ReadarrSerdeable::from),
ReadarrEvent::AddRootFolder(add_root_folder_body) => self
.add_readarr_root_folder(add_root_folder_body)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::AddTag(tag) => self.add_readarr_tag(tag).await.map(ReadarrSerdeable::from),
ReadarrEvent::DeleteAuthor(delete_author_params) => self
.delete_author(delete_author_params)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::DeleteBook(delete_book_params) => self
.delete_book(delete_book_params)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::DeleteBookFile(book_file_id) => self
.delete_readarr_book_file(book_file_id)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::DeleteRootFolder(root_folder_id) => self
.delete_readarr_root_folder(root_folder_id)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::DeleteTag(tag_id) => self
.delete_readarr_tag(tag_id)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::EditAuthor(edit_author_params) => self
.edit_author(edit_author_params)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetAuthorDetails(author_id) => self
.get_author_details(author_id)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetAuthorHistory(author_id) => self
.get_readarr_author_history(author_id)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetBookDetails(book_id) => self
.get_book_details(book_id)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetBookEditions(book_id) => self
.get_book_editions(book_id)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetBookFiles(book_id) => self
.get_book_files(book_id)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetBookHistory(author_id, book_id) => self
.get_readarr_book_history(author_id, book_id)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetBooks(author_id) => {
self.get_books(author_id).await.map(ReadarrSerdeable::from)
}
ReadarrEvent::GetDiskSpace => self
.get_readarr_diskspace()
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetHostConfig => self
.get_readarr_host_config()
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetLogs(events) => self
.get_readarr_logs(events)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetMetadataProfiles => self
.get_readarr_metadata_profiles()
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetQualityProfiles => self
.get_readarr_quality_profiles()
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetQueuedEvents => self
.get_queued_readarr_events()
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetRootFolders => self
.get_readarr_root_folders()
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetSecurityConfig => self
.get_readarr_security_config()
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::GetStatus => self.get_readarr_status().await.map(ReadarrSerdeable::from),
ReadarrEvent::GetTags => self.get_readarr_tags().await.map(ReadarrSerdeable::from),
ReadarrEvent::GetTasks => self.get_readarr_tasks().await.map(ReadarrSerdeable::from),
ReadarrEvent::GetUpdates => self.get_readarr_updates().await.map(ReadarrSerdeable::from),
ReadarrEvent::HealthCheck => self
.get_readarr_healthcheck()
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::ListAuthors => self.list_authors().await.map(ReadarrSerdeable::from),
ReadarrEvent::SearchNewAuthor(query) => {
self.search_author(query).await.map(ReadarrSerdeable::from)
}
ReadarrEvent::StartTask(task_name) => self
.start_readarr_task(task_name)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::ToggleAuthorMonitoring(author_id) => self
.toggle_author_monitoring(author_id)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::ToggleBookMonitoring(book_id) => self
.toggle_book_monitoring(book_id)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::TriggerAutomaticAuthorSearch(author_id) => self
.trigger_automatic_author_search(author_id)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::TriggerAutomaticBookSearch(book_id) => self
.trigger_automatic_book_search(book_id)
.await
.map(ReadarrSerdeable::from),
ReadarrEvent::UpdateAllAuthors => self.update_all_authors().await.map(ReadarrSerdeable::from),
ReadarrEvent::UpdateAndScanAuthor(author_id) => self
.update_and_scan_author(author_id)
.await
.map(ReadarrSerdeable::from),
}
}
pub(in crate::network::readarr_network) async fn get_readarr_healthcheck(
&mut self,
) -> Result<()> {
info!("Performing Readarr health check");
let event = ReadarrEvent::HealthCheck;
let request_props = self
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
.await;
self
.handle_request::<(), ()>(request_props, |_, _| ())
.await
}
async fn get_readarr_metadata_profiles(&mut self) -> Result<Vec<MetadataProfile>> {
info!("Fetching Readarr metadata profiles");
let event = ReadarrEvent::GetMetadataProfiles;
let request_props = self
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
.await;
self
.handle_request::<(), Vec<MetadataProfile>>(request_props, |metadata_profiles, mut app| {
app.data.readarr_data.metadata_profile_map = metadata_profiles
.into_iter()
.map(|profile| (profile.id, profile.name))
.collect();
})
.await
}
async fn get_readarr_quality_profiles(&mut self) -> Result<Vec<QualityProfile>> {
info!("Fetching Readarr quality profiles");
let event = ReadarrEvent::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.readarr_data.quality_profile_map = quality_profiles
.into_iter()
.map(|profile| (profile.id, profile.name))
.collect();
})
.await
}
async fn get_readarr_tags(&mut self) -> Result<Vec<Tag>> {
info!("Fetching Readarr tags");
let event = ReadarrEvent::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.readarr_data.tags_map = tags_vec
.into_iter()
.map(|tag| (tag.id, tag.label))
.collect();
})
.await
}
async fn add_readarr_tag(&mut self, tag: String) -> Result<Tag> {
info!("Adding a new Readarr tag");
let event = ReadarrEvent::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.readarr_data.tags_map.insert(tag.id, tag.label);
})
.await
}
async fn delete_readarr_tag(&mut self, id: i64) -> Result<()> {
info!("Deleting Readarr tag with ID: {id}");
let event = ReadarrEvent::DeleteTag(id);
let request_props = self
.request_props_from(
event,
RequestMethod::Delete,
None::<()>,
Some(format!("/{id}")),
None,
)
.await;
self
.handle_request::<(), ()>(request_props, |_, _| ())
.await
}
pub(in crate::network::readarr_network) async fn extract_and_add_readarr_tag_ids_vec(
&mut self,
edit_tags: &str,
) -> Vec<i64> {
let missing_tags_vec = {
let tags_map = &self.app.lock().await.data.readarr_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_readarr_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
.readarr_data
.tags_map
.get_by_right(tag.to_lowercase().trim())
.unwrap()
})
.collect()
}
}