feat: CLI support for searching for discography releases in Lidarr
This commit is contained in:
@@ -136,7 +136,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_start_task_requires_task_name() {
|
fn test_start_task_requires_task_name() {
|
||||||
let result = Cli::command().try_get_matches_from(["managarr", "sonarr", "start-task"]);
|
let result = Cli::command().try_get_matches_from(["managarr", "lidarr", "start-task"]);
|
||||||
|
|
||||||
assert_err!(&result);
|
assert_err!(&result);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -232,6 +232,7 @@ mod tests {
|
|||||||
use crate::cli::lidarr::add_command_handler::LidarrAddCommand;
|
use crate::cli::lidarr::add_command_handler::LidarrAddCommand;
|
||||||
use crate::cli::lidarr::edit_command_handler::LidarrEditCommand;
|
use crate::cli::lidarr::edit_command_handler::LidarrEditCommand;
|
||||||
use crate::cli::lidarr::get_command_handler::LidarrGetCommand;
|
use crate::cli::lidarr::get_command_handler::LidarrGetCommand;
|
||||||
|
use crate::cli::lidarr::manual_search_command_handler::LidarrManualSearchCommand;
|
||||||
use crate::cli::lidarr::refresh_command_handler::LidarrRefreshCommand;
|
use crate::cli::lidarr::refresh_command_handler::LidarrRefreshCommand;
|
||||||
use crate::cli::lidarr::trigger_automatic_search_command_handler::LidarrTriggerAutomaticSearchCommand;
|
use crate::cli::lidarr::trigger_automatic_search_command_handler::LidarrTriggerAutomaticSearchCommand;
|
||||||
use crate::models::lidarr_models::LidarrTaskName;
|
use crate::models::lidarr_models::LidarrTaskName;
|
||||||
@@ -436,6 +437,34 @@ mod tests {
|
|||||||
assert_ok!(&result);
|
assert_ok!(&result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_lidarr_cli_handler_delegates_manual_search_commands_to_the_manual_search_command_handler()
|
||||||
|
{
|
||||||
|
let expected_artist_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
LidarrEvent::GetDiscographyReleases(expected_artist_id).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||||
|
let manual_episode_search_command =
|
||||||
|
LidarrCommand::ManualSearch(LidarrManualSearchCommand::Discography { artist_id: 1 });
|
||||||
|
|
||||||
|
let result =
|
||||||
|
LidarrCliHandler::with(&app_arc, manual_episode_search_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_lidarr_cli_handler_delegates_trigger_automatic_search_commands_to_the_trigger_automatic_search_command_handler()
|
async fn test_lidarr_cli_handler_delegates_trigger_automatic_search_commands_to_the_trigger_automatic_search_command_handler()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
use crate::app::App;
|
||||||
|
use crate::cli::lidarr::LidarrCommand;
|
||||||
|
use crate::cli::{CliCommandHandler, Command};
|
||||||
|
use crate::network::NetworkTrait;
|
||||||
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Subcommand;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "manual_search_command_handler_tests.rs"]
|
||||||
|
mod manual_search_command_handler_tests;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Subcommand)]
|
||||||
|
pub enum LidarrManualSearchCommand {
|
||||||
|
#[command(
|
||||||
|
about = "Trigger a manual search of discography releases for the given artist corresponding to the artist with the given ID.\nNote that when downloading a discography release, ensure that the release includes 'discography: true', otherwise you'll run into issues"
|
||||||
|
)]
|
||||||
|
Discography {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The Lidarr ID of the artist whose discography releases you wish to fetch and list",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
artist_id: i64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LidarrManualSearchCommand> for Command {
|
||||||
|
fn from(value: LidarrManualSearchCommand) -> Self {
|
||||||
|
Command::Lidarr(LidarrCommand::ManualSearch(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct LidarrManualSearchCommandHandler<'a, 'b> {
|
||||||
|
_app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: LidarrManualSearchCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrManualSearchCommand>
|
||||||
|
for LidarrManualSearchCommandHandler<'a, 'b>
|
||||||
|
{
|
||||||
|
fn with(
|
||||||
|
_app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: LidarrManualSearchCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
) -> Self {
|
||||||
|
LidarrManualSearchCommandHandler {
|
||||||
|
_app,
|
||||||
|
command,
|
||||||
|
network,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle(self) -> Result<String> {
|
||||||
|
let result = match self.command {
|
||||||
|
LidarrManualSearchCommand::Discography { artist_id } => {
|
||||||
|
println!("Searching for artist discography releases. This may take a minute...");
|
||||||
|
let resp = self
|
||||||
|
.network
|
||||||
|
.handle_network_event(LidarrEvent::GetDiscographyReleases(artist_id).into())
|
||||||
|
.await?;
|
||||||
|
serde_json::to_string_pretty(&resp)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::cli::Command;
|
||||||
|
use crate::cli::lidarr::LidarrCommand;
|
||||||
|
use crate::cli::lidarr::manual_search_command_handler::LidarrManualSearchCommand;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_manual_search_command_from() {
|
||||||
|
let command = LidarrManualSearchCommand::Discography { artist_id: 1 };
|
||||||
|
|
||||||
|
let result = Command::from(command.clone());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
Command::Lidarr(LidarrCommand::ManualSearch(command))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod cli {
|
||||||
|
use crate::Cli;
|
||||||
|
use clap::CommandFactory;
|
||||||
|
use clap::error::ErrorKind;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_manual_discography_search_requires_artist_id() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "lidarr", "manual-search", "discography"]);
|
||||||
|
|
||||||
|
assert_err!(&result);
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_manual_discography_search_requirements_satisfied() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"lidarr",
|
||||||
|
"manual-search",
|
||||||
|
"discography",
|
||||||
|
"--artist-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod handler {
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::cli::CliCommandHandler;
|
||||||
|
use crate::cli::lidarr::manual_search_command_handler::{
|
||||||
|
LidarrManualSearchCommand, LidarrManualSearchCommandHandler,
|
||||||
|
};
|
||||||
|
use crate::models::Serdeable;
|
||||||
|
use crate::models::lidarr_models::LidarrSerdeable;
|
||||||
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
|
use crate::network::{MockNetworkTrait, NetworkEvent};
|
||||||
|
use mockall::predicate::eq;
|
||||||
|
use serde_json::json;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_manual_discography_search_command() {
|
||||||
|
let expected_artist_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
LidarrEvent::GetDiscographyReleases(expected_artist_id).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||||
|
let manual_discography_search_command =
|
||||||
|
LidarrManualSearchCommand::Discography { artist_id: 1 };
|
||||||
|
|
||||||
|
let result = LidarrManualSearchCommandHandler::with(
|
||||||
|
&app_arc,
|
||||||
|
manual_discography_search_command,
|
||||||
|
&mut mock_network,
|
||||||
|
)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,9 @@ use trigger_automatic_search_command_handler::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{CliCommandHandler, Command};
|
use super::{CliCommandHandler, Command};
|
||||||
|
use crate::cli::lidarr::manual_search_command_handler::{
|
||||||
|
LidarrManualSearchCommand, LidarrManualSearchCommandHandler,
|
||||||
|
};
|
||||||
use crate::models::lidarr_models::LidarrTaskName;
|
use crate::models::lidarr_models::LidarrTaskName;
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
use crate::{app::App, network::NetworkTrait};
|
use crate::{app::App, network::NetworkTrait};
|
||||||
@@ -30,6 +33,7 @@ mod trigger_automatic_search_command_handler;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "lidarr_command_tests.rs"]
|
#[path = "lidarr_command_tests.rs"]
|
||||||
mod lidarr_command_tests;
|
mod lidarr_command_tests;
|
||||||
|
mod manual_search_command_handler;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Subcommand)]
|
#[derive(Debug, Clone, PartialEq, Eq, Subcommand)]
|
||||||
pub enum LidarrCommand {
|
pub enum LidarrCommand {
|
||||||
@@ -63,6 +67,8 @@ pub enum LidarrCommand {
|
|||||||
about = "Commands to refresh the data in your Lidarr instance"
|
about = "Commands to refresh the data in your Lidarr instance"
|
||||||
)]
|
)]
|
||||||
Refresh(LidarrRefreshCommand),
|
Refresh(LidarrRefreshCommand),
|
||||||
|
#[command(subcommand, about = "Commands to manually search for releases")]
|
||||||
|
ManualSearch(LidarrManualSearchCommand),
|
||||||
#[command(
|
#[command(
|
||||||
subcommand,
|
subcommand,
|
||||||
about = "Commands to trigger automatic searches for releases of different resources in your Lidarr instance"
|
about = "Commands to trigger automatic searches for releases of different resources in your Lidarr instance"
|
||||||
@@ -186,6 +192,11 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrCommand> for LidarrCliHandler<'a, '
|
|||||||
.handle()
|
.handle()
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
|
LidarrCommand::ManualSearch(manual_search_command) => {
|
||||||
|
LidarrManualSearchCommandHandler::with(self.app, manual_search_command, self.network)
|
||||||
|
.handle()
|
||||||
|
.await?
|
||||||
|
}
|
||||||
LidarrCommand::TriggerAutomaticSearch(trigger_automatic_search_command) => {
|
LidarrCommand::TriggerAutomaticSearch(trigger_automatic_search_command) => {
|
||||||
LidarrTriggerAutomaticSearchCommandHandler::with(
|
LidarrTriggerAutomaticSearchCommandHandler::with(
|
||||||
self.app,
|
self.app,
|
||||||
|
|||||||
@@ -464,6 +464,30 @@ impl Display for LidarrTaskName {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct LidarrRelease {
|
||||||
|
pub guid: String,
|
||||||
|
pub protocol: String,
|
||||||
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
|
pub age: i64,
|
||||||
|
pub title: HorizontallyScrollableText,
|
||||||
|
pub discography: bool,
|
||||||
|
pub artist_name: Option<String>,
|
||||||
|
pub album_title: Option<String>,
|
||||||
|
pub indexer: String,
|
||||||
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
|
pub indexer_id: i64,
|
||||||
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
|
pub size: i64,
|
||||||
|
pub rejected: bool,
|
||||||
|
pub rejections: Option<Vec<String>>,
|
||||||
|
pub seeders: Option<Number>,
|
||||||
|
pub leechers: Option<Number>,
|
||||||
|
pub quality: QualityWrapper,
|
||||||
|
}
|
||||||
|
|
||||||
impl From<LidarrSerdeable> for Serdeable {
|
impl From<LidarrSerdeable> for Serdeable {
|
||||||
fn from(value: LidarrSerdeable) -> Serdeable {
|
fn from(value: LidarrSerdeable) -> Serdeable {
|
||||||
Serdeable::Lidarr(value)
|
Serdeable::Lidarr(value)
|
||||||
@@ -489,6 +513,7 @@ serde_enum_from!(
|
|||||||
MetadataProfiles(Vec<MetadataProfile>),
|
MetadataProfiles(Vec<MetadataProfile>),
|
||||||
QualityProfiles(Vec<QualityProfile>),
|
QualityProfiles(Vec<QualityProfile>),
|
||||||
QueueEvents(Vec<QueueEvent>),
|
QueueEvents(Vec<QueueEvent>),
|
||||||
|
Releases(Vec<LidarrRelease>),
|
||||||
RootFolders(Vec<RootFolder>),
|
RootFolders(Vec<RootFolder>),
|
||||||
SecurityConfig(SecurityConfig),
|
SecurityConfig(SecurityConfig),
|
||||||
SystemStatus(SystemStatus),
|
SystemStatus(SystemStatus),
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ mod tests {
|
|||||||
|
|
||||||
use crate::models::lidarr_models::{
|
use crate::models::lidarr_models::{
|
||||||
AddArtistSearchResult, Album, DownloadRecord, DownloadStatus, DownloadsResponse,
|
AddArtistSearchResult, Album, DownloadRecord, DownloadStatus, DownloadsResponse,
|
||||||
LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, LidarrTask, Member,
|
LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, LidarrRelease, LidarrTask,
|
||||||
MetadataProfile, MonitorType, NewItemMonitorType, SystemStatus,
|
Member, MetadataProfile, MonitorType, NewItemMonitorType, SystemStatus,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_models::{
|
use crate::models::servarr_models::{
|
||||||
DiskSpace, HostConfig, Indexer, IndexerSettings, IndexerTestResult, Log, LogResponse,
|
DiskSpace, HostConfig, Indexer, IndexerSettings, IndexerTestResult, Log, LogResponse,
|
||||||
@@ -460,6 +460,18 @@ mod tests {
|
|||||||
assert_eq!(lidarr_serdeable, LidarrSerdeable::RootFolders(root_folders));
|
assert_eq!(lidarr_serdeable, LidarrSerdeable::RootFolders(root_folders));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_serdeable_from_releases() {
|
||||||
|
let releases = vec![LidarrRelease {
|
||||||
|
guid: "test".to_owned(),
|
||||||
|
..LidarrRelease::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let lidarr_serdeable: LidarrSerdeable = releases.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(lidarr_serdeable, LidarrSerdeable::Releases(releases));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lidarr_serdeable_from_security_config() {
|
fn test_lidarr_serdeable_from_security_config() {
|
||||||
let security_config = SecurityConfig {
|
let security_config = SecurityConfig {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::app::context_clues::{
|
|||||||
use crate::app::lidarr::lidarr_context_clues::{
|
use crate::app::lidarr::lidarr_context_clues::{
|
||||||
ARTIST_DETAILS_CONTEXT_CLUES, ARTIST_HISTORY_CONTEXT_CLUES, ARTISTS_CONTEXT_CLUES,
|
ARTIST_DETAILS_CONTEXT_CLUES, ARTIST_HISTORY_CONTEXT_CLUES, ARTISTS_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::models::lidarr_models::LidarrTask;
|
use crate::models::lidarr_models::{LidarrRelease, LidarrTask};
|
||||||
use crate::models::servarr_data::modals::EditIndexerModal;
|
use crate::models::servarr_data::modals::EditIndexerModal;
|
||||||
use crate::models::servarr_models::{IndexerSettings, QueueEvent};
|
use crate::models::servarr_models::{IndexerSettings, QueueEvent};
|
||||||
use crate::models::stateful_list::StatefulList;
|
use crate::models::stateful_list::StatefulList;
|
||||||
@@ -35,6 +35,9 @@ use {
|
|||||||
metadata_profile, metadata_profile_map, quality_profile, root_folder, tags_map,
|
metadata_profile, metadata_profile_map, quality_profile, root_folder, tags_map,
|
||||||
},
|
},
|
||||||
crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{log_line, task},
|
crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{log_line, task},
|
||||||
|
crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{
|
||||||
|
torrent_release, usenet_release,
|
||||||
|
},
|
||||||
crate::network::servarr_test_utils::diskspace,
|
crate::network::servarr_test_utils::diskspace,
|
||||||
crate::network::servarr_test_utils::indexer_test_result,
|
crate::network::servarr_test_utils::indexer_test_result,
|
||||||
crate::network::servarr_test_utils::queued_event,
|
crate::network::servarr_test_utils::queued_event,
|
||||||
@@ -58,6 +61,7 @@ pub struct LidarrData<'a> {
|
|||||||
pub artist_info_tabs: TabState,
|
pub artist_info_tabs: TabState,
|
||||||
pub artists: StatefulTable<Artist>,
|
pub artists: StatefulTable<Artist>,
|
||||||
pub delete_files: bool,
|
pub delete_files: bool,
|
||||||
|
pub discography_releases: StatefulTable<LidarrRelease>,
|
||||||
pub disk_space_vec: Vec<DiskSpace>,
|
pub disk_space_vec: Vec<DiskSpace>,
|
||||||
pub downloads: StatefulTable<DownloadRecord>,
|
pub downloads: StatefulTable<DownloadRecord>,
|
||||||
pub edit_artist_modal: Option<EditArtistModal>,
|
pub edit_artist_modal: Option<EditArtistModal>,
|
||||||
@@ -92,6 +96,7 @@ impl LidarrData<'_> {
|
|||||||
|
|
||||||
pub fn reset_artist_info_tabs(&mut self) {
|
pub fn reset_artist_info_tabs(&mut self) {
|
||||||
self.albums = StatefulTable::default();
|
self.albums = StatefulTable::default();
|
||||||
|
self.discography_releases = StatefulTable::default();
|
||||||
self.artist_history = None;
|
self.artist_history = None;
|
||||||
self.artist_info_tabs.index = 0;
|
self.artist_info_tabs.index = 0;
|
||||||
}
|
}
|
||||||
@@ -140,6 +145,7 @@ impl<'a> Default for LidarrData<'a> {
|
|||||||
artist_history: None,
|
artist_history: None,
|
||||||
artists: StatefulTable::default(),
|
artists: StatefulTable::default(),
|
||||||
delete_files: false,
|
delete_files: false,
|
||||||
|
discography_releases: StatefulTable::default(),
|
||||||
disk_space_vec: Vec::new(),
|
disk_space_vec: Vec::new(),
|
||||||
downloads: StatefulTable::default(),
|
downloads: StatefulTable::default(),
|
||||||
edit_artist_modal: None,
|
edit_artist_modal: None,
|
||||||
@@ -333,6 +339,12 @@ impl LidarrData<'_> {
|
|||||||
}]);
|
}]);
|
||||||
lidarr_data.history.search = Some("test search".into());
|
lidarr_data.history.search = Some("test search".into());
|
||||||
lidarr_data.history.filter = Some("test filter".into());
|
lidarr_data.history.filter = Some("test filter".into());
|
||||||
|
lidarr_data
|
||||||
|
.discography_releases
|
||||||
|
.set_items(vec![torrent_release(), usenet_release()]);
|
||||||
|
lidarr_data
|
||||||
|
.discography_releases
|
||||||
|
.sorting(vec![sort_option!(indexer_id)]);
|
||||||
lidarr_data.root_folders.set_items(vec![root_folder()]);
|
lidarr_data.root_folders.set_items(vec![root_folder()]);
|
||||||
lidarr_data.indexers.set_items(vec![indexer()]);
|
lidarr_data.indexers.set_items(vec![indexer()]);
|
||||||
lidarr_data.queued_events.set_items(vec![queued_event()]);
|
lidarr_data.queued_events.set_items(vec![queued_event()]);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ mod tests {
|
|||||||
use crate::app::lidarr::lidarr_context_clues::{
|
use crate::app::lidarr::lidarr_context_clues::{
|
||||||
ARTIST_DETAILS_CONTEXT_CLUES, ARTIST_HISTORY_CONTEXT_CLUES, ARTISTS_CONTEXT_CLUES,
|
ARTIST_DETAILS_CONTEXT_CLUES, ARTIST_HISTORY_CONTEXT_CLUES, ARTISTS_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::models::lidarr_models::Album;
|
use crate::models::lidarr_models::{Album, LidarrRelease};
|
||||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||||
ADD_ARTIST_BLOCKS, ADD_ARTIST_SELECTION_BLOCKS, ADD_ROOT_FOLDER_BLOCKS, ARTIST_DETAILS_BLOCKS,
|
ADD_ARTIST_BLOCKS, ADD_ARTIST_SELECTION_BLOCKS, ADD_ROOT_FOLDER_BLOCKS, ARTIST_DETAILS_BLOCKS,
|
||||||
DELETE_ALBUM_BLOCKS, DELETE_ALBUM_SELECTION_BLOCKS, DELETE_ARTIST_BLOCKS,
|
DELETE_ALBUM_BLOCKS, DELETE_ALBUM_SELECTION_BLOCKS, DELETE_ARTIST_BLOCKS,
|
||||||
@@ -60,12 +60,16 @@ mod tests {
|
|||||||
fn test_reset_artist_info_tabs() {
|
fn test_reset_artist_info_tabs() {
|
||||||
let mut lidarr_data = LidarrData::default();
|
let mut lidarr_data = LidarrData::default();
|
||||||
lidarr_data.albums.set_items(vec![Album::default()]);
|
lidarr_data.albums.set_items(vec![Album::default()]);
|
||||||
|
lidarr_data
|
||||||
|
.discography_releases
|
||||||
|
.set_items(vec![LidarrRelease::default()]);
|
||||||
lidarr_data.artist_history = Some(StatefulTable::default());
|
lidarr_data.artist_history = Some(StatefulTable::default());
|
||||||
lidarr_data.artist_info_tabs.index = 1;
|
lidarr_data.artist_info_tabs.index = 1;
|
||||||
|
|
||||||
lidarr_data.reset_artist_info_tabs();
|
lidarr_data.reset_artist_info_tabs();
|
||||||
|
|
||||||
assert_is_empty!(lidarr_data.albums);
|
assert_is_empty!(lidarr_data.albums);
|
||||||
|
assert_is_empty!(lidarr_data.discography_releases);
|
||||||
assert_none!(lidarr_data.artist_history);
|
assert_none!(lidarr_data.artist_history);
|
||||||
assert_eq!(lidarr_data.artist_info_tabs.index, 0);
|
assert_eq!(lidarr_data.artist_info_tabs.index, 0);
|
||||||
}
|
}
|
||||||
@@ -146,6 +150,7 @@ mod tests {
|
|||||||
assert_is_empty!(lidarr_data.downloads);
|
assert_is_empty!(lidarr_data.downloads);
|
||||||
assert_none!(lidarr_data.edit_artist_modal);
|
assert_none!(lidarr_data.edit_artist_modal);
|
||||||
assert_none!(lidarr_data.add_root_folder_modal);
|
assert_none!(lidarr_data.add_root_folder_modal);
|
||||||
|
assert_is_empty!(lidarr_data.discography_releases);
|
||||||
assert_is_empty!(lidarr_data.history);
|
assert_is_empty!(lidarr_data.history);
|
||||||
assert_is_empty!(lidarr_data.logs);
|
assert_is_empty!(lidarr_data.logs);
|
||||||
assert_is_empty!(lidarr_data.log_details);
|
assert_is_empty!(lidarr_data.log_details);
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::models::lidarr_models::{
|
use crate::models::lidarr_models::{
|
||||||
AddArtistBody, AddArtistOptions, AddArtistSearchResult, Artist, DeleteParams, EditArtistParams,
|
AddArtistBody, AddArtistOptions, AddArtistSearchResult, Artist, DeleteParams, EditArtistParams,
|
||||||
LidarrHistoryItem, LidarrSerdeable, MonitorType, NewItemMonitorType,
|
LidarrHistoryItem, LidarrRelease, LidarrSerdeable, MonitorType, NewItemMonitorType,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
|
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
|
||||||
use crate::models::stateful_table::{SortOption, StatefulTable};
|
use crate::models::stateful_table::{SortOption, StatefulTable};
|
||||||
use crate::network::NetworkResource;
|
use crate::network::NetworkResource;
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{
|
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{
|
||||||
ADD_ARTIST_SEARCH_RESULT_JSON, ARTIST_JSON, artist, lidarr_history_item,
|
ADD_ARTIST_SEARCH_RESULT_JSON, ARTIST_JSON, artist, lidarr_history_item, torrent_release,
|
||||||
};
|
};
|
||||||
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
|
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
|
||||||
use bimap::BiMap;
|
use bimap::BiMap;
|
||||||
@@ -393,6 +393,87 @@ mod tests {
|
|||||||
assert_eq!(history_items, response);
|
assert_eq!(history_items, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_artist_discography_releases_event() {
|
||||||
|
let release_json = json!([
|
||||||
|
{
|
||||||
|
"guid": "1234",
|
||||||
|
"protocol": "torrent",
|
||||||
|
"age": 1,
|
||||||
|
"title": "Test Release",
|
||||||
|
"indexer": "kickass torrents",
|
||||||
|
"indexerId": 2,
|
||||||
|
"artistName": "Alex",
|
||||||
|
"albumTitle": "Something",
|
||||||
|
"size": 1234,
|
||||||
|
"rejected": true,
|
||||||
|
"rejections": [ "Unknown quality profile", "Release is already mapped" ],
|
||||||
|
"seeders": 2,
|
||||||
|
"leechers": 1,
|
||||||
|
"quality": { "quality": { "name": "Lossless" }},
|
||||||
|
"discography": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"guid": "4567",
|
||||||
|
"protocol": "torrent",
|
||||||
|
"age": 1,
|
||||||
|
"title": "Test Release",
|
||||||
|
"indexer": "kickass torrents",
|
||||||
|
"indexerId": 2,
|
||||||
|
"artistName": "Alex",
|
||||||
|
"albumTitle": "Something",
|
||||||
|
"size": 1234,
|
||||||
|
"rejected": true,
|
||||||
|
"rejections": [ "Unknown quality profile", "Release is already mapped" ],
|
||||||
|
"seeders": 2,
|
||||||
|
"leechers": 1,
|
||||||
|
"quality": { "quality": { "name": "Lossless" }},
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
let expected_filtered_lidarr_release = LidarrRelease {
|
||||||
|
discography: true,
|
||||||
|
..torrent_release()
|
||||||
|
};
|
||||||
|
let expected_raw_lidarr_releases = vec![
|
||||||
|
LidarrRelease {
|
||||||
|
discography: true,
|
||||||
|
..torrent_release()
|
||||||
|
},
|
||||||
|
LidarrRelease {
|
||||||
|
guid: "4567".to_owned(),
|
||||||
|
..torrent_release()
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let (mock, app, _server) = MockServarrApi::get()
|
||||||
|
.returns(release_json)
|
||||||
|
.query("artistId=1")
|
||||||
|
.build_for(LidarrEvent::GetDiscographyReleases(1))
|
||||||
|
.await;
|
||||||
|
app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.artists
|
||||||
|
.set_items(vec![artist()]);
|
||||||
|
app.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
let LidarrSerdeable::Releases(releases_vec) = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetDiscographyReleases(1))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("Expected Releases")
|
||||||
|
};
|
||||||
|
mock.assert_async().await;
|
||||||
|
assert_eq!(
|
||||||
|
app.lock().await.data.lidarr_data.discography_releases.items,
|
||||||
|
vec![expected_filtered_lidarr_release]
|
||||||
|
);
|
||||||
|
assert_eq!(releases_vec, expected_raw_lidarr_releases);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_handle_toggle_artist_monitoring_event() {
|
async fn test_handle_toggle_artist_monitoring_event() {
|
||||||
let artist_json = json!({
|
let artist_json = json!({
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use serde_json::{Value, json};
|
|||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
use crate::models::lidarr_models::{
|
use crate::models::lidarr_models::{
|
||||||
AddArtistBody, AddArtistSearchResult, Artist, DeleteParams, EditArtistParams, LidarrCommandBody,
|
AddArtistBody, AddArtistSearchResult, Artist, DeleteParams, EditArtistParams, LidarrCommandBody,
|
||||||
LidarrHistoryItem,
|
LidarrHistoryItem, LidarrRelease,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
|
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
|
||||||
use crate::models::stateful_table::StatefulTable;
|
use crate::models::stateful_table::StatefulTable;
|
||||||
@@ -317,6 +317,39 @@ impl Network<'_, '_> {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(in crate::network::lidarr_network) async fn get_artist_discography_releases(
|
||||||
|
&mut self,
|
||||||
|
artist_id: i64,
|
||||||
|
) -> Result<Vec<LidarrRelease>> {
|
||||||
|
let event = LidarrEvent::GetDiscographyReleases(artist_id);
|
||||||
|
info!("Fetching discography releases for artist with ID: {artist_id}");
|
||||||
|
|
||||||
|
let request_props = self
|
||||||
|
.request_props_from(
|
||||||
|
event,
|
||||||
|
RequestMethod::Get,
|
||||||
|
None::<()>,
|
||||||
|
None,
|
||||||
|
Some(format!("artistId={artist_id}")),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<(), Vec<LidarrRelease>>(request_props, |release_vec, mut app| {
|
||||||
|
let artist_releases_vec = release_vec
|
||||||
|
.into_iter()
|
||||||
|
.filter(|release| release.discography)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.discography_releases
|
||||||
|
.set_items(artist_releases_vec);
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub(in crate::network::lidarr_network) async fn edit_artist(
|
pub(in crate::network::lidarr_network) async fn edit_artist(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut edit_artist_params: EditArtistParams,
|
mut edit_artist_params: EditArtistParams,
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ pub mod test_utils {
|
|||||||
use crate::models::lidarr_models::{
|
use crate::models::lidarr_models::{
|
||||||
AddArtistSearchResult, Album, AlbumStatistics, Artist, ArtistStatistics, ArtistStatus,
|
AddArtistSearchResult, Album, AlbumStatistics, Artist, ArtistStatistics, ArtistStatus,
|
||||||
DownloadRecord, DownloadStatus, DownloadsResponse, EditArtistParams, LidarrHistoryData,
|
DownloadRecord, DownloadStatus, DownloadsResponse, EditArtistParams, LidarrHistoryData,
|
||||||
LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, LidarrTask, LidarrTaskName,
|
LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, LidarrRelease, LidarrTask,
|
||||||
Member, MetadataProfile, NewItemMonitorType, Ratings, SystemStatus,
|
LidarrTaskName, Member, MetadataProfile, NewItemMonitorType, Ratings, SystemStatus,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_models::IndexerSettings;
|
use crate::models::servarr_models::IndexerSettings;
|
||||||
use crate::models::servarr_models::{
|
use crate::models::servarr_models::{
|
||||||
@@ -377,4 +377,51 @@ pub mod test_utils {
|
|||||||
* Fixed bug 2"
|
* Fixed bug 2"
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rejections() -> Vec<String> {
|
||||||
|
vec![
|
||||||
|
"Unknown quality profile".to_owned(),
|
||||||
|
"Release is already mapped".to_owned(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn torrent_release() -> LidarrRelease {
|
||||||
|
LidarrRelease {
|
||||||
|
guid: "1234".to_owned(),
|
||||||
|
protocol: "torrent".to_owned(),
|
||||||
|
age: 1,
|
||||||
|
title: HorizontallyScrollableText::from("Test Release"),
|
||||||
|
discography: false,
|
||||||
|
artist_name: Some("Alex".to_owned()),
|
||||||
|
album_title: Some("Something".to_owned()),
|
||||||
|
indexer: "kickass torrents".to_owned(),
|
||||||
|
indexer_id: 2,
|
||||||
|
size: 1234,
|
||||||
|
rejected: true,
|
||||||
|
rejections: Some(rejections()),
|
||||||
|
seeders: Some(Number::from(2)),
|
||||||
|
leechers: Some(Number::from(1)),
|
||||||
|
quality: quality_wrapper(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn usenet_release() -> LidarrRelease {
|
||||||
|
LidarrRelease {
|
||||||
|
guid: "1234".to_owned(),
|
||||||
|
protocol: "usenet".to_owned(),
|
||||||
|
age: 1,
|
||||||
|
title: HorizontallyScrollableText::from("Test Release"),
|
||||||
|
discography: false,
|
||||||
|
artist_name: Some("Alex".to_owned()),
|
||||||
|
album_title: Some("Something".to_owned()),
|
||||||
|
indexer: "DrunkenSlug".to_owned(),
|
||||||
|
indexer_id: 1,
|
||||||
|
size: 1234,
|
||||||
|
rejected: true,
|
||||||
|
rejections: Some(rejections()),
|
||||||
|
seeders: None,
|
||||||
|
leechers: None,
|
||||||
|
quality: quality_wrapper(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,6 +124,11 @@ mod tests {
|
|||||||
assert_str_eq!(event.resource(), "/rootfolder");
|
assert_str_eq!(event.resource(), "/rootfolder");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_resource_release(#[values(LidarrEvent::GetDiscographyReleases(0))] event: LidarrEvent) {
|
||||||
|
assert_str_eq!(event.resource(), "/release");
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(LidarrEvent::GetDiskSpace, "/diskspace")]
|
#[case(LidarrEvent::GetDiskSpace, "/diskspace")]
|
||||||
#[case(LidarrEvent::GetMetadataProfiles, "/metadataprofile")]
|
#[case(LidarrEvent::GetMetadataProfiles, "/metadataprofile")]
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ pub enum LidarrEvent {
|
|||||||
GetArtistHistory(i64),
|
GetArtistHistory(i64),
|
||||||
GetAllIndexerSettings,
|
GetAllIndexerSettings,
|
||||||
GetArtistDetails(i64),
|
GetArtistDetails(i64),
|
||||||
|
GetDiscographyReleases(i64),
|
||||||
GetDiskSpace,
|
GetDiskSpace,
|
||||||
GetDownloads(u64),
|
GetDownloads(u64),
|
||||||
GetHistory(u64),
|
GetHistory(u64),
|
||||||
@@ -96,6 +97,7 @@ impl NetworkResource for LidarrEvent {
|
|||||||
LidarrEvent::GetDownloads(_) | LidarrEvent::DeleteDownload(_) => "/queue",
|
LidarrEvent::GetDownloads(_) | LidarrEvent::DeleteDownload(_) => "/queue",
|
||||||
LidarrEvent::GetHistory(_) => "/history",
|
LidarrEvent::GetHistory(_) => "/history",
|
||||||
LidarrEvent::MarkHistoryItemAsFailed(_) => "/history/failed",
|
LidarrEvent::MarkHistoryItemAsFailed(_) => "/history/failed",
|
||||||
|
LidarrEvent::GetDiscographyReleases(_) => "/release",
|
||||||
LidarrEvent::GetHostConfig | LidarrEvent::GetSecurityConfig => "/config/host",
|
LidarrEvent::GetHostConfig | LidarrEvent::GetSecurityConfig => "/config/host",
|
||||||
LidarrEvent::GetIndexers | LidarrEvent::DeleteIndexer(_) | LidarrEvent::EditIndexer(_) => {
|
LidarrEvent::GetIndexers | LidarrEvent::DeleteIndexer(_) | LidarrEvent::EditIndexer(_) => {
|
||||||
"/indexer"
|
"/indexer"
|
||||||
@@ -184,6 +186,10 @@ impl Network<'_, '_> {
|
|||||||
.get_album_details(album_id)
|
.get_album_details(album_id)
|
||||||
.await
|
.await
|
||||||
.map(LidarrSerdeable::from),
|
.map(LidarrSerdeable::from),
|
||||||
|
LidarrEvent::GetDiscographyReleases(artist_id) => self
|
||||||
|
.get_artist_discography_releases(artist_id)
|
||||||
|
.await
|
||||||
|
.map(LidarrSerdeable::from),
|
||||||
LidarrEvent::GetDiskSpace => self.get_lidarr_diskspace().await.map(LidarrSerdeable::from),
|
LidarrEvent::GetDiskSpace => self.get_lidarr_diskspace().await.map(LidarrSerdeable::from),
|
||||||
LidarrEvent::GetDownloads(count) => self
|
LidarrEvent::GetDownloads(count) => self
|
||||||
.get_lidarr_downloads(count)
|
.get_lidarr_downloads(count)
|
||||||
|
|||||||
Reference in New Issue
Block a user