feat(network): Added get quality profiles and get episode details events for Sonarr
This commit is contained in:
@@ -1,19 +1,22 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use indoc::formatdoc;
|
||||
use log::info;
|
||||
use managarr_tree_widget::TreeItem;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::{
|
||||
models::{
|
||||
servarr_data::sonarr::sonarr_data::ActiveSonarrBlock,
|
||||
servarr_data::sonarr::{modals::EpisodeDetailsModal, sonarr_data::ActiveSonarrBlock},
|
||||
sonarr_models::{
|
||||
BlocklistResponse, Episode, LogResponse, Series, SonarrSerdeable, SystemStatus,
|
||||
BlocklistResponse, DownloadRecord, Episode, LogResponse, QualityProfile, Series,
|
||||
SonarrSerdeable, SystemStatus,
|
||||
},
|
||||
HorizontallyScrollableText, Route, Scrollable,
|
||||
HorizontallyScrollableText, Route, Scrollable, ScrollableText,
|
||||
},
|
||||
network::RequestMethod,
|
||||
utils::convert_to_gb,
|
||||
};
|
||||
|
||||
use super::{Network, NetworkEvent, NetworkResource};
|
||||
@@ -26,8 +29,10 @@ pub enum SonarrEvent {
|
||||
ClearBlocklist,
|
||||
DeleteBlocklistItem(Option<i64>),
|
||||
GetBlocklist,
|
||||
GetEpisodeDetails(Option<i64>),
|
||||
GetEpisodes(Option<i64>),
|
||||
GetLogs(Option<u64>),
|
||||
GetQualityProfiles,
|
||||
GetStatus,
|
||||
HealthCheck,
|
||||
ListSeries,
|
||||
@@ -39,8 +44,9 @@ impl NetworkResource for SonarrEvent {
|
||||
SonarrEvent::ClearBlocklist => "/blocklist/bulk",
|
||||
SonarrEvent::DeleteBlocklistItem(_) => "/blocklist",
|
||||
SonarrEvent::GetBlocklist => "/blocklist?page=1&pageSize=10000",
|
||||
SonarrEvent::GetEpisodes(_) => "/episode",
|
||||
SonarrEvent::GetEpisodes(_) | SonarrEvent::GetEpisodeDetails(_) => "/episode",
|
||||
SonarrEvent::GetLogs(_) => "/log",
|
||||
SonarrEvent::GetQualityProfiles => "/qualityprofile",
|
||||
SonarrEvent::GetStatus => "/system/status",
|
||||
SonarrEvent::HealthCheck => "/health",
|
||||
SonarrEvent::ListSeries => "/series",
|
||||
@@ -73,6 +79,14 @@ impl<'a, 'b> Network<'a, 'b> {
|
||||
.get_episodes(series_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetEpisodeDetails(episode_id) => self
|
||||
.get_episode_details(episode_id)
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetQualityProfiles => self
|
||||
.get_sonarr_quality_profiles()
|
||||
.await
|
||||
.map(SonarrSerdeable::from),
|
||||
SonarrEvent::GetLogs(events) => self
|
||||
.get_sonarr_logs(events)
|
||||
.await
|
||||
@@ -204,6 +218,22 @@ impl<'a, 'b> Network<'a, 'b> {
|
||||
self
|
||||
.handle_request::<(), Vec<Episode>>(request_props, |mut episode_vec, mut app| {
|
||||
episode_vec.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
if !matches!(
|
||||
app.get_current_route(),
|
||||
Route::Sonarr(ActiveSonarrBlock::EpisodesTableSortPrompt, _)
|
||||
) {
|
||||
app
|
||||
.data
|
||||
.sonarr_data
|
||||
.episodes_table
|
||||
.set_items(episode_vec.clone());
|
||||
app
|
||||
.data
|
||||
.sonarr_data
|
||||
.episodes_table
|
||||
.apply_sorting_toggle(false);
|
||||
}
|
||||
|
||||
let mut seasons = BTreeMap::new();
|
||||
|
||||
for episode in episode_vec {
|
||||
@@ -226,7 +256,114 @@ impl<'a, 'b> Network<'a, 'b> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
app.data.sonarr_data.episodes.set_items(tree);
|
||||
app.data.sonarr_data.episodes_tree.set_items(tree);
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_episode_details(&mut self, episode_id: Option<i64>) -> Result<Episode> {
|
||||
info!("Fetching Sonarr episode details");
|
||||
let event = SonarrEvent::GetEpisodeDetails(None);
|
||||
let id = self.extract_episode_id(episode_id).await;
|
||||
|
||||
info!("Fetching episode details for episode with ID: {id}");
|
||||
|
||||
let request_props = self
|
||||
.request_props_from(
|
||||
event,
|
||||
RequestMethod::Get,
|
||||
None::<()>,
|
||||
Some(format!("/{id}")),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), Episode>(request_props, |episode_response, mut app| {
|
||||
let Episode {
|
||||
id,
|
||||
title,
|
||||
air_date_utc,
|
||||
overview,
|
||||
has_file,
|
||||
season_number,
|
||||
episode_number,
|
||||
episode_file,
|
||||
..
|
||||
} = episode_response;
|
||||
let status = get_episode_status(has_file, &app.data.sonarr_data.downloads.items, id);
|
||||
let air_date = if let Some(air_date) = air_date_utc {
|
||||
format!("{air_date}")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let mut episode_details_modal = EpisodeDetailsModal {
|
||||
episode_details: ScrollableText::with_string(formatdoc!(
|
||||
"
|
||||
Title: {}
|
||||
Season: {season_number}
|
||||
Episode Number: {episode_number}
|
||||
Air Date: {air_date}
|
||||
Status: {status}
|
||||
Description: {}",
|
||||
title.unwrap_or_default(),
|
||||
overview.unwrap_or_default(),
|
||||
)),
|
||||
..EpisodeDetailsModal::default()
|
||||
};
|
||||
if let Some(file) = episode_file {
|
||||
let size = convert_to_gb(file.size);
|
||||
episode_details_modal.file_details = formatdoc!(
|
||||
"
|
||||
Relative Path: {}
|
||||
Absolute Path: {}
|
||||
Size: {size:.2} GB
|
||||
Language: {}
|
||||
Date Added: {}",
|
||||
file.relative_path,
|
||||
file.path,
|
||||
file.language.name,
|
||||
file.date_added,
|
||||
);
|
||||
|
||||
if let Some(media_info) = file.media_info {
|
||||
episode_details_modal.audio_details = formatdoc!(
|
||||
"
|
||||
Bitrate: {}
|
||||
Channels: {:.1}
|
||||
Codec: {}
|
||||
Languages: {}
|
||||
Stream Count: {}",
|
||||
media_info.audio_bitrate,
|
||||
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
|
||||
);
|
||||
|
||||
episode_details_modal.video_details = formatdoc!(
|
||||
"
|
||||
Bit Depth: {}
|
||||
Bitrate: {}
|
||||
Codec: {}
|
||||
FPS: {}
|
||||
Resolution: {}
|
||||
Scan Type: {}
|
||||
Runtime: {}
|
||||
Subtitles: {}",
|
||||
media_info.video_bit_depth,
|
||||
media_info.video_bitrate,
|
||||
media_info.video_codec,
|
||||
media_info.video_fps.as_f64().unwrap(),
|
||||
media_info.resolution,
|
||||
media_info.scan_type,
|
||||
media_info.run_time,
|
||||
media_info.subtitles.unwrap_or_default()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
app.data.sonarr_data.episode_details_modal = Some(episode_details_modal);
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -278,6 +415,24 @@ impl<'a, 'b> Network<'a, 'b> {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_sonarr_quality_profiles(&mut self) -> Result<Vec<QualityProfile>> {
|
||||
info!("Fetching Sonarr quality profiles");
|
||||
let event = SonarrEvent::GetQualityProfiles;
|
||||
|
||||
let request_props = self
|
||||
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), Vec<QualityProfile>>(request_props, |quality_profiles, mut app| {
|
||||
app.data.sonarr_data.quality_profile_map = quality_profiles
|
||||
.into_iter()
|
||||
.map(|profile| (profile.id, profile.name))
|
||||
.collect();
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn list_series(&mut self) -> Result<Vec<Series>> {
|
||||
info!("Fetching Sonarr library");
|
||||
let event = SonarrEvent::ListSeries;
|
||||
@@ -332,4 +487,49 @@ impl<'a, 'b> Network<'a, 'b> {
|
||||
};
|
||||
(series_id, format!("seriesId={series_id}"))
|
||||
}
|
||||
|
||||
async fn extract_episode_id(&mut self, episode_id: Option<i64>) -> i64 {
|
||||
let app = self.app.lock().await;
|
||||
|
||||
let episode_id = if let Some(id) = episode_id {
|
||||
id
|
||||
} else if matches!(
|
||||
app.get_current_route(),
|
||||
Route::Sonarr(ActiveSonarrBlock::EpisodesTable, _)
|
||||
) {
|
||||
app.data.sonarr_data.episodes_table.current_selection().id
|
||||
} else {
|
||||
app
|
||||
.data
|
||||
.sonarr_data
|
||||
.episodes_tree
|
||||
.current_selection()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.id
|
||||
};
|
||||
|
||||
episode_id
|
||||
}
|
||||
}
|
||||
|
||||
fn get_episode_status(has_file: bool, downloads_vec: &[DownloadRecord], episode_id: i64) -> String {
|
||||
if !has_file {
|
||||
if let Some(download) = downloads_vec
|
||||
.iter()
|
||||
.find(|&download| download.episode_id == episode_id)
|
||||
{
|
||||
if download.status == "downloading" {
|
||||
return "Downloading".to_owned();
|
||||
}
|
||||
|
||||
if download.status == "completed" {
|
||||
return "Awaiting Import".to_owned();
|
||||
}
|
||||
}
|
||||
|
||||
return "Missing".to_owned();
|
||||
}
|
||||
|
||||
"Downloaded".to_owned()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user