about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-07-10 16:58:59 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-07-10 16:58:59 +0200
commitc3abafd4878df886dc8765a048cb0b70f282f1f3 (patch)
tree24d5bb2a35d99f7206ba619609e652dffc1c5d17
parentdocs(crates/libmpv2): Correctly format doc-test (diff)
downloadyt-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).
-rw-r--r--crates/yt/src/ansi_escape_codes.rs15
-rw-r--r--crates/yt/src/app.rs8
-rw-r--r--crates/yt/src/cache/mod.rs7
-rw-r--r--crates/yt/src/cli.rs67
-rw-r--r--crates/yt/src/comments/comment.rs61
-rw-r--r--crates/yt/src/comments/description.rs5
-rw-r--r--crates/yt/src/comments/display.rs2
-rw-r--r--crates/yt/src/comments/mod.rs2
-rw-r--r--crates/yt/src/comments/output.rs8
-rw-r--r--crates/yt/src/constants.rs2
-rw-r--r--crates/yt/src/download/download_options.rs7
-rw-r--r--crates/yt/src/download/mod.rs10
-rw-r--r--crates/yt/src/download/progress_hook.rs11
-rw-r--r--crates/yt/src/main.rs36
-rw-r--r--crates/yt/src/select/cmds/add.rs2
-rw-r--r--crates/yt/src/select/mod.rs8
-rw-r--r--crates/yt/src/select/selection_file/duration.rs22
-rw-r--r--crates/yt/src/select/selection_file/mod.rs4
-rw-r--r--crates/yt/src/status/mod.rs12
-rw-r--r--crates/yt/src/storage/migrate/mod.rs22
-rw-r--r--crates/yt/src/storage/mod.rs6
-rw-r--r--crates/yt/src/subscribe/mod.rs11
-rw-r--r--crates/yt/src/update/mod.rs4
-rw-r--r--crates/yt/src/update/updater.rs2
-rw-r--r--crates/yt/src/version/mod.rs2
-rw-r--r--crates/yt/src/videos/display/format_video.rs7
-rw-r--r--crates/yt/src/videos/display/mod.rs50
-rw-r--r--crates/yt/src/videos/mod.rs8
-rw-r--r--crates/yt/src/watch/mod.rs2
-rw-r--r--crates/yt/src/watch/playlist.rs2
-rw-r--r--crates/yt/src/watch/playlist_handler/mod.rs4
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 => {