Implemented the ability to edit collections and fixed a refresh bug so screens will automatically refresh when users edit movies or collections

This commit is contained in:
2023-08-08 10:50:06 -06:00
parent 173e81fc17
commit bf5ecbd5a6
18 changed files with 2040 additions and 273 deletions
+273 -17
View File
@@ -24,6 +24,7 @@ pub enum RadarrEvent {
DeleteMovie,
DownloadRelease,
EditMovie,
EditCollection,
GetCollections,
GetDownloads,
GetMovieCredits,
@@ -48,7 +49,7 @@ pub enum RadarrEvent {
impl RadarrEvent {
const fn resource(self) -> &'static str {
match self {
RadarrEvent::GetCollections => "/collection",
RadarrEvent::GetCollections | RadarrEvent::EditCollection => "/collection",
RadarrEvent::GetDownloads | RadarrEvent::DeleteDownload => "/queue",
RadarrEvent::AddMovie
| RadarrEvent::EditMovie
@@ -88,6 +89,7 @@ impl<'a> Network<'a> {
RadarrEvent::DeleteDownload => self.delete_download().await,
RadarrEvent::DownloadRelease => self.download_release().await,
RadarrEvent::EditMovie => self.edit_movie().await,
RadarrEvent::EditCollection => self.edit_collection().await,
RadarrEvent::GetCollections => self.get_collections().await,
RadarrEvent::GetDownloads => self.get_downloads().await,
RadarrEvent::GetMovieCredits => self.get_credits().await,
@@ -762,13 +764,13 @@ impl<'a> Network<'a> {
let monitor = app
.data
.radarr_data
.movie_monitor_list
.monitor_list
.current_selection()
.to_string();
let minimum_availability = app
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.current_selection()
.to_string();
@@ -827,8 +829,8 @@ impl<'a> Network<'a> {
let quality_profile_id = self.extract_quality_profile_id().await;
let tag_ids_vec = self.extract_and_add_tag_ids_vec().await;
let mut app = self.app.lock().await;
let mut detailed_movie_body: Value = serde_json::from_str(&app.response).unwrap();
app.response = String::default();
let response = app.response.drain(..).collect::<String>();
let mut detailed_movie_body: Value = serde_json::from_str(&response).unwrap();
let path: String = app.data.radarr_data.edit_path.drain();
@@ -836,7 +838,7 @@ impl<'a> Network<'a> {
let minimum_availability = app
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.current_selection()
.to_string();
@@ -864,6 +866,82 @@ impl<'a> Network<'a> {
.await;
}
async fn edit_collection(&self) {
info!("Editing Radarr collection");
info!("Fetching collection details");
let collection_id = self.extract_collection_id().await;
let request_props = self
.radarr_request_props_from(
format!(
"{}/{}",
RadarrEvent::GetCollections.resource(),
collection_id
)
.as_str(),
RequestMethod::Get,
None::<()>,
)
.await;
self
.handle_request::<(), Value>(request_props, |detailed_collection_body, mut app| {
app.response = detailed_collection_body.to_string()
})
.await;
info!("Constructing edit collection body");
let body = {
let quality_profile_id = self.extract_quality_profile_id().await;
let mut app = self.app.lock().await;
let response = app.response.drain(..).collect::<String>();
let mut detailed_collection_body: Value = serde_json::from_str(&response).unwrap();
let root_folder_path: String = app.data.radarr_data.edit_path.drain();
let monitored = app.data.radarr_data.edit_monitored.unwrap_or_default();
let search_on_add = app.data.radarr_data.edit_search_on_add.unwrap_or_default();
let minimum_availability = app
.data
.radarr_data
.minimum_availability_list
.current_selection()
.to_string();
*detailed_collection_body.get_mut("monitored").unwrap() = json!(monitored);
*detailed_collection_body
.get_mut("minimumAvailability")
.unwrap() = json!(minimum_availability);
*detailed_collection_body
.get_mut("qualityProfileId")
.unwrap() = json!(quality_profile_id);
*detailed_collection_body.get_mut("rootFolderPath").unwrap() = json!(root_folder_path);
*detailed_collection_body.get_mut("searchOnAdd").unwrap() = json!(search_on_add);
detailed_collection_body
};
debug!("Edit collection body: {:?}", body);
let request_props = self
.radarr_request_props_from(
format!(
"{}/{}",
RadarrEvent::EditCollection.resource(),
collection_id
)
.as_str(),
RequestMethod::Put,
Some(body),
)
.await;
self
.handle_request::<Value, ()>(request_props, |_, _| ())
.await;
}
async fn download_release(&self) {
let (guid, title, indexer_id) = {
let app = self.app.lock().await;
@@ -899,7 +977,7 @@ impl<'a> Network<'a> {
let quality_profile = app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.current_selection();
*app
.data
@@ -988,6 +1066,43 @@ impl<'a> Network<'a> {
}
}
async fn extract_collection_id(&self) -> u64 {
if !self
.app
.lock()
.await
.data
.radarr_data
.filtered_collections
.items
.is_empty()
{
self
.app
.lock()
.await
.data
.radarr_data
.filtered_collections
.current_selection()
.id
.as_u64()
.unwrap()
} else {
self
.app
.lock()
.await
.data
.radarr_data
.collections
.current_selection()
.id
.as_u64()
.unwrap()
}
}
async fn append_movie_id_param(&self, resource: &str) -> String {
let movie_id = self.extract_movie_id().await;
format!("{}?movieId={}", resource, movie_id)
@@ -1117,8 +1232,12 @@ mod test {
}
},
"collection": {
"id": 123,
"title": "Test Collection",
"rootFolderPath": "/nfs/movies",
"searchOnAdd": true,
"monitored": true,
"minimumAvailability": "released",
"overview": "Collection blah blah blah",
"qualityProfileId": 2222,
"movies": [
@@ -1791,8 +1910,12 @@ mod test {
#[tokio::test]
async fn test_handle_get_collections_event() {
let collection_json = json!([{
"id": 123,
"title": "Test Collection",
"rootFolderPath": "/nfs/movies",
"searchOnAdd": true,
"monitored": true,
"minimumAvailability": "released",
"overview": "Collection blah blah blah",
"qualityProfileId": 2222,
"movies": [{
@@ -2110,17 +2233,17 @@ mod test {
app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.set_items(vec!["HD - 1080p".to_owned()]);
app
.data
.radarr_data
.movie_monitor_list
.monitor_list
.set_items(Vec::from_iter(Monitor::iter()));
app
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
if collection_details_context {
app
@@ -2180,12 +2303,12 @@ mod test {
app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
app
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
app.data.radarr_data.movies.set_items(vec![Movie {
monitored: false,
@@ -2201,11 +2324,105 @@ mod test {
async_details_server.assert_async().await;
async_edit_server.assert_async().await;
let app = app_arc.lock().await;
assert!(app.response.is_empty());
assert!(app.data.radarr_data.edit_path.text.is_empty());
assert!(app.data.radarr_data.movie_details.items.is_empty());
}
#[tokio::test]
async fn test_handle_edit_collection_event() {
let detailed_collection_body = json!({
"id": 123,
"title": "Test Collection",
"rootFolderPath": "/nfs/movies",
"searchOnAdd": true,
"monitored": true,
"minimumAvailability": "released",
"overview": "Collection blah blah blah",
"qualityProfileId": 2222,
"movies": [
{
"title": "Test",
"overview": "Collection blah blah blah",
"year": 2023,
"runtime": 120,
"tmdbId": 1234,
"genres": ["cool", "family", "fun"],
"ratings": {
"imdb": {
"value": 9.9
},
"tmdb": {
"value": 9.9
},
"rottenTomatoes": {
"value": 9.9
}
}
}
]
});
let mut expected_body = detailed_collection_body.clone();
*expected_body.get_mut("monitored").unwrap() = json!(false);
*expected_body.get_mut("minimumAvailability").unwrap() = json!("announced");
*expected_body.get_mut("qualityProfileId").unwrap() = json!(1111);
*expected_body.get_mut("rootFolderPath").unwrap() = json!("/nfs/Test Path");
*expected_body.get_mut("searchOnAdd").unwrap() = json!(false);
let (async_details_server, app_arc, mut server) = mock_radarr_api(
RequestMethod::Get,
None,
Some(detailed_collection_body),
format!("{}/123", RadarrEvent::GetCollections.resource()).as_str(),
)
.await;
let async_edit_server = server
.mock(
"PUT",
format!("/api/v3{}/123", RadarrEvent::EditCollection.resource()).as_str(),
)
.with_status(202)
.match_header("X-Api-Key", "test1234")
.match_body(Matcher::Json(expected_body))
.create_async()
.await;
{
let app = app_arc.lock().await;
assert!(app.data.radarr_data.edit_path.text.is_empty());
assert!(app.data.radarr_data.movie_details.items.is_empty());
let mut app = app_arc.lock().await;
app.data.radarr_data.edit_path = "/nfs/Test Path".to_owned().into();
app.data.radarr_data.edit_monitored = Some(false);
app.data.radarr_data.edit_search_on_add = Some(false);
app
.data
.radarr_data
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
app
.data
.radarr_data
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
app.data.radarr_data.collections.set_items(vec![Collection {
monitored: false,
search_on_add: false,
..collection()
}]);
app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
}
let network = Network::new(reqwest::Client::new(), &app_arc);
network
.handle_radarr_event(RadarrEvent::EditCollection)
.await;
async_details_server.assert_async().await;
async_edit_server.assert_async().await;
let app = app_arc.lock().await;
assert!(app.response.is_empty());
assert!(app.data.radarr_data.edit_path.text.is_empty());
assert!(app.data.radarr_data.movie_details.items.is_empty());
}
#[tokio::test]
@@ -2244,7 +2461,7 @@ mod test {
app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(1, "Any".to_owned()), (2, "HD - 1080p".to_owned())]);
@@ -2338,6 +2555,42 @@ mod test {
assert_eq!(network.extract_movie_id().await, 1);
}
#[tokio::test]
async fn test_extract_collection_id() {
let app_arc = Arc::new(Mutex::new(App::default()));
app_arc
.lock()
.await
.data
.radarr_data
.collections
.set_items(vec![Collection {
id: Number::from(1),
..Collection::default()
}]);
let network = Network::new(reqwest::Client::new(), &app_arc);
assert_eq!(network.extract_collection_id().await, 1);
}
#[tokio::test]
async fn test_extract_collection_id_filtered_collection() {
let app_arc = Arc::new(Mutex::new(App::default()));
app_arc
.lock()
.await
.data
.radarr_data
.filtered_collections
.set_items(vec![Collection {
id: Number::from(1),
..Collection::default()
}]);
let network = Network::new(reqwest::Client::new(), &app_arc);
assert_eq!(network.extract_collection_id().await, 1);
}
#[tokio::test]
async fn test_append_movie_id_param() {
let app_arc = Arc::new(Mutex::new(App::default()));
@@ -2562,9 +2815,12 @@ mod test {
fn collection() -> Collection {
Collection {
id: Number::from(123),
title: "Test Collection".to_owned().into(),
root_folder_path: None,
root_folder_path: Some("/nfs/movies".to_owned()),
search_on_add: true,
monitored: true,
minimum_availability: MinimumAvailability::Released,
overview: Some("Collection blah blah blah".to_owned()),
quality_profile_id: Number::from(2222),
movies: Some(vec![collection_movie()]),