From ae506789abb10bb8f213b56a991c13eeb2e3aa23 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Fri, 12 Dec 2025 09:17:36 -0700 Subject: [PATCH 1/4] fix: Fixed breaking Sonarr Episode file API calls after recent Sonarr API update --- src/handlers/sonarr_handlers/sonarr_handler_test_utils.rs | 2 +- src/models/sonarr_models.rs | 2 +- src/network/sonarr_network/library/episodes/mod.rs | 8 +++++++- src/network/sonarr_network/sonarr_network_test_utils.rs | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/handlers/sonarr_handlers/sonarr_handler_test_utils.rs b/src/handlers/sonarr_handlers/sonarr_handler_test_utils.rs index e994020..0790d59 100644 --- a/src/handlers/sonarr_handlers/sonarr_handler_test_utils.rs +++ b/src/handlers/sonarr_handlers/sonarr_handler_test_utils.rs @@ -228,7 +228,7 @@ pub(in crate::handlers::sonarr_handlers) mod utils { path: "/nfs/tv/series/season 1/episode 1.mkv".to_owned(), size: 3543348019, quality: quality_wrapper(), - languages: vec![language()], + languages: vec![Some(language())], date_added: DateTime::from(DateTime::parse_from_rfc3339("2024-02-10T07:28:45Z").unwrap()), media_info: Some(media_info()), } diff --git a/src/models/sonarr_models.rs b/src/models/sonarr_models.rs index 1a9886e..36927bd 100644 --- a/src/models/sonarr_models.rs +++ b/src/models/sonarr_models.rs @@ -215,7 +215,7 @@ pub struct EpisodeFile { pub path: String, #[serde(deserialize_with = "super::from_i64")] pub size: i64, - pub languages: Vec, + pub languages: Vec>, pub quality: QualityWrapper, pub date_added: DateTime, pub media_info: Option, diff --git a/src/network/sonarr_network/library/episodes/mod.rs b/src/network/sonarr_network/library/episodes/mod.rs index 80c4557..1973c70 100644 --- a/src/network/sonarr_network/library/episodes/mod.rs +++ b/src/network/sonarr_network/library/episodes/mod.rs @@ -297,7 +297,13 @@ impl Network<'_, '_> { Date Added: {}", file.relative_path, file.path, - file.languages.first().unwrap_or(&Language::default()).name, + file + .languages + .first() + .unwrap_or(&Some(Language::default())) + .as_ref() + .unwrap_or(&Language::default()) + .name, file.date_added, ); diff --git a/src/network/sonarr_network/sonarr_network_test_utils.rs b/src/network/sonarr_network/sonarr_network_test_utils.rs index d3e0d4b..109de6e 100644 --- a/src/network/sonarr_network/sonarr_network_test_utils.rs +++ b/src/network/sonarr_network/sonarr_network_test_utils.rs @@ -180,7 +180,7 @@ pub(in crate::network::sonarr_network) mod test_utils { path: "/nfs/tv/series/season 1/episode 1.mkv".to_owned(), size: 3543348019, quality: quality_wrapper(), - languages: vec![language()], + languages: vec![Some(language())], date_added: DateTime::from(DateTime::parse_from_rfc3339("2024-02-10T07:28:45Z").unwrap()), media_info: Some(media_info()), } From 3e133fa147eca05ae73b42fddbf0cdcd27f9aa68 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Fri, 12 Dec 2025 09:20:05 -0700 Subject: [PATCH 2/4] refactor: Replaced all modulo usages of tick_until_poll with is_multiple_of --- src/app/mod.rs | 5 ++++- src/app/radarr/mod.rs | 2 +- src/app/sonarr/mod.rs | 2 +- src/ui/mod.rs | 2 +- src/ui/radarr_ui/blocklist/mod.rs | 2 +- src/ui/radarr_ui/collections/collection_details_ui.rs | 2 +- src/ui/radarr_ui/collections/mod.rs | 2 +- src/ui/radarr_ui/downloads/mod.rs | 2 +- src/ui/radarr_ui/indexers/test_all_indexers_ui.rs | 2 +- src/ui/radarr_ui/library/add_movie_ui.rs | 2 +- src/ui/radarr_ui/library/mod.rs | 2 +- src/ui/radarr_ui/library/movie_details_ui.rs | 4 ++-- src/ui/sonarr_ui/downloads/mod.rs | 2 +- src/ui/sonarr_ui/history/mod.rs | 2 +- src/ui/sonarr_ui/indexers/test_all_indexers_ui.rs | 2 +- src/ui/sonarr_ui/library/add_series_ui.rs | 2 +- src/ui/sonarr_ui/library/episode_details_ui.rs | 4 ++-- src/ui/sonarr_ui/library/mod.rs | 2 +- src/ui/sonarr_ui/library/season_details_ui.rs | 4 ++-- src/ui/sonarr_ui/library/series_details_ui.rs | 2 +- 20 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 7ff3a48..2eb28e6 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -160,7 +160,10 @@ impl App<'_> { } pub async fn on_tick(&mut self) { - if self.tick_count % self.tick_until_poll == 0 || self.is_routing || self.should_refresh { + if self.tick_count.is_multiple_of(self.tick_until_poll) + || self.is_routing + || self.should_refresh + { match self.get_current_route() { Route::Radarr(active_radarr_block, _) => self.radarr_on_tick(active_radarr_block).await, Route::Sonarr(active_sonarr_block, _) => self.sonarr_on_tick(active_sonarr_block).await, diff --git a/src/app/radarr/mod.rs b/src/app/radarr/mod.rs index b749d03..3e939ef 100644 --- a/src/app/radarr/mod.rs +++ b/src/app/radarr/mod.rs @@ -185,7 +185,7 @@ impl App<'_> { } } - if self.tick_count % self.tick_until_poll == 0 { + if self.tick_count.is_multiple_of(self.tick_until_poll) { self.refresh_radarr_metadata().await; } } diff --git a/src/app/sonarr/mod.rs b/src/app/sonarr/mod.rs index 620784d..c0c5eca 100644 --- a/src/app/sonarr/mod.rs +++ b/src/app/sonarr/mod.rs @@ -215,7 +215,7 @@ impl App<'_> { } } - if self.tick_count % self.tick_until_poll == 0 { + if self.tick_count.is_multiple_of(self.tick_until_poll) { self.refresh_sonarr_metadata().await; } } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 78a89f2..7068648 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -121,7 +121,7 @@ fn draw_error(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { app.error.scroll_left_or_reset( area.width as usize, true, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); let paragraph = Paragraph::new(Text::from(app.error.to_string().failure())) diff --git a/src/ui/radarr_ui/blocklist/mod.rs b/src/ui/radarr_ui/blocklist/mod.rs index c6b30f0..260cfbd 100644 --- a/src/ui/radarr_ui/blocklist/mod.rs +++ b/src/ui/radarr_ui/blocklist/mod.rs @@ -97,7 +97,7 @@ fn draw_blocklist_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { movie.title.scroll_left_or_reset( get_width_from_percentage(area, 20), current_selection == *blocklist_item, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); let languages_string = languages diff --git a/src/ui/radarr_ui/collections/collection_details_ui.rs b/src/ui/radarr_ui/collections/collection_details_ui.rs index 7b9337e..67b9436 100644 --- a/src/ui/radarr_ui/collections/collection_details_ui.rs +++ b/src/ui/radarr_ui/collections/collection_details_ui.rs @@ -90,7 +90,7 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) movie.title.scroll_left_or_reset( get_width_from_percentage(table_area, 20), current_selection == *movie, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); let (hours, minutes) = convert_runtime(movie.runtime); let imdb_rating = movie diff --git a/src/ui/radarr_ui/collections/mod.rs b/src/ui/radarr_ui/collections/mod.rs index 90034e7..3ab7f97 100644 --- a/src/ui/radarr_ui/collections/mod.rs +++ b/src/ui/radarr_ui/collections/mod.rs @@ -71,7 +71,7 @@ pub(super) fn draw_collections(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) collection.title.scroll_left_or_reset( get_width_from_percentage(area, 25), *collection == current_selection, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); let monitored = if collection.monitored { "🏷" } else { "" }; let search_on_add = if collection.search_on_add { diff --git a/src/ui/radarr_ui/downloads/mod.rs b/src/ui/radarr_ui/downloads/mod.rs index 6f27f39..3766d1b 100644 --- a/src/ui/radarr_ui/downloads/mod.rs +++ b/src/ui/radarr_ui/downloads/mod.rs @@ -88,7 +88,7 @@ fn draw_downloads(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { output_path.as_ref().unwrap().scroll_left_or_reset( get_width_from_percentage(area, 18), current_selection == *download_record, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); } diff --git a/src/ui/radarr_ui/indexers/test_all_indexers_ui.rs b/src/ui/radarr_ui/indexers/test_all_indexers_ui.rs index b24375c..00b8b5d 100644 --- a/src/ui/radarr_ui/indexers/test_all_indexers_ui.rs +++ b/src/ui/radarr_ui/indexers/test_all_indexers_ui.rs @@ -46,7 +46,7 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are result.validation_failures.scroll_left_or_reset( get_width_from_percentage(area, 86), *result == current_selection, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); let pass_fail = if result.is_valid { "✔" } else { "❌" }; let row = Row::new(vec![ diff --git a/src/ui/radarr_ui/library/add_movie_ui.rs b/src/ui/radarr_ui/library/add_movie_ui.rs index f981e52..b28663d 100644 --- a/src/ui/radarr_ui/library/add_movie_ui.rs +++ b/src/ui/radarr_ui/library/add_movie_ui.rs @@ -134,7 +134,7 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { movie.title.scroll_left_or_reset( get_width_from_percentage(area, 27), *movie == current_selection, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); Row::new(vec![ diff --git a/src/ui/radarr_ui/library/mod.rs b/src/ui/radarr_ui/library/mod.rs index c683fd0..ddf8697 100644 --- a/src/ui/radarr_ui/library/mod.rs +++ b/src/ui/radarr_ui/library/mod.rs @@ -90,7 +90,7 @@ fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { movie.title.scroll_left_or_reset( get_width_from_percentage(area, 27), *movie == current_selection, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); let monitored = if movie.monitored { "🏷" } else { "" }; let studio = movie.studio.clone().unwrap_or_default(); diff --git a/src/ui/radarr_ui/library/movie_details_ui.rs b/src/ui/radarr_ui/library/movie_details_ui.rs index d7291c0..38ef583 100644 --- a/src/ui/radarr_ui/library/movie_details_ui.rs +++ b/src/ui/radarr_ui/library/movie_details_ui.rs @@ -241,7 +241,7 @@ fn draw_movie_history(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { movie_history_item.source_title.scroll_left_or_reset( get_width_from_percentage(area, 34), current_selection == *movie_history_item, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); Row::new(vec![ @@ -393,7 +393,7 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { get_width_from_percentage(area, 30), current_selection == *release && current_route != ActiveRadarrBlock::ManualSearchConfirmPrompt.into(), - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); let size = convert_to_gb(*size); let rejected_str = if *rejected { "⛔" } else { "" }; diff --git a/src/ui/sonarr_ui/downloads/mod.rs b/src/ui/sonarr_ui/downloads/mod.rs index 01362b1..643716f 100644 --- a/src/ui/sonarr_ui/downloads/mod.rs +++ b/src/ui/sonarr_ui/downloads/mod.rs @@ -88,7 +88,7 @@ fn draw_downloads(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { output_path.as_ref().unwrap().scroll_left_or_reset( get_width_from_percentage(area, 18), current_selection == *download_record, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); } diff --git a/src/ui/sonarr_ui/history/mod.rs b/src/ui/sonarr_ui/history/mod.rs index ea85300..c62a31c 100644 --- a/src/ui/sonarr_ui/history/mod.rs +++ b/src/ui/sonarr_ui/history/mod.rs @@ -68,7 +68,7 @@ fn draw_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { source_title.scroll_left_or_reset( get_width_from_percentage(area, 40), current_selection == *history_item, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); Row::new(vec![ diff --git a/src/ui/sonarr_ui/indexers/test_all_indexers_ui.rs b/src/ui/sonarr_ui/indexers/test_all_indexers_ui.rs index 1712a2e..5e53cac 100644 --- a/src/ui/sonarr_ui/indexers/test_all_indexers_ui.rs +++ b/src/ui/sonarr_ui/indexers/test_all_indexers_ui.rs @@ -44,7 +44,7 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are result.validation_failures.scroll_left_or_reset( get_width_from_percentage(area, 86), *result == current_selection, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); let pass_fail = if result.is_valid { "✔" } else { "❌" }; let row = Row::new(vec![ diff --git a/src/ui/sonarr_ui/library/add_series_ui.rs b/src/ui/sonarr_ui/library/add_series_ui.rs index 2754ad7..f601532 100644 --- a/src/ui/sonarr_ui/library/add_series_ui.rs +++ b/src/ui/sonarr_ui/library/add_series_ui.rs @@ -120,7 +120,7 @@ fn draw_add_series_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { series.title.scroll_left_or_reset( get_width_from_percentage(area, 27), *series == current_selection, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); Row::new(vec![ diff --git a/src/ui/sonarr_ui/library/episode_details_ui.rs b/src/ui/sonarr_ui/library/episode_details_ui.rs index 786edf7..0c12b01 100644 --- a/src/ui/sonarr_ui/library/episode_details_ui.rs +++ b/src/ui/sonarr_ui/library/episode_details_ui.rs @@ -271,7 +271,7 @@ fn draw_episode_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) source_title.scroll_left_or_reset( get_width_from_percentage(area, 40), current_selection == *history_item, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); Row::new(vec![ @@ -415,7 +415,7 @@ fn draw_episode_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { get_width_from_percentage(area, 30), current_selection == *release && active_sonarr_block != ActiveSonarrBlock::ManualEpisodeSearchConfirmPrompt, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); let size = convert_to_gb(*size); let rejected_str = if *rejected { "⛔" } else { "" }; diff --git a/src/ui/sonarr_ui/library/mod.rs b/src/ui/sonarr_ui/library/mod.rs index fa2d6da..2140743 100644 --- a/src/ui/sonarr_ui/library/mod.rs +++ b/src/ui/sonarr_ui/library/mod.rs @@ -95,7 +95,7 @@ fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { series.title.scroll_left_or_reset( get_width_from_percentage(area, 23), *series == current_selection, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); let monitored = if series.monitored { "🏷" } else { "" }; let certification = series.certification.clone().unwrap_or_default(); diff --git a/src/ui/sonarr_ui/library/season_details_ui.rs b/src/ui/sonarr_ui/library/season_details_ui.rs index 6a8ca36..638d260 100644 --- a/src/ui/sonarr_ui/library/season_details_ui.rs +++ b/src/ui/sonarr_ui/library/season_details_ui.rs @@ -267,7 +267,7 @@ fn draw_season_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { source_title.scroll_left_or_reset( get_width_from_percentage(area, 40), current_selection == *history_item, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); Row::new(vec![ @@ -372,7 +372,7 @@ fn draw_season_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { get_width_from_percentage(area, 30), current_selection == *release && active_sonarr_block != ActiveSonarrBlock::ManualSeasonSearchConfirmPrompt, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); let size = convert_to_gb(*size); let rejected_str = if *rejected { "⛔" } else { "" }; diff --git a/src/ui/sonarr_ui/library/series_details_ui.rs b/src/ui/sonarr_ui/library/series_details_ui.rs index b0986e4..055db3e 100644 --- a/src/ui/sonarr_ui/library/series_details_ui.rs +++ b/src/ui/sonarr_ui/library/series_details_ui.rs @@ -315,7 +315,7 @@ fn draw_series_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { source_title.scroll_left_or_reset( get_width_from_percentage(area, 40), current_selection == *history_item, - app.tick_count % app.ticks_until_scroll == 0, + app.tick_count.is_multiple_of(app.ticks_until_scroll), ); Row::new(vec![ From b69973b9afc43d0459a31f34ee424098fbaac02b Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Fri, 12 Dec 2025 09:54:15 -0700 Subject: [PATCH 3/4] ci: Fix the commitizen version issue [skip ci] --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5dc4e6b..82de4e0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,8 +50,8 @@ jobs: - name: Install Commitizen run: | - python -m pip install --upgrade pip - pip install commitizen + python3 -m pip install --upgrade pip + pip3 install commitizen npm install -g conventional-changelog-cli - name: Configure Git user From ff4eb8ca981adff47a4e1d307dd0929e4577dad2 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Fri, 12 Dec 2025 10:00:11 -0700 Subject: [PATCH 4/4] ci: Specify commitizen version [skip ci] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 82de4e0..4e89bfb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -51,7 +51,7 @@ jobs: - name: Install Commitizen run: | python3 -m pip install --upgrade pip - pip3 install commitizen + pip3 install commitizen==4.8.3 npm install -g conventional-changelog-cli - name: Configure Git user