diff options
author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-07-10 16:58:59 +0200 |
---|---|---|
committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-07-10 16:58:59 +0200 |
commit | c3abafd4878df886dc8765a048cb0b70f282f1f3 (patch) | |
tree | 24d5bb2a35d99f7206ba619609e652dffc1c5d17 /crates | |
parent | docs(crates/libmpv2): Correctly format doc-test (diff) | |
download | yt-c3abafd4878df886dc8765a048cb0b70f282f1f3.zip |
refactor(crates/yt): Make every `pub` item `pub(crate)`
Otherwise, rust will not warn use about unused code (and `yt` is not a library).
Diffstat (limited to 'crates')
31 files changed, 206 insertions, 203 deletions
diff --git a/crates/yt/src/ansi_escape_codes.rs b/crates/yt/src/ansi_escape_codes.rs index 462a126..4528bd0 100644 --- a/crates/yt/src/ansi_escape_codes.rs +++ b/crates/yt/src/ansi_escape_codes.rs @@ -10,10 +10,10 @@ // see: https://en.wikipedia.org/wiki/ANSI_escape_code#Control_Sequence_Introducer_commands const CSI: &str = "\x1b["; -pub fn erase_in_display_from_cursor() { +pub(crate) fn erase_in_display_from_cursor() { print!("{CSI}0J"); } -pub fn cursor_up(number: usize) { +pub(crate) fn cursor_up(number: usize) { // HACK(@bpeetz): The default is `1` and running this command with a // number of `0` results in it using the default (i.e., `1`) <2025-03-25> if number != 0 { @@ -21,16 +21,9 @@ pub fn cursor_up(number: usize) { } } -pub fn clear_whole_line() { +pub(crate) fn clear_whole_line() { eprint!("{CSI}2K"); } -pub fn move_to_col(x: usize) { +pub(crate) fn move_to_col(x: usize) { eprint!("{CSI}{x}G"); } - -pub fn hide_cursor() { - eprint!("{CSI}?25l"); -} -pub fn show_cursor() { - eprint!("{CSI}?25h"); -} diff --git a/crates/yt/src/app.rs b/crates/yt/src/app.rs index 15a9388..3ea12a4 100644 --- a/crates/yt/src/app.rs +++ b/crates/yt/src/app.rs @@ -16,13 +16,13 @@ use sqlx::{SqlitePool, sqlite::SqliteConnectOptions}; use crate::{config::Config, storage::migrate::migrate_db}; #[derive(Debug)] -pub struct App { - pub database: SqlitePool, - pub config: Config, +pub(crate) struct App { + pub(crate) database: SqlitePool, + pub(crate) config: Config, } impl App { - pub async fn new(config: Config, should_migrate_db: bool) -> Result<Self> { + pub(crate) async fn new(config: Config, should_migrate_db: bool) -> Result<Self> { let options = SqliteConnectOptions::new() .filename(&config.paths.database_path) .optimize_on_close(true, None) diff --git a/crates/yt/src/cache/mod.rs b/crates/yt/src/cache/mod.rs index 83d5ee0..589c6ba 100644 --- a/crates/yt/src/cache/mod.rs +++ b/crates/yt/src/cache/mod.rs @@ -55,7 +55,7 @@ async fn invalidate_video(app: &App, video: &Video, hard: bool) -> Result<()> { Ok(()) } -pub async fn invalidate(app: &App, hard: bool) -> Result<()> { +pub(crate) async fn invalidate(app: &App, hard: bool) -> Result<()> { let all_cached_things = get::videos(app, &[VideoStatusMarker::Cached]).await?; info!("Got videos to invalidate: '{}'", all_cached_things.len()); @@ -67,14 +67,13 @@ pub async fn invalidate(app: &App, hard: bool) -> Result<()> { Ok(()) } -/// # Panics -/// Only if internal assertions fail. -pub async fn maintain(app: &App, all: bool) -> Result<()> { let domain = if all { VideoStatusMarker::ALL.as_slice() } else { &[VideoStatusMarker::Watch, VideoStatusMarker::Cached] }; +/// Remove the cache paths from the db, that no longer exist on the file system. +pub(crate) async fn maintain(app: &App, all: bool) -> Result<()> { let cached_videos = get::videos(app, domain).await?; diff --git a/crates/yt/src/cli.rs b/crates/yt/src/cli.rs index 98bbb2d..e30f5f7 100644 --- a/crates/yt/src/cli.rs +++ b/crates/yt/src/cli.rs @@ -18,7 +18,6 @@ use std::{ }; use anyhow::Context; -use bytes::Bytes; use chrono::NaiveDate; use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum}; use clap_complete::{ArgValueCompleter, CompletionCandidate}; @@ -29,6 +28,7 @@ use crate::{ app::App, config::Config, select::selection_file::duration::MaybeDuration, + shared::bytes::Bytes, storage::{subscriptions, video_database::extractor_hash::LazyExtractorHash}, }; @@ -36,44 +36,44 @@ use crate::{ #[clap(author, about, long_about = None)] #[allow(clippy::module_name_repetitions)] /// An command line interface to select, download and watch videos -pub struct CliArgs { +pub(crate) struct CliArgs { #[command(subcommand)] /// The subcommand to execute [default: select] - pub command: Option<Command>, + pub(crate) command: Option<Command>, /// Show the version and exit #[arg(long, short = 'V', action= ArgAction::SetTrue)] - pub version: bool, + pub(crate) version: bool, /// Do not perform database migration before starting. /// Setting this could cause runtime database access errors. #[arg(long, short, action=ArgAction::SetTrue, default_value_t = false)] - pub no_migrate_db: bool, + pub(crate) no_migrate_db: bool, /// Display colors [defaults to true, if the config file has no value] #[arg(long, short = 'C')] - pub color: Option<bool>, + pub(crate) color: Option<bool>, /// Set the path to the videos.db. This overrides the default and the config file. #[arg(long, short)] - pub db_path: Option<PathBuf>, + pub(crate) db_path: Option<PathBuf>, /// Set the path to the config.toml. /// This overrides the default. #[arg(long, short)] - pub config_path: Option<PathBuf>, + pub(crate) config_path: Option<PathBuf>, /// Increase message verbosity #[arg(long="verbose", short = 'v', action = ArgAction::Count)] - pub verbosity: u8, + pub(crate) verbosity: u8, /// Silence all output #[arg(long, short = 'q')] - pub quiet: bool, + pub(crate) quiet: bool, } #[derive(Subcommand, Debug)] -pub enum Command { +pub(crate) enum Command { /// Download and cache URLs Download { /// Forcefully re-download all cached videos (i.e. delete the cache path, then download). @@ -189,7 +189,7 @@ impl Default for Command { } #[derive(Subcommand, Clone, Debug)] -pub enum VideosCommand { +pub(crate) enum VideosCommand { /// List the videos in the database #[command(visible_alias = "ls")] List { @@ -206,11 +206,16 @@ pub enum VideosCommand { Info { /// The short hash of the video hash: LazyExtractorHash, + + /// 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>, }, } #[derive(Subcommand, Clone, Debug)] -pub enum SubscriptionCommand { +pub(crate) enum SubscriptionCommand { /// Subscribe to an URL Add { #[arg(short, long)] @@ -247,35 +252,35 @@ pub enum SubscriptionCommand { #[derive(Clone, Debug, Args)] #[command(infer_subcommands = true)] /// Mark the video given by the hash to be watched -pub struct SharedSelectionCommandArgs { +pub(crate) struct SharedSelectionCommandArgs { /// The ordering priority (higher means more at the top) #[arg(short, long)] - pub priority: Option<i64>, + pub(crate) priority: Option<i64>, /// The subtitles to download (e.g. 'en,de,sv') #[arg(short = 'l', long)] - pub subtitle_langs: Option<String>, + pub(crate) subtitle_langs: Option<String>, /// The speed to set mpv to #[arg(short, long)] - pub speed: Option<f64>, + pub(crate) speed: Option<f64>, /// The short extractor hash - pub hash: LazyExtractorHash, + pub(crate) hash: LazyExtractorHash, - pub title: Option<String>, + pub(crate) title: Option<String>, - pub date: Option<OptionalNaiveDate>, + pub(crate) date: Option<OptionalNaiveDate>, - pub publisher: Option<OptionalPublisher>, + pub(crate) publisher: Option<OptionalPublisher>, - pub duration: Option<MaybeDuration>, + pub(crate) duration: Option<MaybeDuration>, - pub url: Option<Url>, + pub(crate) url: Option<Url>, } #[derive(Clone, Debug, Copy)] -pub struct OptionalNaiveDate { - pub date: Option<NaiveDate>, +pub(crate) struct OptionalNaiveDate { + pub(crate) date: Option<NaiveDate>, } impl FromStr for OptionalNaiveDate { type Err = anyhow::Error; @@ -290,8 +295,8 @@ impl FromStr for OptionalNaiveDate { } } #[derive(Clone, Debug)] -pub struct OptionalPublisher { - pub publisher: Option<String>, +pub(crate) struct OptionalPublisher { + pub(crate) publisher: Option<String>, } impl FromStr for OptionalPublisher { type Err = anyhow::Error; @@ -307,7 +312,7 @@ impl FromStr for OptionalPublisher { } #[derive(Default, ValueEnum, Clone, Copy, Debug)] -pub enum SelectSplitSortKey { +pub(crate) enum SelectSplitSortKey { /// Sort by the name of the publisher. #[default] Publisher, @@ -325,7 +330,7 @@ impl Display for SelectSplitSortKey { } #[derive(Default, ValueEnum, Clone, Copy, Debug)] -pub enum SelectSplitSortMode { +pub(crate) enum SelectSplitSortMode { /// Sort in ascending order (small -> big) #[default] Asc, @@ -346,7 +351,7 @@ impl Display for SelectSplitSortMode { #[derive(Subcommand, Clone, Debug)] // NOTE: Keep this in sync with the [`constants::HELP_STR`] constant. <2024-08-20> // NOTE: Also keep this in sync with the `tree-sitter-yts/grammar.js`. <2024-11-04> -pub enum SelectCommand { +pub(crate) enum SelectCommand { /// Open a `git rebase` like file to select the videos to watch (the default) File { /// Include done (watched, dropped) videos @@ -436,7 +441,7 @@ impl Default for SelectCommand { } #[derive(Subcommand, Clone, Copy, Debug)] -pub enum CacheCommand { +pub(crate) enum CacheCommand { /// Invalidate all cache entries Invalidate { /// Also delete the cache path diff --git a/crates/yt/src/comments/comment.rs b/crates/yt/src/comments/comment.rs index 5bc939c..d9e3036 100644 --- a/crates/yt/src/comments/comment.rs +++ b/crates/yt/src/comments/comment.rs @@ -44,8 +44,8 @@ impl From<String> for Parent { #[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, PartialOrd, Ord)] #[serde(from = "String")] #[serde(deny_unknown_fields)] -pub struct Id { - pub id: String, +pub(crate) struct Id { + pub(crate) id: String, } impl From<String> for Id { fn from(value: String) -> Self { @@ -58,26 +58,26 @@ impl From<String> for Id { #[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, PartialOrd, Ord)] #[allow(clippy::struct_excessive_bools)] -pub struct Comment { - pub id: Id, - pub text: String, +pub(crate) struct Comment { + pub(crate) id: Id, + pub(crate) text: String, #[serde(default = "zero")] - pub like_count: u32, - pub is_pinned: bool, - pub author_id: String, + pub(crate) like_count: u32, + pub(crate) is_pinned: bool, + pub(crate) author_id: String, #[serde(default = "unknown")] - pub author: String, - pub author_is_verified: bool, - pub author_thumbnail: Url, - pub parent: Parent, + pub(crate) author: String, + pub(crate) author_is_verified: bool, + pub(crate) author_thumbnail: Url, + pub(crate) parent: Parent, #[serde(deserialize_with = "edited_from_time_text", alias = "_time_text")] - pub edited: bool, + pub(crate) edited: bool, // Can't also be deserialized, as it's already used in 'edited' // _time_text: String, - pub timestamp: i64, - pub author_url: Option<Url>, - pub author_is_uploader: bool, - pub is_favorited: bool, + pub(crate) timestamp: i64, + pub(crate) author_url: Option<Url>, + pub(crate) author_is_uploader: bool, + pub(crate) is_favorited: bool, } fn unknown() -> String { @@ -100,46 +100,37 @@ where #[derive(Debug, Clone)] #[allow(clippy::module_name_repetitions)] -pub struct CommentExt { - pub value: Comment, - pub replies: Vec<CommentExt>, +pub(crate) struct CommentExt { + pub(crate) value: Comment, + pub(crate) replies: Vec<CommentExt>, } #[derive(Debug, Default)] -pub struct Comments { +pub(crate) struct Comments { pub(super) vec: Vec<CommentExt>, } impl Comments { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn push(&mut self, value: CommentExt) { + pub(crate) fn push(&mut self, value: CommentExt) { self.vec.push(value); } - pub fn get_mut(&mut self, key: &str) -> Option<&mut CommentExt> { - self.vec.iter_mut().filter(|c| c.value.id.id == key).last() - } - pub fn insert(&mut self, key: &str, value: CommentExt) { + pub(crate) fn insert(&mut self, key: &str, value: CommentExt) { let parent = self .vec .iter_mut() .filter(|c| c.value.id.id == key) - .last() + .next_back() .expect("One of these should exist"); parent.push_reply(value); } } impl CommentExt { - pub fn push_reply(&mut self, value: CommentExt) { + pub(crate) fn push_reply(&mut self, value: CommentExt) { self.replies.push(value); } - pub fn get_mut_reply(&mut self, key: &str) -> Option<&mut CommentExt> { - self.replies - .iter_mut() - .filter(|c| c.value.id.id == key) - .last() - } } impl From<Comment> for CommentExt { diff --git a/crates/yt/src/comments/description.rs b/crates/yt/src/comments/description.rs index 878b573..9f87441 100644 --- a/crates/yt/src/comments/description.rs +++ b/crates/yt/src/comments/description.rs @@ -19,7 +19,7 @@ use crate::{ use anyhow::{Result, bail}; use yt_dlp::json_cast; -pub async fn description(app: &App) -> Result<()> { +pub(crate) async fn description(app: &App) -> Result<()> { let description = get(app).await?; display_fmt_and_less(description).await?; @@ -40,7 +40,6 @@ pub async fn get(app: &App) -> Result<String> { Ok(info_json .get("description") - .map(|val| json_cast!(val, as_str)) - .unwrap_or("<No description>") + .map_or("<No description>", |val| json_cast!(val, as_str)) .to_owned()) } diff --git a/crates/yt/src/comments/display.rs b/crates/yt/src/comments/display.rs index 6166b2b..de50614 100644 --- a/crates/yt/src/comments/display.rs +++ b/crates/yt/src/comments/display.rs @@ -19,7 +19,7 @@ use crate::comments::comment::CommentExt; use super::comment::Comments; impl Comments { - pub fn render(&self, color: bool) -> String { + pub(crate) fn render(&self, color: bool) -> String { self.render_help(color).expect("This should never fail.") } diff --git a/crates/yt/src/comments/mod.rs b/crates/yt/src/comments/mod.rs index 54031a4..b856cad 100644 --- a/crates/yt/src/comments/mod.rs +++ b/crates/yt/src/comments/mod.rs @@ -149,7 +149,7 @@ pub async fn get(app: &App) -> Result<Comments> { Ok(comments) } -pub async fn comments(app: &App) -> Result<()> { +pub(crate) async fn comments(app: &App) -> Result<()> { let comments = get(app).await?; display_fmt_and_less(comments.render(true)).await?; diff --git a/crates/yt/src/comments/output.rs b/crates/yt/src/comments/output.rs index cb3a9c4..67dec8f 100644 --- a/crates/yt/src/comments/output.rs +++ b/crates/yt/src/comments/output.rs @@ -17,9 +17,7 @@ use std::{ use anyhow::{Context, Result}; use uu_fmt::{FmtOptions, process_text}; -use crate::unreachable::Unreachable; - -pub async fn display_fmt_and_less(input: String) -> Result<()> { +pub(crate) async fn display_fmt_and_less(input: String) -> Result<()> { let mut less = Command::new("less") .args(["--raw-control-chars"]) .stdin(Stdio::piped()) @@ -41,7 +39,9 @@ pub async fn display_fmt_and_less(input: String) -> Result<()> { } #[must_use] -pub fn format_text(input: &str) -> String { +pub(crate) fn format_text(input: &str) -> String { + let input = input.trim(); + let width = termsize::get().map_or(90, |size| size.cols); let fmt_opts = FmtOptions { uniform: true, diff --git a/crates/yt/src/constants.rs b/crates/yt/src/constants.rs index 0f5b918..690e018 100644 --- a/crates/yt/src/constants.rs +++ b/crates/yt/src/constants.rs @@ -9,4 +9,4 @@ // 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>. -pub const HELP_STR: &str = include_str!("./select/selection_file/help.str"); +pub(crate) const HELP_STR: &str = include_str!("./select/selection_file/help.str"); diff --git a/crates/yt/src/download/download_options.rs b/crates/yt/src/download/download_options.rs index 558adfd..2eac4c3 100644 --- a/crates/yt/src/download/download_options.rs +++ b/crates/yt/src/download/download_options.rs @@ -13,11 +13,14 @@ use anyhow::Context; use serde_json::{Value, json}; use yt_dlp::{YoutubeDL, options::YoutubeDLOptions}; -use crate::{app::App, storage::video_database::YtDlpOptions}; +use crate::app::App; use super::progress_hook::wrapped_progress_hook; -pub fn download_opts(app: &App, additional_opts: &YtDlpOptions) -> anyhow::Result<YoutubeDL> { +pub(crate) fn download_opts( + app: &App, + subtitle_langs: Option<&String>, +) -> anyhow::Result<YoutubeDL> { YoutubeDLOptions::new() .with_progress_hook(wrapped_progress_hook) .set("extract_flat", "in_playlist") diff --git a/crates/yt/src/download/mod.rs b/crates/yt/src/download/mod.rs index 6065cf9..2e7315d 100644 --- a/crates/yt/src/download/mod.rs +++ b/crates/yt/src/download/mod.rs @@ -72,7 +72,7 @@ enum CacheSizeCheck { } #[derive(Debug)] -pub struct Downloader { +pub(crate) struct Downloader { current_download: Option<CurrentDownload>, video_size_cache: HashMap<ExtractorHash, u64>, printed_warning: bool, @@ -87,7 +87,7 @@ impl Default for Downloader { impl Downloader { #[must_use] - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self { current_download: None, video_size_cache: HashMap::new(), @@ -179,8 +179,8 @@ impl Downloader { /// This Downloader will periodically check if the database has changed, and then also /// change which videos it downloads. /// This will run, until the database doesn't contain any watchable videos - pub async fn consume(&mut self, app: Arc<App>, max_cache_size: u64) -> Result<()> { - while let Some(next_video) = get_next_uncached_video(&app).await? { + pub(crate) async fn consume(&mut self, app: Arc<App>, max_cache_size: u64) -> Result<()> { + while let Some(next_video) = Video::next_to_download(&app).await? { match self .is_enough_cache_available(&app, max_cache_size, &next_video) .await? @@ -238,7 +238,7 @@ impl Downloader { Ok(()) } - pub async fn get_current_cache_allocation(app: &App) -> Result<Bytes> { + pub(crate) async fn get_current_cache_allocation(app: &App) -> Result<Bytes> { fn dir_size(mut dir: fs::ReadDir) -> BoxFuture<'static, Result<Bytes>> { async move { let mut acc = 0; diff --git a/crates/yt/src/download/progress_hook.rs b/crates/yt/src/download/progress_hook.rs index ad754b0..99d1a74 100644 --- a/crates/yt/src/download/progress_hook.rs +++ b/crates/yt/src/download/progress_hook.rs @@ -13,19 +13,20 @@ use std::{ process, }; -use bytes::Bytes; use log::{Level, log_enabled}; -use yt_dlp::mk_python_function; +use owo_colors::OwoColorize; +use yt_dlp::{json_cast, json_get, wrap_progress_hook}; use crate::{ ansi_escape_codes::{clear_whole_line, move_to_col}, select::selection_file::duration::MaybeDuration, + shared::bytes::Bytes, }; /// # Panics /// If expectations fail. -#[allow(clippy::too_many_lines, clippy::needless_pass_by_value)] -pub fn progress_hook( +#[allow(clippy::needless_pass_by_value)] +pub(crate) fn progress_hook( input: serde_json::Map<String, serde_json::Value>, ) -> Result<(), std::io::Error> { // Only add the handler, if the log-level is higher than Debug (this avoids covering debug @@ -195,4 +196,4 @@ pub fn progress_hook( Ok(()) } -mk_python_function!(progress_hook, wrapped_progress_hook); +wrap_progress_hook!(progress_hook, wrapped_progress_hook); diff --git a/crates/yt/src/main.rs b/crates/yt/src/main.rs index faee401..52b8e07 100644 --- a/crates/yt/src/main.rs +++ b/crates/yt/src/main.rs @@ -36,24 +36,24 @@ use crate::{ storage::subscription, }; -pub mod ansi_escape_codes; -pub mod app; -pub mod cli; -pub mod unreachable; - -pub mod cache; -pub mod comments; -pub mod config; -pub mod constants; -pub mod download; -pub mod select; -pub mod status; -pub mod storage; -pub mod subscribe; -pub mod update; -pub mod version; -pub mod videos; -pub mod watch; +pub(crate) mod ansi_escape_codes; +pub(crate) mod app; +pub(crate) mod cli; +pub(crate) mod shared; + +pub(crate) mod cache; +pub(crate) mod comments; +pub(crate) mod config; +pub(crate) mod constants; +pub(crate) mod download; +pub(crate) mod select; +pub(crate) mod status; +pub(crate) mod storage; +pub(crate) mod subscribe; +pub(crate) mod update; +pub(crate) mod version; +pub(crate) mod videos; +pub(crate) mod watch; #[tokio::main] // This is _the_ main function after all. It is not really good, but it sort of works. diff --git a/crates/yt/src/select/cmds/add.rs b/crates/yt/src/select/cmds/add.rs index 2c9a323..f793117 100644 --- a/crates/yt/src/select/cmds/add.rs +++ b/crates/yt/src/select/cmds/add.rs @@ -23,7 +23,7 @@ use url::Url; use yt_dlp::{YoutubeDL, info_json::InfoJson, json_cast, json_get}; #[allow(clippy::too_many_lines)] -pub(super) async fn add( +pub(crate) async fn add( app: &App, urls: Vec<Url>, start: Option<usize>, diff --git a/crates/yt/src/select/mod.rs b/crates/yt/src/select/mod.rs index 2478b76..543013e 100644 --- a/crates/yt/src/select/mod.rs +++ b/crates/yt/src/select/mod.rs @@ -36,10 +36,10 @@ use selection_file::process_line; use tempfile::Builder; use tokio::process::Command; -pub mod cmds; -pub mod selection_file; +pub(crate) mod cmds; +pub(crate) mod selection_file; -pub async fn select_split( +pub(crate) async fn select_split( app: &App, done: bool, sort_key: SelectSplitSortKey, @@ -158,7 +158,7 @@ pub async fn select_split( Ok(()) } -pub async fn select_file(app: &App, done: bool, use_last_selection: bool) -> Result<()> { +pub(crate) async fn select_file(app: &App, done: bool, use_last_selection: bool) -> Result<()> { let temp_file = Builder::new() .prefix("yt_video_select-") .suffix(".yts") diff --git a/crates/yt/src/select/selection_file/duration.rs b/crates/yt/src/select/selection_file/duration.rs index 668a0b8..e536f18 100644 --- a/crates/yt/src/select/selection_file/duration.rs +++ b/crates/yt/src/select/selection_file/duration.rs @@ -20,51 +20,45 @@ const HOUR: u64 = 60 * MINUTE; const DAY: u64 = 24 * HOUR; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct MaybeDuration { +pub(crate) struct MaybeDuration { time: Option<Duration>, } impl MaybeDuration { #[must_use] - pub fn from_std(d: Duration) -> Self { + pub(crate) fn from_std(d: Duration) -> Self { Self { time: Some(d) } } #[must_use] - pub fn from_secs_f64(d: f64) -> Self { + pub(crate) fn from_secs_f64(d: f64) -> Self { Self { time: Some(Duration::from_secs_f64(d)), } } #[must_use] - pub fn from_maybe_secs_f64(d: Option<f64>) -> Self { + pub(crate) fn from_maybe_secs_f64(d: Option<f64>) -> Self { Self { time: d.map(Duration::from_secs_f64), } } #[must_use] - pub fn from_secs(d: u64) -> Self { + #[cfg(test)] + pub(crate) fn from_secs(d: u64) -> Self { Self { time: Some(Duration::from_secs(d)), } } - #[must_use] - pub fn zero() -> Self { - Self { - time: Some(Duration::default()), - } - } - /// Try to return the current duration encoded as seconds. #[must_use] - pub fn as_secs(&self) -> Option<u64> { + pub(crate) fn as_secs(&self) -> Option<u64> { self.time.map(|v| v.as_secs()) } /// Try to return the current duration encoded as seconds and nanoseconds. #[must_use] - pub fn as_secs_f64(&self) -> Option<f64> { + pub(crate) fn as_secs_f64(&self) -> Option<f64> { self.time.map(|v| v.as_secs_f64()) } } diff --git a/crates/yt/src/select/selection_file/mod.rs b/crates/yt/src/select/selection_file/mod.rs index f5e0531..36342f8 100644 --- a/crates/yt/src/select/selection_file/mod.rs +++ b/crates/yt/src/select/selection_file/mod.rs @@ -14,11 +14,11 @@ use anyhow::{Result, bail}; use shlex::Shlex; -pub mod duration; +pub(crate) mod duration; /// # Panics /// If internal assertions fail. -pub fn process_line(line: &str) -> Result<Option<Vec<String>>> { +pub(crate) fn process_line(line: &str) -> Result<Option<Vec<String>>> { // Filter out comments and empty lines if line.starts_with('#') || line.trim().is_empty() { Ok(None) diff --git a/crates/yt/src/status/mod.rs b/crates/yt/src/status/mod.rs index 6883802..f629324 100644 --- a/crates/yt/src/status/mod.rs +++ b/crates/yt/src/status/mod.rs @@ -15,14 +15,14 @@ use crate::{ app::App, download::Downloader, select::selection_file::duration::MaybeDuration, - storage::{ - subscriptions, - video_database::{VideoStatusMarker, get}, + shared::bytes::Bytes, + storage::db::{ + subscription::Subscriptions, + video::{Video, VideoStatusMarker}, }, }; use anyhow::{Context, Result}; -use bytes::Bytes; macro_rules! get { ($videos:expr, $status:ident) => { @@ -40,7 +40,7 @@ macro_rules! get { }; } -pub async fn show(app: &App) -> Result<()> { +pub(crate) async fn show(app: &App) -> Result<()> { let all_videos = get::videos(app, VideoStatusMarker::ALL).await?; // lengths @@ -121,7 +121,7 @@ Dropped Videos: {dropped_videos_len} Ok(()) } -pub fn config(app: &App) -> Result<()> { +pub(crate) fn config(app: &App) -> Result<()> { let config_str = toml::to_string(&app.config)?; print!("{config_str}"); diff --git a/crates/yt/src/storage/migrate/mod.rs b/crates/yt/src/storage/migrate/mod.rs index 953d079..4fa39ab 100644 --- a/crates/yt/src/storage/migrate/mod.rs +++ b/crates/yt/src/storage/migrate/mod.rs @@ -75,7 +75,7 @@ macro_rules! make_upgrade { } #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] -pub enum DbVersion { +pub(crate) enum DbVersion { /// The database is not yet initialized. Empty, @@ -222,9 +222,10 @@ fn get_current_date() -> i64 { /// /// # Panics /// Only if internal assertions fail. -pub async fn get_version(app: &App) -> Result<DbVersion> { +pub(crate) async fn get_version(app: &App) -> Result<DbVersion> { get_version_db(&app.database).await } + /// Return the current database version. /// /// In contrast to the [`get_version`] function, this function does not @@ -232,13 +233,19 @@ pub async fn get_version(app: &App) -> Result<DbVersion> { /// /// # Panics /// Only if internal assertions fail. -pub async fn get_version_db(pool: &SqlitePool) -> Result<DbVersion> { +pub(crate) async fn get_version_db(pool: &SqlitePool) -> Result<DbVersion> { let version_table_exists = { let query = query!( - "SELECT 1 as result FROM sqlite_master WHERE type = 'table' AND name = 'version'" + " + SELECT 1 as result + FROM sqlite_master + WHERE type = 'table' + AND name = 'version' + " ) .fetch_optional(pool) .await?; + if let Some(output) = query { assert_eq!(output.result, 1); true @@ -246,13 +253,16 @@ pub async fn get_version_db(pool: &SqlitePool) -> Result<DbVersion> { false } }; + if !version_table_exists { return Ok(DbVersion::Empty); } let current_version = query!( " - SELECT namespace, number FROM version WHERE valid_to IS NULL; + SELECT namespace, number + FROM version + WHERE valid_to IS NULL; " ) .fetch_one(pool) @@ -262,7 +272,7 @@ pub async fn get_version_db(pool: &SqlitePool) -> Result<DbVersion> { DbVersion::from_db(current_version.number, current_version.namespace.as_str()) } -pub async fn migrate_db(app: &App) -> Result<()> { +pub(crate) async fn migrate_db(app: &App) -> Result<()> { let current_version = get_version(app) .await .context("Failed to determine initial version")?; diff --git a/crates/yt/src/storage/mod.rs b/crates/yt/src/storage/mod.rs index d352b41..2d9133b 100644 --- a/crates/yt/src/storage/mod.rs +++ b/crates/yt/src/storage/mod.rs @@ -9,6 +9,6 @@ // 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>. -pub mod migrate; -pub mod subscriptions; -pub mod video_database; +pub(crate) mod migrate; +pub(crate) mod db; +pub(crate) mod notify; diff --git a/crates/yt/src/subscribe/mod.rs b/crates/yt/src/subscribe/mod.rs index 66797e8..89ea9f7 100644 --- a/crates/yt/src/subscribe/mod.rs +++ b/crates/yt/src/subscribe/mod.rs @@ -12,7 +12,6 @@ use std::str::FromStr; use anyhow::{Context, Result, bail}; -use futures::FutureExt; use log::{error, warn}; use tokio::io::{AsyncBufRead, AsyncBufReadExt}; use url::Url; @@ -26,7 +25,7 @@ use crate::{ unreachable::Unreachable, }; -pub async fn unsubscribe(app: &App, name: String) -> Result<()> { +pub(crate) async fn unsubscribe(app: &App, name: String) -> Result<()> { let present_subscriptions = get(app).await?; if let Some(subscription) = present_subscriptions.0.get(&name) { @@ -38,7 +37,7 @@ pub async fn unsubscribe(app: &App, name: String) -> Result<()> { Ok(()) } -pub async fn import<W: AsyncBufRead + AsyncBufReadExt + Unpin>( +pub(crate) async fn import<W: AsyncBufRead + AsyncBufReadExt + Unpin>( app: &App, reader: W, force: bool, @@ -67,7 +66,11 @@ pub async fn import<W: AsyncBufRead + AsyncBufReadExt + Unpin>( Ok(()) } -pub async fn subscribe(app: &App, name: Option<String>, url: Url) -> Result<()> { +pub(crate) async fn subscribe( + app: &App, + name: Option<String>, + url: Url, +) -> Result<()> { if !(url.as_str().ends_with("videos") || url.as_str().ends_with("streams") || url.as_str().ends_with("shorts") diff --git a/crates/yt/src/update/mod.rs b/crates/yt/src/update/mod.rs index 7f9bee7..fbe23da 100644 --- a/crates/yt/src/update/mod.rs +++ b/crates/yt/src/update/mod.rs @@ -32,7 +32,7 @@ use crate::{ mod updater; use updater::Updater; -pub async fn update( +pub(crate) async fn update( app: &App, max_backlog: usize, subscription_names_to_update: Vec<String>, @@ -64,7 +64,7 @@ pub async fn update( } #[allow(clippy::too_many_lines)] -pub fn video_entry_to_video(entry: &InfoJson, sub: Option<&Subscription>) -> Result<Video> { +pub(crate) fn video_entry_to_video(entry: &InfoJson, sub: Option<&Subscription>) -> Result<Video> { fn fmt_context(date: &str, extended: Option<&str>) -> String { let f = format!( "Failed to parse the `upload_date` of the entry ('{date}'). \ diff --git a/crates/yt/src/update/updater.rs b/crates/yt/src/update/updater.rs index 75d12dc..54dea97 100644 --- a/crates/yt/src/update/updater.rs +++ b/crates/yt/src/update/updater.rs @@ -41,7 +41,7 @@ pub(super) struct Updater { static REACHED_NUMBER: AtomicUsize = const { AtomicUsize::new(1) }; impl Updater { - pub(super) fn new(max_backlog: usize, hashes: Vec<Hash>) -> Self { + pub(super) fn new(max_backlog: usize, hashes: Vec<ExtractorHash>) -> Self { // TODO(@bpeetz): The number should not be hardcoded. <2025-06-14> let pool = LocalPoolHandle::new(16); diff --git a/crates/yt/src/version/mod.rs b/crates/yt/src/version/mod.rs index 2cc41c7..a3aa7ff 100644 --- a/crates/yt/src/version/mod.rs +++ b/crates/yt/src/version/mod.rs @@ -14,7 +14,7 @@ use yt_dlp::options::YoutubeDLOptions; use crate::{config::Config, storage::migrate::get_version_db}; -pub async fn show(config: &Config) -> Result<()> { +pub(crate) async fn show(config: &Config) -> Result<()> { let db_version = { let options = SqliteConnectOptions::new() .filename(&config.paths.database_path) diff --git a/crates/yt/src/videos/display/format_video.rs b/crates/yt/src/videos/display/format_video.rs index b97acb1..659c3be 100644 --- a/crates/yt/src/videos/display/format_video.rs +++ b/crates/yt/src/videos/display/format_video.rs @@ -14,7 +14,10 @@ use anyhow::Result; use crate::{app::App, comments::output::format_text, storage::video_database::Video}; impl Video { - pub async fn to_info_display(&self, app: &App) -> Result<String> { + pub(crate) async fn to_info_display( + &self, + app: &App, + ) -> Result<String> { let cache_path = self.cache_path_fmt(app); let description = self.description_fmt(); let duration = self.duration_fmt(app); @@ -29,7 +32,7 @@ impl Video { let title = self.title_fmt(app); let url = self.url_fmt(app); let watch_progress = self.watch_progress_fmt(app); - let video_options = self.video_options_fmt(app).await?; + let video_options = self.video_options_fmt(app); let watched_percentage_fmt = { if let Some(duration) = self.duration.as_secs() { diff --git a/crates/yt/src/videos/display/mod.rs b/crates/yt/src/videos/display/mod.rs index 1188569..89f7575 100644 --- a/crates/yt/src/videos/display/mod.rs +++ b/crates/yt/src/videos/display/mod.rs @@ -18,9 +18,7 @@ use crate::{ storage::video_database::{TimeStamp, Video, VideoStatus, get::get_video_opts}, }; -use anyhow::{Context, Result}; - -pub mod format_video; +pub(crate) mod format_video; macro_rules! get { ($value:expr, $key:ident, $name:expr, $code:tt) => { @@ -44,7 +42,7 @@ where } impl Video { #[must_use] - pub fn cache_path_fmt(&self, app: &App) -> String { + pub(crate) fn cache_path_fmt(&self, app: &App) -> String { let cache_path = if let VideoStatus::Cached { cache_path, is_focused: _, @@ -58,7 +56,7 @@ impl Video { } #[must_use] - pub fn description_fmt(&self) -> String { + pub(crate) fn description_fmt(&self) -> String { get!( self, description, @@ -68,17 +66,17 @@ impl Video { } #[must_use] - pub fn duration_fmt_no_color(&self) -> String { + pub(crate) fn duration_fmt_no_color(&self) -> String { self.duration.to_string() } #[must_use] - pub fn duration_fmt(&self, app: &App) -> String { + pub(crate) fn duration_fmt(&self, app: &App) -> String { let duration = self.duration_fmt_no_color(); maybe_add_color(app, duration, |v| v.cyan().bold().to_string()) } #[must_use] - pub fn watch_progress_fmt(&self, app: &App) -> String { + pub(crate) fn watch_progress_fmt(&self, app: &App) -> String { maybe_add_color( app, MaybeDuration::from_std(self.watch_progress).to_string(), @@ -86,7 +84,7 @@ impl Video { ) } - pub async fn extractor_hash_fmt_no_color(&self, app: &App) -> Result<String> { + pub(crate) async fn extractor_hash_fmt_no_color(&self, app: &App) -> Result<String> { let hash = self .extractor_hash .into_short_hash(app) @@ -100,7 +98,7 @@ impl Video { .to_string(); Ok(hash) } - pub async fn extractor_hash_fmt(&self, app: &App) -> Result<String> { + pub(crate) async fn extractor_hash_fmt(&self, app: &App) -> Result<String> { let hash = self.extractor_hash_fmt_no_color(app).await?; Ok(maybe_add_color(app, hash, |v| { v.bright_purple().italic().to_string() @@ -108,7 +106,7 @@ impl Video { } #[must_use] - pub fn in_playlist_fmt(&self, app: &App) -> String { + pub(crate) fn in_playlist_fmt(&self, app: &App) -> String { let output = match &self.status { VideoStatus::Pick | VideoStatus::Watch @@ -126,14 +124,14 @@ impl Video { maybe_add_color(app, output.to_owned(), |v| v.yellow().italic().to_string()) } #[must_use] - pub fn last_status_change_fmt(&self, app: &App) -> String { + pub(crate) fn last_status_change_fmt(&self, app: &App) -> String { maybe_add_color(app, self.last_status_change.to_string(), |v| { v.bright_cyan().to_string() }) } #[must_use] - pub fn parent_subscription_name_fmt_no_color(&self) -> String { + pub(crate) fn parent_subscription_name_fmt_no_color(&self) -> String { get!( self, parent_subscription_name, @@ -142,18 +140,18 @@ impl Video { ) } #[must_use] - pub fn parent_subscription_name_fmt(&self, app: &App) -> String { + pub(crate) fn parent_subscription_name_fmt(&self, app: &App) -> String { let psn = self.parent_subscription_name_fmt_no_color(); maybe_add_color(app, psn, |v| v.bright_magenta().to_string()) } #[must_use] - pub fn priority_fmt(&self) -> String { + pub(crate) fn priority_fmt(&self) -> String { self.priority.to_string() } #[must_use] - pub fn publish_date_fmt_no_color(&self) -> String { + pub(crate) fn publish_date_fmt_no_color(&self) -> String { get!( self, publish_date, @@ -162,25 +160,25 @@ impl Video { ) } #[must_use] - pub fn publish_date_fmt(&self, app: &App) -> String { + pub(crate) fn publish_date_fmt(&self, app: &App) -> String { let date = self.publish_date_fmt_no_color(); maybe_add_color(app, date, |v| v.bright_white().bold().to_string()) } #[must_use] - pub fn status_fmt_no_color(&self) -> String { + pub(crate) fn status_fmt_no_color(&self) -> String { // TODO: We might support `.trim()`ing that, as the extra whitespace could be bad in the // selection file. <2024-10-07> self.status.as_marker().as_command().to_string() } #[must_use] - pub fn status_fmt(&self, app: &App) -> String { + pub(crate) fn status_fmt(&self, app: &App) -> String { let status = self.status_fmt_no_color(); maybe_add_color(app, status, |v| v.red().bold().to_string()) } #[must_use] - pub fn thumbnail_url_fmt(&self) -> String { + pub(crate) fn thumbnail_url_fmt(&self) -> String { get!( self, thumbnail_url, @@ -190,26 +188,26 @@ impl Video { } #[must_use] - pub fn title_fmt_no_color(&self) -> String { + pub(crate) fn title_fmt_no_color(&self) -> String { self.title.replace(['"', '„', '”', '“'], "'") } #[must_use] - pub fn title_fmt(&self, app: &App) -> String { + pub(crate) fn title_fmt(&self, app: &App) -> String { let title = self.title_fmt_no_color(); maybe_add_color(app, title, |v| v.green().bold().to_string()) } #[must_use] - pub fn url_fmt_no_color(&self) -> String { + pub(crate) fn url_fmt_no_color(&self) -> String { self.url.as_str().replace('"', "\\\"") } #[must_use] - pub fn url_fmt(&self, app: &App) -> String { + pub(crate) fn url_fmt(&self, app: &App) -> String { let url = self.url_fmt_no_color(); maybe_add_color(app, url, |v| v.italic().to_string()) } - pub async fn video_options_fmt_no_color(&self, app: &App) -> Result<String> { + 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 @@ -222,7 +220,7 @@ impl Video { }; Ok(video_options) } - pub async fn video_options_fmt(&self, app: &App) -> Result<String> { + 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())) } diff --git a/crates/yt/src/videos/mod.rs b/crates/yt/src/videos/mod.rs index 960340b..092e75c 100644 --- a/crates/yt/src/videos/mod.rs +++ b/crates/yt/src/videos/mod.rs @@ -12,7 +12,7 @@ use anyhow::Result; use futures::{TryStreamExt, stream::FuturesUnordered}; -pub mod display; +pub(crate) mod display; use crate::{ app::App, @@ -23,7 +23,11 @@ async fn to_line_display_owned(video: Video, app: &App) -> Result<String> { video.to_line_display(app).await } -pub async fn query(app: &App, limit: Option<usize>, search_query: Option<String>) -> Result<()> { +pub(crate) async fn query( + app: &App, + limit: Option<usize>, + search_query: Option<String>, +) -> Result<()> { let all_videos = get::videos(app, VideoStatusMarker::ALL).await?; // turn one video to a color display, to pre-warm the hash shrinking cache diff --git a/crates/yt/src/watch/mod.rs b/crates/yt/src/watch/mod.rs index c32a76f..b4c68a0 100644 --- a/crates/yt/src/watch/mod.rs +++ b/crates/yt/src/watch/mod.rs @@ -93,7 +93,7 @@ fn init_mpv(app: &App) -> Result<(Mpv, EventContext)> { Ok((mpv, ev_ctx)) } -pub async fn watch(app: Arc<App>) -> Result<()> { +pub(crate) async fn watch(app: Arc<App>) -> Result<()> { maintain(&app, false).await?; let (mpv, mut ev_ctx) = init_mpv(&app).context("Failed to initialize mpv instance")?; diff --git a/crates/yt/src/watch/playlist.rs b/crates/yt/src/watch/playlist.rs index ff383d0..f456bfd 100644 --- a/crates/yt/src/watch/playlist.rs +++ b/crates/yt/src/watch/playlist.rs @@ -34,7 +34,7 @@ fn cache_values(video: &Video) -> (&Path, bool) { /// # Panics /// Only if internal assertions fail. -pub async fn playlist(app: &App, watch: bool) -> Result<()> { +pub(crate) async fn playlist(app: &App, watch: bool) -> Result<()> { let mut previous_output_length = 0; loop { let playlist = get::playlist(app).await?.to_videos(); diff --git a/crates/yt/src/watch/playlist_handler/mod.rs b/crates/yt/src/watch/playlist_handler/mod.rs index 29b8f39..8cf50c9 100644 --- a/crates/yt/src/watch/playlist_handler/mod.rs +++ b/crates/yt/src/watch/playlist_handler/mod.rs @@ -27,7 +27,7 @@ use log::{debug, info}; mod client_messages; #[derive(Debug, Clone, Copy)] -pub enum Status { +pub(crate) enum Status { /// There are no videos cached and no more marked to be watched. /// Waiting is pointless. NoMoreAvailable, @@ -213,7 +213,7 @@ pub async fn status(app: &App) -> Result<Status> { /// # Panics /// Only if internal assertions fail. #[allow(clippy::too_many_lines)] -pub async fn handle_mpv_event(app: &App, mpv: &Mpv, event: &Event<'_>) -> Result<bool> { +pub(crate) async fn handle_mpv_event(app: &App, mpv: &Mpv, event: &Event<'_>) -> Result<bool> { match event { Event::EndFile(r) => match r.reason { EndFileReason::Eof => { |