Implemented the ability to view indexers
This commit is contained in:
+15
-1
@@ -4,7 +4,7 @@ use strum::IntoEnumIterator;
|
||||
|
||||
use crate::app::{App, Route};
|
||||
use crate::models::radarr_models::{
|
||||
AddMovieSearchResult, Collection, CollectionMovie, Credit, DiskSpace, DownloadRecord,
|
||||
AddMovieSearchResult, Collection, CollectionMovie, Credit, DiskSpace, DownloadRecord, Indexer,
|
||||
MinimumAvailability, Monitor, Movie, MovieHistoryItem, QueueEvent, Release, ReleaseField,
|
||||
RootFolder, Task,
|
||||
};
|
||||
@@ -36,6 +36,7 @@ pub struct RadarrData<'a> {
|
||||
pub root_folder_list: StatefulList<RootFolder>,
|
||||
pub selected_block: BlockSelectionState<'a, ActiveRadarrBlock>,
|
||||
pub downloads: StatefulTable<DownloadRecord>,
|
||||
pub indexers: StatefulTable<Indexer>,
|
||||
pub quality_profile_map: BiMap<u64, String>,
|
||||
pub tags_map: BiMap<u64, String>,
|
||||
pub movie_details: ScrollableText,
|
||||
@@ -261,6 +262,7 @@ impl<'a> Default for RadarrData<'a> {
|
||||
selected_block: BlockSelectionState::default(),
|
||||
filtered_movies: StatefulTable::default(),
|
||||
downloads: StatefulTable::default(),
|
||||
indexers: StatefulTable::default(),
|
||||
quality_profile_map: BiMap::default(),
|
||||
tags_map: BiMap::default(),
|
||||
file_details: String::default(),
|
||||
@@ -318,6 +320,12 @@ impl<'a> Default for RadarrData<'a> {
|
||||
help: "",
|
||||
contextual_help: Some("<a> add | <del> delete | <r> refresh"),
|
||||
},
|
||||
TabRoute {
|
||||
title: "Indexers",
|
||||
route: ActiveRadarrBlock::Indexers.into(),
|
||||
help: "",
|
||||
contextual_help: Some("<r> refresh"),
|
||||
},
|
||||
TabRoute {
|
||||
title: "System",
|
||||
route: ActiveRadarrBlock::System.into(),
|
||||
@@ -410,6 +418,7 @@ pub enum ActiveRadarrBlock {
|
||||
FileInfo,
|
||||
FilterCollections,
|
||||
FilterMovies,
|
||||
Indexers,
|
||||
ManualSearch,
|
||||
ManualSearchSortPrompt,
|
||||
ManualSearchConfirmPrompt,
|
||||
@@ -573,6 +582,11 @@ impl<'a> App<'a> {
|
||||
.dispatch_network_event(RadarrEvent::GetDownloads.into())
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::Indexers => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetIndexers.into())
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::System => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetTasks.into())
|
||||
|
||||
+32
-3
@@ -273,6 +273,7 @@ mod tests {
|
||||
assert_eq!(radarr_data.selected_block, BlockSelectionState::default());
|
||||
assert!(radarr_data.filtered_movies.items.is_empty());
|
||||
assert!(radarr_data.downloads.items.is_empty());
|
||||
assert!(radarr_data.indexers.items.is_empty());
|
||||
assert!(radarr_data.quality_profile_map.is_empty());
|
||||
assert!(radarr_data.tags_map.is_empty());
|
||||
assert!(radarr_data.file_details.is_empty());
|
||||
@@ -306,7 +307,7 @@ mod tests {
|
||||
assert!(!radarr_data.delete_movie_files);
|
||||
assert!(!radarr_data.add_list_exclusion);
|
||||
|
||||
assert_eq!(radarr_data.main_tabs.tabs.len(), 5);
|
||||
assert_eq!(radarr_data.main_tabs.tabs.len(), 6);
|
||||
|
||||
assert_str_eq!(radarr_data.main_tabs.tabs[0].title, "Library");
|
||||
assert_eq!(
|
||||
@@ -348,14 +349,25 @@ mod tests {
|
||||
Some("<a> add | <del> delete | <r> refresh")
|
||||
);
|
||||
|
||||
assert_str_eq!(radarr_data.main_tabs.tabs[4].title, "System");
|
||||
assert_str_eq!(radarr_data.main_tabs.tabs[4].title, "Indexers");
|
||||
assert_eq!(
|
||||
radarr_data.main_tabs.tabs[4].route,
|
||||
ActiveRadarrBlock::System.into()
|
||||
ActiveRadarrBlock::Indexers.into()
|
||||
);
|
||||
assert!(radarr_data.main_tabs.tabs[4].help.is_empty());
|
||||
assert_eq!(
|
||||
radarr_data.main_tabs.tabs[4].contextual_help,
|
||||
Some("<r> refresh")
|
||||
);
|
||||
|
||||
assert_str_eq!(radarr_data.main_tabs.tabs[5].title, "System");
|
||||
assert_eq!(
|
||||
radarr_data.main_tabs.tabs[5].route,
|
||||
ActiveRadarrBlock::System.into()
|
||||
);
|
||||
assert!(radarr_data.main_tabs.tabs[5].help.is_empty());
|
||||
assert_eq!(
|
||||
radarr_data.main_tabs.tabs[5].contextual_help,
|
||||
Some("<t> open tasks | <z> open queue | <l> open logs | <u> open updates | <r> refresh")
|
||||
);
|
||||
|
||||
@@ -684,6 +696,23 @@ mod tests {
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_indexers_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app
|
||||
.dispatch_by_radarr_block(&ActiveRadarrBlock::Indexers)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetIndexers.into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_system_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
@@ -116,6 +116,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
|
||||
}
|
||||
}
|
||||
ActiveRadarrBlock::Downloads => self.app.data.radarr_data.downloads.scroll_up(),
|
||||
ActiveRadarrBlock::Indexers => self.app.data.radarr_data.indexers.scroll_up(),
|
||||
ActiveRadarrBlock::RootFolders => self.app.data.radarr_data.root_folders.scroll_up(),
|
||||
_ => (),
|
||||
}
|
||||
@@ -145,6 +146,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
|
||||
}
|
||||
}
|
||||
ActiveRadarrBlock::Downloads => self.app.data.radarr_data.downloads.scroll_down(),
|
||||
ActiveRadarrBlock::Indexers => self.app.data.radarr_data.indexers.scroll_down(),
|
||||
ActiveRadarrBlock::RootFolders => self.app.data.radarr_data.root_folders.scroll_down(),
|
||||
_ => (),
|
||||
}
|
||||
@@ -179,6 +181,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
|
||||
}
|
||||
}
|
||||
ActiveRadarrBlock::Downloads => self.app.data.radarr_data.downloads.scroll_to_top(),
|
||||
ActiveRadarrBlock::Indexers => self.app.data.radarr_data.indexers.scroll_to_top(),
|
||||
ActiveRadarrBlock::RootFolders => self.app.data.radarr_data.root_folders.scroll_to_top(),
|
||||
ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchCollection => {
|
||||
self.app.data.radarr_data.search.scroll_home()
|
||||
@@ -220,6 +223,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
|
||||
}
|
||||
}
|
||||
ActiveRadarrBlock::Downloads => self.app.data.radarr_data.downloads.scroll_to_bottom(),
|
||||
ActiveRadarrBlock::Indexers => self.app.data.radarr_data.indexers.scroll_to_bottom(),
|
||||
ActiveRadarrBlock::RootFolders => self.app.data.radarr_data.root_folders.scroll_to_bottom(),
|
||||
ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchCollection => {
|
||||
self.app.data.radarr_data.search.reset_offset()
|
||||
@@ -257,6 +261,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
|
||||
| ActiveRadarrBlock::Downloads
|
||||
| ActiveRadarrBlock::Collections
|
||||
| ActiveRadarrBlock::RootFolders
|
||||
| ActiveRadarrBlock::Indexers
|
||||
| ActiveRadarrBlock::System => match self.key {
|
||||
_ if *self.key == DEFAULT_KEYBINDINGS.left.key => {
|
||||
self.app.data.radarr_data.main_tabs.previous();
|
||||
@@ -521,6 +526,12 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
ActiveRadarrBlock::Indexers => match self.key {
|
||||
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
|
||||
self.app.should_refresh = true;
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
ActiveRadarrBlock::Collections => match self.key {
|
||||
_ if *key == DEFAULT_KEYBINDINGS.search.key => {
|
||||
self
|
||||
|
||||
@@ -16,7 +16,7 @@ mod tests {
|
||||
mod test_handle_scroll_up_and_down {
|
||||
use rstest::rstest;
|
||||
|
||||
use crate::models::radarr_models::{DownloadRecord, RootFolder};
|
||||
use crate::models::radarr_models::{DownloadRecord, Indexer, RootFolder};
|
||||
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
|
||||
|
||||
use super::*;
|
||||
@@ -75,6 +75,16 @@ mod tests {
|
||||
title
|
||||
);
|
||||
|
||||
test_iterable_scroll!(
|
||||
test_indexers_scroll,
|
||||
RadarrHandler,
|
||||
indexers,
|
||||
simple_stateful_iterable_vec!(Indexer, String, protocol),
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
protocol
|
||||
);
|
||||
|
||||
test_iterable_scroll!(
|
||||
test_root_folders_scroll,
|
||||
RadarrHandler,
|
||||
@@ -89,7 +99,7 @@ mod tests {
|
||||
mod test_handle_home_end {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use crate::models::radarr_models::{DownloadRecord, RootFolder};
|
||||
use crate::models::radarr_models::{DownloadRecord, Indexer, RootFolder};
|
||||
use crate::{
|
||||
extended_stateful_iterable_vec, test_iterable_home_and_end, test_text_box_home_end_keys,
|
||||
};
|
||||
@@ -150,6 +160,16 @@ mod tests {
|
||||
title
|
||||
);
|
||||
|
||||
test_iterable_home_and_end!(
|
||||
test_indexers_home_end,
|
||||
RadarrHandler,
|
||||
indexers,
|
||||
extended_stateful_iterable_vec!(Indexer, String, protocol),
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
protocol
|
||||
);
|
||||
|
||||
test_iterable_home_and_end!(
|
||||
test_root_folders_home_end,
|
||||
RadarrHandler,
|
||||
@@ -250,7 +270,8 @@ mod tests {
|
||||
|
||||
#[rstest]
|
||||
#[case(ActiveRadarrBlock::Movies, 0, ActiveRadarrBlock::System)]
|
||||
#[case(ActiveRadarrBlock::System, 4, ActiveRadarrBlock::RootFolders)]
|
||||
#[case(ActiveRadarrBlock::System, 5, ActiveRadarrBlock::Indexers)]
|
||||
#[case(ActiveRadarrBlock::Indexers, 4, ActiveRadarrBlock::RootFolders)]
|
||||
#[case(ActiveRadarrBlock::RootFolders, 3, ActiveRadarrBlock::Collections)]
|
||||
#[case(ActiveRadarrBlock::Collections, 2, ActiveRadarrBlock::Downloads)]
|
||||
#[case(ActiveRadarrBlock::Downloads, 1, ActiveRadarrBlock::Movies)]
|
||||
@@ -281,8 +302,9 @@ mod tests {
|
||||
#[case(ActiveRadarrBlock::Movies, 0, ActiveRadarrBlock::Downloads)]
|
||||
#[case(ActiveRadarrBlock::Downloads, 1, ActiveRadarrBlock::Collections)]
|
||||
#[case(ActiveRadarrBlock::Collections, 2, ActiveRadarrBlock::RootFolders)]
|
||||
#[case(ActiveRadarrBlock::RootFolders, 3, ActiveRadarrBlock::System)]
|
||||
#[case(ActiveRadarrBlock::System, 4, ActiveRadarrBlock::Movies)]
|
||||
#[case(ActiveRadarrBlock::RootFolders, 3, ActiveRadarrBlock::Indexers)]
|
||||
#[case(ActiveRadarrBlock::Indexers, 4, ActiveRadarrBlock::System)]
|
||||
#[case(ActiveRadarrBlock::System, 5, ActiveRadarrBlock::Movies)]
|
||||
fn test_radarr_tab_right(
|
||||
#[case] active_radarr_block: ActiveRadarrBlock,
|
||||
#[case] index: usize,
|
||||
@@ -963,6 +985,7 @@ mod tests {
|
||||
ActiveRadarrBlock::Movies,
|
||||
ActiveRadarrBlock::Collections,
|
||||
ActiveRadarrBlock::Downloads,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
ActiveRadarrBlock::RootFolders,
|
||||
ActiveRadarrBlock::System
|
||||
)]
|
||||
|
||||
+337
-289
@@ -3,7 +3,7 @@ use std::fmt::{Display, Formatter};
|
||||
use chrono::{DateTime, Utc};
|
||||
use derivative::Derivative;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Number;
|
||||
use serde_json::{Number, Value};
|
||||
use strum_macros::{Display, EnumIter};
|
||||
|
||||
use crate::models::HorizontallyScrollableText;
|
||||
@@ -12,86 +12,47 @@ use crate::models::HorizontallyScrollableText;
|
||||
#[path = "radarr_models_tests.rs"]
|
||||
mod radarr_models_tests;
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
|
||||
#[derive(Default, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DiskSpace {
|
||||
pub free_space: Number,
|
||||
pub total_space: Number,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SystemStatus {
|
||||
pub version: String,
|
||||
pub start_time: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug, Clone, Eq, PartialEq)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RootFolder {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
pub path: String,
|
||||
pub accessible: bool,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub free_space: Number,
|
||||
pub unmapped_folders: Option<Vec<UnmappedFolder>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, Eq, PartialEq)]
|
||||
pub struct UnmappedFolder {
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub struct AddMovieBody {
|
||||
pub tmdb_id: u64,
|
||||
pub title: String,
|
||||
pub root_folder_path: String,
|
||||
pub quality_profile_id: u64,
|
||||
pub minimum_availability: String,
|
||||
pub monitored: bool,
|
||||
pub tags: Vec<u64>,
|
||||
pub add_options: AddOptions,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Movie {
|
||||
pub struct AddMovieSearchResult {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
pub tmdb_id: Number,
|
||||
pub title: HorizontallyScrollableText,
|
||||
pub original_language: Language,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub size_on_disk: Number,
|
||||
pub status: String,
|
||||
pub overview: String,
|
||||
pub path: String,
|
||||
pub studio: String,
|
||||
pub genres: Vec<String>,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub year: Number,
|
||||
pub monitored: bool,
|
||||
pub has_file: bool,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub runtime: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub tmdb_id: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub quality_profile_id: Number,
|
||||
pub minimum_availability: MinimumAvailability,
|
||||
pub certification: Option<String>,
|
||||
pub tags: Vec<Number>,
|
||||
pub ratings: RatingsList,
|
||||
pub movie_file: Option<MovieFile>,
|
||||
pub collection: Option<Collection>,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[derivative(Default)]
|
||||
#[derive(Default, Serialize, Debug, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CollectionMovie {
|
||||
pub title: HorizontallyScrollableText,
|
||||
pub overview: String,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub year: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub runtime: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub tmdb_id: Number,
|
||||
pub genres: Vec<String>,
|
||||
pub ratings: RatingsList,
|
||||
pub struct AddOptions {
|
||||
pub monitor: String,
|
||||
pub search_for_movie: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Debug)]
|
||||
pub struct AddRootFolderBody {
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Derivative, Clone, Debug, PartialEq, Eq)]
|
||||
@@ -112,14 +73,148 @@ pub struct Collection {
|
||||
pub movies: Option<Vec<CollectionMovie>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Derivative, Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Derivative, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MovieFile {
|
||||
pub relative_path: String,
|
||||
pub path: String,
|
||||
pub date_added: DateTime<Utc>,
|
||||
pub media_info: Option<MediaInfo>,
|
||||
pub struct CollectionMovie {
|
||||
pub title: HorizontallyScrollableText,
|
||||
pub overview: String,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub year: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub runtime: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub tmdb_id: Number,
|
||||
pub genres: Vec<String>,
|
||||
pub ratings: RatingsList,
|
||||
}
|
||||
|
||||
#[derive(Default, Derivative, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CommandBody {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Clone, Debug, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Credit {
|
||||
pub person_name: String,
|
||||
pub character: Option<String>,
|
||||
pub department: Option<String>,
|
||||
pub job: Option<String>,
|
||||
#[serde(rename(deserialize = "type"))]
|
||||
pub credit_type: CreditType,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, PartialEq, Eq, Clone, Debug)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum CreditType {
|
||||
#[default]
|
||||
Cast,
|
||||
Crew,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DiskSpace {
|
||||
pub free_space: Number,
|
||||
pub total_space: Number,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DownloadRecord {
|
||||
pub title: String,
|
||||
pub status: String,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub movie_id: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub size: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub sizeleft: Number,
|
||||
pub output_path: Option<HorizontallyScrollableText>,
|
||||
pub indexer: String,
|
||||
pub download_client: String,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DownloadsResponse {
|
||||
pub records: Vec<DownloadRecord>,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Indexer {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
pub name: Option<String>,
|
||||
pub implementation: Option<String>,
|
||||
pub implementation_name: Option<String>,
|
||||
pub config_contract: Option<String>,
|
||||
pub supports_rss: bool,
|
||||
pub supports_search: bool,
|
||||
pub fields: Option<Vec<IndexerField>>,
|
||||
pub enable_rss: bool,
|
||||
pub enable_automatic_search: bool,
|
||||
pub enable_interactive_search: bool,
|
||||
pub protocol: String,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub priority: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub download_client_id: Number,
|
||||
pub tags: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IndexerField {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub order: Number,
|
||||
pub name: Option<String>,
|
||||
pub label: Option<String>,
|
||||
pub value: Option<Value>,
|
||||
pub advanced: bool,
|
||||
pub select_options: Option<Vec<IndexerSelectOption>>,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IndexerSelectOption {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub value: Number,
|
||||
pub name: Option<String>,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub order: Number,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub struct Language {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Log {
|
||||
pub time: DateTime<Utc>,
|
||||
pub exception: Option<String>,
|
||||
pub exception_type: Option<String>,
|
||||
pub level: String,
|
||||
pub logger: Option<String>,
|
||||
pub message: Option<String>,
|
||||
pub method: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Debug, Eq, PartialEq)]
|
||||
pub struct LogResponse {
|
||||
pub records: Vec<Log>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Derivative, Debug, Clone, PartialEq, Eq)]
|
||||
@@ -146,206 +241,6 @@ pub struct MediaInfo {
|
||||
pub scan_type: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RatingsList {
|
||||
pub imdb: Option<Rating>,
|
||||
pub tmdb: Option<Rating>,
|
||||
pub rotten_tomatoes: Option<Rating>,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[derivative(Default)]
|
||||
pub struct Rating {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub value: Number,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DownloadsResponse {
|
||||
pub records: Vec<DownloadRecord>,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DownloadRecord {
|
||||
pub title: String,
|
||||
pub status: String,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub movie_id: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub size: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub sizeleft: Number,
|
||||
pub output_path: Option<HorizontallyScrollableText>,
|
||||
pub indexer: String,
|
||||
pub download_client: String,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug)]
|
||||
#[derivative(Default)]
|
||||
pub struct QualityProfile {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug)]
|
||||
#[derivative(Default)]
|
||||
pub struct Tag {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MovieHistoryItem {
|
||||
pub source_title: HorizontallyScrollableText,
|
||||
pub quality: QualityWrapper,
|
||||
pub languages: Vec<Language>,
|
||||
pub date: DateTime<Utc>,
|
||||
pub event_type: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub struct Language {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub struct Quality {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub struct QualityWrapper {
|
||||
pub quality: Quality,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, PartialEq, Eq, Clone, Debug)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum CreditType {
|
||||
#[default]
|
||||
Cast,
|
||||
Crew,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Clone, Debug, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Credit {
|
||||
pub person_name: String,
|
||||
pub character: Option<String>,
|
||||
pub department: Option<String>,
|
||||
pub job: Option<String>,
|
||||
#[serde(rename(deserialize = "type"))]
|
||||
pub credit_type: CreditType,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Derivative, Clone, Debug, PartialEq, Eq)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Release {
|
||||
pub guid: String,
|
||||
pub protocol: String,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub age: Number,
|
||||
pub title: HorizontallyScrollableText,
|
||||
pub indexer: String,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub indexer_id: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub size: Number,
|
||||
pub rejected: bool,
|
||||
pub rejections: Option<Vec<String>>,
|
||||
pub seeders: Option<Number>,
|
||||
pub leechers: Option<Number>,
|
||||
pub languages: Option<Vec<Language>>,
|
||||
pub quality: QualityWrapper,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, EnumIter, Display)]
|
||||
pub enum ReleaseField {
|
||||
#[default]
|
||||
Source,
|
||||
Age,
|
||||
Rejected,
|
||||
Title,
|
||||
Indexer,
|
||||
Size,
|
||||
Peers,
|
||||
Language,
|
||||
Quality,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AddMovieBody {
|
||||
pub tmdb_id: u64,
|
||||
pub title: String,
|
||||
pub root_folder_path: String,
|
||||
pub quality_profile_id: u64,
|
||||
pub minimum_availability: String,
|
||||
pub monitored: bool,
|
||||
pub tags: Vec<u64>,
|
||||
pub add_options: AddOptions,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Debug, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AddOptions {
|
||||
pub monitor: String,
|
||||
pub search_for_movie: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ReleaseDownloadBody {
|
||||
pub guid: String,
|
||||
pub indexer_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AddMovieSearchResult {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub tmdb_id: Number,
|
||||
pub title: HorizontallyScrollableText,
|
||||
pub original_language: Language,
|
||||
pub status: String,
|
||||
pub overview: String,
|
||||
pub genres: Vec<String>,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub year: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub runtime: Number,
|
||||
pub ratings: RatingsList,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Debug)]
|
||||
pub struct AddRootFolderBody {
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Derivative, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MovieCommandBody {
|
||||
pub name: String,
|
||||
pub movie_ids: Vec<u64>,
|
||||
}
|
||||
|
||||
#[derive(Default, Derivative, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CommandBody {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Clone, Copy, Debug, EnumIter)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum MinimumAvailability {
|
||||
@@ -408,21 +303,181 @@ impl Monitor {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Derivative, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Log {
|
||||
pub time: DateTime<Utc>,
|
||||
pub exception: Option<String>,
|
||||
pub exception_type: Option<String>,
|
||||
pub level: String,
|
||||
pub logger: Option<String>,
|
||||
pub message: Option<String>,
|
||||
pub method: Option<String>,
|
||||
pub struct Movie {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
pub title: HorizontallyScrollableText,
|
||||
pub original_language: Language,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub size_on_disk: Number,
|
||||
pub status: String,
|
||||
pub overview: String,
|
||||
pub path: String,
|
||||
pub studio: String,
|
||||
pub genres: Vec<String>,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub year: Number,
|
||||
pub monitored: bool,
|
||||
pub has_file: bool,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub runtime: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub tmdb_id: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub quality_profile_id: Number,
|
||||
pub minimum_availability: MinimumAvailability,
|
||||
pub certification: Option<String>,
|
||||
pub tags: Vec<Number>,
|
||||
pub ratings: RatingsList,
|
||||
pub movie_file: Option<MovieFile>,
|
||||
pub collection: Option<Collection>,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Debug, Eq, PartialEq)]
|
||||
pub struct LogResponse {
|
||||
pub records: Vec<Log>,
|
||||
#[derive(Default, Derivative, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MovieCommandBody {
|
||||
pub name: String,
|
||||
pub movie_ids: Vec<u64>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Derivative, Debug, Clone, PartialEq, Eq)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MovieFile {
|
||||
pub relative_path: String,
|
||||
pub path: String,
|
||||
pub date_added: DateTime<Utc>,
|
||||
pub media_info: Option<MediaInfo>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MovieHistoryItem {
|
||||
pub source_title: HorizontallyScrollableText,
|
||||
pub quality: QualityWrapper,
|
||||
pub languages: Vec<Language>,
|
||||
pub date: DateTime<Utc>,
|
||||
pub event_type: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub struct Quality {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug)]
|
||||
#[derivative(Default)]
|
||||
pub struct QualityProfile {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub struct QualityWrapper {
|
||||
pub quality: Quality,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct QueueEvent {
|
||||
pub trigger: String,
|
||||
pub name: String,
|
||||
pub command_name: String,
|
||||
pub status: String,
|
||||
pub queued: DateTime<Utc>,
|
||||
pub started: Option<DateTime<Utc>>,
|
||||
pub ended: Option<DateTime<Utc>>,
|
||||
pub duration: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[derivative(Default)]
|
||||
pub struct Rating {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub value: Number,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RatingsList {
|
||||
pub imdb: Option<Rating>,
|
||||
pub tmdb: Option<Rating>,
|
||||
pub rotten_tomatoes: Option<Rating>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Derivative, Clone, Debug, PartialEq, Eq)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Release {
|
||||
pub guid: String,
|
||||
pub protocol: String,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub age: Number,
|
||||
pub title: HorizontallyScrollableText,
|
||||
pub indexer: String,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub indexer_id: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub size: Number,
|
||||
pub rejected: bool,
|
||||
pub rejections: Option<Vec<String>>,
|
||||
pub seeders: Option<Number>,
|
||||
pub leechers: Option<Number>,
|
||||
pub languages: Option<Vec<Language>>,
|
||||
pub quality: QualityWrapper,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ReleaseDownloadBody {
|
||||
pub guid: String,
|
||||
pub indexer_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, EnumIter, Display)]
|
||||
pub enum ReleaseField {
|
||||
#[default]
|
||||
Source,
|
||||
Age,
|
||||
Rejected,
|
||||
Title,
|
||||
Indexer,
|
||||
Size,
|
||||
Peers,
|
||||
Language,
|
||||
Quality,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug, Clone, Eq, PartialEq)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RootFolder {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
pub path: String,
|
||||
pub accessible: bool,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub free_space: Number,
|
||||
pub unmapped_folders: Option<Vec<UnmappedFolder>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SystemStatus {
|
||||
pub version: String,
|
||||
pub start_time: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug)]
|
||||
#[derivative(Default)]
|
||||
pub struct Tag {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
@@ -438,17 +493,10 @@ pub struct Task {
|
||||
pub next_execution: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct QueueEvent {
|
||||
pub trigger: String,
|
||||
#[derive(Deserialize, Default, Debug, Clone, Eq, PartialEq)]
|
||||
pub struct UnmappedFolder {
|
||||
pub name: String,
|
||||
pub command_name: String,
|
||||
pub status: String,
|
||||
pub queued: DateTime<Utc>,
|
||||
pub started: Option<DateTime<Utc>>,
|
||||
pub ended: Option<DateTime<Utc>>,
|
||||
pub duration: Option<String>,
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
|
||||
+724
-703
File diff suppressed because it is too large
Load Diff
@@ -13,8 +13,8 @@ mod test {
|
||||
|
||||
use crate::app::radarr::ActiveRadarrBlock;
|
||||
use crate::models::radarr_models::{
|
||||
CollectionMovie, Language, MediaInfo, MinimumAvailability, Monitor, MovieFile, Quality,
|
||||
QualityWrapper, Rating, RatingsList,
|
||||
CollectionMovie, IndexerField, IndexerSelectOption, Language, MediaInfo, MinimumAvailability,
|
||||
Monitor, MovieFile, Quality, QualityWrapper, Rating, RatingsList,
|
||||
};
|
||||
use crate::models::HorizontallyScrollableText;
|
||||
use crate::App;
|
||||
@@ -854,6 +854,71 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_indexers_event() {
|
||||
let indexers_response_json = json!([{
|
||||
"enableRss": true,
|
||||
"enableAutomaticSearch": true,
|
||||
"enableInteractiveSearch": true,
|
||||
"supportsRss": true,
|
||||
"supportsSearch": true,
|
||||
"protocol": "torrent",
|
||||
"priority": 25,
|
||||
"downloadClientId": 0,
|
||||
"name": "Test Indexer",
|
||||
"fields": [
|
||||
{
|
||||
"order": 0,
|
||||
"name": "valueIsString",
|
||||
"label": "Value Is String",
|
||||
"value": "hello",
|
||||
"advanced": false
|
||||
},
|
||||
{
|
||||
"order": 1,
|
||||
"name": "emptyValueWithSelectOptions",
|
||||
"label": "Empty Value With Select Options",
|
||||
"advanced": true,
|
||||
"selectOptions": [
|
||||
{
|
||||
"value": -2,
|
||||
"name": "Original",
|
||||
"order": 0,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"order": 2,
|
||||
"name": "valueIsAnArray",
|
||||
"label": "Value is an array",
|
||||
"value": [1, 2],
|
||||
"advanced": false,
|
||||
},
|
||||
],
|
||||
"implementationName": "Torznab",
|
||||
"implementation": "Torznab",
|
||||
"configContract": "TorznabSettings",
|
||||
"tags": ["test_tag"],
|
||||
"id": 1
|
||||
}]);
|
||||
let (async_server, app_arc, _server) = mock_radarr_api(
|
||||
RequestMethod::Get,
|
||||
None,
|
||||
Some(indexers_response_json),
|
||||
RadarrEvent::GetIndexers.resource(),
|
||||
)
|
||||
.await;
|
||||
let network = Network::new(reqwest::Client::new(), &app_arc);
|
||||
|
||||
network.handle_radarr_event(RadarrEvent::GetIndexers).await;
|
||||
|
||||
async_server.assert_async().await;
|
||||
assert_eq!(
|
||||
app_arc.lock().await.data.radarr_data.indexers.items,
|
||||
vec![indexer()]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_queued_events_event() {
|
||||
let queued_events_json = json!([{
|
||||
@@ -2125,4 +2190,53 @@ mod test {
|
||||
credit_type: CreditType::Crew,
|
||||
}
|
||||
}
|
||||
|
||||
fn indexer() -> Indexer {
|
||||
Indexer {
|
||||
enable_rss: true,
|
||||
enable_automatic_search: true,
|
||||
enable_interactive_search: true,
|
||||
supports_rss: true,
|
||||
supports_search: true,
|
||||
protocol: "torrent".to_owned(),
|
||||
priority: Number::from(25),
|
||||
download_client_id: Number::from(0),
|
||||
name: Some("Test Indexer".to_owned()),
|
||||
implementation_name: Some("Torznab".to_owned()),
|
||||
implementation: Some("Torznab".to_owned()),
|
||||
config_contract: Some("TorznabSettings".to_owned()),
|
||||
tags: Some(vec!["test_tag".to_owned()]),
|
||||
id: Number::from(1),
|
||||
fields: Some(vec![
|
||||
IndexerField {
|
||||
order: Number::from(0),
|
||||
name: Some("valueIsString".to_owned()),
|
||||
label: Some("Value Is String".to_owned()),
|
||||
value: Some(json!("hello")),
|
||||
advanced: false,
|
||||
select_options: None,
|
||||
},
|
||||
IndexerField {
|
||||
order: Number::from(1),
|
||||
name: Some("emptyValueWithSelectOptions".to_owned()),
|
||||
label: Some("Empty Value With Select Options".to_owned()),
|
||||
value: None,
|
||||
advanced: true,
|
||||
select_options: Some(vec![IndexerSelectOption {
|
||||
value: Number::from(-2),
|
||||
name: Some("Original".to_owned()),
|
||||
order: Number::from(0),
|
||||
}]),
|
||||
},
|
||||
IndexerField {
|
||||
order: Number::from(2),
|
||||
name: Some("valueIsAnArray".to_owned()),
|
||||
label: Some("Value is an array".to_owned()),
|
||||
value: Some(json!([1, 2])),
|
||||
advanced: false,
|
||||
select_options: None,
|
||||
},
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
use tui::backend::Backend;
|
||||
use tui::layout::{Constraint, Rect};
|
||||
use tui::text::Text;
|
||||
use tui::widgets::{Cell, Row};
|
||||
use tui::Frame;
|
||||
|
||||
use crate::app::radarr::ActiveRadarrBlock;
|
||||
use crate::app::App;
|
||||
use crate::models::radarr_models::Indexer;
|
||||
use crate::models::Route;
|
||||
use crate::ui::utils::{layout_block_top_border, style_failure, style_primary, style_success};
|
||||
use crate::ui::{draw_table, DrawUi, TableProps};
|
||||
|
||||
pub(super) struct IndexersUi {}
|
||||
|
||||
impl DrawUi for IndexersUi {
|
||||
fn draw<B: Backend>(f: &mut Frame<'_, B>, app: &mut App<'_>, content_rect: Rect) {
|
||||
if matches!(
|
||||
*app.get_current_route(),
|
||||
Route::Radarr(ActiveRadarrBlock::Indexers, _)
|
||||
) {
|
||||
draw_indexers(f, app, content_rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_indexers<B: Backend>(f: &mut Frame<'_, B>, app: &mut App<'_>, area: Rect) {
|
||||
draw_table(
|
||||
f,
|
||||
area,
|
||||
layout_block_top_border(),
|
||||
TableProps {
|
||||
content: &mut app.data.radarr_data.indexers,
|
||||
table_headers: vec![
|
||||
"Indexer",
|
||||
"RSS",
|
||||
"Automatic Search",
|
||||
"Interactive Search",
|
||||
"Priority",
|
||||
],
|
||||
constraints: vec![
|
||||
Constraint::Ratio(1, 5),
|
||||
Constraint::Ratio(1, 5),
|
||||
Constraint::Ratio(1, 5),
|
||||
Constraint::Ratio(1, 5),
|
||||
Constraint::Ratio(1, 5),
|
||||
],
|
||||
help: app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help(),
|
||||
},
|
||||
|indexer: &'_ Indexer| {
|
||||
let Indexer {
|
||||
name,
|
||||
enable_rss,
|
||||
enable_automatic_search,
|
||||
enable_interactive_search,
|
||||
priority,
|
||||
..
|
||||
} = indexer;
|
||||
let bool_to_text = |flag: bool| {
|
||||
if flag {
|
||||
return ("Enabled", style_success());
|
||||
}
|
||||
|
||||
("Disabled", style_failure())
|
||||
};
|
||||
|
||||
let (rss_text, rss_style) = bool_to_text(*enable_rss);
|
||||
let mut rss = Text::from(rss_text);
|
||||
rss.patch_style(rss_style);
|
||||
|
||||
let (auto_search_text, auto_search_style) = bool_to_text(*enable_automatic_search);
|
||||
let mut automatic_search = Text::from(auto_search_text);
|
||||
automatic_search.patch_style(auto_search_style);
|
||||
|
||||
let (interactive_search_text, interactive_search_style) =
|
||||
bool_to_text(*enable_interactive_search);
|
||||
let mut interactive_search = Text::from(interactive_search_text);
|
||||
interactive_search.patch_style(interactive_search_style);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(name.clone().unwrap_or_default()),
|
||||
Cell::from(rss),
|
||||
Cell::from(automatic_search),
|
||||
Cell::from(interactive_search),
|
||||
Cell::from(priority.as_u64().unwrap().to_string()),
|
||||
])
|
||||
.style(style_primary())
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
)
|
||||
}
|
||||
@@ -21,6 +21,7 @@ use crate::models::Route;
|
||||
use crate::ui::draw_selectable_list;
|
||||
use crate::ui::draw_tabs;
|
||||
use crate::ui::loading;
|
||||
use crate::ui::radarr_ui::indexers_ui::IndexersUi;
|
||||
use crate::ui::radarr_ui::system_details_ui::SystemDetailsUi;
|
||||
use crate::ui::radarr_ui::system_ui::SystemUi;
|
||||
use crate::ui::radarr_ui::{
|
||||
@@ -44,6 +45,7 @@ mod delete_movie_ui;
|
||||
mod downloads_ui;
|
||||
mod edit_collection_ui;
|
||||
mod edit_movie_ui;
|
||||
mod indexers_ui;
|
||||
mod library_ui;
|
||||
mod movie_details_ui;
|
||||
mod radarr_ui_utils;
|
||||
@@ -72,6 +74,7 @@ impl DrawUi for RadarrUi {
|
||||
ActiveRadarrBlock::Downloads
|
||||
| ActiveRadarrBlock::DeleteDownloadPrompt
|
||||
| ActiveRadarrBlock::UpdateDownloadsPrompt => DownloadsUi::draw(f, app, content_rect),
|
||||
ActiveRadarrBlock::Indexers => IndexersUi::draw(f, app, content_rect),
|
||||
ActiveRadarrBlock::RootFolders
|
||||
| ActiveRadarrBlock::AddRootFolderPrompt
|
||||
| ActiveRadarrBlock::DeleteRootFolderPrompt => RootFoldersUi::draw(f, app, content_rect),
|
||||
|
||||
Reference in New Issue
Block a user