feat: Full Lidarr system support for both the CLI and TUI

This commit is contained in:
Dark-Alex-17 committed 2026-01-14 14:50:33 -07:00
1 parent c74d5936d2
commit 8b9467bd39
63 files changed
+4824 -74

No files matched your search

+211 -11
View File
@@ -1,10 +1,14 @@
use anyhow::Result;
use log::info;
use crate::models::lidarr_models::SystemStatus;
use crate::models::servarr_models::{DiskSpace, HostConfig, SecurityConfig};
use crate::models::lidarr_models::{LidarrTask, LidarrTaskName, SystemStatus};
use crate::models::servarr_models::{
CommandBody, DiskSpace, HostConfig, LogResponse, QueueEvent, SecurityConfig, Update,
};
use crate::models::{HorizontallyScrollableText, Scrollable, ScrollableText};
use crate::network::lidarr_network::LidarrEvent;
use crate::network::{Network, RequestMethod};
use anyhow::Result;
use indoc::formatdoc;
use log::info;
use serde_json::Value;
#[cfg(test)]
#[path = "lidarr_system_network_tests.rs"]
@@ -26,18 +30,62 @@ impl Network<'_, '_> {
.await
}
pub(in crate::network::lidarr_network) async fn get_lidarr_security_config(
pub(in crate::network::lidarr_network) async fn get_lidarr_logs(
&mut self,
) -> Result<SecurityConfig> {
info!("Fetching Lidarr security config");
let event = LidarrEvent::GetSecurityConfig;
events: u64,
) -> Result<LogResponse> {
info!("Fetching Lidarr logs");
let event = LidarrEvent::GetLogs(events);
let params = format!("pageSize={events}&sortDirection=descending&sortKey=time");
let request_props = self
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
.request_props_from(event, RequestMethod::Get, None::<()>, None, Some(params))
.await;
self
.handle_request::<(), SecurityConfig>(request_props, |_, _| ())
.handle_request::<(), LogResponse>(request_props, |log_response, mut app| {
let mut logs = log_response.records;
logs.reverse();
let log_lines = logs
.into_iter()
.map(|log| {
if log.exception.is_some() {
HorizontallyScrollableText::from(format!(
"{}|{}|{}|{}|{}",
log.time,
log.level.to_uppercase(),
log
.logger
.as_ref()
.expect("logger must exist when exception is present"),
log
.exception_type
.as_ref()
.expect("exception_type must exist when exception is present"),
log
.exception
.as_ref()
.expect("exception must exist in this branch")
))
} else {
HorizontallyScrollableText::from(format!(
"{}|{}|{}|{}",
log.time,
log.level.to_uppercase(),
log.logger.as_ref().expect("logger must exist in log entry"),
log
.message
.as_ref()
.expect("message must exist when exception is not present")
))
}
})
.collect();
app.data.lidarr_data.logs.set_items(log_lines);
app.data.lidarr_data.logs.scroll_to_bottom();
})
.await
}
@@ -58,6 +106,42 @@ impl Network<'_, '_> {
.await
}
pub(in crate::network::lidarr_network) async fn get_queued_lidarr_events(
&mut self,
) -> Result<Vec<QueueEvent>> {
info!("Fetching Lidarr queued events");
let event = LidarrEvent::GetQueuedEvents;
let request_props = self
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
.await;
self
.handle_request::<(), Vec<QueueEvent>>(request_props, |queued_events_vec, mut app| {
app
.data
.lidarr_data
.queued_events
.set_items(queued_events_vec);
})
.await
}
pub(in crate::network::lidarr_network) async fn get_lidarr_security_config(
&mut self,
) -> Result<SecurityConfig> {
info!("Fetching Lidarr security config");
let event = LidarrEvent::GetSecurityConfig;
let request_props = self
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
.await;
self
.handle_request::<(), SecurityConfig>(request_props, |_, _| ())
.await
}
pub(in crate::network::lidarr_network) async fn get_lidarr_status(
&mut self,
) -> Result<SystemStatus> {
@@ -75,4 +159,120 @@ impl Network<'_, '_> {
})
.await
}
pub(in crate::network::lidarr_network) async fn get_lidarr_tasks(
&mut self,
) -> Result<Vec<LidarrTask>> {
info!("Fetching Lidarr tasks");
let event = LidarrEvent::GetTasks;
let request_props = self
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
.await;
self
.handle_request::<(), Vec<LidarrTask>>(request_props, |tasks_vec, mut app| {
app.data.lidarr_data.tasks.set_items(tasks_vec);
})
.await
}
pub(in crate::network::lidarr_network) async fn get_lidarr_updates(
&mut self,
) -> Result<Vec<Update>> {
info!("Fetching Lidarr updates");
let event = LidarrEvent::GetUpdates;
let request_props = self
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
.await;
self
.handle_request::<(), Vec<Update>>(request_props, |updates_vec, mut app| {
let latest_installed = if updates_vec
.iter()
.any(|update| update.latest && update.installed_on.is_some())
{
"already".to_owned()
} else {
"not".to_owned()
};
let updates = updates_vec
.into_iter()
.map(|update| {
let install_status = if update.installed_on.is_some() {
if update.installed {
" (Currently Installed)".to_owned()
} else {
" (Previously Installed)".to_owned()
}
} else {
String::new()
};
let vec_to_bullet_points = |vec: Vec<String>| {
vec
.iter()
.map(|change| format!(" * {change}"))
.collect::<Vec<String>>()
.join("\n")
};
let mut update_info = formatdoc!(
"{} - {}{install_status}
{}",
update.version,
update.release_date,
"-".repeat(200)
);
if let Some(new_changes) = update.changes.new {
let changes = vec_to_bullet_points(new_changes);
update_info = formatdoc!(
"{update_info}
New:
{changes}"
)
}
if let Some(fixes) = update.changes.fixed {
let fixes = vec_to_bullet_points(fixes);
update_info = formatdoc!(
"{update_info}
Fixed:
{fixes}"
);
}
update_info
})
.reduce(|version_1, version_2| format!("{version_1}\n\n\n{version_2}"))
.unwrap();
app.data.lidarr_data.updates = ScrollableText::with_string(formatdoc!(
"The latest version of Lidarr is {latest_installed} installed
{updates}"
));
})
.await
}
pub(in crate::network::lidarr_network) async fn start_lidarr_task(
&mut self,
task: LidarrTaskName,
) -> Result<Value> {
let event = LidarrEvent::StartTask(task);
let task_name = task.to_string();
info!("Starting Lidarr task: {task_name}");
let body = CommandBody { name: task_name };
let request_props = self
.request_props_from(event, RequestMethod::Post, Some(body), None, None)
.await;
self
.handle_request::<CommandBody, Value>(request_props, |_, _| ())
.await
}
}