feat: Full Lidarr system support for both the CLI and TUI
This commit is contained in:
@@ -1,19 +1,20 @@
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
pub mod test_utils {
|
||||
use crate::models::HorizontallyScrollableText;
|
||||
use crate::models::lidarr_models::{
|
||||
AddArtistSearchResult, Album, AlbumStatistics, Artist, ArtistStatistics, ArtistStatus,
|
||||
DownloadRecord, DownloadStatus, DownloadsResponse, EditArtistParams, LidarrHistoryData,
|
||||
LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, Member, MetadataProfile,
|
||||
NewItemMonitorType, Ratings, SystemStatus,
|
||||
LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, LidarrTask, LidarrTaskName,
|
||||
Member, MetadataProfile, NewItemMonitorType, Ratings, SystemStatus,
|
||||
};
|
||||
use crate::models::servarr_models::IndexerSettings;
|
||||
use crate::models::servarr_models::{
|
||||
Indexer, IndexerField, Quality, QualityProfile, QualityWrapper, RootFolder, Tag,
|
||||
};
|
||||
use crate::models::{HorizontallyScrollableText, ScrollableText};
|
||||
use bimap::BiMap;
|
||||
use chrono::DateTime;
|
||||
use indoc::formatdoc;
|
||||
use serde_json::{Number, json};
|
||||
|
||||
pub const ADD_ARTIST_SEARCH_RESULT_JSON: &str = r#"{
|
||||
@@ -333,4 +334,47 @@ pub mod test_utils {
|
||||
rss_sync_interval: 60,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn task() -> LidarrTask {
|
||||
LidarrTask {
|
||||
name: "Backup".to_owned(),
|
||||
task_name: LidarrTaskName::Backup,
|
||||
interval: 60,
|
||||
last_execution: DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T21:29:16Z").unwrap()),
|
||||
next_execution: DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T22:29:16Z").unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_line() -> &'static str {
|
||||
"2025-12-16 16:40:59 UTC|INFO|ImportListSyncService|No list items to process"
|
||||
}
|
||||
|
||||
pub fn updates() -> ScrollableText {
|
||||
let line_break = "-".repeat(200);
|
||||
ScrollableText::with_string(formatdoc!(
|
||||
"
|
||||
The latest version of Lidarr is already installed
|
||||
|
||||
4.3.2.1 - 2023-04-15 02:02:53 UTC (Currently Installed)
|
||||
{line_break}
|
||||
New:
|
||||
* Cool new thing
|
||||
Fixed:
|
||||
* Some bugs killed
|
||||
|
||||
|
||||
3.2.1.0 - 2023-04-15 02:02:53 UTC (Previously Installed)
|
||||
{line_break}
|
||||
New:
|
||||
* Cool new thing (old)
|
||||
* Other cool new thing (old)
|
||||
|
||||
|
||||
2.1.0 - 2023-04-15 02:02:53 UTC
|
||||
{line_break}
|
||||
Fixed:
|
||||
* Killed bug 1
|
||||
* Fixed bug 2"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,9 @@ mod tests {
|
||||
LidarrEvent::UpdateAllArtists,
|
||||
LidarrEvent::TriggerAutomaticArtistSearch(0),
|
||||
LidarrEvent::UpdateAndScanArtist(0),
|
||||
LidarrEvent::UpdateDownloads
|
||||
LidarrEvent::UpdateDownloads,
|
||||
LidarrEvent::GetQueuedEvents,
|
||||
LidarrEvent::StartTask(Default::default())
|
||||
)]
|
||||
event: LidarrEvent,
|
||||
) {
|
||||
@@ -128,6 +130,9 @@ mod tests {
|
||||
#[case(LidarrEvent::GetQualityProfiles, "/qualityprofile")]
|
||||
#[case(LidarrEvent::GetStatus, "/system/status")]
|
||||
#[case(LidarrEvent::GetTags, "/tag")]
|
||||
#[case(LidarrEvent::GetLogs(500), "/log")]
|
||||
#[case(LidarrEvent::GetTasks, "/system/task")]
|
||||
#[case(LidarrEvent::GetUpdates, "/update")]
|
||||
#[case(LidarrEvent::HealthCheck, "/health")]
|
||||
#[case(LidarrEvent::MarkHistoryItemAsFailed(0), "/history/failed")]
|
||||
#[case(LidarrEvent::TestIndexer(0), "/indexer/test")]
|
||||
|
||||
@@ -4,7 +4,7 @@ use log::info;
|
||||
use super::{NetworkEvent, NetworkResource};
|
||||
use crate::models::lidarr_models::{
|
||||
AddArtistBody, AddLidarrRootFolderBody, DeleteParams, EditArtistParams, LidarrSerdeable,
|
||||
MetadataProfile,
|
||||
LidarrTaskName, MetadataProfile,
|
||||
};
|
||||
use crate::models::servarr_models::{EditIndexerParams, IndexerSettings, QualityProfile, Tag};
|
||||
use crate::network::{Network, RequestMethod};
|
||||
@@ -47,16 +47,21 @@ pub enum LidarrEvent {
|
||||
GetHistory(u64),
|
||||
GetHostConfig,
|
||||
GetIndexers,
|
||||
GetLogs(u64),
|
||||
MarkHistoryItemAsFailed(i64),
|
||||
GetMetadataProfiles,
|
||||
GetQualityProfiles,
|
||||
GetQueuedEvents,
|
||||
GetRootFolders,
|
||||
GetSecurityConfig,
|
||||
GetStatus,
|
||||
GetUpdates,
|
||||
GetTags,
|
||||
GetTasks,
|
||||
HealthCheck,
|
||||
ListArtists,
|
||||
SearchNewArtist(String),
|
||||
StartTask(LidarrTaskName),
|
||||
TestIndexer(i64),
|
||||
TestAllIndexers,
|
||||
ToggleAlbumMonitoring(i64),
|
||||
@@ -84,6 +89,7 @@ impl NetworkResource for LidarrEvent {
|
||||
| LidarrEvent::ToggleAlbumMonitoring(_)
|
||||
| LidarrEvent::GetAlbumDetails(_)
|
||||
| LidarrEvent::DeleteAlbum(_) => "/album",
|
||||
LidarrEvent::GetLogs(_) => "/log",
|
||||
LidarrEvent::GetDiskSpace => "/diskspace",
|
||||
LidarrEvent::GetDownloads(_) | LidarrEvent::DeleteDownload(_) => "/queue",
|
||||
LidarrEvent::GetHistory(_) => "/history",
|
||||
@@ -95,7 +101,9 @@ impl NetworkResource for LidarrEvent {
|
||||
LidarrEvent::TriggerAutomaticArtistSearch(_)
|
||||
| LidarrEvent::UpdateAllArtists
|
||||
| LidarrEvent::UpdateAndScanArtist(_)
|
||||
| LidarrEvent::UpdateDownloads => "/command",
|
||||
| LidarrEvent::UpdateDownloads
|
||||
| LidarrEvent::GetQueuedEvents
|
||||
| LidarrEvent::StartTask(_) => "/command",
|
||||
LidarrEvent::GetMetadataProfiles => "/metadataprofile",
|
||||
LidarrEvent::GetQualityProfiles => "/qualityprofile",
|
||||
LidarrEvent::GetRootFolders
|
||||
@@ -104,6 +112,8 @@ impl NetworkResource for LidarrEvent {
|
||||
LidarrEvent::TestIndexer(_) => "/indexer/test",
|
||||
LidarrEvent::TestAllIndexers => "/indexer/testall",
|
||||
LidarrEvent::GetStatus => "/system/status",
|
||||
LidarrEvent::GetTasks => "/system/task",
|
||||
LidarrEvent::GetUpdates => "/update",
|
||||
LidarrEvent::HealthCheck => "/health",
|
||||
LidarrEvent::SearchNewArtist(_) => "/artist/lookup",
|
||||
}
|
||||
@@ -182,6 +192,10 @@ impl Network<'_, '_> {
|
||||
.get_lidarr_history(events)
|
||||
.await
|
||||
.map(LidarrSerdeable::from),
|
||||
LidarrEvent::GetLogs(events) => self
|
||||
.get_lidarr_logs(events)
|
||||
.await
|
||||
.map(LidarrSerdeable::from),
|
||||
LidarrEvent::MarkHistoryItemAsFailed(history_item_id) => self
|
||||
.mark_lidarr_history_item_as_failed(history_item_id)
|
||||
.await
|
||||
@@ -198,6 +212,10 @@ impl Network<'_, '_> {
|
||||
.get_lidarr_quality_profiles()
|
||||
.await
|
||||
.map(LidarrSerdeable::from),
|
||||
LidarrEvent::GetQueuedEvents => self
|
||||
.get_queued_lidarr_events()
|
||||
.await
|
||||
.map(LidarrSerdeable::from),
|
||||
LidarrEvent::GetRootFolders => self
|
||||
.get_lidarr_root_folders()
|
||||
.await
|
||||
@@ -208,6 +226,8 @@ impl Network<'_, '_> {
|
||||
.map(LidarrSerdeable::from),
|
||||
LidarrEvent::GetStatus => self.get_lidarr_status().await.map(LidarrSerdeable::from),
|
||||
LidarrEvent::GetTags => self.get_lidarr_tags().await.map(LidarrSerdeable::from),
|
||||
LidarrEvent::GetTasks => self.get_lidarr_tasks().await.map(LidarrSerdeable::from),
|
||||
LidarrEvent::GetUpdates => self.get_lidarr_updates().await.map(LidarrSerdeable::from),
|
||||
LidarrEvent::HealthCheck => self
|
||||
.get_lidarr_healthcheck()
|
||||
.await
|
||||
@@ -216,6 +236,10 @@ impl Network<'_, '_> {
|
||||
LidarrEvent::SearchNewArtist(query) => {
|
||||
self.search_artist(query).await.map(LidarrSerdeable::from)
|
||||
}
|
||||
LidarrEvent::StartTask(task_name) => self
|
||||
.start_lidarr_task(task_name)
|
||||
.await
|
||||
.map(LidarrSerdeable::from),
|
||||
LidarrEvent::ToggleAlbumMonitoring(album_id) => self
|
||||
.toggle_album_monitoring(album_id)
|
||||
.await
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::models::lidarr_models::{LidarrSerdeable, SystemStatus};
|
||||
use crate::models::servarr_models::{DiskSpace, HostConfig, SecurityConfig};
|
||||
use crate::models::HorizontallyScrollableText;
|
||||
use crate::models::lidarr_models::{LidarrSerdeable, LidarrTask, LidarrTaskName, SystemStatus};
|
||||
use crate::models::servarr_models::{
|
||||
DiskSpace, HostConfig, LogResponse, QueueEvent, SecurityConfig, Update,
|
||||
};
|
||||
use crate::network::lidarr_network::LidarrEvent;
|
||||
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::updates;
|
||||
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
|
||||
use chrono::DateTime;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
|
||||
@@ -104,6 +109,117 @@ mod tests {
|
||||
assert_eq!(security_config, response);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_lidarr_logs_event() {
|
||||
let expected_logs = vec![
|
||||
HorizontallyScrollableText::from(
|
||||
"2023-05-20 21:29:16 UTC|FATAL|LidarrError|Some.Big.Bad.Exception|test exception",
|
||||
),
|
||||
HorizontallyScrollableText::from("2023-05-20 21:29:16 UTC|INFO|TestLogger|test message"),
|
||||
];
|
||||
let logs_response_json = json!({
|
||||
"page": 1,
|
||||
"pageSize": 500,
|
||||
"sortKey": "time",
|
||||
"sortDirection": "descending",
|
||||
"totalRecords": 2,
|
||||
"records": [
|
||||
{
|
||||
"time": "2023-05-20T21:29:16Z",
|
||||
"level": "info",
|
||||
"logger": "TestLogger",
|
||||
"message": "test message",
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"time": "2023-05-20T21:29:16Z",
|
||||
"level": "fatal",
|
||||
"logger": "LidarrError",
|
||||
"exception": "test exception",
|
||||
"exceptionType": "Some.Big.Bad.Exception",
|
||||
"id": 2
|
||||
}
|
||||
]
|
||||
});
|
||||
let response: LogResponse = serde_json::from_value(logs_response_json.clone()).unwrap();
|
||||
let (mock, app, _server) = MockServarrApi::get()
|
||||
.returns(logs_response_json)
|
||||
.query("pageSize=500&sortDirection=descending&sortKey=time")
|
||||
.build_for(LidarrEvent::GetLogs(500))
|
||||
.await;
|
||||
app.lock().await.server_tabs.set_index(2);
|
||||
let mut network = test_network(&app);
|
||||
|
||||
let LidarrSerdeable::LogResponse(logs) = network
|
||||
.handle_lidarr_event(LidarrEvent::GetLogs(500))
|
||||
.await
|
||||
.unwrap()
|
||||
else {
|
||||
panic!("Expected LogResponse")
|
||||
};
|
||||
mock.assert_async().await;
|
||||
assert_eq!(app.lock().await.data.lidarr_data.logs.items, expected_logs);
|
||||
assert!(
|
||||
app
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.lidarr_data
|
||||
.logs
|
||||
.current_selection()
|
||||
.text
|
||||
.contains("INFO")
|
||||
);
|
||||
assert_eq!(logs, response);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_queued_lidarr_events_event() {
|
||||
let queued_events_json = json!([{
|
||||
"name": "RefreshMonitoredDownloads",
|
||||
"commandName": "Refresh Monitored Downloads",
|
||||
"status": "completed",
|
||||
"queued": "2023-05-20T21:29:16Z",
|
||||
"started": "2023-05-20T21:29:16Z",
|
||||
"ended": "2023-05-20T21:29:16Z",
|
||||
"duration": "00:00:00.5111547",
|
||||
"trigger": "scheduled",
|
||||
}]);
|
||||
let response: Vec<QueueEvent> = serde_json::from_value(queued_events_json.clone()).unwrap();
|
||||
let timestamp = DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T21:29:16Z").unwrap());
|
||||
let expected_event = QueueEvent {
|
||||
name: "RefreshMonitoredDownloads".to_owned(),
|
||||
command_name: "Refresh Monitored Downloads".to_owned(),
|
||||
status: "completed".to_owned(),
|
||||
queued: timestamp,
|
||||
started: Some(timestamp),
|
||||
ended: Some(timestamp),
|
||||
duration: Some("00:00:00.5111547".to_owned()),
|
||||
trigger: "scheduled".to_owned(),
|
||||
};
|
||||
|
||||
let (mock, app, _server) = MockServarrApi::get()
|
||||
.returns(queued_events_json)
|
||||
.build_for(LidarrEvent::GetQueuedEvents)
|
||||
.await;
|
||||
app.lock().await.server_tabs.set_index(2);
|
||||
let mut network = test_network(&app);
|
||||
|
||||
let LidarrSerdeable::QueueEvents(events) = network
|
||||
.handle_lidarr_event(LidarrEvent::GetQueuedEvents)
|
||||
.await
|
||||
.unwrap()
|
||||
else {
|
||||
panic!("Expected QueueEvents")
|
||||
};
|
||||
mock.assert_async().await;
|
||||
assert_eq!(
|
||||
app.lock().await.data.lidarr_data.queued_events.items,
|
||||
vec![expected_event]
|
||||
);
|
||||
assert_eq!(events, response);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_status_event() {
|
||||
let status_json = json!({
|
||||
@@ -129,4 +245,171 @@ mod tests {
|
||||
assert_eq!(status, response);
|
||||
assert_eq!(app.lock().await.data.lidarr_data.version, "1.0.0");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_lidarr_tasks_event() {
|
||||
let tasks_json = json!([{
|
||||
"name": "Application Update Check",
|
||||
"taskName": "ApplicationUpdateCheck",
|
||||
"interval": 360,
|
||||
"lastExecution": "2023-05-20T21:29:16Z",
|
||||
"nextExecution": "2023-05-20T21:29:16Z",
|
||||
},
|
||||
{
|
||||
"name": "Backup",
|
||||
"taskName": "Backup",
|
||||
"interval": 10080,
|
||||
"lastExecution": "2023-05-20T21:29:16Z",
|
||||
"nextExecution": "2023-05-20T21:29:16Z",
|
||||
}]);
|
||||
let response: Vec<LidarrTask> = serde_json::from_value(tasks_json.clone()).unwrap();
|
||||
let timestamp = DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T21:29:16Z").unwrap());
|
||||
let expected_tasks = vec![
|
||||
LidarrTask {
|
||||
name: "Application Update Check".to_owned(),
|
||||
task_name: LidarrTaskName::ApplicationUpdateCheck,
|
||||
interval: 360,
|
||||
last_execution: timestamp,
|
||||
next_execution: timestamp,
|
||||
},
|
||||
LidarrTask {
|
||||
name: "Backup".to_owned(),
|
||||
task_name: LidarrTaskName::Backup,
|
||||
interval: 10080,
|
||||
last_execution: timestamp,
|
||||
next_execution: timestamp,
|
||||
},
|
||||
];
|
||||
let (mock, app, _server) = MockServarrApi::get()
|
||||
.returns(tasks_json)
|
||||
.build_for(LidarrEvent::GetTasks)
|
||||
.await;
|
||||
app.lock().await.server_tabs.set_index(2);
|
||||
let mut network = test_network(&app);
|
||||
|
||||
let LidarrSerdeable::Tasks(tasks) = network
|
||||
.handle_lidarr_event(LidarrEvent::GetTasks)
|
||||
.await
|
||||
.unwrap()
|
||||
else {
|
||||
panic!("Expected Tasks")
|
||||
};
|
||||
mock.assert_async().await;
|
||||
assert_eq!(
|
||||
app.lock().await.data.lidarr_data.tasks.items,
|
||||
expected_tasks
|
||||
);
|
||||
assert_eq!(tasks, response);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_lidarr_updates_event() {
|
||||
let updates_json = json!([{
|
||||
"version": "4.3.2.1",
|
||||
"releaseDate": "2023-04-15T02:02:53Z",
|
||||
"installed": true,
|
||||
"installedOn": "2023-04-15T02:02:53Z",
|
||||
"latest": true,
|
||||
"changes": {
|
||||
"new": [
|
||||
"Cool new thing"
|
||||
],
|
||||
"fixed": [
|
||||
"Some bugs killed"
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"version": "3.2.1.0",
|
||||
"releaseDate": "2023-04-15T02:02:53Z",
|
||||
"installed": false,
|
||||
"installedOn": "2023-04-15T02:02:53Z",
|
||||
"latest": false,
|
||||
"changes": {
|
||||
"new": [
|
||||
"Cool new thing (old)",
|
||||
"Other cool new thing (old)"
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"releaseDate": "2023-04-15T02:02:53Z",
|
||||
"installed": false,
|
||||
"latest": false,
|
||||
"changes": {
|
||||
"fixed": [
|
||||
"Killed bug 1",
|
||||
"Fixed bug 2"
|
||||
]
|
||||
},
|
||||
}]);
|
||||
let response: Vec<Update> = serde_json::from_value(updates_json.clone()).unwrap();
|
||||
let expected_text = updates();
|
||||
let (mock, app, _server) = MockServarrApi::get()
|
||||
.returns(updates_json)
|
||||
.build_for(LidarrEvent::GetUpdates)
|
||||
.await;
|
||||
app.lock().await.server_tabs.set_index(2);
|
||||
let mut network = test_network(&app);
|
||||
|
||||
let LidarrSerdeable::Updates(updates) = network
|
||||
.handle_lidarr_event(LidarrEvent::GetUpdates)
|
||||
.await
|
||||
.unwrap()
|
||||
else {
|
||||
panic!("Expected Updates")
|
||||
};
|
||||
mock.assert_async().await;
|
||||
let actual_text = app.lock().await.data.lidarr_data.updates.get_text();
|
||||
let expected = expected_text.get_text();
|
||||
|
||||
// Trim trailing whitespace from each line for comparison
|
||||
let actual_trimmed: Vec<&str> = actual_text.lines().map(|l| l.trim_end()).collect();
|
||||
let expected_trimmed: Vec<&str> = expected.lines().map(|l| l.trim_end()).collect();
|
||||
|
||||
assert_eq!(
|
||||
actual_trimmed, expected_trimmed,
|
||||
"Updates text mismatch (after trimming trailing whitespace)"
|
||||
);
|
||||
assert_eq!(updates, response);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_start_lidarr_task_event() {
|
||||
let response = json!({ "test": "test"});
|
||||
let (mock, app, _server) = MockServarrApi::post()
|
||||
.with_request_body(json!({
|
||||
"name": "ApplicationUpdateCheck"
|
||||
}))
|
||||
.returns(response.clone())
|
||||
.build_for(LidarrEvent::StartTask(
|
||||
LidarrTaskName::ApplicationUpdateCheck,
|
||||
))
|
||||
.await;
|
||||
app
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.lidarr_data
|
||||
.tasks
|
||||
.set_items(vec![LidarrTask {
|
||||
task_name: LidarrTaskName::default(),
|
||||
..LidarrTask::default()
|
||||
}]);
|
||||
app.lock().await.server_tabs.set_index(2);
|
||||
let mut network = test_network(&app);
|
||||
|
||||
let LidarrSerdeable::Value(value) = network
|
||||
.handle_lidarr_event(LidarrEvent::StartTask(
|
||||
LidarrTaskName::ApplicationUpdateCheck,
|
||||
))
|
||||
.await
|
||||
.unwrap()
|
||||
else {
|
||||
panic!("Expected Value")
|
||||
};
|
||||
mock.assert_async().await;
|
||||
assert_eq!(value, response);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user