feat: Support for toggling the monitoring of a given artist via the CLI and TUI
This commit is contained in:
@@ -7,7 +7,11 @@ use crate::models::Route;
|
|||||||
#[path = "lidarr_context_clues_tests.rs"]
|
#[path = "lidarr_context_clues_tests.rs"]
|
||||||
mod lidarr_context_clues_tests;
|
mod lidarr_context_clues_tests;
|
||||||
|
|
||||||
pub static ARTISTS_CONTEXT_CLUES: [ContextClue; 6] = [
|
pub static ARTISTS_CONTEXT_CLUES: [ContextClue; 7] = [
|
||||||
|
(
|
||||||
|
DEFAULT_KEYBINDINGS.toggle_monitoring,
|
||||||
|
DEFAULT_KEYBINDINGS.toggle_monitoring.desc,
|
||||||
|
),
|
||||||
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||||
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
|
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
|
||||||
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
|
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
|
||||||
|
|||||||
@@ -13,6 +13,13 @@ mod tests {
|
|||||||
fn test_artists_context_clues() {
|
fn test_artists_context_clues() {
|
||||||
let mut artists_context_clues_iter = ARTISTS_CONTEXT_CLUES.iter();
|
let mut artists_context_clues_iter = ARTISTS_CONTEXT_CLUES.iter();
|
||||||
|
|
||||||
|
assert_some_eq_x!(
|
||||||
|
artists_context_clues_iter.next(),
|
||||||
|
&(
|
||||||
|
DEFAULT_KEYBINDINGS.toggle_monitoring,
|
||||||
|
DEFAULT_KEYBINDINGS.toggle_monitoring.desc
|
||||||
|
)
|
||||||
|
);
|
||||||
assert_some_eq_x!(
|
assert_some_eq_x!(
|
||||||
artists_context_clues_iter.next(),
|
artists_context_clues_iter.next(),
|
||||||
&(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc)
|
&(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc)
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ mod tests {
|
|||||||
|
|
||||||
mod cli {
|
mod cli {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use clap::error::ErrorKind;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list_artists_has_no_arg_requirements() {
|
fn test_list_artists_has_no_arg_requirements() {
|
||||||
@@ -40,6 +42,31 @@ mod tests {
|
|||||||
|
|
||||||
assert_err!(&result);
|
assert_err!(&result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_toggle_artist_monitoring_requires_artist_id() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "lidarr", "toggle-artist-monitoring"]);
|
||||||
|
|
||||||
|
assert_err!(&result);
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_toggle_artist_monitoring_requirements_satisfied() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"lidarr",
|
||||||
|
"toggle-artist-monitoring",
|
||||||
|
"--artist-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod handler {
|
mod handler {
|
||||||
@@ -119,5 +146,33 @@ mod tests {
|
|||||||
|
|
||||||
assert_ok!(&result);
|
assert_ok!(&result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_toggle_artist_monitoring_command() {
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
LidarrEvent::ToggleArtistMonitoring(1).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||||
|
let toggle_artist_monitoring_command = LidarrCommand::ToggleArtistMonitoring { artist_id: 1 };
|
||||||
|
|
||||||
|
let result = LidarrCliHandler::with(
|
||||||
|
&app_arc,
|
||||||
|
toggle_artist_monitoring_command,
|
||||||
|
&mut mock_network,
|
||||||
|
)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-1
@@ -1,11 +1,12 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Subcommand;
|
use clap::{Subcommand, arg};
|
||||||
use delete_command_handler::{LidarrDeleteCommand, LidarrDeleteCommandHandler};
|
use delete_command_handler::{LidarrDeleteCommand, LidarrDeleteCommandHandler};
|
||||||
use list_command_handler::{LidarrListCommand, LidarrListCommandHandler};
|
use list_command_handler::{LidarrListCommand, LidarrListCommandHandler};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
use crate::{app::App, network::NetworkTrait};
|
use crate::{app::App, network::NetworkTrait};
|
||||||
|
|
||||||
use super::{CliCommandHandler, Command};
|
use super::{CliCommandHandler, Command};
|
||||||
@@ -29,6 +30,17 @@ pub enum LidarrCommand {
|
|||||||
about = "Commands to list attributes from your Lidarr instance"
|
about = "Commands to list attributes from your Lidarr instance"
|
||||||
)]
|
)]
|
||||||
List(LidarrListCommand),
|
List(LidarrListCommand),
|
||||||
|
#[command(
|
||||||
|
about = "Toggle monitoring for the specified artist corresponding to the given artist ID"
|
||||||
|
)]
|
||||||
|
ToggleArtistMonitoring {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The Lidarr ID of the artist to toggle monitoring on",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
artist_id: i64,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LidarrCommand> for Command {
|
impl From<LidarrCommand> for Command {
|
||||||
@@ -68,6 +80,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrCommand> for LidarrCliHandler<'a, '
|
|||||||
.handle()
|
.handle()
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
|
LidarrCommand::ToggleArtistMonitoring { artist_id } => {
|
||||||
|
let resp = self
|
||||||
|
.network
|
||||||
|
.handle_network_event(LidarrEvent::ToggleArtistMonitoring(artist_id).into())
|
||||||
|
.await?;
|
||||||
|
serde_json::to_string_pretty(&resp)?
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
|||||||
@@ -6,10 +6,14 @@ mod tests {
|
|||||||
use serde_json::Number;
|
use serde_json::Number;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
|
use crate::assert_modal_absent;
|
||||||
use crate::handlers::KeyEventHandler;
|
use crate::handlers::KeyEventHandler;
|
||||||
use crate::handlers::lidarr_handlers::library::{LibraryHandler, artists_sorting_options};
|
use crate::handlers::lidarr_handlers::library::{LibraryHandler, artists_sorting_options};
|
||||||
use crate::models::lidarr_models::{Artist, ArtistStatistics, ArtistStatus};
|
use crate::models::lidarr_models::{Artist, ArtistStatistics, ArtistStatus};
|
||||||
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LIBRARY_BLOCKS};
|
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LIBRARY_BLOCKS};
|
||||||
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_library_handler_accepts() {
|
fn test_library_handler_accepts() {
|
||||||
@@ -214,6 +218,55 @@ mod tests {
|
|||||||
assert_str_eq!(sort_option.name, "Tags");
|
assert_str_eq!(sort_option.name, "Tags");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_toggle_monitoring_key() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.artists
|
||||||
|
.set_items(vec![Artist::default()]);
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
|
||||||
|
app.is_routing = false;
|
||||||
|
|
||||||
|
LibraryHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.toggle_monitoring.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::Artists,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Artists.into());
|
||||||
|
assert!(app.data.lidarr_data.prompt_confirm);
|
||||||
|
assert!(app.is_routing);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
&app.data.lidarr_data.prompt_confirm_action,
|
||||||
|
&LidarrEvent::ToggleArtistMonitoring(0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_toggle_monitoring_key_no_op_when_not_ready() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
|
||||||
|
app.is_routing = false;
|
||||||
|
|
||||||
|
LibraryHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.toggle_monitoring.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::Artists,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Artists.into());
|
||||||
|
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||||
|
assert_modal_absent!(app.data.lidarr_data.prompt_confirm_action);
|
||||||
|
assert!(!app.is_routing);
|
||||||
|
}
|
||||||
|
|
||||||
fn artists_vec() -> Vec<Artist> {
|
fn artists_vec() -> Vec<Artist> {
|
||||||
vec![
|
vec![
|
||||||
Artist {
|
Artist {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
stateful_table::SortOption,
|
stateful_table::SortOption,
|
||||||
},
|
},
|
||||||
|
network::lidarr_network::LidarrEvent,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::handle_change_tab_left_right_keys;
|
use super::handle_change_tab_left_right_keys;
|
||||||
@@ -31,6 +32,12 @@ pub(super) struct LibraryHandler<'a, 'b> {
|
|||||||
_context: Option<ActiveLidarrBlock>,
|
_context: Option<ActiveLidarrBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LibraryHandler<'_, '_> {
|
||||||
|
fn extract_artist_id(&self) -> i64 {
|
||||||
|
self.app.data.lidarr_data.artists.current_selection().id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LibraryHandler<'a, 'b> {
|
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LibraryHandler<'a, 'b> {
|
||||||
fn handle(&mut self) {
|
fn handle(&mut self) {
|
||||||
let artists_table_handling_config = TableHandlingConfig::new(ActiveLidarrBlock::Artists.into())
|
let artists_table_handling_config = TableHandlingConfig::new(ActiveLidarrBlock::Artists.into())
|
||||||
@@ -114,9 +121,24 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LibraryHandler<'a, '
|
|||||||
|
|
||||||
fn handle_char_key_event(&mut self) {
|
fn handle_char_key_event(&mut self) {
|
||||||
let key = self.key;
|
let key = self.key;
|
||||||
if self.active_lidarr_block == ActiveLidarrBlock::Artists && matches_key!(refresh, key) {
|
if self.active_lidarr_block == ActiveLidarrBlock::Artists {
|
||||||
|
match key {
|
||||||
|
_ if matches_key!(toggle_monitoring, key) => {
|
||||||
|
self.app.data.lidarr_data.prompt_confirm = true;
|
||||||
|
self.app.data.lidarr_data.prompt_confirm_action = Some(
|
||||||
|
LidarrEvent::ToggleArtistMonitoring(self.extract_artist_id()),
|
||||||
|
);
|
||||||
|
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.pop_and_push_navigation_stack(self.active_lidarr_block.into());
|
||||||
|
}
|
||||||
|
_ if matches_key!(refresh, key) => {
|
||||||
self.app.should_refresh = true;
|
self.app.should_refresh = true;
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app_mut(&mut self) -> &mut App<'b> {
|
fn app_mut(&mut self) -> &mut App<'b> {
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ use serde::{Deserialize, Serialize};
|
|||||||
use serde_json::{Number, Value};
|
use serde_json::{Number, Value};
|
||||||
use strum::{Display, EnumIter};
|
use strum::{Display, EnumIter};
|
||||||
|
|
||||||
use super::{HorizontallyScrollableText, Serdeable};
|
use super::{
|
||||||
|
HorizontallyScrollableText, Serdeable,
|
||||||
|
servarr_models::{DiskSpace, QualityProfile, RootFolder, Tag},
|
||||||
|
};
|
||||||
use crate::serde_enum_from;
|
use crate::serde_enum_from;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -29,6 +32,7 @@ pub struct Artist {
|
|||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
pub metadata_profile_id: i64,
|
pub metadata_profile_id: i64,
|
||||||
pub monitored: bool,
|
pub monitored: bool,
|
||||||
|
pub monitor_new_items: NewItemMonitorType,
|
||||||
pub genres: Vec<String>,
|
pub genres: Vec<String>,
|
||||||
pub tags: Vec<Number>,
|
pub tags: Vec<Number>,
|
||||||
pub added: DateTime<Utc>,
|
pub added: DateTime<Utc>,
|
||||||
@@ -94,6 +98,31 @@ impl From<(&i64, &String)> for MetadataProfile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Debug,
|
||||||
|
EnumIter,
|
||||||
|
Display,
|
||||||
|
EnumDisplayStyle,
|
||||||
|
)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[strum(serialize_all = "camelCase")]
|
||||||
|
pub enum NewItemMonitorType {
|
||||||
|
#[default]
|
||||||
|
#[display_style(name = "All Albums")]
|
||||||
|
All,
|
||||||
|
#[display_style(name = "No New Albums")]
|
||||||
|
None,
|
||||||
|
#[display_style(name = "New Albums")]
|
||||||
|
New,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
#[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DownloadRecord {
|
pub struct DownloadRecord {
|
||||||
@@ -174,14 +203,15 @@ impl From<LidarrSerdeable> for Serdeable {
|
|||||||
|
|
||||||
serde_enum_from!(
|
serde_enum_from!(
|
||||||
LidarrSerdeable {
|
LidarrSerdeable {
|
||||||
|
Artist(Artist),
|
||||||
Artists(Vec<Artist>),
|
Artists(Vec<Artist>),
|
||||||
DiskSpaces(Vec<super::servarr_models::DiskSpace>),
|
DiskSpaces(Vec<DiskSpace>),
|
||||||
DownloadsResponse(DownloadsResponse),
|
DownloadsResponse(DownloadsResponse),
|
||||||
MetadataProfiles(Vec<MetadataProfile>),
|
MetadataProfiles(Vec<MetadataProfile>),
|
||||||
QualityProfiles(Vec<super::servarr_models::QualityProfile>),
|
QualityProfiles(Vec<QualityProfile>),
|
||||||
RootFolders(Vec<super::servarr_models::RootFolder>),
|
RootFolders(Vec<RootFolder>),
|
||||||
SystemStatus(SystemStatus),
|
SystemStatus(SystemStatus),
|
||||||
Tags(Vec<super::servarr_models::Tag>),
|
Tags(Vec<Tag>),
|
||||||
Value(Value),
|
Value(Value),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use chrono::Utc;
|
||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
|
use crate::models::lidarr_models::{
|
||||||
|
DownloadRecord, DownloadStatus, DownloadsResponse, MetadataProfile, NewItemMonitorType,
|
||||||
|
SystemStatus,
|
||||||
|
};
|
||||||
|
use crate::models::servarr_models::{DiskSpace, QualityProfile, RootFolder, Tag};
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
Serdeable,
|
Serdeable,
|
||||||
lidarr_models::{Artist, ArtistStatistics, ArtistStatus, LidarrSerdeable, Ratings},
|
lidarr_models::{Artist, ArtistStatistics, ArtistStatus, LidarrSerdeable, Ratings},
|
||||||
@@ -13,6 +19,20 @@ mod tests {
|
|||||||
assert_eq!(ArtistStatus::default(), ArtistStatus::Continuing);
|
assert_eq!(ArtistStatus::default(), ArtistStatus::Continuing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_new_item_monitor_type_display() {
|
||||||
|
assert_str_eq!(NewItemMonitorType::All.to_string(), "all");
|
||||||
|
assert_str_eq!(NewItemMonitorType::None.to_string(), "none");
|
||||||
|
assert_str_eq!(NewItemMonitorType::New.to_string(), "new");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_new_item_monitor_type_to_display_str() {
|
||||||
|
assert_str_eq!(NewItemMonitorType::All.to_display_str(), "All Albums");
|
||||||
|
assert_str_eq!(NewItemMonitorType::None.to_display_str(), "No New Albums");
|
||||||
|
assert_str_eq!(NewItemMonitorType::New.to_display_str(), "New Albums");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lidarr_serdeable_from() {
|
fn test_lidarr_serdeable_from() {
|
||||||
let lidarr_serdeable = LidarrSerdeable::Value(json!({}));
|
let lidarr_serdeable = LidarrSerdeable::Value(json!({}));
|
||||||
@@ -65,6 +85,7 @@ mod tests {
|
|||||||
"qualityProfileId": 1,
|
"qualityProfileId": 1,
|
||||||
"metadataProfileId": 1,
|
"metadataProfileId": 1,
|
||||||
"monitored": true,
|
"monitored": true,
|
||||||
|
"monitorNewItems": "all",
|
||||||
"genres": ["Rock", "Alternative"],
|
"genres": ["Rock", "Alternative"],
|
||||||
"tags": [1, 2],
|
"tags": [1, 2],
|
||||||
"added": "2023-01-01T00:00:00Z",
|
"added": "2023-01-01T00:00:00Z",
|
||||||
@@ -95,6 +116,7 @@ mod tests {
|
|||||||
assert_eq!(artist.quality_profile_id, 1);
|
assert_eq!(artist.quality_profile_id, 1);
|
||||||
assert_eq!(artist.metadata_profile_id, 1);
|
assert_eq!(artist.metadata_profile_id, 1);
|
||||||
assert!(artist.monitored);
|
assert!(artist.monitored);
|
||||||
|
assert_eq!(artist.monitor_new_items, NewItemMonitorType::All);
|
||||||
assert_eq!(artist.genres, vec!["Rock", "Alternative"]);
|
assert_eq!(artist.genres, vec!["Rock", "Alternative"]);
|
||||||
assert_eq!(artist.tags.len(), 2);
|
assert_eq!(artist.tags.len(), 2);
|
||||||
assert_some!(&artist.ratings);
|
assert_some!(&artist.ratings);
|
||||||
@@ -184,6 +206,7 @@ mod tests {
|
|||||||
"qualityProfileId": 1,
|
"qualityProfileId": 1,
|
||||||
"metadataProfileId": 1,
|
"metadataProfileId": 1,
|
||||||
"monitored": false,
|
"monitored": false,
|
||||||
|
"monitorNewItems": "all",
|
||||||
"genres": [],
|
"genres": [],
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"added": "2023-01-01T00:00:00Z"
|
"added": "2023-01-01T00:00:00Z"
|
||||||
@@ -194,7 +217,169 @@ mod tests {
|
|||||||
assert_none!(&artist.overview);
|
assert_none!(&artist.overview);
|
||||||
assert_none!(&artist.artist_type);
|
assert_none!(&artist.artist_type);
|
||||||
assert_none!(&artist.disambiguation);
|
assert_none!(&artist.disambiguation);
|
||||||
|
assert_eq!(artist.monitor_new_items, NewItemMonitorType::All);
|
||||||
assert_none!(&artist.ratings);
|
assert_none!(&artist.ratings);
|
||||||
assert_none!(&artist.statistics);
|
assert_none!(&artist.statistics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_serdeable_from_artist() {
|
||||||
|
let artist = Artist {
|
||||||
|
id: 1,
|
||||||
|
..Artist::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let lidarr_serdeable: LidarrSerdeable = artist.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(lidarr_serdeable, LidarrSerdeable::Artist(artist));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_serdeable_from_disk_spaces() {
|
||||||
|
let disk_spaces = vec![DiskSpace {
|
||||||
|
free_space: 1,
|
||||||
|
total_space: 1,
|
||||||
|
}];
|
||||||
|
|
||||||
|
let lidarr_serdeable: LidarrSerdeable = disk_spaces.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(lidarr_serdeable, LidarrSerdeable::DiskSpaces(disk_spaces));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_serdeable_from_downloads_response() {
|
||||||
|
let downloads_response = DownloadsResponse {
|
||||||
|
records: vec![DownloadRecord {
|
||||||
|
id: 1,
|
||||||
|
..DownloadRecord::default()
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
let lidarr_serdeable: LidarrSerdeable = downloads_response.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lidarr_serdeable,
|
||||||
|
LidarrSerdeable::DownloadsResponse(downloads_response)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_serdeable_from_metadata_profiles() {
|
||||||
|
let metadata_profiles = vec![MetadataProfile {
|
||||||
|
id: 1,
|
||||||
|
name: "Standard".to_owned(),
|
||||||
|
}];
|
||||||
|
|
||||||
|
let lidarr_serdeable: LidarrSerdeable = metadata_profiles.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lidarr_serdeable,
|
||||||
|
LidarrSerdeable::MetadataProfiles(metadata_profiles)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_serdeable_from_quality_profiles() {
|
||||||
|
let quality_profiles = vec![QualityProfile {
|
||||||
|
id: 1,
|
||||||
|
name: "Any".to_owned(),
|
||||||
|
}];
|
||||||
|
|
||||||
|
let lidarr_serdeable: LidarrSerdeable = quality_profiles.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lidarr_serdeable,
|
||||||
|
LidarrSerdeable::QualityProfiles(quality_profiles)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_serdeable_from_root_folders() {
|
||||||
|
let root_folders = vec![RootFolder {
|
||||||
|
id: 1,
|
||||||
|
path: "/music".to_owned(),
|
||||||
|
accessible: true,
|
||||||
|
free_space: 1000000,
|
||||||
|
unmapped_folders: None,
|
||||||
|
}];
|
||||||
|
|
||||||
|
let lidarr_serdeable: LidarrSerdeable = root_folders.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(lidarr_serdeable, LidarrSerdeable::RootFolders(root_folders));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_serdeable_from_system_status() {
|
||||||
|
let system_status = SystemStatus {
|
||||||
|
version: "1.0.0".to_owned(),
|
||||||
|
start_time: Utc::now(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let lidarr_serdeable: LidarrSerdeable = system_status.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lidarr_serdeable,
|
||||||
|
LidarrSerdeable::SystemStatus(system_status)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_serdeable_from_tags() {
|
||||||
|
let tags = vec![Tag {
|
||||||
|
id: 1,
|
||||||
|
label: "rock".to_owned(),
|
||||||
|
}];
|
||||||
|
|
||||||
|
let lidarr_serdeable: LidarrSerdeable = tags.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(lidarr_serdeable, LidarrSerdeable::Tags(tags));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_artist_status_display() {
|
||||||
|
assert_str_eq!(ArtistStatus::Continuing.to_string(), "continuing");
|
||||||
|
assert_str_eq!(ArtistStatus::Ended.to_string(), "ended");
|
||||||
|
assert_str_eq!(ArtistStatus::Deleted.to_string(), "deleted");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_artist_status_to_display_str() {
|
||||||
|
assert_str_eq!(ArtistStatus::Continuing.to_display_str(), "Continuing");
|
||||||
|
assert_str_eq!(ArtistStatus::Ended.to_display_str(), "Ended");
|
||||||
|
assert_str_eq!(ArtistStatus::Deleted.to_display_str(), "Deleted");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_download_status_display() {
|
||||||
|
assert_str_eq!(DownloadStatus::Unknown.to_string(), "unknown");
|
||||||
|
assert_str_eq!(DownloadStatus::Queued.to_string(), "queued");
|
||||||
|
assert_str_eq!(DownloadStatus::Paused.to_string(), "paused");
|
||||||
|
assert_str_eq!(DownloadStatus::Downloading.to_string(), "downloading");
|
||||||
|
assert_str_eq!(DownloadStatus::Completed.to_string(), "completed");
|
||||||
|
assert_str_eq!(DownloadStatus::Failed.to_string(), "failed");
|
||||||
|
assert_str_eq!(DownloadStatus::Warning.to_string(), "warning");
|
||||||
|
assert_str_eq!(DownloadStatus::Delay.to_string(), "delay");
|
||||||
|
assert_str_eq!(
|
||||||
|
DownloadStatus::DownloadClientUnavailable.to_string(),
|
||||||
|
"downloadClientUnavailable"
|
||||||
|
);
|
||||||
|
assert_str_eq!(DownloadStatus::Fallback.to_string(), "fallback");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_download_status_to_display_str() {
|
||||||
|
assert_str_eq!(DownloadStatus::Unknown.to_display_str(), "Unknown");
|
||||||
|
assert_str_eq!(DownloadStatus::Queued.to_display_str(), "Queued");
|
||||||
|
assert_str_eq!(DownloadStatus::Paused.to_display_str(), "Paused");
|
||||||
|
assert_str_eq!(DownloadStatus::Downloading.to_display_str(), "Downloading");
|
||||||
|
assert_str_eq!(DownloadStatus::Completed.to_display_str(), "Completed");
|
||||||
|
assert_str_eq!(DownloadStatus::Failed.to_display_str(), "Failed");
|
||||||
|
assert_str_eq!(DownloadStatus::Warning.to_display_str(), "Warning");
|
||||||
|
assert_str_eq!(DownloadStatus::Delay.to_display_str(), "Delay");
|
||||||
|
assert_str_eq!(
|
||||||
|
DownloadStatus::DownloadClientUnavailable.to_display_str(),
|
||||||
|
"Download Client Unavailable"
|
||||||
|
);
|
||||||
|
assert_str_eq!(DownloadStatus::Fallback.to_display_str(), "Fallback");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ mod tests {
|
|||||||
use crate::models::lidarr_models::{Artist, DeleteArtistParams, LidarrSerdeable};
|
use crate::models::lidarr_models::{Artist, DeleteArtistParams, LidarrSerdeable};
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
|
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
|
||||||
|
use mockito::Matcher;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ mod tests {
|
|||||||
"qualityProfileId": 1,
|
"qualityProfileId": 1,
|
||||||
"metadataProfileId": 1,
|
"metadataProfileId": 1,
|
||||||
"monitored": true,
|
"monitored": true,
|
||||||
|
"monitorNewItems": "all",
|
||||||
"genres": [],
|
"genres": [],
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"added": "2023-01-01T00:00:00Z"
|
"added": "2023-01-01T00:00:00Z"
|
||||||
@@ -66,4 +68,88 @@ mod tests {
|
|||||||
|
|
||||||
async_server.assert_async().await;
|
async_server.assert_async().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_artist_details_event() {
|
||||||
|
let artist_json = json!({
|
||||||
|
"id": 1,
|
||||||
|
"mbId": "test-mb-id",
|
||||||
|
"artistName": "Test Artist",
|
||||||
|
"foreignArtistId": "test-foreign-id",
|
||||||
|
"status": "continuing",
|
||||||
|
"path": "/music/test-artist",
|
||||||
|
"qualityProfileId": 1,
|
||||||
|
"metadataProfileId": 1,
|
||||||
|
"monitored": true,
|
||||||
|
"monitorNewItems": "all",
|
||||||
|
"genres": [],
|
||||||
|
"tags": [],
|
||||||
|
"added": "2023-01-01T00:00:00Z"
|
||||||
|
});
|
||||||
|
let response: Artist = serde_json::from_value(artist_json.clone()).unwrap();
|
||||||
|
let (mock, app, _server) = MockServarrApi::get()
|
||||||
|
.returns(artist_json)
|
||||||
|
.path("/1")
|
||||||
|
.build_for(LidarrEvent::GetArtistDetails(1))
|
||||||
|
.await;
|
||||||
|
app.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
let result = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetArtistDetails(1))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
mock.assert_async().await;
|
||||||
|
|
||||||
|
let LidarrSerdeable::Artist(artist) = result.unwrap() else {
|
||||||
|
panic!("Expected Artist");
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(artist, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_toggle_artist_monitoring_event() {
|
||||||
|
let artist_json = json!({
|
||||||
|
"id": 1,
|
||||||
|
"mbId": "test-mb-id",
|
||||||
|
"artistName": "Test Artist",
|
||||||
|
"foreignArtistId": "test-foreign-id",
|
||||||
|
"status": "continuing",
|
||||||
|
"path": "/music/test-artist",
|
||||||
|
"qualityProfileId": 1,
|
||||||
|
"metadataProfileId": 1,
|
||||||
|
"monitored": true,
|
||||||
|
"monitorNewItems": "all",
|
||||||
|
"genres": [],
|
||||||
|
"tags": [],
|
||||||
|
"added": "2023-01-01T00:00:00Z"
|
||||||
|
});
|
||||||
|
let mut expected_body = artist_json.clone();
|
||||||
|
*expected_body.get_mut("monitored").unwrap() = json!(false);
|
||||||
|
let (get_mock, app, mut server) = MockServarrApi::get()
|
||||||
|
.returns(artist_json)
|
||||||
|
.path("/1")
|
||||||
|
.build_for(LidarrEvent::GetArtistDetails(1))
|
||||||
|
.await;
|
||||||
|
let put_mock = server
|
||||||
|
.mock("PUT", "/api/v1/artist/1")
|
||||||
|
.match_body(Matcher::Json(expected_body))
|
||||||
|
.match_header("X-Api-Key", "test1234")
|
||||||
|
.with_status(202)
|
||||||
|
.create_async()
|
||||||
|
.await;
|
||||||
|
app.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
network
|
||||||
|
.handle_lidarr_event(LidarrEvent::ToggleArtistMonitoring(1))
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
|
||||||
|
get_mock.assert_async().await;
|
||||||
|
put_mock.assert_async().await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use log::info;
|
use log::{debug, info, warn};
|
||||||
|
use serde_json::{Value, json};
|
||||||
|
|
||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
use crate::models::lidarr_models::{Artist, DeleteArtistParams};
|
use crate::models::lidarr_models::{Artist, DeleteArtistParams};
|
||||||
@@ -65,4 +66,89 @@ impl Network<'_, '_> {
|
|||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(in crate::network::lidarr_network) async fn get_artist_details(
|
||||||
|
&mut self,
|
||||||
|
artist_id: i64,
|
||||||
|
) -> Result<Artist> {
|
||||||
|
info!("Fetching details for Lidarr artist with ID: {artist_id}");
|
||||||
|
let event = LidarrEvent::GetArtistDetails(artist_id);
|
||||||
|
|
||||||
|
let request_props = self
|
||||||
|
.request_props_from(
|
||||||
|
event,
|
||||||
|
RequestMethod::Get,
|
||||||
|
None::<()>,
|
||||||
|
Some(format!("/{artist_id}")),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<(), Artist>(request_props, |_, _| ())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::network::lidarr_network) async fn toggle_artist_monitoring(
|
||||||
|
&mut self,
|
||||||
|
artist_id: i64,
|
||||||
|
) -> Result<()> {
|
||||||
|
let event = LidarrEvent::ToggleArtistMonitoring(artist_id);
|
||||||
|
|
||||||
|
let detail_event = LidarrEvent::GetArtistDetails(artist_id);
|
||||||
|
info!("Toggling artist monitoring for artist with ID: {artist_id}");
|
||||||
|
info!("Fetching artist details for artist with ID: {artist_id}");
|
||||||
|
|
||||||
|
let request_props = self
|
||||||
|
.request_props_from(
|
||||||
|
detail_event,
|
||||||
|
RequestMethod::Get,
|
||||||
|
None::<()>,
|
||||||
|
Some(format!("/{artist_id}")),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let mut response = String::new();
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<(), Value>(request_props, |detailed_artist_body, _| {
|
||||||
|
response = detailed_artist_body.to_string()
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
info!("Constructing toggle artist monitoring body");
|
||||||
|
|
||||||
|
match serde_json::from_str::<Value>(&response) {
|
||||||
|
Ok(mut detailed_artist_body) => {
|
||||||
|
let monitored = detailed_artist_body
|
||||||
|
.get("monitored")
|
||||||
|
.unwrap()
|
||||||
|
.as_bool()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
*detailed_artist_body.get_mut("monitored").unwrap() = json!(!monitored);
|
||||||
|
|
||||||
|
debug!("Toggle artist monitoring body: {detailed_artist_body:?}");
|
||||||
|
|
||||||
|
let request_props = self
|
||||||
|
.request_props_from(
|
||||||
|
event,
|
||||||
|
RequestMethod::Put,
|
||||||
|
Some(detailed_artist_body),
|
||||||
|
Some(format!("/{artist_id}")),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<Value, ()>(request_props, |_, _| ())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
warn!("Request for detailed artist body was interrupted");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,18 @@ mod tests {
|
|||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_resource_artist(
|
||||||
|
#[values(
|
||||||
|
LidarrEvent::GetArtistDetails(0),
|
||||||
|
LidarrEvent::ListArtists,
|
||||||
|
LidarrEvent::ToggleArtistMonitoring(0)
|
||||||
|
)]
|
||||||
|
event: LidarrEvent,
|
||||||
|
) {
|
||||||
|
assert_str_eq!(event.resource(), "/artist");
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(LidarrEvent::GetDiskSpace, "/diskspace")]
|
#[case(LidarrEvent::GetDiskSpace, "/diskspace")]
|
||||||
#[case(LidarrEvent::GetDownloads(500), "/queue")]
|
#[case(LidarrEvent::GetDownloads(500), "/queue")]
|
||||||
@@ -17,7 +29,6 @@ mod tests {
|
|||||||
#[case(LidarrEvent::GetStatus, "/system/status")]
|
#[case(LidarrEvent::GetStatus, "/system/status")]
|
||||||
#[case(LidarrEvent::GetTags, "/tag")]
|
#[case(LidarrEvent::GetTags, "/tag")]
|
||||||
#[case(LidarrEvent::HealthCheck, "/health")]
|
#[case(LidarrEvent::HealthCheck, "/health")]
|
||||||
#[case(LidarrEvent::ListArtists, "/artist")]
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ mod lidarr_network_tests;
|
|||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
pub enum LidarrEvent {
|
pub enum LidarrEvent {
|
||||||
DeleteArtist(DeleteArtistParams),
|
DeleteArtist(DeleteArtistParams),
|
||||||
|
GetArtistDetails(i64),
|
||||||
GetDiskSpace,
|
GetDiskSpace,
|
||||||
GetDownloads(u64),
|
GetDownloads(u64),
|
||||||
GetMetadataProfiles,
|
GetMetadataProfiles,
|
||||||
@@ -27,12 +28,16 @@ pub enum LidarrEvent {
|
|||||||
GetTags,
|
GetTags,
|
||||||
HealthCheck,
|
HealthCheck,
|
||||||
ListArtists,
|
ListArtists,
|
||||||
|
ToggleArtistMonitoring(i64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkResource for LidarrEvent {
|
impl NetworkResource for LidarrEvent {
|
||||||
fn resource(&self) -> &'static str {
|
fn resource(&self) -> &'static str {
|
||||||
match &self {
|
match &self {
|
||||||
LidarrEvent::DeleteArtist(_) | LidarrEvent::ListArtists => "/artist",
|
LidarrEvent::DeleteArtist(_)
|
||||||
|
| LidarrEvent::GetArtistDetails(_)
|
||||||
|
| LidarrEvent::ListArtists
|
||||||
|
| LidarrEvent::ToggleArtistMonitoring(_) => "/artist",
|
||||||
LidarrEvent::GetDiskSpace => "/diskspace",
|
LidarrEvent::GetDiskSpace => "/diskspace",
|
||||||
LidarrEvent::GetDownloads(_) => "/queue",
|
LidarrEvent::GetDownloads(_) => "/queue",
|
||||||
LidarrEvent::GetMetadataProfiles => "/metadataprofile",
|
LidarrEvent::GetMetadataProfiles => "/metadataprofile",
|
||||||
@@ -60,6 +65,10 @@ impl Network<'_, '_> {
|
|||||||
LidarrEvent::DeleteArtist(params) => {
|
LidarrEvent::DeleteArtist(params) => {
|
||||||
self.delete_artist(params).await.map(LidarrSerdeable::from)
|
self.delete_artist(params).await.map(LidarrSerdeable::from)
|
||||||
}
|
}
|
||||||
|
LidarrEvent::GetArtistDetails(artist_id) => self
|
||||||
|
.get_artist_details(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)
|
||||||
@@ -84,6 +93,10 @@ impl Network<'_, '_> {
|
|||||||
.await
|
.await
|
||||||
.map(LidarrSerdeable::from),
|
.map(LidarrSerdeable::from),
|
||||||
LidarrEvent::ListArtists => self.list_artists().await.map(LidarrSerdeable::from),
|
LidarrEvent::ListArtists => self.list_artists().await.map(LidarrSerdeable::from),
|
||||||
|
LidarrEvent::ToggleArtistMonitoring(artist_id) => self
|
||||||
|
.toggle_artist_monitoring(artist_id)
|
||||||
|
.await
|
||||||
|
.map(LidarrSerdeable::from),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user