Full support for editing movies and managing tags
This commit is contained in:
+43
-1
@@ -25,6 +25,7 @@ pub struct App {
|
||||
pub client: Client,
|
||||
pub title: &'static str,
|
||||
pub tick_until_poll: u64,
|
||||
pub ticks_until_scroll: u64,
|
||||
pub tick_count: u64,
|
||||
pub last_tick: Instant,
|
||||
pub network_tick_frequency: Duration,
|
||||
@@ -48,6 +49,7 @@ impl App {
|
||||
pub async fn dispatch_network_event(&mut self, action: NetworkEvent) {
|
||||
debug!("Dispatching network event: {:?}", action);
|
||||
|
||||
self.is_loading = true;
|
||||
if let Some(network_tx) = &self.network_tx {
|
||||
if let Err(e) = network_tx.send(action).await {
|
||||
self.is_loading = false;
|
||||
@@ -136,6 +138,7 @@ impl Default for App {
|
||||
client: Client::new(),
|
||||
title: "Managarr",
|
||||
tick_until_poll: 50,
|
||||
ticks_until_scroll: 4,
|
||||
tick_count: 0,
|
||||
network_tick_frequency: Duration::from_secs(20),
|
||||
last_tick: Instant::now(),
|
||||
@@ -178,16 +181,55 @@ impl Default for RadarrConfig {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::app::radarr::{ActiveRadarrBlock, RadarrData};
|
||||
use crate::app::{App, Data, RadarrConfig, DEFAULT_ROUTE};
|
||||
use crate::models::HorizontallyScrollableText;
|
||||
use crate::models::{HorizontallyScrollableText, Route, TabRoute};
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
use crate::network::NetworkEvent;
|
||||
|
||||
#[test]
|
||||
fn test_app_default() {
|
||||
let app = App::default();
|
||||
|
||||
assert_eq!(app.navigation_stack, vec![DEFAULT_ROUTE]);
|
||||
assert!(app.network_tx.is_none());
|
||||
assert_eq!(app.error, HorizontallyScrollableText::default());
|
||||
assert_eq!(app.response, String::default());
|
||||
assert_eq!(app.server_tabs.index, 0);
|
||||
assert_eq!(
|
||||
app.server_tabs.tabs,
|
||||
vec![
|
||||
TabRoute {
|
||||
title: "Radarr".to_owned(),
|
||||
route: ActiveRadarrBlock::Movies.into(),
|
||||
help: "<↑↓> scroll | ←→ change tab | <tab> change servarr | <q> quit ".to_owned(),
|
||||
contextual_help: None,
|
||||
},
|
||||
TabRoute {
|
||||
title: "Sonarr".to_owned(),
|
||||
route: Route::Sonarr,
|
||||
help: "<tab> change servarr | <q> quit ".to_owned(),
|
||||
contextual_help: None,
|
||||
}
|
||||
]
|
||||
);
|
||||
assert_str_eq!(app.title, "Managarr");
|
||||
assert_eq!(app.tick_until_poll, 50);
|
||||
assert_eq!(app.ticks_until_scroll, 4);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
assert_eq!(app.network_tick_frequency, Duration::from_secs(20));
|
||||
assert!(!app.is_loading);
|
||||
assert!(!app.is_routing);
|
||||
assert!(!app.should_refresh);
|
||||
assert!(!app.should_ignore_quit_key);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_navigation_stack_methods() {
|
||||
let mut app = App::default();
|
||||
|
||||
+59
-36
@@ -1,4 +1,3 @@
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use bimap::BiMap;
|
||||
@@ -28,7 +27,7 @@ pub struct RadarrData {
|
||||
pub movie_quality_profile_list: StatefulList<String>,
|
||||
pub selected_block: ActiveRadarrBlock,
|
||||
pub downloads: StatefulTable<DownloadRecord>,
|
||||
pub quality_profile_map: HashMap<u64, String>,
|
||||
pub quality_profile_map: BiMap<u64, String>,
|
||||
pub tags_map: BiMap<u64, String>,
|
||||
pub movie_details: ScrollableText,
|
||||
pub file_details: String,
|
||||
@@ -112,7 +111,7 @@ impl RadarrData {
|
||||
.movie_minimum_availability_list
|
||||
.set_items(Vec::from_iter(MinimumAvailability::iter()));
|
||||
let mut quality_profile_names: Vec<String> =
|
||||
self.quality_profile_map.values().cloned().collect();
|
||||
self.quality_profile_map.right_values().cloned().collect();
|
||||
quality_profile_names.sort();
|
||||
self
|
||||
.movie_quality_profile_list
|
||||
@@ -161,7 +160,7 @@ impl RadarrData {
|
||||
|
||||
let quality_profile_name = self
|
||||
.quality_profile_map
|
||||
.get(&quality_profile_id.as_u64().unwrap())
|
||||
.get_by_left(&quality_profile_id.as_u64().unwrap())
|
||||
.unwrap();
|
||||
let quality_profile_index = self
|
||||
.movie_quality_profile_list
|
||||
@@ -190,7 +189,7 @@ impl Default for RadarrData {
|
||||
selected_block: ActiveRadarrBlock::AddMovieSelectMonitor,
|
||||
filtered_movies: StatefulTable::default(),
|
||||
downloads: StatefulTable::default(),
|
||||
quality_profile_map: HashMap::default(),
|
||||
quality_profile_map: BiMap::default(),
|
||||
tags_map: BiMap::default(),
|
||||
file_details: String::default(),
|
||||
audio_details: String::default(),
|
||||
@@ -289,6 +288,7 @@ pub enum ActiveRadarrBlock {
|
||||
AddMovieSelectMonitor,
|
||||
AddMovieConfirmPrompt,
|
||||
AddMovieTagsInput,
|
||||
AddMovieEmptySearchResults,
|
||||
AutomaticallySearchMoviePrompt,
|
||||
Collections,
|
||||
CollectionDetails,
|
||||
@@ -322,9 +322,10 @@ pub enum ActiveRadarrBlock {
|
||||
ViewMovieOverview,
|
||||
}
|
||||
|
||||
pub const ADD_MOVIE_BLOCKS: [ActiveRadarrBlock; 8] = [
|
||||
pub const ADD_MOVIE_BLOCKS: [ActiveRadarrBlock; 9] = [
|
||||
ActiveRadarrBlock::AddMovieSearchInput,
|
||||
ActiveRadarrBlock::AddMovieSearchResults,
|
||||
ActiveRadarrBlock::AddMovieEmptySearchResults,
|
||||
ActiveRadarrBlock::AddMoviePrompt,
|
||||
ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
|
||||
ActiveRadarrBlock::AddMovieSelectMonitor,
|
||||
@@ -444,24 +445,19 @@ impl App {
|
||||
pub(super) async fn dispatch_by_radarr_block(&mut self, active_radarr_block: &ActiveRadarrBlock) {
|
||||
match active_radarr_block {
|
||||
ActiveRadarrBlock::Collections => {
|
||||
self.is_loading = true;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetCollections.into())
|
||||
.await;
|
||||
self.check_for_prompt_action().await;
|
||||
}
|
||||
ActiveRadarrBlock::CollectionDetails => {
|
||||
self.is_loading = true;
|
||||
self.populate_movie_collection_table().await;
|
||||
self.is_loading = false;
|
||||
self.check_for_prompt_action().await;
|
||||
}
|
||||
ActiveRadarrBlock::Downloads => {
|
||||
self.is_loading = true;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetDownloads.into())
|
||||
.await;
|
||||
self.check_for_prompt_action().await;
|
||||
}
|
||||
ActiveRadarrBlock::Movies => {
|
||||
self
|
||||
@@ -470,54 +466,42 @@ impl App {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetDownloads.into())
|
||||
.await;
|
||||
self.check_for_prompt_action().await;
|
||||
}
|
||||
ActiveRadarrBlock::AddMovieSearchResults => {
|
||||
self.is_loading = true;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::SearchNewMovie.into())
|
||||
.await;
|
||||
|
||||
self.check_for_prompt_action().await;
|
||||
}
|
||||
ActiveRadarrBlock::MovieDetails | ActiveRadarrBlock::FileInfo => {
|
||||
self.is_loading = true;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetMovieDetails.into())
|
||||
.await;
|
||||
self.check_for_prompt_action().await;
|
||||
}
|
||||
ActiveRadarrBlock::MovieHistory => {
|
||||
self.is_loading = true;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetMovieHistory.into())
|
||||
.await;
|
||||
self.check_for_prompt_action().await;
|
||||
}
|
||||
ActiveRadarrBlock::Cast | ActiveRadarrBlock::Crew => {
|
||||
if self.data.radarr_data.movie_cast.items.is_empty()
|
||||
|| self.data.radarr_data.movie_crew.items.is_empty()
|
||||
{
|
||||
self.is_loading = true;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetMovieCredits.into())
|
||||
.await;
|
||||
}
|
||||
self.check_for_prompt_action().await;
|
||||
}
|
||||
ActiveRadarrBlock::ManualSearch => {
|
||||
if self.data.radarr_data.movie_releases.items.is_empty() && !self.is_loading {
|
||||
self.is_loading = true;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetReleases.into())
|
||||
.await;
|
||||
}
|
||||
|
||||
self.check_for_prompt_action().await;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.check_for_prompt_action().await;
|
||||
self.reset_tick_count();
|
||||
}
|
||||
|
||||
@@ -563,16 +547,20 @@ impl App {
|
||||
.unwrap_or_else(|| Duration::from_secs(0))
|
||||
.is_zero()
|
||||
{
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetQualityProfiles.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetTags.into())
|
||||
.await;
|
||||
self.refresh_metadata().await;
|
||||
self.dispatch_by_radarr_block(&active_radarr_block).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn refresh_metadata(&mut self) {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetQualityProfiles.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetTags.into())
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn populate_movie_collection_table(&mut self) {
|
||||
let collection_movies = if !self.data.radarr_data.filtered_collections.items.is_empty() {
|
||||
self
|
||||
@@ -735,8 +723,6 @@ pub mod radarr_test_utils {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod radarr_data_tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use bimap::BiMap;
|
||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||
use rstest::rstest;
|
||||
@@ -824,7 +810,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_populate_movie_preferences_lists() {
|
||||
let mut radarr_data = RadarrData {
|
||||
quality_profile_map: HashMap::from([
|
||||
quality_profile_map: BiMap::from_iter([
|
||||
(2222, "HD - 1080p".to_owned()),
|
||||
(1111, "Any".to_owned()),
|
||||
]),
|
||||
@@ -853,7 +839,7 @@ mod tests {
|
||||
edit_path: HorizontallyScrollableText::default(),
|
||||
edit_tags: HorizontallyScrollableText::default(),
|
||||
edit_monitored: None,
|
||||
quality_profile_map: HashMap::from([
|
||||
quality_profile_map: BiMap::from_iter([
|
||||
(2222, "HD - 1080p".to_owned()),
|
||||
(1111, "Any".to_owned()),
|
||||
]),
|
||||
@@ -1064,6 +1050,25 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_collection_details_block() {
|
||||
let (mut app, _) = construct_app_unit();
|
||||
|
||||
app.data.radarr_data.collections.set_items(vec![Collection {
|
||||
movies: Some(vec![CollectionMovie::default()]),
|
||||
..Collection::default()
|
||||
}]);
|
||||
|
||||
app
|
||||
.dispatch_by_radarr_block(&ActiveRadarrBlock::CollectionDetails)
|
||||
.await;
|
||||
|
||||
assert!(!app.is_loading);
|
||||
assert!(!app.data.radarr_data.collection_movies.items.is_empty());
|
||||
assert_eq!(app.tick_count, 0);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_collection_details_block_with_add_movie() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie);
|
||||
|
||||
@@ -1076,7 +1081,7 @@ mod tests {
|
||||
.dispatch_by_radarr_block(&ActiveRadarrBlock::CollectionDetails)
|
||||
.await;
|
||||
|
||||
assert!(!app.is_loading);
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::AddMovie.into()
|
||||
@@ -1111,7 +1116,7 @@ mod tests {
|
||||
.dispatch_by_radarr_block(&ActiveRadarrBlock::Movies)
|
||||
.await;
|
||||
|
||||
assert!(!app.is_loading);
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetMovies.into()
|
||||
@@ -1360,6 +1365,24 @@ mod tests {
|
||||
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_radarr_refresh_metadata() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.is_routing = true;
|
||||
|
||||
app.refresh_metadata().await;
|
||||
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetQualityProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetTags.into()
|
||||
);
|
||||
assert!(app.is_loading);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_radarr_on_tick_first_render() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
Reference in New Issue
Block a user