feat(handlers): Support for toggling the monitoring status of a season in the Sonarr UI
This commit is contained in:
@@ -27,7 +27,7 @@ mod test {
|
|||||||
#[case(DEFAULT_KEYBINDINGS.tasks, Key::Char('t'), "tasks")]
|
#[case(DEFAULT_KEYBINDINGS.tasks, Key::Char('t'), "tasks")]
|
||||||
#[case(DEFAULT_KEYBINDINGS.test, Key::Char('t'), "test")]
|
#[case(DEFAULT_KEYBINDINGS.test, Key::Char('t'), "test")]
|
||||||
#[case(DEFAULT_KEYBINDINGS.test_all, Key::Char('T'), "test all")]
|
#[case(DEFAULT_KEYBINDINGS.test_all, Key::Char('T'), "test all")]
|
||||||
#[case(DEFAULT_KEYBINDINGS.test_all, Key::Char('m'), "toggle monitoring")]
|
#[case(DEFAULT_KEYBINDINGS.toggle_monitoring, Key::Char('m'), "toggle monitoring")]
|
||||||
#[case(DEFAULT_KEYBINDINGS.refresh, Key::Ctrl('r'), "refresh")]
|
#[case(DEFAULT_KEYBINDINGS.refresh, Key::Ctrl('r'), "refresh")]
|
||||||
#[case(DEFAULT_KEYBINDINGS.update, Key::Char('u'), "update")]
|
#[case(DEFAULT_KEYBINDINGS.update, Key::Char('u'), "update")]
|
||||||
#[case(DEFAULT_KEYBINDINGS.home, Key::Home, "home")]
|
#[case(DEFAULT_KEYBINDINGS.home, Key::Home, "home")]
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ impl<'a> App<'a> {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
ActiveSonarrBlock::SeriesDetails => {
|
ActiveSonarrBlock::SeriesDetails => {
|
||||||
|
self
|
||||||
|
.dispatch_network_event(SonarrEvent::ListSeries.into())
|
||||||
|
.await;
|
||||||
self.is_loading = true;
|
self.is_loading = true;
|
||||||
self.populate_seasons_table().await;
|
self.populate_seasons_table().await;
|
||||||
self.is_loading = false;
|
self.is_loading = false;
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_dispatch_by_series_details_block() {
|
async fn test_dispatch_by_series_details_block() {
|
||||||
let (mut app, _) = construct_app_unit();
|
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||||
|
|
||||||
app.data.sonarr_data.series.set_items(vec![Series {
|
app.data.sonarr_data.series.set_items(vec![Series {
|
||||||
seasons: Some(vec![Season::default()]),
|
seasons: Some(vec![Season::default()]),
|
||||||
@@ -68,6 +68,10 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
assert!(!app.is_loading);
|
assert!(!app.is_loading);
|
||||||
|
assert_eq!(
|
||||||
|
sync_network_rx.recv().await.unwrap(),
|
||||||
|
SonarrEvent::ListSeries.into()
|
||||||
|
);
|
||||||
assert!(!app.data.sonarr_data.seasons.items.is_empty());
|
assert!(!app.data.sonarr_data.seasons.items.is_empty());
|
||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||||
|
|||||||
@@ -257,6 +257,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeriesDetailsHandler
|
|||||||
self.app.data.sonarr_data.selected_block =
|
self.app.data.sonarr_data.selected_block =
|
||||||
BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS);
|
BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS);
|
||||||
}
|
}
|
||||||
|
_ if key == DEFAULT_KEYBINDINGS.toggle_monitoring.key => {
|
||||||
|
self.app.data.sonarr_data.prompt_confirm = true;
|
||||||
|
self.app.data.sonarr_data.prompt_confirm_action =
|
||||||
|
Some(SonarrEvent::ToggleSeasonMonitoring(None));
|
||||||
|
|
||||||
|
self.app.pop_and_push_navigation_stack(self.active_sonarr_block.into());
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
ActiveSonarrBlock::SeriesHistory => match self.key {
|
ActiveSonarrBlock::SeriesHistory => match self.key {
|
||||||
|
|||||||
@@ -375,6 +375,56 @@ mod tests {
|
|||||||
assert!(app.data.sonarr_data.edit_series_modal.is_none());
|
assert!(app.data.sonarr_data.edit_series_modal.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_toggle_monitoring_key() {
|
||||||
|
let mut app = App::default();
|
||||||
|
let mut series_history = StatefulTable::default();
|
||||||
|
series_history.set_items(vec![SonarrHistoryItem::default()]);
|
||||||
|
app.data.sonarr_data.series_history = Some(series_history);
|
||||||
|
app.push_navigation_stack(ActiveSonarrBlock::SeriesDetails.into());
|
||||||
|
app.is_routing = false;
|
||||||
|
|
||||||
|
SeriesDetailsHandler::with(
|
||||||
|
DEFAULT_KEYBINDINGS.toggle_monitoring.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveSonarrBlock::SeriesDetails,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
app.get_current_route(),
|
||||||
|
ActiveSonarrBlock::SeriesDetails.into()
|
||||||
|
);
|
||||||
|
assert!(app.data.sonarr_data.prompt_confirm);
|
||||||
|
assert!(app.is_routing);
|
||||||
|
assert_eq!(
|
||||||
|
app.data.sonarr_data.prompt_confirm_action,
|
||||||
|
Some(SonarrEvent::ToggleSeasonMonitoring(None))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_toggle_monitoring_key_no_op_when_not_ready() {
|
||||||
|
let mut app = App::default();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(ActiveSonarrBlock::SeriesDetails.into());
|
||||||
|
app.is_routing = false;
|
||||||
|
|
||||||
|
SeriesDetailsHandler::with(
|
||||||
|
DEFAULT_KEYBINDINGS.toggle_monitoring.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveSonarrBlock::SeriesDetails,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(app.get_current_route(), ActiveSonarrBlock::SeriesDetails.into());
|
||||||
|
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||||
|
assert!(app.data.sonarr_data.prompt_confirm_action.is_none());
|
||||||
|
assert!(!app.is_routing);
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn test_auto_search_key(
|
fn test_auto_search_key(
|
||||||
#[values(ActiveSonarrBlock::SeriesDetails, ActiveSonarrBlock::SeriesHistory)]
|
#[values(ActiveSonarrBlock::SeriesDetails, ActiveSonarrBlock::SeriesHistory)]
|
||||||
|
|||||||
@@ -879,7 +879,7 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
|
|
||||||
info!("Constructing edit indexer body");
|
info!("Constructing edit indexer body");
|
||||||
|
|
||||||
let mut detailed_indexer_body: Value = serde_json::from_str(&response).unwrap();
|
let mut detailed_indexer_body: Value = serde_json::from_str(&response)?;
|
||||||
|
|
||||||
let (
|
let (
|
||||||
name,
|
name,
|
||||||
@@ -1127,7 +1127,7 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
|
|
||||||
info!("Constructing edit series body");
|
info!("Constructing edit series body");
|
||||||
|
|
||||||
let mut detailed_series_body: Value = serde_json::from_str(&response).unwrap();
|
let mut detailed_series_body: Value = serde_json::from_str(&response)?;
|
||||||
let (
|
let (
|
||||||
monitored,
|
monitored,
|
||||||
use_season_folders,
|
use_season_folders,
|
||||||
@@ -1294,7 +1294,7 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (series_id, _) = self.extract_series_id(series_id).await;
|
let (series_id, _) = self.extract_series_id(series_id).await;
|
||||||
let (season_number, _) = self.extract_season_number(season_number).await;
|
if let Ok((season_number, _)) = self.extract_season_number(season_number).await {
|
||||||
info!("Toggling season monitoring for season {season_number} in series with ID: {series_id}");
|
info!("Toggling season monitoring for season {season_number} in series with ID: {series_id}");
|
||||||
info!("Fetching series details for series with ID: {series_id}");
|
info!("Fetching series details for series with ID: {series_id}");
|
||||||
|
|
||||||
@@ -1318,7 +1318,8 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
|
|
||||||
info!("Constructing toggle season monitoring body");
|
info!("Constructing toggle season monitoring body");
|
||||||
|
|
||||||
let mut detailed_series_body: Value = serde_json::from_str(&response).unwrap();
|
let mut detailed_series_body: Value =
|
||||||
|
serde_json::from_str(&response).expect("Request for detailed series body was interrupted");
|
||||||
let monitored = detailed_series_body
|
let monitored = detailed_series_body
|
||||||
.get("seasons")
|
.get("seasons")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -1358,6 +1359,10 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
self
|
self
|
||||||
.handle_request::<Value, ()>(request_props, |_, _| ())
|
.handle_request::<Value, ()>(request_props, |_, _| ())
|
||||||
.await
|
.await
|
||||||
|
} else {
|
||||||
|
warn!("Season number was not provided. Aborting...");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_all_sonarr_indexer_settings(&mut self) -> Result<IndexerSettings> {
|
async fn get_all_sonarr_indexer_settings(&mut self) -> Result<IndexerSettings> {
|
||||||
@@ -2003,7 +2008,7 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (series_id, series_id_param) = self.extract_series_id(series_id).await;
|
let (series_id, series_id_param) = self.extract_series_id(series_id).await;
|
||||||
let (season_number, season_number_param) = self.extract_season_number(season_number).await;
|
let (season_number, season_number_param) = self.extract_season_number(season_number).await?;
|
||||||
|
|
||||||
info!("Fetching releases for series with ID: {series_id} and season number: {season_number}");
|
info!("Fetching releases for series with ID: {series_id} and season number: {season_number}");
|
||||||
|
|
||||||
@@ -2053,7 +2058,7 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (series_id, series_id_param) = self.extract_series_id(series_id).await;
|
let (series_id, series_id_param) = self.extract_series_id(series_id).await;
|
||||||
let (season_number, season_number_param) = self.extract_season_number(season_number).await;
|
let (season_number, season_number_param) = self.extract_season_number(season_number).await?;
|
||||||
|
|
||||||
info!("Fetching history for series with ID: {series_id} and season number: {season_number}");
|
info!("Fetching history for series with ID: {series_id} and season number: {season_number}");
|
||||||
|
|
||||||
@@ -2629,7 +2634,7 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (series_id, _) = self.extract_series_id(series_id).await;
|
let (series_id, _) = self.extract_series_id(series_id).await;
|
||||||
let (season_number, _) = self.extract_season_number(season_number).await;
|
let (season_number, _) = self.extract_season_number(season_number).await?;
|
||||||
info!("Searching indexers for series with ID: {series_id} and season number: {season_number}");
|
info!("Searching indexers for series with ID: {series_id} and season number: {season_number}");
|
||||||
|
|
||||||
let body = SonarrCommandBody {
|
let body = SonarrCommandBody {
|
||||||
@@ -2767,11 +2772,11 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
(series_id, format!("seriesId={series_id}"))
|
(series_id, format!("seriesId={series_id}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn extract_season_number(&mut self, season_number: Option<i64>) -> (i64, String) {
|
async fn extract_season_number(&mut self, season_number: Option<i64>) -> Result<(i64, String)> {
|
||||||
let season_number = if let Some(number) = season_number {
|
if let Some(number) = season_number {
|
||||||
number
|
Ok((number, format!("seasonNumber={number}")))
|
||||||
} else {
|
} else if !self.app.lock().await.data.sonarr_data.seasons.is_empty() {
|
||||||
self
|
let season_number = self
|
||||||
.app
|
.app
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
@@ -2779,9 +2784,11 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
.sonarr_data
|
.sonarr_data
|
||||||
.seasons
|
.seasons
|
||||||
.current_selection()
|
.current_selection()
|
||||||
.season_number
|
.season_number;
|
||||||
};
|
Ok((season_number, format!("seasonNumber={season_number}")))
|
||||||
(season_number, format!("seasonNumber={season_number}"))
|
} else {
|
||||||
|
Err(anyhow!("No season number provided"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn extract_episode_id(&mut self, episode_id: Option<i64>) -> i64 {
|
async fn extract_episode_id(&mut self, episode_id: Option<i64>) -> i64 {
|
||||||
|
|||||||
@@ -5129,12 +5129,14 @@ mod test {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let mut filtered_series = StatefulTable::default();
|
let mut filtered_series = StatefulTable::default();
|
||||||
|
filtered_series.set_items(vec![Series::default()]);
|
||||||
filtered_series.set_filtered_items(vec![Series {
|
filtered_series.set_filtered_items(vec![Series {
|
||||||
id: 1,
|
id: 1,
|
||||||
..Series::default()
|
..Series::default()
|
||||||
}]);
|
}]);
|
||||||
app_arc.lock().await.data.sonarr_data.series = filtered_series;
|
app_arc.lock().await.data.sonarr_data.series = filtered_series;
|
||||||
let mut filtered_seasons = StatefulTable::default();
|
let mut filtered_seasons = StatefulTable::default();
|
||||||
|
filtered_seasons.set_items(vec![Season::default()]);
|
||||||
filtered_seasons.set_filtered_items(vec![Season {
|
filtered_seasons.set_filtered_items(vec![Season {
|
||||||
season_number: 1,
|
season_number: 1,
|
||||||
..Season::default()
|
..Season::default()
|
||||||
@@ -7024,12 +7026,14 @@ mod test {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let mut filtered_series = StatefulTable::default();
|
let mut filtered_series = StatefulTable::default();
|
||||||
|
filtered_series.set_items(vec![Series::default()]);
|
||||||
filtered_series.set_filtered_items(vec![Series {
|
filtered_series.set_filtered_items(vec![Series {
|
||||||
id: 1,
|
id: 1,
|
||||||
..Series::default()
|
..Series::default()
|
||||||
}]);
|
}]);
|
||||||
app_arc.lock().await.data.sonarr_data.series = filtered_series;
|
app_arc.lock().await.data.sonarr_data.series = filtered_series;
|
||||||
let mut filtered_seasons = StatefulTable::default();
|
let mut filtered_seasons = StatefulTable::default();
|
||||||
|
filtered_seasons.set_items(vec![Season::default()]);
|
||||||
filtered_seasons.set_filtered_items(vec![Season {
|
filtered_seasons.set_filtered_items(vec![Season {
|
||||||
season_number: 1,
|
season_number: 1,
|
||||||
..Season::default()
|
..Season::default()
|
||||||
@@ -7341,7 +7345,7 @@ mod test {
|
|||||||
}]);
|
}]);
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
|
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
|
||||||
|
|
||||||
let (id, season_number_param) = network.extract_season_number(None).await;
|
let (id, season_number_param) = network.extract_season_number(None).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(id, 1);
|
assert_eq!(id, 1);
|
||||||
assert_str_eq!(season_number_param, "seasonNumber=1");
|
assert_str_eq!(season_number_param, "seasonNumber=1");
|
||||||
@@ -7361,7 +7365,7 @@ mod test {
|
|||||||
..Season::default()
|
..Season::default()
|
||||||
}]);
|
}]);
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
|
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
|
||||||
let (id, season_number_param) = network.extract_season_number(Some(2)).await;
|
let (id, season_number_param) = network.extract_season_number(Some(2)).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(id, 2);
|
assert_eq!(id, 2);
|
||||||
assert_str_eq!(season_number_param, "seasonNumber=2");
|
assert_str_eq!(season_number_param, "seasonNumber=2");
|
||||||
@@ -7371,6 +7375,7 @@ mod test {
|
|||||||
async fn test_extract_season_number_filtered_seasons() {
|
async fn test_extract_season_number_filtered_seasons() {
|
||||||
let app_arc = Arc::new(Mutex::new(App::default()));
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
let mut filtered_seasons = StatefulTable::default();
|
let mut filtered_seasons = StatefulTable::default();
|
||||||
|
filtered_seasons.set_items(vec![Season::default()]);
|
||||||
filtered_seasons.set_filtered_items(vec![Season {
|
filtered_seasons.set_filtered_items(vec![Season {
|
||||||
season_number: 1,
|
season_number: 1,
|
||||||
..Season::default()
|
..Season::default()
|
||||||
@@ -7378,12 +7383,21 @@ mod test {
|
|||||||
app_arc.lock().await.data.sonarr_data.seasons = filtered_seasons;
|
app_arc.lock().await.data.sonarr_data.seasons = filtered_seasons;
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
|
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
|
||||||
|
|
||||||
let (id, season_number_param) = network.extract_season_number(None).await;
|
let (id, season_number_param) = network.extract_season_number(None).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(id, 1);
|
assert_eq!(id, 1);
|
||||||
assert_str_eq!(season_number_param, "seasonNumber=1");
|
assert_str_eq!(season_number_param, "seasonNumber=1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_extract_season_number_empty_seasons_table() {
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
|
||||||
|
let season_number = network.extract_season_number(None).await;
|
||||||
|
|
||||||
|
assert!(season_number.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_extract_episode_id() {
|
async fn test_extract_episode_id() {
|
||||||
let app_arc = Arc::new(Mutex::new(App::default()));
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
|||||||
Reference in New Issue
Block a user