Compare commits

...

3 Commits

11 changed files with 334 additions and 30 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
# Adapted from https://github.com/joshka/github-workflows/blob/main/.github/workflows/rust-release-plz.yml # Adapted from https://github.com/joshka/github-workflows/blob/main/.github/workflows/rust-release-plz.yml
# Thanks to joshka for permission to use this template! # Thanks to joshka for permission to use this template!
name: Create Release PR and publish to crates.io name: Create Release PR and Publish Release
permissions: permissions:
pull-requests: write pull-requests: write
+7
View File
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.1.4](https://github.com/Dark-Alex-17/managarr/compare/v0.1.3...v0.1.4) - 2024-11-01
### Other
- Added the ability to fetch host configs and security configs to the CLI
- Updated README to be more clear about what features are supported [skip ci]
## [0.1.2](https://github.com/Dark-Alex-17/managarr/compare/v0.1.1...v0.1.2) - 2024-10-30 ## [0.1.2](https://github.com/Dark-Alex-17/managarr/compare/v0.1.1...v0.1.2) - 2024-10-30
### Other ### Other
Generated
+1 -1
View File
@@ -1148,7 +1148,7 @@ dependencies = [
[[package]] [[package]]
name = "managarr" name = "managarr"
version = "0.1.3" version = "0.1.4"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"assert_cmd", "assert_cmd",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "managarr" name = "managarr"
version = "0.1.3" version = "0.1.4"
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"] authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
description = "A TUI and CLI to manage your Servarrs" description = "A TUI and CLI to manage your Servarrs"
keywords = ["managarr", "tui-rs", "dashboard", "servarr", "tui"] keywords = ["managarr", "tui-rs", "dashboard", "servarr", "tui"]
+22 -16
View File
@@ -17,14 +17,14 @@ Managarr is a TUI and CLI to help you manage your HTPC (Home Theater PC). Built
## What Servarrs are supported? ## What Servarrs are supported?
- ![radarr_logo](logos/radarr.png) [Radarr](https://wiki.servarr.com/radarr) - [x] ![radarr_logo](logos/radarr.png) [Radarr](https://wiki.servarr.com/radarr)
- ![sonarr_logo](logos/sonarr.png) [Sonarr](https://wiki.servarr.com/en/sonarr) - [ ] ![sonarr_logo](logos/sonarr.png) [Sonarr](https://wiki.servarr.com/en/sonarr)
- ![readarr_logo](logos/readarr.png) [Readarr](https://wiki.servarr.com/en/readarr) - [ ] ![readarr_logo](logos/readarr.png) [Readarr](https://wiki.servarr.com/en/readarr)
- ![lidarr_logo](logos/lidarr.png) [Lidarr](https://wiki.servarr.com/en/lidarr) - [ ] ![lidarr_logo](logos/lidarr.png) [Lidarr](https://wiki.servarr.com/en/lidarr)
- ![prowlarr_logo](logos/prowlarr.png) [Prowlarr](https://wiki.servarr.com/en/prowlarr) - [ ] ![prowlarr_logo](logos/prowlarr.png) [Prowlarr](https://wiki.servarr.com/en/prowlarr)
- ![whisparr_logo](logos/whisparr.png) [Whisparr](https://wiki.servarr.com/whisparr) - [ ] ![whisparr_logo](logos/whisparr.png) [Whisparr](https://wiki.servarr.com/whisparr)
- ![bazarr_logo](logos/bazarr.png) [Bazarr](https://www.bazarr.media/) - [ ] ![bazarr_logo](logos/bazarr.png) [Bazarr](https://www.bazarr.media/)
- ![tautulli_logo](logos/tautulli.png) [Tautulli](https://tautulli.com/) - [ ] ![tautulli_logo](logos/tautulli.png) [Tautulli](https://tautulli.com/)
## Try Before You Buy ## Try Before You Buy
To try out Managarr before linking it to your HTPC, you can use the purpose built [managarr-demo](https://github.com/Dark-Alex-17/managarr-demo) repository. To try out Managarr before linking it to your HTPC, you can use the purpose built [managarr-demo](https://github.com/Dark-Alex-17/managarr-demo) repository.
@@ -60,6 +60,7 @@ You can also clone this repo and run `make docker` to build a docker image local
- [x] View your library, downloads, collections, and blocklist - [x] View your library, downloads, collections, and blocklist
- [x] View details of a specific movie including description, history, downloaded file info, or the credits - [x] View details of a specific movie including description, history, downloaded file info, or the credits
- [x] View details of any collection and the movies in them - [x] View details of any collection and the movies in them
- [x] View your host and security configs from the CLI to programmatically fetch the API token, among other settings
- [x] Search your library or collections - [x] Search your library or collections
- [x] Add movies to your library - [x] Add movies to your library
- [x] Delete movies, downloads, and indexers - [x] Delete movies, downloads, and indexers
@@ -103,7 +104,8 @@ You can also clone this repo and run `make docker` to build a docker image local
### The Managarr CLI ### The Managarr CLI
Managarr can be used in one of two ways: As a TUI, or as a CLI for managing your Servarrs. Managarr can be used in one of two ways: As a TUI, or as a CLI for managing your Servarrs.
All management features available in the TUI are also available in the CLI. All management features available in the TUI are also available in the CLI. However, the CLI is
equipped with additional features to allow for more advanced usage and automation.
The CLI can be helpful for automating tasks or for use in scripts. For example, you can use the CLI to trigger a search for a movie, or to add a movie to your library. The CLI can be helpful for automating tasks or for use in scripts. For example, you can use the CLI to trigger a search for a movie, or to add a movie to your library.
@@ -111,12 +113,12 @@ To see all available commands, simply run `managarr --help`:
```shell ```shell
$ managarr --help $ managarr --help
managarr 0.0.36 managarr 0.1.3
Alex Clarke <alex.j.tusa@gmail.com> Alex Clarke <alex.j.tusa@gmail.com>
A TUI and CLI to manage your Servarrs A TUI and CLI to manage your Servarrs
Usage: managarr [COMMAND] Usage: managarr [OPTIONS] [COMMAND]
Commands: Commands:
radarr Commands for manging your Radarr instance radarr Commands for manging your Radarr instance
@@ -124,8 +126,10 @@ Commands:
help Print this message or the help of the given subcommand(s) help Print this message or the help of the given subcommand(s)
Options: Options:
-h, --help Print help --config <CONFIG> The Managarr configuration file to use
-V, --version Print version --disable-terminal-size-checks Disable the terminal size checks
-h, --help Print help
-V, --version Print version
``` ```
All subcommands also have detailed help menus to show you how to use them. For example, to see all available commands for Radarr, you would run: All subcommands also have detailed help menus to show you how to use them. For example, to see all available commands for Radarr, you would run:
@@ -134,7 +138,7 @@ All subcommands also have detailed help menus to show you how to use them. For e
$ managarr radarr --help $ managarr radarr --help
Commands for manging your Radarr instance Commands for manging your Radarr instance
Usage: managarr radarr <COMMAND> Usage: managarr radarr [OPTIONS] <COMMAND>
Commands: Commands:
add Commands to add or create new resources within your Radarr instance add Commands to add or create new resources within your Radarr instance
@@ -154,7 +158,9 @@ Commands:
help Print this message or the help of the given subcommand(s) help Print this message or the help of the given subcommand(s)
Options: Options:
-h, --help Print help --config <CONFIG> The Managarr configuration file to use
--disable-terminal-size-checks Disable the terminal size checks
-h, --help Print help
``` ```
**Pro Tip:** The CLI is even more powerful and useful when used in conjunction with the `jq` CLI tool. This allows you to parse the JSON response from the Managarr CLI and use it in your scripts; For example, to extract the `movieId` of the movie "Ad Astra", you would run: **Pro Tip:** The CLI is even more powerful and useful when used in conjunction with the `jq` CLI tool. This allows you to parse the JSON response from the Managarr CLI and use it in your scripts; For example, to extract the `movieId` of the movie "Ad Astra", you would run:
@@ -185,7 +191,7 @@ $HOME/Library/Application Support/managarr/config.yml
%APPDATA%/Roaming/managarr/config.yml %APPDATA%/Roaming/managarr/config.yml
``` ```
## Specify Configuration File ## Specify Which Configuration File to Use
It can sometimes be useful to specify the configuration file you wish to use. This is useful in cases It can sometimes be useful to specify the configuration file you wish to use. This is useful in cases
where you may have more than one instance of a given Servarr running. Thus, you can specify the where you may have more than one instance of a given Servarr running. Thus, you can specify the
config file using the `--config` flag: config file using the `--config` flag:
+10
View File
@@ -21,6 +21,8 @@ mod get_command_handler_tests;
pub enum RadarrGetCommand { pub enum RadarrGetCommand {
#[command(about = "Get the shared settings for all indexers")] #[command(about = "Get the shared settings for all indexers")]
AllIndexerSettings, AllIndexerSettings,
#[command(about = "Fetch the host config for your Radarr instance")]
HostConfig,
#[command(about = "Get detailed information for the movie with the given ID")] #[command(about = "Get detailed information for the movie with the given ID")]
MovieDetails { MovieDetails {
#[arg( #[arg(
@@ -39,6 +41,8 @@ pub enum RadarrGetCommand {
)] )]
movie_id: i64, movie_id: i64,
}, },
#[command(about = "Fetch the security config for your Radarr instance")]
SecurityConfig,
#[command(about = "Get the system status")] #[command(about = "Get the system status")]
SystemStatus, SystemStatus,
} }
@@ -73,12 +77,18 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrGetCommand> for RadarrGetCommandHan
RadarrGetCommand::AllIndexerSettings => { RadarrGetCommand::AllIndexerSettings => {
execute_network_event!(self, RadarrEvent::GetAllIndexerSettings); execute_network_event!(self, RadarrEvent::GetAllIndexerSettings);
} }
RadarrGetCommand::HostConfig => {
execute_network_event!(self, RadarrEvent::GetHostConfig);
}
RadarrGetCommand::MovieDetails { movie_id } => { RadarrGetCommand::MovieDetails { movie_id } => {
execute_network_event!(self, RadarrEvent::GetMovieDetails(Some(movie_id))); execute_network_event!(self, RadarrEvent::GetMovieDetails(Some(movie_id)));
} }
RadarrGetCommand::MovieHistory { movie_id } => { RadarrGetCommand::MovieHistory { movie_id } => {
execute_network_event!(self, RadarrEvent::GetMovieHistory(Some(movie_id))); execute_network_event!(self, RadarrEvent::GetMovieHistory(Some(movie_id)));
} }
RadarrGetCommand::SecurityConfig => {
execute_network_event!(self, RadarrEvent::GetSecurityConfig);
}
RadarrGetCommand::SystemStatus => { RadarrGetCommand::SystemStatus => {
execute_network_event!(self, RadarrEvent::GetStatus); execute_network_event!(self, RadarrEvent::GetStatus);
} }
@@ -29,6 +29,14 @@ mod test {
assert!(result.is_ok()); assert!(result.is_ok());
} }
#[test]
fn test_get_host_config_has_no_arg_requirements() {
let result =
Cli::command().try_get_matches_from(["managarr", "radarr", "get", "host-config"]);
assert!(result.is_ok());
}
#[test] #[test]
fn test_movie_details_requires_movie_id() { fn test_movie_details_requires_movie_id() {
let result = let result =
@@ -81,6 +89,14 @@ mod test {
assert!(result.is_ok()); assert!(result.is_ok());
} }
#[test]
fn test_get_security_config_has_no_arg_requirements() {
let result =
Cli::command().try_get_matches_from(["managarr", "radarr", "get", "security-config"]);
assert!(result.is_ok());
}
#[test] #[test]
fn test_system_status_has_no_arg_requirements() { fn test_system_status_has_no_arg_requirements() {
let result = let result =
@@ -135,6 +151,29 @@ mod test {
assert!(result.is_ok()); assert!(result.is_ok());
} }
#[tokio::test]
async fn test_handle_get_host_config_command() {
let mut mock_network = MockNetworkTrait::new();
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(RadarrEvent::GetHostConfig.into()))
.times(1)
.returning(|_| {
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
json!({"testResponse": "response"}),
)))
});
let app_arc = Arc::new(Mutex::new(App::default()));
let get_host_config_command = RadarrGetCommand::HostConfig;
let result =
RadarrGetCommandHandler::with(&app_arc, get_host_config_command, &mut mock_network)
.handle()
.await;
assert!(result.is_ok());
}
#[tokio::test] #[tokio::test]
async fn test_handle_get_movie_details_command() { async fn test_handle_get_movie_details_command() {
let expected_movie_id = 1; let expected_movie_id = 1;
@@ -187,6 +226,29 @@ mod test {
assert!(result.is_ok()); assert!(result.is_ok());
} }
#[tokio::test]
async fn test_handle_get_security_config_command() {
let mut mock_network = MockNetworkTrait::new();
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(RadarrEvent::GetSecurityConfig.into()))
.times(1)
.returning(|_| {
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
json!({"testResponse": "response"}),
)))
});
let app_arc = Arc::new(Mutex::new(App::default()));
let get_security_config_command = RadarrGetCommand::SecurityConfig;
let result =
RadarrGetCommandHandler::with(&app_arc, get_security_config_command, &mut mock_network)
.handle()
.await;
assert!(result.is_ok());
}
#[tokio::test] #[tokio::test]
async fn test_handle_get_system_status_command() { async fn test_handle_get_system_status_command() {
let mut mock_network = MockNetworkTrait::new(); let mut mock_network = MockNetworkTrait::new();
+85
View File
@@ -57,6 +57,44 @@ pub struct AddRootFolderBody {
pub path: String, pub path: String,
} }
#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Clone, Copy, Debug, ValueEnum)]
#[serde(rename_all = "camelCase")]
pub enum AuthenticationMethod {
#[default]
Basic,
Forms,
None,
}
impl Display for AuthenticationMethod {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let authentication_method = match self {
AuthenticationMethod::Basic => "basic",
AuthenticationMethod::Forms => "forms",
AuthenticationMethod::None => "none",
};
write!(f, "{authentication_method}")
}
}
#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Clone, Copy, Debug, ValueEnum)]
#[serde(rename_all = "camelCase")]
pub enum AuthenticationRequired {
Enabled,
#[default]
DisabledForLocalAddresses,
}
impl Display for AuthenticationRequired {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let authentication_required = match self {
AuthenticationRequired::Enabled => "enabled",
AuthenticationRequired::DisabledForLocalAddresses => "disabledForLocalAddresses",
};
write!(f, "{authentication_required}")
}
}
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct BlocklistResponse { pub struct BlocklistResponse {
pub records: Vec<BlocklistItem>, pub records: Vec<BlocklistItem>,
@@ -85,6 +123,26 @@ pub struct BlocklistItemMovie {
pub title: HorizontallyScrollableText, pub title: HorizontallyScrollableText,
} }
#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Clone, Copy, Debug, ValueEnum)]
#[serde(rename_all = "camelCase")]
pub enum CertificateValidation {
#[default]
Enabled,
DisabledForLocalAddresses,
Disabled,
}
impl Display for CertificateValidation {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let certificate_validation = match self {
CertificateValidation::Enabled => "enabled",
CertificateValidation::DisabledForLocalAddresses => "disabledForLocalAddresses",
CertificateValidation::Disabled => "disabled",
};
write!(f, "{certificate_validation}")
}
}
#[derive(Serialize, Deserialize, Derivative, Default, Clone, Debug, PartialEq, Eq)] #[derive(Serialize, Deserialize, Derivative, Default, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Collection { pub struct Collection {
@@ -223,6 +281,18 @@ pub struct EditMovieParams {
pub clear_tags: bool, pub clear_tags: bool,
} }
#[derive(Default, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct HostConfig {
pub bind_address: HorizontallyScrollableText,
#[serde(deserialize_with = "super::from_i64")]
pub port: i64,
pub url_base: Option<HorizontallyScrollableText>,
pub instance_name: Option<HorizontallyScrollableText>,
pub application_url: Option<HorizontallyScrollableText>,
pub enable_ssl: bool,
}
#[derive(Default, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)] #[derive(Default, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Indexer { pub struct Indexer {
@@ -560,6 +630,17 @@ pub struct RootFolder {
pub unmapped_folders: Option<Vec<UnmappedFolder>>, pub unmapped_folders: Option<Vec<UnmappedFolder>>,
} }
#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct SecurityConfig {
pub authentication_method: AuthenticationMethod,
pub authentication_required: AuthenticationRequired,
pub username: String,
pub password: Option<String>,
pub api_key: String,
pub certificate_validation: CertificateValidation,
}
#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct SystemStatus { pub struct SystemStatus {
@@ -647,6 +728,7 @@ pub enum RadarrSerdeable {
Credits(Vec<Credit>), Credits(Vec<Credit>),
DiskSpaces(Vec<DiskSpace>), DiskSpaces(Vec<DiskSpace>),
DownloadsResponse(DownloadsResponse), DownloadsResponse(DownloadsResponse),
HostConfig(HostConfig),
Indexers(Vec<Indexer>), Indexers(Vec<Indexer>),
IndexerSettings(IndexerSettings), IndexerSettings(IndexerSettings),
LogResponse(LogResponse), LogResponse(LogResponse),
@@ -657,6 +739,7 @@ pub enum RadarrSerdeable {
QueueEvents(Vec<QueueEvent>), QueueEvents(Vec<QueueEvent>),
Releases(Vec<Release>), Releases(Vec<Release>),
RootFolders(Vec<RootFolder>), RootFolders(Vec<RootFolder>),
SecurityConfig(SecurityConfig),
SystemStatus(SystemStatus), SystemStatus(SystemStatus),
Tags(Vec<Tag>), Tags(Vec<Tag>),
Tasks(Vec<Task>), Tasks(Vec<Task>),
@@ -686,6 +769,7 @@ serde_enum_from!(
Credits(Vec<Credit>), Credits(Vec<Credit>),
DiskSpaces(Vec<DiskSpace>), DiskSpaces(Vec<DiskSpace>),
DownloadsResponse(DownloadsResponse), DownloadsResponse(DownloadsResponse),
HostConfig(HostConfig),
Indexers(Vec<Indexer>), Indexers(Vec<Indexer>),
IndexerSettings(IndexerSettings), IndexerSettings(IndexerSettings),
LogResponse(LogResponse), LogResponse(LogResponse),
@@ -696,6 +780,7 @@ serde_enum_from!(
QueueEvents(Vec<QueueEvent>), QueueEvents(Vec<QueueEvent>),
Releases(Vec<Release>), Releases(Vec<Release>),
RootFolders(Vec<RootFolder>), RootFolders(Vec<RootFolder>),
SecurityConfig(SecurityConfig),
SystemStatus(SystemStatus), SystemStatus(SystemStatus),
Tags(Vec<Tag>), Tags(Vec<Tag>),
Tasks(Vec<Task>), Tasks(Vec<Task>),
+31 -4
View File
@@ -5,14 +5,41 @@ mod tests {
use crate::models::{ use crate::models::{
radarr_models::{ radarr_models::{
AddMovieSearchResult, BlocklistItem, BlocklistResponse, Collection, Credit, DiskSpace, AddMovieSearchResult, AuthenticationMethod, AuthenticationRequired, BlocklistItem,
DownloadRecord, DownloadsResponse, Indexer, IndexerSettings, IndexerTestResult, Log, BlocklistResponse, CertificateValidation, Collection, Credit, DiskSpace, DownloadRecord,
LogResponse, MinimumAvailability, Monitor, Movie, MovieHistoryItem, QualityProfile, DownloadsResponse, Indexer, IndexerSettings, IndexerTestResult, Log, LogResponse,
QueueEvent, RadarrSerdeable, Release, RootFolder, SystemStatus, Tag, Task, TaskName, Update, MinimumAvailability, Monitor, Movie, MovieHistoryItem, QualityProfile, QueueEvent,
RadarrSerdeable, Release, RootFolder, SystemStatus, Tag, Task, TaskName, Update,
}, },
Serdeable, Serdeable,
}; };
#[test]
fn test_authentication_method_display() {
assert_str_eq!(AuthenticationMethod::Basic.to_string(), "basic");
assert_str_eq!(AuthenticationMethod::Forms.to_string(), "forms");
assert_str_eq!(AuthenticationMethod::None.to_string(), "none");
}
#[test]
fn test_authentication_required_display() {
assert_str_eq!(AuthenticationRequired::Enabled.to_string(), "enabled");
assert_str_eq!(
AuthenticationRequired::DisabledForLocalAddresses.to_string(),
"disabledForLocalAddresses"
);
}
#[test]
fn test_certificate_validation_display() {
assert_str_eq!(CertificateValidation::Enabled.to_string(), "enabled");
assert_str_eq!(
CertificateValidation::DisabledForLocalAddresses.to_string(),
"disabledForLocalAddresses"
);
assert_str_eq!(CertificateValidation::Disabled.to_string(), "disabled");
}
#[test] #[test]
fn test_task_name_display() { fn test_task_name_display() {
assert_str_eq!( assert_str_eq!(
+43 -6
View File
@@ -11,10 +11,10 @@ use crate::app::RadarrConfig;
use crate::models::radarr_models::{ use crate::models::radarr_models::{
AddMovieBody, AddMovieSearchResult, AddOptions, AddRootFolderBody, BlocklistResponse, Collection, AddMovieBody, AddMovieSearchResult, AddOptions, AddRootFolderBody, BlocklistResponse, Collection,
CollectionMovie, CommandBody, Credit, CreditType, DeleteMovieParams, DiskSpace, DownloadRecord, CollectionMovie, CommandBody, Credit, CreditType, DeleteMovieParams, DiskSpace, DownloadRecord,
DownloadsResponse, EditCollectionParams, EditIndexerParams, EditMovieParams, Indexer, DownloadsResponse, EditCollectionParams, EditIndexerParams, EditMovieParams, HostConfig, Indexer,
IndexerSettings, IndexerTestResult, LogResponse, Movie, MovieCommandBody, MovieHistoryItem, IndexerSettings, IndexerTestResult, LogResponse, Movie, MovieCommandBody, MovieHistoryItem,
QualityProfile, QueueEvent, RadarrSerdeable, Release, ReleaseDownloadBody, RootFolder, QualityProfile, QueueEvent, RadarrSerdeable, Release, ReleaseDownloadBody, RootFolder,
SystemStatus, Tag, Task, TaskName, Update, SecurityConfig, SystemStatus, Tag, Task, TaskName, Update,
}; };
use crate::models::servarr_data::radarr::modals::{ use crate::models::servarr_data::radarr::modals::{
AddMovieModal, EditCollectionModal, EditIndexerModal, EditMovieModal, IndexerTestResultModalItem, AddMovieModal, EditCollectionModal, EditIndexerModal, EditMovieModal, IndexerTestResultModalItem,
@@ -50,6 +50,7 @@ pub enum RadarrEvent {
GetBlocklist, GetBlocklist,
GetCollections, GetCollections,
GetDownloads, GetDownloads,
GetHostConfig,
GetIndexers, GetIndexers,
GetAllIndexerSettings, GetAllIndexerSettings,
GetLogs(Option<u64>), GetLogs(Option<u64>),
@@ -62,6 +63,7 @@ pub enum RadarrEvent {
GetQueuedEvents, GetQueuedEvents,
GetReleases(Option<i64>), GetReleases(Option<i64>),
GetRootFolders, GetRootFolders,
GetSecurityConfig,
GetStatus, GetStatus,
GetTags, GetTags,
GetTasks, GetTasks,
@@ -86,6 +88,7 @@ impl RadarrEvent {
RadarrEvent::GetBlocklist => "/blocklist?page=1&pageSize=10000", RadarrEvent::GetBlocklist => "/blocklist?page=1&pageSize=10000",
RadarrEvent::GetCollections | RadarrEvent::EditCollection(_) => "/collection", RadarrEvent::GetCollections | RadarrEvent::EditCollection(_) => "/collection",
RadarrEvent::GetDownloads | RadarrEvent::DeleteDownload(_) => "/queue", RadarrEvent::GetDownloads | RadarrEvent::DeleteDownload(_) => "/queue",
RadarrEvent::GetHostConfig | RadarrEvent::GetSecurityConfig => "/config/host",
RadarrEvent::GetIndexers | RadarrEvent::EditIndexer(_) | RadarrEvent::DeleteIndexer(_) => { RadarrEvent::GetIndexers | RadarrEvent::EditIndexer(_) | RadarrEvent::DeleteIndexer(_) => {
"/indexer" "/indexer"
} }
@@ -179,14 +182,15 @@ impl<'a, 'b> Network<'a, 'b> {
self.edit_indexer(params).await.map(RadarrSerdeable::from) self.edit_indexer(params).await.map(RadarrSerdeable::from)
} }
RadarrEvent::EditMovie(params) => self.edit_movie(params).await.map(RadarrSerdeable::from), RadarrEvent::EditMovie(params) => self.edit_movie(params).await.map(RadarrSerdeable::from),
RadarrEvent::GetBlocklist => self.get_blocklist().await.map(RadarrSerdeable::from),
RadarrEvent::GetCollections => self.get_collections().await.map(RadarrSerdeable::from),
RadarrEvent::GetDownloads => self.get_downloads().await.map(RadarrSerdeable::from),
RadarrEvent::GetIndexers => self.get_indexers().await.map(RadarrSerdeable::from),
RadarrEvent::GetAllIndexerSettings => self RadarrEvent::GetAllIndexerSettings => self
.get_all_indexer_settings() .get_all_indexer_settings()
.await .await
.map(RadarrSerdeable::from), .map(RadarrSerdeable::from),
RadarrEvent::GetBlocklist => self.get_blocklist().await.map(RadarrSerdeable::from),
RadarrEvent::GetCollections => self.get_collections().await.map(RadarrSerdeable::from),
RadarrEvent::GetDownloads => self.get_downloads().await.map(RadarrSerdeable::from),
RadarrEvent::GetHostConfig => self.get_host_config().await.map(RadarrSerdeable::from),
RadarrEvent::GetIndexers => self.get_indexers().await.map(RadarrSerdeable::from),
RadarrEvent::GetLogs(events) => self.get_logs(events).await.map(RadarrSerdeable::from), RadarrEvent::GetLogs(events) => self.get_logs(events).await.map(RadarrSerdeable::from),
RadarrEvent::GetMovieCredits(movie_id) => { RadarrEvent::GetMovieCredits(movie_id) => {
self.get_credits(movie_id).await.map(RadarrSerdeable::from) self.get_credits(movie_id).await.map(RadarrSerdeable::from)
@@ -209,6 +213,7 @@ impl<'a, 'b> Network<'a, 'b> {
self.get_releases(movie_id).await.map(RadarrSerdeable::from) self.get_releases(movie_id).await.map(RadarrSerdeable::from)
} }
RadarrEvent::GetRootFolders => self.get_root_folders().await.map(RadarrSerdeable::from), RadarrEvent::GetRootFolders => self.get_root_folders().await.map(RadarrSerdeable::from),
RadarrEvent::GetSecurityConfig => self.get_security_config().await.map(RadarrSerdeable::from),
RadarrEvent::GetStatus => self.get_status().await.map(RadarrSerdeable::from), RadarrEvent::GetStatus => self.get_status().await.map(RadarrSerdeable::from),
RadarrEvent::GetTags => self.get_tags().await.map(RadarrSerdeable::from), RadarrEvent::GetTags => self.get_tags().await.map(RadarrSerdeable::from),
RadarrEvent::GetTasks => self.get_tasks().await.map(RadarrSerdeable::from), RadarrEvent::GetTasks => self.get_tasks().await.map(RadarrSerdeable::from),
@@ -1354,6 +1359,22 @@ impl<'a, 'b> Network<'a, 'b> {
.await .await
} }
async fn get_host_config(&mut self) -> Result<HostConfig> {
info!("Fetching Radarr host config");
let request_props = self
.radarr_request_props_from(
RadarrEvent::GetHostConfig.resource(),
RequestMethod::Get,
None::<()>,
)
.await;
self
.handle_request::<(), HostConfig>(request_props, |_, _| ())
.await
}
async fn get_indexers(&mut self) -> Result<Vec<Indexer>> { async fn get_indexers(&mut self) -> Result<Vec<Indexer>> {
info!("Fetching Radarr indexers"); info!("Fetching Radarr indexers");
@@ -1765,6 +1786,22 @@ impl<'a, 'b> Network<'a, 'b> {
.await .await
} }
async fn get_security_config(&mut self) -> Result<SecurityConfig> {
info!("Fetching Radarr security config");
let request_props = self
.radarr_request_props_from(
RadarrEvent::GetSecurityConfig.resource(),
RequestMethod::Get,
None::<()>,
)
.await;
self
.handle_request::<(), SecurityConfig>(request_props, |_, _| ())
.await
}
async fn get_status(&mut self) -> Result<SystemStatus> { async fn get_status(&mut self) -> Result<SystemStatus> {
info!("Fetching Radarr system status"); info!("Fetching Radarr system status");
+71 -1
View File
@@ -174,6 +174,13 @@ mod test {
assert_str_eq!(event.resource(), "/queue"); assert_str_eq!(event.resource(), "/queue");
} }
#[rstest]
fn test_resource_host_config(
#[values(RadarrEvent::GetHostConfig, RadarrEvent::GetSecurityConfig)] event: RadarrEvent,
) {
assert_str_eq!(event.resource(), "/config/host");
}
#[rstest] #[rstest]
fn test_resource_command( fn test_resource_command(
#[values( #[values(
@@ -2171,6 +2178,37 @@ mod test {
} }
} }
#[tokio::test]
async fn test_handle_get_host_config_event() {
let host_config_response = json!({
"bindAddress": "*",
"port": 7878,
"urlBase": "some.test.site/radarr",
"instanceName": "Radarr",
"applicationUrl": "https://some.test.site:7878/radarr",
"enableSsl": true
});
let response: HostConfig = serde_json::from_value(host_config_response.clone()).unwrap();
let (async_server, app_arc, _server) = mock_radarr_api(
RequestMethod::Get,
None,
Some(host_config_response),
None,
RadarrEvent::GetHostConfig.resource(),
)
.await;
let mut network = Network::new(&app_arc, CancellationToken::new());
if let RadarrSerdeable::HostConfig(host_config) = network
.handle_radarr_event(RadarrEvent::GetHostConfig)
.await
.unwrap()
{
async_server.assert_async().await;
assert_eq!(host_config, response);
}
}
#[tokio::test] #[tokio::test]
async fn test_handle_get_indexers_event() { async fn test_handle_get_indexers_event() {
let indexers_response_json = json!([{ let indexers_response_json = json!([{
@@ -2711,7 +2749,7 @@ mod test {
} }
#[tokio::test] #[tokio::test]
async fn test_add_tag() { async fn test_handle_add_tag() {
let tag_json = json!({ "id": 3, "label": "testing" }); let tag_json = json!({ "id": 3, "label": "testing" });
let response: Tag = serde_json::from_value(tag_json.clone()).unwrap(); let response: Tag = serde_json::from_value(tag_json.clone()).unwrap();
let (async_server, app_arc, _server) = mock_radarr_api( let (async_server, app_arc, _server) = mock_radarr_api(
@@ -2792,6 +2830,38 @@ mod test {
} }
} }
#[tokio::test]
async fn test_handle_get_security_config_event() {
let security_config_response = json!({
"authenticationMethod": "forms",
"authenticationRequired": "disabledForLocalAddresses",
"username": "test",
"password": "some password",
"apiKey": "someApiKey12345",
"certificateValidation": "disabledForLocalAddresses",
});
let response: SecurityConfig =
serde_json::from_value(security_config_response.clone()).unwrap();
let (async_server, app_arc, _server) = mock_radarr_api(
RequestMethod::Get,
None,
Some(security_config_response),
None,
RadarrEvent::GetSecurityConfig.resource(),
)
.await;
let mut network = Network::new(&app_arc, CancellationToken::new());
if let RadarrSerdeable::SecurityConfig(security_config) = network
.handle_radarr_event(RadarrEvent::GetSecurityConfig)
.await
.unwrap()
{
async_server.assert_async().await;
assert_eq!(security_config, response);
}
}
#[tokio::test] #[tokio::test]
async fn test_handle_get_movie_credits_event() { async fn test_handle_get_movie_credits_event() {
let credits_json = json!([ let credits_json = json!([