Lidarr support #1

Merged
Dark-Alex-17 merged 61 commits from lidarr into main 2026-01-21 21:30:47 +00:00
4 changed files with 140 additions and 144 deletions
Showing only changes of commit 7add62b245 - Show all commits
@@ -1,4 +1,4 @@
use crate::models::servarr_data::sonarr::modals::{EpisodeDetailsModal, SeasonDetailsModal};
use crate::models::servarr_data::sonarr::modals::SeasonDetailsModal;
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
use crate::models::servarr_models::Language;
use crate::models::sonarr_models::{
@@ -65,10 +65,6 @@ impl Network<'_, '_> {
app.get_current_route(),
Route::Sonarr(ActiveSonarrBlock::EpisodesSortPrompt, _)
) {
if app.data.sonarr_data.season_details_modal.is_none() {
app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default());
}
let season_episodes_vec = if !app.data.sonarr_data.seasons.is_empty() {
let season_number = app
.data
@@ -85,22 +81,14 @@ impl Network<'_, '_> {
episode_vec
};
app
let season_details_modal = app
.data
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
.episodes
.set_items(season_episodes_vec);
app
.data
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
.episodes
.apply_sorting_toggle(false);
.get_or_insert_default();
season_details_modal.episodes.set_items(season_episodes_vec);
season_details_modal.episodes.apply_sorting_toggle(false);
}
})
.await
@@ -125,16 +113,13 @@ impl Network<'_, '_> {
self
.handle_request::<(), Vec<EpisodeFile>>(request_props, |episode_file_vec, mut app| {
if app.data.sonarr_data.season_details_modal.is_none() {
app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default());
}
app
let season_details_modal = app
.data
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
.get_or_insert_default();
season_details_modal
.episode_files
.set_items(episode_file_vec);
})
@@ -156,50 +141,19 @@ impl Network<'_, '_> {
self
.handle_request::<(), SonarrHistoryWrapper>(request_props, |history_response, mut app| {
if app.data.sonarr_data.season_details_modal.is_none() {
app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default());
}
if app
let season_details_modal = app
.data
.sonarr_data
.season_details_modal
.as_ref()
.unwrap()
.get_or_insert_default();
let episode_details_modal = season_details_modal
.episode_details_modal
.is_none()
{
app
.data
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
.episode_details_modal = Some(EpisodeDetailsModal::default());
}
.get_or_insert_default();
let mut history_vec = history_response.records;
history_vec.sort_by(|a, b| a.id.cmp(&b.id));
app
.data
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
.episode_details_modal
.as_mut()
.unwrap()
.episode_history
.set_items(history_vec);
app
.data
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
.episode_details_modal
.as_mut()
.unwrap()
episode_details_modal.episode_history.set_items(history_vec);
episode_details_modal
.episode_history
.apply_sorting_toggle(false);
})
@@ -231,24 +185,6 @@ impl Network<'_, '_> {
app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default());
}
if app
.data
.sonarr_data
.season_details_modal
.as_mut()
.expect("Season details modal is empty")
.episode_details_modal
.is_none()
{
app
.data
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
.episode_details_modal = Some(EpisodeDetailsModal::default());
}
let Episode {
id,
title,
@@ -271,10 +207,9 @@ impl Network<'_, '_> {
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
.expect("Season details modal is empty")
.episode_details_modal
.as_mut()
.unwrap();
.get_or_insert_default();
episode_details_modal.episode_details = ScrollableText::with_string(formatdoc!(
"
Title: {}
@@ -366,42 +301,21 @@ impl Network<'_, '_> {
self
.handle_request::<(), Vec<SonarrRelease>>(request_props, |release_vec, mut app| {
if app.data.sonarr_data.season_details_modal.is_none() {
app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default());
}
let season_details_modal = app
.data
.sonarr_data
.season_details_modal
.get_or_insert_default();
let episode_details_modal = season_details_modal
.episode_details_modal
.get_or_insert_default();
let episode_releases_vec = release_vec
.into_iter()
.filter(|release| !release.full_season)
.collect();
if app
.data
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
.episode_details_modal
.is_none()
{
app
.data
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
.episode_details_modal = Some(EpisodeDetailsModal::default());
}
app
.data
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
.episode_details_modal
.as_mut()
.unwrap()
episode_details_modal
.episode_releases
.set_items(episode_releases_vec);
})
@@ -821,8 +821,7 @@ mod tests {
.path("/1")
.build_for(SonarrEvent::GetEpisodeDetails(1))
.await;
let mut episode_details_modal = EpisodeDetailsModal::default();
episode_details_modal.episode_details_tabs.next();
let episode_details_modal = EpisodeDetailsModal::default();
let mut season_details_modal = SeasonDetailsModal::default();
season_details_modal.episodes.set_items(vec![episode()]);
season_details_modal.episode_details_modal = Some(episode_details_modal);
@@ -868,7 +867,7 @@ mod tests {
.unwrap()
.episode_details_tabs
.get_active_route(),
ActiveSonarrBlock::EpisodeHistory.into()
ActiveSonarrBlock::EpisodeDetails.into()
);
assert_eq!(episode, response);
@@ -1,4 +1,5 @@
use crate::models::servarr_data::sonarr::modals::SeasonDetailsModal;
use crate::models::Route;
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
use crate::models::sonarr_models::{SonarrCommandBody, SonarrHistoryItem, SonarrRelease};
use crate::network::sonarr_network::SonarrEvent;
use crate::network::{Network, RequestMethod};
@@ -111,21 +112,18 @@ impl Network<'_, '_> {
self
.handle_request::<(), Vec<SonarrRelease>>(request_props, |release_vec, mut app| {
if app.data.sonarr_data.season_details_modal.is_none() {
app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default());
}
let season_details_modal = app
.data
.sonarr_data
.season_details_modal
.get_or_insert_default();
let season_releases_vec = release_vec
.into_iter()
.filter(|release| release.full_season)
.collect();
app
.data
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
season_details_modal
.season_releases
.set_items(season_releases_vec);
})
@@ -147,28 +145,25 @@ impl Network<'_, '_> {
self
.handle_request::<(), Vec<SonarrHistoryItem>>(request_props, |history_items, mut app| {
if app.data.sonarr_data.season_details_modal.is_none() {
app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default());
}
let is_sorting = matches!(
app.get_current_route(),
Route::Sonarr(ActiveSonarrBlock::SeasonHistorySortPrompt, _)
);
let season_details_modal = app
.data
.sonarr_data
.season_details_modal
.get_or_insert_default();
if !is_sorting {
let mut history_vec = history_items;
history_vec.sort_by(|a, b| a.id.cmp(&b.id));
app
.data
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
.season_history
.set_items(history_vec);
app
.data
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
season_details_modal.season_history.set_items(history_vec);
season_details_modal
.season_history
.apply_sorting_toggle(false);
}
})
.await
}
@@ -1,6 +1,7 @@
#[cfg(test)]
mod tests {
use crate::models::servarr_data::sonarr::modals::SeasonDetailsModal;
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
use crate::models::sonarr_models::{SonarrHistoryItem, SonarrRelease, SonarrSerdeable};
use crate::network::NetworkResource;
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
@@ -466,6 +467,93 @@ mod tests {
assert_eq!(history, response);
}
#[tokio::test]
async fn test_handle_get_sonarr_season_history_event_no_op_when_user_is_selecting_sort_option() {
let history_json = json!([{
"id": 123,
"sourceTitle": "z episode",
"episodeId": 1007,
"quality": { "quality": { "name": "Bluray-1080p" } },
"languages": [{ "id": 1, "name": "English" }],
"date": "2024-02-10T07:28:45Z",
"eventType": "grabbed",
"data": {
"droppedPath": "/nfs/nzbget/completed/series/Coolness/something.cool.mkv",
"importedPath": "/nfs/tv/Coolness/Season 1/Coolness - S01E01 - Something Cool Bluray-1080p.mkv"
}
},
{
"id": 456,
"sourceTitle": "A Episode",
"episodeId": 2001,
"quality": { "quality": { "name": "Bluray-1080p" } },
"languages": [{ "id": 1, "name": "English" }],
"date": "2024-02-10T07:28:45Z",
"eventType": "grabbed",
"data": {
"droppedPath": "/nfs/nzbget/completed/series/Coolness/something.cool.mkv",
"importedPath": "/nfs/tv/Coolness/Season 1/Coolness - S01E01 - Something Cool Bluray-1080p.mkv"
}
}]);
let response: Vec<SonarrHistoryItem> = serde_json::from_value(history_json.clone()).unwrap();
let (mock, app, _server) = MockServarrApi::get()
.returns(history_json)
.query("seriesId=1&seasonNumber=1")
.build_for(SonarrEvent::GetSeasonHistory((1, 1)))
.await;
app.lock().await.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default());
app
.lock()
.await
.data
.sonarr_data
.season_details_modal
.as_mut()
.unwrap()
.season_history
.sort_asc = true;
app.lock().await.server_tabs.next();
app
.lock()
.await
.push_navigation_stack(ActiveSonarrBlock::SeasonHistorySortPrompt.into());
let mut network = test_network(&app);
let SonarrSerdeable::SonarrHistoryItems(history) = network
.handle_sonarr_event(SonarrEvent::GetSeasonHistory((1, 1)))
.await
.unwrap()
else {
panic!("Expected SonarrHistoryItems")
};
mock.assert_async().await;
assert_is_empty!(
app
.lock()
.await
.data
.sonarr_data
.season_details_modal
.as_ref()
.unwrap()
.season_history
.items
);
assert!(
app
.lock()
.await
.data
.sonarr_data
.season_details_modal
.as_ref()
.unwrap()
.season_history
.sort_asc
);
assert_eq!(history, response);
}
#[tokio::test]
async fn test_handle_trigger_automatic_season_search_event() {
let (mock, app, _server) = MockServarrApi::post()