Added collections support

This commit is contained in:
2023-08-08 10:50:04 -06:00
parent 43e35da49f
commit ff6e392af1
6 changed files with 565 additions and 36 deletions
+1 -1
View File
@@ -55,7 +55,7 @@ impl App {
}
}
pub async fn dispatch(&mut self, action: NetworkEvent) {
pub async fn dispatch_network_event(&mut self, action: NetworkEvent) {
if let Some(network_tx) = &self.network_tx {
if let Err(e) = network_tx.send(action).await {
self.is_loading = false;
+86 -13
View File
@@ -7,7 +7,8 @@ use strum::EnumIter;
use crate::app::models::{ScrollableText, StatefulTable, TabRoute, TabState};
use crate::app::App;
use crate::network::radarr_network::{
Credit, DiskSpace, DownloadRecord, Movie, MovieHistoryItem, RadarrEvent,
Collection, CollectionMovie, Credit, DiskSpace, DownloadRecord, Movie, MovieHistoryItem,
RadarrEvent,
};
pub struct RadarrData {
@@ -18,15 +19,27 @@ pub struct RadarrData {
pub downloads: StatefulTable<DownloadRecord>,
pub quality_profile_map: HashMap<u64, String>,
pub movie_details: ScrollableText,
pub file_details: String,
pub audio_details: String,
pub video_details: String,
pub movie_history: StatefulTable<MovieHistoryItem>,
pub movie_cast: StatefulTable<Credit>,
pub movie_crew: StatefulTable<Credit>,
pub collections: StatefulTable<Collection>,
pub collection_movies: StatefulTable<CollectionMovie>,
pub main_tabs: TabState,
pub movie_info_tabs: TabState,
}
impl RadarrData {
pub fn reset_movie_collection_table(&mut self) {
self.collection_movies = StatefulTable::default();
}
pub fn reset_movie_info_tabs(&mut self) {
self.file_details = String::default();
self.audio_details = String::default();
self.video_details = String::default();
self.movie_details = ScrollableText::default();
self.movie_history = StatefulTable::default();
self.movie_cast = StatefulTable::default();
@@ -48,10 +61,15 @@ impl Default for RadarrData {
movies: StatefulTable::default(),
downloads: StatefulTable::default(),
quality_profile_map: HashMap::default(),
file_details: String::default(),
audio_details: String::default(),
video_details: String::default(),
movie_details: ScrollableText::default(),
movie_history: StatefulTable::default(),
movie_cast: StatefulTable::default(),
movie_crew: StatefulTable::default(),
collections: StatefulTable::default(),
collection_movies: StatefulTable::default(),
main_tabs: TabState::new(vec![
TabRoute {
title: "Library".to_owned(),
@@ -61,6 +79,10 @@ impl Default for RadarrData {
title: "Downloads".to_owned(),
route: ActiveRadarrBlock::Downloads.into(),
},
TabRoute {
title: "Collections".to_owned(),
route: ActiveRadarrBlock::Collections.into(),
},
]),
movie_info_tabs: TabState::new(vec![
TabRoute {
@@ -71,6 +93,10 @@ impl Default for RadarrData {
title: "History".to_owned(),
route: ActiveRadarrBlock::MovieHistory.into(),
},
TabRoute {
title: "File".to_owned(),
route: ActiveRadarrBlock::FileInfo.into(),
},
TabRoute {
title: "Cast".to_owned(),
route: ActiveRadarrBlock::Cast.into(),
@@ -89,9 +115,11 @@ pub enum ActiveRadarrBlock {
AddMovie,
Calendar,
Collections,
CollectionDetails,
Cast,
Crew,
Events,
FileInfo,
Logs,
Movies,
MovieDetails,
@@ -100,30 +128,57 @@ pub enum ActiveRadarrBlock {
SearchMovie,
SortOptions,
Tasks,
ViewMovieOverview,
}
impl App {
pub(super) async fn dispatch_by_radarr_block(&mut self, active_radarr_block: &ActiveRadarrBlock) {
match active_radarr_block {
ActiveRadarrBlock::Downloads => self.dispatch(RadarrEvent::GetDownloads.into()).await,
ActiveRadarrBlock::Movies => {
self.dispatch(RadarrEvent::GetMovies.into()).await;
self.dispatch(RadarrEvent::GetDownloads.into()).await;
}
ActiveRadarrBlock::MovieDetails => {
ActiveRadarrBlock::Collections => {
self.is_loading = true;
self.dispatch(RadarrEvent::GetMovieDetails.into()).await;
self
.dispatch_network_event(RadarrEvent::GetCollections.into())
.await
}
ActiveRadarrBlock::CollectionDetails => {
self.is_loading = true;
self.populate_movie_collection_table().await;
self.is_loading = false;
}
ActiveRadarrBlock::Downloads => {
self.is_loading = true;
self
.dispatch_network_event(RadarrEvent::GetDownloads.into())
.await
}
ActiveRadarrBlock::Movies => {
self
.dispatch_network_event(RadarrEvent::GetMovies.into())
.await;
self
.dispatch_network_event(RadarrEvent::GetDownloads.into())
.await;
}
ActiveRadarrBlock::MovieDetails | ActiveRadarrBlock::FileInfo => {
self.is_loading = true;
self
.dispatch_network_event(RadarrEvent::GetMovieDetails.into())
.await;
}
ActiveRadarrBlock::MovieHistory => {
self.is_loading = true;
self.dispatch(RadarrEvent::GetMovieHistory.into()).await;
self
.dispatch_network_event(RadarrEvent::GetMovieHistory.into())
.await;
}
ActiveRadarrBlock::Cast | ActiveRadarrBlock::Crew => {
if self.data.radarr_data.movie_cast.items.is_empty()
|| self.data.radarr_data.movie_crew.items.is_empty()
{
self.is_loading = true;
self.dispatch(RadarrEvent::GetMovieCredits.into()).await;
self
.dispatch_network_event(RadarrEvent::GetMovieCredits.into())
.await;
}
}
_ => (),
@@ -138,9 +193,15 @@ impl App {
is_first_render: bool,
) {
if is_first_render {
self.dispatch(RadarrEvent::GetQualityProfiles.into()).await;
self.dispatch(RadarrEvent::GetOverview.into()).await;
self.dispatch(RadarrEvent::GetStatus.into()).await;
self
.dispatch_network_event(RadarrEvent::GetQualityProfiles.into())
.await;
self
.dispatch_network_event(RadarrEvent::GetOverview.into())
.await;
self
.dispatch_network_event(RadarrEvent::GetStatus.into())
.await;
self.dispatch_by_radarr_block(&active_radarr_block).await;
}
@@ -154,4 +215,16 @@ impl App {
self.dispatch_by_radarr_block(&active_radarr_block).await;
}
}
async fn populate_movie_collection_table(&mut self) {
self.data.radarr_data.collection_movies.set_items(
self
.data
.radarr_data
.collections
.current_selection_clone()
.movies
.unwrap_or_default(),
);
}
}
+34 -13
View File
@@ -1,5 +1,5 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::models::{Scrollable, ScrollableText, StatefulTable};
use crate::app::models::Scrollable;
use crate::app::radarr::ActiveRadarrBlock;
use crate::handlers::handle_clear_errors;
use crate::{App, Key};
@@ -23,21 +23,26 @@ pub async fn handle_radarr_key_events(
async fn handle_tab_action(key: Key, app: &mut App, active_radarr_block: ActiveRadarrBlock) {
match active_radarr_block {
ActiveRadarrBlock::Movies | ActiveRadarrBlock::Downloads => match key {
_ if key == DEFAULT_KEYBINDINGS.left.key => {
app.data.radarr_data.main_tabs.previous();
app
.pop_and_push_navigation_stack(app.data.radarr_data.main_tabs.get_active_route().clone());
ActiveRadarrBlock::Movies | ActiveRadarrBlock::Downloads | ActiveRadarrBlock::Collections => {
match key {
_ if key == DEFAULT_KEYBINDINGS.left.key => {
app.data.radarr_data.main_tabs.previous();
app.pop_and_push_navigation_stack(
app.data.radarr_data.main_tabs.get_active_route().clone(),
);
}
_ if key == DEFAULT_KEYBINDINGS.right.key => {
app.data.radarr_data.main_tabs.next();
app.pop_and_push_navigation_stack(
app.data.radarr_data.main_tabs.get_active_route().clone(),
);
}
_ => (),
}
_ if key == DEFAULT_KEYBINDINGS.right.key => {
app.data.radarr_data.main_tabs.next();
app
.pop_and_push_navigation_stack(app.data.radarr_data.main_tabs.get_active_route().clone());
}
_ => (),
},
}
ActiveRadarrBlock::MovieDetails
| ActiveRadarrBlock::MovieHistory
| ActiveRadarrBlock::FileInfo
| ActiveRadarrBlock::Cast
| ActiveRadarrBlock::Crew => match key {
_ if key == DEFAULT_KEYBINDINGS.left.key => {
@@ -70,6 +75,8 @@ async fn handle_tab_action(key: Key, app: &mut App, active_radarr_block: ActiveR
async fn handle_scroll_up(app: &mut App, active_radarr_block: ActiveRadarrBlock) {
match active_radarr_block {
ActiveRadarrBlock::Collections => app.data.radarr_data.collections.scroll_up(),
ActiveRadarrBlock::CollectionDetails => app.data.radarr_data.collection_movies.scroll_up(),
ActiveRadarrBlock::Movies => app.data.radarr_data.movies.scroll_up(),
ActiveRadarrBlock::MovieDetails => app.data.radarr_data.movie_details.scroll_up(),
ActiveRadarrBlock::MovieHistory => app.data.radarr_data.movie_history.scroll_up(),
@@ -82,6 +89,8 @@ async fn handle_scroll_up(app: &mut App, active_radarr_block: ActiveRadarrBlock)
async fn handle_scroll_down(app: &mut App, active_radarr_block: ActiveRadarrBlock) {
match active_radarr_block {
ActiveRadarrBlock::Collections => app.data.radarr_data.collections.scroll_down(),
ActiveRadarrBlock::CollectionDetails => app.data.radarr_data.collection_movies.scroll_down(),
ActiveRadarrBlock::Movies => app.data.radarr_data.movies.scroll_down(),
ActiveRadarrBlock::MovieDetails => app.data.radarr_data.movie_details.scroll_down(),
ActiveRadarrBlock::MovieHistory => app.data.radarr_data.movie_history.scroll_down(),
@@ -95,6 +104,12 @@ async fn handle_scroll_down(app: &mut App, active_radarr_block: ActiveRadarrBloc
async fn handle_submit(app: &mut App, active_radarr_block: ActiveRadarrBlock) {
match active_radarr_block {
ActiveRadarrBlock::Movies => app.push_navigation_stack(ActiveRadarrBlock::MovieDetails.into()),
ActiveRadarrBlock::Collections => {
app.push_navigation_stack(ActiveRadarrBlock::CollectionDetails.into())
}
ActiveRadarrBlock::CollectionDetails => {
app.push_navigation_stack(ActiveRadarrBlock::ViewMovieOverview.into())
}
_ => (),
}
}
@@ -103,11 +118,17 @@ async fn handle_esc(app: &mut App, active_radarr_block: ActiveRadarrBlock) {
match active_radarr_block {
ActiveRadarrBlock::MovieDetails
| ActiveRadarrBlock::MovieHistory
| ActiveRadarrBlock::FileInfo
| ActiveRadarrBlock::Cast
| ActiveRadarrBlock::Crew => {
app.pop_navigation_stack();
app.data.radarr_data.reset_movie_info_tabs();
}
ActiveRadarrBlock::CollectionDetails => {
app.pop_navigation_stack();
app.data.radarr_data.reset_movie_collection_table();
}
ActiveRadarrBlock::ViewMovieOverview => app.pop_navigation_stack(),
_ => handle_clear_errors(app).await,
}
}
+129 -1
View File
@@ -17,6 +17,7 @@ use crate::utils::{convert_runtime, convert_to_gb};
#[derive(Debug, Eq, PartialEq)]
pub enum RadarrEvent {
GetCollections,
GetDownloads,
GetMovies,
GetMovieCredits,
@@ -31,6 +32,7 @@ pub enum RadarrEvent {
impl RadarrEvent {
const fn resource(self) -> &'static str {
match self {
RadarrEvent::GetCollections => "/collection",
RadarrEvent::GetDownloads => "/queue",
RadarrEvent::GetMovies | RadarrEvent::GetMovieDetails => "/movie",
RadarrEvent::GetMovieCredits => "/credit",
@@ -88,6 +90,69 @@ pub struct Movie {
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)]
@@ -184,6 +249,11 @@ pub struct Credit {
impl<'a> Network<'a> {
pub async fn handle_radarr_event(&self, radarr_event: RadarrEvent) {
match radarr_event {
RadarrEvent::GetCollections => {
self
.get_collections(RadarrEvent::GetCollections.resource())
.await
}
RadarrEvent::HealthCheck => {
self
.get_healthcheck(RadarrEvent::HealthCheck.resource())
@@ -275,6 +345,8 @@ impl<'a> Network<'a> {
genres,
runtime,
ratings,
movie_file,
collection,
..
} = movie_response;
let (hours, minutes) = convert_runtime(runtime.as_u64().unwrap());
@@ -317,11 +389,13 @@ impl<'a> Network<'a> {
};
let status = get_movie_status(has_file, &app.data.radarr_data.downloads.items, id);
let collection = collection.unwrap_or_default();
app.data.radarr_data.movie_details = ScrollableText::with_string(formatdoc!(
"Title: {}
Year: {}
Runtime: {}h {}m
Collection: {}
Status: {}
Description: {}
TMDB: {}
@@ -336,6 +410,7 @@ impl<'a> Network<'a> {
year,
hours,
minutes,
collection.title,
status,
overview,
tmdb_rating,
@@ -346,7 +421,52 @@ impl<'a> Network<'a> {
path,
studio,
genres.join(", ")
))
));
if let Some(file) = movie_file {
app.data.radarr_data.file_details = formatdoc!(
"Relative Path: {}
Absolute Path: {}
Size: {:.2} GB
Date Added: {}",
file.relative_path,
file.path,
size,
file.date_added
);
let media_info = file.media_info;
app.data.radarr_data.audio_details = formatdoc!(
"Bitrate: {}
Channels: {:.1}
Codec: {}
Languages: {}
Stream Count: {}",
media_info.audio_bitrate.as_u64().unwrap(),
media_info.audio_channels.as_f64().unwrap(),
media_info.audio_codec.unwrap_or_default(),
media_info.audio_languages.unwrap_or_default(),
media_info.audio_stream_count.as_u64().unwrap()
);
app.data.radarr_data.video_details = formatdoc!(
"Bit Depth: {}
Bitrate: {}
Codec: {}
FPS: {}
Resolution: {}
Scan Type: {}
Runtime: {}",
media_info.video_bit_depth.as_u64().unwrap(),
media_info.video_bitrate.as_u64().unwrap(),
media_info.video_codec,
media_info.video_fps.as_f64().unwrap(),
media_info.resolution,
media_info.scan_type,
media_info.run_time
);
}
},
)
.await;
@@ -369,6 +489,14 @@ impl<'a> Network<'a> {
.await;
}
async fn get_collections(&self, resource: &str) {
self
.handle_get_request::<Vec<Collection>>(resource, |collections_vec, mut app| {
app.data.radarr_data.collections.set_items(collections_vec);
})
.await;
}
async fn get_downloads(&self, resource: &str) {
self
.handle_get_request::<DownloadsResponse>(resource, |queue_response, mut app| {
+282 -8
View File
@@ -5,7 +5,7 @@ use chrono::{Duration, Utc};
use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Rect};
use tui::style::{Color, Style};
use tui::text::Text;
use tui::text::{Spans, Text};
use tui::widgets::{Block, Cell, Paragraph, Row, Wrap};
use tui::Frame;
@@ -14,26 +14,41 @@ use crate::app::{App, Route};
use crate::logos::RADARR_LOGO;
use crate::network::radarr_network::{Credit, DiskSpace, DownloadRecord, Movie, MovieHistoryItem};
use crate::ui::utils::{
horizontal_chunks, layout_block_top_border, line_gauge_with_label, line_gauge_with_title,
style_bold, style_failure, style_success, style_warning, title_block,
vertical_chunks_with_margin,
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,
title_block, title_style, vertical_chunks, vertical_chunks_with_margin,
};
use crate::ui::{
draw_large_popup_over, draw_small_popup_over, draw_table, draw_tabs, loading, TableProps,
};
use crate::ui::{draw_large_popup_over, draw_table, draw_tabs, loading, TableProps};
use crate::utils::{convert_runtime, convert_to_gb};
pub(super) fn draw_radarr_ui<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
let (content_rect, _) = draw_tabs(f, area, " Movies ", &app.data.radarr_data.main_tabs);
if let Route::Radarr(active_radarr_block) = app.get_current_route() {
if let Route::Radarr(active_radarr_block) = app.get_current_route().clone() {
match active_radarr_block {
ActiveRadarrBlock::Movies => draw_library(f, app, content_rect),
ActiveRadarrBlock::Downloads => draw_downloads(f, app, content_rect),
ActiveRadarrBlock::Collections => draw_collections(f, app, content_rect),
ActiveRadarrBlock::MovieDetails
| ActiveRadarrBlock::MovieHistory
| ActiveRadarrBlock::FileInfo
| ActiveRadarrBlock::Cast
| ActiveRadarrBlock::Crew => {
draw_large_popup_over(f, app, content_rect, draw_library, draw_movie_info)
}
ActiveRadarrBlock::CollectionDetails | ActiveRadarrBlock::ViewMovieOverview => {
draw_large_popup_over(
f,
app,
content_rect,
draw_collections,
draw_collection_details_popup,
)
}
_ => (),
}
}
@@ -189,12 +204,183 @@ fn draw_downloads<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
Cell::from(indexer.to_owned()),
Cell::from(download_client.to_owned()),
])
.style(style_success())
.style(style_primary())
},
app.is_loading,
);
}
fn draw_collections<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
let quality_profile_map = &app.data.radarr_data.quality_profile_map;
draw_table(
f,
area,
layout_block_top_border(),
TableProps {
content: &mut app.data.radarr_data.collections,
table_headers: vec![
"Collection",
"Search on Add?",
"Number of Movies",
"Root Folder Path",
"Quality Profile",
],
constraints: iter::repeat(Constraint::Ratio(1, 5)).take(5).collect(),
},
|collection| {
let number_of_movies = collection.movies.clone().unwrap_or_default().len();
Row::new(vec![
Cell::from(collection.title.to_owned()),
Cell::from(collection.search_on_add.to_string()),
Cell::from(number_of_movies.to_string()),
Cell::from(collection.root_folder_path.clone().unwrap_or_default()),
Cell::from(
quality_profile_map
.get(&collection.quality_profile_id.as_u64().unwrap())
.unwrap()
.to_owned(),
),
])
.style(style_primary())
},
app.is_loading,
);
}
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)],
content_area,
1,
);
let collection_selection = app.data.radarr_data.collections.current_selection();
let quality_profile = app
.data
.radarr_data
.quality_profile_map
.get(&collection_selection.quality_profile_id.as_u64().unwrap())
.unwrap()
.to_owned();
let collection_description = Text::from(vec![
spans_info_primary(
"Overview: ".to_owned(),
collection_selection.overview.clone().unwrap_or_default(),
),
spans_info_primary(
"Root Folder Path: ".to_owned(),
collection_selection
.root_folder_path
.clone()
.unwrap_or_default(),
),
spans_info_primary(
"Search on Add: ".to_owned(),
collection_selection.search_on_add.to_string(),
),
spans_info_primary("Quality Profile: ".to_owned(), quality_profile),
]);
let description_paragraph = Paragraph::new(collection_description)
.block(borderless_block())
.wrap(Wrap { trim: false });
f.render_widget(
layout_block_with_title(title_style(&collection_selection.title)),
content_area,
);
f.render_widget(description_paragraph, chunks[0]);
draw_table(
f,
chunks[1],
layout_block_top_border_with_title(title_style("Movies")),
TableProps {
content: &mut app.data.radarr_data.collection_movies,
table_headers: vec![
"Title",
"Year",
"Runtime",
"IMDB Rating",
"Rotten Tomatoes Rating",
"Genres",
],
constraints: vec![
Constraint::Percentage(20),
Constraint::Percentage(8),
Constraint::Percentage(10),
Constraint::Percentage(10),
Constraint::Percentage(18),
Constraint::Percentage(30),
],
},
|movie| {
let (hours, minutes) = convert_runtime(movie.runtime.as_u64().unwrap());
let imdb_rating = movie
.ratings
.imdb
.clone()
.unwrap_or_default()
.value
.as_f64()
.unwrap();
let rotten_tomatoes_rating = movie
.ratings
.rotten_tomatoes
.clone()
.unwrap_or_default()
.value
.as_u64()
.unwrap();
let imdb_rating = if imdb_rating == 0.0 {
String::default()
} else {
format!("{:.1}", imdb_rating)
};
let rotten_tomatoes_rating = if rotten_tomatoes_rating == 0 {
String::default()
} else {
format!("{}%", rotten_tomatoes_rating)
};
Row::new(vec![
Cell::from(movie.title.to_owned()),
Cell::from(movie.year.as_u64().unwrap().to_string()),
Cell::from(format!("{}h {}m", hours, minutes)),
Cell::from(imdb_rating),
Cell::from(rotten_tomatoes_rating),
Cell::from(movie.genres.join(", ")),
])
.style(style_primary())
},
app.is_loading,
);
}
fn draw_collection_details_popup<B: Backend>(
f: &mut Frame<'_, B>,
app: &mut App,
content_area: Rect,
) {
if let Route::Radarr(active_radarr_block) = app.get_current_route() {
match active_radarr_block {
ActiveRadarrBlock::ViewMovieOverview => {
draw_small_popup_over(
f,
app,
content_area,
draw_collection_details,
draw_movie_overview,
);
}
ActiveRadarrBlock::CollectionDetails => draw_collection_details(f, app, content_area),
_ => (),
}
}
}
fn draw_movie_info<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
let (content_area, block) =
draw_tabs(f, area, "Movie Info", &app.data.radarr_data.movie_info_tabs);
@@ -203,6 +389,7 @@ fn draw_movie_info<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
app.data.radarr_data.movie_info_tabs.get_active_route()
{
match active_radarr_block {
ActiveRadarrBlock::FileInfo => draw_file_info(f, app, content_area, block),
ActiveRadarrBlock::MovieDetails => draw_movie_details(f, app, content_area, block),
ActiveRadarrBlock::MovieHistory => draw_movie_history(f, app, content_area, block),
ActiveRadarrBlock::Cast => draw_movie_cast(f, app, content_area, block),
@@ -212,6 +399,24 @@ 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 mut overview = Text::from(
app
.data
.radarr_data
.collection_movies
.current_selection_clone()
.overview,
);
overview.patch_style(style_default());
let paragraph = Paragraph::new(overview)
.block(layout_block_with_title(title_style("Overview")))
.wrap(Wrap { trim: false });
f.render_widget(paragraph, content_area);
}
fn draw_movie_details<B: Backend>(
f: &mut Frame<'_, B>,
app: &App,
@@ -231,7 +436,21 @@ fn draw_movie_details<B: Backend>(
.unwrap()
.split(": ")
.collect::<Vec<&str>>()[1];
let mut text = Text::from(movie_details);
let mut text = Text::from(
app
.data
.radarr_data
.movie_details
.items
.iter()
.map(|line| {
let split = line.split(':').collect::<Vec<&str>>();
let title = format!("{}:", split[0]);
spans_info_default(title, split[1].to_owned())
})
.collect::<Vec<Spans>>(),
);
text.patch_style(determine_style_from_download_status(download_status));
let paragraph = Paragraph::new(text)
@@ -245,6 +464,61 @@ fn draw_movie_details<B: Backend>(
}
}
fn draw_file_info<B: Backend>(f: &mut Frame<'_, B>, app: &App, content_area: Rect, block: Block) {
let file_info = app.data.radarr_data.file_details.to_owned();
if !file_info.is_empty() {
let audio_details = app.data.radarr_data.audio_details.to_owned();
let video_details = app.data.radarr_data.video_details.to_owned();
let chunks = vertical_chunks(
vec![
Constraint::Length(1),
Constraint::Length(5),
Constraint::Length(1),
Constraint::Length(6),
Constraint::Length(1),
Constraint::Length(7),
],
content_area,
);
let mut file_details_title = Text::from("File Details");
let mut audio_details_title = Text::from("Audio Details");
let mut video_details_title = Text::from("Video Details");
file_details_title.patch_style(style_bold());
audio_details_title.patch_style(style_bold());
video_details_title.patch_style(style_bold());
let file_details_title_paragraph = Paragraph::new(file_details_title).block(borderless_block());
let audio_details_title_paragraph =
Paragraph::new(audio_details_title).block(borderless_block());
let video_details_title_paragraph =
Paragraph::new(video_details_title).block(borderless_block());
let file_details = Text::from(file_info);
let audio_details = Text::from(audio_details);
let video_details = Text::from(video_details);
let file_details_paragraph = Paragraph::new(file_details)
.block(layout_block_bottom_border())
.wrap(Wrap { trim: false });
let audio_details_paragraph = Paragraph::new(audio_details)
.block(layout_block_bottom_border())
.wrap(Wrap { trim: false });
let video_details_paragraph = Paragraph::new(video_details)
.block(borderless_block())
.wrap(Wrap { trim: false });
f.render_widget(file_details_title_paragraph, chunks[0]);
f.render_widget(file_details_paragraph, chunks[1]);
f.render_widget(audio_details_title_paragraph, chunks[2]);
f.render_widget(audio_details_paragraph, chunks[3]);
f.render_widget(video_details_title_paragraph, chunks[4]);
f.render_widget(video_details_paragraph, chunks[5]);
} else {
loading(f, block, content_area, app.is_loading);
}
}
fn draw_movie_history<B: Backend>(
f: &mut Frame<'_, B>,
app: &mut App,
+33
View File
@@ -66,6 +66,39 @@ pub fn layout_block_top_border<'a>() -> Block<'a> {
Block::default().borders(Borders::TOP)
}
pub fn layout_block_bottom_border<'a>() -> Block<'a> {
Block::default().borders(Borders::BOTTOM)
}
pub fn borderless_block<'a>() -> Block<'a> {
Block::default()
}
pub fn spans_info_with_style<'a>(
title: String,
content: String,
title_style: Style,
content_style: Style,
) -> Spans<'a> {
Spans::from(vec![
Span::styled(title, title_style),
Span::styled(content, content_style),
])
}
pub fn spans_info_default<'a>(title: String, content: String) -> Spans<'a> {
spans_info_with_style(title, content, style_bold(), style_default())
}
pub fn spans_info_primary<'a>(title: String, content: String) -> Spans<'a> {
spans_info_with_style(
title,
content,
style_primary().add_modifier(Modifier::BOLD),
style_default(),
)
}
pub fn style_bold() -> Style {
Style::default().add_modifier(Modifier::BOLD)
}