feat: CLI and TUI support for track history and track details in Lidarr
This commit is contained in:
@@ -8,6 +8,7 @@ use crate::models::Route;
|
|||||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||||
ADD_ARTIST_BLOCKS, ADD_ROOT_FOLDER_BLOCKS, ALBUM_DETAILS_BLOCKS, ARTIST_DETAILS_BLOCKS,
|
ADD_ARTIST_BLOCKS, ADD_ROOT_FOLDER_BLOCKS, ALBUM_DETAILS_BLOCKS, ARTIST_DETAILS_BLOCKS,
|
||||||
ActiveLidarrBlock, EDIT_ARTIST_BLOCKS, EDIT_INDEXER_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
ActiveLidarrBlock, EDIT_ARTIST_BLOCKS, EDIT_INDEXER_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
||||||
|
TRACK_DETAILS_BLOCKS,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -103,8 +104,8 @@ pub static ALBUM_DETAILS_CONTEXT_CLUES: [ContextClue; 6] = [
|
|||||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||||
),
|
),
|
||||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||||
(DEFAULT_KEYBINDINGS.submit, "episode details"),
|
(DEFAULT_KEYBINDINGS.submit, "track details"),
|
||||||
(DEFAULT_KEYBINDINGS.delete, "delete episode"),
|
(DEFAULT_KEYBINDINGS.delete, "delete track"),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub static ALBUM_HISTORY_CONTEXT_CLUES: [ContextClue; 7] = [
|
pub static ALBUM_HISTORY_CONTEXT_CLUES: [ContextClue; 7] = [
|
||||||
@@ -137,6 +138,26 @@ pub static MANUAL_ALBUM_SEARCH_CONTEXT_CLUES: [ContextClue; 5] = [
|
|||||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pub static TRACK_DETAILS_CONTEXT_CLUES: [ContextClue; 2] = [
|
||||||
|
(
|
||||||
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
|
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||||
|
),
|
||||||
|
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||||
|
];
|
||||||
|
|
||||||
|
pub static TRACK_HISTORY_CONTEXT_CLUES: [ContextClue; 6] = [
|
||||||
|
(
|
||||||
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
|
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||||
|
),
|
||||||
|
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||||
|
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
|
||||||
|
(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc),
|
||||||
|
(DEFAULT_KEYBINDINGS.submit, "details"),
|
||||||
|
(DEFAULT_KEYBINDINGS.esc, "cancel filter/close"),
|
||||||
|
];
|
||||||
|
|
||||||
pub(in crate::app) struct LidarrContextClueProvider;
|
pub(in crate::app) struct LidarrContextClueProvider;
|
||||||
|
|
||||||
impl ContextClueProvider for LidarrContextClueProvider {
|
impl ContextClueProvider for LidarrContextClueProvider {
|
||||||
@@ -156,9 +177,20 @@ impl ContextClueProvider for LidarrContextClueProvider {
|
|||||||
.lidarr_data
|
.lidarr_data
|
||||||
.album_details_modal
|
.album_details_modal
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.expect("album_details_modal is empty")
|
||||||
.album_details_tabs
|
.album_details_tabs
|
||||||
.get_active_route_contextual_help(),
|
.get_active_route_contextual_help(),
|
||||||
|
_ if TRACK_DETAILS_BLOCKS.contains(&active_lidarr_block) => app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.expect("album_details_modal is empty")
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.expect("track_details_modal is empty")
|
||||||
|
.track_details_tabs
|
||||||
|
.get_active_route_contextual_help(),
|
||||||
ActiveLidarrBlock::AddArtistSearchInput
|
ActiveLidarrBlock::AddArtistSearchInput
|
||||||
| ActiveLidarrBlock::AddArtistEmptySearchResults
|
| ActiveLidarrBlock::AddArtistEmptySearchResults
|
||||||
| ActiveLidarrBlock::TestAllIndexers
|
| ActiveLidarrBlock::TestAllIndexers
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ mod tests {
|
|||||||
ADD_ARTIST_SEARCH_RESULTS_CONTEXT_CLUES, ALBUM_DETAILS_CONTEXT_CLUES,
|
ADD_ARTIST_SEARCH_RESULTS_CONTEXT_CLUES, ALBUM_DETAILS_CONTEXT_CLUES,
|
||||||
ALBUM_HISTORY_CONTEXT_CLUES, ARTIST_DETAILS_CONTEXT_CLUES, ARTIST_HISTORY_CONTEXT_CLUES,
|
ALBUM_HISTORY_CONTEXT_CLUES, ARTIST_DETAILS_CONTEXT_CLUES, ARTIST_HISTORY_CONTEXT_CLUES,
|
||||||
ARTISTS_CONTEXT_CLUES, LidarrContextClueProvider, MANUAL_ALBUM_SEARCH_CONTEXT_CLUES,
|
ARTISTS_CONTEXT_CLUES, LidarrContextClueProvider, MANUAL_ALBUM_SEARCH_CONTEXT_CLUES,
|
||||||
MANUAL_ARTIST_SEARCH_CONTEXT_CLUES,
|
MANUAL_ARTIST_SEARCH_CONTEXT_CLUES, TRACK_DETAILS_CONTEXT_CLUES, TRACK_HISTORY_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||||
ADD_ROOT_FOLDER_BLOCKS, ActiveLidarrBlock, EDIT_ARTIST_BLOCKS, EDIT_INDEXER_BLOCKS,
|
ADD_ROOT_FOLDER_BLOCKS, ActiveLidarrBlock, EDIT_ARTIST_BLOCKS, EDIT_INDEXER_BLOCKS,
|
||||||
INDEXER_SETTINGS_BLOCKS, LidarrData,
|
INDEXER_SETTINGS_BLOCKS, LidarrData,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::lidarr::modals::AlbumDetailsModal;
|
use crate::models::servarr_data::lidarr::modals::{AlbumDetailsModal, TrackDetailsModal};
|
||||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
|
||||||
@@ -266,11 +266,11 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_some_eq_x!(
|
assert_some_eq_x!(
|
||||||
album_details_context_clues_iter.next(),
|
album_details_context_clues_iter.next(),
|
||||||
&(DEFAULT_KEYBINDINGS.submit, "episode details")
|
&(DEFAULT_KEYBINDINGS.submit, "track details")
|
||||||
);
|
);
|
||||||
assert_some_eq_x!(
|
assert_some_eq_x!(
|
||||||
album_details_context_clues_iter.next(),
|
album_details_context_clues_iter.next(),
|
||||||
&(DEFAULT_KEYBINDINGS.delete, "delete episode")
|
&(DEFAULT_KEYBINDINGS.delete, "delete track")
|
||||||
);
|
);
|
||||||
assert_none!(album_details_context_clues_iter.next());
|
assert_none!(album_details_context_clues_iter.next());
|
||||||
}
|
}
|
||||||
@@ -278,6 +278,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_album_history_context_clues() {
|
fn test_album_history_context_clues() {
|
||||||
let mut album_history_context_clues_iter = ALBUM_HISTORY_CONTEXT_CLUES.iter();
|
let mut album_history_context_clues_iter = ALBUM_HISTORY_CONTEXT_CLUES.iter();
|
||||||
|
|
||||||
assert_some_eq_x!(
|
assert_some_eq_x!(
|
||||||
album_history_context_clues_iter.next(),
|
album_history_context_clues_iter.next(),
|
||||||
&(
|
&(
|
||||||
@@ -348,6 +349,58 @@ mod tests {
|
|||||||
assert_none!(manual_album_search_context_clues_iter.next());
|
assert_none!(manual_album_search_context_clues_iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_details_context_clues() {
|
||||||
|
let mut track_details_context_clues_iter = TRACK_DETAILS_CONTEXT_CLUES.iter();
|
||||||
|
|
||||||
|
assert_some_eq_x!(
|
||||||
|
track_details_context_clues_iter.next(),
|
||||||
|
&(
|
||||||
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
|
DEFAULT_KEYBINDINGS.refresh.desc
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
track_details_context_clues_iter.next(),
|
||||||
|
&(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc)
|
||||||
|
);
|
||||||
|
assert_none!(track_details_context_clues_iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_history_context_clues() {
|
||||||
|
let mut track_history_context_clues_iter = TRACK_HISTORY_CONTEXT_CLUES.iter();
|
||||||
|
|
||||||
|
assert_some_eq_x!(
|
||||||
|
track_history_context_clues_iter.next(),
|
||||||
|
&(
|
||||||
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
|
DEFAULT_KEYBINDINGS.refresh.desc
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
track_history_context_clues_iter.next(),
|
||||||
|
&(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc)
|
||||||
|
);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
track_history_context_clues_iter.next(),
|
||||||
|
&(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc)
|
||||||
|
);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
track_history_context_clues_iter.next(),
|
||||||
|
&(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc)
|
||||||
|
);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
track_history_context_clues_iter.next(),
|
||||||
|
&(DEFAULT_KEYBINDINGS.submit, "details")
|
||||||
|
);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
track_history_context_clues_iter.next(),
|
||||||
|
&(DEFAULT_KEYBINDINGS.esc, "cancel filter/close")
|
||||||
|
);
|
||||||
|
assert_none!(track_history_context_clues_iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(0, ActiveLidarrBlock::ArtistDetails, &ARTIST_DETAILS_CONTEXT_CLUES)]
|
#[case(0, ActiveLidarrBlock::ArtistDetails, &ARTIST_DETAILS_CONTEXT_CLUES)]
|
||||||
#[case(1, ActiveLidarrBlock::ArtistHistory, &ARTIST_HISTORY_CONTEXT_CLUES)]
|
#[case(1, ActiveLidarrBlock::ArtistHistory, &ARTIST_HISTORY_CONTEXT_CLUES)]
|
||||||
@@ -391,6 +444,33 @@ mod tests {
|
|||||||
assert_some_eq_x!(context_clues, expected_context_clues);
|
assert_some_eq_x!(context_clues, expected_context_clues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(0, ActiveLidarrBlock::TrackDetails, &TRACK_DETAILS_CONTEXT_CLUES)]
|
||||||
|
#[case(1, ActiveLidarrBlock::TrackHistory, &TRACK_HISTORY_CONTEXT_CLUES)]
|
||||||
|
fn test_lidarr_context_clue_provider_track_details_tabs(
|
||||||
|
#[case] index: usize,
|
||||||
|
#[case] active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
#[case] expected_context_clues: &[ContextClue],
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
let mut track_details_modal = TrackDetailsModal::default();
|
||||||
|
track_details_modal.track_details_tabs.set_index(index);
|
||||||
|
let album_details_modal = AlbumDetailsModal {
|
||||||
|
track_details_modal: Some(track_details_modal),
|
||||||
|
..AlbumDetailsModal::default()
|
||||||
|
};
|
||||||
|
let lidarr_data = LidarrData {
|
||||||
|
album_details_modal: Some(album_details_modal),
|
||||||
|
..LidarrData::default()
|
||||||
|
};
|
||||||
|
app.data.lidarr_data = lidarr_data;
|
||||||
|
app.push_navigation_stack(active_lidarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = LidarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert_some_eq_x!(context_clues, expected_context_clues);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lidarr_context_clue_provider_artists_block() {
|
fn test_lidarr_context_clue_provider_artists_block() {
|
||||||
let mut app = App::test_default();
|
let mut app = App::test_default();
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ mod tests {
|
|||||||
use crate::models::servarr_models::Indexer;
|
use crate::models::servarr_models::Indexer;
|
||||||
use crate::network::NetworkEvent;
|
use crate::network::NetworkEvent;
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::artist;
|
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{
|
||||||
|
album, artist, track,
|
||||||
|
};
|
||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
@@ -464,6 +466,46 @@ mod tests {
|
|||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_dispatch_by_track_details_block() {
|
||||||
|
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.data.lidarr_data.prompt_confirm = true;
|
||||||
|
app.network_tx = Some(tx);
|
||||||
|
|
||||||
|
app
|
||||||
|
.dispatch_by_lidarr_block(&ActiveLidarrBlock::TrackDetails)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(app.is_loading);
|
||||||
|
assert_eq!(
|
||||||
|
rx.recv().await.unwrap(),
|
||||||
|
LidarrEvent::GetTrackDetails(1).into()
|
||||||
|
);
|
||||||
|
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||||
|
assert_eq!(app.tick_count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_dispatch_by_track_history_block() {
|
||||||
|
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.data.lidarr_data.prompt_confirm = true;
|
||||||
|
app.network_tx = Some(tx);
|
||||||
|
|
||||||
|
app
|
||||||
|
.dispatch_by_lidarr_block(&ActiveLidarrBlock::TrackHistory)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(app.is_loading);
|
||||||
|
assert_eq!(
|
||||||
|
rx.recv().await.unwrap(),
|
||||||
|
LidarrEvent::GetTrackHistory(1, 1, 1).into()
|
||||||
|
);
|
||||||
|
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||||
|
assert_eq!(app.tick_count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_check_for_lidarr_prompt_action_no_prompt_confirm() {
|
async fn test_check_for_lidarr_prompt_action_no_prompt_confirm() {
|
||||||
let mut app = App::test_default();
|
let mut app = App::test_default();
|
||||||
@@ -684,6 +726,32 @@ mod tests {
|
|||||||
assert_eq!(app.extract_artist_id().await, 1);
|
assert_eq!(app.extract_artist_id().await, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_extract_album_id() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.lidarr_data.albums.set_items(vec![album()]);
|
||||||
|
|
||||||
|
assert_eq!(app.extract_album_id().await, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_extract_track_id() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
let mut album_details_modal = AlbumDetailsModal::default();
|
||||||
|
album_details_modal.tracks.set_items(vec![track()]);
|
||||||
|
app.data.lidarr_data.album_details_modal = Some(album_details_modal);
|
||||||
|
|
||||||
|
assert_eq!(app.extract_track_id().await, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[should_panic(expected = "album_details_modal is empty")]
|
||||||
|
async fn test_extract_track_id_panics_when_album_details_modal_is_not_set() {
|
||||||
|
let app = App::test_default();
|
||||||
|
|
||||||
|
app.extract_track_id().await;
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_extract_lidarr_indexer_id() {
|
async fn test_extract_lidarr_indexer_id() {
|
||||||
let mut app = App::test_default();
|
let mut app = App::test_default();
|
||||||
|
|||||||
@@ -153,6 +153,23 @@ impl App<'_> {
|
|||||||
.dispatch_network_event(LidarrEvent::GetUpdates.into())
|
.dispatch_network_event(LidarrEvent::GetUpdates.into())
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
ActiveLidarrBlock::TrackDetails => {
|
||||||
|
self
|
||||||
|
.dispatch_network_event(
|
||||||
|
LidarrEvent::GetTrackDetails(self.extract_track_id().await).into(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ActiveLidarrBlock::TrackHistory => {
|
||||||
|
let artist_id = self.extract_artist_id().await;
|
||||||
|
let album_id = self.extract_album_id().await;
|
||||||
|
let track_id = self.extract_track_id().await;
|
||||||
|
self
|
||||||
|
.dispatch_network_event(
|
||||||
|
LidarrEvent::GetTrackHistory(artist_id, album_id, track_id).into(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +196,18 @@ impl App<'_> {
|
|||||||
self.data.lidarr_data.albums.current_selection().id
|
self.data.lidarr_data.albums.current_selection().id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn extract_track_id(&self) -> i64 {
|
||||||
|
self
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.expect("album_details_modal is empty")
|
||||||
|
.tracks
|
||||||
|
.current_selection()
|
||||||
|
.id
|
||||||
|
}
|
||||||
|
|
||||||
async fn extract_lidarr_indexer_id(&self) -> i64 {
|
async fn extract_lidarr_indexer_id(&self) -> i64 {
|
||||||
self.data.lidarr_data.indexers.current_selection().id
|
self.data.lidarr_data.indexers.current_selection().id
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,15 @@ pub enum LidarrGetCommand {
|
|||||||
SecurityConfig,
|
SecurityConfig,
|
||||||
#[command(about = "Get the system status")]
|
#[command(about = "Get the system status")]
|
||||||
SystemStatus,
|
SystemStatus,
|
||||||
|
#[command(about = "Get detailed information for the track with the given ID")]
|
||||||
|
TrackDetails {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The Lidarr ID of the track whose details you wish to fetch",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
track_id: i64,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LidarrGetCommand> for Command {
|
impl From<LidarrGetCommand> for Command {
|
||||||
@@ -115,6 +124,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrGetCommand> for LidarrGetCommandHan
|
|||||||
.await?;
|
.await?;
|
||||||
serde_json::to_string_pretty(&resp)?
|
serde_json::to_string_pretty(&resp)?
|
||||||
}
|
}
|
||||||
|
LidarrGetCommand::TrackDetails { track_id } => {
|
||||||
|
let resp = self
|
||||||
|
.network
|
||||||
|
.handle_network_event(LidarrEvent::GetTrackDetails(track_id).into())
|
||||||
|
.await?;
|
||||||
|
serde_json::to_string_pretty(&resp)?
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
|||||||
@@ -106,6 +106,32 @@ mod tests {
|
|||||||
|
|
||||||
assert_ok!(&result);
|
assert_ok!(&result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_details_requires_track_id() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "lidarr", "get", "track-details"]);
|
||||||
|
|
||||||
|
assert_err!(&result);
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_details_requirements_satisfied() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"lidarr",
|
||||||
|
"get",
|
||||||
|
"track-details",
|
||||||
|
"--track-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod handler {
|
mod handler {
|
||||||
@@ -273,5 +299,31 @@ mod tests {
|
|||||||
|
|
||||||
assert_ok!(&result);
|
assert_ok!(&result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_track_details_command() {
|
||||||
|
let expected_track_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
LidarrEvent::GetTrackDetails(expected_track_id).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||||
|
let get_track_details_command = LidarrGetCommand::TrackDetails { track_id: 1 };
|
||||||
|
|
||||||
|
let result =
|
||||||
|
LidarrGetCommandHandler::with(&app_arc, get_track_details_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,18 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{Subcommand, arg};
|
use clap::{Subcommand, arg};
|
||||||
|
use serde_json::json;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use super::LidarrCommand;
|
||||||
|
use crate::models::Serdeable;
|
||||||
|
use crate::models::lidarr_models::{LidarrHistoryItem, LidarrSerdeable};
|
||||||
use crate::{
|
use crate::{
|
||||||
app::App,
|
app::App,
|
||||||
cli::{CliCommandHandler, Command},
|
cli::{CliCommandHandler, Command},
|
||||||
network::{NetworkTrait, lidarr_network::LidarrEvent},
|
network::{NetworkTrait, lidarr_network::LidarrEvent},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::LidarrCommand;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "list_command_handler_tests.rs"]
|
#[path = "list_command_handler_tests.rs"]
|
||||||
mod list_command_handler_tests;
|
mod list_command_handler_tests;
|
||||||
@@ -89,6 +91,27 @@ pub enum LidarrListCommand {
|
|||||||
Tags,
|
Tags,
|
||||||
#[command(about = "List all Lidarr tasks")]
|
#[command(about = "List all Lidarr tasks")]
|
||||||
Tasks,
|
Tasks,
|
||||||
|
#[command(about = "Fetch all history events for the track with the given ID")]
|
||||||
|
TrackHistory {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The artist ID that the track belongs to",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
artist_id: i64,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The album ID that the track is a part of",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
album_id: i64,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The Lidarr ID of the track whose history you wish to fetch",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
track_id: i64,
|
||||||
|
},
|
||||||
#[command(
|
#[command(
|
||||||
about = "List the tracks for the album that corresponds to the artist with the given ID"
|
about = "List the tracks for the album that corresponds to the artist with the given ID"
|
||||||
)]
|
)]
|
||||||
@@ -257,6 +280,27 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrListCommand> for LidarrListCommandH
|
|||||||
.await?;
|
.await?;
|
||||||
serde_json::to_string_pretty(&resp)?
|
serde_json::to_string_pretty(&resp)?
|
||||||
}
|
}
|
||||||
|
LidarrListCommand::TrackHistory {
|
||||||
|
artist_id,
|
||||||
|
album_id,
|
||||||
|
track_id,
|
||||||
|
} => {
|
||||||
|
match self
|
||||||
|
.network
|
||||||
|
.handle_network_event(LidarrEvent::GetTrackHistory(artist_id, album_id, track_id).into())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(Serdeable::Lidarr(LidarrSerdeable::LidarrHistoryItems(history_vec))) => {
|
||||||
|
let history_items_vec: Vec<LidarrHistoryItem> = history_vec
|
||||||
|
.into_iter()
|
||||||
|
.filter(|it| it.track_id == track_id)
|
||||||
|
.collect();
|
||||||
|
serde_json::to_string_pretty(&history_items_vec)?
|
||||||
|
}
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
_ => serde_json::to_string_pretty(&json!({"message": "Failed to parse response"}))?,
|
||||||
|
}
|
||||||
|
}
|
||||||
LidarrListCommand::Tracks {
|
LidarrListCommand::Tracks {
|
||||||
artist_id,
|
artist_id,
|
||||||
album_id,
|
album_id,
|
||||||
|
|||||||
@@ -225,6 +225,96 @@ mod tests {
|
|||||||
assert_eq!(logs_command, expected_args);
|
assert_eq!(logs_command, expected_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_track_history_requires_artist_id() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"lidarr",
|
||||||
|
"list",
|
||||||
|
"track-history",
|
||||||
|
"--album-id",
|
||||||
|
"1",
|
||||||
|
"--track-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_err!(&result);
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_track_history_requires_album_id() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"lidarr",
|
||||||
|
"list",
|
||||||
|
"track-history",
|
||||||
|
"--artist-id",
|
||||||
|
"1",
|
||||||
|
"--track-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_err!(&result);
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_track_history_requires_track_id() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"lidarr",
|
||||||
|
"list",
|
||||||
|
"track-history",
|
||||||
|
"--artist-id",
|
||||||
|
"1",
|
||||||
|
"--album-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_err!(&result);
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_track_history_success() {
|
||||||
|
let expected_args = LidarrListCommand::TrackHistory {
|
||||||
|
artist_id: 1,
|
||||||
|
album_id: 1,
|
||||||
|
track_id: 1,
|
||||||
|
};
|
||||||
|
let result = Cli::try_parse_from([
|
||||||
|
"managarr",
|
||||||
|
"lidarr",
|
||||||
|
"list",
|
||||||
|
"track-history",
|
||||||
|
"--artist-id",
|
||||||
|
"1",
|
||||||
|
"--album-id",
|
||||||
|
"1",
|
||||||
|
"--track-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
|
||||||
|
let Some(Command::Lidarr(LidarrCommand::List(track_history_command))) =
|
||||||
|
result.unwrap().command
|
||||||
|
else {
|
||||||
|
panic!("Unexpected command type");
|
||||||
|
};
|
||||||
|
assert_eq!(track_history_command, expected_args);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list_tracks_requires_artist_id() {
|
fn test_list_tracks_requires_artist_id() {
|
||||||
let result = Cli::command().try_get_matches_from([
|
let result = Cli::command().try_get_matches_from([
|
||||||
@@ -325,6 +415,7 @@ mod tests {
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use mockall::predicate::eq;
|
use mockall::predicate::eq;
|
||||||
|
use pretty_assertions::assert_str_eq;
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
@@ -332,8 +423,9 @@ mod tests {
|
|||||||
use crate::cli::CliCommandHandler;
|
use crate::cli::CliCommandHandler;
|
||||||
use crate::cli::lidarr::list_command_handler::{LidarrListCommand, LidarrListCommandHandler};
|
use crate::cli::lidarr::list_command_handler::{LidarrListCommand, LidarrListCommandHandler};
|
||||||
use crate::models::Serdeable;
|
use crate::models::Serdeable;
|
||||||
use crate::models::lidarr_models::LidarrSerdeable;
|
use crate::models::lidarr_models::{LidarrHistoryItem, LidarrSerdeable};
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
|
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::lidarr_history_item;
|
||||||
use crate::{
|
use crate::{
|
||||||
app::App,
|
app::App,
|
||||||
network::{MockNetworkTrait, NetworkEvent},
|
network::{MockNetworkTrait, NetworkEvent},
|
||||||
@@ -531,6 +623,49 @@ mod tests {
|
|||||||
assert_ok!(&result);
|
assert_ok!(&result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_list_track_history_command() {
|
||||||
|
let expected_artist_id = 1;
|
||||||
|
let expected_album_id = 1;
|
||||||
|
let expected_track_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
LidarrEvent::GetTrackHistory(expected_artist_id, expected_album_id, expected_track_id)
|
||||||
|
.into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Lidarr(LidarrSerdeable::LidarrHistoryItems(
|
||||||
|
vec![
|
||||||
|
lidarr_history_item(),
|
||||||
|
LidarrHistoryItem {
|
||||||
|
track_id: 2,
|
||||||
|
..lidarr_history_item()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||||
|
let list_track_history_command = LidarrListCommand::TrackHistory {
|
||||||
|
artist_id: expected_artist_id,
|
||||||
|
album_id: expected_album_id,
|
||||||
|
track_id: expected_track_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result =
|
||||||
|
LidarrListCommandHandler::with(&app_arc, list_track_history_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
assert_str_eq!(
|
||||||
|
result.unwrap(),
|
||||||
|
serde_json::to_string_pretty(&[lidarr_history_item()]).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_handle_list_tracks_command() {
|
async fn test_handle_list_tracks_command() {
|
||||||
let expected_artist_id = 1;
|
let expected_artist_id = 1;
|
||||||
|
|||||||
@@ -109,16 +109,20 @@ mod tests {
|
|||||||
LidarrManualSearchCommand, LidarrManualSearchCommandHandler,
|
LidarrManualSearchCommand, LidarrManualSearchCommandHandler,
|
||||||
};
|
};
|
||||||
use crate::models::Serdeable;
|
use crate::models::Serdeable;
|
||||||
use crate::models::lidarr_models::LidarrSerdeable;
|
use crate::models::lidarr_models::{LidarrRelease, LidarrSerdeable};
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
|
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{
|
||||||
|
torrent_release, usenet_release,
|
||||||
|
};
|
||||||
use crate::network::{MockNetworkTrait, NetworkEvent};
|
use crate::network::{MockNetworkTrait, NetworkEvent};
|
||||||
use mockall::predicate::eq;
|
use mockall::predicate::eq;
|
||||||
use serde_json::json;
|
use pretty_assertions::assert_str_eq;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_manual_album_search_command() {
|
async fn test_manual_album_search_command() {
|
||||||
|
let expected_releases = [torrent_release()];
|
||||||
let expected_artist_id = 1;
|
let expected_artist_id = 1;
|
||||||
let expected_album_id = 1;
|
let expected_album_id = 1;
|
||||||
let mut mock_network = MockNetworkTrait::new();
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
@@ -129,9 +133,13 @@ mod tests {
|
|||||||
))
|
))
|
||||||
.times(1)
|
.times(1)
|
||||||
.returning(|_| {
|
.returning(|_| {
|
||||||
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
Ok(Serdeable::Lidarr(LidarrSerdeable::Releases(vec![
|
||||||
json!({"testResponse": "response"}),
|
torrent_release(),
|
||||||
)))
|
LidarrRelease {
|
||||||
|
discography: true,
|
||||||
|
..usenet_release()
|
||||||
|
},
|
||||||
|
])))
|
||||||
});
|
});
|
||||||
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||||
let manual_album_search_command = LidarrManualSearchCommand::Album {
|
let manual_album_search_command = LidarrManualSearchCommand::Album {
|
||||||
@@ -148,10 +156,18 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
assert_ok!(&result);
|
assert_ok!(&result);
|
||||||
|
assert_str_eq!(
|
||||||
|
result.unwrap(),
|
||||||
|
serde_json::to_string_pretty(&expected_releases).unwrap()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_manual_discography_search_command() {
|
async fn test_manual_discography_search_command() {
|
||||||
|
let expected_releases = [LidarrRelease {
|
||||||
|
discography: true,
|
||||||
|
..usenet_release()
|
||||||
|
}];
|
||||||
let expected_artist_id = 1;
|
let expected_artist_id = 1;
|
||||||
let mut mock_network = MockNetworkTrait::new();
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
mock_network
|
mock_network
|
||||||
@@ -161,9 +177,13 @@ mod tests {
|
|||||||
))
|
))
|
||||||
.times(1)
|
.times(1)
|
||||||
.returning(|_| {
|
.returning(|_| {
|
||||||
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
Ok(Serdeable::Lidarr(LidarrSerdeable::Releases(vec![
|
||||||
json!({"testResponse": "response"}),
|
torrent_release(),
|
||||||
)))
|
LidarrRelease {
|
||||||
|
discography: true,
|
||||||
|
..usenet_release()
|
||||||
|
},
|
||||||
|
])))
|
||||||
});
|
});
|
||||||
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||||
let manual_discography_search_command =
|
let manual_discography_search_command =
|
||||||
@@ -178,6 +198,10 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
assert_ok!(&result);
|
assert_ok!(&result);
|
||||||
|
assert_str_eq!(
|
||||||
|
result.unwrap(),
|
||||||
|
serde_json::to_string_pretty(&expected_releases).unwrap()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,22 +227,22 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for AlbumDetailsHandler<
|
|||||||
|
|
||||||
fn handle_submit(&mut self) {
|
fn handle_submit(&mut self) {
|
||||||
match self.active_lidarr_block {
|
match self.active_lidarr_block {
|
||||||
// ActiveLidarrBlock::AlbumDetails
|
ActiveLidarrBlock::AlbumDetails
|
||||||
// if self.app.data.lidarr_data.album_details_modal.is_some()
|
if self.app.data.lidarr_data.album_details_modal.is_some()
|
||||||
// && !self
|
&& !self
|
||||||
// .app
|
.app
|
||||||
// .data
|
.data
|
||||||
// .lidarr_data
|
.lidarr_data
|
||||||
// .album_details_modal
|
.album_details_modal
|
||||||
// .as_ref()
|
.as_ref()
|
||||||
// .unwrap()
|
.unwrap()
|
||||||
// .tracks
|
.tracks
|
||||||
// .is_empty() =>
|
.is_empty() =>
|
||||||
// {
|
{
|
||||||
// self
|
self
|
||||||
// .app
|
.app
|
||||||
// .push_navigation_stack(ActiveLidarrBlock::TrackDetails.into())
|
.push_navigation_stack(ActiveLidarrBlock::TrackDetails.into())
|
||||||
// }
|
}
|
||||||
ActiveLidarrBlock::AlbumHistory => self
|
ActiveLidarrBlock::AlbumHistory => self
|
||||||
.app
|
.app
|
||||||
.push_navigation_stack(ActiveLidarrBlock::AlbumHistoryDetails.into()),
|
.push_navigation_stack(ActiveLidarrBlock::AlbumHistoryDetails.into()),
|
||||||
|
|||||||
@@ -155,37 +155,37 @@ mod tests {
|
|||||||
|
|
||||||
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
|
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn test_album_details_submit() {
|
fn test_album_details_submit() {
|
||||||
// let mut app = App::test_default_fully_populated();
|
let mut app = App::test_default_fully_populated();
|
||||||
// app.push_navigation_stack(ActiveLidarrBlock::AlbumDetails.into());
|
app.push_navigation_stack(ActiveLidarrBlock::AlbumDetails.into());
|
||||||
//
|
|
||||||
// AlbumDetailsHandler::new(SUBMIT_KEY, &mut app, ActiveLidarrBlock::AlbumDetails, None)
|
|
||||||
// .handle();
|
|
||||||
//
|
|
||||||
// assert_navigation_pushed!(app, ActiveLidarrBlock::TrackDetails.into());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
AlbumDetailsHandler::new(SUBMIT_KEY, &mut app, ActiveLidarrBlock::AlbumDetails, None)
|
||||||
// fn test_album_details_submit_no_op_on_empty_tracks_table() {
|
.handle();
|
||||||
// let mut app = App::test_default_fully_populated();
|
|
||||||
// app
|
assert_navigation_pushed!(app, ActiveLidarrBlock::TrackDetails.into());
|
||||||
// .data
|
}
|
||||||
// .lidarr_data
|
|
||||||
// .album_details_modal
|
#[test]
|
||||||
// .as_mut()
|
fn test_album_details_submit_no_op_on_empty_tracks_table() {
|
||||||
// .unwrap()
|
let mut app = App::test_default_fully_populated();
|
||||||
// .tracks = StatefulTable::default();
|
app
|
||||||
// app.push_navigation_stack(ActiveLidarrBlock::AlbumDetails.into());
|
.data
|
||||||
//
|
.lidarr_data
|
||||||
// AlbumDetailsHandler::new(SUBMIT_KEY, &mut app, ActiveLidarrBlock::AlbumDetails, None)
|
.album_details_modal
|
||||||
// .handle();
|
.as_mut()
|
||||||
//
|
.unwrap()
|
||||||
// assert_eq!(
|
.tracks = StatefulTable::default();
|
||||||
// app.get_current_route(),
|
app.push_navigation_stack(ActiveLidarrBlock::AlbumDetails.into());
|
||||||
// ActiveLidarrBlock::AlbumDetails.into()
|
|
||||||
// );
|
AlbumDetailsHandler::new(SUBMIT_KEY, &mut app, ActiveLidarrBlock::AlbumDetails, None)
|
||||||
// }
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
app.get_current_route(),
|
||||||
|
ActiveLidarrBlock::AlbumDetails.into()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_album_details_submit_no_op_when_not_ready() {
|
fn test_album_details_submit_no_op_when_not_ready() {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::event::Key;
|
use crate::event::Key;
|
||||||
use crate::handlers::lidarr_handlers::history::history_sorting_options;
|
use crate::handlers::lidarr_handlers::history::history_sorting_options;
|
||||||
use crate::handlers::lidarr_handlers::library::album_details_handler::AlbumDetailsHandler;
|
|
||||||
use crate::handlers::lidarr_handlers::library::delete_album_handler::DeleteAlbumHandler;
|
|
||||||
use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
|
use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
|
||||||
use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
|
use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
|
||||||
use crate::matches_key;
|
use crate::matches_key;
|
||||||
@@ -26,7 +24,7 @@ pub struct ArtistDetailsHandler<'a, 'b> {
|
|||||||
key: Key,
|
key: Key,
|
||||||
app: &'a mut App<'b>,
|
app: &'a mut App<'b>,
|
||||||
active_lidarr_block: ActiveLidarrBlock,
|
active_lidarr_block: ActiveLidarrBlock,
|
||||||
context: Option<ActiveLidarrBlock>,
|
_context: Option<ActiveLidarrBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArtistDetailsHandler<'_, '_> {
|
impl ArtistDetailsHandler<'_, '_> {
|
||||||
@@ -76,24 +74,12 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for ArtistDetailsHandler
|
|||||||
|app| &mut app.data.lidarr_data.discography_releases,
|
|app| &mut app.data.lidarr_data.discography_releases,
|
||||||
artist_releases_table_handling_config,
|
artist_releases_table_handling_config,
|
||||||
) {
|
) {
|
||||||
match self.active_lidarr_block {
|
self.handle_key_event();
|
||||||
_ if DeleteAlbumHandler::accepts(self.active_lidarr_block) => {
|
|
||||||
DeleteAlbumHandler::new(self.key, self.app, self.active_lidarr_block, self.context)
|
|
||||||
.handle();
|
|
||||||
}
|
|
||||||
_ if AlbumDetailsHandler::accepts(self.active_lidarr_block) => {
|
|
||||||
AlbumDetailsHandler::new(self.key, self.app, self.active_lidarr_block, self.context)
|
|
||||||
.handle();
|
|
||||||
}
|
|
||||||
_ => self.handle_key_event(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepts(active_block: ActiveLidarrBlock) -> bool {
|
fn accepts(active_block: ActiveLidarrBlock) -> bool {
|
||||||
DeleteAlbumHandler::accepts(active_block)
|
ARTIST_DETAILS_BLOCKS.contains(&active_block)
|
||||||
|| AlbumDetailsHandler::accepts(active_block)
|
|
||||||
|| ARTIST_DETAILS_BLOCKS.contains(&active_block)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ignore_special_keys(&self) -> bool {
|
fn ignore_special_keys(&self) -> bool {
|
||||||
@@ -104,13 +90,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for ArtistDetailsHandler
|
|||||||
key: Key,
|
key: Key,
|
||||||
app: &'a mut App<'b>,
|
app: &'a mut App<'b>,
|
||||||
active_block: ActiveLidarrBlock,
|
active_block: ActiveLidarrBlock,
|
||||||
context: Option<ActiveLidarrBlock>,
|
_context: Option<ActiveLidarrBlock>,
|
||||||
) -> ArtistDetailsHandler<'a, 'b> {
|
) -> ArtistDetailsHandler<'a, 'b> {
|
||||||
ArtistDetailsHandler {
|
ArtistDetailsHandler {
|
||||||
key,
|
key,
|
||||||
app,
|
app,
|
||||||
active_lidarr_block: active_block,
|
active_lidarr_block: active_block,
|
||||||
context,
|
_context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,11 @@ mod tests {
|
|||||||
ArtistDetailsHandler, releases_sorting_options,
|
ArtistDetailsHandler, releases_sorting_options,
|
||||||
};
|
};
|
||||||
use crate::models::HorizontallyScrollableText;
|
use crate::models::HorizontallyScrollableText;
|
||||||
use crate::models::lidarr_models::{Album, LidarrHistoryItem, LidarrRelease};
|
use crate::models::lidarr_models::{LidarrHistoryItem, LidarrRelease};
|
||||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||||
ALBUM_DETAILS_BLOCKS, ARTIST_DETAILS_BLOCKS, ActiveLidarrBlock, DELETE_ALBUM_BLOCKS,
|
ARTIST_DETAILS_BLOCKS, ActiveLidarrBlock,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_models::{Quality, QualityWrapper};
|
use crate::models::servarr_models::{Quality, QualityWrapper};
|
||||||
use crate::test_handler_delegation;
|
|
||||||
|
|
||||||
mod test_handle_delete {
|
mod test_handle_delete {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -812,12 +811,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_artist_details_handler_accepts() {
|
fn test_artist_details_handler_accepts() {
|
||||||
let mut artist_details_blocks = ARTIST_DETAILS_BLOCKS.clone().to_vec();
|
|
||||||
artist_details_blocks.extend(DELETE_ALBUM_BLOCKS);
|
|
||||||
artist_details_blocks.extend(ALBUM_DETAILS_BLOCKS);
|
|
||||||
|
|
||||||
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
||||||
if artist_details_blocks.contains(&active_lidarr_block) {
|
if ARTIST_DETAILS_BLOCKS.contains(&active_lidarr_block) {
|
||||||
assert!(ArtistDetailsHandler::accepts(active_lidarr_block));
|
assert!(ArtistDetailsHandler::accepts(active_lidarr_block));
|
||||||
} else {
|
} else {
|
||||||
assert!(!ArtistDetailsHandler::accepts(active_lidarr_block));
|
assert!(!ArtistDetailsHandler::accepts(active_lidarr_block));
|
||||||
@@ -977,58 +972,6 @@ mod tests {
|
|||||||
assert!(handler.is_ready());
|
assert!(handler.is_ready());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_delegates_delete_album_blocks_to_delete_album_handler() {
|
|
||||||
let mut app = App::test_default();
|
|
||||||
app
|
|
||||||
.data
|
|
||||||
.lidarr_data
|
|
||||||
.albums
|
|
||||||
.set_items(vec![Album::default()]);
|
|
||||||
app.push_navigation_stack(ActiveLidarrBlock::ArtistDetails.into());
|
|
||||||
app.push_navigation_stack(ActiveLidarrBlock::DeleteAlbumPrompt.into());
|
|
||||||
|
|
||||||
ArtistDetailsHandler::new(
|
|
||||||
DEFAULT_KEYBINDINGS.esc.key,
|
|
||||||
&mut app,
|
|
||||||
ActiveLidarrBlock::DeleteAlbumPrompt,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.handle();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
app.get_current_route(),
|
|
||||||
ActiveLidarrBlock::ArtistDetails.into()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn test_delegates_album_details_blocks_to_album_details_handler(
|
|
||||||
#[values(
|
|
||||||
ActiveLidarrBlock::AlbumDetails,
|
|
||||||
ActiveLidarrBlock::AlbumHistory,
|
|
||||||
ActiveLidarrBlock::SearchTracks,
|
|
||||||
ActiveLidarrBlock::SearchTracksError,
|
|
||||||
ActiveLidarrBlock::AutomaticallySearchAlbumPrompt,
|
|
||||||
ActiveLidarrBlock::SearchAlbumHistory,
|
|
||||||
ActiveLidarrBlock::SearchAlbumHistoryError,
|
|
||||||
ActiveLidarrBlock::FilterAlbumHistory,
|
|
||||||
ActiveLidarrBlock::FilterAlbumHistoryError,
|
|
||||||
ActiveLidarrBlock::AlbumHistorySortPrompt,
|
|
||||||
ActiveLidarrBlock::AlbumHistoryDetails,
|
|
||||||
ActiveLidarrBlock::ManualAlbumSearch,
|
|
||||||
ActiveLidarrBlock::ManualAlbumSearchSortPrompt,
|
|
||||||
ActiveLidarrBlock::DeleteTrackFilePrompt
|
|
||||||
)]
|
|
||||||
active_sonarr_block: ActiveLidarrBlock,
|
|
||||||
) {
|
|
||||||
test_handler_delegation!(
|
|
||||||
ArtistDetailsHandler,
|
|
||||||
ActiveLidarrBlock::Artists,
|
|
||||||
active_sonarr_block
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_releases_sorting_options_source() {
|
fn test_releases_sorting_options_source() {
|
||||||
let expected_cmp_fn: fn(&LidarrRelease, &LidarrRelease) -> Ordering =
|
let expected_cmp_fn: fn(&LidarrRelease, &LidarrRelease) -> Ordering =
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ mod tests {
|
|||||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||||
ADD_ARTIST_BLOCKS, ALBUM_DETAILS_BLOCKS, ARTIST_DETAILS_BLOCKS, ActiveLidarrBlock,
|
ADD_ARTIST_BLOCKS, ALBUM_DETAILS_BLOCKS, ARTIST_DETAILS_BLOCKS, ActiveLidarrBlock,
|
||||||
DELETE_ALBUM_BLOCKS, DELETE_ARTIST_BLOCKS, EDIT_ARTIST_BLOCKS, EDIT_ARTIST_SELECTION_BLOCKS,
|
DELETE_ALBUM_BLOCKS, DELETE_ARTIST_BLOCKS, EDIT_ARTIST_BLOCKS, EDIT_ARTIST_SELECTION_BLOCKS,
|
||||||
LIBRARY_BLOCKS,
|
LIBRARY_BLOCKS, TRACK_DETAILS_BLOCKS,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::lidarr::modals::EditArtistModal;
|
use crate::models::servarr_data::lidarr::modals::EditArtistModal;
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
@@ -34,10 +34,14 @@ mod tests {
|
|||||||
library_handler_blocks.extend(EDIT_ARTIST_BLOCKS);
|
library_handler_blocks.extend(EDIT_ARTIST_BLOCKS);
|
||||||
library_handler_blocks.extend(ADD_ARTIST_BLOCKS);
|
library_handler_blocks.extend(ADD_ARTIST_BLOCKS);
|
||||||
library_handler_blocks.extend(ALBUM_DETAILS_BLOCKS);
|
library_handler_blocks.extend(ALBUM_DETAILS_BLOCKS);
|
||||||
|
library_handler_blocks.extend(TRACK_DETAILS_BLOCKS);
|
||||||
|
|
||||||
ActiveLidarrBlock::iter().for_each(|lidarr_block| {
|
ActiveLidarrBlock::iter().for_each(|lidarr_block| {
|
||||||
if library_handler_blocks.contains(&lidarr_block) {
|
if library_handler_blocks.contains(&lidarr_block) {
|
||||||
assert!(LibraryHandler::accepts(lidarr_block));
|
assert!(
|
||||||
|
LibraryHandler::accepts(lidarr_block),
|
||||||
|
"{lidarr_block} is not accepted by the LibraryHandler"
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
assert!(!LibraryHandler::accepts(lidarr_block));
|
assert!(!LibraryHandler::accepts(lidarr_block));
|
||||||
}
|
}
|
||||||
@@ -670,6 +674,27 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_delegates_track_details_blocks_to_track_details_handler(
|
||||||
|
#[values(
|
||||||
|
ActiveLidarrBlock::TrackDetails,
|
||||||
|
ActiveLidarrBlock::TrackHistory,
|
||||||
|
ActiveLidarrBlock::TrackHistoryDetails,
|
||||||
|
ActiveLidarrBlock::SearchTrackHistory,
|
||||||
|
ActiveLidarrBlock::SearchTrackHistoryError,
|
||||||
|
ActiveLidarrBlock::FilterTrackHistory,
|
||||||
|
ActiveLidarrBlock::FilterTrackHistoryError,
|
||||||
|
ActiveLidarrBlock::TrackHistorySortPrompt
|
||||||
|
)]
|
||||||
|
active_sonarr_block: ActiveLidarrBlock,
|
||||||
|
) {
|
||||||
|
test_handler_delegation!(
|
||||||
|
LibraryHandler,
|
||||||
|
ActiveLidarrBlock::AlbumDetails,
|
||||||
|
active_sonarr_block
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_edit_key() {
|
fn test_edit_key() {
|
||||||
let mut app = App::test_default();
|
let mut app = App::test_default();
|
||||||
|
|||||||
@@ -19,18 +19,22 @@ use super::handle_change_tab_left_right_keys;
|
|||||||
use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
|
use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
|
||||||
|
|
||||||
mod add_artist_handler;
|
mod add_artist_handler;
|
||||||
|
mod album_details_handler;
|
||||||
mod artist_details_handler;
|
mod artist_details_handler;
|
||||||
mod delete_album_handler;
|
mod delete_album_handler;
|
||||||
mod delete_artist_handler;
|
mod delete_artist_handler;
|
||||||
mod edit_artist_handler;
|
mod edit_artist_handler;
|
||||||
|
mod track_details_handler;
|
||||||
|
|
||||||
|
use crate::handlers::lidarr_handlers::library::album_details_handler::AlbumDetailsHandler;
|
||||||
|
use crate::handlers::lidarr_handlers::library::delete_album_handler::DeleteAlbumHandler;
|
||||||
|
use crate::handlers::lidarr_handlers::library::track_details_handler::TrackDetailsHandler;
|
||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
pub(in crate::handlers::lidarr_handlers) use add_artist_handler::AddArtistHandler;
|
pub(in crate::handlers::lidarr_handlers) use add_artist_handler::AddArtistHandler;
|
||||||
pub(in crate::handlers::lidarr_handlers) use artist_details_handler::ArtistDetailsHandler;
|
pub(in crate::handlers::lidarr_handlers) use artist_details_handler::ArtistDetailsHandler;
|
||||||
pub(in crate::handlers::lidarr_handlers) use delete_artist_handler::DeleteArtistHandler;
|
pub(in crate::handlers::lidarr_handlers) use delete_artist_handler::DeleteArtistHandler;
|
||||||
pub(in crate::handlers::lidarr_handlers) use edit_artist_handler::EditArtistHandler;
|
pub(in crate::handlers::lidarr_handlers) use edit_artist_handler::EditArtistHandler;
|
||||||
|
|
||||||
mod album_details_handler;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "library_handler_tests.rs"]
|
#[path = "library_handler_tests.rs"]
|
||||||
mod library_handler_tests;
|
mod library_handler_tests;
|
||||||
@@ -82,6 +86,18 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LibraryHandler<'a, '
|
|||||||
ArtistDetailsHandler::new(self.key, self.app, self.active_lidarr_block, self.context)
|
ArtistDetailsHandler::new(self.key, self.app, self.active_lidarr_block, self.context)
|
||||||
.handle();
|
.handle();
|
||||||
}
|
}
|
||||||
|
_ if DeleteAlbumHandler::accepts(self.active_lidarr_block) => {
|
||||||
|
DeleteAlbumHandler::new(self.key, self.app, self.active_lidarr_block, self.context)
|
||||||
|
.handle();
|
||||||
|
}
|
||||||
|
_ if AlbumDetailsHandler::accepts(self.active_lidarr_block) => {
|
||||||
|
AlbumDetailsHandler::new(self.key, self.app, self.active_lidarr_block, self.context)
|
||||||
|
.handle();
|
||||||
|
}
|
||||||
|
_ if TrackDetailsHandler::accepts(self.active_lidarr_block) => {
|
||||||
|
TrackDetailsHandler::new(self.key, self.app, self.active_lidarr_block, self.context)
|
||||||
|
.handle();
|
||||||
|
}
|
||||||
_ => self.handle_key_event(),
|
_ => self.handle_key_event(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,8 +106,11 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LibraryHandler<'a, '
|
|||||||
fn accepts(active_block: ActiveLidarrBlock) -> bool {
|
fn accepts(active_block: ActiveLidarrBlock) -> bool {
|
||||||
AddArtistHandler::accepts(active_block)
|
AddArtistHandler::accepts(active_block)
|
||||||
|| DeleteArtistHandler::accepts(active_block)
|
|| DeleteArtistHandler::accepts(active_block)
|
||||||
|
|| DeleteAlbumHandler::accepts(active_block)
|
||||||
|| EditArtistHandler::accepts(active_block)
|
|| EditArtistHandler::accepts(active_block)
|
||||||
|| ArtistDetailsHandler::accepts(active_block)
|
|| ArtistDetailsHandler::accepts(active_block)
|
||||||
|
|| AlbumDetailsHandler::accepts(active_block)
|
||||||
|
|| TrackDetailsHandler::accepts(active_block)
|
||||||
|| LIBRARY_BLOCKS.contains(&active_block)
|
|| LIBRARY_BLOCKS.contains(&active_block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,225 @@
|
|||||||
|
use crate::app::App;
|
||||||
|
use crate::event::Key;
|
||||||
|
use crate::handlers::KeyEventHandler;
|
||||||
|
use crate::handlers::lidarr_handlers::history::history_sorting_options;
|
||||||
|
use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
|
||||||
|
use crate::matches_key;
|
||||||
|
use crate::models::Route;
|
||||||
|
use crate::models::lidarr_models::LidarrHistoryItem;
|
||||||
|
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, TRACK_DETAILS_BLOCKS};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "track_details_handler_tests.rs"]
|
||||||
|
mod track_details_handler_tests;
|
||||||
|
|
||||||
|
pub(super) struct TrackDetailsHandler<'a, 'b> {
|
||||||
|
key: Key,
|
||||||
|
app: &'a mut App<'b>,
|
||||||
|
active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
_context: Option<ActiveLidarrBlock>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for TrackDetailsHandler<'a, 'b> {
|
||||||
|
fn handle(&mut self) {
|
||||||
|
let track_history_table_handling_config =
|
||||||
|
TableHandlingConfig::new(ActiveLidarrBlock::TrackHistory.into())
|
||||||
|
.sorting_block(ActiveLidarrBlock::TrackHistorySortPrompt.into())
|
||||||
|
.sort_options(history_sorting_options())
|
||||||
|
.searching_block(ActiveLidarrBlock::SearchTrackHistory.into())
|
||||||
|
.search_error_block(ActiveLidarrBlock::SearchTrackHistoryError.into())
|
||||||
|
.search_field_fn(|history_item: &LidarrHistoryItem| &history_item.source_title.text)
|
||||||
|
.filtering_block(ActiveLidarrBlock::FilterTrackHistory.into())
|
||||||
|
.filter_error_block(ActiveLidarrBlock::FilterTrackHistoryError.into())
|
||||||
|
.filter_field_fn(|history_item: &LidarrHistoryItem| &history_item.source_title.text);
|
||||||
|
|
||||||
|
if !handle_table(
|
||||||
|
self,
|
||||||
|
|app| {
|
||||||
|
&mut app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.expect("Album details modal is undefined")
|
||||||
|
.track_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.expect("Track details modal is undefined")
|
||||||
|
.track_history
|
||||||
|
},
|
||||||
|
track_history_table_handling_config,
|
||||||
|
) {
|
||||||
|
self.handle_key_event();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accepts(active_block: ActiveLidarrBlock) -> bool {
|
||||||
|
TRACK_DETAILS_BLOCKS.contains(&active_block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(
|
||||||
|
key: Key,
|
||||||
|
app: &'a mut App<'b>,
|
||||||
|
active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
_context: Option<ActiveLidarrBlock>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
key,
|
||||||
|
app,
|
||||||
|
active_lidarr_block,
|
||||||
|
_context,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_key(&self) -> Key {
|
||||||
|
self.key
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ignore_special_keys(&self) -> bool {
|
||||||
|
self.app.ignore_special_keys_for_textbox_input
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ready(&self) -> bool {
|
||||||
|
if self.app.is_loading {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(album_details_modal) = self.app.data.lidarr_data.album_details_modal.as_ref() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(track_details_modal) = &album_details_modal.track_details_modal else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.active_lidarr_block {
|
||||||
|
ActiveLidarrBlock::TrackDetails => !track_details_modal.track_details.is_empty(),
|
||||||
|
ActiveLidarrBlock::TrackHistory => !track_details_modal.track_history.is_empty(),
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_scroll_up(&mut self) {}
|
||||||
|
|
||||||
|
fn handle_scroll_down(&mut self) {}
|
||||||
|
|
||||||
|
fn handle_home(&mut self) {}
|
||||||
|
|
||||||
|
fn handle_end(&mut self) {}
|
||||||
|
|
||||||
|
fn handle_delete(&mut self) {}
|
||||||
|
|
||||||
|
fn handle_left_right_action(&mut self) {
|
||||||
|
match self.active_lidarr_block {
|
||||||
|
ActiveLidarrBlock::TrackDetails | ActiveLidarrBlock::TrackHistory => match self.key {
|
||||||
|
_ if matches_key!(left, self.key) => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_tabs
|
||||||
|
.previous();
|
||||||
|
self.app.pop_and_push_navigation_stack(
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_tabs
|
||||||
|
.get_active_route(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ if matches_key!(right, self.key) => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_tabs
|
||||||
|
.next();
|
||||||
|
self.app.pop_and_push_navigation_stack(
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_tabs
|
||||||
|
.get_active_route(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_submit(&mut self) {
|
||||||
|
if self.active_lidarr_block == ActiveLidarrBlock::TrackHistory {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveLidarrBlock::TrackHistoryDetails.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_esc(&mut self) {
|
||||||
|
match self.active_lidarr_block {
|
||||||
|
ActiveLidarrBlock::TrackDetails | ActiveLidarrBlock::TrackHistory => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal = None;
|
||||||
|
}
|
||||||
|
ActiveLidarrBlock::TrackHistoryDetails => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_char_key_event(&mut self) {
|
||||||
|
match self.active_lidarr_block {
|
||||||
|
ActiveLidarrBlock::TrackDetails | ActiveLidarrBlock::TrackHistory => match self.key {
|
||||||
|
_ if matches_key!(refresh, self.key) => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.pop_and_push_navigation_stack(self.active_lidarr_block.into());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app_mut(&mut self) -> &mut App<'b> {
|
||||||
|
self.app
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_route(&self) -> Route {
|
||||||
|
self.app.get_current_route()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,407 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
|
use crate::assert_navigation_pushed;
|
||||||
|
use crate::handlers::KeyEventHandler;
|
||||||
|
use crate::handlers::lidarr_handlers::library::track_details_handler::TrackDetailsHandler;
|
||||||
|
use crate::models::ScrollableText;
|
||||||
|
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, TRACK_DETAILS_BLOCKS};
|
||||||
|
use crate::models::stateful_table::StatefulTable;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use rstest::rstest;
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
mod test_handle_left_right_actions {
|
||||||
|
use super::*;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(ActiveLidarrBlock::TrackDetails, ActiveLidarrBlock::TrackHistory)]
|
||||||
|
#[case(ActiveLidarrBlock::TrackHistory, ActiveLidarrBlock::TrackDetails)]
|
||||||
|
fn test_track_details_tabs_left_right_action(
|
||||||
|
#[case] left_block: ActiveLidarrBlock,
|
||||||
|
#[case] right_block: ActiveLidarrBlock,
|
||||||
|
#[values(true, false)] is_ready: bool,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::AlbumDetails.into());
|
||||||
|
app.is_loading = is_ready;
|
||||||
|
app.push_navigation_stack(right_block.into());
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_tabs
|
||||||
|
.index = app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_tabs
|
||||||
|
.tabs
|
||||||
|
.iter()
|
||||||
|
.position(|tab_route| tab_route.route == right_block.into())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
TrackDetailsHandler::new(DEFAULT_KEYBINDINGS.left.key, &mut app, right_block, None).handle();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
app.get_current_route(),
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_tabs
|
||||||
|
.get_active_route()
|
||||||
|
);
|
||||||
|
assert_navigation_pushed!(app, left_block.into());
|
||||||
|
|
||||||
|
TrackDetailsHandler::new(DEFAULT_KEYBINDINGS.right.key, &mut app, left_block, None).handle();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
app.get_current_route(),
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_tabs
|
||||||
|
.get_active_route()
|
||||||
|
);
|
||||||
|
assert_navigation_pushed!(app, right_block.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test_handle_submit {
|
||||||
|
use super::*;
|
||||||
|
use crate::event::Key;
|
||||||
|
use crate::models::stateful_table::StatefulTable;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_history_submit() {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
|
||||||
|
TrackDetailsHandler::new(SUBMIT_KEY, &mut app, ActiveLidarrBlock::TrackHistory, None)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_navigation_pushed!(app, ActiveLidarrBlock::TrackHistoryDetails.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_history_submit_no_op_when_track_history_is_empty() {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_history = StatefulTable::default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::TrackHistory.into());
|
||||||
|
|
||||||
|
TrackDetailsHandler::new(SUBMIT_KEY, &mut app, ActiveLidarrBlock::TrackHistory, None)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
app.get_current_route(),
|
||||||
|
ActiveLidarrBlock::TrackHistory.into()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_history_submit_no_op_when_not_ready() {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::TrackHistory.into());
|
||||||
|
|
||||||
|
TrackDetailsHandler::new(SUBMIT_KEY, &mut app, ActiveLidarrBlock::TrackHistory, None)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
app.get_current_route(),
|
||||||
|
ActiveLidarrBlock::TrackHistory.into()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test_handle_esc {
|
||||||
|
use super::*;
|
||||||
|
use crate::assert_navigation_popped;
|
||||||
|
use crate::event::Key;
|
||||||
|
|
||||||
|
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_history_details_block_esc() {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::TrackHistory.into());
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::TrackHistoryDetails.into());
|
||||||
|
|
||||||
|
TrackDetailsHandler::new(
|
||||||
|
ESC_KEY,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::TrackHistoryDetails,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_navigation_popped!(app, ActiveLidarrBlock::TrackHistory.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_track_details_tabs_esc(
|
||||||
|
#[values(ActiveLidarrBlock::TrackDetails, ActiveLidarrBlock::TrackHistory)]
|
||||||
|
active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::AlbumDetails.into());
|
||||||
|
app.push_navigation_stack(active_lidarr_block.into());
|
||||||
|
|
||||||
|
TrackDetailsHandler::new(ESC_KEY, &mut app, active_lidarr_block, None).handle();
|
||||||
|
|
||||||
|
assert_navigation_popped!(app, ActiveLidarrBlock::AlbumDetails.into());
|
||||||
|
assert_none!(
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test_handle_key_char {
|
||||||
|
use super::*;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_refresh_key(
|
||||||
|
#[values(ActiveLidarrBlock::TrackDetails, ActiveLidarrBlock::TrackHistory)]
|
||||||
|
active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.push_navigation_stack(active_lidarr_block.into());
|
||||||
|
app.is_routing = false;
|
||||||
|
|
||||||
|
TrackDetailsHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.refresh.key,
|
||||||
|
&mut app,
|
||||||
|
active_lidarr_block,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_navigation_pushed!(app, active_lidarr_block.into());
|
||||||
|
assert!(app.is_routing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_refresh_key_no_op_when_not_ready(
|
||||||
|
#[values(ActiveLidarrBlock::TrackDetails, ActiveLidarrBlock::TrackHistory)]
|
||||||
|
active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(active_lidarr_block.into());
|
||||||
|
app.is_routing = false;
|
||||||
|
|
||||||
|
TrackDetailsHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.refresh.key,
|
||||||
|
&mut app,
|
||||||
|
active_lidarr_block,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(app.get_current_route(), active_lidarr_block.into());
|
||||||
|
assert!(!app.is_routing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_details_handler_accepts() {
|
||||||
|
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
||||||
|
if TRACK_DETAILS_BLOCKS.contains(&active_lidarr_block) {
|
||||||
|
assert!(TrackDetailsHandler::accepts(active_lidarr_block));
|
||||||
|
} else {
|
||||||
|
assert!(!TrackDetailsHandler::accepts(active_lidarr_block));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_track_details_handler_ignore_special_keys(
|
||||||
|
#[values(true, false)] ignore_special_keys_for_textbox_input: bool,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.ignore_special_keys_for_textbox_input = ignore_special_keys_for_textbox_input;
|
||||||
|
let handler = TrackDetailsHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.esc.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::default(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
handler.ignore_special_keys(),
|
||||||
|
ignore_special_keys_for_textbox_input
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_details_handler_is_not_ready_when_loading() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::TrackDetails.into());
|
||||||
|
app.is_loading = true;
|
||||||
|
|
||||||
|
let handler = TrackDetailsHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.esc.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::TrackDetails,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(!handler.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_details_handler_is_not_ready_when_album_details_modal_is_empty() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::TrackDetails.into());
|
||||||
|
app.is_loading = false;
|
||||||
|
|
||||||
|
let handler = TrackDetailsHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.esc.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::TrackDetails,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(!handler.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_details_handler_is_not_ready_when_track_details_modal_is_empty() {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal = None;
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::TrackDetails.into());
|
||||||
|
app.is_loading = false;
|
||||||
|
|
||||||
|
let handler = TrackDetailsHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.esc.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::TrackDetails,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(!handler.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_details_handler_is_not_ready_when_track_details_is_empty() {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details = ScrollableText::default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::TrackDetails.into());
|
||||||
|
app.is_loading = false;
|
||||||
|
|
||||||
|
let handler = TrackDetailsHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.esc.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::TrackDetails,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(!handler.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_details_handler_is_not_ready_when_track_history_table_is_empty() {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_history = StatefulTable::default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::TrackHistory.into());
|
||||||
|
app.is_loading = false;
|
||||||
|
|
||||||
|
let handler = TrackDetailsHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.esc.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::TrackHistory,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(!handler.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_track_details_handler_is_ready(
|
||||||
|
#[values(
|
||||||
|
ActiveLidarrBlock::TrackDetails,
|
||||||
|
ActiveLidarrBlock::TrackHistory,
|
||||||
|
)]
|
||||||
|
active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.push_navigation_stack(active_lidarr_block.into());
|
||||||
|
app.is_loading = false;
|
||||||
|
|
||||||
|
let handler = TrackDetailsHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.esc.key,
|
||||||
|
&mut app,
|
||||||
|
active_lidarr_block,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(handler.is_ready());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -422,6 +422,8 @@ pub struct LidarrHistoryItem {
|
|||||||
pub album_id: i64,
|
pub album_id: i64,
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
pub artist_id: i64,
|
pub artist_id: i64,
|
||||||
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
|
pub track_id: i64,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub quality: QualityWrapper,
|
pub quality: QualityWrapper,
|
||||||
pub date: DateTime<Utc>,
|
pub date: DateTime<Utc>,
|
||||||
@@ -556,6 +558,7 @@ pub struct Track {
|
|||||||
pub duration: i64,
|
pub duration: i64,
|
||||||
pub has_file: bool,
|
pub has_file: bool,
|
||||||
pub ratings: Ratings,
|
pub ratings: Ratings,
|
||||||
|
pub track_file: Option<TrackFile>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LidarrSerdeable> for Serdeable {
|
impl From<LidarrSerdeable> for Serdeable {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ use itertools::Itertools;
|
|||||||
use strum::EnumIter;
|
use strum::EnumIter;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use {
|
use {
|
||||||
|
super::modals::TrackDetailsModal,
|
||||||
crate::models::lidarr_models::{MonitorType, NewItemMonitorType},
|
crate::models::lidarr_models::{MonitorType, NewItemMonitorType},
|
||||||
crate::models::stateful_table::SortOption,
|
crate::models::stateful_table::SortOption,
|
||||||
crate::network::lidarr_network::lidarr_network_test_utils::test_utils::indexer_settings,
|
crate::network::lidarr_network::lidarr_network_test_utils::test_utils::indexer_settings,
|
||||||
@@ -292,7 +293,21 @@ impl LidarrData<'_> {
|
|||||||
.metadata_profile_list
|
.metadata_profile_list
|
||||||
.set_items(vec![metadata_profile().name]);
|
.set_items(vec![metadata_profile().name]);
|
||||||
|
|
||||||
let mut album_details_modal = AlbumDetailsModal::default();
|
let mut track_details_modal = TrackDetailsModal::default();
|
||||||
|
track_details_modal.track_details = ScrollableText::with_string("Some details".to_owned());
|
||||||
|
track_details_modal
|
||||||
|
.track_history
|
||||||
|
.set_items(vec![lidarr_history_item()]);
|
||||||
|
track_details_modal.track_history.search = Some("track history search".into());
|
||||||
|
track_details_modal.track_history.filter = Some("track history filter".into());
|
||||||
|
track_details_modal
|
||||||
|
.track_history
|
||||||
|
.sorting(vec![sort_option!(id)]);
|
||||||
|
|
||||||
|
let mut album_details_modal = AlbumDetailsModal {
|
||||||
|
track_details_modal: Some(track_details_modal),
|
||||||
|
..AlbumDetailsModal::default()
|
||||||
|
};
|
||||||
album_details_modal.tracks.set_items(vec![track()]);
|
album_details_modal.tracks.set_items(vec![track()]);
|
||||||
album_details_modal.tracks.search = Some("album search".into());
|
album_details_modal.tracks.search = Some("album search".into());
|
||||||
album_details_modal
|
album_details_modal
|
||||||
@@ -469,6 +484,8 @@ pub enum ActiveLidarrBlock {
|
|||||||
FilterHistoryError,
|
FilterHistoryError,
|
||||||
FilterArtistHistory,
|
FilterArtistHistory,
|
||||||
FilterArtistHistoryError,
|
FilterArtistHistoryError,
|
||||||
|
FilterTrackHistory,
|
||||||
|
FilterTrackHistoryError,
|
||||||
History,
|
History,
|
||||||
HistoryItemDetails,
|
HistoryItemDetails,
|
||||||
HistorySortPrompt,
|
HistorySortPrompt,
|
||||||
@@ -499,12 +516,18 @@ pub enum ActiveLidarrBlock {
|
|||||||
SearchArtistHistoryError,
|
SearchArtistHistoryError,
|
||||||
SearchTracks,
|
SearchTracks,
|
||||||
SearchTracksError,
|
SearchTracksError,
|
||||||
|
SearchTrackHistory,
|
||||||
|
SearchTrackHistoryError,
|
||||||
System,
|
System,
|
||||||
SystemLogs,
|
SystemLogs,
|
||||||
SystemQueuedEvents,
|
SystemQueuedEvents,
|
||||||
SystemTasks,
|
SystemTasks,
|
||||||
SystemTaskStartConfirmPrompt,
|
SystemTaskStartConfirmPrompt,
|
||||||
SystemUpdates,
|
SystemUpdates,
|
||||||
|
TrackDetails,
|
||||||
|
TrackHistory,
|
||||||
|
TrackHistoryDetails,
|
||||||
|
TrackHistorySortPrompt,
|
||||||
UpdateAllArtistsPrompt,
|
UpdateAllArtistsPrompt,
|
||||||
UpdateAndScanArtistPrompt,
|
UpdateAndScanArtistPrompt,
|
||||||
UpdateDownloadsPrompt,
|
UpdateDownloadsPrompt,
|
||||||
@@ -767,6 +790,17 @@ pub static SYSTEM_DETAILS_BLOCKS: [ActiveLidarrBlock; 5] = [
|
|||||||
ActiveLidarrBlock::SystemUpdates,
|
ActiveLidarrBlock::SystemUpdates,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pub static TRACK_DETAILS_BLOCKS: [ActiveLidarrBlock; 8] = [
|
||||||
|
ActiveLidarrBlock::TrackDetails,
|
||||||
|
ActiveLidarrBlock::TrackHistory,
|
||||||
|
ActiveLidarrBlock::TrackHistoryDetails,
|
||||||
|
ActiveLidarrBlock::SearchTrackHistory,
|
||||||
|
ActiveLidarrBlock::SearchTrackHistoryError,
|
||||||
|
ActiveLidarrBlock::FilterTrackHistory,
|
||||||
|
ActiveLidarrBlock::FilterTrackHistoryError,
|
||||||
|
ActiveLidarrBlock::TrackHistorySortPrompt,
|
||||||
|
];
|
||||||
|
|
||||||
impl From<ActiveLidarrBlock> for Route {
|
impl From<ActiveLidarrBlock> for Route {
|
||||||
fn from(active_lidarr_block: ActiveLidarrBlock) -> Route {
|
fn from(active_lidarr_block: ActiveLidarrBlock) -> Route {
|
||||||
Route::Lidarr(active_lidarr_block, None)
|
Route::Lidarr(active_lidarr_block, None)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ mod tests {
|
|||||||
EDIT_ARTIST_SELECTION_BLOCKS, EDIT_INDEXER_BLOCKS, EDIT_INDEXER_NZB_SELECTION_BLOCKS,
|
EDIT_ARTIST_SELECTION_BLOCKS, EDIT_INDEXER_BLOCKS, EDIT_INDEXER_NZB_SELECTION_BLOCKS,
|
||||||
EDIT_INDEXER_TORRENT_SELECTION_BLOCKS, HISTORY_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
EDIT_INDEXER_TORRENT_SELECTION_BLOCKS, HISTORY_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
||||||
INDEXER_SETTINGS_SELECTION_BLOCKS, INDEXERS_BLOCKS, ROOT_FOLDERS_BLOCKS, SYSTEM_DETAILS_BLOCKS,
|
INDEXER_SETTINGS_SELECTION_BLOCKS, INDEXERS_BLOCKS, ROOT_FOLDERS_BLOCKS, SYSTEM_DETAILS_BLOCKS,
|
||||||
|
TRACK_DETAILS_BLOCKS,
|
||||||
};
|
};
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
BlockSelectionState, Route,
|
BlockSelectionState, Route,
|
||||||
@@ -695,4 +696,17 @@ mod tests {
|
|||||||
assert!(SYSTEM_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::SystemTaskStartConfirmPrompt));
|
assert!(SYSTEM_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::SystemTaskStartConfirmPrompt));
|
||||||
assert!(SYSTEM_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::SystemUpdates));
|
assert!(SYSTEM_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::SystemUpdates));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_details_blocks_contents() {
|
||||||
|
assert_eq!(TRACK_DETAILS_BLOCKS.len(), 8);
|
||||||
|
assert!(TRACK_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::TrackDetails));
|
||||||
|
assert!(TRACK_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::TrackHistory));
|
||||||
|
assert!(TRACK_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::TrackHistoryDetails));
|
||||||
|
assert!(TRACK_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::SearchTrackHistory));
|
||||||
|
assert!(TRACK_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::SearchTrackHistoryError));
|
||||||
|
assert!(TRACK_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::FilterTrackHistory));
|
||||||
|
assert!(TRACK_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::FilterTrackHistoryError));
|
||||||
|
assert!(TRACK_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::TrackHistorySortPrompt));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
use super::lidarr_data::{ActiveLidarrBlock, LidarrData};
|
use super::lidarr_data::{ActiveLidarrBlock, LidarrData};
|
||||||
use crate::app::lidarr::lidarr_context_clues::{
|
use crate::app::lidarr::lidarr_context_clues::{
|
||||||
ALBUM_DETAILS_CONTEXT_CLUES, ALBUM_HISTORY_CONTEXT_CLUES, MANUAL_ALBUM_SEARCH_CONTEXT_CLUES,
|
ALBUM_DETAILS_CONTEXT_CLUES, ALBUM_HISTORY_CONTEXT_CLUES, MANUAL_ALBUM_SEARCH_CONTEXT_CLUES,
|
||||||
|
TRACK_DETAILS_CONTEXT_CLUES, TRACK_HISTORY_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::models::lidarr_models::{LidarrHistoryItem, LidarrRelease, Track, TrackFile};
|
use crate::models::lidarr_models::{LidarrHistoryItem, LidarrRelease, Track, TrackFile};
|
||||||
use crate::models::servarr_data::modals::EditIndexerModal;
|
use crate::models::servarr_data::modals::EditIndexerModal;
|
||||||
use crate::models::servarr_models::Indexer;
|
use crate::models::servarr_models::Indexer;
|
||||||
use crate::models::stateful_table::StatefulTable;
|
use crate::models::stateful_table::StatefulTable;
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
HorizontallyScrollableText, TabRoute, TabState,
|
HorizontallyScrollableText, ScrollableText, TabRoute, TabState,
|
||||||
lidarr_models::{MonitorType, NewItemMonitorType},
|
lidarr_models::{MonitorType, NewItemMonitorType},
|
||||||
servarr_models::RootFolder,
|
servarr_models::RootFolder,
|
||||||
stateful_list::StatefulList,
|
stateful_list::StatefulList,
|
||||||
@@ -226,7 +227,7 @@ impl From<&LidarrData<'_>> for AddRootFolderModal {
|
|||||||
pub struct AlbumDetailsModal {
|
pub struct AlbumDetailsModal {
|
||||||
pub tracks: StatefulTable<Track>,
|
pub tracks: StatefulTable<Track>,
|
||||||
pub track_files: StatefulTable<TrackFile>,
|
pub track_files: StatefulTable<TrackFile>,
|
||||||
// pub track_details_modal: Option<EpisodeDetailsModal>,
|
pub track_details_modal: Option<TrackDetailsModal>,
|
||||||
pub album_history: StatefulTable<LidarrHistoryItem>,
|
pub album_history: StatefulTable<LidarrHistoryItem>,
|
||||||
pub album_releases: StatefulTable<LidarrRelease>,
|
pub album_releases: StatefulTable<LidarrRelease>,
|
||||||
pub album_details_tabs: TabState,
|
pub album_details_tabs: TabState,
|
||||||
@@ -236,7 +237,7 @@ impl Default for AlbumDetailsModal {
|
|||||||
fn default() -> AlbumDetailsModal {
|
fn default() -> AlbumDetailsModal {
|
||||||
AlbumDetailsModal {
|
AlbumDetailsModal {
|
||||||
tracks: StatefulTable::default(),
|
tracks: StatefulTable::default(),
|
||||||
// TODO episode_details_modal: None,
|
track_details_modal: None,
|
||||||
track_files: StatefulTable::default(),
|
track_files: StatefulTable::default(),
|
||||||
album_releases: StatefulTable::default(),
|
album_releases: StatefulTable::default(),
|
||||||
album_history: StatefulTable::default(),
|
album_history: StatefulTable::default(),
|
||||||
@@ -263,3 +264,33 @@ impl Default for AlbumDetailsModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, derive(Debug))]
|
||||||
|
pub struct TrackDetailsModal {
|
||||||
|
pub track_details: ScrollableText,
|
||||||
|
pub track_history: StatefulTable<LidarrHistoryItem>,
|
||||||
|
pub track_details_tabs: TabState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TrackDetailsModal {
|
||||||
|
fn default() -> Self {
|
||||||
|
TrackDetailsModal {
|
||||||
|
track_details: ScrollableText::default(),
|
||||||
|
track_history: StatefulTable::default(),
|
||||||
|
track_details_tabs: TabState::new(vec![
|
||||||
|
TabRoute {
|
||||||
|
title: "Track Details".to_string(),
|
||||||
|
route: ActiveLidarrBlock::TrackDetails.into(),
|
||||||
|
contextual_help: Some(&TRACK_DETAILS_CONTEXT_CLUES),
|
||||||
|
config: None,
|
||||||
|
},
|
||||||
|
TabRoute {
|
||||||
|
title: "History".to_string(),
|
||||||
|
route: ActiveLidarrBlock::TrackHistory.into(),
|
||||||
|
contextual_help: Some(&TRACK_HISTORY_CONTEXT_CLUES),
|
||||||
|
config: None,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,11 +2,12 @@
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::app::lidarr::lidarr_context_clues::{
|
use crate::app::lidarr::lidarr_context_clues::{
|
||||||
ALBUM_DETAILS_CONTEXT_CLUES, ALBUM_HISTORY_CONTEXT_CLUES, MANUAL_ALBUM_SEARCH_CONTEXT_CLUES,
|
ALBUM_DETAILS_CONTEXT_CLUES, ALBUM_HISTORY_CONTEXT_CLUES, MANUAL_ALBUM_SEARCH_CONTEXT_CLUES,
|
||||||
|
TRACK_DETAILS_CONTEXT_CLUES, TRACK_HISTORY_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::models::lidarr_models::{Artist, MonitorType, NewItemMonitorType};
|
use crate::models::lidarr_models::{Artist, MonitorType, NewItemMonitorType};
|
||||||
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LidarrData};
|
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LidarrData};
|
||||||
use crate::models::servarr_data::lidarr::modals::{
|
use crate::models::servarr_data::lidarr::modals::{
|
||||||
AddArtistModal, AlbumDetailsModal, EditArtistModal,
|
AddArtistModal, AlbumDetailsModal, EditArtistModal, TrackDetailsModal,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::modals::EditIndexerModal;
|
use crate::models::servarr_data::modals::EditIndexerModal;
|
||||||
use crate::models::servarr_models::{Indexer, IndexerField, RootFolder};
|
use crate::models::servarr_models::{Indexer, IndexerField, RootFolder};
|
||||||
@@ -92,14 +93,14 @@ mod tests {
|
|||||||
quality_profile_id: 1,
|
quality_profile_id: 1,
|
||||||
metadata_profile_id: 1,
|
metadata_profile_id: 1,
|
||||||
path: "/nfs/music/test_artist".to_owned(),
|
path: "/nfs/music/test_artist".to_owned(),
|
||||||
tags: vec![serde_json::Number::from(1)],
|
tags: vec![Number::from(1)],
|
||||||
..Artist::default()
|
..Artist::default()
|
||||||
};
|
};
|
||||||
lidarr_data.artists.set_items(vec![artist]);
|
lidarr_data.artists.set_items(vec![artist]);
|
||||||
|
|
||||||
let edit_artist_modal = EditArtistModal::from(&lidarr_data);
|
let edit_artist_modal = EditArtistModal::from(&lidarr_data);
|
||||||
|
|
||||||
assert_eq!(edit_artist_modal.monitored, Some(true));
|
assert_some_eq_x!(&edit_artist_modal.monitored, &true);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*edit_artist_modal.monitor_list.current_selection(),
|
*edit_artist_modal.monitor_list.current_selection(),
|
||||||
NewItemMonitorType::All
|
NewItemMonitorType::All
|
||||||
@@ -205,24 +206,24 @@ mod tests {
|
|||||||
let edit_indexer_modal = EditIndexerModal::from(&lidarr_data);
|
let edit_indexer_modal = EditIndexerModal::from(&lidarr_data);
|
||||||
|
|
||||||
assert_str_eq!(edit_indexer_modal.name.text, "Test");
|
assert_str_eq!(edit_indexer_modal.name.text, "Test");
|
||||||
assert_eq!(edit_indexer_modal.enable_rss, Some(true));
|
assert_some_eq_x!(&edit_indexer_modal.enable_rss, &true);
|
||||||
assert_eq!(edit_indexer_modal.enable_automatic_search, Some(true));
|
assert_some_eq_x!(&edit_indexer_modal.enable_automatic_search, &true);
|
||||||
assert_eq!(edit_indexer_modal.enable_interactive_search, Some(true));
|
assert_some_eq_x!(&edit_indexer_modal.enable_interactive_search, &true);
|
||||||
assert_eq!(edit_indexer_modal.priority, 1);
|
assert_eq!(edit_indexer_modal.priority, 1);
|
||||||
assert_str_eq!(edit_indexer_modal.url.text, "https://test.com");
|
assert_str_eq!(edit_indexer_modal.url.text, "https://test.com");
|
||||||
assert_str_eq!(edit_indexer_modal.api_key.text, "1234");
|
assert_str_eq!(edit_indexer_modal.api_key.text, "1234");
|
||||||
assert!(edit_indexer_modal.seed_ratio.text.is_empty());
|
assert_is_empty!(edit_indexer_modal.seed_ratio.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_album_details_modal_default() {
|
fn test_album_details_modal_default() {
|
||||||
let album_details_modal = AlbumDetailsModal::default();
|
let album_details_modal = AlbumDetailsModal::default();
|
||||||
|
|
||||||
assert!(album_details_modal.tracks.is_empty());
|
assert_is_empty!(album_details_modal.tracks);
|
||||||
// assert!(album_details_modal.track_details_modal.is_none());
|
assert_none!(album_details_modal.track_details_modal);
|
||||||
assert!(album_details_modal.track_files.is_empty());
|
assert_is_empty!(album_details_modal.track_files);
|
||||||
assert!(album_details_modal.album_releases.is_empty());
|
assert_is_empty!(album_details_modal.album_releases);
|
||||||
assert!(album_details_modal.album_history.is_empty());
|
assert_is_empty!(album_details_modal.album_history);
|
||||||
|
|
||||||
assert_eq!(album_details_modal.album_details_tabs.tabs.len(), 3);
|
assert_eq!(album_details_modal.album_details_tabs.tabs.len(), 3);
|
||||||
|
|
||||||
@@ -234,15 +235,8 @@ mod tests {
|
|||||||
album_details_modal.album_details_tabs.tabs[0].route,
|
album_details_modal.album_details_tabs.tabs[0].route,
|
||||||
ActiveLidarrBlock::AlbumDetails.into()
|
ActiveLidarrBlock::AlbumDetails.into()
|
||||||
);
|
);
|
||||||
assert!(
|
assert_some_eq_x!(
|
||||||
album_details_modal.album_details_tabs.tabs[0]
|
&album_details_modal.album_details_tabs.tabs[0].contextual_help,
|
||||||
.contextual_help
|
|
||||||
.is_some()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
album_details_modal.album_details_tabs.tabs[0]
|
|
||||||
.contextual_help
|
|
||||||
.unwrap(),
|
|
||||||
&ALBUM_DETAILS_CONTEXT_CLUES
|
&ALBUM_DETAILS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(album_details_modal.album_details_tabs.tabs[0].config, None);
|
assert_eq!(album_details_modal.album_details_tabs.tabs[0].config, None);
|
||||||
@@ -255,15 +249,8 @@ mod tests {
|
|||||||
album_details_modal.album_details_tabs.tabs[1].route,
|
album_details_modal.album_details_tabs.tabs[1].route,
|
||||||
ActiveLidarrBlock::AlbumHistory.into()
|
ActiveLidarrBlock::AlbumHistory.into()
|
||||||
);
|
);
|
||||||
assert!(
|
assert_some_eq_x!(
|
||||||
album_details_modal.album_details_tabs.tabs[1]
|
&album_details_modal.album_details_tabs.tabs[1].contextual_help,
|
||||||
.contextual_help
|
|
||||||
.is_some()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
album_details_modal.album_details_tabs.tabs[1]
|
|
||||||
.contextual_help
|
|
||||||
.unwrap(),
|
|
||||||
&ALBUM_HISTORY_CONTEXT_CLUES
|
&ALBUM_HISTORY_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(album_details_modal.album_details_tabs.tabs[1].config, None);
|
assert_eq!(album_details_modal.album_details_tabs.tabs[1].config, None);
|
||||||
@@ -276,17 +263,48 @@ mod tests {
|
|||||||
album_details_modal.album_details_tabs.tabs[2].route,
|
album_details_modal.album_details_tabs.tabs[2].route,
|
||||||
ActiveLidarrBlock::ManualAlbumSearch.into()
|
ActiveLidarrBlock::ManualAlbumSearch.into()
|
||||||
);
|
);
|
||||||
assert!(
|
assert_some_eq_x!(
|
||||||
album_details_modal.album_details_tabs.tabs[2]
|
&album_details_modal.album_details_tabs.tabs[2].contextual_help,
|
||||||
.contextual_help
|
|
||||||
.is_some()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
album_details_modal.album_details_tabs.tabs[2]
|
|
||||||
.contextual_help
|
|
||||||
.unwrap(),
|
|
||||||
&MANUAL_ALBUM_SEARCH_CONTEXT_CLUES
|
&MANUAL_ALBUM_SEARCH_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(album_details_modal.album_details_tabs.tabs[2].config, None);
|
assert_eq!(album_details_modal.album_details_tabs.tabs[2].config, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_details_modal_default() {
|
||||||
|
let track_details_modal = TrackDetailsModal::default();
|
||||||
|
|
||||||
|
assert_is_empty!(track_details_modal.track_details);
|
||||||
|
assert_is_empty!(track_details_modal.track_history);
|
||||||
|
|
||||||
|
assert_eq!(track_details_modal.track_details_tabs.tabs.len(), 2);
|
||||||
|
|
||||||
|
assert_str_eq!(
|
||||||
|
track_details_modal.track_details_tabs.tabs[0].title,
|
||||||
|
"Track Details"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
track_details_modal.track_details_tabs.tabs[0].route,
|
||||||
|
ActiveLidarrBlock::TrackDetails.into()
|
||||||
|
);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
&track_details_modal.track_details_tabs.tabs[0].contextual_help,
|
||||||
|
&TRACK_DETAILS_CONTEXT_CLUES
|
||||||
|
);
|
||||||
|
assert_eq!(track_details_modal.track_details_tabs.tabs[0].config, None);
|
||||||
|
|
||||||
|
assert_str_eq!(
|
||||||
|
track_details_modal.track_details_tabs.tabs[1].title,
|
||||||
|
"History"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
track_details_modal.track_details_tabs.tabs[1].route,
|
||||||
|
ActiveLidarrBlock::TrackHistory.into()
|
||||||
|
);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
&track_details_modal.track_details_tabs.tabs[1].contextual_help,
|
||||||
|
&TRACK_HISTORY_CONTEXT_CLUES
|
||||||
|
);
|
||||||
|
assert_eq!(track_details_modal.track_details_tabs.tabs[1].config, None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ mod tests {
|
|||||||
"sourceTitle": "z album",
|
"sourceTitle": "z album",
|
||||||
"albumId": 1007,
|
"albumId": 1007,
|
||||||
"artistId": 1007,
|
"artistId": 1007,
|
||||||
|
"trackId": 1007,
|
||||||
"quality": { "quality": { "name": "Lossless" } },
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
"date": "2023-01-01T00:00:00Z",
|
"date": "2023-01-01T00:00:00Z",
|
||||||
"eventType": "grabbed",
|
"eventType": "grabbed",
|
||||||
@@ -30,6 +31,7 @@ mod tests {
|
|||||||
"sourceTitle": "An Album",
|
"sourceTitle": "An Album",
|
||||||
"albumId": 2001,
|
"albumId": 2001,
|
||||||
"artistId": 2001,
|
"artistId": 2001,
|
||||||
|
"trackId": 2001,
|
||||||
"quality": { "quality": { "name": "Lossless" } },
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
"date": "2023-01-01T00:00:00Z",
|
"date": "2023-01-01T00:00:00Z",
|
||||||
"eventType": "grabbed",
|
"eventType": "grabbed",
|
||||||
@@ -49,6 +51,7 @@ mod tests {
|
|||||||
id: 123,
|
id: 123,
|
||||||
album_id: 1007,
|
album_id: 1007,
|
||||||
artist_id: 1007,
|
artist_id: 1007,
|
||||||
|
track_id: 1007,
|
||||||
source_title: "z album".into(),
|
source_title: "z album".into(),
|
||||||
..lidarr_history_item()
|
..lidarr_history_item()
|
||||||
},
|
},
|
||||||
@@ -56,6 +59,7 @@ mod tests {
|
|||||||
id: 456,
|
id: 456,
|
||||||
album_id: 2001,
|
album_id: 2001,
|
||||||
artist_id: 2001,
|
artist_id: 2001,
|
||||||
|
track_id: 2001,
|
||||||
source_title: "An Album".into(),
|
source_title: "An Album".into(),
|
||||||
..lidarr_history_item()
|
..lidarr_history_item()
|
||||||
},
|
},
|
||||||
@@ -113,6 +117,7 @@ mod tests {
|
|||||||
"sourceTitle": "z album",
|
"sourceTitle": "z album",
|
||||||
"albumId": 1007,
|
"albumId": 1007,
|
||||||
"artistId": 1007,
|
"artistId": 1007,
|
||||||
|
"trackId": 1007,
|
||||||
"quality": { "quality": { "name": "Lossless" } },
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
"date": "2023-01-01T00:00:00Z",
|
"date": "2023-01-01T00:00:00Z",
|
||||||
"eventType": "grabbed",
|
"eventType": "grabbed",
|
||||||
@@ -126,6 +131,7 @@ mod tests {
|
|||||||
"sourceTitle": "An Album",
|
"sourceTitle": "An Album",
|
||||||
"albumId": 2001,
|
"albumId": 2001,
|
||||||
"artistId": 2001,
|
"artistId": 2001,
|
||||||
|
"trackId": 2001,
|
||||||
"quality": { "quality": { "name": "Lossless" } },
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
"date": "2023-01-01T00:00:00Z",
|
"date": "2023-01-01T00:00:00Z",
|
||||||
"eventType": "grabbed",
|
"eventType": "grabbed",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ mod tests {
|
|||||||
use crate::models::lidarr_models::{
|
use crate::models::lidarr_models::{
|
||||||
Album, DeleteParams, LidarrHistoryItem, LidarrRelease, LidarrSerdeable,
|
Album, DeleteParams, LidarrHistoryItem, LidarrRelease, LidarrSerdeable,
|
||||||
};
|
};
|
||||||
|
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
|
||||||
use crate::models::servarr_data::lidarr::modals::AlbumDetailsModal;
|
use crate::models::servarr_data::lidarr::modals::AlbumDetailsModal;
|
||||||
use crate::models::stateful_table::SortOption;
|
use crate::models::stateful_table::SortOption;
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
@@ -146,6 +147,7 @@ mod tests {
|
|||||||
"sourceTitle": "z album",
|
"sourceTitle": "z album",
|
||||||
"albumId": 1007,
|
"albumId": 1007,
|
||||||
"artistId": 1007,
|
"artistId": 1007,
|
||||||
|
"trackId": 1007,
|
||||||
"quality": { "quality": { "name": "Lossless" } },
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
"date": "2023-01-01T00:00:00Z",
|
"date": "2023-01-01T00:00:00Z",
|
||||||
"eventType": "grabbed",
|
"eventType": "grabbed",
|
||||||
@@ -159,6 +161,7 @@ mod tests {
|
|||||||
"sourceTitle": "An Album",
|
"sourceTitle": "An Album",
|
||||||
"albumId": 2001,
|
"albumId": 2001,
|
||||||
"artistId": 2001,
|
"artistId": 2001,
|
||||||
|
"trackId": 2001,
|
||||||
"quality": { "quality": { "name": "Lossless" } },
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
"date": "2023-01-01T00:00:00Z",
|
"date": "2023-01-01T00:00:00Z",
|
||||||
"eventType": "grabbed",
|
"eventType": "grabbed",
|
||||||
@@ -173,6 +176,7 @@ mod tests {
|
|||||||
id: 123,
|
id: 123,
|
||||||
artist_id: 1007,
|
artist_id: 1007,
|
||||||
album_id: 1007,
|
album_id: 1007,
|
||||||
|
track_id: 1007,
|
||||||
source_title: "z album".into(),
|
source_title: "z album".into(),
|
||||||
..lidarr_history_item()
|
..lidarr_history_item()
|
||||||
},
|
},
|
||||||
@@ -180,6 +184,7 @@ mod tests {
|
|||||||
id: 456,
|
id: 456,
|
||||||
artist_id: 2001,
|
artist_id: 2001,
|
||||||
album_id: 2001,
|
album_id: 2001,
|
||||||
|
track_id: 2001,
|
||||||
source_title: "An Album".into(),
|
source_title: "An Album".into(),
|
||||||
..lidarr_history_item()
|
..lidarr_history_item()
|
||||||
},
|
},
|
||||||
@@ -270,6 +275,7 @@ mod tests {
|
|||||||
"sourceTitle": "z album",
|
"sourceTitle": "z album",
|
||||||
"albumId": 1007,
|
"albumId": 1007,
|
||||||
"artistId": 1007,
|
"artistId": 1007,
|
||||||
|
"trackId": 1007,
|
||||||
"quality": { "quality": { "name": "Lossless" } },
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
"date": "2023-01-01T00:00:00Z",
|
"date": "2023-01-01T00:00:00Z",
|
||||||
"eventType": "grabbed",
|
"eventType": "grabbed",
|
||||||
@@ -283,6 +289,7 @@ mod tests {
|
|||||||
"sourceTitle": "An Album",
|
"sourceTitle": "An Album",
|
||||||
"albumId": 2001,
|
"albumId": 2001,
|
||||||
"artistId": 2001,
|
"artistId": 2001,
|
||||||
|
"trackId": 2001,
|
||||||
"quality": { "quality": { "name": "Lossless" } },
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
"date": "2023-01-01T00:00:00Z",
|
"date": "2023-01-01T00:00:00Z",
|
||||||
"eventType": "grabbed",
|
"eventType": "grabbed",
|
||||||
@@ -297,6 +304,7 @@ mod tests {
|
|||||||
id: 123,
|
id: 123,
|
||||||
artist_id: 1007,
|
artist_id: 1007,
|
||||||
album_id: 1007,
|
album_id: 1007,
|
||||||
|
track_id: 1007,
|
||||||
source_title: "z album".into(),
|
source_title: "z album".into(),
|
||||||
..lidarr_history_item()
|
..lidarr_history_item()
|
||||||
},
|
},
|
||||||
@@ -304,6 +312,7 @@ mod tests {
|
|||||||
id: 456,
|
id: 456,
|
||||||
artist_id: 2001,
|
artist_id: 2001,
|
||||||
album_id: 2001,
|
album_id: 2001,
|
||||||
|
track_id: 2001,
|
||||||
source_title: "An Album".into(),
|
source_title: "An Album".into(),
|
||||||
..lidarr_history_item()
|
..lidarr_history_item()
|
||||||
},
|
},
|
||||||
@@ -350,6 +359,95 @@ mod tests {
|
|||||||
assert_eq!(history, response);
|
assert_eq!(history, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_lidarr_album_history_event_no_op_when_user_is_selecting_sort_options() {
|
||||||
|
let history_json = json!([{
|
||||||
|
"id": 123,
|
||||||
|
"sourceTitle": "z album",
|
||||||
|
"albumId": 1007,
|
||||||
|
"artistId": 1007,
|
||||||
|
"trackId": 1007,
|
||||||
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
|
"date": "2023-01-01T00:00:00Z",
|
||||||
|
"eventType": "grabbed",
|
||||||
|
"data": {
|
||||||
|
"droppedPath": "/nfs/nzbget/completed/music/Something/cool.mp3",
|
||||||
|
"importedPath": "/nfs/music/Something/Album 1/Cool.mp3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 456,
|
||||||
|
"sourceTitle": "An Album",
|
||||||
|
"albumId": 2001,
|
||||||
|
"artistId": 2001,
|
||||||
|
"trackId": 2001,
|
||||||
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
|
"date": "2023-01-01T00:00:00Z",
|
||||||
|
"eventType": "grabbed",
|
||||||
|
"data": {
|
||||||
|
"droppedPath": "/nfs/nzbget/completed/music/Something/cool.mp3",
|
||||||
|
"importedPath": "/nfs/music/Something/Album 1/Cool.mp3"
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
let response: Vec<LidarrHistoryItem> = serde_json::from_value(history_json.clone()).unwrap();
|
||||||
|
let (mock, app, _server) = MockServarrApi::get()
|
||||||
|
.returns(history_json)
|
||||||
|
.query("artistId=1&albumId=1")
|
||||||
|
.build_for(LidarrEvent::GetAlbumHistory(1, 1))
|
||||||
|
.await;
|
||||||
|
app.lock().await.data.lidarr_data.album_details_modal = Some(AlbumDetailsModal::default());
|
||||||
|
app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.album_history
|
||||||
|
.sort_asc = true;
|
||||||
|
app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.push_navigation_stack(ActiveLidarrBlock::AlbumHistorySortPrompt.into());
|
||||||
|
app.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
let LidarrSerdeable::LidarrHistoryItems(history) = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetAlbumHistory(1, 1))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("Expected LidarrHistoryItems")
|
||||||
|
};
|
||||||
|
mock.assert_async().await;
|
||||||
|
assert_is_empty!(
|
||||||
|
app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.album_history
|
||||||
|
.items
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.album_history
|
||||||
|
.sort_asc
|
||||||
|
);
|
||||||
|
assert_eq!(history, response);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_handle_get_album_releases_event() {
|
async fn test_handle_get_album_releases_event() {
|
||||||
let release_json = json!([
|
let release_json = json!([
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
use crate::models::Route;
|
||||||
use crate::models::lidarr_models::{
|
use crate::models::lidarr_models::{
|
||||||
Album, DeleteParams, LidarrCommandBody, LidarrHistoryItem, LidarrRelease,
|
Album, DeleteParams, LidarrCommandBody, LidarrHistoryItem, LidarrRelease,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::lidarr::modals::AlbumDetailsModal;
|
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
use crate::network::{Network, RequestMethod};
|
use crate::network::{Network, RequestMethod};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
@@ -75,28 +76,25 @@ impl Network<'_, '_> {
|
|||||||
|
|
||||||
self
|
self
|
||||||
.handle_request::<(), Vec<LidarrHistoryItem>>(request_props, |history_items, mut app| {
|
.handle_request::<(), Vec<LidarrHistoryItem>>(request_props, |history_items, mut app| {
|
||||||
if app.data.lidarr_data.album_details_modal.is_none() {
|
let is_sorting = matches!(
|
||||||
app.data.lidarr_data.album_details_modal = Some(AlbumDetailsModal::default());
|
app.get_current_route(),
|
||||||
}
|
Route::Lidarr(ActiveLidarrBlock::AlbumHistorySortPrompt, _)
|
||||||
|
);
|
||||||
|
|
||||||
|
if !is_sorting {
|
||||||
|
let album_details_modal = app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.get_or_insert_default();
|
||||||
|
|
||||||
let mut history_vec = history_items;
|
let mut history_vec = history_items;
|
||||||
history_vec.sort_by(|a, b| a.id.cmp(&b.id));
|
history_vec.sort_by(|a, b| a.id.cmp(&b.id));
|
||||||
app
|
album_details_modal.album_history.set_items(history_vec);
|
||||||
.data
|
album_details_modal
|
||||||
.lidarr_data
|
|
||||||
.album_details_modal
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.album_history
|
|
||||||
.set_items(history_vec);
|
|
||||||
app
|
|
||||||
.data
|
|
||||||
.lidarr_data
|
|
||||||
.album_details_modal
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.album_history
|
.album_history
|
||||||
.apply_sorting_toggle(false);
|
.apply_sorting_toggle(false);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -121,21 +119,18 @@ impl Network<'_, '_> {
|
|||||||
|
|
||||||
self
|
self
|
||||||
.handle_request::<(), Vec<LidarrRelease>>(request_props, |release_vec, mut app| {
|
.handle_request::<(), Vec<LidarrRelease>>(request_props, |release_vec, mut app| {
|
||||||
if app.data.lidarr_data.album_details_modal.is_none() {
|
let album_details_modal = app
|
||||||
app.data.lidarr_data.album_details_modal = Some(AlbumDetailsModal::default());
|
.data
|
||||||
}
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.get_or_insert_default();
|
||||||
|
|
||||||
let album_releases_vec = release_vec
|
let album_releases_vec = release_vec
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|release| !release.discography)
|
.filter(|release| !release.discography)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
app
|
album_details_modal
|
||||||
.data
|
|
||||||
.lidarr_data
|
|
||||||
.album_details_modal
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.album_releases
|
.album_releases
|
||||||
.set_items(album_releases_vec);
|
.set_items(album_releases_vec);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ mod tests {
|
|||||||
"sourceTitle": "z album",
|
"sourceTitle": "z album",
|
||||||
"albumId": 1007,
|
"albumId": 1007,
|
||||||
"artistId": 1007,
|
"artistId": 1007,
|
||||||
|
"trackId": 1007,
|
||||||
"quality": { "quality": { "name": "Lossless" } },
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
"date": "2023-01-01T00:00:00Z",
|
"date": "2023-01-01T00:00:00Z",
|
||||||
"eventType": "grabbed",
|
"eventType": "grabbed",
|
||||||
@@ -126,6 +127,7 @@ mod tests {
|
|||||||
"sourceTitle": "An Album",
|
"sourceTitle": "An Album",
|
||||||
"albumId": 2001,
|
"albumId": 2001,
|
||||||
"artistId": 2001,
|
"artistId": 2001,
|
||||||
|
"trackId": 2001,
|
||||||
"quality": { "quality": { "name": "Lossless" } },
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
"date": "2023-01-01T00:00:00Z",
|
"date": "2023-01-01T00:00:00Z",
|
||||||
"eventType": "grabbed",
|
"eventType": "grabbed",
|
||||||
@@ -140,6 +142,7 @@ mod tests {
|
|||||||
id: 123,
|
id: 123,
|
||||||
artist_id: 1007,
|
artist_id: 1007,
|
||||||
album_id: 1007,
|
album_id: 1007,
|
||||||
|
track_id: 1007,
|
||||||
source_title: "z album".into(),
|
source_title: "z album".into(),
|
||||||
..lidarr_history_item()
|
..lidarr_history_item()
|
||||||
},
|
},
|
||||||
@@ -147,6 +150,7 @@ mod tests {
|
|||||||
id: 456,
|
id: 456,
|
||||||
artist_id: 2001,
|
artist_id: 2001,
|
||||||
album_id: 2001,
|
album_id: 2001,
|
||||||
|
track_id: 2001,
|
||||||
source_title: "An Album".into(),
|
source_title: "An Album".into(),
|
||||||
..lidarr_history_item()
|
..lidarr_history_item()
|
||||||
},
|
},
|
||||||
@@ -212,6 +216,7 @@ mod tests {
|
|||||||
"sourceTitle": "z album",
|
"sourceTitle": "z album",
|
||||||
"albumId": 1007,
|
"albumId": 1007,
|
||||||
"artistId": 1007,
|
"artistId": 1007,
|
||||||
|
"trackId": 1007,
|
||||||
"quality": { "quality": { "name": "Lossless" } },
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
"date": "2023-01-01T00:00:00Z",
|
"date": "2023-01-01T00:00:00Z",
|
||||||
"eventType": "grabbed",
|
"eventType": "grabbed",
|
||||||
@@ -225,6 +230,7 @@ mod tests {
|
|||||||
"sourceTitle": "An Album",
|
"sourceTitle": "An Album",
|
||||||
"albumId": 2001,
|
"albumId": 2001,
|
||||||
"artistId": 2001,
|
"artistId": 2001,
|
||||||
|
"trackId": 2001,
|
||||||
"quality": { "quality": { "name": "Lossless" } },
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
"date": "2023-01-01T00:00:00Z",
|
"date": "2023-01-01T00:00:00Z",
|
||||||
"eventType": "grabbed",
|
"eventType": "grabbed",
|
||||||
@@ -239,6 +245,7 @@ mod tests {
|
|||||||
id: 123,
|
id: 123,
|
||||||
artist_id: 1007,
|
artist_id: 1007,
|
||||||
album_id: 1007,
|
album_id: 1007,
|
||||||
|
track_id: 1007,
|
||||||
source_title: "z album".into(),
|
source_title: "z album".into(),
|
||||||
..lidarr_history_item()
|
..lidarr_history_item()
|
||||||
},
|
},
|
||||||
@@ -246,6 +253,7 @@ mod tests {
|
|||||||
id: 456,
|
id: 456,
|
||||||
artist_id: 2001,
|
artist_id: 2001,
|
||||||
album_id: 2001,
|
album_id: 2001,
|
||||||
|
track_id: 2001,
|
||||||
source_title: "An Album".into(),
|
source_title: "An Album".into(),
|
||||||
..lidarr_history_item()
|
..lidarr_history_item()
|
||||||
},
|
},
|
||||||
@@ -289,6 +297,7 @@ mod tests {
|
|||||||
"sourceTitle": "z album",
|
"sourceTitle": "z album",
|
||||||
"albumId": 1007,
|
"albumId": 1007,
|
||||||
"artistId": 1007,
|
"artistId": 1007,
|
||||||
|
"trackId": 1007,
|
||||||
"quality": { "quality": { "name": "Lossless" } },
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
"date": "2023-01-01T00:00:00Z",
|
"date": "2023-01-01T00:00:00Z",
|
||||||
"eventType": "grabbed",
|
"eventType": "grabbed",
|
||||||
@@ -302,6 +311,7 @@ mod tests {
|
|||||||
"sourceTitle": "An Album",
|
"sourceTitle": "An Album",
|
||||||
"albumId": 2001,
|
"albumId": 2001,
|
||||||
"artistId": 2001,
|
"artistId": 2001,
|
||||||
|
"trackId": 2001,
|
||||||
"quality": { "quality": { "name": "Lossless" } },
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
"date": "2023-01-01T00:00:00Z",
|
"date": "2023-01-01T00:00:00Z",
|
||||||
"eventType": "grabbed",
|
"eventType": "grabbed",
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::models::lidarr_models::LidarrSerdeable;
|
use crate::models::lidarr_models::{LidarrHistoryItem, LidarrSerdeable, Track, TrackFile};
|
||||||
use crate::models::servarr_data::lidarr::modals::AlbumDetailsModal;
|
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
|
||||||
|
use crate::models::servarr_data::lidarr::modals::{AlbumDetailsModal, TrackDetailsModal};
|
||||||
|
use crate::models::stateful_table::SortOption;
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{track, track_file};
|
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{
|
||||||
|
lidarr_history_item, track, track_file,
|
||||||
|
};
|
||||||
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
|
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
|
||||||
use pretty_assertions::assert_eq;
|
use indoc::formatdoc;
|
||||||
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
|
use rstest::rstest;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -28,6 +34,272 @@ mod tests {
|
|||||||
async_server.assert_async().await;
|
async_server.assert_async().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_track_details_event() {
|
||||||
|
let response = track();
|
||||||
|
let (async_server, app_arc, _server) = MockServarrApi::get()
|
||||||
|
.returns(serde_json::to_value(track()).unwrap())
|
||||||
|
.path("/1")
|
||||||
|
.build_for(LidarrEvent::GetTrackDetails(1))
|
||||||
|
.await;
|
||||||
|
app_arc.lock().await.data.lidarr_data.album_details_modal = Some(AlbumDetailsModal {
|
||||||
|
track_details_modal: Some(TrackDetailsModal::default()),
|
||||||
|
..AlbumDetailsModal::default()
|
||||||
|
});
|
||||||
|
app_arc.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app_arc);
|
||||||
|
|
||||||
|
let result = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetTrackDetails(1))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
async_server.assert_async().await;
|
||||||
|
assert_ok!(&result);
|
||||||
|
let LidarrSerdeable::Track(track) = result.unwrap() else {
|
||||||
|
panic!("Expected Track")
|
||||||
|
};
|
||||||
|
assert_eq!(track, response);
|
||||||
|
let app = app_arc.lock().await;
|
||||||
|
assert_eq!(
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_tabs
|
||||||
|
.get_active_route(),
|
||||||
|
ActiveLidarrBlock::TrackDetails.into()
|
||||||
|
);
|
||||||
|
let track_details = &app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details;
|
||||||
|
assert_str_eq!(
|
||||||
|
track_details.get_text(),
|
||||||
|
formatdoc!(
|
||||||
|
"
|
||||||
|
Title: Test title
|
||||||
|
Track Number: 1
|
||||||
|
Duration: 3:20
|
||||||
|
Explicit: false
|
||||||
|
Quality: Lossless
|
||||||
|
File Path: /music/P!nk/TRUSTFALL/01 - When I Get There.flac
|
||||||
|
File Size: 37.40 MB
|
||||||
|
Date Added: 2023-05-20 21:29:16 UTC
|
||||||
|
Codec: FLAC
|
||||||
|
Channels: 2
|
||||||
|
Bits: 24bit
|
||||||
|
Bit Rate: 1563 kbps
|
||||||
|
Sample Rate: 44.1kHz
|
||||||
|
"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_track_details_event_empty_media_info() {
|
||||||
|
let expected_track = Track {
|
||||||
|
track_file: Some(TrackFile {
|
||||||
|
media_info: None,
|
||||||
|
..track_file()
|
||||||
|
}),
|
||||||
|
..track()
|
||||||
|
};
|
||||||
|
let response = expected_track.clone();
|
||||||
|
let (async_server, app_arc, _server) = MockServarrApi::get()
|
||||||
|
.returns(serde_json::to_value(expected_track.clone()).unwrap())
|
||||||
|
.path("/1")
|
||||||
|
.build_for(LidarrEvent::GetTrackDetails(1))
|
||||||
|
.await;
|
||||||
|
app_arc.lock().await.data.lidarr_data.album_details_modal = Some(AlbumDetailsModal {
|
||||||
|
track_details_modal: Some(TrackDetailsModal::default()),
|
||||||
|
..AlbumDetailsModal::default()
|
||||||
|
});
|
||||||
|
app_arc.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app_arc);
|
||||||
|
|
||||||
|
let result = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetTrackDetails(1))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
async_server.assert_async().await;
|
||||||
|
assert_ok!(&result);
|
||||||
|
let LidarrSerdeable::Track(track) = result.unwrap() else {
|
||||||
|
panic!("Expected Track")
|
||||||
|
};
|
||||||
|
assert_eq!(track, response);
|
||||||
|
let app = app_arc.lock().await;
|
||||||
|
assert_eq!(
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_tabs
|
||||||
|
.get_active_route(),
|
||||||
|
ActiveLidarrBlock::TrackDetails.into()
|
||||||
|
);
|
||||||
|
let track_details = &app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details;
|
||||||
|
assert_str_eq!(
|
||||||
|
track_details.get_text(),
|
||||||
|
formatdoc!(
|
||||||
|
"
|
||||||
|
Title: Test title
|
||||||
|
Track Number: 1
|
||||||
|
Duration: 3:20
|
||||||
|
Explicit: false
|
||||||
|
Quality: Lossless
|
||||||
|
File Path: /music/P!nk/TRUSTFALL/01 - When I Get There.flac
|
||||||
|
File Size: 37.40 MB
|
||||||
|
Date Added: 2023-05-20 21:29:16 UTC
|
||||||
|
"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_track_details_event_empty_track_file() {
|
||||||
|
let expected_track = Track {
|
||||||
|
track_file: None,
|
||||||
|
..track()
|
||||||
|
};
|
||||||
|
let response = expected_track.clone();
|
||||||
|
let (async_server, app_arc, _server) = MockServarrApi::get()
|
||||||
|
.returns(serde_json::to_value(expected_track.clone()).unwrap())
|
||||||
|
.path("/1")
|
||||||
|
.build_for(LidarrEvent::GetTrackDetails(1))
|
||||||
|
.await;
|
||||||
|
app_arc.lock().await.data.lidarr_data.album_details_modal = Some(AlbumDetailsModal {
|
||||||
|
track_details_modal: Some(TrackDetailsModal::default()),
|
||||||
|
..AlbumDetailsModal::default()
|
||||||
|
});
|
||||||
|
app_arc.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app_arc);
|
||||||
|
|
||||||
|
let result = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetTrackDetails(1))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
async_server.assert_async().await;
|
||||||
|
assert_ok!(&result);
|
||||||
|
let LidarrSerdeable::Track(track) = result.unwrap() else {
|
||||||
|
panic!("Expected Track")
|
||||||
|
};
|
||||||
|
assert_eq!(track, response);
|
||||||
|
let app = app_arc.lock().await;
|
||||||
|
assert_eq!(
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_tabs
|
||||||
|
.get_active_route(),
|
||||||
|
ActiveLidarrBlock::TrackDetails.into()
|
||||||
|
);
|
||||||
|
let track_details = &app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details;
|
||||||
|
assert_str_eq!(
|
||||||
|
track_details.get_text(),
|
||||||
|
formatdoc!(
|
||||||
|
"
|
||||||
|
Title: Test title
|
||||||
|
Track Number: 1
|
||||||
|
Duration: 3:20
|
||||||
|
Explicit: false
|
||||||
|
"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_track_details_event_album_details_modal_not_required_in_cli_mode() {
|
||||||
|
let response = track();
|
||||||
|
let (async_server, app_arc, _server) = MockServarrApi::get()
|
||||||
|
.returns(serde_json::to_value(track()).unwrap())
|
||||||
|
.path("/1")
|
||||||
|
.build_for(LidarrEvent::GetTrackDetails(1))
|
||||||
|
.await;
|
||||||
|
app_arc.lock().await.cli_mode = true;
|
||||||
|
app_arc.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app_arc);
|
||||||
|
|
||||||
|
let result = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetTrackDetails(1))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
async_server.assert_async().await;
|
||||||
|
assert_ok!(&result);
|
||||||
|
let LidarrSerdeable::Track(track) = result.unwrap() else {
|
||||||
|
panic!("Expected Track")
|
||||||
|
};
|
||||||
|
assert_eq!(track, response);
|
||||||
|
let app = app_arc.lock().await;
|
||||||
|
assert_some!(&app.data.lidarr_data.album_details_modal);
|
||||||
|
assert_some!(
|
||||||
|
&app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[should_panic(expected = "Album details modal is empty")]
|
||||||
|
async fn test_handle_get_track_details_event_requires_album_details_modal_to_be_some_when_in_tui_mode()
|
||||||
|
{
|
||||||
|
let (_async_server, app_arc, _server) = MockServarrApi::get()
|
||||||
|
.returns(serde_json::to_value(track()).unwrap())
|
||||||
|
.path("/1")
|
||||||
|
.build_for(LidarrEvent::GetTrackDetails(1))
|
||||||
|
.await;
|
||||||
|
app_arc.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app_arc);
|
||||||
|
|
||||||
|
network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetTrackDetails(1))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_handle_get_tracks_event() {
|
async fn test_handle_get_tracks_event() {
|
||||||
let expected_tracks = vec![track()];
|
let expected_tracks = vec![track()];
|
||||||
@@ -170,4 +442,430 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(track_files, vec![track_file()]);
|
assert_eq!(track_files, vec![track_file()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_lidarr_track_history_event(
|
||||||
|
#[values(true, false)] use_custom_sorting: bool,
|
||||||
|
) {
|
||||||
|
let history_json = json!([{
|
||||||
|
"id": 123,
|
||||||
|
"sourceTitle": "z track",
|
||||||
|
"albumId": 1007,
|
||||||
|
"artistId": 1007,
|
||||||
|
"trackId": 1007,
|
||||||
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
|
"date": "2023-01-01T00:00:00Z",
|
||||||
|
"eventType": "grabbed",
|
||||||
|
"data": {
|
||||||
|
"droppedPath": "/nfs/nzbget/completed/music/Something/cool.mp3",
|
||||||
|
"importedPath": "/nfs/music/Something/Album 1/Cool.mp3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 456,
|
||||||
|
"sourceTitle": "A Track",
|
||||||
|
"albumId": 2001,
|
||||||
|
"artistId": 2001,
|
||||||
|
"trackId": 2001,
|
||||||
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
|
"date": "2023-01-01T00:00:00Z",
|
||||||
|
"eventType": "grabbed",
|
||||||
|
"data": {
|
||||||
|
"droppedPath": "/nfs/nzbget/completed/music/Something/cool.mp3",
|
||||||
|
"importedPath": "/nfs/music/Something/Album 1/Cool.mp3"
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
let response: Vec<LidarrHistoryItem> = serde_json::from_value(history_json.clone()).unwrap();
|
||||||
|
let mut expected_history_items = vec![LidarrHistoryItem {
|
||||||
|
id: 456,
|
||||||
|
artist_id: 2001,
|
||||||
|
album_id: 2001,
|
||||||
|
track_id: 2001,
|
||||||
|
source_title: "A Track".into(),
|
||||||
|
..lidarr_history_item()
|
||||||
|
}];
|
||||||
|
let (mock, app, _server) = MockServarrApi::get()
|
||||||
|
.returns(history_json)
|
||||||
|
.query("artistId=2001&albumId=2001")
|
||||||
|
.build_for(LidarrEvent::GetTrackHistory(2001, 2001, 2001))
|
||||||
|
.await;
|
||||||
|
let album_details_modal = AlbumDetailsModal {
|
||||||
|
track_details_modal: Some(TrackDetailsModal::default()),
|
||||||
|
..AlbumDetailsModal::default()
|
||||||
|
};
|
||||||
|
app.lock().await.data.lidarr_data.album_details_modal = Some(album_details_modal);
|
||||||
|
if use_custom_sorting {
|
||||||
|
let cmp_fn = |a: &LidarrHistoryItem, b: &LidarrHistoryItem| {
|
||||||
|
a.source_title
|
||||||
|
.text
|
||||||
|
.to_lowercase()
|
||||||
|
.cmp(&b.source_title.text.to_lowercase())
|
||||||
|
};
|
||||||
|
expected_history_items.sort_by(cmp_fn);
|
||||||
|
|
||||||
|
let history_sort_option = SortOption {
|
||||||
|
name: "Source Title",
|
||||||
|
cmp_fn: Some(cmp_fn),
|
||||||
|
};
|
||||||
|
app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_history
|
||||||
|
.sorting(vec![history_sort_option]);
|
||||||
|
}
|
||||||
|
app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_history
|
||||||
|
.sort_asc = true;
|
||||||
|
app.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
let LidarrSerdeable::LidarrHistoryItems(history) = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetTrackHistory(2001, 2001, 2001))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("Expected LidarrHistoryItems")
|
||||||
|
};
|
||||||
|
mock.assert_async().await;
|
||||||
|
assert_eq!(
|
||||||
|
app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_history
|
||||||
|
.items,
|
||||||
|
expected_history_items
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_history
|
||||||
|
.sort_asc
|
||||||
|
);
|
||||||
|
assert_eq!(history, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_lidarr_track_history_event_empty_track_details_modal() {
|
||||||
|
let history_json = json!([{
|
||||||
|
"id": 123,
|
||||||
|
"sourceTitle": "z track",
|
||||||
|
"albumId": 1007,
|
||||||
|
"artistId": 1007,
|
||||||
|
"trackId": 1007,
|
||||||
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
|
"date": "2023-01-01T00:00:00Z",
|
||||||
|
"eventType": "grabbed",
|
||||||
|
"data": {
|
||||||
|
"droppedPath": "/nfs/nzbget/completed/music/Something/cool.mp3",
|
||||||
|
"importedPath": "/nfs/music/Something/Album 1/Cool.mp3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 456,
|
||||||
|
"sourceTitle": "A Track",
|
||||||
|
"albumId": 2001,
|
||||||
|
"artistId": 2001,
|
||||||
|
"trackId": 2001,
|
||||||
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
|
"date": "2023-01-01T00:00:00Z",
|
||||||
|
"eventType": "grabbed",
|
||||||
|
"data": {
|
||||||
|
"droppedPath": "/nfs/nzbget/completed/music/Something/cool.mp3",
|
||||||
|
"importedPath": "/nfs/music/Something/Album 1/Cool.mp3"
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
let response: Vec<LidarrHistoryItem> = serde_json::from_value(history_json.clone()).unwrap();
|
||||||
|
let expected_history_items = vec![LidarrHistoryItem {
|
||||||
|
id: 456,
|
||||||
|
artist_id: 2001,
|
||||||
|
album_id: 2001,
|
||||||
|
track_id: 2001,
|
||||||
|
source_title: "A Track".into(),
|
||||||
|
..lidarr_history_item()
|
||||||
|
}];
|
||||||
|
let (mock, app, _server) = MockServarrApi::get()
|
||||||
|
.returns(history_json)
|
||||||
|
.query("artistId=2001&albumId=2001")
|
||||||
|
.build_for(LidarrEvent::GetTrackHistory(2001, 2001, 2001))
|
||||||
|
.await;
|
||||||
|
app.lock().await.data.lidarr_data.album_details_modal = Some(AlbumDetailsModal::default());
|
||||||
|
app.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
let LidarrSerdeable::LidarrHistoryItems(history) = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetTrackHistory(2001, 2001, 2001))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("Expected LidarrHistoryItems")
|
||||||
|
};
|
||||||
|
mock.assert_async().await;
|
||||||
|
let app = app.lock().await;
|
||||||
|
assert_some!(&app.data.lidarr_data.album_details_modal);
|
||||||
|
assert_some!(
|
||||||
|
&app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_history
|
||||||
|
.items,
|
||||||
|
expected_history_items
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_history
|
||||||
|
.sort_asc
|
||||||
|
);
|
||||||
|
assert_eq!(history, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_lidarr_track_history_event_empty_album_details_modal() {
|
||||||
|
let history_json = json!([{
|
||||||
|
"id": 123,
|
||||||
|
"sourceTitle": "z track",
|
||||||
|
"albumId": 1007,
|
||||||
|
"artistId": 1007,
|
||||||
|
"trackId": 1007,
|
||||||
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
|
"date": "2023-01-01T00:00:00Z",
|
||||||
|
"eventType": "grabbed",
|
||||||
|
"data": {
|
||||||
|
"droppedPath": "/nfs/nzbget/completed/music/Something/cool.mp3",
|
||||||
|
"importedPath": "/nfs/music/Something/Album 1/Cool.mp3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 456,
|
||||||
|
"sourceTitle": "A Track",
|
||||||
|
"albumId": 2001,
|
||||||
|
"artistId": 2001,
|
||||||
|
"trackId": 2001,
|
||||||
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
|
"date": "2023-01-01T00:00:00Z",
|
||||||
|
"eventType": "grabbed",
|
||||||
|
"data": {
|
||||||
|
"droppedPath": "/nfs/nzbget/completed/music/Something/cool.mp3",
|
||||||
|
"importedPath": "/nfs/music/Something/Album 1/Cool.mp3"
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
let response: Vec<LidarrHistoryItem> = serde_json::from_value(history_json.clone()).unwrap();
|
||||||
|
let expected_history_items = vec![LidarrHistoryItem {
|
||||||
|
id: 456,
|
||||||
|
artist_id: 2001,
|
||||||
|
album_id: 2001,
|
||||||
|
track_id: 2001,
|
||||||
|
source_title: "A Track".into(),
|
||||||
|
..lidarr_history_item()
|
||||||
|
}];
|
||||||
|
let (mock, app, _server) = MockServarrApi::get()
|
||||||
|
.returns(history_json)
|
||||||
|
.query("artistId=2001&albumId=2001")
|
||||||
|
.build_for(LidarrEvent::GetTrackHistory(2001, 2001, 2001))
|
||||||
|
.await;
|
||||||
|
app.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
let LidarrSerdeable::LidarrHistoryItems(history) = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetTrackHistory(2001, 2001, 2001))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("Expected LidarrHistoryItems")
|
||||||
|
};
|
||||||
|
mock.assert_async().await;
|
||||||
|
let app = app.lock().await;
|
||||||
|
assert_some!(&app.data.lidarr_data.album_details_modal);
|
||||||
|
assert_some!(
|
||||||
|
&app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_history
|
||||||
|
.items,
|
||||||
|
expected_history_items
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_history
|
||||||
|
.sort_asc
|
||||||
|
);
|
||||||
|
assert_eq!(history, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_lidarr_track_history_event_no_op_when_user_is_selecting_sort_options() {
|
||||||
|
let history_json = json!([{
|
||||||
|
"id": 123,
|
||||||
|
"sourceTitle": "z track",
|
||||||
|
"albumId": 1007,
|
||||||
|
"artistId": 1007,
|
||||||
|
"trackId": 1007,
|
||||||
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
|
"date": "2023-01-01T00:00:00Z",
|
||||||
|
"eventType": "grabbed",
|
||||||
|
"data": {
|
||||||
|
"droppedPath": "/nfs/nzbget/completed/music/Something/cool.mp3",
|
||||||
|
"importedPath": "/nfs/music/Something/Album 1/Cool.mp3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 456,
|
||||||
|
"sourceTitle": "A Track",
|
||||||
|
"albumId": 2001,
|
||||||
|
"artistId": 2001,
|
||||||
|
"trackId": 2001,
|
||||||
|
"quality": { "quality": { "name": "Lossless" } },
|
||||||
|
"date": "2023-01-01T00:00:00Z",
|
||||||
|
"eventType": "grabbed",
|
||||||
|
"data": {
|
||||||
|
"droppedPath": "/nfs/nzbget/completed/music/Something/cool.mp3",
|
||||||
|
"importedPath": "/nfs/music/Something/Album 1/Cool.mp3"
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
let response: Vec<LidarrHistoryItem> = serde_json::from_value(history_json.clone()).unwrap();
|
||||||
|
let (mock, app, _server) = MockServarrApi::get()
|
||||||
|
.returns(history_json)
|
||||||
|
.query("artistId=2001&albumId=2001")
|
||||||
|
.build_for(LidarrEvent::GetTrackHistory(2001, 2001, 2001))
|
||||||
|
.await;
|
||||||
|
let album_details_modal = AlbumDetailsModal {
|
||||||
|
track_details_modal: Some(TrackDetailsModal::default()),
|
||||||
|
..AlbumDetailsModal::default()
|
||||||
|
};
|
||||||
|
app.lock().await.data.lidarr_data.album_details_modal = Some(album_details_modal);
|
||||||
|
app.lock().await.server_tabs.set_index(2);
|
||||||
|
app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.push_navigation_stack(ActiveLidarrBlock::TrackHistorySortPrompt.into());
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
let LidarrSerdeable::LidarrHistoryItems(history) = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetTrackHistory(2001, 2001, 2001))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("Expected LidarrHistoryItems")
|
||||||
|
};
|
||||||
|
mock.assert_async().await;
|
||||||
|
let app = app.lock().await;
|
||||||
|
assert_some!(&app.data.lidarr_data.album_details_modal);
|
||||||
|
assert_some!(
|
||||||
|
&app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
);
|
||||||
|
assert_is_empty!(
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_history
|
||||||
|
.items,
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.track_history
|
||||||
|
.sort_asc
|
||||||
|
);
|
||||||
|
assert_eq!(history, response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
use crate::models::lidarr_models::{Track, TrackFile};
|
use crate::models::lidarr_models::{LidarrHistoryItem, MediaInfo, Track, TrackFile};
|
||||||
|
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
|
||||||
use crate::models::servarr_data::lidarr::modals::AlbumDetailsModal;
|
use crate::models::servarr_data::lidarr::modals::AlbumDetailsModal;
|
||||||
|
use crate::models::{Route, ScrollableText};
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
use crate::network::{Network, RequestMethod};
|
use crate::network::{Network, RequestMethod};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use indoc::formatdoc;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -53,18 +56,117 @@ impl Network<'_, '_> {
|
|||||||
self
|
self
|
||||||
.handle_request::<(), Vec<Track>>(request_props, |mut track_vec, mut app| {
|
.handle_request::<(), Vec<Track>>(request_props, |mut track_vec, mut app| {
|
||||||
track_vec.sort_by(|a, b| a.id.cmp(&b.id));
|
track_vec.sort_by(|a, b| a.id.cmp(&b.id));
|
||||||
if app.data.lidarr_data.album_details_modal.is_none() {
|
let album_details_modal = app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.get_or_insert_default();
|
||||||
|
|
||||||
|
album_details_modal.tracks.set_items(track_vec);
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::network::lidarr_network) async fn get_track_details(
|
||||||
|
&mut self,
|
||||||
|
track_id: i64,
|
||||||
|
) -> Result<Track> {
|
||||||
|
let event = LidarrEvent::GetTrackDetails(track_id);
|
||||||
|
info!("Fetching Lidarr track details for track with ID: {track_id}");
|
||||||
|
|
||||||
|
let request_props = self
|
||||||
|
.request_props_from(
|
||||||
|
event,
|
||||||
|
RequestMethod::Get,
|
||||||
|
None::<()>,
|
||||||
|
Some(format!("/{track_id}")),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<(), Track>(request_props, |track_response, mut app| {
|
||||||
|
if app.cli_mode {
|
||||||
app.data.lidarr_data.album_details_modal = Some(AlbumDetailsModal::default());
|
app.data.lidarr_data.album_details_modal = Some(AlbumDetailsModal::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
app
|
let Track {
|
||||||
|
explicit,
|
||||||
|
track_number,
|
||||||
|
title,
|
||||||
|
duration,
|
||||||
|
track_file,
|
||||||
|
..
|
||||||
|
} = track_response;
|
||||||
|
let duration_secs = duration / 1000;
|
||||||
|
let mins = duration_secs / 60;
|
||||||
|
let secs = duration_secs % 60;
|
||||||
|
let track_length = format!("{mins}:{secs:02}");
|
||||||
|
let track_details_modal = app
|
||||||
.data
|
.data
|
||||||
.lidarr_data
|
.lidarr_data
|
||||||
.album_details_modal
|
.album_details_modal
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.expect("Album details modal is empty")
|
||||||
.tracks
|
.track_details_modal
|
||||||
.set_items(track_vec);
|
.get_or_insert_default();
|
||||||
|
let mut details = formatdoc!(
|
||||||
|
"
|
||||||
|
Title: {title}
|
||||||
|
Track Number: {track_number}
|
||||||
|
Duration: {track_length}
|
||||||
|
Explicit: {explicit}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(file) = track_file {
|
||||||
|
let TrackFile {
|
||||||
|
path,
|
||||||
|
size,
|
||||||
|
quality,
|
||||||
|
date_added,
|
||||||
|
media_info,
|
||||||
|
..
|
||||||
|
} = file;
|
||||||
|
let quality_name = quality.quality.name;
|
||||||
|
let size_mb = size as f64 / 1024f64.powi(2);
|
||||||
|
|
||||||
|
details.push_str(&formatdoc!(
|
||||||
|
"
|
||||||
|
Quality: {quality_name}
|
||||||
|
File Path: {path}
|
||||||
|
File Size: {size_mb:.2} MB
|
||||||
|
Date Added: {date_added}
|
||||||
|
"
|
||||||
|
));
|
||||||
|
|
||||||
|
if let Some(info) = media_info {
|
||||||
|
let MediaInfo {
|
||||||
|
audio_bit_rate,
|
||||||
|
audio_channels,
|
||||||
|
audio_codec,
|
||||||
|
audio_bits,
|
||||||
|
audio_sample_rate,
|
||||||
|
} = info;
|
||||||
|
|
||||||
|
details.push_str(&formatdoc!(
|
||||||
|
"
|
||||||
|
Codec: {}
|
||||||
|
Channels: {}
|
||||||
|
Bits: {}
|
||||||
|
Bit Rate: {}
|
||||||
|
Sample Rate: {}
|
||||||
|
",
|
||||||
|
audio_codec.unwrap_or_default(),
|
||||||
|
audio_channels,
|
||||||
|
audio_bits.unwrap_or_default(),
|
||||||
|
audio_bit_rate.unwrap_or_default(),
|
||||||
|
audio_sample_rate.unwrap_or_default()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
track_details_modal.track_details = ScrollableText::with_string(details);
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -88,18 +190,60 @@ impl Network<'_, '_> {
|
|||||||
|
|
||||||
self
|
self
|
||||||
.handle_request::<(), Vec<TrackFile>>(request_props, |track_file_vec, mut app| {
|
.handle_request::<(), Vec<TrackFile>>(request_props, |track_file_vec, mut app| {
|
||||||
if app.data.lidarr_data.album_details_modal.is_none() {
|
let album_details_modal = app
|
||||||
app.data.lidarr_data.album_details_modal = Some(AlbumDetailsModal::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
app
|
|
||||||
.data
|
.data
|
||||||
.lidarr_data
|
.lidarr_data
|
||||||
.album_details_modal
|
.album_details_modal
|
||||||
.as_mut()
|
.get_or_insert_default();
|
||||||
.unwrap()
|
|
||||||
.track_files
|
album_details_modal.track_files.set_items(track_file_vec);
|
||||||
.set_items(track_file_vec);
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::network::lidarr_network) async fn get_lidarr_track_history(
|
||||||
|
&mut self,
|
||||||
|
artist_id: i64,
|
||||||
|
album_id: i64,
|
||||||
|
track_id: i64,
|
||||||
|
) -> Result<Vec<LidarrHistoryItem>> {
|
||||||
|
let event = LidarrEvent::GetTrackHistory(artist_id, album_id, track_id);
|
||||||
|
info!(
|
||||||
|
"Fetching history for artist with ID: {artist_id} and album with ID: {album_id} and track with ID: {track_id}"
|
||||||
|
);
|
||||||
|
|
||||||
|
let params = format!("artistId={artist_id}&albumId={album_id}");
|
||||||
|
let request_props = self
|
||||||
|
.request_props_from(event, RequestMethod::Get, None::<()>, None, Some(params))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<(), Vec<LidarrHistoryItem>>(request_props, |history_items, mut app| {
|
||||||
|
let is_sorting = matches!(
|
||||||
|
app.get_current_route(),
|
||||||
|
Route::Lidarr(ActiveLidarrBlock::TrackHistorySortPrompt, _)
|
||||||
|
);
|
||||||
|
|
||||||
|
let album_details_modal = app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.get_or_insert_default();
|
||||||
|
let track_details_modal = album_details_modal
|
||||||
|
.track_details_modal
|
||||||
|
.get_or_insert_default();
|
||||||
|
|
||||||
|
if !is_sorting {
|
||||||
|
let mut history_vec: Vec<LidarrHistoryItem> = history_items
|
||||||
|
.into_iter()
|
||||||
|
.filter(|it| it.track_id == track_id)
|
||||||
|
.collect();
|
||||||
|
history_vec.sort_by(|a, b| a.id.cmp(&b.id));
|
||||||
|
track_details_modal.track_history.set_items(history_vec);
|
||||||
|
track_details_modal
|
||||||
|
.track_history
|
||||||
|
.apply_sorting_toggle(false);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -278,6 +278,7 @@ pub mod test_utils {
|
|||||||
source_title: "Test source title".into(),
|
source_title: "Test source title".into(),
|
||||||
album_id: 1,
|
album_id: 1,
|
||||||
artist_id: 1,
|
artist_id: 1,
|
||||||
|
track_id: 1,
|
||||||
quality: quality_wrapper(),
|
quality: quality_wrapper(),
|
||||||
date: DateTime::from(DateTime::parse_from_rfc3339("2023-01-01T00:00:00Z").unwrap()),
|
date: DateTime::from(DateTime::parse_from_rfc3339("2023-01-01T00:00:00Z").unwrap()),
|
||||||
event_type: LidarrHistoryEventType::Grabbed,
|
event_type: LidarrHistoryEventType::Grabbed,
|
||||||
@@ -473,6 +474,7 @@ pub mod test_utils {
|
|||||||
duration: 200173,
|
duration: 200173,
|
||||||
has_file: false,
|
has_file: false,
|
||||||
ratings: ratings(),
|
ratings: ratings(),
|
||||||
|
track_file: Some(track_file()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,11 @@ mod tests {
|
|||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn test_resource_artist_history(
|
fn test_resource_artist_history(
|
||||||
#[values(LidarrEvent::GetArtistHistory(0), LidarrEvent::GetAlbumHistory(0, 0))]
|
#[values(
|
||||||
|
LidarrEvent::GetArtistHistory(0),
|
||||||
|
LidarrEvent::GetAlbumHistory(0, 0),
|
||||||
|
LidarrEvent::GetTrackHistory(0, 0, 0)
|
||||||
|
)]
|
||||||
event: LidarrEvent,
|
event: LidarrEvent,
|
||||||
) {
|
) {
|
||||||
assert_str_eq!(event.resource(), "/history/artist");
|
assert_str_eq!(event.resource(), "/history/artist");
|
||||||
@@ -147,6 +151,13 @@ mod tests {
|
|||||||
assert_str_eq!(event.resource(), "/trackfile");
|
assert_str_eq!(event.resource(), "/trackfile");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_resource_track(
|
||||||
|
#[values(LidarrEvent::GetTracks(0, 0), LidarrEvent::GetTrackDetails(0))] event: LidarrEvent,
|
||||||
|
) {
|
||||||
|
assert_str_eq!(event.resource(), "/track");
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(LidarrEvent::GetDiskSpace, "/diskspace")]
|
#[case(LidarrEvent::GetDiskSpace, "/diskspace")]
|
||||||
#[case(LidarrEvent::GetMetadataProfiles, "/metadataprofile")]
|
#[case(LidarrEvent::GetMetadataProfiles, "/metadataprofile")]
|
||||||
@@ -161,7 +172,6 @@ mod tests {
|
|||||||
#[case(LidarrEvent::GetHistory(0), "/history")]
|
#[case(LidarrEvent::GetHistory(0), "/history")]
|
||||||
#[case(LidarrEvent::TestIndexer(0), "/indexer/test")]
|
#[case(LidarrEvent::TestIndexer(0), "/indexer/test")]
|
||||||
#[case(LidarrEvent::TestAllIndexers, "/indexer/testall")]
|
#[case(LidarrEvent::TestAllIndexers, "/indexer/testall")]
|
||||||
#[case(LidarrEvent::GetTracks(0, 0), "/track")]
|
|
||||||
fn test_resource(#[case] event: LidarrEvent, #[case] expected_uri: &str) {
|
fn test_resource(#[case] event: LidarrEvent, #[case] expected_uri: &str) {
|
||||||
assert_str_eq!(event.resource(), expected_uri);
|
assert_str_eq!(event.resource(), expected_uri);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,8 +61,10 @@ pub enum LidarrEvent {
|
|||||||
GetRootFolders,
|
GetRootFolders,
|
||||||
GetSecurityConfig,
|
GetSecurityConfig,
|
||||||
GetStatus,
|
GetStatus,
|
||||||
|
GetTrackDetails(i64),
|
||||||
GetTracks(i64, i64),
|
GetTracks(i64, i64),
|
||||||
GetTrackFiles(i64),
|
GetTrackFiles(i64),
|
||||||
|
GetTrackHistory(i64, i64, i64),
|
||||||
GetUpdates,
|
GetUpdates,
|
||||||
GetTags,
|
GetTags,
|
||||||
GetTasks,
|
GetTasks,
|
||||||
@@ -99,7 +101,9 @@ impl NetworkResource for LidarrEvent {
|
|||||||
| LidarrEvent::ToggleAlbumMonitoring(_)
|
| LidarrEvent::ToggleAlbumMonitoring(_)
|
||||||
| LidarrEvent::GetAlbumDetails(_)
|
| LidarrEvent::GetAlbumDetails(_)
|
||||||
| LidarrEvent::DeleteAlbum(_) => "/album",
|
| LidarrEvent::DeleteAlbum(_) => "/album",
|
||||||
LidarrEvent::GetArtistHistory(_) | LidarrEvent::GetAlbumHistory(_, _) => "/history/artist",
|
LidarrEvent::GetArtistHistory(_)
|
||||||
|
| LidarrEvent::GetAlbumHistory(_, _)
|
||||||
|
| LidarrEvent::GetTrackHistory(_, _, _) => "/history/artist",
|
||||||
LidarrEvent::GetLogs(_) => "/log",
|
LidarrEvent::GetLogs(_) => "/log",
|
||||||
LidarrEvent::GetDiskSpace => "/diskspace",
|
LidarrEvent::GetDiskSpace => "/diskspace",
|
||||||
LidarrEvent::GetDownloads(_) | LidarrEvent::DeleteDownload(_) => "/queue",
|
LidarrEvent::GetDownloads(_) | LidarrEvent::DeleteDownload(_) => "/queue",
|
||||||
@@ -128,7 +132,7 @@ impl NetworkResource for LidarrEvent {
|
|||||||
LidarrEvent::TestAllIndexers => "/indexer/testall",
|
LidarrEvent::TestAllIndexers => "/indexer/testall",
|
||||||
LidarrEvent::GetStatus => "/system/status",
|
LidarrEvent::GetStatus => "/system/status",
|
||||||
LidarrEvent::GetTasks => "/system/task",
|
LidarrEvent::GetTasks => "/system/task",
|
||||||
LidarrEvent::GetTracks(_, _) => "/track",
|
LidarrEvent::GetTracks(_, _) | LidarrEvent::GetTrackDetails(_) => "/track",
|
||||||
LidarrEvent::GetUpdates => "/update",
|
LidarrEvent::GetUpdates => "/update",
|
||||||
LidarrEvent::HealthCheck => "/health",
|
LidarrEvent::HealthCheck => "/health",
|
||||||
LidarrEvent::SearchNewArtist(_) => "/artist/lookup",
|
LidarrEvent::SearchNewArtist(_) => "/artist/lookup",
|
||||||
@@ -267,6 +271,10 @@ impl Network<'_, '_> {
|
|||||||
LidarrEvent::GetStatus => self.get_lidarr_status().await.map(LidarrSerdeable::from),
|
LidarrEvent::GetStatus => self.get_lidarr_status().await.map(LidarrSerdeable::from),
|
||||||
LidarrEvent::GetTags => self.get_lidarr_tags().await.map(LidarrSerdeable::from),
|
LidarrEvent::GetTags => self.get_lidarr_tags().await.map(LidarrSerdeable::from),
|
||||||
LidarrEvent::GetTasks => self.get_lidarr_tasks().await.map(LidarrSerdeable::from),
|
LidarrEvent::GetTasks => self.get_lidarr_tasks().await.map(LidarrSerdeable::from),
|
||||||
|
LidarrEvent::GetTrackDetails(track_id) => self
|
||||||
|
.get_track_details(track_id)
|
||||||
|
.await
|
||||||
|
.map(LidarrSerdeable::from),
|
||||||
LidarrEvent::GetTracks(artist_id, album_id) => self
|
LidarrEvent::GetTracks(artist_id, album_id) => self
|
||||||
.get_tracks(artist_id, album_id)
|
.get_tracks(artist_id, album_id)
|
||||||
.await
|
.await
|
||||||
@@ -275,6 +283,10 @@ impl Network<'_, '_> {
|
|||||||
.get_track_files(album_id)
|
.get_track_files(album_id)
|
||||||
.await
|
.await
|
||||||
.map(LidarrSerdeable::from),
|
.map(LidarrSerdeable::from),
|
||||||
|
LidarrEvent::GetTrackHistory(artist_id, album_id, track_id) => self
|
||||||
|
.get_lidarr_track_history(artist_id, album_id, track_id)
|
||||||
|
.await
|
||||||
|
.map(LidarrSerdeable::from),
|
||||||
LidarrEvent::GetUpdates => self.get_lidarr_updates().await.map(LidarrSerdeable::from),
|
LidarrEvent::GetUpdates => self.get_lidarr_updates().await.map(LidarrSerdeable::from),
|
||||||
LidarrEvent::HealthCheck => self
|
LidarrEvent::HealthCheck => self
|
||||||
.get_lidarr_healthcheck()
|
.get_lidarr_healthcheck()
|
||||||
|
|||||||
-31
@@ -1,31 +0,0 @@
|
|||||||
---
|
|
||||||
source: src/ui/lidarr_ui/indexers/indexers_ui_tests.rs
|
|
||||||
expression: output
|
|
||||||
---
|
|
||||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
||||||
Indexer RSS Automatic Search Interactive Search Priority Tags
|
|
||||||
=> Test Indexer Enabled Enabled Enabled 25 alex
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
╭────────────── Success ──────────────╮
|
|
||||||
│ Indexer test succeeded! │
|
|
||||||
│ │
|
|
||||||
╰───────────────────────────────────────╯
|
|
||||||
@@ -2,6 +2,7 @@ use crate::app::App;
|
|||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
use crate::models::lidarr_models::{LidarrHistoryItem, LidarrRelease, Track};
|
use crate::models::lidarr_models::{LidarrHistoryItem, LidarrRelease, Track};
|
||||||
use crate::models::servarr_data::lidarr::lidarr_data::{ALBUM_DETAILS_BLOCKS, ActiveLidarrBlock};
|
use crate::models::servarr_data::lidarr::lidarr_data::{ALBUM_DETAILS_BLOCKS, ActiveLidarrBlock};
|
||||||
|
use crate::ui::lidarr_ui::library::track_details_ui::TrackDetailsUi;
|
||||||
use crate::ui::lidarr_ui::lidarr_ui_utils::create_history_event_details;
|
use crate::ui::lidarr_ui::lidarr_ui_utils::create_history_event_details;
|
||||||
use crate::ui::styles::{ManagarrStyle, secondary_style};
|
use crate::ui::styles::{ManagarrStyle, secondary_style};
|
||||||
use crate::ui::utils::{
|
use crate::ui::utils::{
|
||||||
@@ -31,10 +32,11 @@ impl DrawUi for AlbumDetailsUi {
|
|||||||
let Route::Lidarr(active_lidarr_block, _) = route else {
|
let Route::Lidarr(active_lidarr_block, _) = route else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
ALBUM_DETAILS_BLOCKS.contains(&active_lidarr_block)
|
TrackDetailsUi::accepts(route) || ALBUM_DETAILS_BLOCKS.contains(&active_lidarr_block)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, _area: Rect) {
|
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, _area: Rect) {
|
||||||
|
let route = app.get_current_route();
|
||||||
if app.data.lidarr_data.album_details_modal.is_some()
|
if app.data.lidarr_data.album_details_modal.is_some()
|
||||||
&& let Route::Lidarr(active_lidarr_block, _) = app.get_current_route()
|
&& let Route::Lidarr(active_lidarr_block, _) = app.get_current_route()
|
||||||
{
|
{
|
||||||
@@ -106,6 +108,10 @@ impl DrawUi for AlbumDetailsUi {
|
|||||||
};
|
};
|
||||||
|
|
||||||
draw_popup(f, app, draw_album_details_popup, Size::XLarge);
|
draw_popup(f, app, draw_album_details_popup, Size::XLarge);
|
||||||
|
|
||||||
|
if TrackDetailsUi::accepts(route) {
|
||||||
|
TrackDetailsUi::draw(f, app, _area);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ mod tests {
|
|||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::lidarr::lidarr_data::{ALBUM_DETAILS_BLOCKS, ActiveLidarrBlock};
|
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||||
|
ALBUM_DETAILS_BLOCKS, ActiveLidarrBlock, TRACK_DETAILS_BLOCKS,
|
||||||
|
};
|
||||||
use crate::models::stateful_table::StatefulTable;
|
use crate::models::stateful_table::StatefulTable;
|
||||||
use crate::ui::DrawUi;
|
use crate::ui::DrawUi;
|
||||||
use crate::ui::lidarr_ui::library::album_details_ui::AlbumDetailsUi;
|
use crate::ui::lidarr_ui::library::album_details_ui::AlbumDetailsUi;
|
||||||
@@ -11,8 +13,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_album_details_ui_accepts() {
|
fn test_album_details_ui_accepts() {
|
||||||
|
let mut album_details_blocks = ALBUM_DETAILS_BLOCKS.to_vec();
|
||||||
|
album_details_blocks.extend(TRACK_DETAILS_BLOCKS);
|
||||||
|
|
||||||
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
||||||
if ALBUM_DETAILS_BLOCKS.contains(&active_lidarr_block) {
|
if album_details_blocks.contains(&active_lidarr_block) {
|
||||||
assert!(AlbumDetailsUi::accepts(active_lidarr_block.into()));
|
assert!(AlbumDetailsUi::accepts(active_lidarr_block.into()));
|
||||||
} else {
|
} else {
|
||||||
assert!(!AlbumDetailsUi::accepts(active_lidarr_block.into()));
|
assert!(!AlbumDetailsUi::accepts(active_lidarr_block.into()));
|
||||||
@@ -127,5 +132,17 @@ mod tests {
|
|||||||
output
|
output
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_album_details_ui_renders_track_details_over_album_details() {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::TrackDetails.into());
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
AlbumDetailsUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ mod tests {
|
|||||||
|
|
||||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||||
ALBUM_DETAILS_BLOCKS, ARTIST_DETAILS_BLOCKS, ActiveLidarrBlock, DELETE_ALBUM_BLOCKS,
|
ALBUM_DETAILS_BLOCKS, ARTIST_DETAILS_BLOCKS, ActiveLidarrBlock, DELETE_ALBUM_BLOCKS,
|
||||||
|
TRACK_DETAILS_BLOCKS,
|
||||||
};
|
};
|
||||||
use crate::ui::DrawUi;
|
use crate::ui::DrawUi;
|
||||||
use crate::ui::lidarr_ui::library::artist_details_ui::ArtistDetailsUi;
|
use crate::ui::lidarr_ui::library::artist_details_ui::ArtistDetailsUi;
|
||||||
@@ -13,6 +14,7 @@ mod tests {
|
|||||||
let mut blocks = ARTIST_DETAILS_BLOCKS.clone().to_vec();
|
let mut blocks = ARTIST_DETAILS_BLOCKS.clone().to_vec();
|
||||||
blocks.extend(DELETE_ALBUM_BLOCKS);
|
blocks.extend(DELETE_ALBUM_BLOCKS);
|
||||||
blocks.extend(ALBUM_DETAILS_BLOCKS);
|
blocks.extend(ALBUM_DETAILS_BLOCKS);
|
||||||
|
blocks.extend(TRACK_DETAILS_BLOCKS);
|
||||||
|
|
||||||
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
||||||
if blocks.contains(&active_lidarr_block) {
|
if blocks.contains(&active_lidarr_block) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ mod tests {
|
|||||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||||
ADD_ARTIST_BLOCKS, ALBUM_DETAILS_BLOCKS, ARTIST_DETAILS_BLOCKS, ActiveLidarrBlock,
|
ADD_ARTIST_BLOCKS, ALBUM_DETAILS_BLOCKS, ARTIST_DETAILS_BLOCKS, ActiveLidarrBlock,
|
||||||
DELETE_ALBUM_BLOCKS, DELETE_ARTIST_BLOCKS, EDIT_ARTIST_BLOCKS, LIBRARY_BLOCKS,
|
DELETE_ALBUM_BLOCKS, DELETE_ARTIST_BLOCKS, EDIT_ARTIST_BLOCKS, LIBRARY_BLOCKS,
|
||||||
|
TRACK_DETAILS_BLOCKS,
|
||||||
};
|
};
|
||||||
use crate::ui::DrawUi;
|
use crate::ui::DrawUi;
|
||||||
use crate::ui::lidarr_ui::library::{LibraryUi, decorate_artist_row_with_style};
|
use crate::ui::lidarr_ui::library::{LibraryUi, decorate_artist_row_with_style};
|
||||||
@@ -23,12 +24,19 @@ mod tests {
|
|||||||
library_ui_blocks.extend(ADD_ARTIST_BLOCKS);
|
library_ui_blocks.extend(ADD_ARTIST_BLOCKS);
|
||||||
library_ui_blocks.extend(ARTIST_DETAILS_BLOCKS);
|
library_ui_blocks.extend(ARTIST_DETAILS_BLOCKS);
|
||||||
library_ui_blocks.extend(ALBUM_DETAILS_BLOCKS);
|
library_ui_blocks.extend(ALBUM_DETAILS_BLOCKS);
|
||||||
|
library_ui_blocks.extend(TRACK_DETAILS_BLOCKS);
|
||||||
|
|
||||||
for active_lidarr_block in ActiveLidarrBlock::iter() {
|
for active_lidarr_block in ActiveLidarrBlock::iter() {
|
||||||
if library_ui_blocks.contains(&active_lidarr_block) {
|
if library_ui_blocks.contains(&active_lidarr_block) {
|
||||||
assert!(LibraryUi::accepts(active_lidarr_block.into()));
|
assert!(
|
||||||
|
LibraryUi::accepts(active_lidarr_block.into()),
|
||||||
|
"{active_lidarr_block} is not accepted by the LibraryUi"
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
assert!(!LibraryUi::accepts(active_lidarr_block.into()));
|
assert!(
|
||||||
|
!LibraryUi::accepts(active_lidarr_block.into()),
|
||||||
|
"{active_lidarr_block} should not be accepted by LibraryUi"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,10 +31,11 @@ use crate::{
|
|||||||
mod add_artist_ui;
|
mod add_artist_ui;
|
||||||
mod album_details_ui;
|
mod album_details_ui;
|
||||||
mod artist_details_ui;
|
mod artist_details_ui;
|
||||||
|
mod delete_album_ui;
|
||||||
mod delete_artist_ui;
|
mod delete_artist_ui;
|
||||||
mod edit_artist_ui;
|
mod edit_artist_ui;
|
||||||
|
mod track_details_ui;
|
||||||
|
|
||||||
mod delete_album_ui;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "library_ui_tests.rs"]
|
#[path = "library_ui_tests.rs"]
|
||||||
mod library_ui_tests;
|
mod library_ui_tests;
|
||||||
|
|||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/library/album_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Test Album Details ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Tracks │ History │ Manual Search │
|
||||||
|
│──────╭ Track Details ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮─────│
|
||||||
|
│ # │ Track Details │ History │ │
|
||||||
|
│=> 1 │──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│ │
|
||||||
|
│ │Some details: │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
-48
@@ -1,48 +0,0 @@
|
|||||||
---
|
|
||||||
source: src/ui/lidarr_ui/library/library_ui_tests.rs
|
|
||||||
expression: output
|
|
||||||
---
|
|
||||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
||||||
Name ▼ Type Status Quality Profile Metadata Profile Albums Tracks Size Monitored Tags
|
|
||||||
=> Alex Person Continuing Lossless Standard 1 15/15 0.00 GB 🏷 alex
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
╭─────────────────────────────────── Edit - Alex (American pianist) ────────────────────────────────────╮
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ ╭───╮ │
|
|
||||||
│ Monitored: │ ✔ │ │
|
|
||||||
│ ╰───╯ │
|
|
||||||
│ ╭─────────────────────────────────────────────────╮ │
|
|
||||||
│ Monitor New Albums: │All Albums ▼ │ │
|
|
||||||
│ ╰─────────────────────────────────────────────────╯ │
|
|
||||||
│ ╭─────────────────────────────────────────────────╮ │
|
|
||||||
│ Quality Profile: │Lossless ▼ │ │
|
|
||||||
│ ╰─────────────────────────────────────────────────╯ │
|
|
||||||
│ ╭─────────────────────────────────────────────────╮ │
|
|
||||||
│ Metadata Profile: │Standard ▼ │ │
|
|
||||||
│ ╰─────────────────────────────────────────────────╯ │
|
|
||||||
│ ╭─────────────────────────────────────────────────╮ │
|
|
||||||
│ Path: │/nfs/music │ │
|
|
||||||
│ ╰─────────────────────────────────────────────────╯ │
|
|
||||||
│ ╭─────────────────────────────────────────────────╮ │
|
|
||||||
│ Tags: │alex │ │
|
|
||||||
│ ╰─────────────────────────────────────────────────╯ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
|
|
||||||
││ Save ││ Cancel ││
|
|
||||||
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
|
|
||||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/library/track_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Track Details ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Track Details │ History │
|
||||||
|
│──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/library/track_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Track Details ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Track Details │ History │
|
||||||
|
│──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/library/track_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Track Details ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Track Details │ History │
|
||||||
|
│──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ Loading ... │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/library/track_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Track Details ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Track Details │ History │
|
||||||
|
│──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ Loading ... │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/library/track_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Track Details ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Track Details │ History │
|
||||||
|
│──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
|
│ Source Title ▼ Event Type Quality Date │
|
||||||
|
│=> Test source title grabbed Lossless 2023-01-01 00:00:00 UTC │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ ╭────────── Error ──────────╮ │
|
||||||
|
│ │ The given filter produced │ │
|
||||||
|
│ ╰─────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/library/track_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Track Details ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Track Details │ History │
|
||||||
|
│──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
|
│ Source Title ▼ Event Type Quality Date │
|
||||||
|
│=> Test source title grabbed Lossless 2023-01-01 00:00:00 UTC │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ ╭─────────── Filter ───────────╮ │
|
||||||
|
│ │track history filter │ │
|
||||||
|
│ ╰────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/library/track_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Track Details ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Track Details │ History │
|
||||||
|
│──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
|
│ Source Title ▼ Event Type Quality Date │
|
||||||
|
│=> Test source title grabbed Lossless 2023-01-01 00:00:00 UTC │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ ╭────────── Error ──────────╮ │
|
||||||
|
│ │ No items found matching │ │
|
||||||
|
│ ╰─────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/library/track_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Track Details ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Track Details │ History │
|
||||||
|
│──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
|
│ Source Title ▼ Event Type Quality Date │
|
||||||
|
│=> Test source title grabbed Lossless 2023-01-01 00:00:00 UTC │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ ╭─────────── Search ───────────╮ │
|
||||||
|
│ │track history search │ │
|
||||||
|
│ ╰────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/library/track_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Track Details ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Track Details │ History │
|
||||||
|
│──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
|
│Some details: │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/library/track_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Track Details ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Track Details │ History │
|
||||||
|
│──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
|
│ Source Title ▼ Event Type Quality Date │
|
||||||
|
│=> Test source title grabbed Lossless 2023-01-01 00:00:00 UTC │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ ╭─────────────────────────────────── Details ───────────────────────────────────╮ │
|
||||||
|
│ │Source Title: Test source title │ │
|
||||||
|
│ │Event Type: grabbed │ │
|
||||||
|
│ │Quality: Lossless │ │
|
||||||
|
│ │Date: 2023-01-01 00:00:00 UTC │ │
|
||||||
|
│ │Indexer: │ │
|
||||||
|
│ │NZB Info URL: │ │
|
||||||
|
│ │Release Group: │ │
|
||||||
|
│ │Age: 0 days │ │
|
||||||
|
│ │Published Date: 1970-01-01 00:00:00 UTC │ │
|
||||||
|
│ │Download Client: │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ ╰─────────────────────────────────────────────────────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/library/track_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Track Details ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Track Details │ History │
|
||||||
|
│──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
|
│ Source Title Event Type Quality Date │
|
||||||
|
│=> Test source title grabbed Lossless 2023-01-01 00:00:00 UTC │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ ╭──────────────────────╮ │
|
||||||
|
│ │Something │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ ╰──────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/library/track_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Track Details ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Track Details │ History │
|
||||||
|
│──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
|
│ Source Title ▼ Event Type Quality Date │
|
||||||
|
│=> Test source title grabbed Lossless 2023-01-01 00:00:00 UTC │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
use crate::app::App;
|
||||||
|
use crate::models::Route;
|
||||||
|
use crate::models::lidarr_models::{LidarrHistoryItem, Track};
|
||||||
|
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, TRACK_DETAILS_BLOCKS};
|
||||||
|
use crate::ui::lidarr_ui::lidarr_ui_utils::create_history_event_details;
|
||||||
|
use crate::ui::styles::ManagarrStyle;
|
||||||
|
use crate::ui::styles::{downloaded_style, missing_style, secondary_style};
|
||||||
|
use crate::ui::utils::{get_width_from_percentage, layout_block_top_border};
|
||||||
|
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||||
|
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||||
|
use crate::ui::widgets::message::Message;
|
||||||
|
use crate::ui::widgets::popup::{Popup, Size};
|
||||||
|
use crate::ui::{DrawUi, draw_popup, draw_tabs};
|
||||||
|
use ratatui::Frame;
|
||||||
|
use ratatui::layout::{Alignment, Constraint, Rect};
|
||||||
|
use ratatui::style::{Style, Stylize};
|
||||||
|
use ratatui::text::{Line, Span, Text};
|
||||||
|
use ratatui::widgets::{Cell, Paragraph, Row, Wrap};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "track_details_ui_tests.rs"]
|
||||||
|
mod track_details_ui_tests;
|
||||||
|
|
||||||
|
pub(super) struct TrackDetailsUi;
|
||||||
|
|
||||||
|
impl DrawUi for TrackDetailsUi {
|
||||||
|
fn accepts(route: Route) -> bool {
|
||||||
|
let Route::Lidarr(active_lidarr_block, _) = route else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
TRACK_DETAILS_BLOCKS.contains(&active_lidarr_block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, _area: Rect) {
|
||||||
|
if let Some(album_details_modal) = app.data.lidarr_data.album_details_modal.as_ref()
|
||||||
|
&& album_details_modal.track_details_modal.is_some()
|
||||||
|
&& let Route::Lidarr(active_lidarr_block, _) = app.get_current_route()
|
||||||
|
{
|
||||||
|
let draw_track_details_popup = |f: &mut Frame<'_>, app: &mut App<'_>, popup_area: Rect| {
|
||||||
|
let content_area = draw_tabs(
|
||||||
|
f,
|
||||||
|
popup_area,
|
||||||
|
"Track Details",
|
||||||
|
&app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.expect("album_details_modal must exist in this context")
|
||||||
|
.track_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.expect("track_details_modal must exist in this context")
|
||||||
|
.track_details_tabs,
|
||||||
|
);
|
||||||
|
draw_track_details_tabs(f, app, content_area);
|
||||||
|
|
||||||
|
if active_lidarr_block == ActiveLidarrBlock::TrackHistoryDetails {
|
||||||
|
draw_history_item_details_popup(f, app);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
draw_popup(f, app, draw_track_details_popup, Size::Large);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_track_details_tabs(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
if let Some(album_details_modal) = app.data.lidarr_data.album_details_modal.as_ref()
|
||||||
|
&& let Some(track_details_modal) = album_details_modal.track_details_modal.as_ref()
|
||||||
|
&& let Route::Lidarr(active_lidarr_block, _) =
|
||||||
|
track_details_modal.track_details_tabs.get_active_route()
|
||||||
|
{
|
||||||
|
match active_lidarr_block {
|
||||||
|
ActiveLidarrBlock::TrackDetails => draw_track_details(f, app, area),
|
||||||
|
ActiveLidarrBlock::TrackHistory => draw_track_history_table(f, app, area),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_track_details(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||||
|
let block = layout_block_top_border();
|
||||||
|
|
||||||
|
match app.data.lidarr_data.album_details_modal.as_ref() {
|
||||||
|
Some(album_details_modal) if !app.is_loading => {
|
||||||
|
if let Some(track_details_modal) = album_details_modal.track_details_modal.as_ref() {
|
||||||
|
let track = album_details_modal.tracks.current_selection().clone();
|
||||||
|
let track_details = &track_details_modal.track_details;
|
||||||
|
let text = Text::from(
|
||||||
|
track_details
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.filter(|it| !it.is_empty())
|
||||||
|
.map(|line| {
|
||||||
|
let split = line.split(':').collect::<Vec<&str>>();
|
||||||
|
let title = format!("{}:", split[0]);
|
||||||
|
let style = style_from_status(&track);
|
||||||
|
|
||||||
|
Line::from(vec![
|
||||||
|
title.bold().style(style),
|
||||||
|
Span::styled(split[1..].join(":"), style),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
.collect::<Vec<Line<'_>>>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let paragraph = Paragraph::new(text)
|
||||||
|
.block(block)
|
||||||
|
.wrap(Wrap { trim: false })
|
||||||
|
.scroll((track_details.offset, 0));
|
||||||
|
|
||||||
|
f.render_widget(paragraph, area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => f.render_widget(
|
||||||
|
LoadingBlock::new(
|
||||||
|
app.is_loading
|
||||||
|
|| app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.expect("album_details_modal must exist in this context")
|
||||||
|
.track_details_modal
|
||||||
|
.is_none(),
|
||||||
|
block,
|
||||||
|
),
|
||||||
|
area,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_track_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
match app.data.lidarr_data.album_details_modal.as_ref() {
|
||||||
|
Some(album_details_modal) if !app.is_loading => {
|
||||||
|
let Route::Lidarr(active_lidarr_block, _) = app.get_current_route() else {
|
||||||
|
panic!("Non-Lidarr route is being used");
|
||||||
|
};
|
||||||
|
if let Some(track_details_modal) = album_details_modal.track_details_modal.as_ref() {
|
||||||
|
let current_selection = if track_details_modal.track_history.is_empty() {
|
||||||
|
LidarrHistoryItem::default()
|
||||||
|
} else {
|
||||||
|
track_details_modal
|
||||||
|
.track_history
|
||||||
|
.current_selection()
|
||||||
|
.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let history_row_mapping = |history_item: &LidarrHistoryItem| {
|
||||||
|
let LidarrHistoryItem {
|
||||||
|
source_title,
|
||||||
|
quality,
|
||||||
|
event_type,
|
||||||
|
date,
|
||||||
|
..
|
||||||
|
} = history_item;
|
||||||
|
|
||||||
|
source_title.scroll_left_or_reset(
|
||||||
|
get_width_from_percentage(area, 40),
|
||||||
|
current_selection == *history_item,
|
||||||
|
app.ui_scroll_tick_count == 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
Row::new(vec![
|
||||||
|
Cell::from(source_title.to_string()),
|
||||||
|
Cell::from(event_type.to_string()),
|
||||||
|
Cell::from(quality.quality.name.to_owned()),
|
||||||
|
Cell::from(date.to_string()),
|
||||||
|
])
|
||||||
|
.primary()
|
||||||
|
};
|
||||||
|
let mut track_history_table = &mut app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.expect("album_details_modal must exist in this context")
|
||||||
|
.track_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.expect("track_details_modal must exist in this context")
|
||||||
|
.track_history;
|
||||||
|
let history_table = ManagarrTable::new(Some(&mut track_history_table), history_row_mapping)
|
||||||
|
.block(layout_block_top_border())
|
||||||
|
.loading(app.is_loading)
|
||||||
|
.sorting(active_lidarr_block == ActiveLidarrBlock::TrackHistorySortPrompt)
|
||||||
|
.searching(active_lidarr_block == ActiveLidarrBlock::SearchTrackHistory)
|
||||||
|
.search_produced_empty_results(
|
||||||
|
active_lidarr_block == ActiveLidarrBlock::SearchTrackHistoryError,
|
||||||
|
)
|
||||||
|
.filtering(active_lidarr_block == ActiveLidarrBlock::FilterTrackHistory)
|
||||||
|
.filter_produced_empty_results(
|
||||||
|
active_lidarr_block == ActiveLidarrBlock::FilterTrackHistoryError,
|
||||||
|
)
|
||||||
|
.headers(["Source Title", "Event Type", "Quality", "Date"])
|
||||||
|
.constraints([
|
||||||
|
Constraint::Percentage(40),
|
||||||
|
Constraint::Percentage(20),
|
||||||
|
Constraint::Percentage(15),
|
||||||
|
Constraint::Percentage(25),
|
||||||
|
]);
|
||||||
|
|
||||||
|
f.render_widget(history_table, area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => f.render_widget(
|
||||||
|
LoadingBlock::new(
|
||||||
|
app.is_loading
|
||||||
|
|| app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.expect("album_details_modal must exist in this context")
|
||||||
|
.track_details_modal
|
||||||
|
.is_none(),
|
||||||
|
layout_block_top_border(),
|
||||||
|
),
|
||||||
|
area,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_history_item_details_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||||
|
let current_selection =
|
||||||
|
if let Some(album_details_modal) = app.data.lidarr_data.album_details_modal.as_ref() {
|
||||||
|
if let Some(track_details_modal) = album_details_modal.track_details_modal.as_ref() {
|
||||||
|
if track_details_modal.track_history.is_empty() {
|
||||||
|
LidarrHistoryItem::default()
|
||||||
|
} else {
|
||||||
|
track_details_modal
|
||||||
|
.track_history
|
||||||
|
.current_selection()
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LidarrHistoryItem::default()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LidarrHistoryItem::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let line_vec = create_history_event_details(current_selection);
|
||||||
|
let text = Text::from(line_vec);
|
||||||
|
|
||||||
|
let message = Message::new(text)
|
||||||
|
.title("Details")
|
||||||
|
.style(secondary_style())
|
||||||
|
.alignment(Alignment::Left);
|
||||||
|
|
||||||
|
f.render_widget(Popup::new(message).size(Size::NarrowLongMessage), f.area());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn style_from_status(track: &Track) -> Style {
|
||||||
|
if !track.has_file {
|
||||||
|
return missing_style();
|
||||||
|
}
|
||||||
|
|
||||||
|
downloaded_style()
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, TRACK_DETAILS_BLOCKS};
|
||||||
|
use crate::ui::DrawUi;
|
||||||
|
use crate::ui::lidarr_ui::library::track_details_ui::TrackDetailsUi;
|
||||||
|
use crate::ui::ui_test_utils::test_utils::render_to_string_with_app;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_track_details_ui_accepts() {
|
||||||
|
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
||||||
|
if TRACK_DETAILS_BLOCKS.contains(&active_lidarr_block) {
|
||||||
|
assert!(TrackDetailsUi::accepts(active_lidarr_block.into()));
|
||||||
|
} else {
|
||||||
|
assert!(!TrackDetailsUi::accepts(active_lidarr_block.into()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mod snapshot_tests {
|
||||||
|
use crate::ui::ui_test_utils::test_utils::TerminalSize;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(ActiveLidarrBlock::TrackDetails, 0)]
|
||||||
|
#[case(ActiveLidarrBlock::TrackHistory, 1)]
|
||||||
|
#[case(ActiveLidarrBlock::TrackHistoryDetails, 1)]
|
||||||
|
#[case(ActiveLidarrBlock::SearchTrackHistory, 1)]
|
||||||
|
#[case(ActiveLidarrBlock::SearchTrackHistoryError, 1)]
|
||||||
|
#[case(ActiveLidarrBlock::FilterTrackHistory, 1)]
|
||||||
|
#[case(ActiveLidarrBlock::FilterTrackHistoryError, 1)]
|
||||||
|
#[case(ActiveLidarrBlock::TrackHistorySortPrompt, 1)]
|
||||||
|
#[case(ActiveLidarrBlock::TrackHistoryDetails, 1)]
|
||||||
|
fn test_track_details_ui_renders(
|
||||||
|
#[case] active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
#[case] index: usize,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.push_navigation_stack(active_lidarr_block.into());
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_tabs
|
||||||
|
.set_index(index);
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
TrackDetailsUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(
|
||||||
|
format!("track_details_{active_lidarr_block}_{index}"),
|
||||||
|
output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(ActiveLidarrBlock::TrackDetails, 0)]
|
||||||
|
#[case(ActiveLidarrBlock::TrackHistory, 1)]
|
||||||
|
fn test_track_details_ui_renders_loading(
|
||||||
|
#[case] active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
#[case] index: usize,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(active_lidarr_block.into());
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_tabs
|
||||||
|
.set_index(index);
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
TrackDetailsUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(
|
||||||
|
format!("loading_track_details_{active_lidarr_block}_{index}"),
|
||||||
|
output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(ActiveLidarrBlock::TrackDetails, 0)]
|
||||||
|
#[case(ActiveLidarrBlock::TrackHistory, 1)]
|
||||||
|
fn test_track_details_ui_renders_empty(
|
||||||
|
#[case] active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
#[case] index: usize,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.push_navigation_stack(active_lidarr_block.into());
|
||||||
|
{
|
||||||
|
let track_details_modal = app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.album_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.track_details_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap();
|
||||||
|
track_details_modal.track_details_tabs.set_index(index);
|
||||||
|
track_details_modal.track_details = Default::default();
|
||||||
|
track_details_modal.track_history = Default::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
TrackDetailsUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(
|
||||||
|
format!("empty_track_details_{active_lidarr_block}_{index}"),
|
||||||
|
output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -425,6 +425,7 @@ mod tests {
|
|||||||
source_title: "\nTest Album - Artist Name".into(),
|
source_title: "\nTest Album - Artist Name".into(),
|
||||||
album_id: 100,
|
album_id: 100,
|
||||||
artist_id: 10,
|
artist_id: 10,
|
||||||
|
track_id: 1,
|
||||||
event_type,
|
event_type,
|
||||||
quality: QualityWrapper {
|
quality: QualityWrapper {
|
||||||
quality: Quality {
|
quality: Quality {
|
||||||
|
|||||||
Reference in New Issue
Block a user