feat: Created a History tab in the Radarr UI and created a list history command and mark-history-item-as-failed command for Radarr
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
use crate::models::Route;
|
||||
use crate::models::radarr_models::RadarrHistoryWrapper;
|
||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
use crate::network::{Network, RequestMethod};
|
||||
use anyhow::Result;
|
||||
use log::info;
|
||||
use serde_json::Value;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "radarr_history_network_tests.rs"]
|
||||
mod radarr_history_network_tests;
|
||||
|
||||
impl Network<'_, '_> {
|
||||
pub(in crate::network::radarr_network) async fn get_radarr_history(
|
||||
&mut self,
|
||||
events: u64,
|
||||
) -> Result<RadarrHistoryWrapper> {
|
||||
info!("Fetching all Radarr history events");
|
||||
let event = RadarrEvent::GetHistory(events);
|
||||
|
||||
let params = format!("pageSize={events}&sortDirection=descending&sortKey=date");
|
||||
let request_props = self
|
||||
.request_props_from(event, RequestMethod::Get, None::<()>, None, Some(params))
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), RadarrHistoryWrapper>(request_props, |history_response, mut app| {
|
||||
if !matches!(
|
||||
app.get_current_route(),
|
||||
Route::Radarr(ActiveRadarrBlock::HistorySortPrompt, _)
|
||||
) {
|
||||
let mut history_vec = history_response.records;
|
||||
history_vec.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
app.data.radarr_data.history.set_items(history_vec);
|
||||
app.data.radarr_data.history.apply_sorting_toggle(false);
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub(in crate::network::radarr_network) async fn mark_radarr_history_item_as_failed(
|
||||
&mut self,
|
||||
history_item_id: i64,
|
||||
) -> Result<Value> {
|
||||
info!("Marking the Radarr history item with ID: {history_item_id} as 'failed'");
|
||||
let event = RadarrEvent::MarkHistoryItemAsFailed(history_item_id);
|
||||
|
||||
let request_props = self
|
||||
.request_props_from(
|
||||
event,
|
||||
RequestMethod::Post,
|
||||
None,
|
||||
Some(format!("/{history_item_id}")),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), Value>(request_props, |_, _| ())
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::models::radarr_models::{RadarrHistoryItem, RadarrHistoryWrapper, RadarrSerdeable};
|
||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||
use crate::models::stateful_table::SortOption;
|
||||
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
use crate::network::radarr_network::radarr_network_test_utils::test_utils::radarr_history_item;
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
use serde_json::json;
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_radarr_history_event(#[values(true, false)] use_custom_sorting: bool) {
|
||||
let history_json = json!({"records": [{
|
||||
"id": 123,
|
||||
"sourceTitle": "z movie",
|
||||
"movieId": 1007,
|
||||
"quality": { "quality": { "name": "HD - 1080p" } },
|
||||
"languages": [{ "id": 1, "name": "English" }],
|
||||
"date": "2022-12-30T07:37:56Z",
|
||||
"eventType": "grabbed",
|
||||
"data": {
|
||||
"indexer": "DrunkenSlug (Prowlarr)",
|
||||
"releaseGroup": "SPARKS",
|
||||
"downloadClient": "transmission",
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 456,
|
||||
"sourceTitle": "A Movie",
|
||||
"movieId": 2001,
|
||||
"quality": { "quality": { "name": "HD - 1080p" } },
|
||||
"languages": [{ "id": 1, "name": "English" }],
|
||||
"date": "2022-12-30T07:37:56Z",
|
||||
"eventType": "grabbed",
|
||||
"data": {
|
||||
"indexer": "DrunkenSlug (Prowlarr)",
|
||||
"releaseGroup": "SPARKS",
|
||||
"downloadClient": "transmission",
|
||||
}
|
||||
}]});
|
||||
let response: RadarrHistoryWrapper = serde_json::from_value(history_json.clone()).unwrap();
|
||||
let mut expected_history_items = vec![
|
||||
RadarrHistoryItem {
|
||||
id: 123,
|
||||
movie_id: 1007,
|
||||
source_title: "z movie".into(),
|
||||
..radarr_history_item()
|
||||
},
|
||||
RadarrHistoryItem {
|
||||
id: 456,
|
||||
movie_id: 2001,
|
||||
source_title: "A Movie".into(),
|
||||
..radarr_history_item()
|
||||
},
|
||||
];
|
||||
let (mock, app, _server) = MockServarrApi::get()
|
||||
.returns(history_json)
|
||||
.query("pageSize=500&sortDirection=descending&sortKey=date")
|
||||
.build_for(RadarrEvent::GetHistory(500))
|
||||
.await;
|
||||
app.lock().await.data.radarr_data.history.sort_asc = true;
|
||||
if use_custom_sorting {
|
||||
let cmp_fn = |a: &RadarrHistoryItem, b: &RadarrHistoryItem| {
|
||||
a.source_title
|
||||
.text
|
||||
.to_lowercase()
|
||||
.cmp(&b.source_title.text.to_lowercase())
|
||||
};
|
||||
expected_history_items.sort_by(cmp_fn);
|
||||
|
||||
let history_sort_option = SortOption {
|
||||
name: "Source Title",
|
||||
cmp_fn: Some(cmp_fn),
|
||||
};
|
||||
app
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.radarr_data
|
||||
.history
|
||||
.sorting(vec![history_sort_option]);
|
||||
}
|
||||
let mut network = test_network(&app);
|
||||
|
||||
let RadarrSerdeable::HistoryWrapper(history) = network
|
||||
.handle_radarr_event(RadarrEvent::GetHistory(500))
|
||||
.await
|
||||
.unwrap()
|
||||
else {
|
||||
panic!("Expected HistoryWrapper")
|
||||
};
|
||||
mock.assert_async().await;
|
||||
assert_eq!(
|
||||
app.lock().await.data.radarr_data.history.items,
|
||||
expected_history_items
|
||||
);
|
||||
assert!(app.lock().await.data.radarr_data.history.sort_asc);
|
||||
assert_eq!(history, response);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_radarr_history_event_no_op_when_user_is_selecting_sort_options() {
|
||||
let history_json = json!({"records": [{
|
||||
"id": 123,
|
||||
"sourceTitle": "z movie",
|
||||
"movieId": 1007,
|
||||
"quality": { "quality": { "name": "Bluray-1080p" } },
|
||||
"languages": [{ "id": 1, "name": "English" }],
|
||||
"date": "2024-02-10T07:28:45Z",
|
||||
"eventType": "grabbed",
|
||||
"data": {
|
||||
"indexer": "DrunkenSlug (Prowlarr)",
|
||||
"releaseGroup": "SPARKS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 456,
|
||||
"sourceTitle": "A Movie",
|
||||
"movieId": 2001,
|
||||
"quality": { "quality": { "name": "Bluray-1080p" } },
|
||||
"languages": [{ "id": 1, "name": "English" }],
|
||||
"date": "2024-02-10T07:28:45Z",
|
||||
"eventType": "grabbed",
|
||||
"data": {
|
||||
"indexer": "DrunkenSlug (Prowlarr)",
|
||||
"releaseGroup": "SPARKS"
|
||||
}
|
||||
}]});
|
||||
let response: RadarrHistoryWrapper = serde_json::from_value(history_json.clone()).unwrap();
|
||||
let (mock, app, _server) = MockServarrApi::get()
|
||||
.returns(history_json)
|
||||
.query("pageSize=500&sortDirection=descending&sortKey=date")
|
||||
.build_for(RadarrEvent::GetHistory(500))
|
||||
.await;
|
||||
app.lock().await.data.radarr_data.history.sort_asc = true;
|
||||
app
|
||||
.lock()
|
||||
.await
|
||||
.push_navigation_stack(ActiveRadarrBlock::HistorySortPrompt.into());
|
||||
let cmp_fn = |a: &RadarrHistoryItem, b: &RadarrHistoryItem| {
|
||||
a.source_title
|
||||
.text
|
||||
.to_lowercase()
|
||||
.cmp(&b.source_title.text.to_lowercase())
|
||||
};
|
||||
let history_sort_option = SortOption {
|
||||
name: "Source Title",
|
||||
cmp_fn: Some(cmp_fn),
|
||||
};
|
||||
app
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.radarr_data
|
||||
.history
|
||||
.sorting(vec![history_sort_option]);
|
||||
let mut network = test_network(&app);
|
||||
|
||||
let RadarrSerdeable::HistoryWrapper(history) = network
|
||||
.handle_radarr_event(RadarrEvent::GetHistory(500))
|
||||
.await
|
||||
.unwrap()
|
||||
else {
|
||||
panic!("Expected HistoryWrapper")
|
||||
};
|
||||
mock.assert_async().await;
|
||||
assert_is_empty!(app.lock().await.data.radarr_data.history);
|
||||
assert!(app.lock().await.data.radarr_data.history.sort_asc);
|
||||
assert_eq!(history, response);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_mark_radarr_history_item_as_failed_event() {
|
||||
let expected_history_item_id = 1;
|
||||
let (mock, app, _server) = MockServarrApi::post()
|
||||
.returns(json!({}))
|
||||
.path("/1")
|
||||
.build_for(RadarrEvent::MarkHistoryItemAsFailed(
|
||||
expected_history_item_id,
|
||||
))
|
||||
.await;
|
||||
let mut network = test_network(&app);
|
||||
|
||||
let result = network
|
||||
.handle_radarr_event(RadarrEvent::MarkHistoryItemAsFailed(
|
||||
expected_history_item_id,
|
||||
))
|
||||
.await;
|
||||
|
||||
mock.assert_async().await;
|
||||
assert_ok!(result);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ use super::NetworkResource;
|
||||
mod blocklist;
|
||||
mod collections;
|
||||
mod downloads;
|
||||
mod history;
|
||||
mod indexers;
|
||||
mod library;
|
||||
mod root_folders;
|
||||
@@ -47,10 +48,12 @@ pub enum RadarrEvent {
|
||||
GetBlocklist,
|
||||
GetCollections,
|
||||
GetDownloads(u64),
|
||||
GetHistory(u64),
|
||||
GetHostConfig,
|
||||
GetIndexers,
|
||||
GetAllIndexerSettings,
|
||||
GetLogs(u64),
|
||||
MarkHistoryItemAsFailed(i64),
|
||||
GetMovieCredits(i64),
|
||||
GetMovieDetails(i64),
|
||||
GetMovieHistory(i64),
|
||||
@@ -86,7 +89,9 @@ impl NetworkResource for RadarrEvent {
|
||||
RadarrEvent::GetBlocklist => "/blocklist?page=1&pageSize=10000",
|
||||
RadarrEvent::GetCollections | RadarrEvent::EditCollection(_) => "/collection",
|
||||
RadarrEvent::GetDownloads(_) | RadarrEvent::DeleteDownload(_) => "/queue",
|
||||
RadarrEvent::GetHistory(_) => "/history",
|
||||
RadarrEvent::GetHostConfig | RadarrEvent::GetSecurityConfig => "/config/host",
|
||||
RadarrEvent::MarkHistoryItemAsFailed(_) => "/history/failed",
|
||||
RadarrEvent::GetIndexers | RadarrEvent::EditIndexer(_) | RadarrEvent::DeleteIndexer(_) => {
|
||||
"/indexer"
|
||||
}
|
||||
@@ -199,6 +204,10 @@ impl Network<'_, '_> {
|
||||
.get_radarr_downloads(count)
|
||||
.await
|
||||
.map(RadarrSerdeable::from),
|
||||
RadarrEvent::GetHistory(events) => self
|
||||
.get_radarr_history(events)
|
||||
.await
|
||||
.map(RadarrSerdeable::from),
|
||||
RadarrEvent::GetHostConfig => self
|
||||
.get_radarr_host_config()
|
||||
.await
|
||||
@@ -208,6 +217,10 @@ impl Network<'_, '_> {
|
||||
.get_radarr_logs(events)
|
||||
.await
|
||||
.map(RadarrSerdeable::from),
|
||||
RadarrEvent::MarkHistoryItemAsFailed(history_item_id) => self
|
||||
.mark_radarr_history_item_as_failed(history_item_id)
|
||||
.await
|
||||
.map(RadarrSerdeable::from),
|
||||
RadarrEvent::GetMovieCredits(movie_id) => {
|
||||
self.get_credits(movie_id).await.map(RadarrSerdeable::from)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ pub mod test_utils {
|
||||
use crate::models::radarr_models::{
|
||||
AddMovieSearchResult, BlocklistItem, BlocklistItemMovie, Collection, CollectionMovie, Credit,
|
||||
CreditType, DownloadRecord, DownloadsResponse, IndexerSettings, MediaInfo, MinimumAvailability,
|
||||
Movie, MovieCollection, MovieFile, MovieHistoryItem, RadarrRelease, RadarrTask, RadarrTaskName,
|
||||
Rating, RatingsList,
|
||||
Movie, MovieCollection, MovieFile, MovieHistoryItem, RadarrHistoryData, RadarrHistoryEventType,
|
||||
RadarrHistoryItem, RadarrRelease, RadarrTask, RadarrTaskName, Rating, RatingsList,
|
||||
};
|
||||
use crate::models::servarr_models::{
|
||||
Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder,
|
||||
@@ -313,6 +313,24 @@ pub mod test_utils {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn radarr_history_item() -> RadarrHistoryItem {
|
||||
RadarrHistoryItem {
|
||||
id: 1,
|
||||
source_title: HorizontallyScrollableText::from("Test"),
|
||||
movie_id: 1,
|
||||
quality: quality_wrapper(),
|
||||
languages: vec![language()],
|
||||
date: DateTime::from(DateTime::parse_from_rfc3339("2022-12-30T07:37:56Z").unwrap()),
|
||||
event_type: RadarrHistoryEventType::Grabbed,
|
||||
data: RadarrHistoryData {
|
||||
indexer: Some("DrunkenSlug (Prowlarr)".to_owned()),
|
||||
release_group: Some("SPARKS".to_owned()),
|
||||
download_client: Some("transmission".to_owned()),
|
||||
..RadarrHistoryData::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn download_record() -> DownloadRecord {
|
||||
DownloadRecord {
|
||||
title: "Test Download Title".to_owned(),
|
||||
|
||||
@@ -136,7 +136,9 @@ mod test {
|
||||
#[case(RadarrEvent::ClearBlocklist, "/blocklist/bulk")]
|
||||
#[case(RadarrEvent::DeleteBlocklistItem(1), "/blocklist")]
|
||||
#[case(RadarrEvent::GetBlocklist, "/blocklist?page=1&pageSize=10000")]
|
||||
#[case(RadarrEvent::GetHistory(500), "/history")]
|
||||
#[case(RadarrEvent::GetLogs(500), "/log")]
|
||||
#[case(RadarrEvent::MarkHistoryItemAsFailed(1), "/history/failed")]
|
||||
#[case(RadarrEvent::SearchNewMovie(String::new()), "/movie/lookup")]
|
||||
#[case(RadarrEvent::GetMovieCredits(0), "/credit")]
|
||||
#[case(RadarrEvent::GetMovieHistory(0), "/history/movie")]
|
||||
|
||||
Reference in New Issue
Block a user