Added full support for managing the blocklist
This commit is contained in:
+5
-1
@@ -143,7 +143,11 @@ impl<'a, 'b> Network<'a, 'b> {
|
||||
.put(uri)
|
||||
.json(&body.unwrap_or_default())
|
||||
.header("X-Api-Key", api_token),
|
||||
RequestMethod::Delete => self.client.delete(uri).header("X-Api-Key", api_token),
|
||||
RequestMethod::Delete => self
|
||||
.client
|
||||
.delete(uri)
|
||||
.json(&body.unwrap_or_default())
|
||||
.header("X-Api-Key", api_token),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ use urlencoding::encode;
|
||||
|
||||
use crate::app::RadarrConfig;
|
||||
use crate::models::radarr_models::{
|
||||
AddMovieBody, AddMovieSearchResult, AddOptions, AddRootFolderBody, Collection, CollectionMovie,
|
||||
CommandBody, Credit, CreditType, DiskSpace, DownloadRecord, DownloadsResponse, Indexer,
|
||||
IndexerSettings, IndexerTestResult, LogResponse, Movie, MovieCommandBody, MovieHistoryItem,
|
||||
QualityProfile, QueueEvent, Release, ReleaseDownloadBody, RootFolder, SystemStatus, Tag, Task,
|
||||
Update,
|
||||
AddMovieBody, AddMovieSearchResult, AddOptions, AddRootFolderBody, BlocklistResponse, Collection,
|
||||
CollectionMovie, CommandBody, Credit, CreditType, DiskSpace, DownloadRecord, DownloadsResponse,
|
||||
Indexer, IndexerSettings, IndexerTestResult, LogResponse, Movie, MovieCommandBody,
|
||||
MovieHistoryItem, QualityProfile, QueueEvent, Release, ReleaseDownloadBody, RootFolder,
|
||||
SystemStatus, Tag, Task, Update,
|
||||
};
|
||||
use crate::models::servarr_data::radarr::modals::{
|
||||
AddMovieModal, EditCollectionModal, EditIndexerModal, EditMovieModal, IndexerTestResultModalItem,
|
||||
@@ -33,6 +33,8 @@ mod radarr_network_tests;
|
||||
pub enum RadarrEvent {
|
||||
AddMovie,
|
||||
AddRootFolder,
|
||||
ClearBlocklist,
|
||||
DeleteBlocklistItem,
|
||||
DeleteDownload,
|
||||
DeleteIndexer,
|
||||
DeleteMovie,
|
||||
@@ -42,6 +44,7 @@ pub enum RadarrEvent {
|
||||
EditCollection,
|
||||
EditIndexer,
|
||||
EditMovie,
|
||||
GetBlocklist,
|
||||
GetCollections,
|
||||
GetDownloads,
|
||||
GetIndexers,
|
||||
@@ -75,6 +78,9 @@ pub enum RadarrEvent {
|
||||
impl RadarrEvent {
|
||||
const fn resource(self) -> &'static str {
|
||||
match self {
|
||||
RadarrEvent::ClearBlocklist => "/blocklist/bulk",
|
||||
RadarrEvent::DeleteBlocklistItem => "/blocklist",
|
||||
RadarrEvent::GetBlocklist => "/blocklist?page=1&pageSize=10000",
|
||||
RadarrEvent::GetCollections | RadarrEvent::EditCollection => "/collection",
|
||||
RadarrEvent::GetDownloads | RadarrEvent::DeleteDownload => "/queue",
|
||||
RadarrEvent::GetIndexers | RadarrEvent::EditIndexer | RadarrEvent::DeleteIndexer => {
|
||||
@@ -125,6 +131,8 @@ impl<'a, 'b> Network<'a, 'b> {
|
||||
match radarr_event {
|
||||
RadarrEvent::AddMovie => self.add_movie().await,
|
||||
RadarrEvent::AddRootFolder => self.add_root_folder().await,
|
||||
RadarrEvent::ClearBlocklist => self.clear_blocklist().await,
|
||||
RadarrEvent::DeleteBlocklistItem => self.delete_blocklist_item().await,
|
||||
RadarrEvent::DeleteDownload => self.delete_download().await,
|
||||
RadarrEvent::DeleteIndexer => self.delete_indexer().await,
|
||||
RadarrEvent::DeleteMovie => self.delete_movie().await,
|
||||
@@ -134,6 +142,7 @@ impl<'a, 'b> Network<'a, 'b> {
|
||||
RadarrEvent::EditCollection => self.edit_collection().await,
|
||||
RadarrEvent::EditIndexer => self.edit_indexer().await,
|
||||
RadarrEvent::EditMovie => self.edit_movie().await,
|
||||
RadarrEvent::GetBlocklist => self.get_blocklist().await,
|
||||
RadarrEvent::GetCollections => self.get_collections().await,
|
||||
RadarrEvent::GetDownloads => self.get_downloads().await,
|
||||
RadarrEvent::GetIndexers => self.get_indexers().await,
|
||||
@@ -319,6 +328,64 @@ impl<'a, 'b> Network<'a, 'b> {
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn clear_blocklist(&mut self) {
|
||||
info!("Clearing Radarr blocklist");
|
||||
|
||||
let ids = self
|
||||
.app
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.items
|
||||
.iter()
|
||||
.map(|item| item.id)
|
||||
.collect::<Vec<i64>>();
|
||||
|
||||
let request_props = self
|
||||
.radarr_request_props_from(
|
||||
RadarrEvent::ClearBlocklist.resource(),
|
||||
RequestMethod::Delete,
|
||||
Some(json!({"ids": ids})),
|
||||
)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<Value, ()>(request_props, |_, _| ())
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn delete_blocklist_item(&mut self) {
|
||||
let blocklist_item_id = self
|
||||
.app
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.current_selection()
|
||||
.id;
|
||||
|
||||
info!("Deleting Radarr blocklist item for item with id: {blocklist_item_id}");
|
||||
|
||||
let request_props = self
|
||||
.radarr_request_props_from(
|
||||
format!(
|
||||
"{}/{blocklist_item_id}",
|
||||
RadarrEvent::DeleteBlocklistItem.resource()
|
||||
)
|
||||
.as_str(),
|
||||
RequestMethod::Delete,
|
||||
None::<()>,
|
||||
)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), ()>(request_props, |_, _| ())
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn delete_download(&mut self) {
|
||||
let download_id = self
|
||||
.app
|
||||
@@ -794,6 +861,32 @@ impl<'a, 'b> Network<'a, 'b> {
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn get_blocklist(&mut self) {
|
||||
info!("Fetching blocklist");
|
||||
|
||||
let request_props = self
|
||||
.radarr_request_props_from(
|
||||
RadarrEvent::GetBlocklist.resource(),
|
||||
RequestMethod::Get,
|
||||
None::<()>,
|
||||
)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), BlocklistResponse>(request_props, |blocklist_resp, mut app| {
|
||||
if !matches!(
|
||||
app.get_current_route(),
|
||||
Route::Radarr(ActiveRadarrBlock::BlocklistSortPrompt, _)
|
||||
) {
|
||||
let mut blocklist_vec = blocklist_resp.records;
|
||||
blocklist_vec.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
app.data.radarr_data.blocklist.set_items(blocklist_vec);
|
||||
app.data.radarr_data.blocklist.apply_sorting_toggle(false);
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn get_collections(&mut self) {
|
||||
info!("Fetching Radarr collections");
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ mod test {
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::models::radarr_models::{
|
||||
CollectionMovie, IndexerField, Language, MediaInfo, MinimumAvailability, Monitor, MovieFile,
|
||||
Quality, QualityWrapper, Rating, RatingsList,
|
||||
BlocklistItem, CollectionMovie, IndexerField, Language, MediaInfo, MinimumAvailability,
|
||||
Monitor, MovieFile, Quality, QualityWrapper, Rating, RatingsList,
|
||||
};
|
||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||
use crate::models::stateful_table::SortOption;
|
||||
@@ -186,6 +186,9 @@ mod test {
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(RadarrEvent::ClearBlocklist, "/blocklist/bulk")]
|
||||
#[case(RadarrEvent::DeleteBlocklistItem, "/blocklist")]
|
||||
#[case(RadarrEvent::GetBlocklist, "/blocklist?page=1&pageSize=10000")]
|
||||
#[case(RadarrEvent::GetLogs, "/log")]
|
||||
#[case(RadarrEvent::SearchNewMovie, "/movie/lookup")]
|
||||
#[case(RadarrEvent::GetMovieCredits, "/credit")]
|
||||
@@ -1302,6 +1305,271 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_blocklist_event(#[values(true, false)] use_custom_sorting: bool) {
|
||||
let blocklist_json = json!({"records": [{
|
||||
"id": 123,
|
||||
"movieId": 1007,
|
||||
"sourceTitle": "z movie",
|
||||
"languages": [{"name": "English"}],
|
||||
"quality": {"quality": {"name": "HD - 1080p"}},
|
||||
"customFormats": [{"name": "English"}],
|
||||
"date": "2024-02-10T07:28:45Z",
|
||||
"protocol": "usenet",
|
||||
"indexer": "DrunkenSlug (Prowlarr)",
|
||||
"message": "test message",
|
||||
"movie": {
|
||||
"id": 1007,
|
||||
"title": "z movie",
|
||||
"tmdbId": 1234,
|
||||
"originalLanguage": {"name": "English"},
|
||||
"sizeOnDisk": 3543348019i64,
|
||||
"status": "Downloaded",
|
||||
"overview": "Blah blah blah",
|
||||
"path": "/nfs/movies",
|
||||
"studio": "21st Century Alex",
|
||||
"genres": ["cool", "family", "fun"],
|
||||
"year": 2023,
|
||||
"monitored": true,
|
||||
"hasFile": true,
|
||||
"runtime": 120,
|
||||
"qualityProfileId": 2222,
|
||||
"minimumAvailability": "announced",
|
||||
"certification": "R",
|
||||
"tags": [1],
|
||||
"ratings": {
|
||||
"imdb": {"value": 9.9},
|
||||
"tmdb": {"value": 9.9},
|
||||
"rottenTomatoes": {"value": 9.9}
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"id": 456,
|
||||
"movieId": 2001,
|
||||
"sourceTitle": "A Movie",
|
||||
"languages": [{"name": "English"}],
|
||||
"quality": {"quality": {"name": "HD - 1080p"}},
|
||||
"customFormats": [{"name": "English"}],
|
||||
"date": "2024-02-10T07:28:45Z",
|
||||
"protocol": "usenet",
|
||||
"indexer": "DrunkenSlug (Prowlarr)",
|
||||
"message": "test message",
|
||||
"movie": {
|
||||
"id": 2001,
|
||||
"title": "A Movie",
|
||||
"tmdbId": 1234,
|
||||
"originalLanguage": {"name": "English"},
|
||||
"sizeOnDisk": 3543348019i64,
|
||||
"status": "Downloaded",
|
||||
"overview": "Blah blah blah",
|
||||
"path": "/nfs/movies",
|
||||
"studio": "21st Century Alex",
|
||||
"genres": ["cool", "family", "fun"],
|
||||
"year": 2023,
|
||||
"monitored": true,
|
||||
"hasFile": true,
|
||||
"runtime": 120,
|
||||
"qualityProfileId": 2222,
|
||||
"minimumAvailability": "announced",
|
||||
"certification": "R",
|
||||
"tags": [1],
|
||||
"ratings": {
|
||||
"imdb": {"value": 9.9},
|
||||
"tmdb": {"value": 9.9},
|
||||
"rottenTomatoes": {"value": 9.9}
|
||||
},
|
||||
},
|
||||
}]});
|
||||
let mut expected_blocklist = vec![
|
||||
BlocklistItem {
|
||||
id: 123,
|
||||
movie_id: 1007,
|
||||
source_title: "z movie".into(),
|
||||
movie: Movie {
|
||||
id: 1007,
|
||||
title: "z movie".into(),
|
||||
movie_file: None,
|
||||
collection: None,
|
||||
..movie()
|
||||
},
|
||||
..blocklist_item()
|
||||
},
|
||||
BlocklistItem {
|
||||
id: 456,
|
||||
movie_id: 2001,
|
||||
source_title: "A Movie".into(),
|
||||
movie: Movie {
|
||||
id: 2001,
|
||||
title: "A Movie".into(),
|
||||
movie_file: None,
|
||||
collection: None,
|
||||
..movie()
|
||||
},
|
||||
..blocklist_item()
|
||||
},
|
||||
];
|
||||
let (async_server, app_arc, _server) = mock_radarr_api(
|
||||
RequestMethod::Get,
|
||||
None,
|
||||
Some(blocklist_json),
|
||||
None,
|
||||
RadarrEvent::GetBlocklist.resource(),
|
||||
)
|
||||
.await;
|
||||
app_arc.lock().await.data.radarr_data.blocklist.sort_asc = true;
|
||||
if use_custom_sorting {
|
||||
let cmp_fn = |a: &BlocklistItem, b: &BlocklistItem| {
|
||||
a.source_title
|
||||
.to_lowercase()
|
||||
.cmp(&b.source_title.to_lowercase())
|
||||
};
|
||||
expected_blocklist.sort_by(cmp_fn);
|
||||
|
||||
let blocklist_sort_option = SortOption {
|
||||
name: "Source Title",
|
||||
cmp_fn: Some(cmp_fn),
|
||||
};
|
||||
app_arc
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.sorting(vec![blocklist_sort_option]);
|
||||
}
|
||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||
|
||||
network.handle_radarr_event(RadarrEvent::GetBlocklist).await;
|
||||
|
||||
async_server.assert_async().await;
|
||||
assert_eq!(
|
||||
app_arc.lock().await.data.radarr_data.blocklist.items,
|
||||
expected_blocklist
|
||||
);
|
||||
assert!(app_arc.lock().await.data.radarr_data.blocklist.sort_asc);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_blocklist_event_no_op_when_user_is_selecting_sort_options() {
|
||||
let blocklist_json = json!({"records": [{
|
||||
"id": 123,
|
||||
"movieId": 1007,
|
||||
"sourceTitle": "z movie",
|
||||
"languages": [{"name": "English"}],
|
||||
"quality": {"quality": {"name": "HD - 1080p"}},
|
||||
"customFormats": [{"name": "English"}],
|
||||
"date": "2024-02-10T07:28:45Z",
|
||||
"protocol": "usenet",
|
||||
"indexer": "DrunkenSlug (Prowlarr)",
|
||||
"message": "test message",
|
||||
"movie": {
|
||||
"id": 1007,
|
||||
"title": "z movie",
|
||||
"tmdbId": 1234,
|
||||
"originalLanguage": {"name": "English"},
|
||||
"sizeOnDisk": 3543348019i64,
|
||||
"status": "Downloaded",
|
||||
"overview": "Blah blah blah",
|
||||
"path": "/nfs/movies",
|
||||
"studio": "21st Century Alex",
|
||||
"genres": ["cool", "family", "fun"],
|
||||
"year": 2023,
|
||||
"monitored": true,
|
||||
"hasFile": true,
|
||||
"runtime": 120,
|
||||
"qualityProfileId": 2222,
|
||||
"minimumAvailability": "announced",
|
||||
"certification": "R",
|
||||
"tags": [1],
|
||||
"ratings": {
|
||||
"imdb": {"value": 9.9},
|
||||
"tmdb": {"value": 9.9},
|
||||
"rottenTomatoes": {"value": 9.9}
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"id": 456,
|
||||
"movieId": 2001,
|
||||
"sourceTitle": "A Movie",
|
||||
"languages": [{"name": "English"}],
|
||||
"quality": {"quality": {"name": "HD - 1080p"}},
|
||||
"customFormats": [{"name": "English"}],
|
||||
"date": "2024-02-10T07:28:45Z",
|
||||
"protocol": "usenet",
|
||||
"indexer": "DrunkenSlug (Prowlarr)",
|
||||
"message": "test message",
|
||||
"movie": {
|
||||
"id": 2001,
|
||||
"title": "A Movie",
|
||||
"tmdbId": 1234,
|
||||
"originalLanguage": {"name": "English"},
|
||||
"sizeOnDisk": 3543348019i64,
|
||||
"status": "Downloaded",
|
||||
"overview": "Blah blah blah",
|
||||
"path": "/nfs/movies",
|
||||
"studio": "21st Century Alex",
|
||||
"genres": ["cool", "family", "fun"],
|
||||
"year": 2023,
|
||||
"monitored": true,
|
||||
"hasFile": true,
|
||||
"runtime": 120,
|
||||
"qualityProfileId": 2222,
|
||||
"minimumAvailability": "announced",
|
||||
"certification": "R",
|
||||
"tags": [1],
|
||||
"ratings": {
|
||||
"imdb": {"value": 9.9},
|
||||
"tmdb": {"value": 9.9},
|
||||
"rottenTomatoes": {"value": 9.9}
|
||||
},
|
||||
},
|
||||
}]});
|
||||
let (async_server, app_arc, _server) = mock_radarr_api(
|
||||
RequestMethod::Get,
|
||||
None,
|
||||
Some(blocklist_json),
|
||||
None,
|
||||
RadarrEvent::GetBlocklist.resource(),
|
||||
)
|
||||
.await;
|
||||
app_arc.lock().await.data.radarr_data.blocklist.sort_asc = true;
|
||||
app_arc
|
||||
.lock()
|
||||
.await
|
||||
.push_navigation_stack(ActiveRadarrBlock::BlocklistSortPrompt.into());
|
||||
let cmp_fn = |a: &BlocklistItem, b: &BlocklistItem| {
|
||||
a.source_title
|
||||
.to_lowercase()
|
||||
.cmp(&b.source_title.to_lowercase())
|
||||
};
|
||||
let blocklist_sort_option = SortOption {
|
||||
name: "Source Title",
|
||||
cmp_fn: Some(cmp_fn),
|
||||
};
|
||||
app_arc
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.sorting(vec![blocklist_sort_option]);
|
||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||
|
||||
network.handle_radarr_event(RadarrEvent::GetBlocklist).await;
|
||||
|
||||
async_server.assert_async().await;
|
||||
assert!(app_arc
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.items
|
||||
.is_empty());
|
||||
assert!(app_arc.lock().await.data.radarr_data.blocklist.sort_asc);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_collections_event(#[values(true, false)] use_custom_sorting: bool) {
|
||||
@@ -2154,6 +2422,68 @@ mod test {
|
||||
assert!(!app_arc.lock().await.data.radarr_data.add_list_exclusion);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_clear_blocklist_event() {
|
||||
let blocklist_items = vec![
|
||||
BlocklistItem {
|
||||
id: 1,
|
||||
..blocklist_item()
|
||||
},
|
||||
BlocklistItem {
|
||||
id: 2,
|
||||
..blocklist_item()
|
||||
},
|
||||
BlocklistItem {
|
||||
id: 3,
|
||||
..blocklist_item()
|
||||
},
|
||||
];
|
||||
let expected_request_json = json!({ "ids": [1, 2, 3]});
|
||||
let (async_server, app_arc, _server) = mock_radarr_api(
|
||||
RequestMethod::Delete,
|
||||
Some(expected_request_json),
|
||||
None,
|
||||
None,
|
||||
RadarrEvent::ClearBlocklist.resource(),
|
||||
)
|
||||
.await;
|
||||
app_arc
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.set_items(blocklist_items);
|
||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||
|
||||
network
|
||||
.handle_radarr_event(RadarrEvent::ClearBlocklist)
|
||||
.await;
|
||||
|
||||
async_server.assert_async().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_delete_blocklist_item_event() {
|
||||
let resource = format!("{}/1", RadarrEvent::DeleteBlocklistItem.resource());
|
||||
let (async_server, app_arc, _server) =
|
||||
mock_radarr_api(RequestMethod::Delete, None, None, None, &resource).await;
|
||||
app_arc
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.set_items(vec![blocklist_item()]);
|
||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||
|
||||
network
|
||||
.handle_radarr_event(RadarrEvent::DeleteBlocklistItem)
|
||||
.await;
|
||||
|
||||
async_server.assert_async().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_delete_download_event() {
|
||||
let resource = format!("{}/1", RadarrEvent::DeleteDownload.resource());
|
||||
@@ -3311,6 +3641,22 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
fn blocklist_item() -> BlocklistItem {
|
||||
BlocklistItem {
|
||||
id: 1,
|
||||
movie_id: 1,
|
||||
source_title: "z movie".to_owned(),
|
||||
languages: vec![language()],
|
||||
quality: quality_wrapper(),
|
||||
custom_formats: Some(vec![language()]),
|
||||
date: DateTime::from(DateTime::parse_from_rfc3339("2024-02-10T07:28:45Z").unwrap()),
|
||||
protocol: "usenet".to_owned(),
|
||||
indexer: "DrunkenSlug (Prowlarr)".to_owned(),
|
||||
message: "test message".to_owned(),
|
||||
movie: movie(),
|
||||
}
|
||||
}
|
||||
|
||||
fn collection() -> Collection {
|
||||
Collection {
|
||||
id: 123,
|
||||
|
||||
Reference in New Issue
Block a user