Refactored things a bit and added help text support
This commit is contained in:
+3
-14
@@ -7,26 +7,13 @@ use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use tokio::time::Instant;
|
||||
|
||||
use crate::app::models::{HorizontallyScrollableText, TabRoute, TabState};
|
||||
use crate::app::radarr::{ActiveRadarrBlock, RadarrData};
|
||||
use crate::models::{HorizontallyScrollableText, Route, TabRoute, TabState};
|
||||
use crate::network::NetworkEvent;
|
||||
|
||||
pub(crate) mod key_binding;
|
||||
pub mod models;
|
||||
pub mod radarr;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Route {
|
||||
Radarr(ActiveRadarrBlock),
|
||||
Sonarr,
|
||||
}
|
||||
|
||||
impl From<ActiveRadarrBlock> for Route {
|
||||
fn from(active_radarr_block: ActiveRadarrBlock) -> Route {
|
||||
Route::Radarr(active_radarr_block)
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_ROUTE: Route = Route::Radarr(ActiveRadarrBlock::Movies);
|
||||
|
||||
pub struct App {
|
||||
@@ -130,10 +117,12 @@ impl Default for App {
|
||||
TabRoute {
|
||||
title: "Radarr".to_owned(),
|
||||
route: ActiveRadarrBlock::Movies.into(),
|
||||
help: "<tab> change servarr | <?> help | <q> quit ".to_owned(),
|
||||
},
|
||||
TabRoute {
|
||||
title: "Sonarr".to_owned(),
|
||||
route: Route::Sonarr,
|
||||
help: "<tab> change servarr | <?> help | <q> quit ".to_owned(),
|
||||
},
|
||||
]),
|
||||
client: Client::new(),
|
||||
|
||||
+18
-4
@@ -4,12 +4,12 @@ use std::time::Duration;
|
||||
use chrono::{DateTime, Utc};
|
||||
use strum::EnumIter;
|
||||
|
||||
use crate::app::models::{ScrollableText, StatefulTable, TabRoute, TabState};
|
||||
use crate::app::App;
|
||||
use crate::network::radarr_network::{
|
||||
use crate::app::{App, Route};
|
||||
use crate::models::radarr_models::{
|
||||
Collection, CollectionMovie, Credit, DiskSpace, DownloadRecord, Movie, MovieHistoryItem,
|
||||
RadarrEvent,
|
||||
};
|
||||
use crate::models::{ScrollableText, StatefulTable, TabRoute, TabState};
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
|
||||
pub struct RadarrData {
|
||||
pub disk_space_vec: Vec<DiskSpace>,
|
||||
@@ -74,36 +74,44 @@ impl Default for RadarrData {
|
||||
TabRoute {
|
||||
title: "Library".to_owned(),
|
||||
route: ActiveRadarrBlock::Movies.into(),
|
||||
help: "<↑↓> scroll table | <enter> movie details | ←→ change tab ".to_owned(),
|
||||
},
|
||||
TabRoute {
|
||||
title: "Downloads".to_owned(),
|
||||
route: ActiveRadarrBlock::Downloads.into(),
|
||||
help: "<↑↓> scroll table | ←→ change tab ".to_owned(),
|
||||
},
|
||||
TabRoute {
|
||||
title: "Collections".to_owned(),
|
||||
route: ActiveRadarrBlock::Collections.into(),
|
||||
help: "<↑↓> scroll table | <enter> collection details | ←→ change tab ".to_owned(),
|
||||
},
|
||||
]),
|
||||
movie_info_tabs: TabState::new(vec![
|
||||
TabRoute {
|
||||
title: "Details".to_owned(),
|
||||
route: ActiveRadarrBlock::MovieDetails.into(),
|
||||
help: "←→ change tab | <esc> close ".to_owned(),
|
||||
},
|
||||
TabRoute {
|
||||
title: "History".to_owned(),
|
||||
route: ActiveRadarrBlock::MovieHistory.into(),
|
||||
help: "<↑↓> scroll table | ←→ change tab | <esc> close ".to_owned(),
|
||||
},
|
||||
TabRoute {
|
||||
title: "File".to_owned(),
|
||||
route: ActiveRadarrBlock::FileInfo.into(),
|
||||
help: "←→ change tab | <esc> close ".to_owned(),
|
||||
},
|
||||
TabRoute {
|
||||
title: "Cast".to_owned(),
|
||||
route: ActiveRadarrBlock::Cast.into(),
|
||||
help: "<↑↓> scroll table | ←→ change tab | <esc> close ".to_owned(),
|
||||
},
|
||||
TabRoute {
|
||||
title: "Crew".to_owned(),
|
||||
route: ActiveRadarrBlock::Crew.into(),
|
||||
help: "<↑↓> scroll table | ←→ change tab | <esc> close ".to_owned(),
|
||||
},
|
||||
]),
|
||||
}
|
||||
@@ -131,6 +139,12 @@ pub enum ActiveRadarrBlock {
|
||||
ViewMovieOverview,
|
||||
}
|
||||
|
||||
impl From<ActiveRadarrBlock> for Route {
|
||||
fn from(active_radarr_block: ActiveRadarrBlock) -> Route {
|
||||
Route::Radarr(active_radarr_block)
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub(super) async fn dispatch_by_radarr_block(&mut self, active_radarr_block: &ActiveRadarrBlock) {
|
||||
match active_radarr_block {
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
use crate::app::models::HorizontallyScrollableText;
|
||||
use crate::app::{App, Route};
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::radarr_handler::handle_radarr_key_events;
|
||||
use crate::models::{HorizontallyScrollableText, Route};
|
||||
|
||||
mod radarr_handler;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::models::Scrollable;
|
||||
use crate::app::radarr::ActiveRadarrBlock;
|
||||
use crate::handlers::handle_clear_errors;
|
||||
use crate::models::Scrollable;
|
||||
use crate::{App, Key};
|
||||
|
||||
pub async fn handle_radarr_key_events(
|
||||
|
||||
+1
-1
@@ -7,7 +7,6 @@ use crossterm::execute;
|
||||
use crossterm::terminal::{
|
||||
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
|
||||
};
|
||||
use log::debug;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
use tui::backend::CrosstermBackend;
|
||||
@@ -23,6 +22,7 @@ mod app;
|
||||
mod event;
|
||||
mod handlers;
|
||||
mod logos;
|
||||
mod models;
|
||||
mod network;
|
||||
mod ui;
|
||||
mod utils;
|
||||
|
||||
@@ -4,7 +4,15 @@ use std::fmt::{Display, Formatter};
|
||||
use serde::Deserialize;
|
||||
use tui::widgets::TableState;
|
||||
|
||||
use crate::app::Route;
|
||||
use crate::app::radarr::ActiveRadarrBlock;
|
||||
|
||||
pub mod radarr_models;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Route {
|
||||
Radarr(ActiveRadarrBlock),
|
||||
Sonarr,
|
||||
}
|
||||
|
||||
pub trait Scrollable {
|
||||
fn scroll_down(&mut self);
|
||||
@@ -161,6 +169,7 @@ impl HorizontallyScrollableText {
|
||||
pub struct TabRoute {
|
||||
pub title: String,
|
||||
pub route: Route,
|
||||
pub help: String,
|
||||
}
|
||||
|
||||
pub struct TabState {
|
||||
@@ -182,6 +191,10 @@ impl TabState {
|
||||
&self.tabs[self.index].route
|
||||
}
|
||||
|
||||
pub fn get_active_tab_help(&self) -> String {
|
||||
self.tabs[self.index].help.clone()
|
||||
}
|
||||
|
||||
pub fn next(&mut self) {
|
||||
self.index = (self.index + 1) % self.tabs.len();
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use derivative::Derivative;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Number;
|
||||
|
||||
use crate::models::HorizontallyScrollableText;
|
||||
|
||||
#[derive(Deserialize, 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)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Movie {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
pub title: String,
|
||||
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 quality_profile_id: Number,
|
||||
pub certification: Option<String>,
|
||||
pub ratings: RatingsList,
|
||||
pub movie_file: Option<MovieFile>,
|
||||
pub collection: Option<Collection>,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug, Clone)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CollectionMovie {
|
||||
pub title: String,
|
||||
pub overview: String,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub year: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub runtime: Number,
|
||||
pub genres: Vec<String>,
|
||||
pub ratings: RatingsList,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Derivative, Clone, Debug)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Collection {
|
||||
pub title: String,
|
||||
pub root_folder_path: Option<String>,
|
||||
pub search_on_add: bool,
|
||||
pub overview: Option<String>,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub quality_profile_id: Number,
|
||||
pub movies: Option<Vec<CollectionMovie>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Derivative, Debug, Clone)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MovieFile {
|
||||
pub relative_path: String,
|
||||
pub path: String,
|
||||
pub date_added: DateTime<Utc>,
|
||||
pub media_info: MediaInfo,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Derivative, Debug, Clone)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MediaInfo {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub audio_bitrate: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub audio_channels: Number,
|
||||
pub audio_codec: Option<String>,
|
||||
pub audio_languages: Option<String>,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub audio_stream_count: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub video_bit_depth: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub video_bitrate: Number,
|
||||
pub video_codec: String,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub video_fps: Number,
|
||||
pub resolution: String,
|
||||
pub run_time: String,
|
||||
pub scan_type: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Debug, Clone)]
|
||||
#[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)]
|
||||
#[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 movie_id: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub size: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub sizeleft: Number,
|
||||
pub output_path: HorizontallyScrollableText,
|
||||
pub indexer: String,
|
||||
pub download_client: String,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct QualityProfile {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MovieHistoryItem {
|
||||
pub source_title: HorizontallyScrollableText,
|
||||
pub quality: QualityHistory,
|
||||
pub languages: Vec<Language>,
|
||||
pub date: DateTime<Utc>,
|
||||
pub event_type: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Language {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct QualityHistory {
|
||||
pub quality: Quality,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Quality {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, PartialEq, Eq, Clone, Debug)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum CreditType {
|
||||
Cast,
|
||||
Crew,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[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,
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
use anyhow::anyhow;
|
||||
use chrono::{DateTime, Utc};
|
||||
use derivative::Derivative;
|
||||
use indoc::formatdoc;
|
||||
use log::{debug, error};
|
||||
use reqwest::RequestBuilder;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Number;
|
||||
use tokio::sync::MutexGuard;
|
||||
|
||||
use crate::app::models::{HorizontallyScrollableText, ScrollableText};
|
||||
use crate::app::{App, RadarrConfig};
|
||||
use crate::models::radarr_models::{
|
||||
Collection, Credit, CreditType, DiskSpace, DownloadsResponse, Movie, MovieHistoryItem,
|
||||
QualityProfile, SystemStatus,
|
||||
};
|
||||
use crate::models::ScrollableText;
|
||||
use crate::network::utils::get_movie_status;
|
||||
use crate::network::{utils, Network, NetworkEvent};
|
||||
use crate::utils::{convert_runtime, convert_to_gb};
|
||||
@@ -51,201 +51,6 @@ impl From<RadarrEvent> for NetworkEvent {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DiskSpace {
|
||||
pub free_space: Number,
|
||||
pub total_space: Number,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct SystemStatus {
|
||||
version: String,
|
||||
start_time: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug, Clone)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Movie {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
pub title: String,
|
||||
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 quality_profile_id: Number,
|
||||
pub certification: Option<String>,
|
||||
pub ratings: RatingsList,
|
||||
pub movie_file: Option<MovieFile>,
|
||||
pub collection: Option<Collection>,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug, Clone)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CollectionMovie {
|
||||
pub title: String,
|
||||
pub overview: String,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub year: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub runtime: Number,
|
||||
pub genres: Vec<String>,
|
||||
pub ratings: RatingsList,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Derivative, Clone, Debug)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Collection {
|
||||
pub title: String,
|
||||
pub root_folder_path: Option<String>,
|
||||
pub search_on_add: bool,
|
||||
pub overview: Option<String>,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub quality_profile_id: Number,
|
||||
pub movies: Option<Vec<CollectionMovie>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Derivative, Debug, Clone)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MovieFile {
|
||||
pub relative_path: String,
|
||||
pub path: String,
|
||||
pub date_added: DateTime<Utc>,
|
||||
pub media_info: MediaInfo,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Derivative, Debug, Clone)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MediaInfo {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub audio_bitrate: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub audio_channels: Number,
|
||||
pub audio_codec: Option<String>,
|
||||
pub audio_languages: Option<String>,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub audio_stream_count: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub video_bit_depth: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub video_bitrate: Number,
|
||||
pub video_codec: String,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub video_fps: Number,
|
||||
pub resolution: String,
|
||||
pub run_time: String,
|
||||
pub scan_type: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Debug, Clone)]
|
||||
#[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)]
|
||||
#[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 movie_id: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub size: Number,
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub sizeleft: Number,
|
||||
pub output_path: HorizontallyScrollableText,
|
||||
pub indexer: String,
|
||||
pub download_client: String,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Deserialize, Debug)]
|
||||
#[derivative(Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct QualityProfile {
|
||||
#[derivative(Default(value = "Number::from(0)"))]
|
||||
pub id: Number,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MovieHistoryItem {
|
||||
pub source_title: HorizontallyScrollableText,
|
||||
pub quality: QualityHistory,
|
||||
pub languages: Vec<Language>,
|
||||
pub date: DateTime<Utc>,
|
||||
pub event_type: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Language {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct QualityHistory {
|
||||
pub quality: Quality,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Quality {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, PartialEq, Eq, Clone, Debug)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum CreditType {
|
||||
Cast,
|
||||
Crew,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[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,
|
||||
}
|
||||
|
||||
impl<'a> Network<'a> {
|
||||
pub async fn handle_radarr_event(&self, radarr_event: RadarrEvent) {
|
||||
match radarr_event {
|
||||
|
||||
@@ -2,7 +2,7 @@ use reqwest::Response;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json::Number;
|
||||
|
||||
use crate::network::radarr_network::DownloadRecord;
|
||||
use crate::models::radarr_models::DownloadRecord;
|
||||
|
||||
pub async fn parse_response<T: DeserializeOwned>(response: Response) -> Result<T, reqwest::Error> {
|
||||
response.json::<T>().await
|
||||
|
||||
+22
-16
@@ -1,4 +1,3 @@
|
||||
use log::debug;
|
||||
use tui::backend::Backend;
|
||||
use tui::layout::{Alignment, Constraint, Rect};
|
||||
use tui::text::{Span, Spans, Text};
|
||||
@@ -10,15 +9,12 @@ use tui::widgets::Tabs;
|
||||
use tui::widgets::{Block, Borders, Wrap};
|
||||
use tui::Frame;
|
||||
|
||||
use crate::app::models::{StatefulTable, TabState};
|
||||
use crate::app::{App, Route};
|
||||
use crate::logos::{
|
||||
BAZARR_LOGO, LIDARR_LOGO, PROWLARR_LOGO, RADARR_LOGO, READARR_LOGO, SONARR_LOGO,
|
||||
};
|
||||
use crate::app::App;
|
||||
use crate::models::{Route, StatefulTable, TabState};
|
||||
use crate::ui::utils::{
|
||||
centered_rect, horizontal_chunks_with_margin, layout_block_top_border, logo_block,
|
||||
style_default_bold, style_failure, style_help, style_highlight, style_primary, style_secondary,
|
||||
style_system_function, title_block, vertical_chunks_with_margin,
|
||||
borderless_block, centered_rect, horizontal_chunks_with_margin, layout_block_top_border,
|
||||
logo_block, style_default_bold, style_failure, style_help, style_highlight, style_primary,
|
||||
style_secondary, style_system_function, title_block, vertical_chunks_with_margin,
|
||||
};
|
||||
|
||||
mod radarr_ui;
|
||||
@@ -65,6 +61,7 @@ pub fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
|
||||
fn draw_header_row<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
|
||||
let chunks =
|
||||
horizontal_chunks_with_margin(vec![Constraint::Length(75), Constraint::Min(0)], area, 1);
|
||||
let help_text = Text::from(app.server_tabs.get_active_tab_help());
|
||||
|
||||
let titles = app
|
||||
.server_tabs
|
||||
@@ -76,19 +73,17 @@ fn draw_header_row<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
|
||||
.block(logo_block())
|
||||
.highlight_style(style_secondary())
|
||||
.select(app.server_tabs.index);
|
||||
let help = Paragraph::new(Text::from(
|
||||
"<↑↓> scroll | <enter> select | <tab> change servarr | <?> help ",
|
||||
))
|
||||
.block(Block::default())
|
||||
.style(style_help())
|
||||
.alignment(Alignment::Right);
|
||||
let help = Paragraph::new(help_text)
|
||||
.block(borderless_block())
|
||||
.style(style_help())
|
||||
.alignment(Alignment::Right);
|
||||
|
||||
f.render_widget(tabs, area);
|
||||
f.render_widget(help, chunks[1]);
|
||||
}
|
||||
|
||||
fn draw_error<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
|
||||
let block = Block::default()
|
||||
let block = borderless_block()
|
||||
.title("Error | <esc> to close")
|
||||
.style(style_failure())
|
||||
.borders(Borders::ALL);
|
||||
@@ -169,7 +164,14 @@ fn draw_tabs<'a, B: Backend>(
|
||||
) -> (Rect, Block<'a>) {
|
||||
let chunks =
|
||||
vertical_chunks_with_margin(vec![Constraint::Length(2), Constraint::Min(0)], area, 1);
|
||||
let horizontal_chunks = horizontal_chunks_with_margin(
|
||||
vec![Constraint::Percentage(10), Constraint::Min(0)],
|
||||
area,
|
||||
1,
|
||||
);
|
||||
let block = title_block(title);
|
||||
let mut help_text = Text::from(tab_state.get_active_tab_help());
|
||||
help_text.patch_style(style_help());
|
||||
|
||||
let titles = tab_state
|
||||
.tabs
|
||||
@@ -180,8 +182,12 @@ fn draw_tabs<'a, B: Backend>(
|
||||
.block(block)
|
||||
.highlight_style(style_secondary())
|
||||
.select(tab_state.index);
|
||||
let help = Paragraph::new(help_text)
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Right);
|
||||
|
||||
f.render_widget(tabs, area);
|
||||
f.render_widget(help, horizontal_chunks[1]);
|
||||
|
||||
(chunks[1], layout_block_top_border())
|
||||
}
|
||||
|
||||
+39
-17
@@ -10,14 +10,15 @@ use tui::widgets::{Block, Cell, Paragraph, Row, Wrap};
|
||||
use tui::Frame;
|
||||
|
||||
use crate::app::radarr::{ActiveRadarrBlock, RadarrData};
|
||||
use crate::app::{App, Route};
|
||||
use crate::app::App;
|
||||
use crate::logos::RADARR_LOGO;
|
||||
use crate::network::radarr_network::{Credit, DiskSpace, DownloadRecord, Movie, MovieHistoryItem};
|
||||
use crate::models::radarr_models::{Credit, DiskSpace, DownloadRecord, Movie, MovieHistoryItem};
|
||||
use crate::models::Route;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, horizontal_chunks, layout_block_bottom_border, layout_block_top_border,
|
||||
layout_block_top_border_with_title, layout_block_with_title, line_gauge_with_label,
|
||||
line_gauge_with_title, spans_info_default, spans_info_primary, spans_info_with_style, style_bold,
|
||||
style_default, style_default_bold, style_failure, style_primary, style_success, style_warning,
|
||||
borderless_block, horizontal_chunks, horizontal_chunks_with_margin, layout_block_bottom_border,
|
||||
layout_block_top_border, layout_block_top_border_with_title, layout_block_with_title,
|
||||
line_gauge_with_label, line_gauge_with_title, spans_info_default, spans_info_primary, style_bold,
|
||||
style_default, style_failure, style_help, style_primary, style_success, style_warning,
|
||||
title_block, title_style, vertical_chunks, vertical_chunks_with_margin,
|
||||
};
|
||||
use crate::ui::{
|
||||
@@ -250,7 +251,11 @@ fn draw_collections<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
|
||||
|
||||
fn draw_collection_details<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, content_area: Rect) {
|
||||
let chunks = vertical_chunks_with_margin(
|
||||
vec![Constraint::Length(10), Constraint::Min(0)],
|
||||
vec![
|
||||
Constraint::Percentage(20),
|
||||
Constraint::Percentage(75),
|
||||
Constraint::Percentage(5),
|
||||
],
|
||||
content_area,
|
||||
1,
|
||||
);
|
||||
@@ -262,6 +267,8 @@ fn draw_collection_details<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, cont
|
||||
.get(&collection_selection.quality_profile_id.as_u64().unwrap())
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
let mut help_text = Text::from("<↑↓> scroll table | <enter> show overview | <esc> close");
|
||||
help_text.patch_style(style_help());
|
||||
|
||||
let collection_description = Text::from(vec![
|
||||
spans_info_primary(
|
||||
@@ -285,13 +292,14 @@ fn draw_collection_details<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, cont
|
||||
let description_paragraph = Paragraph::new(collection_description)
|
||||
.block(borderless_block())
|
||||
.wrap(Wrap { trim: false });
|
||||
let help_paragraph = Paragraph::new(help_text)
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Center);
|
||||
|
||||
f.render_widget(
|
||||
layout_block_with_title(title_style(&collection_selection.title)),
|
||||
content_area,
|
||||
);
|
||||
f.render_widget(title_block(&collection_selection.title), content_area);
|
||||
|
||||
f.render_widget(description_paragraph, chunks[0]);
|
||||
f.render_widget(help_paragraph, chunks[2]);
|
||||
|
||||
draw_table(
|
||||
f,
|
||||
@@ -400,6 +408,14 @@ fn draw_movie_info<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
|
||||
}
|
||||
|
||||
fn draw_movie_overview<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, content_area: Rect) {
|
||||
let title_block = title_block("Overview");
|
||||
f.render_widget(title_block, content_area);
|
||||
|
||||
let chunks = vertical_chunks_with_margin(
|
||||
vec![Constraint::Percentage(95), Constraint::Percentage(5)],
|
||||
content_area,
|
||||
1,
|
||||
);
|
||||
let mut overview = Text::from(
|
||||
app
|
||||
.data
|
||||
@@ -409,12 +425,18 @@ fn draw_movie_overview<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, content_
|
||||
.overview,
|
||||
);
|
||||
overview.patch_style(style_default());
|
||||
let mut help_text = Text::from("<esc> close");
|
||||
help_text.patch_style(style_help());
|
||||
|
||||
let paragraph = Paragraph::new(overview)
|
||||
.block(layout_block_with_title(title_style("Overview")))
|
||||
.block(borderless_block())
|
||||
.wrap(Wrap { trim: false });
|
||||
let help_paragraph = Paragraph::new(help_text)
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Center);
|
||||
|
||||
f.render_widget(paragraph, content_area);
|
||||
f.render_widget(paragraph, chunks[0]);
|
||||
f.render_widget(help_paragraph, chunks[1]);
|
||||
}
|
||||
|
||||
fn draw_movie_details<B: Backend>(
|
||||
@@ -678,7 +700,7 @@ fn draw_stats_context<B: Backend>(f: &mut Frame<'_, B>, app: &App, area: Rect) {
|
||||
"Radarr Version: {}",
|
||||
app.data.radarr_data.version
|
||||
)))
|
||||
.block(Block::default());
|
||||
.block(borderless_block());
|
||||
|
||||
let uptime = Utc::now().sub(start_time.to_owned());
|
||||
let days = uptime.num_days();
|
||||
@@ -698,15 +720,15 @@ fn draw_stats_context<B: Backend>(f: &mut Frame<'_, B>, app: &App, area: Rect) {
|
||||
seconds,
|
||||
width = 2
|
||||
)))
|
||||
.block(Block::default());
|
||||
.block(borderless_block());
|
||||
|
||||
let mut logo_text = Text::from(RADARR_LOGO);
|
||||
logo_text.patch_style(Style::default().fg(Color::LightYellow));
|
||||
let logo = Paragraph::new(logo_text)
|
||||
.block(Block::default())
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Center);
|
||||
let storage =
|
||||
Paragraph::new(Text::from("Storage:")).block(Block::default().style(style_bold()));
|
||||
Paragraph::new(Text::from("Storage:")).block(borderless_block().style(style_bold()));
|
||||
|
||||
f.render_widget(logo, chunks[0]);
|
||||
f.render_widget(version_paragraph, chunks[1]);
|
||||
|
||||
Reference in New Issue
Block a user