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::{
|
||||
ADD_ARTIST_BLOCKS, ADD_ROOT_FOLDER_BLOCKS, ALBUM_DETAILS_BLOCKS, ARTIST_DETAILS_BLOCKS,
|
||||
ActiveLidarrBlock, EDIT_ARTIST_BLOCKS, EDIT_INDEXER_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
||||
TRACK_DETAILS_BLOCKS,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -103,8 +104,8 @@ pub static ALBUM_DETAILS_CONTEXT_CLUES: [ContextClue; 6] = [
|
||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||
(DEFAULT_KEYBINDINGS.submit, "episode details"),
|
||||
(DEFAULT_KEYBINDINGS.delete, "delete episode"),
|
||||
(DEFAULT_KEYBINDINGS.submit, "track details"),
|
||||
(DEFAULT_KEYBINDINGS.delete, "delete track"),
|
||||
];
|
||||
|
||||
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),
|
||||
];
|
||||
|
||||
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;
|
||||
|
||||
impl ContextClueProvider for LidarrContextClueProvider {
|
||||
@@ -156,9 +177,20 @@ impl ContextClueProvider for LidarrContextClueProvider {
|
||||
.lidarr_data
|
||||
.album_details_modal
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.expect("album_details_modal is empty")
|
||||
.album_details_tabs
|
||||
.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::AddArtistEmptySearchResults
|
||||
| ActiveLidarrBlock::TestAllIndexers
|
||||
|
||||
@@ -10,13 +10,13 @@ mod tests {
|
||||
ADD_ARTIST_SEARCH_RESULTS_CONTEXT_CLUES, ALBUM_DETAILS_CONTEXT_CLUES,
|
||||
ALBUM_HISTORY_CONTEXT_CLUES, ARTIST_DETAILS_CONTEXT_CLUES, ARTIST_HISTORY_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::{
|
||||
ADD_ROOT_FOLDER_BLOCKS, ActiveLidarrBlock, EDIT_ARTIST_BLOCKS, EDIT_INDEXER_BLOCKS,
|
||||
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 rstest::rstest;
|
||||
|
||||
@@ -266,11 +266,11 @@ mod tests {
|
||||
);
|
||||
assert_some_eq_x!(
|
||||
album_details_context_clues_iter.next(),
|
||||
&(DEFAULT_KEYBINDINGS.submit, "episode details")
|
||||
&(DEFAULT_KEYBINDINGS.submit, "track details")
|
||||
);
|
||||
assert_some_eq_x!(
|
||||
album_details_context_clues_iter.next(),
|
||||
&(DEFAULT_KEYBINDINGS.delete, "delete episode")
|
||||
&(DEFAULT_KEYBINDINGS.delete, "delete track")
|
||||
);
|
||||
assert_none!(album_details_context_clues_iter.next());
|
||||
}
|
||||
@@ -278,6 +278,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_album_history_context_clues() {
|
||||
let mut album_history_context_clues_iter = ALBUM_HISTORY_CONTEXT_CLUES.iter();
|
||||
|
||||
assert_some_eq_x!(
|
||||
album_history_context_clues_iter.next(),
|
||||
&(
|
||||
@@ -348,6 +349,58 @@ mod tests {
|
||||
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]
|
||||
#[case(0, ActiveLidarrBlock::ArtistDetails, &ARTIST_DETAILS_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);
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn test_lidarr_context_clue_provider_artists_block() {
|
||||
let mut app = App::test_default();
|
||||
|
||||
@@ -7,7 +7,9 @@ mod tests {
|
||||
use crate::models::servarr_models::Indexer;
|
||||
use crate::network::NetworkEvent;
|
||||
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 tokio::sync::mpsc;
|
||||
|
||||
@@ -464,6 +466,46 @@ mod tests {
|
||||
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]
|
||||
async fn test_check_for_lidarr_prompt_action_no_prompt_confirm() {
|
||||
let mut app = App::test_default();
|
||||
@@ -684,6 +726,32 @@ mod tests {
|
||||
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]
|
||||
async fn test_extract_lidarr_indexer_id() {
|
||||
let mut app = App::test_default();
|
||||
|
||||
@@ -153,6 +153,23 @@ impl App<'_> {
|
||||
.dispatch_network_event(LidarrEvent::GetUpdates.into())
|
||||
.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
|
||||
}
|
||||
|
||||
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 {
|
||||
self.data.lidarr_data.indexers.current_selection().id
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user