feat: CLI and TUI support for track history and track details in Lidarr

This commit is contained in:
2026-01-19 14:50:20 -07:00
parent 7add62b245
commit eff1a901eb
54 changed files with 3462 additions and 329 deletions
@@ -3,6 +3,7 @@ mod tests {
use crate::models::lidarr_models::{
Album, DeleteParams, LidarrHistoryItem, LidarrRelease, LidarrSerdeable,
};
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
use crate::models::servarr_data::lidarr::modals::AlbumDetailsModal;
use crate::models::stateful_table::SortOption;
use crate::network::lidarr_network::LidarrEvent;
@@ -146,6 +147,7 @@ mod tests {
"sourceTitle": "z album",
"albumId": 1007,
"artistId": 1007,
"trackId": 1007,
"quality": { "quality": { "name": "Lossless" } },
"date": "2023-01-01T00:00:00Z",
"eventType": "grabbed",
@@ -159,6 +161,7 @@ mod tests {
"sourceTitle": "An Album",
"albumId": 2001,
"artistId": 2001,
"trackId": 2001,
"quality": { "quality": { "name": "Lossless" } },
"date": "2023-01-01T00:00:00Z",
"eventType": "grabbed",
@@ -173,6 +176,7 @@ mod tests {
id: 123,
artist_id: 1007,
album_id: 1007,
track_id: 1007,
source_title: "z album".into(),
..lidarr_history_item()
},
@@ -180,6 +184,7 @@ mod tests {
id: 456,
artist_id: 2001,
album_id: 2001,
track_id: 2001,
source_title: "An Album".into(),
..lidarr_history_item()
},
@@ -270,6 +275,7 @@ mod tests {
"sourceTitle": "z album",
"albumId": 1007,
"artistId": 1007,
"trackId": 1007,
"quality": { "quality": { "name": "Lossless" } },
"date": "2023-01-01T00:00:00Z",
"eventType": "grabbed",
@@ -283,6 +289,7 @@ mod tests {
"sourceTitle": "An Album",
"albumId": 2001,
"artistId": 2001,
"trackId": 2001,
"quality": { "quality": { "name": "Lossless" } },
"date": "2023-01-01T00:00:00Z",
"eventType": "grabbed",
@@ -297,6 +304,7 @@ mod tests {
id: 123,
artist_id: 1007,
album_id: 1007,
track_id: 1007,
source_title: "z album".into(),
..lidarr_history_item()
},
@@ -304,6 +312,7 @@ mod tests {
id: 456,
artist_id: 2001,
album_id: 2001,
track_id: 2001,
source_title: "An Album".into(),
..lidarr_history_item()
},
@@ -350,6 +359,95 @@ mod tests {
assert_eq!(history, response);
}
#[tokio::test]
async fn test_handle_get_lidarr_album_history_event_no_op_when_user_is_selecting_sort_options() {
let history_json = json!([{
"id": 123,
"sourceTitle": "z album",
"albumId": 1007,
"artistId": 1007,
"trackId": 1007,
"quality": { "quality": { "name": "Lossless" } },
"date": "2023-01-01T00:00:00Z",
"eventType": "grabbed",
"data": {
"droppedPath": "/nfs/nzbget/completed/music/Something/cool.mp3",
"importedPath": "/nfs/music/Something/Album 1/Cool.mp3"
}
},
{
"id": 456,
"sourceTitle": "An Album",
"albumId": 2001,
"artistId": 2001,
"trackId": 2001,
"quality": { "quality": { "name": "Lossless" } },
"date": "2023-01-01T00:00:00Z",
"eventType": "grabbed",
"data": {
"droppedPath": "/nfs/nzbget/completed/music/Something/cool.mp3",
"importedPath": "/nfs/music/Something/Album 1/Cool.mp3"
}
}]);
let response: Vec<LidarrHistoryItem> = serde_json::from_value(history_json.clone()).unwrap();
let (mock, app, _server) = MockServarrApi::get()
.returns(history_json)
.query("artistId=1&albumId=1")
.build_for(LidarrEvent::GetAlbumHistory(1, 1))
.await;
app.lock().await.data.lidarr_data.album_details_modal = Some(AlbumDetailsModal::default());
app
.lock()
.await
.data
.lidarr_data
.album_details_modal
.as_mut()
.unwrap()
.album_history
.sort_asc = true;
app
.lock()
.await
.push_navigation_stack(ActiveLidarrBlock::AlbumHistorySortPrompt.into());
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
let LidarrSerdeable::LidarrHistoryItems(history) = network
.handle_lidarr_event(LidarrEvent::GetAlbumHistory(1, 1))
.await
.unwrap()
else {
panic!("Expected LidarrHistoryItems")
};
mock.assert_async().await;
assert_is_empty!(
app
.lock()
.await
.data
.lidarr_data
.album_details_modal
.as_ref()
.unwrap()
.album_history
.items
);
assert!(
app
.lock()
.await
.data
.lidarr_data
.album_details_modal
.as_ref()
.unwrap()
.album_history
.sort_asc
);
assert_eq!(history, response);
}
#[tokio::test]
async fn test_handle_get_album_releases_event() {
let release_json = json!([
@@ -1,7 +1,8 @@
use crate::models::Route;
use crate::models::lidarr_models::{
Album, DeleteParams, LidarrCommandBody, LidarrHistoryItem, LidarrRelease,
};
use crate::models::servarr_data::lidarr::modals::AlbumDetailsModal;
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
use crate::network::lidarr_network::LidarrEvent;
use crate::network::{Network, RequestMethod};
use anyhow::Result;
@@ -75,28 +76,25 @@ impl Network<'_, '_> {
self
.handle_request::<(), Vec<LidarrHistoryItem>>(request_props, |history_items, mut app| {
if app.data.lidarr_data.album_details_modal.is_none() {
app.data.lidarr_data.album_details_modal = Some(AlbumDetailsModal::default());
}
let is_sorting = matches!(
app.get_current_route(),
Route::Lidarr(ActiveLidarrBlock::AlbumHistorySortPrompt, _)
);
let mut history_vec = history_items;
history_vec.sort_by(|a, b| a.id.cmp(&b.id));
app
.data
.lidarr_data
.album_details_modal
.as_mut()
.unwrap()
.album_history
.set_items(history_vec);
app
.data
.lidarr_data
.album_details_modal
.as_mut()
.unwrap()
.album_history
.apply_sorting_toggle(false);
if !is_sorting {
let album_details_modal = app
.data
.lidarr_data
.album_details_modal
.get_or_insert_default();
let mut history_vec = history_items;
history_vec.sort_by(|a, b| a.id.cmp(&b.id));
album_details_modal.album_history.set_items(history_vec);
album_details_modal
.album_history
.apply_sorting_toggle(false);
}
})
.await
}
@@ -121,21 +119,18 @@ impl Network<'_, '_> {
self
.handle_request::<(), Vec<LidarrRelease>>(request_props, |release_vec, mut app| {
if app.data.lidarr_data.album_details_modal.is_none() {
app.data.lidarr_data.album_details_modal = Some(AlbumDetailsModal::default());
}
let album_details_modal = app
.data
.lidarr_data
.album_details_modal
.get_or_insert_default();
let album_releases_vec = release_vec
.into_iter()
.filter(|release| !release.discography)
.collect();
app
.data
.lidarr_data
.album_details_modal
.as_mut()
.unwrap()
album_details_modal
.album_releases
.set_items(album_releases_vec);
})