diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/yt/src/cli.rs | 11 | ||||
-rw-r--r-- | crates/yt/src/main.rs | 11 | ||||
-rw-r--r-- | crates/yt/src/select/mod.rs | 2 | ||||
-rw-r--r-- | crates/yt/src/status/mod.rs | 38 | ||||
-rw-r--r-- | crates/yt/src/update/mod.rs | 2 | ||||
-rw-r--r-- | crates/yt/src/videos/display/format_video.rs | 79 | ||||
-rw-r--r-- | crates/yt/src/videos/display/mod.rs | 36 | ||||
-rw-r--r-- | crates/yt/src/videos/mod.rs | 13 |
8 files changed, 144 insertions, 48 deletions
diff --git a/crates/yt/src/cli.rs b/crates/yt/src/cli.rs index cd7ce2f..a19e035 100644 --- a/crates/yt/src/cli.rs +++ b/crates/yt/src/cli.rs @@ -116,7 +116,11 @@ pub(crate) enum Command { }, /// Show, which videos have been selected to be watched (and their cache status) - Status {}, + Status { + /// Which format to use + #[arg(short, long)] + format: Option<String>, + }, /// Show, the configuration options in effect Config {}, @@ -182,6 +186,11 @@ pub(crate) enum VideosCommand { #[arg(action = ArgAction::Append)] search_query: Option<String>, + /// The format string to use. + // TODO(@bpeetz): Encode the default format, as the default string here. <2025-07-04> + #[arg(short, long)] + format: Option<String>, + /// The number of videos to show #[arg(short, long)] limit: Option<usize>, diff --git a/crates/yt/src/main.rs b/crates/yt/src/main.rs index f9f48bf..4b9c4c2 100644 --- a/crates/yt/src/main.rs +++ b/crates/yt/src/main.rs @@ -148,18 +148,19 @@ async fn main() -> Result<()> { VideosCommand::List { search_query, limit, + format, } => { - videos::query(&app, limit, search_query) + videos::query(&app, limit, search_query, format) .await .context("Failed to query videos")?; } - VideosCommand::Info { hash } => { - let video = video_by_hash(&app, &hash.realize(&app).await?).await?; + VideosCommand::Info { hash, format } => { + let video = hash.realize(&app).await?.get_with_app(&app).await?; print!( "{}", &video - .to_info_display(&app) + .to_info_display(&app, format) .await .context("Failed to format video")? ); @@ -226,7 +227,7 @@ async fn main() -> Result<()> { } => watch::watch(Arc::new(app), provide_ipc_socket, headless).await?, Command::Playlist { watch } => watch::playlist::playlist(&app, watch).await?, - Command::Status {} => status::show(&app).await?, + Command::Status { format } => status::show(&app, format).await?, Command::Config {} => status::config(&app)?, Command::Database { command } => match command { diff --git a/crates/yt/src/select/mod.rs b/crates/yt/src/select/mod.rs index 9afe071..76d5ae8 100644 --- a/crates/yt/src/select/mod.rs +++ b/crates/yt/src/select/mod.rs @@ -210,7 +210,7 @@ async fn write_videos_to_file(app: &App, file: &File, videos: &[Video]) -> Resul // Warm-up the cache for the display rendering of the videos. // Otherwise the futures would all try to warm it up at the same time. if let Some(vid) = videos.first() { - drop(vid.to_line_display(app).await?); + drop(vid.to_line_display(app, None).await?); } let mut edit_file = BufWriter::new(file); diff --git a/crates/yt/src/status/mod.rs b/crates/yt/src/status/mod.rs index f9528e2..de706ec 100644 --- a/crates/yt/src/status/mod.rs +++ b/crates/yt/src/status/mod.rs @@ -101,8 +101,39 @@ pub(crate) async fn show(app: &App, format: Option<String>) -> Result<()> { .await .context("Failed to get current cache allocation")?; let cache_usage: Bytes = cache_usage_raw; - println!( - "\ + + if let Some(fmt) = format { + let output = fmt + .replace( + "{picked_videos_len}", + picked_videos_len.to_string().as_str(), + ) + .replace("{watch_videos_len}", watch_videos_len.to_string().as_str()) + .replace( + "{cached_videos_len}", + cached_videos_len.to_string().as_str(), + ) + .replace( + "{watched_videos_len}", + watched_videos_len.to_string().as_str(), + ) + .replace("{watch_rate}", watch_rate.to_string().as_str()) + .replace("{drop_videos_len}", drop_videos_len.to_string().as_str()) + .replace( + "{dropped_videos_len}", + dropped_videos_len.to_string().as_str(), + ) + .replace("{watchtime_status}", watchtime_status.to_string().as_str()) + .replace( + "{subscriptions_len}", + subscriptions_len.to_string().as_str(), + ) + .replace("{cache_usage}", cache_usage.to_string().as_str()); + + print!("{output}"); + } else { + println!( + "\ Picked Videos: {picked_videos_len} Watch Videos: {watch_videos_len} @@ -116,7 +147,8 @@ Dropped Videos: {dropped_videos_len} Subscriptions: {subscriptions_len} Cache usage: {cache_usage}" - ); + ); + } Ok(()) } diff --git a/crates/yt/src/update/mod.rs b/crates/yt/src/update/mod.rs index 2fb1069..809289c 100644 --- a/crates/yt/src/update/mod.rs +++ b/crates/yt/src/update/mod.rs @@ -203,7 +203,7 @@ async fn process_subscription(app: &App, sub: Subscription, entry: InfoJson) -> println!( "{}", &video - .to_line_display(app) + .to_line_display(app, None) .await .with_context(|| format!("Failed to format video: '{}'", video.title))? ); diff --git a/crates/yt/src/videos/display/format_video.rs b/crates/yt/src/videos/display/format_video.rs index 659c3be..80ac5dd 100644 --- a/crates/yt/src/videos/display/format_video.rs +++ b/crates/yt/src/videos/display/format_video.rs @@ -11,12 +11,13 @@ use anyhow::Result; -use crate::{app::App, comments::output::format_text, storage::video_database::Video}; +use crate::{app::App, comments::output::format_text, storage::db::video::Video}; impl Video { pub(crate) async fn to_info_display( &self, app: &App, + format: Option<String>, ) -> Result<String> { let cache_path = self.cache_path_fmt(app); let description = self.description_fmt(); @@ -45,8 +46,30 @@ impl Video { } }; - let string = format!( - "\ + let options = video_options.to_string(); + let options = options.trim(); + let description = format_text(description.to_string().as_str()); + + let string = if let Some(format) = format { + format + .replace("{title}", &title) + .replace("{extractor_hash}", &extractor_hash) + .replace("{cache_path}", &cache_path) + .replace("{duration}", &duration) + .replace("{watched_percentage_fmt}", &watched_percentage_fmt) + .replace("{parent_subscription_name}", &parent_subscription_name) + .replace("{priority}", &priority) + .replace("{publish_date}", &publish_date) + .replace("{status}", &status) + .replace("{last_status_change}", &last_status_change) + .replace("{in_playlist}", &in_playlist) + .replace("{thumbnail_url}", &thumbnail_url) + .replace("{url}", &url) + .replace("{options}", options) + .replace("{description}", &description) + } else { + format!( + "\ {title} ({extractor_hash}) | -> {cache_path} | -> {duration}{watched_percentage_fmt} @@ -56,33 +79,49 @@ impl Video { | -> status: {status} since {last_status_change} ({in_playlist}) | -> {thumbnail_url} | -> {url} -| -> options: {} -{}\n", - video_options.to_string().trim(), - format_text(description.to_string().as_str()) - ); +| -> options: {options} +{description}\n", + ) + }; Ok(string) } - pub async fn to_line_display(&self, app: &App) -> Result<String> { - let f = format!( - "{} {} {} {} {} {}", - self.status_fmt(app), - self.extractor_hash_fmt(app).await?, - self.title_fmt(app), - self.publish_date_fmt(app), - self.parent_subscription_name_fmt(app), - self.duration_fmt(app) - ); + pub(crate) async fn to_line_display( + &self, + app: &App, + format: Option<String>, + ) -> Result<String> { + let status = self.status_fmt(app); + let extractor_hash = self.extractor_hash_fmt(app).await?; + let title = self.title_fmt(app); + let publish_date = self.publish_date_fmt(app); + let parent_subscription_name = self.parent_subscription_name_fmt(app); + let duration = self.duration_fmt(app); + let url = self.url_fmt(app); + + let f = if let Some(format) = format { + format + .replace("{status}", &status) + .replace("{extractor_hash}", &extractor_hash) + .replace("{title}", &title) + .replace("{publish_date}", &publish_date) + .replace("{parent_subscription_name}", &parent_subscription_name) + .replace("{duration}", &duration) + .replace("{url}", &url) + } else { + format!( + "{status} {extractor_hash} {title} {publish_date} {parent_subscription_name} {duration}" + ) + }; Ok(f) } - pub async fn to_select_file_display(&self, app: &App) -> Result<String> { + pub(crate) async fn to_select_file_display(&self, app: &App) -> Result<String> { let f = format!( r#"{}{} {} "{}" "{}" "{}" "{}" "{}"{}"#, self.status_fmt_no_color(), - self.video_options_fmt_no_color(app).await?, + self.video_options_fmt_no_color(app), self.extractor_hash_fmt_no_color(app).await?, self.title_fmt_no_color(), self.publish_date_fmt_no_color(), diff --git a/crates/yt/src/videos/display/mod.rs b/crates/yt/src/videos/display/mod.rs index 89f7575..54e98ed 100644 --- a/crates/yt/src/videos/display/mod.rs +++ b/crates/yt/src/videos/display/mod.rs @@ -9,13 +9,16 @@ // You should have received a copy of the License along with this program. // If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>. +use std::fmt::Write; + +use anyhow::{Context, Result}; use owo_colors::OwoColorize; use url::Url; use crate::{ app::App, select::selection_file::duration::MaybeDuration, - storage::video_database::{TimeStamp, Video, VideoStatus, get::get_video_opts}, + storage::db::video::{TimeStamp, Video, VideoStatus}, }; pub(crate) mod format_video; @@ -209,19 +212,30 @@ impl Video { pub(crate) fn video_options_fmt_no_color(&self, app: &App) -> String { let video_options = { - let opts = get_video_opts(app, &self.extractor_hash) - .await - .with_context(|| { - format!("Failed to get video options for video: '{}'", self.title) - })? - .to_cli_flags(app); + let mut opts = String::new(); + + if let Some(playback_speed) = self.playback_speed { + if (playback_speed - app.config.select.playback_speed).abs() > f64::EPSILON { + write!(opts, " --playback-speed '{}'", playback_speed).expect("In-memory"); + } + } + + if let Some(subtitle_langs) = &self.subtitle_langs { + if subtitle_langs != &app.config.select.subtitle_langs { + write!(opts, " --subtitle-langs '{}'", subtitle_langs).expect("In-memory"); + } + } + + let opts = opts.trim().to_owned(); + let opts_white = if opts.is_empty() { "" } else { " " }; format!("{opts_white}{opts}") }; - Ok(video_options) + video_options } - pub(crate) fn video_options_fmt(&self, app: &App) -> Result<String> { - let opts = self.video_options_fmt_no_color(app).await?; - Ok(maybe_add_color(app, opts, |v| v.bright_green().to_string())) + + pub(crate) fn video_options_fmt(&self, app: &App) -> String { + let opts = self.video_options_fmt_no_color(app); + maybe_add_color(app, opts, |v| v.bright_green().to_string()) } } diff --git a/crates/yt/src/videos/mod.rs b/crates/yt/src/videos/mod.rs index 092e75c..673d46e 100644 --- a/crates/yt/src/videos/mod.rs +++ b/crates/yt/src/videos/mod.rs @@ -16,23 +16,24 @@ pub(crate) mod display; use crate::{ app::App, - storage::video_database::{Video, VideoStatusMarker, get}, + storage::db::video::{Video, VideoStatusMarker}, }; -async fn to_line_display_owned(video: Video, app: &App) -> Result<String> { - video.to_line_display(app).await +async fn to_line_display_owned(video: Video, app: &App, format: Option<String>) -> Result<String> { + video.to_line_display(app, format).await } pub(crate) async fn query( app: &App, limit: Option<usize>, search_query: Option<String>, + format: Option<String>, ) -> Result<()> { - let all_videos = get::videos(app, VideoStatusMarker::ALL).await?; + let all_videos = Video::in_states(app, VideoStatusMarker::ALL).await?; // turn one video to a color display, to pre-warm the hash shrinking cache if let Some(val) = all_videos.first() { - val.to_line_display(app).await?; + val.to_line_display(app, format.clone()).await?; } let limit = limit.unwrap_or(all_videos.len()); @@ -40,7 +41,7 @@ pub(crate) async fn query( let all_video_strings: Vec<String> = all_videos .into_iter() .take(limit) - .map(|vid| to_line_display_owned(vid, app)) + .map(|vid| to_line_display_owned(vid, app, format.clone())) .collect::<FuturesUnordered<_>>() .try_collect::<Vec<String>>() .await?; |