about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--crates/yt/src/cache/mod.rs64
-rw-r--r--crates/yt/src/cli.rs458
-rw-r--r--crates/yt/src/commands/comments/implm/mod.rs15
-rw-r--r--crates/yt/src/commands/comments/mod.rs6
-rw-r--r--crates/yt/src/commands/config/implm.rs13
-rw-r--r--crates/yt/src/commands/config/mod.rs6
-rw-r--r--crates/yt/src/commands/description/implm.rs16
-rw-r--r--crates/yt/src/commands/description/mod.rs6
-rw-r--r--crates/yt/src/commands/download/implm/download/download_options.rs (renamed from crates/yt/src/download/download_options.rs)0
-rw-r--r--crates/yt/src/commands/download/implm/download/mod.rs (renamed from crates/yt/src/download/mod.rs)114
-rw-r--r--crates/yt/src/commands/download/implm/download/progress_hook.rs (renamed from crates/yt/src/download/progress_hook.rs)39
-rw-r--r--crates/yt/src/commands/download/implm/mod.rs45
-rw-r--r--crates/yt/src/commands/download/mod.rs24
-rw-r--r--crates/yt/src/commands/mod.rs153
-rw-r--r--crates/yt/src/commands/playlist/implm.rs105
-rw-r--r--crates/yt/src/commands/playlist/mod.rs10
-rw-r--r--crates/yt/src/commands/select/implm/fs_generators/help.str (renamed from crates/yt/src/select/selection_file/help.str)0
-rw-r--r--crates/yt/src/commands/select/implm/fs_generators/help.str.license (renamed from crates/yt/src/select/selection_file/help.str.license)0
-rw-r--r--crates/yt/src/commands/select/implm/fs_generators/mod.rs345
-rw-r--r--crates/yt/src/commands/select/implm/mod.rs42
-rw-r--r--crates/yt/src/commands/select/implm/standalone/add.rs (renamed from crates/yt/src/select/cmds/add.rs)13
-rw-r--r--crates/yt/src/commands/select/implm/standalone/mod.rs (renamed from crates/yt/src/select/cmds/mod.rs)15
-rw-r--r--crates/yt/src/commands/select/mod.rs219
-rw-r--r--crates/yt/src/commands/status/implm.rs147
-rw-r--r--crates/yt/src/commands/status/mod.rs10
-rw-r--r--crates/yt/src/commands/subscriptions/implm.rs (renamed from crates/yt/src/subscribe/mod.rs)100
-rw-r--r--crates/yt/src/commands/subscriptions/mod.rs52
-rw-r--r--crates/yt/src/commands/update/implm/mod.rs52
-rw-r--r--crates/yt/src/commands/update/implm/updater.rs (renamed from crates/yt/src/update/updater.rs)74
-rw-r--r--crates/yt/src/commands/update/mod.rs17
-rw-r--r--crates/yt/src/commands/videos/implm.rs63
-rw-r--r--crates/yt/src/commands/videos/mod.rs36
-rw-r--r--crates/yt/src/commands/watch/implm/mod.rs20
-rw-r--r--crates/yt/src/commands/watch/implm/watch/mod.rs (renamed from crates/yt/src/watch/mod.rs)6
-rw-r--r--crates/yt/src/commands/watch/implm/watch/playlist_handler/client_messages.rs (renamed from crates/yt/src/watch/playlist_handler/client_messages/mod.rs)6
-rw-r--r--crates/yt/src/commands/watch/implm/watch/playlist_handler/mod.rs (renamed from crates/yt/src/watch/playlist_handler/mod.rs)0
-rw-r--r--crates/yt/src/commands/watch/mod.rs14
-rw-r--r--crates/yt/src/comments/description.rs39
-rw-r--r--crates/yt/src/comments/mod.rs162
-rw-r--r--crates/yt/src/constants.rs12
-rw-r--r--crates/yt/src/main.rs225
-rw-r--r--crates/yt/src/output/mod.rs (renamed from crates/yt/src/comments/output.rs)11
-rw-r--r--crates/yt/src/select/duration.rs (renamed from crates/yt/src/select/selection_file/duration.rs)2
-rw-r--r--crates/yt/src/select/mod.rs316
-rw-r--r--crates/yt/src/select/selection_file/mod.rs42
-rw-r--r--crates/yt/src/status/mod.rs162
-rw-r--r--crates/yt/src/storage/db/get/video/mod.rs76
-rw-r--r--crates/yt/src/storage/db/video/comments/display.rs (renamed from crates/yt/src/comments/display.rs)83
-rw-r--r--crates/yt/src/storage/db/video/comments/mod.rs187
-rw-r--r--crates/yt/src/storage/db/video/comments/raw.rs (renamed from crates/yt/src/comments/comment.rs)93
-rw-r--r--crates/yt/src/storage/db/video/comments/tests.rs219
-rw-r--r--crates/yt/src/storage/db/video/mod.rs (renamed from crates/yt/src/storage/db/video.rs)13
-rw-r--r--crates/yt/src/update/mod.rs211
-rw-r--r--crates/yt/src/videos/display/mod.rs241
-rw-r--r--crates/yt/src/videos/format_video.rs (renamed from crates/yt/src/videos/display/format_video.rs)71
-rw-r--r--crates/yt/src/watch/playlist.rs111
-rw-r--r--crates/yt/src/yt_dlp/mod.rs249
57 files changed, 2428 insertions, 2402 deletions
diff --git a/crates/yt/src/cache/mod.rs b/crates/yt/src/cache/mod.rs
deleted file mode 100644
index 44a7e72..0000000
--- a/crates/yt/src/cache/mod.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// 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 anyhow::Result;
-use log::info;
-
-use crate::{
-    app::App,
-    storage::db::{
-        insert::{Operations, video::Operation},
-        video::{Video, VideoStatus, VideoStatusMarker},
-    },
-};
-
-fn invalidate_video(video: &mut Video, ops: &mut Operations<Operation>) {
-    info!("Deleting downloaded path of video: '{}'", video.title);
-
-    assert_eq!(video.status.as_marker(), VideoStatusMarker::Cached);
-
-    video.remove_download_path(ops);
-}
-
-pub(crate) async fn invalidate(app: &App) -> Result<()> {
-    let mut all_cached_things = Video::in_states(app, &[VideoStatusMarker::Cached]).await?;
-
-    info!("Got videos to invalidate: '{}'", all_cached_things.len());
-
-    let mut ops = Operations::new("Cache: Invalidate cache entries");
-
-    for video in &mut all_cached_things {
-        invalidate_video(video, &mut ops);
-    }
-
-    ops.commit(app).await?;
-
-    Ok(())
-}
-
-/// Remove the cache paths from the db, that no longer exist on the file system.
-pub(crate) async fn maintain(app: &App) -> Result<()> {
-    let mut cached_videos = Video::in_states(app, &[VideoStatusMarker::Cached]).await?;
-
-    let mut ops = Operations::new("DbMaintain: init");
-    for vid in &mut cached_videos {
-        if let VideoStatus::Cached { cache_path, .. } = &vid.status {
-            if !cache_path.exists() {
-                invalidate_video(vid, &mut ops);
-            }
-        } else {
-            unreachable!("We only asked for cached videos.")
-        }
-    }
-    ops.commit(app).await?;
-
-    Ok(())
-}
diff --git a/crates/yt/src/cli.rs b/crates/yt/src/cli.rs
index f12b58d..9a24403 100644
--- a/crates/yt/src/cli.rs
+++ b/crates/yt/src/cli.rs
@@ -9,28 +9,11 @@
 // 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::{
-    ffi::OsStr,
-    fmt::{self, Display, Formatter},
-    path::PathBuf,
-    str::FromStr,
-    thread,
-};
+use std::path::PathBuf;
 
-use anyhow::Context;
-use chrono::NaiveDate;
-use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum};
-use clap_complete::{ArgValueCompleter, CompletionCandidate};
-use tokio::runtime::Runtime;
-use url::Url;
+use clap::{ArgAction, Parser};
 
-use crate::{
-    app::App,
-    config::Config,
-    select::selection_file::duration::MaybeDuration,
-    shared::bytes::Bytes,
-    storage::db::{extractor_hash::LazyExtractorHash, subscription::Subscriptions},
-};
+use crate::commands::Command;
 
 #[derive(Parser, Debug)]
 #[clap(author, about, long_about = None)]
@@ -72,441 +55,6 @@ pub(crate) struct CliArgs {
     pub(crate) quiet: bool,
 }
 
-#[derive(Subcommand, Debug)]
-pub(crate) enum Command {
-    /// Download and cache URLs
-    Download {
-        /// Forcefully re-download all cached videos (i.e. delete the cache path, then download).
-        #[arg(short, long)]
-        force: bool,
-
-        /// The maximum size the download dir should have. Beware that the value must be given in
-        /// bytes.
-        #[arg(short, long, value_parser = byte_parser)]
-        max_cache_size: Option<u64>,
-    },
-
-    /// Select, download and watch in one command.
-    Sedowa {},
-    /// Download and watch in one command.
-    Dowa {},
-
-    /// Work with single videos
-    Videos {
-        #[command(subcommand)]
-        cmd: VideosCommand,
-    },
-
-    /// Watch the already cached (and selected) videos
-    Watch {
-        /// Print the path to an ipc socket for mpv control to stdout at startup.
-        #[arg(long)]
-        provide_ipc_socket: bool,
-
-        /// Don't start an mpv window at all.
-        #[arg(long)]
-        headless: bool,
-    },
-
-    /// Visualize the current playlist
-    Playlist {
-        /// Linger and display changes
-        #[arg(short, long)]
-        watch: bool,
-    },
-
-    /// Show, which videos have been selected to be watched (and their cache status)
-    Status {
-        /// Which format to use
-        #[arg(short, long)]
-        format: Option<String>,
-    },
-
-    /// Show, the configuration options in effect
-    Config {},
-
-    /// Display the comments of the currently playing video
-    Comments {},
-    /// Display the description of the currently playing video
-    Description {},
-
-    /// Manipulate the video cache in the database
-    #[command(visible_alias = "db")]
-    Database {
-        #[command(subcommand)]
-        command: CacheCommand,
-    },
-
-    /// Change the state of videos in the database (the default)
-    Select {
-        #[command(subcommand)]
-        cmd: Option<SelectCommand>,
-    },
-
-    /// Update the video database
-    Update {
-        /// The maximal number of videos to fetch for each subscription.
-        #[arg(short, long)]
-        max_backlog: Option<usize>,
-
-        /// The subscriptions to update
-        #[arg(add = ArgValueCompleter::new(complete_subscription))]
-        subscriptions: Vec<String>,
-    },
-
-    /// Manipulate subscription
-    #[command(visible_alias = "subs")]
-    Subscriptions {
-        #[command(subcommand)]
-        cmd: SubscriptionCommand,
-    },
-}
-
-fn byte_parser(input: &str) -> Result<u64, anyhow::Error> {
-    Ok(input
-        .parse::<Bytes>()
-        .with_context(|| format!("Failed to parse '{input}' as bytes!"))?
-        .as_u64())
-}
-
-impl Default for Command {
-    fn default() -> Self {
-        Self::Select {
-            cmd: Some(SelectCommand::default()),
-        }
-    }
-}
-
-#[derive(Subcommand, Clone, Debug)]
-pub(crate) enum VideosCommand {
-    /// List the videos in the database
-    #[command(visible_alias = "ls")]
-    List {
-        /// An optional search query to limit the results
-        #[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>,
-    },
-
-    /// Get detailed information about a video
-    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(crate) enum SubscriptionCommand {
-    /// Subscribe to an URL
-    Add {
-        #[arg(short, long)]
-        /// The human readable name of the subscription
-        name: Option<String>,
-
-        /// The URL to listen to
-        url: Url,
-
-        /// Don't check, whether the URL actually points to something yt understands.
-        #[arg(long, default_value_t = false)]
-        no_check: bool,
-    },
-
-    /// Unsubscribe from an URL
-    Remove {
-        /// The human readable name of the subscription
-        #[arg(add = ArgValueCompleter::new(complete_subscription))]
-        name: String,
-    },
-
-    /// Import a bunch of URLs as subscriptions.
-    Import {
-        /// The file containing the URLs. Will use Stdin otherwise.
-        file: Option<PathBuf>,
-
-        /// Remove any previous subscriptions
-        #[arg(short, long)]
-        force: bool,
-
-        /// Don't check, whether the URLs actually point to something yt understands.
-        #[arg(long, default_value_t = false)]
-        no_check: bool,
-    },
-    /// Write all subscriptions in an format understood by `import`
-    Export {},
-
-    /// List all subscriptions
-    List {},
-}
-
-#[derive(Clone, Debug, Args)]
-#[command(infer_subcommands = true)]
-/// Mark the video given by the hash to be watched
-pub(crate) struct SharedSelectionCommandArgs {
-    /// The ordering priority (higher means more at the top)
-    #[arg(short, long)]
-    pub(crate) priority: Option<i64>,
-
-    /// The subtitles to download (e.g. 'en,de,sv')
-    #[arg(short = 'l', long)]
-    pub(crate) subtitle_langs: Option<String>,
-
-    /// The speed to set mpv to
-    #[arg(short = 's', long)]
-    pub(crate) playback_speed: Option<f64>,
-
-    /// The short extractor hash
-    pub(crate) hash: LazyExtractorHash,
-
-    pub(crate) title: Option<String>,
-
-    pub(crate) date: Option<OptionalNaiveDate>,
-
-    pub(crate) publisher: Option<OptionalPublisher>,
-
-    pub(crate) duration: Option<MaybeDuration>,
-
-    pub(crate) url: Option<Url>,
-}
-
-impl SelectCommand {
-    pub(crate) fn into_shared(self) -> Option<SharedSelectionCommandArgs> {
-        match self {
-            SelectCommand::File { .. }
-            | SelectCommand::Split { .. }
-            | SelectCommand::Add { .. } => None,
-            SelectCommand::Watch { shared }
-            | SelectCommand::Drop { shared }
-            | SelectCommand::Watched { shared }
-            | SelectCommand::Url { shared }
-            | SelectCommand::Pick { shared } => Some(shared),
-        }
-    }
-}
-
-#[derive(Clone, Debug, Copy)]
-pub(crate) struct OptionalNaiveDate {
-    pub(crate) date: Option<NaiveDate>,
-}
-impl FromStr for OptionalNaiveDate {
-    type Err = anyhow::Error;
-    fn from_str(v: &str) -> Result<Self, Self::Err> {
-        if v == "[No release date]" {
-            Ok(Self { date: None })
-        } else {
-            Ok(Self {
-                date: Some(NaiveDate::from_str(v)?),
-            })
-        }
-    }
-}
-#[derive(Clone, Debug)]
-pub(crate) struct OptionalPublisher {
-    pub(crate) publisher: Option<String>,
-}
-impl FromStr for OptionalPublisher {
-    type Err = anyhow::Error;
-    fn from_str(v: &str) -> Result<Self, Self::Err> {
-        if v == "[No author]" {
-            Ok(Self { publisher: None })
-        } else {
-            Ok(Self {
-                publisher: Some(v.to_owned()),
-            })
-        }
-    }
-}
-
-#[derive(Default, ValueEnum, Clone, Copy, Debug)]
-pub(crate) enum SelectSplitSortKey {
-    /// Sort by the name of the publisher.
-    #[default]
-    Publisher,
-
-    /// Sort by the number of unselected videos per publisher.
-    Videos,
-}
-impl Display for SelectSplitSortKey {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        match self {
-            SelectSplitSortKey::Publisher => f.write_str("publisher"),
-            SelectSplitSortKey::Videos => f.write_str("videos"),
-        }
-    }
-}
-
-#[derive(Default, ValueEnum, Clone, Copy, Debug)]
-pub(crate) enum SelectSplitSortMode {
-    /// Sort in ascending order (small -> big)
-    #[default]
-    Asc,
-
-    /// Sort in descending order (big -> small)
-    Desc,
-}
-
-impl Display for SelectSplitSortMode {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        match self {
-            SelectSplitSortMode::Asc => f.write_str("asc"),
-            SelectSplitSortMode::Desc => f.write_str("desc"),
-        }
-    }
-}
-
-#[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(crate) enum SelectCommand {
-    /// Open a `git rebase` like file to select the videos to watch (the default)
-    File {
-        /// Include done (watched, dropped) videos
-        #[arg(long, short)]
-        done: bool,
-
-        /// Use the last selection file (useful if you've spend time on it and want to get it again)
-        #[arg(long, short, conflicts_with = "done")]
-        use_last_selection: bool,
-    },
-
-    /// Generate a directory, where each file contains only one subscription.
-    Split {
-        /// Include done (watched, dropped) videos
-        #[arg(long, short)]
-        done: bool,
-
-        /// Which key to use for sorting.
-        #[arg(default_value_t)]
-        sort_key: SelectSplitSortKey,
-
-        /// Which mode to use for sorting.
-        #[arg(default_value_t)]
-        sort_mode: SelectSplitSortMode,
-    },
-
-    /// Add a video to the database
-    ///
-    /// This optionally supports to add a playlist.
-    /// When a playlist is added, the `start` and `stop` arguments can be used to select which
-    /// playlist entries to include.
-    #[command(visible_alias = "a")]
-    Add {
-        urls: Vec<Url>,
-
-        /// Start adding playlist entries at this playlist index (zero based and inclusive)
-        #[arg(short = 's', long)]
-        start: Option<usize>,
-
-        /// Stop adding playlist entries at this playlist index (zero based and inclusive)
-        #[arg(short = 'e', long)]
-        stop: Option<usize>,
-    },
-
-    /// Mark the video given by the hash to be watched
-    #[command(visible_alias = "w")]
-    Watch {
-        #[command(flatten)]
-        shared: SharedSelectionCommandArgs,
-    },
-
-    /// Mark the video given by the hash to be dropped
-    #[command(visible_alias = "d")]
-    Drop {
-        #[command(flatten)]
-        shared: SharedSelectionCommandArgs,
-    },
-
-    /// Mark the video given by the hash as already watched
-    #[command(visible_alias = "wd")]
-    Watched {
-        #[command(flatten)]
-        shared: SharedSelectionCommandArgs,
-    },
-
-    /// Open the video URL in Firefox's `timesinks.youtube` profile
-    #[command(visible_alias = "u")]
-    Url {
-        #[command(flatten)]
-        shared: SharedSelectionCommandArgs,
-    },
-
-    /// Reset the videos status to 'Pick'
-    #[command(visible_alias = "p")]
-    Pick {
-        #[command(flatten)]
-        shared: SharedSelectionCommandArgs,
-    },
-}
-impl Default for SelectCommand {
-    fn default() -> Self {
-        Self::File {
-            done: false,
-            use_last_selection: false,
-        }
-    }
-}
-
-#[derive(Subcommand, Clone, Copy, Debug)]
-pub(crate) enum CacheCommand {
-    /// Invalidate all cache entries
-    Invalidate {},
-
-    /// Perform basic maintenance operations on the database.
-    /// This helps recovering from invalid db states after a crash (or force exit via <CTRL-C>).
-    ///
-    /// 1. Check every path for validity (removing all invalid cache entries)
-    #[command(verbatim_doc_comment)]
-    Maintain {},
-}
-
-fn complete_subscription(current: &OsStr) -> Vec<CompletionCandidate> {
-    let mut output = vec![];
-
-    let Some(current_prog) = current.to_str().map(ToOwned::to_owned) else {
-        return output;
-    };
-
-    let Ok(config) = Config::from_config_file(None, None, None) else {
-        return output;
-    };
-
-    let handle = thread::spawn(move || {
-        let Ok(rt) = Runtime::new() else {
-            return output;
-        };
-
-        let Ok(app) = rt.block_on(App::new(config, false)) else {
-            return output;
-        };
-
-        let Ok(all) = rt.block_on(Subscriptions::get(&app)) else {
-            return output;
-        };
-
-        for sub in all.0.into_keys() {
-            if sub.starts_with(&current_prog) {
-                output.push(CompletionCandidate::new(sub));
-            }
-        }
-
-        output
-    });
-
-    handle.join().unwrap_or_default()
-}
-
 #[cfg(test)]
 mod test {
     use clap::CommandFactory;
diff --git a/crates/yt/src/commands/comments/implm/mod.rs b/crates/yt/src/commands/comments/implm/mod.rs
new file mode 100644
index 0000000..1c02718
--- /dev/null
+++ b/crates/yt/src/commands/comments/implm/mod.rs
@@ -0,0 +1,15 @@
+use crate::{
+    app::App, commands::comments::CommentsCommand, output::display_less, storage::db::video::Video,
+};
+
+use anyhow::Result;
+
+impl CommentsCommand {
+    pub(crate) async fn implm(self, app: &App) -> Result<()> {
+        let comments = Video::get_current_comments(app).await?;
+
+        display_less(comments.render(app.config.global.display_colors))?;
+
+        Ok(())
+    }
+}
diff --git a/crates/yt/src/commands/comments/mod.rs b/crates/yt/src/commands/comments/mod.rs
new file mode 100644
index 0000000..d87c75d
--- /dev/null
+++ b/crates/yt/src/commands/comments/mod.rs
@@ -0,0 +1,6 @@
+use clap::Parser;
+
+mod implm;
+
+#[derive(Parser, Debug)]
+pub(crate) struct CommentsCommand {}
diff --git a/crates/yt/src/commands/config/implm.rs b/crates/yt/src/commands/config/implm.rs
new file mode 100644
index 0000000..409ef43
--- /dev/null
+++ b/crates/yt/src/commands/config/implm.rs
@@ -0,0 +1,13 @@
+use crate::{app::App, commands::config::ConfigCommand};
+
+use anyhow::Result;
+
+impl ConfigCommand {
+    pub(crate) fn implm(self, app: &App) -> Result<()> {
+        let config_str = toml::to_string(&app.config)?;
+
+        print!("{config_str}");
+
+        Ok(())
+    }
+}
diff --git a/crates/yt/src/commands/config/mod.rs b/crates/yt/src/commands/config/mod.rs
new file mode 100644
index 0000000..9ec289b
--- /dev/null
+++ b/crates/yt/src/commands/config/mod.rs
@@ -0,0 +1,6 @@
+use clap::Parser;
+
+mod implm;
+
+#[derive(Parser, Debug)]
+pub(crate) struct ConfigCommand {}
diff --git a/crates/yt/src/commands/description/implm.rs b/crates/yt/src/commands/description/implm.rs
new file mode 100644
index 0000000..7c39b1c
--- /dev/null
+++ b/crates/yt/src/commands/description/implm.rs
@@ -0,0 +1,16 @@
+use crate::{
+    app::App, commands::description::DescriptionCommand, output::display_fmt_and_less,
+    storage::db::video::Video,
+};
+
+use anyhow::Result;
+
+impl DescriptionCommand {
+    pub(crate) async fn implm(self, app: &App) -> Result<()> {
+        let description = Video::get_current_description(app).await?;
+
+        display_fmt_and_less(&description)?;
+
+        Ok(())
+    }
+}
diff --git a/crates/yt/src/commands/description/mod.rs b/crates/yt/src/commands/description/mod.rs
new file mode 100644
index 0000000..b5b2a10
--- /dev/null
+++ b/crates/yt/src/commands/description/mod.rs
@@ -0,0 +1,6 @@
+use clap::Parser;
+
+mod implm;
+
+#[derive(Parser, Debug)]
+pub(crate) struct DescriptionCommand {}
diff --git a/crates/yt/src/download/download_options.rs b/crates/yt/src/commands/download/implm/download/download_options.rs
index 15fed7e..15fed7e 100644
--- a/crates/yt/src/download/download_options.rs
+++ b/crates/yt/src/commands/download/implm/download/download_options.rs
diff --git a/crates/yt/src/download/mod.rs b/crates/yt/src/commands/download/implm/download/mod.rs
index 3eb046a..f0d5f67 100644
--- a/crates/yt/src/download/mod.rs
+++ b/crates/yt/src/commands/download/implm/download/mod.rs
@@ -9,23 +9,23 @@
 // 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::{collections::HashMap, io, path::PathBuf, str::FromStr, sync::Arc, time::Duration};
+use std::{collections::HashMap, path::PathBuf, sync::Arc, time::Duration};
 
 use crate::{
     app::App,
-    download::download_options::download_opts,
+    commands::download::implm::download::download_options::download_opts,
     shared::bytes::Bytes,
     storage::{
         db::{extractor_hash::ExtractorHash, insert::Operations, video::Video},
         notify::{wait_for_cache_reduction, wait_for_db_write},
     },
+    yt_dlp::get_current_cache_allocation,
 };
 
 use anyhow::{Context, Result, bail};
-use futures::{FutureExt, future::BoxFuture};
 use log::{debug, error, info, warn};
-use tokio::{fs, select, task::JoinHandle, time};
-use yt_dlp::{YoutubeDL, json_cast, json_get, options::YoutubeDLOptions};
+use tokio::{select, task::JoinHandle, time};
+use yt_dlp::YoutubeDL;
 
 #[allow(clippy::module_name_repetitions)]
 pub(crate) mod download_options;
@@ -146,7 +146,7 @@ impl Downloader {
                 return Ok(CacheSizeCheck::Fits);
             }
         }
-        let cache_allocation = Self::get_current_cache_allocation(app).await?;
+        let cache_allocation = get_current_cache_allocation(app).await?;
         let video_size = self.get_approx_video_size(next_video)?;
 
         if video_size >= max_cache_size {
@@ -239,10 +239,10 @@ impl Downloader {
                 } else {
                     info!(
                         "Noticed, that the next video is not the video being downloaded, replacing it ('{}' vs. '{}')!",
-                        next_video.extractor_hash.into_short_hash(&app).await?,
+                        next_video.extractor_hash.as_short_hash(&app).await?,
                         current_download
                             .extractor_hash
-                            .into_short_hash(&app)
+                            .as_short_hash(&app)
                             .await?
                     );
 
@@ -276,107 +276,11 @@ impl Downloader {
         Ok(())
     }
 
-    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;
-                while let Some(entry) = dir.next_entry().await? {
-                    let size = match entry.metadata().await? {
-                        data if data.is_dir() => {
-                            let path = entry.path();
-                            let read_dir = fs::read_dir(path).await?;
-
-                            dir_size(read_dir).await?.as_u64()
-                        }
-                        data => data.len(),
-                    };
-                    acc += size;
-                }
-                Ok(Bytes::new(acc))
-            }
-            .boxed()
-        }
-
-        let read_dir_result = match fs::read_dir(&app.config.paths.download_dir).await {
-            Ok(ok) => ok,
-            Err(err) => match err.kind() {
-                io::ErrorKind::NotFound => {
-                    fs::create_dir_all(&app.config.paths.download_dir)
-                        .await
-                        .with_context(|| {
-                            format!(
-                                "Failed to create download dir at: '{}'",
-                                &app.config.paths.download_dir.display()
-                            )
-                        })?;
-
-                    info!(
-                        "Created empty download dir at '{}'",
-                        &app.config.paths.download_dir.display(),
-                    );
-
-                    // The new dir should not contain anything (otherwise we would not have had to
-                    // create it)
-                    return Ok(Bytes::new(0));
-                }
-                err => Err(io::Error::from(err)).with_context(|| {
-                    format!(
-                        "Failed to get dir size of download dir at: '{}'",
-                        &app.config.paths.download_dir.display()
-                    )
-                })?,
-            },
-        };
-
-        dir_size(read_dir_result).await
-    }
-
     fn get_approx_video_size(&mut self, video: &Video) -> Result<u64> {
         if let Some(value) = self.video_size_cache.get(&video.extractor_hash) {
             Ok(*value)
         } else {
-            let yt_dlp = {
-                YoutubeDLOptions::new()
-                    .set("prefer_free_formats", true)
-                    .set("format", "bestvideo[height<=?1080]+bestaudio/best")
-                    .set("fragment_retries", 10)
-                    .set("retries", 10)
-                    .set("getcomments", false)
-                    .set("ignoreerrors", false)
-                    .build()
-                    .context("Failed to instanciate get approx size yt_dlp")
-            }?;
-
-            let result = yt_dlp
-                .extract_info(&video.url, false, true)
-                .with_context(|| {
-                    format!("Failed to extract video information: '{}'", video.title)
-                })?;
-
-            let size = if let Some(val) = result.get("filesize") {
-                json_cast!(val, as_u64)
-            } else if let Some(serde_json::Value::Number(num)) = result.get("filesize_approx") {
-                // NOTE(@bpeetz): yt_dlp sets this value to `Null`, instead of omitting it when it
-                // can't calculate the approximate filesize.
-                // Thus, we have to check, that it is actually non-null, before we cast it. <2025-06-15>
-                json_cast!(num, as_u64)
-            } else if result.get("duration").is_some() && result.get("tbr").is_some() {
-                #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
-                let duration = json_get!(result, "duration", as_f64).ceil() as u64;
-
-                // TODO: yt_dlp gets this from the format
-                #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
-                let tbr = json_get!(result, "tbr", as_f64).ceil() as u64;
-
-                duration * tbr * (1000 / 8)
-            } else {
-                let hardcoded_default = Bytes::from_str("250 MiB").expect("This is hardcoded");
-                error!(
-                    "Failed to find a filesize for video: '{}' (Using hardcoded value of {})",
-                    video.title, hardcoded_default
-                );
-                hardcoded_default.as_u64()
-            };
+            let size = video.get_approx_size()?;
 
             assert_eq!(
                 self.video_size_cache.insert(video.extractor_hash, size),
diff --git a/crates/yt/src/download/progress_hook.rs b/crates/yt/src/commands/download/implm/download/progress_hook.rs
index e5605fd..19fe122 100644
--- a/crates/yt/src/download/progress_hook.rs
+++ b/crates/yt/src/commands/download/implm/download/progress_hook.rs
@@ -11,15 +11,17 @@
 use std::{
     io::{Write, stderr},
     process,
+    sync::atomic::Ordering,
 };
 
+use colors::{Colorize, IntoCanvas};
 use log::{Level, log_enabled};
-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,
+    config::SHOULD_DISPLAY_COLOR,
+    select::duration::MaybeDuration,
     shared::bytes::Bytes,
 };
 
@@ -125,16 +127,33 @@ pub(crate) fn progress_hook(
             clear_whole_line();
             move_to_col(1);
 
+            let should_use_color = SHOULD_DISPLAY_COLOR.load(Ordering::Relaxed);
+
             eprint!(
                 "{} [{}/{} at {}] -> [{} of {}{} {}] ",
-                get_title().bold().blue(),
-                MaybeDuration::from_secs_f64(elapsed).bold().yellow(),
-                MaybeDuration::from_secs_f64(eta).bold().yellow(),
-                format_speed(speed).bold().green(),
-                format_bytes(downloaded_bytes).bold().red(),
-                bytes_is_estimate.bold().red(),
-                format_bytes(total_bytes).bold().red(),
-                format!("{percent:.02}%").bold().cyan(),
+                get_title().bold().blue().render(should_use_color),
+                MaybeDuration::from_secs_f64(elapsed)
+                    .bold()
+                    .yellow()
+                    .render(should_use_color),
+                MaybeDuration::from_secs_f64(eta)
+                    .bold()
+                    .yellow()
+                    .render(should_use_color),
+                format_speed(speed).bold().green().render(should_use_color),
+                format_bytes(downloaded_bytes)
+                    .bold()
+                    .red()
+                    .render(should_use_color),
+                bytes_is_estimate.bold().red().render(should_use_color),
+                format_bytes(total_bytes)
+                    .bold()
+                    .red()
+                    .render(should_use_color),
+                format!("{percent:.02}%")
+                    .bold()
+                    .cyan()
+                    .render(should_use_color),
             );
             stderr().flush()?;
         }
diff --git a/crates/yt/src/commands/download/implm/mod.rs b/crates/yt/src/commands/download/implm/mod.rs
new file mode 100644
index 0000000..e8867cf
--- /dev/null
+++ b/crates/yt/src/commands/download/implm/mod.rs
@@ -0,0 +1,45 @@
+use std::sync::Arc;
+
+use crate::{
+    app::App,
+    commands::download::DownloadCommand,
+    shared::bytes::Bytes,
+    storage::db::{
+        insert::{Operations, maintenance::clear_stale_downloaded_paths},
+        video::{Video, VideoStatusMarker},
+    },
+};
+
+use anyhow::Result;
+use log::info;
+
+mod download;
+
+impl DownloadCommand {
+    pub(crate) async fn implm(self, app: Arc<App>) -> Result<()> {
+        let DownloadCommand {
+            force,
+            max_cache_size,
+        } = self;
+
+        let max_cache_size = max_cache_size.unwrap_or(app.config.download.max_cache_size.as_u64());
+        info!("Max cache size: '{}'", Bytes::new(max_cache_size));
+
+        clear_stale_downloaded_paths(&app).await?;
+        if force {
+            let mut all = Video::in_states(&app, &[VideoStatusMarker::Cached]).await?;
+
+            let mut ops = Operations::new("Download: Clear old download paths due to `--force`");
+            for a in &mut all {
+                a.remove_download_path(&mut ops);
+            }
+            ops.commit(&app).await?;
+        }
+
+        download::Downloader::new()
+            .consume(app, max_cache_size)
+            .await?;
+
+        Ok(())
+    }
+}
diff --git a/crates/yt/src/commands/download/mod.rs b/crates/yt/src/commands/download/mod.rs
new file mode 100644
index 0000000..48c6ee4
--- /dev/null
+++ b/crates/yt/src/commands/download/mod.rs
@@ -0,0 +1,24 @@
+use anyhow::Context;
+use clap::Parser;
+
+use crate::shared::bytes::Bytes;
+
+mod implm;
+
+#[derive(Parser, Debug)]
+pub(crate) struct DownloadCommand {
+    /// Forcefully re-download all cached videos (i.e. delete all already downloaded paths, then download).
+    #[arg(short, long)]
+    force: bool,
+
+    /// The maximum size the download dir should have.
+    #[arg(short, long, value_parser = byte_parser)]
+    max_cache_size: Option<u64>,
+}
+
+fn byte_parser(input: &str) -> Result<u64, anyhow::Error> {
+    Ok(input
+        .parse::<Bytes>()
+        .with_context(|| format!("Failed to parse '{input}' as bytes!"))?
+        .as_u64())
+}
diff --git a/crates/yt/src/commands/mod.rs b/crates/yt/src/commands/mod.rs
new file mode 100644
index 0000000..a6aa2af
--- /dev/null
+++ b/crates/yt/src/commands/mod.rs
@@ -0,0 +1,153 @@
+use std::{ffi::OsStr, thread};
+
+use clap::Subcommand;
+use clap_complete::CompletionCandidate;
+use tokio::runtime::Runtime;
+
+use crate::{
+    app::App,
+    commands::{
+        comments::CommentsCommand, config::ConfigCommand, database::DatabaseCommand,
+        description::DescriptionCommand, download::DownloadCommand, playlist::PlaylistCommand,
+        select::SelectCommand, status::StatusCommand, subscriptions::SubscriptionCommand,
+        update::UpdateCommand, videos::VideosCommand, watch::WatchCommand,
+    },
+    config::Config,
+    storage::db::subscription::Subscriptions,
+};
+
+pub(crate) mod implm;
+
+mod comments;
+mod config;
+mod database;
+mod description;
+mod download;
+mod playlist;
+mod select;
+mod status;
+mod subscriptions;
+mod update;
+mod videos;
+mod watch;
+
+#[derive(Subcommand, Debug)]
+pub(crate) enum Command {
+    /// Display the comments of the currently playing video.
+    Comments {
+        #[command(flatten)]
+        cmd: CommentsCommand,
+    },
+
+    /// Show, the configuration options in effect.
+    Config {
+        #[command(flatten)]
+        cmd: ConfigCommand,
+    },
+
+    /// Interact with the video database.
+    #[command(visible_alias = "db")]
+    Database {
+        #[command(subcommand)]
+        cmd: DatabaseCommand,
+    },
+
+    /// Display the description of the currently playing video
+    Description {
+        #[command(flatten)]
+        cmd: DescriptionCommand,
+    },
+
+    /// Download and cache URLs
+    Download {
+        #[command(flatten)]
+        cmd: DownloadCommand,
+    },
+
+    /// Visualize the current playlist
+    Playlist {
+        #[command(flatten)]
+        cmd: PlaylistCommand,
+    },
+
+    /// Change the state of videos in the database (the default)
+    Select {
+        #[command(subcommand)]
+        cmd: Option<SelectCommand>,
+    },
+
+    /// Show, which videos have been selected to be watched (and their cache status)
+    Status {
+        #[command(flatten)]
+        cmd: StatusCommand,
+    },
+
+    /// Manipulate subscription
+    #[command(visible_alias = "subs")]
+    Subscriptions {
+        #[command(subcommand)]
+        cmd: SubscriptionCommand,
+    },
+
+    /// Update the video database
+    Update {
+        #[command(flatten)]
+        cmd: UpdateCommand,
+    },
+
+    /// Work with single videos
+    Videos {
+        #[command(subcommand)]
+        cmd: VideosCommand,
+    },
+
+    /// Watch the already cached (and selected) videos
+    Watch {
+        #[command(flatten)]
+        cmd: WatchCommand,
+    },
+}
+
+impl Default for Command {
+    fn default() -> Self {
+        Self::Select {
+            cmd: Some(SelectCommand::default()),
+        }
+    }
+}
+
+fn complete_subscription(current: &OsStr) -> Vec<CompletionCandidate> {
+    let mut output = vec![];
+
+    let Some(current_prog) = current.to_str().map(ToOwned::to_owned) else {
+        return output;
+    };
+
+    let Ok(config) = Config::from_config_file(None, None, None) else {
+        return output;
+    };
+
+    let handle = thread::spawn(move || {
+        let Ok(rt) = Runtime::new() else {
+            return output;
+        };
+
+        let Ok(app) = rt.block_on(App::new(config, false)) else {
+            return output;
+        };
+
+        let Ok(all) = rt.block_on(Subscriptions::get(&app)) else {
+            return output;
+        };
+
+        for sub in all.0.into_keys() {
+            if sub.starts_with(&current_prog) {
+                output.push(CompletionCandidate::new(sub));
+            }
+        }
+
+        output
+    });
+
+    handle.join().unwrap_or_default()
+}
diff --git a/crates/yt/src/commands/playlist/implm.rs b/crates/yt/src/commands/playlist/implm.rs
new file mode 100644
index 0000000..98a8e64
--- /dev/null
+++ b/crates/yt/src/commands/playlist/implm.rs
@@ -0,0 +1,105 @@
+use std::{fmt::Write, path::Path};
+
+use crate::{
+    ansi_escape_codes,
+    app::App,
+    commands::playlist::PlaylistCommand,
+    storage::{
+        db::{
+            playlist::Playlist,
+            video::{Video, VideoStatus},
+        },
+        notify::wait_for_db_write,
+    },
+    videos::RenderWithApp,
+};
+
+use anyhow::Result;
+use futures::{TryStreamExt, stream::FuturesOrdered};
+
+impl PlaylistCommand {
+    pub(crate) async fn implm(self, app: &App) -> Result<()> {
+        let PlaylistCommand { watch } = self;
+
+        let mut previous_output_length = 0;
+        loop {
+            let playlist = Playlist::create(app).await?.videos;
+
+            let output = playlist
+                .into_iter()
+                .map(|video| async move {
+                    let mut output = String::new();
+
+                    let (_, is_focused) = cache_values(&video);
+
+                    if is_focused {
+                        output.push_str("🔻 ");
+                    } else {
+                        output.push_str("  ");
+                    }
+
+                    output.push_str(&video.title_fmt().to_string(app));
+
+                    output.push_str(" (");
+                    output.push_str(&video.parent_subscription_name_fmt().to_string(app));
+                    output.push(')');
+
+                    output.push_str(" [");
+                    output.push_str(&video.duration_fmt().to_string(app));
+
+                    if is_focused {
+                        output.push_str(" (");
+                        if let Some(duration) = video.duration.as_secs() {
+                            let watch_progress: f64 = f64::from(
+                                u32::try_from(video.watch_progress.as_secs()).expect("No overflow"),
+                            );
+                            let duration = f64::from(u32::try_from(duration).expect("No overflow"));
+
+                            write!(output, "{:0.0}%", (watch_progress / duration) * 100.0)?;
+                        } else {
+                            write!(output, "{}", video.watch_progress_fmt().to_string(app))?;
+                        }
+
+                        output.push(')');
+                    }
+                    output.push(']');
+
+                    output.push('\n');
+
+                    Ok::<String, anyhow::Error>(output)
+                })
+                .collect::<FuturesOrdered<_>>()
+                .try_collect::<String>()
+                .await?;
+
+            // Delete the previous output
+            ansi_escape_codes::cursor_up(previous_output_length);
+            ansi_escape_codes::erase_from_cursor_to_bottom();
+
+            previous_output_length = output.chars().filter(|ch| *ch == '\n').count();
+
+            print!("{output}");
+
+            if !watch {
+                break;
+            }
+
+            wait_for_db_write(app).await?;
+        }
+
+        Ok(())
+    }
+}
+
+/// Extract the values of the [`VideoStatus::Cached`] value from a Video.
+fn cache_values(video: &Video) -> (&Path, bool) {
+    if let VideoStatus::Cached {
+        cache_path,
+        is_focused,
+    } = &video.status
+    {
+        (cache_path, *is_focused)
+    } else {
+        unreachable!("All of these videos should be cached");
+    }
+}
diff --git a/crates/yt/src/commands/playlist/mod.rs b/crates/yt/src/commands/playlist/mod.rs
new file mode 100644
index 0000000..8290b3e
--- /dev/null
+++ b/crates/yt/src/commands/playlist/mod.rs
@@ -0,0 +1,10 @@
+use clap::Parser;
+
+mod implm;
+
+#[derive(Parser, Debug)]
+pub(crate) struct PlaylistCommand {
+    /// Linger and display changes
+    #[arg(short, long)]
+    watch: bool,
+}
diff --git a/crates/yt/src/select/selection_file/help.str b/crates/yt/src/commands/select/implm/fs_generators/help.str
index e3cc347..e3cc347 100644
--- a/crates/yt/src/select/selection_file/help.str
+++ b/crates/yt/src/commands/select/implm/fs_generators/help.str
diff --git a/crates/yt/src/select/selection_file/help.str.license b/crates/yt/src/commands/select/implm/fs_generators/help.str.license
index a0e196c..a0e196c 100644
--- a/crates/yt/src/select/selection_file/help.str.license
+++ b/crates/yt/src/commands/select/implm/fs_generators/help.str.license
diff --git a/crates/yt/src/commands/select/implm/fs_generators/mod.rs b/crates/yt/src/commands/select/implm/fs_generators/mod.rs
new file mode 100644
index 0000000..8ccda3c
--- /dev/null
+++ b/crates/yt/src/commands/select/implm/fs_generators/mod.rs
@@ -0,0 +1,345 @@
+use std::{
+    collections::HashMap,
+    env,
+    fs::{self, File, OpenOptions},
+    io::{BufRead, BufReader, BufWriter, Read, Write},
+    iter,
+    os::fd::{AsFd, AsRawFd},
+    path::Path,
+};
+
+use crate::{
+    app::App,
+    cli::CliArgs,
+    commands::{
+        Command,
+        select::{
+            SelectCommand, SelectSplitSortKey, SelectSplitSortMode,
+            implm::standalone::{self, handle_select_cmd},
+        },
+    },
+    storage::db::{
+        extractor_hash::ExtractorHash,
+        insert::Operations,
+        video::{Video, VideoStatusMarker},
+    },
+};
+
+use anyhow::{Context, Result, bail};
+use clap::Parser;
+use futures::{TryStreamExt, stream::FuturesOrdered};
+use log::info;
+use shlex::Shlex;
+use tokio::process;
+
+const HELP_STR: &str = include_str!("./help.str");
+
+pub(crate) async fn select_split(
+    app: &App,
+    done: bool,
+    sort_key: SelectSplitSortKey,
+    sort_mode: SelectSplitSortMode,
+) -> Result<()> {
+    let temp_dir = tempfile::Builder::new()
+        .prefix("yt_video_select-")
+        .rand_bytes(6)
+        .tempdir()
+        .context("Failed to get tempdir")?;
+
+    let matching_videos = get_videos(app, done).await?;
+
+    let mut no_author = vec![];
+    let mut author_map = HashMap::new();
+    for video in matching_videos {
+        if let Some(sub) = &video.parent_subscription_name {
+            if author_map.contains_key(sub) {
+                let vec: &mut Vec<_> = author_map
+                    .get_mut(sub)
+                    .expect("This key is set, we checked in the if above");
+
+                vec.push(video);
+            } else {
+                author_map.insert(sub.to_owned(), vec![video]);
+            }
+        } else {
+            no_author.push(video);
+        }
+    }
+
+    let author_map = {
+        let mut temp_vec: Vec<_> = author_map.into_iter().collect();
+
+        match sort_key {
+            SelectSplitSortKey::Publisher => {
+                // PERFORMANCE: The clone here should not be neeed.  <2025-06-15>
+                temp_vec.sort_by_key(|(name, _): &(String, Vec<Video>)| name.to_owned());
+            }
+            SelectSplitSortKey::Videos => {
+                temp_vec.sort_by_key(|(_, videos): &(String, Vec<Video>)| videos.len());
+            }
+        }
+
+        match sort_mode {
+            SelectSplitSortMode::Asc => {
+                // Std's default mode is ascending.
+            }
+            SelectSplitSortMode::Desc => {
+                temp_vec.reverse();
+            }
+        }
+
+        temp_vec
+    };
+
+    for (index, (name, videos)) in author_map
+        .into_iter()
+        .chain(iter::once((
+            "<No parent subscription>".to_owned(),
+            no_author,
+        )))
+        .enumerate()
+    {
+        let mut file_path = temp_dir.path().join(format!("{index:02}_{name}"));
+        file_path.set_extension("yts");
+
+        let tmp_file = File::create(&file_path)
+            .with_context(|| format!("Falied to create file at: {}", file_path.display()))?;
+
+        write_videos_to_file(app, &tmp_file, &videos)
+            .await
+            .with_context(|| format!("Falied to populate file at: {}", file_path.display()))?;
+    }
+
+    open_editor_at(temp_dir.path()).await?;
+
+    let mut paths = vec![];
+    for maybe_entry in temp_dir
+        .path()
+        .read_dir()
+        .context("Failed to open temp dir for reading")?
+    {
+        let entry = maybe_entry.context("Failed to read entry in temp dir")?;
+
+        if !entry.file_type()?.is_file() {
+            bail!("Found non-file entry: {}", entry.path().display());
+        }
+
+        paths.push(entry.path());
+    }
+
+    paths.sort();
+
+    let mut persistent_file = OpenOptions::new()
+        .read(false)
+        .write(true)
+        .create(true)
+        .truncate(true)
+        .open(&app.config.paths.last_selection_path)
+        .context("Failed to open persistent selection file")?;
+
+    for path in paths {
+        let mut read_file = File::open(path)?;
+
+        let mut buffer = vec![];
+        read_file.read_to_end(&mut buffer)?;
+        persistent_file.write_all(&buffer)?;
+    }
+
+    persistent_file.flush()?;
+    let persistent_file = OpenOptions::new()
+        .read(true)
+        .open(format!(
+            "/proc/self/fd/{}",
+            persistent_file.as_fd().as_raw_fd()
+        ))
+        .context("Failed to re-open persistent file")?;
+
+    let processed = process_file(app, &persistent_file).await?;
+
+    info!("Processed {processed} records.");
+    temp_dir.close().context("Failed to close the temp dir")?;
+    Ok(())
+}
+
+pub(crate) async fn select_file(app: &App, done: bool, use_last_selection: bool) -> Result<()> {
+    let temp_file = tempfile::Builder::new()
+        .prefix("yt_video_select-")
+        .suffix(".yts")
+        .rand_bytes(6)
+        .tempfile()
+        .context("Failed to get tempfile")?;
+
+    if use_last_selection {
+        fs::copy(&app.config.paths.last_selection_path, &temp_file)?;
+    } else {
+        let matching_videos = get_videos(app, done).await?;
+
+        write_videos_to_file(app, temp_file.as_file(), &matching_videos).await?;
+    }
+
+    open_editor_at(temp_file.path()).await?;
+
+    let read_file = OpenOptions::new().read(true).open(temp_file.path())?;
+    fs::copy(temp_file.path(), &app.config.paths.last_selection_path)
+        .context("Failed to persist selection file")?;
+
+    let processed = process_file(app, &read_file).await?;
+    info!("Processed {processed} records.");
+
+    Ok(())
+}
+
+async fn get_videos(app: &App, include_done: bool) -> Result<Vec<Video>> {
+    if include_done {
+        Video::in_states(app, VideoStatusMarker::ALL).await
+    } else {
+        Video::in_states(
+            app,
+            &[
+                VideoStatusMarker::Pick,
+                //
+                VideoStatusMarker::Watch,
+                VideoStatusMarker::Cached,
+            ],
+        )
+        .await
+    }
+}
+
+async fn write_videos_to_file(app: &App, file: &File, videos: &[Video]) -> Result<()> {
+    // 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, None).await?);
+    }
+
+    let mut edit_file = BufWriter::new(file);
+
+    videos
+        .iter()
+        .map(|vid| vid.to_select_file_display(app))
+        .collect::<FuturesOrdered<_>>()
+        .try_collect::<Vec<String>>()
+        .await?
+        .into_iter()
+        .try_for_each(|line| -> Result<()> {
+            edit_file
+                .write_all(line.as_bytes())
+                .context("Failed to write to `edit_file`")?;
+
+            Ok(())
+        })?;
+
+    edit_file.write_all(HELP_STR.as_bytes())?;
+    edit_file.flush().context("Failed to flush edit file")?;
+
+    Ok(())
+}
+
+async fn process_file(app: &App, file: &File) -> Result<i64> {
+    let mut line_number = 0;
+
+    let mut ops = Operations::new("Select: process file");
+
+    // Fetch all the hashes once, instead of every time we need to process a line.
+    let all_hashes = ExtractorHash::get_all(app).await?;
+
+    let reader = BufReader::new(file);
+    for line in reader.lines() {
+        let line = line.context("Failed to read a line")?;
+
+        if let Some(line) = process_line(&line)? {
+            line_number -= 1;
+
+            // debug!(
+            //     "Parsed command: `{}`",
+            //     line.iter()
+            //         .map(|val| format!("\"{}\"", val))
+            //         .collect::<Vec<String>>()
+            //         .join(" ")
+            // );
+
+            let arg_line = ["yt", "select"]
+                .into_iter()
+                .chain(line.iter().map(String::as_str));
+
+            let args = CliArgs::parse_from(arg_line);
+
+            let Command::Select { cmd } = args
+                .command
+                .expect("This will be some, as we constructed it above.")
+            else {
+                unreachable!("This is checked in the `filter_line` function")
+            };
+
+            match cmd.expect(
+                "This value should always be some \
+                    here, as it would otherwise thrown an error above.",
+            ) {
+                SelectCommand::File { .. } | SelectCommand::Split { .. } => {
+                    bail!("You cannot use `select file` or `select split` recursively.")
+                }
+                SelectCommand::Add { urls, start, stop } => {
+                    Box::pin(standalone::add::add(app, urls, start, stop)).await?;
+                }
+                other => {
+                    let shared = other
+                        .clone()
+                        .into_shared()
+                        .expect("The ones without shared should have been filtered out.");
+
+                    let hash = shared.hash.realize(app, Some(&all_hashes)).await?;
+                    let mut video = hash
+                        .get_with_app(app)
+                        .await
+                        .expect("The hash was already realized, it should therefore exist");
+
+                    handle_select_cmd(app, other, &mut video, Some(line_number), &mut ops).await?;
+                }
+            }
+        }
+    }
+
+    ops.commit(app).await?;
+    Ok(-line_number)
+}
+
+async fn open_editor_at(path: &Path) -> Result<()> {
+    let editor = env::var("EDITOR").unwrap_or("nvim".to_owned());
+
+    let mut nvim = process::Command::new(&editor);
+    nvim.arg(path);
+    let status = nvim
+        .status()
+        .await
+        .with_context(|| format!("Falied to run editor: {editor}"))?;
+
+    if status.success() {
+        Ok(())
+    } else {
+        bail!("Editor ({editor}) exited with error status: {}", status)
+    }
+}
+
+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)
+    } else {
+        let split: Vec<_> = {
+            let mut shl = Shlex::new(line);
+            let res = shl.by_ref().collect();
+
+            if shl.had_error {
+                bail!("Failed to parse line '{line}'")
+            }
+
+            assert_eq!(shl.line_no, 1, "A unexpected newline appeared");
+            res
+        };
+
+        assert!(!split.is_empty());
+
+        Ok(Some(split))
+    }
+}
diff --git a/crates/yt/src/commands/select/implm/mod.rs b/crates/yt/src/commands/select/implm/mod.rs
new file mode 100644
index 0000000..755076c
--- /dev/null
+++ b/crates/yt/src/commands/select/implm/mod.rs
@@ -0,0 +1,42 @@
+use crate::{app::App, commands::select::SelectCommand, storage::db::insert::Operations};
+
+use anyhow::Result;
+
+mod fs_generators;
+mod standalone;
+
+impl SelectCommand {
+    pub(crate) async fn implm(self, app: &App) -> Result<()> {
+        match self {
+            SelectCommand::File {
+                done,
+                use_last_selection,
+            } => Box::pin(fs_generators::select_file(&app, done, use_last_selection)).await?,
+            SelectCommand::Split {
+                done,
+                sort_key,
+                sort_mode,
+            } => Box::pin(fs_generators::select_split(&app, done, sort_key, sort_mode)).await?,
+            SelectCommand::Add { urls, start, stop } => {
+                Box::pin(standalone::add::add(&app, urls, start, stop)).await?;
+            }
+            other => {
+                let shared = other
+                    .clone()
+                    .into_shared()
+                    .expect("The ones without shared should have been filtered out.");
+                let hash = shared.hash.realize(&app, None).await?;
+                let mut video = hash
+                    .get_with_app(&app)
+                    .await
+                    .expect("The hash was already realized, it should therefore exist");
+
+                let mut ops = Operations::new("Main: handle select cmd");
+                standalone::handle_select_cmd(&app, other, &mut video, None, &mut ops).await?;
+                ops.commit(&app).await?;
+            }
+        }
+
+        Ok(())
+    }
+}
diff --git a/crates/yt/src/select/cmds/add.rs b/crates/yt/src/commands/select/implm/standalone/add.rs
index 43c9f75..ec32039 100644
--- a/crates/yt/src/select/cmds/add.rs
+++ b/crates/yt/src/commands/select/implm/standalone/add.rs
@@ -10,9 +10,8 @@
 
 use crate::{
     app::App,
-    download::download_options::download_opts,
-    storage::db::{extractor_hash::ExtractorHash, insert::Operations},
-    update::video_entry_to_video,
+    storage::db::{extractor_hash::ExtractorHash, insert::Operations, video::Video},
+    yt_dlp::yt_dlp_opts_updating,
 };
 
 use anyhow::{Context, Result, bail};
@@ -52,7 +51,7 @@ pub(crate) async fn add(
                 error!(
                     "Video '{}'{} is already in the database. Skipped adding it",
                     extractor_hash
-                        .into_short_hash(app)
+                        .as_short_hash(app)
                         .await
                         .with_context(|| format!(
                             "Failed to format hash of video '{}' as short hash",
@@ -69,7 +68,7 @@ pub(crate) async fn add(
             }
 
             let mut ops = Operations::new("SelectAdd: Video entry to video");
-            let video = video_entry_to_video(&entry, None)?.add(&mut ops)?;
+            let video = Video::from_info_json(&entry, None)?.add(&mut ops)?;
             ops.commit(app).await?;
 
             println!("{}", &video.to_line_display(app, None).await?);
@@ -77,7 +76,7 @@ pub(crate) async fn add(
             Ok(())
         }
 
-        let yt_dlp = download_opts(app, None)?;
+        let yt_dlp = yt_dlp_opts_updating(start.unwrap_or(0) + stop.unwrap_or(0))?;
 
         let entry = yt_dlp
             .extract_info(&url, false, true)
@@ -160,7 +159,7 @@ fn take_vector<T>(vector: &[T], start: usize, stop: usize) -> Result<&[T]> {
 
 #[cfg(test)]
 mod test {
-    use crate::select::cmds::add::take_vector;
+    use super::take_vector;
 
     #[test]
     fn test_vector_take() {
diff --git a/crates/yt/src/select/cmds/mod.rs b/crates/yt/src/commands/select/implm/standalone/mod.rs
index 1713233..dd6de45 100644
--- a/crates/yt/src/select/cmds/mod.rs
+++ b/crates/yt/src/commands/select/implm/standalone/mod.rs
@@ -1,20 +1,9 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// 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::io::{Write, stderr};
 
 use crate::{
     ansi_escape_codes,
     app::App,
-    cli::{SelectCommand, SharedSelectionCommandArgs},
+    commands::select::{SelectCommand, SharedSelectionCommandArgs},
     storage::db::{
         insert::{Operations, video::Operation},
         video::{Priority, Video, VideoStatus},
@@ -23,7 +12,7 @@ use crate::{
 
 use anyhow::{Context, Result, bail};
 
-pub(crate) mod add;
+pub(super) mod add;
 
 pub(crate) async fn handle_select_cmd(
     app: &App,
diff --git a/crates/yt/src/commands/select/mod.rs b/crates/yt/src/commands/select/mod.rs
new file mode 100644
index 0000000..1b06206
--- /dev/null
+++ b/crates/yt/src/commands/select/mod.rs
@@ -0,0 +1,219 @@
+use std::{
+    fmt::{self, Display, Formatter},
+    str::FromStr,
+};
+
+use chrono::NaiveDate;
+use clap::{Args, Subcommand, ValueEnum};
+use url::Url;
+
+use crate::{select::duration::MaybeDuration, storage::db::extractor_hash::LazyExtractorHash};
+
+mod implm;
+
+#[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(crate) enum SelectCommand {
+    /// Open a `git rebase` like file to select the videos to watch (the default)
+    File {
+        /// Include done (watched, dropped) videos
+        #[arg(long, short)]
+        done: bool,
+
+        /// Use the last selection file (useful if you've spend time on it and want to get it again)
+        #[arg(long, short, conflicts_with = "done")]
+        use_last_selection: bool,
+    },
+
+    /// Generate a directory, where each file contains only one subscription.
+    Split {
+        /// Include done (watched, dropped) videos
+        #[arg(long, short)]
+        done: bool,
+
+        /// Which key to use for sorting.
+        #[arg(default_value_t)]
+        sort_key: SelectSplitSortKey,
+
+        /// Which mode to use for sorting.
+        #[arg(default_value_t)]
+        sort_mode: SelectSplitSortMode,
+    },
+
+    /// Add a video to the database
+    ///
+    /// This optionally supports to add a playlist.
+    /// When a playlist is added, the `start` and `stop` arguments can be used to select which
+    /// playlist entries to include.
+    #[command(visible_alias = "a")]
+    Add {
+        urls: Vec<Url>,
+
+        /// Start adding playlist entries at this playlist index (zero based and inclusive)
+        #[arg(short = 's', long)]
+        start: Option<usize>,
+
+        /// Stop adding playlist entries at this playlist index (zero based and inclusive)
+        #[arg(short = 'e', long)]
+        stop: Option<usize>,
+    },
+
+    /// Mark the video given by the hash to be watched
+    #[command(visible_alias = "w")]
+    Watch {
+        #[command(flatten)]
+        shared: SharedSelectionCommandArgs,
+    },
+
+    /// Mark the video given by the hash to be dropped
+    #[command(visible_alias = "d")]
+    Drop {
+        #[command(flatten)]
+        shared: SharedSelectionCommandArgs,
+    },
+
+    /// Mark the video given by the hash as already watched
+    #[command(visible_alias = "wd")]
+    Watched {
+        #[command(flatten)]
+        shared: SharedSelectionCommandArgs,
+    },
+
+    /// Open the video URL in Firefox's `timesinks.youtube` profile
+    #[command(visible_alias = "u")]
+    Url {
+        #[command(flatten)]
+        shared: SharedSelectionCommandArgs,
+    },
+
+    /// Reset the videos status to 'Pick'
+    #[command(visible_alias = "p")]
+    Pick {
+        #[command(flatten)]
+        shared: SharedSelectionCommandArgs,
+    },
+}
+impl Default for SelectCommand {
+    fn default() -> Self {
+        Self::File {
+            done: false,
+            use_last_selection: false,
+        }
+    }
+}
+
+#[derive(Clone, Debug, Args)]
+#[command(infer_subcommands = true)]
+/// Mark the video given by the hash to be watched
+pub(crate) struct SharedSelectionCommandArgs {
+    /// The ordering priority (higher means more at the top)
+    #[arg(short, long)]
+    pub(crate) priority: Option<i64>,
+
+    /// The subtitles to download (e.g. 'en,de,sv')
+    #[arg(short = 'l', long)]
+    pub(crate) subtitle_langs: Option<String>,
+
+    /// The speed to set mpv to
+    #[arg(short = 's', long)]
+    pub(crate) playback_speed: Option<f64>,
+
+    /// The short extractor hash
+    pub(crate) hash: LazyExtractorHash,
+
+    pub(crate) title: Option<String>,
+
+    pub(crate) date: Option<OptionalNaiveDate>,
+
+    pub(crate) publisher: Option<OptionalPublisher>,
+
+    pub(crate) duration: Option<MaybeDuration>,
+
+    pub(crate) url: Option<Url>,
+}
+
+impl SelectCommand {
+    pub(crate) fn into_shared(self) -> Option<SharedSelectionCommandArgs> {
+        match self {
+            SelectCommand::File { .. }
+            | SelectCommand::Split { .. }
+            | SelectCommand::Add { .. } => None,
+            SelectCommand::Watch { shared }
+            | SelectCommand::Drop { shared }
+            | SelectCommand::Watched { shared }
+            | SelectCommand::Url { shared }
+            | SelectCommand::Pick { shared } => Some(shared),
+        }
+    }
+}
+
+#[derive(Clone, Debug, Copy)]
+pub(crate) struct OptionalNaiveDate {
+    pub(crate) date: Option<NaiveDate>,
+}
+impl FromStr for OptionalNaiveDate {
+    type Err = anyhow::Error;
+    fn from_str(v: &str) -> Result<Self, Self::Err> {
+        if v == "[No release date]" {
+            Ok(Self { date: None })
+        } else {
+            Ok(Self {
+                date: Some(NaiveDate::from_str(v)?),
+            })
+        }
+    }
+}
+#[derive(Clone, Debug)]
+pub(crate) struct OptionalPublisher {
+    pub(crate) publisher: Option<String>,
+}
+impl FromStr for OptionalPublisher {
+    type Err = anyhow::Error;
+    fn from_str(v: &str) -> Result<Self, Self::Err> {
+        if v == "[No author]" {
+            Ok(Self { publisher: None })
+        } else {
+            Ok(Self {
+                publisher: Some(v.to_owned()),
+            })
+        }
+    }
+}
+
+#[derive(Default, ValueEnum, Clone, Copy, Debug)]
+pub(crate) enum SelectSplitSortKey {
+    /// Sort by the name of the publisher.
+    #[default]
+    Publisher,
+
+    /// Sort by the number of unselected videos per publisher.
+    Videos,
+}
+impl Display for SelectSplitSortKey {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        match self {
+            SelectSplitSortKey::Publisher => f.write_str("publisher"),
+            SelectSplitSortKey::Videos => f.write_str("videos"),
+        }
+    }
+}
+
+#[derive(Default, ValueEnum, Clone, Copy, Debug)]
+pub(crate) enum SelectSplitSortMode {
+    /// Sort in ascending order (small -> big)
+    #[default]
+    Asc,
+
+    /// Sort in descending order (big -> small)
+    Desc,
+}
+
+impl Display for SelectSplitSortMode {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        match self {
+            SelectSplitSortMode::Asc => f.write_str("asc"),
+            SelectSplitSortMode::Desc => f.write_str("desc"),
+        }
+    }
+}
diff --git a/crates/yt/src/commands/status/implm.rs b/crates/yt/src/commands/status/implm.rs
new file mode 100644
index 0000000..fc046c9
--- /dev/null
+++ b/crates/yt/src/commands/status/implm.rs
@@ -0,0 +1,147 @@
+use std::time::Duration;
+
+use crate::{
+    app::App,
+    commands::status::StatusCommand,
+    select::duration::MaybeDuration,
+    shared::bytes::Bytes,
+    storage::db::{
+        subscription::Subscriptions,
+        video::{Video, VideoStatusMarker},
+    },
+    yt_dlp::get_current_cache_allocation,
+};
+
+use anyhow::{Context, Result};
+
+macro_rules! get {
+    ($videos:expr, $status:ident) => {
+        $videos
+            .iter()
+            .filter(|vid| vid.status.as_marker() == VideoStatusMarker::$status)
+            .count()
+    };
+
+    (@collect $videos:expr, $status:ident) => {
+        $videos
+            .iter()
+            .filter(|vid| vid.status.as_marker() == VideoStatusMarker::$status)
+            .collect()
+    };
+}
+
+impl StatusCommand {
+    pub(crate) async fn implm(self, app: &App) -> Result<()> {
+        let StatusCommand { format } = self;
+
+        let all_videos = Video::in_states(app, VideoStatusMarker::ALL).await?;
+
+        // lengths
+        let picked_videos_len = get!(all_videos, Pick);
+
+        let watch_videos_len = get!(all_videos, Watch);
+        let cached_videos_len = get!(all_videos, Cached);
+        let watched_videos_len = get!(all_videos, Watched);
+        let watched_videos: Vec<_> = get!(@collect all_videos, Watched);
+
+        let drop_videos_len = get!(all_videos, Drop);
+        let dropped_videos_len = get!(all_videos, Dropped);
+
+        let subscriptions = Subscriptions::get(app).await?;
+        let subscriptions_len = subscriptions.0.len();
+
+        let watchtime_status = {
+            let total_watch_time_raw = watched_videos
+                .iter()
+                .fold(Duration::default(), |acc, vid| acc + vid.watch_progress);
+
+            // Most things are watched at a speed of s (which is defined in the config file).
+            // Thus
+            //      y = x * s -> y / s = x
+            let total_watch_time = Duration::from_secs_f64(
+                (total_watch_time_raw.as_secs_f64()) / app.config.select.playback_speed,
+            );
+
+            let speed = app.config.select.playback_speed;
+
+            // Do not print the adjusted time, if the user has keep the speed level at 1.
+            #[allow(clippy::float_cmp)]
+            if speed == 1.0 {
+                format!(
+                    "Total Watchtime: {}\n",
+                    MaybeDuration::from_std(total_watch_time_raw)
+                )
+            } else {
+                format!(
+                    "Total Watchtime: {} (at {speed} speed: {})\n",
+                    MaybeDuration::from_std(total_watch_time_raw),
+                    MaybeDuration::from_std(total_watch_time),
+                )
+            }
+        };
+
+        let watch_rate: f64 = {
+            fn to_f64(input: usize) -> f64 {
+                f64::from(u32::try_from(input).expect("This should never exceed u32::MAX"))
+            }
+
+            let count =
+                to_f64(watched_videos_len) / (to_f64(drop_videos_len) + to_f64(dropped_videos_len));
+            count * 100.0
+        };
+
+        let cache_usage: Bytes = get_current_cache_allocation(app)
+            .await
+            .context("Failed to get current cache allocation")?;
+
+        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}
+Cached   Videos: {cached_videos_len}
+Watched  Videos: {watched_videos_len} (watch rate: {watch_rate:.2} %)
+
+Drop     Videos: {drop_videos_len}
+Dropped  Videos: {dropped_videos_len}
+
+{watchtime_status}
+
+  Subscriptions: {subscriptions_len}
+    Cache usage: {cache_usage}"
+            );
+        }
+
+        Ok(())
+    }
+}
diff --git a/crates/yt/src/commands/status/mod.rs b/crates/yt/src/commands/status/mod.rs
new file mode 100644
index 0000000..dc6e865
--- /dev/null
+++ b/crates/yt/src/commands/status/mod.rs
@@ -0,0 +1,10 @@
+use clap::Parser;
+
+mod implm;
+
+#[derive(Parser, Debug)]
+pub(crate) struct StatusCommand {
+    /// Which format to use
+    #[arg(short, long)]
+    format: Option<String>,
+}
diff --git a/crates/yt/src/subscribe/mod.rs b/crates/yt/src/commands/subscriptions/implm.rs
index bcf778b..3051522 100644
--- a/crates/yt/src/subscribe/mod.rs
+++ b/crates/yt/src/commands/subscriptions/implm.rs
@@ -1,46 +1,83 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// 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::str::FromStr;
 
-use anyhow::{Context, Result, bail};
-use log::{error, warn};
-use tokio::io::{AsyncBufRead, AsyncBufReadExt};
-use url::Url;
-use yt_dlp::{json_cast, json_get, options::YoutubeDLOptions};
-
 use crate::{
     app::App,
+    commands::subscriptions::SubscriptionCommand,
     storage::db::{
         insert::{Operations, subscription::Operation},
         subscription::{Subscription, Subscriptions, check_url},
     },
 };
 
-pub(crate) async fn unsubscribe(app: &App, name: String) -> Result<()> {
-    let mut present_subscriptions = Subscriptions::get(app).await?;
-
-    let mut ops = Operations::new("Subscribe: unsubscribe");
-    if let Some(subscription) = present_subscriptions.0.remove(&name) {
-        subscription.remove(&mut ops);
-    } else {
-        bail!("Couldn't find subscription: '{}'", &name);
-    }
+use anyhow::{Context, Result, bail};
+use log::{error, warn};
+use tokio::{
+    fs::File,
+    io::{AsyncBufRead, AsyncBufReadExt, BufReader, stdin},
+};
+use url::Url;
+use yt_dlp::{json_cast, json_get, options::YoutubeDLOptions};
 
-    ops.commit(app).await?;
+impl SubscriptionCommand {
+    pub(crate) async fn implm(self, app: &App) -> Result<()> {
+        match self {
+            SubscriptionCommand::Add {
+                name,
+                url,
+                no_check,
+            } => {
+                let mut ops = Operations::new("main: subscribe");
+                subscribe(&app, name, url, no_check, &mut ops)
+                    .await
+                    .context("Failed to add a subscription")?;
+                ops.commit(&app).await?;
+            }
+            SubscriptionCommand::Remove { name } => {
+                let mut present_subscriptions = Subscriptions::get(app).await?;
+
+                let mut ops = Operations::new("Subscribe: unsubscribe");
+                if let Some(subscription) = present_subscriptions.0.remove(&name) {
+                    subscription.remove(&mut ops);
+                } else {
+                    bail!("Couldn't find subscription: '{}'", &name);
+                }
+                ops.commit(app)
+                    .await
+                    .with_context(|| format!("Failed to unsubscribe from {name:?}"))?;
+            }
+            SubscriptionCommand::List {} => {
+                let all_subs = Subscriptions::get(&app).await?;
+
+                for (key, val) in all_subs.0 {
+                    println!("{}: '{}'", key, val.url);
+                }
+            }
+            SubscriptionCommand::Export {} => {
+                let all_subs = Subscriptions::get(&app).await?;
+                for val in all_subs.0.values() {
+                    println!("{}", val.url);
+                }
+            }
+            SubscriptionCommand::Import {
+                file,
+                force,
+                no_check,
+            } => {
+                if let Some(file) = file {
+                    let f = File::open(file).await?;
+
+                    import(&app, BufReader::new(f), force, no_check).await?;
+                } else {
+                    import(&app, BufReader::new(stdin()), force, no_check).await?;
+                }
+            }
+        }
 
-    Ok(())
+        Ok(())
+    }
 }
 
-pub(crate) async fn import<W: AsyncBufRead + AsyncBufReadExt + Unpin>(
+async fn import<W: AsyncBufRead + AsyncBufReadExt + Unpin>(
     app: &App,
     reader: W,
     force: bool,
@@ -77,7 +114,7 @@ pub(crate) async fn import<W: AsyncBufRead + AsyncBufReadExt + Unpin>(
     Ok(())
 }
 
-pub(crate) async fn subscribe(
+async fn subscribe(
     app: &App,
     name: Option<String>,
     url: Url,
@@ -190,7 +227,8 @@ async fn actual_subscribe(
     if let Some(subs) = present_subscriptions.0.get(&name) {
         bail!(
             "The subscription '{}' could not be added, \
-                as another one with the same name ('{}') already exists. It links to the Url: '{}'",
+                as another one with the same name ('{}') already exists. \
+                It points to the Url: '{}'",
             name,
             name,
             subs.url
diff --git a/crates/yt/src/commands/subscriptions/mod.rs b/crates/yt/src/commands/subscriptions/mod.rs
new file mode 100644
index 0000000..530f5f5
--- /dev/null
+++ b/crates/yt/src/commands/subscriptions/mod.rs
@@ -0,0 +1,52 @@
+use std::path::PathBuf;
+
+use clap::Subcommand;
+use clap_complete::ArgValueCompleter;
+use url::Url;
+
+use crate::commands::complete_subscription;
+
+mod implm;
+
+#[derive(Subcommand, Clone, Debug)]
+pub(crate) enum SubscriptionCommand {
+    /// Subscribe to an URL
+    Add {
+        #[arg(short, long)]
+        /// The human readable name of the subscription
+        name: Option<String>,
+
+        /// The URL to listen to
+        url: Url,
+
+        /// Don't check, whether the URL actually points to something yt understands.
+        #[arg(long, default_value_t = false)]
+        no_check: bool,
+    },
+
+    /// Unsubscribe from an URL
+    Remove {
+        /// The human readable name of the subscription
+        #[arg(add = ArgValueCompleter::new(complete_subscription))]
+        name: String,
+    },
+
+    /// Import a bunch of URLs as subscriptions.
+    Import {
+        /// The file containing the URLs. Will use Stdin otherwise.
+        file: Option<PathBuf>,
+
+        /// Remove any previous subscriptions
+        #[arg(short, long)]
+        force: bool,
+
+        /// Don't check, whether the URLs actually point to something yt understands.
+        #[arg(long, default_value_t = false)]
+        no_check: bool,
+    },
+    /// Write all subscriptions in an format understood by `import`
+    Export {},
+
+    /// List all subscriptions
+    List {},
+}
diff --git a/crates/yt/src/commands/update/implm/mod.rs b/crates/yt/src/commands/update/implm/mod.rs
new file mode 100644
index 0000000..bb9323e
--- /dev/null
+++ b/crates/yt/src/commands/update/implm/mod.rs
@@ -0,0 +1,52 @@
+use crate::{
+    app::App,
+    commands::update::{UpdateCommand, implm::updater::Updater},
+    storage::db::{
+        extractor_hash::ExtractorHash,
+        subscription::{Subscription, Subscriptions},
+    },
+};
+
+use anyhow::{Result, bail};
+
+mod updater;
+
+impl UpdateCommand {
+    pub(crate) async fn implm(self, app: &App) -> Result<()> {
+        let UpdateCommand {
+            max_backlog,
+            subscriptions: subscription_names_to_update,
+        } = self;
+
+        let mut all_subs = Subscriptions::get(&app).await?;
+
+        let max_backlog = max_backlog.unwrap_or(app.config.update.max_backlog);
+
+        let subs: Vec<Subscription> = if subscription_names_to_update.is_empty() {
+            all_subs.0.into_values().collect()
+        } else {
+            subscription_names_to_update
+                .into_iter()
+                .map(|sub| {
+                    if let Some(val) = all_subs.0.remove(&sub) {
+                        Ok(val)
+                    } else {
+                        bail!(
+                            "Your specified subscription to update '{}' is not a subscription!",
+                            sub
+                        )
+                    }
+                })
+                .collect::<Result<_>>()?
+        };
+
+        // We can get away with not having to re-fetch the hashes every time, as the returned video
+        // should not contain duplicates.
+        let hashes = ExtractorHash::get_all(app).await?;
+
+        let updater = Updater::new(max_backlog, app.config.update.pool_size, hashes);
+        updater.update(app, subs).await?;
+
+        Ok(())
+    }
+}
diff --git a/crates/yt/src/update/updater.rs b/crates/yt/src/commands/update/implm/updater.rs
index ae9acb1..5969d54 100644
--- a/crates/yt/src/update/updater.rs
+++ b/crates/yt/src/commands/update/implm/updater.rs
@@ -1,22 +1,9 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// 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::{
-    io::{Write, stderr},
-    sync::atomic::{AtomicUsize, Ordering},
-};
+use std::sync::atomic::{AtomicUsize, Ordering};
 
 use anyhow::{Context, Result};
 use futures::{StreamExt, future::join_all, stream};
 use log::{Level, debug, error, log_enabled};
-use serde_json::json;
+use tokio::io::{AsyncWriteExt, stderr};
 use tokio_util::task::LocalPoolHandle;
 use yt_dlp::{
     info_json::InfoJson, json_cast, options::YoutubeDLOptions, process_ie_result,
@@ -24,13 +11,14 @@ use yt_dlp::{
 };
 
 use crate::{
-    ansi_escape_codes::{clear_whole_line, move_to_col},
+    ansi_escape_codes,
     app::App,
-    storage::db::{extractor_hash::ExtractorHash, subscription::Subscription},
+    storage::db::{
+        extractor_hash::ExtractorHash, insert::Operations, subscription::Subscription, video::Video,
+    },
+    yt_dlp::yt_dlp_opts_updating,
 };
 
-use super::process_subscription;
-
 pub(super) struct Updater {
     max_backlog: usize,
     hashes: Vec<ExtractorHash>,
@@ -83,32 +71,21 @@ impl Updater {
         let max_backlog = self.max_backlog;
         let hashes = self.hashes.clone();
 
-        let yt_dlp = YoutubeDLOptions::new()
-            .set("playliststart", 1)
-            .set("playlistend", max_backlog)
-            .set("noplaylist", false)
-            .set(
-                "extractor_args",
-                json! {{"youtubetab": {"approximate_date": [""]}}},
-            )
-            // // TODO: This also removes unlisted and other stuff. Find a good way to remove the
-            // // members-only videos from the feed. <2025-04-17>
-            // .set("match-filter", "availability=public")
-            .build()?;
+        let yt_dlp = yt_dlp_opts_updating(max_backlog)?;
 
         self.pool
             .spawn_pinned(move || {
                 async move {
                     if !log_enabled!(Level::Debug) {
-                        clear_whole_line();
-                        move_to_col(1);
+                        ansi_escape_codes::clear_whole_line();
+                        ansi_escape_codes::move_to_col(1);
                         eprint!(
                             "({}/{total_number}) Checking playlist {}...",
                             REACHED_NUMBER.fetch_add(1, Ordering::Relaxed),
                             sub.name
                         );
-                        move_to_col(1);
-                        stderr().flush()?;
+                        ansi_escape_codes::move_to_col(1);
+                        stderr().flush().await?;
                     }
 
                     let info = yt_dlp
@@ -191,3 +168,30 @@ impl Updater {
             .await?
     }
 }
+
+async fn process_subscription(app: &App, sub: Subscription, entry: InfoJson) -> Result<()> {
+    let mut ops = Operations::new("Update: process subscription");
+    let video = Video::from_info_json(&entry, Some(&sub))
+        .context("Failed to parse search entry as Video")?;
+
+    let title = video.title.clone();
+    let url = video.url.clone();
+    let video = video.add(&mut ops).with_context(|| {
+        format!("Failed to add video to database: '{title}' (with url: '{url}')")
+    })?;
+
+    ops.commit(app).await.with_context(|| {
+        format!(
+            "Failed to add video to database: '{}' (with url: '{}')",
+            video.title, video.url
+        )
+    })?;
+    println!(
+        "{}",
+        &video
+            .to_line_display(app, None)
+            .await
+            .with_context(|| format!("Failed to format video: '{}'", video.title))?
+    );
+    Ok(())
+}
diff --git a/crates/yt/src/commands/update/mod.rs b/crates/yt/src/commands/update/mod.rs
new file mode 100644
index 0000000..6f1c865
--- /dev/null
+++ b/crates/yt/src/commands/update/mod.rs
@@ -0,0 +1,17 @@
+use clap::Parser;
+use clap_complete::ArgValueCompleter;
+
+use crate::commands::complete_subscription;
+
+mod implm;
+
+#[derive(Parser, Debug)]
+pub(crate) struct UpdateCommand {
+    /// The maximal number of videos to fetch for each subscription.
+    #[arg(short, long)]
+    max_backlog: Option<usize>,
+
+    /// The subscriptions to update
+    #[arg(add = ArgValueCompleter::new(complete_subscription))]
+    subscriptions: Vec<String>,
+}
diff --git a/crates/yt/src/commands/videos/implm.rs b/crates/yt/src/commands/videos/implm.rs
new file mode 100644
index 0000000..7d13ceb
--- /dev/null
+++ b/crates/yt/src/commands/videos/implm.rs
@@ -0,0 +1,63 @@
+use crate::{
+    app::App,
+    commands::videos::VideosCommand,
+    storage::db::video::{Video, VideoStatusMarker},
+};
+
+use anyhow::{Context, Result};
+use futures::{TryStreamExt, stream::FuturesUnordered};
+
+impl VideosCommand {
+    pub(crate) async fn implm(self, app: &App) -> Result<()> {
+        match self {
+            VideosCommand::List {
+                search_query,
+                limit,
+                format,
+            } => {
+                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, format.clone()).await?;
+                }
+
+                let limit = limit.unwrap_or(all_videos.len());
+
+                let all_video_strings: Vec<String> = all_videos
+                    .into_iter()
+                    .take(limit)
+                    .map(|vid| to_line_display_owned(vid, app, format.clone()))
+                    .collect::<FuturesUnordered<_>>()
+                    .try_collect::<Vec<String>>()
+                    .await?;
+
+                if let Some(query) = search_query {
+                    all_video_strings
+                        .into_iter()
+                        .filter(|video| video.to_lowercase().contains(&query.to_lowercase()))
+                        .for_each(|video| println!("{video}"));
+                } else {
+                    println!("{}", all_video_strings.join("\n"));
+                }
+            }
+            VideosCommand::Info { hash, format } => {
+                let video = hash.realize(&app, None).await?.get_with_app(&app).await?;
+
+                print!(
+                    "{}",
+                    &video
+                        .to_info_display(&app, format)
+                        .await
+                        .context("Failed to format video")?
+                );
+            }
+        }
+
+        Ok(())
+    }
+}
+
+async fn to_line_display_owned(video: Video, app: &App, format: Option<String>) -> Result<String> {
+    video.to_line_display(app, format).await
+}
diff --git a/crates/yt/src/commands/videos/mod.rs b/crates/yt/src/commands/videos/mod.rs
new file mode 100644
index 0000000..93a11a1
--- /dev/null
+++ b/crates/yt/src/commands/videos/mod.rs
@@ -0,0 +1,36 @@
+use clap::{ArgAction, Subcommand};
+
+use crate::storage::db::extractor_hash::LazyExtractorHash;
+
+mod implm;
+
+#[derive(Subcommand, Clone, Debug)]
+pub(crate) enum VideosCommand {
+    /// List the videos in the database
+    #[command(visible_alias = "ls")]
+    List {
+        /// An optional search query to limit the results
+        #[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>,
+    },
+
+    /// Get detailed information about a video
+    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>,
+    },
+}
diff --git a/crates/yt/src/commands/watch/implm/mod.rs b/crates/yt/src/commands/watch/implm/mod.rs
new file mode 100644
index 0000000..338f80a
--- /dev/null
+++ b/crates/yt/src/commands/watch/implm/mod.rs
@@ -0,0 +1,20 @@
+use std::sync::Arc;
+
+use crate::{app::App, commands::watch::WatchCommand};
+
+use anyhow::Result;
+
+mod watch;
+
+impl WatchCommand {
+    pub(crate) async fn implm(self, app: Arc<App>) -> Result<()> {
+        let WatchCommand {
+            provide_ipc_socket,
+            headless,
+        } = self;
+
+        watch::watch(app, provide_ipc_socket, headless).await?;
+
+        Ok(())
+    }
+}
diff --git a/crates/yt/src/watch/mod.rs b/crates/yt/src/commands/watch/implm/watch/mod.rs
index 2fe34b2..1436d8d 100644
--- a/crates/yt/src/watch/mod.rs
+++ b/crates/yt/src/commands/watch/implm/watch/mod.rs
@@ -26,14 +26,12 @@ use tokio::{task, time};
 use self::playlist_handler::Status;
 use crate::{
     app::App,
-    cache::maintain,
     storage::{
-        db::{insert::Operations, playlist::Playlist},
+        db::{insert::{maintenance::clear_stale_downloaded_paths, Operations}, playlist::Playlist},
         notify::wait_for_db_write,
     },
 };
 
-pub(crate) mod playlist;
 pub(crate) mod playlist_handler;
 
 fn init_mpv(app: &App, ipc_socket: Option<PathBuf>, headless: bool) -> Result<(Mpv, EventContext)> {
@@ -112,7 +110,7 @@ fn init_mpv(app: &App, ipc_socket: Option<PathBuf>, headless: bool) -> Result<(M
 }
 
 pub(crate) async fn watch(app: Arc<App>, provide_ipc_socket: bool, headless: bool) -> Result<()> {
-    maintain(&app).await?;
+    clear_stale_downloaded_paths(&app).await?;
 
     let ipc_socket = if provide_ipc_socket {
         Some(app.config.paths.mpv_ipc_socket_path.clone())
diff --git a/crates/yt/src/watch/playlist_handler/client_messages/mod.rs b/crates/yt/src/commands/watch/implm/watch/playlist_handler/client_messages.rs
index c05ca87..6c8ebbe 100644
--- a/crates/yt/src/watch/playlist_handler/client_messages/mod.rs
+++ b/crates/yt/src/commands/watch/implm/watch/playlist_handler/client_messages.rs
@@ -10,7 +10,7 @@
 
 use std::{env, time::Duration};
 
-use crate::{app::App, comments};
+use crate::{app::App, storage::db::video::Video};
 
 use anyhow::{Context, Result, bail};
 use libmpv2::Mpv;
@@ -72,7 +72,7 @@ pub(super) async fn handle_yt_description_external(app: &App) -> Result<()> {
     Ok(())
 }
 pub(super) async fn handle_yt_description_local(app: &App, mpv: &Mpv) -> Result<()> {
-    let description: String = comments::description::get(app)
+    let description: String = Video::get_current_description(app)
         .await?
         .chars()
         .take(app.config.watch.local_displays_length)
@@ -87,7 +87,7 @@ pub(super) async fn handle_yt_comments_external(app: &App) -> Result<()> {
     Ok(())
 }
 pub(super) async fn handle_yt_comments_local(app: &App, mpv: &Mpv) -> Result<()> {
-    let comments: String = comments::get(app)
+    let comments: String = Video::get_current_comments(app)
         .await?
         .render(false)
         .chars()
diff --git a/crates/yt/src/watch/playlist_handler/mod.rs b/crates/yt/src/commands/watch/implm/watch/playlist_handler/mod.rs
index 443fd26..443fd26 100644
--- a/crates/yt/src/watch/playlist_handler/mod.rs
+++ b/crates/yt/src/commands/watch/implm/watch/playlist_handler/mod.rs
diff --git a/crates/yt/src/commands/watch/mod.rs b/crates/yt/src/commands/watch/mod.rs
new file mode 100644
index 0000000..8bae5c9
--- /dev/null
+++ b/crates/yt/src/commands/watch/mod.rs
@@ -0,0 +1,14 @@
+use clap::Parser;
+
+mod implm;
+
+#[derive(Parser, Debug)]
+pub(crate) struct WatchCommand {
+    /// Print the path to an ipc socket for mpv control to stdout at startup.
+    #[arg(long)]
+    provide_ipc_socket: bool,
+
+    /// Don't start an mpv window at all.
+    #[arg(long)]
+    headless: bool,
+}
diff --git a/crates/yt/src/comments/description.rs b/crates/yt/src/comments/description.rs
deleted file mode 100644
index 2065970..0000000
--- a/crates/yt/src/comments/description.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// 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 crate::{App, comments::output::display_fmt_and_less, storage::db::video::Video};
-
-use anyhow::{Result, bail};
-use yt_dlp::json_cast;
-
-pub(crate) async fn description(app: &App) -> Result<()> {
-    let description = get(app).await?;
-    display_fmt_and_less(description).await?;
-
-    Ok(())
-}
-
-pub(crate) async fn get(app: &App) -> Result<String> {
-    let currently_playing_video: Video = if let Some(video) = Video::currently_focused(app).await? {
-        video
-    } else {
-        bail!("Could not find a currently playing video!");
-    };
-
-    let info_json = &currently_playing_video
-        .get_info_json()?
-        .expect("A currently *playing* must be cached. And thus the info.json should be available");
-
-    Ok(info_json
-        .get("description")
-        .map_or("<No description>", |val| json_cast!(val, as_str))
-        .to_owned())
-}
diff --git a/crates/yt/src/comments/mod.rs b/crates/yt/src/comments/mod.rs
deleted file mode 100644
index e667bd9..0000000
--- a/crates/yt/src/comments/mod.rs
+++ /dev/null
@@ -1,162 +0,0 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// 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::mem;
-
-use anyhow::{Result, bail};
-use comment::{Comment, CommentExt, Comments, Parent};
-use output::display_fmt_and_less;
-use regex::Regex;
-use yt_dlp::json_cast;
-
-use crate::{app::App, storage::db::video::Video};
-
-mod comment;
-mod display;
-pub(crate) mod output;
-
-pub(crate) mod description;
-pub(crate) use description::*;
-
-#[allow(clippy::too_many_lines)]
-pub(crate) async fn get(app: &App) -> Result<Comments> {
-    let currently_playing_video: Video = if let Some(video) = Video::currently_focused(app).await? {
-        video
-    } else {
-        bail!("Could not find a currently playing video!");
-    };
-
-    let info_json = &currently_playing_video.get_info_json()?.expect(
-        "A currently *playing* video must be cached. And thus the info.json should be available",
-    );
-
-    let base_comments = if let Some(comments) = info_json.get("comments") {
-        json_cast!(comments, as_array)
-    } else {
-        bail!(
-            "The video ('{}') does not have comments!",
-            info_json
-                .get("title")
-                .map(|val| json_cast!(val, as_str))
-                .unwrap_or("<No Title>")
-        )
-    };
-
-    let mut comments = Comments::new();
-    for c in base_comments {
-        let c: Comment = serde_json::from_value(c.to_owned())?;
-        if let Parent::Id(id) = &c.parent {
-            comments.insert(&(id.clone()), CommentExt::from(c));
-        } else {
-            comments.push(CommentExt::from(c));
-        }
-    }
-
-    comments.vec.iter_mut().for_each(|comment| {
-       let replies = mem::take(&mut comment.replies);
-       let mut output_replies: Vec<CommentExt>  = vec![];
-
-       let re = Regex::new(r"\u{200b}?(@[^\t\s]+)\u{200b}?").expect("This is hardcoded");
-       for reply in replies {
-           if let Some(replyee_match) =  re.captures(&reply.value.text){
-               let full_match = replyee_match.get(0).expect("This will always exist");
-               let text = reply.
-                   value.
-                   text[0..full_match.start()]
-                   .to_owned()
-                   +
-                   &reply
-                   .value
-                   .text[full_match.end()..];
-               let text: &str = text.trim().trim_matches('\u{200b}');
-
-               let replyee = replyee_match.get(1).expect("This should also exist").as_str();
-
-
-               if let Some(parent) = output_replies
-                   .iter_mut()
-                   // .rev()
-                   .flat_map(|com| &mut com.replies)
-                   .flat_map(|com| &mut com.replies)
-                   .flat_map(|com| &mut com.replies)
-                   .filter(|com| com.value.author == replyee)
-                   .last()
-               {
-                   parent.replies.push(CommentExt::from(Comment {
-                       text: text.to_owned(),
-                       ..reply.value
-                   }));
-               } else if let Some(parent) = output_replies
-                   .iter_mut()
-                   // .rev()
-                   .flat_map(|com| &mut com.replies)
-                   .flat_map(|com| &mut com.replies)
-                   .filter(|com| com.value.author == replyee)
-                   .last()
-               {
-                   parent.replies.push(CommentExt::from(Comment {
-                       text: text.to_owned(),
-                       ..reply.value
-                   }));
-               } else if let Some(parent) = output_replies
-                   .iter_mut()
-                   // .rev()
-                   .flat_map(|com| &mut com.replies)
-                   .filter(|com| com.value.author == replyee)
-                   .last()
-               {
-                   parent.replies.push(CommentExt::from(Comment {
-                       text: text.to_owned(),
-                       ..reply.value
-                   }));
-               } else if let Some(parent) = output_replies.iter_mut()
-                   // .rev()
-                   .filter(|com| com.value.author == replyee)
-                   .last()
-               {
-                   parent.replies.push(CommentExt::from(Comment {
-                       text: text.to_owned(),
-                       ..reply.value
-                   }));
-               } else {
-                   eprintln!(
-                   "Failed to find a parent for ('{}') both directly and via replies! The reply text was:\n'{}'\n",
-                   replyee,
-                   reply.value.text
-               );
-                   output_replies.push(reply);
-               }
-           } else {
-               output_replies.push(reply);
-           }
-       }
-       comment.replies = output_replies;
-    });
-
-    Ok(comments)
-}
-
-pub(crate) async fn comments(app: &App) -> Result<()> {
-    let comments = get(app).await?;
-
-    display_fmt_and_less(comments.render(true)).await?;
-
-    Ok(())
-}
-
-#[cfg(test)]
-mod test {
-    #[test]
-    fn test_string_replacement() {
-        let s = "A \n\nB\n\nC".to_owned();
-        assert_eq!("A \n  \n  B\n  \n  C", s.replace('\n', "\n  "));
-    }
-}
diff --git a/crates/yt/src/constants.rs b/crates/yt/src/constants.rs
index 690e018..e69de29 100644
--- a/crates/yt/src/constants.rs
+++ b/crates/yt/src/constants.rs
@@ -1,12 +0,0 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// 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(crate) const HELP_STR: &str = include_str!("./select/selection_file/help.str");
diff --git a/crates/yt/src/main.rs b/crates/yt/src/main.rs
index 2331dd7..4abebce 100644
--- a/crates/yt/src/main.rs
+++ b/crates/yt/src/main.rs
@@ -13,50 +13,31 @@
 // to print it anyways.
 #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc)]
 
-use std::sync::Arc;
-
-use anyhow::{Context, Result, bail};
+use anyhow::{Context, Result};
 use app::App;
-use cache::{invalidate, maintain};
 use clap::{CommandFactory, Parser};
-use cli::{CacheCommand, SelectCommand, SubscriptionCommand, VideosCommand};
 use config::Config;
-use log::{error, info};
-use select::cmds::handle_select_cmd;
-use tokio::{
-    fs::File,
-    io::{BufReader, stdin},
-    task::JoinHandle,
-};
+use log::info;
+
+use crate::commands::Command;
 
-use crate::{
-    cli::Command,
-    shared::bytes::Bytes,
-    storage::db::{insert::Operations, subscription::Subscriptions},
-};
+pub(crate) mod output;
+pub(crate) mod yt_dlp;
 
 pub(crate) mod ansi_escape_codes;
 pub(crate) mod app;
 pub(crate) mod cli;
+pub(crate) mod commands;
 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.
-#[allow(clippy::too_many_lines)]
 async fn main() -> Result<()> {
     clap_complete::CompleteEnv::with_factory(cli::CliArgs::command).complete();
 
@@ -100,194 +81,10 @@ async fn main() -> Result<()> {
 
     let app = App::new(config, !args.no_migrate_db).await?;
 
-    match args.command.unwrap_or(Command::default()) {
-        Command::Download {
-            force,
-            max_cache_size,
-        } => {
-            let max_cache_size =
-                max_cache_size.unwrap_or(app.config.download.max_cache_size.as_u64());
-            info!("Max cache size: '{}'", Bytes::new(max_cache_size));
-
-            maintain(&app).await?;
-            if force {
-                invalidate(&app).await?;
-            }
-
-            download::Downloader::new()
-                .consume(Arc::new(app), max_cache_size)
-                .await?;
-        }
-        Command::Select { cmd } => {
-            let cmd = cmd.unwrap_or(SelectCommand::default());
-
-            match cmd {
-                SelectCommand::File {
-                    done,
-                    use_last_selection,
-                } => Box::pin(select::select_file(&app, done, use_last_selection)).await?,
-                SelectCommand::Split {
-                    done,
-                    sort_key,
-                    sort_mode,
-                } => Box::pin(select::select_split(&app, done, sort_key, sort_mode)).await?,
-                SelectCommand::Add { urls, start, stop } => {
-                    Box::pin(select::cmds::add::add(&app, urls, start, stop)).await?;
-                }
-                other => {
-                    let shared = other
-                        .clone()
-                        .into_shared()
-                        .expect("The ones without shared should have been filtered out.");
-                    let hash = shared.hash.realize(&app).await?;
-                    let mut video = hash
-                        .get_with_app(&app)
-                        .await
-                        .expect("The hash was already realized, it should therefore exist");
-
-                    let mut ops = Operations::new("Main: handle select cmd");
-                    handle_select_cmd(&app, other, &mut video, None, &mut ops).await?;
-                    ops.commit(&app).await?;
-                }
-            }
-        }
-        Command::Sedowa {} => {
-            Box::pin(select::select_file(&app, false, false)).await?;
-
-            let arc_app = Arc::new(app);
-            dowa(arc_app).await?;
-        }
-        Command::Dowa {} => {
-            let arc_app = Arc::new(app);
-            dowa(arc_app).await?;
-        }
-        Command::Videos { cmd } => match cmd {
-            VideosCommand::List {
-                search_query,
-                limit,
-                format,
-            } => {
-                videos::query(&app, limit, search_query, format)
-                    .await
-                    .context("Failed to query videos")?;
-            }
-            VideosCommand::Info { hash, format } => {
-                let video = hash.realize(&app).await?.get_with_app(&app).await?;
-
-                print!(
-                    "{}",
-                    &video
-                        .to_info_display(&app, format)
-                        .await
-                        .context("Failed to format video")?
-                );
-            }
-        },
-        Command::Update {
-            max_backlog,
-            subscriptions,
-        } => {
-            let all_subs = Subscriptions::get(&app).await?;
-
-            for sub in &subscriptions {
-                if !all_subs.0.contains_key(sub) {
-                    bail!(
-                        "Your specified subscription to update '{}' is not a subscription!",
-                        sub
-                    )
-                }
-            }
-
-            let max_backlog = max_backlog.unwrap_or(app.config.update.max_backlog);
-
-            update::update(&app, max_backlog, subscriptions).await?;
-        }
-        Command::Subscriptions { cmd } => match cmd {
-            SubscriptionCommand::Add {
-                name,
-                url,
-                no_check,
-            } => {
-                let mut ops = Operations::new("main: subscribe");
-                subscribe::subscribe(&app, name, url, no_check, &mut ops)
-                    .await
-                    .context("Failed to add a subscription")?;
-                ops.commit(&app).await?;
-            }
-            SubscriptionCommand::Remove { name } => {
-                subscribe::unsubscribe(&app, name)
-                    .await
-                    .context("Failed to remove a subscription")?;
-            }
-            SubscriptionCommand::List {} => {
-                let all_subs = Subscriptions::get(&app).await?;
-
-                for (key, val) in all_subs.0 {
-                    println!("{}: '{}'", key, val.url);
-                }
-            }
-            SubscriptionCommand::Export {} => {
-                let all_subs = Subscriptions::get(&app).await?;
-                for val in all_subs.0.values() {
-                    println!("{}", val.url);
-                }
-            }
-            SubscriptionCommand::Import {
-                file,
-                force,
-                no_check,
-            } => {
-                if let Some(file) = file {
-                    let f = File::open(file).await?;
-
-                    subscribe::import(&app, BufReader::new(f), force, no_check).await?;
-                } else {
-                    subscribe::import(&app, BufReader::new(stdin()), force, no_check).await?;
-                }
-            }
-        },
-
-        Command::Watch {
-            provide_ipc_socket,
-            headless,
-        } => watch::watch(Arc::new(app), provide_ipc_socket, headless).await?,
-        Command::Playlist { watch } => watch::playlist::playlist(&app, watch).await?,
-
-        Command::Status { format } => status::show(&app, format).await?,
-        Command::Config {} => status::config(&app)?,
-
-        Command::Database { command } => match command {
-            CacheCommand::Invalidate {} => invalidate(&app).await?,
-            CacheCommand::Maintain {} => maintain(&app).await?,
-        },
-
-        Command::Comments {} => {
-            comments::comments(&app).await?;
-        }
-        Command::Description {} => {
-            comments::description(&app).await?;
-        }
-    }
-
-    Ok(())
-}
-
-async fn dowa(arc_app: Arc<App>) -> Result<()> {
-    let max_cache_size = arc_app.config.download.max_cache_size;
-    info!("Max cache size: '{max_cache_size}'");
-
-    let arc_app_clone = Arc::clone(&arc_app);
-    let download: JoinHandle<()> = tokio::spawn(async move {
-        let result = download::Downloader::new()
-            .consume(arc_app_clone, max_cache_size.as_u64())
-            .await;
-
-        if let Err(err) = result {
-            error!("Error from downloader: {err:?}");
-        }
-    });
+    args.command
+        .unwrap_or(Command::default())
+        .implm(app)
+        .await?;
 
-    watch::watch(arc_app, false, false).await?;
-    download.await?;
     Ok(())
 }
diff --git a/crates/yt/src/comments/output.rs b/crates/yt/src/output/mod.rs
index 4a27f3b..2f74519 100644
--- a/crates/yt/src/comments/output.rs
+++ b/crates/yt/src/output/mod.rs
@@ -17,7 +17,7 @@ use std::{
 use anyhow::{Context, Result};
 use uu_fmt::{FmtOptions, process_text};
 
-pub(crate) async fn display_fmt_and_less(input: String) -> Result<()> {
+pub(crate) fn display_less(input: String) -> Result<()> {
     let mut less = Command::new("less")
         .args(["--raw-control-chars"])
         .stdin(Stdio::piped())
@@ -25,7 +25,6 @@ pub(crate) async fn display_fmt_and_less(input: String) -> Result<()> {
         .spawn()
         .context("Failed to run less")?;
 
-    let input = format_text(&input);
     let mut stdin = less.stdin.take().context("Failed to open stdin")?;
     std::thread::spawn(move || {
         stdin
@@ -38,11 +37,15 @@ pub(crate) async fn display_fmt_and_less(input: String) -> Result<()> {
     Ok(())
 }
 
+pub(crate) fn display_fmt_and_less(input: &str) -> Result<()> {
+    display_less(format_text(&input, None))
+}
+
 #[must_use]
-pub(crate) fn format_text(input: &str) -> String {
+pub(crate) fn format_text(input: &str, termsize: Option<u16>) -> String {
     let input = input.trim();
 
-    let width = termsize::get().map_or(90, |size| size.cols);
+    let width = termsize.unwrap_or_else(|| termsize::get().map_or(90, |size| size.cols));
     let fmt_opts = FmtOptions {
         uniform: true,
         split_only: true,
diff --git a/crates/yt/src/select/selection_file/duration.rs b/crates/yt/src/select/duration.rs
index e536f18..f1de2ea 100644
--- a/crates/yt/src/select/selection_file/duration.rs
+++ b/crates/yt/src/select/duration.rs
@@ -203,7 +203,7 @@ impl std::fmt::Display for MaybeDuration {
 mod test {
     use std::str::FromStr;
 
-    use crate::select::selection_file::duration::{DAY, HOUR, MINUTE};
+    use crate::select::duration::{DAY, HOUR, MINUTE};
 
     use super::MaybeDuration;
 
diff --git a/crates/yt/src/select/mod.rs b/crates/yt/src/select/mod.rs
index b39bea2..91940ce 100644
--- a/crates/yt/src/select/mod.rs
+++ b/crates/yt/src/select/mod.rs
@@ -9,322 +9,8 @@
 // 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::{
-    collections::HashMap,
-    env::{self},
-    fs::{self, File, OpenOptions},
-    io::{BufRead, BufReader, BufWriter, Read, Write},
-    iter,
-    os::fd::{AsFd, AsRawFd},
-    path::Path,
-    string::String,
-};
+pub(crate) mod duration;
 
-use crate::{
-    app::App,
-    cli::{CliArgs, SelectCommand, SelectSplitSortKey, SelectSplitSortMode},
-    constants::HELP_STR,
-    storage::db::{
-        insert::Operations,
-        video::{Video, VideoStatusMarker},
-    },
-};
-
-use anyhow::{Context, Result, bail};
-use clap::Parser;
-use cmds::handle_select_cmd;
-use futures::{TryStreamExt, stream::FuturesOrdered};
-use log::info;
-use selection_file::process_line;
-use tempfile::Builder;
-use tokio::process::Command;
-
-pub(crate) mod cmds;
-pub(crate) mod selection_file;
-
-pub(crate) async fn select_split(
-    app: &App,
-    done: bool,
-    sort_key: SelectSplitSortKey,
-    sort_mode: SelectSplitSortMode,
-) -> Result<()> {
-    let temp_dir = Builder::new()
-        .prefix("yt_video_select-")
-        .rand_bytes(6)
-        .tempdir()
-        .context("Failed to get tempdir")?;
-
-    let matching_videos = get_videos(app, done).await?;
-
-    let mut no_author = vec![];
-    let mut author_map = HashMap::new();
-    for video in matching_videos {
-        if let Some(sub) = &video.parent_subscription_name {
-            if author_map.contains_key(sub) {
-                let vec: &mut Vec<_> = author_map
-                    .get_mut(sub)
-                    .expect("This key is set, we checked in the if above");
-
-                vec.push(video);
-            } else {
-                author_map.insert(sub.to_owned(), vec![video]);
-            }
-        } else {
-            no_author.push(video);
-        }
-    }
-
-    let author_map = {
-        let mut temp_vec: Vec<_> = author_map.into_iter().collect();
-
-        match sort_key {
-            SelectSplitSortKey::Publisher => {
-                // PERFORMANCE: The clone here should not be neeed.  <2025-06-15>
-                temp_vec.sort_by_key(|(name, _): &(String, Vec<Video>)| name.to_owned());
-            }
-            SelectSplitSortKey::Videos => {
-                temp_vec.sort_by_key(|(_, videos): &(String, Vec<Video>)| videos.len());
-            }
-        }
-
-        match sort_mode {
-            SelectSplitSortMode::Asc => {
-                // Std's default mode is ascending.
-            }
-            SelectSplitSortMode::Desc => {
-                temp_vec.reverse();
-            }
-        }
-
-        temp_vec
-    };
-
-    for (index, (name, videos)) in author_map
-        .into_iter()
-        .chain(iter::once((
-            "<No parent subscription>".to_owned(),
-            no_author,
-        )))
-        .enumerate()
-    {
-        let mut file_path = temp_dir.path().join(format!("{index:02}_{name}"));
-        file_path.set_extension("yts");
-
-        let tmp_file = File::create(&file_path)
-            .with_context(|| format!("Falied to create file at: {}", file_path.display()))?;
-
-        write_videos_to_file(app, &tmp_file, &videos)
-            .await
-            .with_context(|| format!("Falied to populate file at: {}", file_path.display()))?;
-    }
-
-    open_editor_at(temp_dir.path()).await?;
-
-    let mut paths = vec![];
-    for maybe_entry in temp_dir
-        .path()
-        .read_dir()
-        .context("Failed to open temp dir for reading")?
-    {
-        let entry = maybe_entry.context("Failed to read entry in temp dir")?;
-
-        if !entry.file_type()?.is_file() {
-            bail!("Found non-file entry: {}", entry.path().display());
-        }
-
-        paths.push(entry.path());
-    }
-
-    paths.sort();
-
-    let mut persistent_file = OpenOptions::new()
-        .read(false)
-        .write(true)
-        .create(true)
-        .truncate(true)
-        .open(&app.config.paths.last_selection_path)
-        .context("Failed to open persistent selection file")?;
-
-    for path in paths {
-        let mut read_file = File::open(path)?;
-
-        let mut buffer = vec![];
-        read_file.read_to_end(&mut buffer)?;
-        persistent_file.write_all(&buffer)?;
-    }
-
-    persistent_file.flush()?;
-    let persistent_file = OpenOptions::new()
-        .read(true)
-        .open(format!(
-            "/proc/self/fd/{}",
-            persistent_file.as_fd().as_raw_fd()
-        ))
-        .context("Failed to re-open persistent file")?;
-
-    let processed = process_file(app, &persistent_file).await?;
-
-    info!("Processed {processed} records.");
-    temp_dir.close().context("Failed to close the temp dir")?;
-    Ok(())
-}
-
-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")
-        .rand_bytes(6)
-        .tempfile()
-        .context("Failed to get tempfile")?;
-
-    if use_last_selection {
-        fs::copy(&app.config.paths.last_selection_path, &temp_file)?;
-    } else {
-        let matching_videos = get_videos(app, done).await?;
-
-        write_videos_to_file(app, temp_file.as_file(), &matching_videos).await?;
-    }
-
-    open_editor_at(temp_file.path()).await?;
-
-    let read_file = OpenOptions::new().read(true).open(temp_file.path())?;
-    fs::copy(temp_file.path(), &app.config.paths.last_selection_path)
-        .context("Failed to persist selection file")?;
-
-    let processed = process_file(app, &read_file).await?;
-    info!("Processed {processed} records.");
-
-    Ok(())
-}
-
-async fn get_videos(app: &App, include_done: bool) -> Result<Vec<Video>> {
-    if include_done {
-        Video::in_states(app, VideoStatusMarker::ALL).await
-    } else {
-        Video::in_states(
-            app,
-            &[
-                VideoStatusMarker::Pick,
-                //
-                VideoStatusMarker::Watch,
-                VideoStatusMarker::Cached,
-            ],
-        )
-        .await
-    }
-}
-
-async fn write_videos_to_file(app: &App, file: &File, videos: &[Video]) -> Result<()> {
-    // 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, None).await?);
-    }
-
-    let mut edit_file = BufWriter::new(file);
-
-    videos
-        .iter()
-        .map(|vid| vid.to_select_file_display(app))
-        .collect::<FuturesOrdered<_>>()
-        .try_collect::<Vec<String>>()
-        .await?
-        .into_iter()
-        .try_for_each(|line| -> Result<()> {
-            edit_file
-                .write_all(line.as_bytes())
-                .context("Failed to write to `edit_file`")?;
-
-            Ok(())
-        })?;
-
-    edit_file.write_all(HELP_STR.as_bytes())?;
-    edit_file.flush().context("Failed to flush edit file")?;
-
-    Ok(())
-}
-
-async fn process_file(app: &App, file: &File) -> Result<i64> {
-    let mut line_number = 0;
-
-    let mut ops = Operations::new("Select: process file");
-
-    let reader = BufReader::new(file);
-    for line in reader.lines() {
-        let line = line.context("Failed to read a line")?;
-
-        if let Some(line) = process_line(&line)? {
-            line_number -= 1;
-
-            // debug!(
-            //     "Parsed command: `{}`",
-            //     line.iter()
-            //         .map(|val| format!("\"{}\"", val))
-            //         .collect::<Vec<String>>()
-            //         .join(" ")
-            // );
-
-            let arg_line = ["yt", "select"]
-                .into_iter()
-                .chain(line.iter().map(String::as_str));
-
-            let args = CliArgs::parse_from(arg_line);
-
-            let crate::cli::Command::Select { cmd } = args
-                .command
-                .expect("This will be some, as we constructed it above.")
-            else {
-                unreachable!("This is checked in the `filter_line` function")
-            };
-
-            match cmd.expect(
-                "This value should always be some \
-                    here, as it would otherwise thrown an error above.",
-            ) {
-                SelectCommand::File { .. } | SelectCommand::Split { .. } => {
-                    bail!("You cannot use `select file` or `select split` recursively.")
-                }
-                SelectCommand::Add { urls, start, stop } => {
-                    Box::pin(cmds::add::add(app, urls, start, stop)).await?;
-                }
-                other => {
-                    let shared = other
-                        .clone()
-                        .into_shared()
-                        .expect("The ones without shared should have been filtered out.");
-
-                    let hash = shared.hash.realize(app).await?;
-                    let mut video = hash
-                        .get_with_app(app)
-                        .await
-                        .expect("The hash was already realized, it should therefore exist");
-
-                    handle_select_cmd(app, other, &mut video, Some(line_number), &mut ops).await?;
-                }
-            }
-        }
-    }
-
-    ops.commit(app).await?;
-    Ok(-line_number)
-}
-
-async fn open_editor_at(path: &Path) -> Result<()> {
-    let editor = env::var("EDITOR").unwrap_or("nvim".to_owned());
-
-    let mut nvim = Command::new(&editor);
-    nvim.arg(path);
-    let status = nvim
-        .status()
-        .await
-        .with_context(|| format!("Falied to run editor: {editor}"))?;
-
-    if status.success() {
-        Ok(())
-    } else {
-        bail!("Editor ({editor}) exited with error status: {}", status)
-    }
-}
 
 // // FIXME: There should be no reason why we need to re-run yt, just to get the help string. But I've
 // // yet to find a way to do it without the extra exec <2024-08-20>
diff --git a/crates/yt/src/select/selection_file/mod.rs b/crates/yt/src/select/selection_file/mod.rs
deleted file mode 100644
index 36342f8..0000000
--- a/crates/yt/src/select/selection_file/mod.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// 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>.
-
-//! The data structures needed to express the file, which the user edits
-
-use anyhow::{Result, bail};
-use shlex::Shlex;
-
-pub(crate) mod duration;
-
-/// # Panics
-/// If internal assertions fail.
-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)
-    } else {
-        let split: Vec<_> = {
-            let mut shl = Shlex::new(line);
-            let res = shl.by_ref().collect();
-
-            if shl.had_error {
-                bail!("Failed to parse line '{line}'")
-            }
-
-            assert_eq!(shl.line_no, 1, "A unexpected newline appeared");
-            res
-        };
-
-        assert!(!split.is_empty());
-
-        Ok(Some(split))
-    }
-}
diff --git a/crates/yt/src/status/mod.rs b/crates/yt/src/status/mod.rs
deleted file mode 100644
index de706ec..0000000
--- a/crates/yt/src/status/mod.rs
+++ /dev/null
@@ -1,162 +0,0 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// 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::time::Duration;
-
-use crate::{
-    app::App,
-    download::Downloader,
-    select::selection_file::duration::MaybeDuration,
-    shared::bytes::Bytes,
-    storage::db::{
-        subscription::Subscriptions,
-        video::{Video, VideoStatusMarker},
-    },
-};
-
-use anyhow::{Context, Result};
-
-macro_rules! get {
-    ($videos:expr, $status:ident) => {
-        $videos
-            .iter()
-            .filter(|vid| vid.status.as_marker() == VideoStatusMarker::$status)
-            .count()
-    };
-
-    (@collect $videos:expr, $status:ident) => {
-        $videos
-            .iter()
-            .filter(|vid| vid.status.as_marker() == VideoStatusMarker::$status)
-            .collect()
-    };
-}
-
-pub(crate) async fn show(app: &App, format: Option<String>) -> Result<()> {
-    let all_videos = Video::in_states(app, VideoStatusMarker::ALL).await?;
-
-    // lengths
-    let picked_videos_len = get!(all_videos, Pick);
-
-    let watch_videos_len = get!(all_videos, Watch);
-    let cached_videos_len = get!(all_videos, Cached);
-    let watched_videos_len = get!(all_videos, Watched);
-    let watched_videos: Vec<_> = get!(@collect all_videos, Watched);
-
-    let drop_videos_len = get!(all_videos, Drop);
-    let dropped_videos_len = get!(all_videos, Dropped);
-
-    let subscriptions = Subscriptions::get(app).await?;
-    let subscriptions_len = subscriptions.0.len();
-
-    let watchtime_status = {
-        let total_watch_time_raw = watched_videos
-            .iter()
-            .fold(Duration::default(), |acc, vid| acc + vid.watch_progress);
-
-        // Most things are watched at a speed of s (which is defined in the config file).
-        // Thus
-        //      y = x * s -> y / s = x
-        let total_watch_time = Duration::from_secs_f64(
-            (total_watch_time_raw.as_secs_f64()) / app.config.select.playback_speed,
-        );
-
-        let speed = app.config.select.playback_speed;
-
-        // Do not print the adjusted time, if the user has keep the speed level at 1.
-        #[allow(clippy::float_cmp)]
-        if speed == 1.0 {
-            format!(
-                "Total Watchtime: {}\n",
-                MaybeDuration::from_std(total_watch_time_raw)
-            )
-        } else {
-            format!(
-                "Total Watchtime: {} (at {speed} speed: {})\n",
-                MaybeDuration::from_std(total_watch_time_raw),
-                MaybeDuration::from_std(total_watch_time),
-            )
-        }
-    };
-
-    let watch_rate: f64 = {
-        fn to_f64(input: usize) -> f64 {
-            f64::from(u32::try_from(input).expect("This should never exceed u32::MAX"))
-        }
-
-        let count =
-            to_f64(watched_videos_len) / (to_f64(drop_videos_len) + to_f64(dropped_videos_len));
-        count * 100.0
-    };
-
-    let cache_usage_raw = Downloader::get_current_cache_allocation(app)
-        .await
-        .context("Failed to get current cache allocation")?;
-    let cache_usage: Bytes = cache_usage_raw;
-
-    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}
-Cached   Videos: {cached_videos_len}
-Watched  Videos: {watched_videos_len} (watch rate: {watch_rate:.2} %)
-
-Drop     Videos: {drop_videos_len}
-Dropped  Videos: {dropped_videos_len}
-
-{watchtime_status}
-
-  Subscriptions: {subscriptions_len}
-    Cache usage: {cache_usage}"
-        );
-    }
-
-    Ok(())
-}
-
-pub(crate) fn config(app: &App) -> Result<()> {
-    let config_str = toml::to_string(&app.config)?;
-
-    print!("{config_str}");
-
-    Ok(())
-}
diff --git a/crates/yt/src/storage/db/get/video/mod.rs b/crates/yt/src/storage/db/get/video/mod.rs
index ec00934..5f6700e 100644
--- a/crates/yt/src/storage/db/get/video/mod.rs
+++ b/crates/yt/src/storage/db/get/video/mod.rs
@@ -3,11 +3,15 @@ use std::{fs::File, path::PathBuf};
 use anyhow::{Context, Result, bail};
 use log::debug;
 use sqlx::query;
-use yt_dlp::info_json::InfoJson;
+use yt_dlp::{info_json::InfoJson, json_cast};
 
 use crate::{
     app::App,
-    storage::db::video::{Video, VideoStatus, VideoStatusMarker, video_from_record},
+    storage::db::video::{
+        Video, VideoStatus, VideoStatusMarker,
+        comments::{Comments, raw::RawComment},
+        video_from_record,
+    },
 };
 
 impl Video {
@@ -42,6 +46,70 @@ impl Video {
         }
     }
 
+    /// Returns the description of the current video.
+    /// The returned description will be set to `<No description>` in the absence of one.
+    ///
+    /// # Errors
+    /// If no current video exists.
+    ///
+    /// # Panics
+    /// If the current video lacks the `info.json` file.
+    pub(crate) async fn get_current_description(app: &App) -> Result<String> {
+        let Some(currently_playing_video) = Video::currently_focused(app).await? else {
+            bail!("Could not find a currently playing video!");
+        };
+
+        let info_json = &currently_playing_video.get_info_json()?.expect(
+            "A currently *playing* must be cached. \
+                And thus the info.json should be available.",
+        );
+
+        let description = info_json
+            .get("description")
+            .map_or("<No description>", |val| json_cast!(val, as_str))
+            .to_owned();
+
+        Ok(description)
+    }
+
+    /// Returns the comments of the current video.
+    /// The returned [`Comments`] will be empty in the absence of comments.
+    ///
+    /// # Errors
+    /// If no current video exists.
+    ///
+    /// # Panics
+    /// If the current video lacks the `info.json` file.
+    pub(crate) async fn get_current_comments(app: &App) -> Result<Comments> {
+        let Some(currently_playing_video) = Video::currently_focused(app).await? else {
+            bail!("Could not find a currently playing video!");
+        };
+
+        let info_json = &currently_playing_video.get_info_json()?.expect(
+            "A currently *playing* video must be cached. \
+                And thus the info.json should be available.",
+        );
+
+        let raw_comments = if let Some(comments) = info_json.get("comments") {
+            json_cast!(comments, as_array)
+                .iter()
+                .cloned()
+                .map(serde_json::from_value)
+                .collect::<Result<Vec<RawComment>, _>>()?
+        } else {
+            // TODO(@bpeetz): We could display a `<No-comments>` here. <2025-07-15>
+
+            bail!(
+                "The video ('{}') does not have comments!",
+                info_json
+                    .get("title")
+                    .map_or("<No Title>", |val| json_cast!(val, as_str))
+            )
+        };
+
+        Ok(Comments::from_raw(raw_comments))
+    }
+
     /// Optionally returns the video that is currently focused.
     ///
     /// # Panics
@@ -178,9 +246,7 @@ impl Video {
 
         let real_videos: Vec<Video> = videos
             .iter()
-            .map(|base| -> Video {
-                video_from_record!(base)
-            })
+            .map(|base| -> Video { video_from_record!(base) })
             .collect();
 
         Ok(real_videos)
diff --git a/crates/yt/src/comments/display.rs b/crates/yt/src/storage/db/video/comments/display.rs
index de50614..084e54c 100644
--- a/crates/yt/src/comments/display.rs
+++ b/crates/yt/src/storage/db/video/comments/display.rs
@@ -1,39 +1,23 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// 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 chrono::{Local, TimeZone};
 use chrono_humanize::{Accuracy, HumanTime, Tense};
+use colors::{Colorize, IntoCanvas};
 
-use crate::comments::comment::CommentExt;
-
-use super::comment::Comments;
+use crate::{
+    output::format_text,
+    storage::db::video::comments::{Comment, Comments},
+};
 
 impl Comments {
-    pub(crate) fn render(&self, color: bool) -> String {
-        self.render_help(color).expect("This should never fail.")
+    pub(crate) fn render(&self, use_color: bool) -> String {
+        self.render_help(use_color)
+            .expect("This should never fail.")
     }
 
-    fn render_help(&self, color: bool) -> Result<String, std::fmt::Error> {
-        macro_rules! c {
-            ($color_str:expr, $write:ident, $color:expr) => {
-                if $color {
-                    $write.write_str(concat!("\x1b[", $color_str, "m"))?
-                }
-            };
-        }
-
+    fn render_help(&self, use_color: bool) -> Result<String, std::fmt::Error> {
         fn format(
-            comment: &CommentExt,
+            comment: &Comment,
             f: &mut String,
             ident_count: u32,
             color: bool,
@@ -43,14 +27,16 @@ impl Comments {
 
             f.write_str(ident)?;
 
-            if value.author_is_uploader {
-                c!("91;1", f, color);
-            } else {
-                c!("35", f, color);
-            }
+            write!(
+                f,
+                "{}",
+                if value.author_is_uploader {
+                    (&value.author).bold().bright_red().render(color)
+                } else {
+                    (&value.author).purple().render(color)
+                }
+            )?;
 
-            f.write_str(&value.author)?;
-            c!("0", f, color);
             if value.edited || value.is_favorited {
                 f.write_str("[")?;
                 if value.edited {
@@ -65,7 +51,6 @@ impl Comments {
                 f.write_str("]")?;
             }
 
-            c!("36;1", f, color);
             write!(
                 f,
                 " {}",
@@ -76,17 +61,31 @@ impl Comments {
                         .expect("This should be valid")
                 )
                 .to_text_en(Accuracy::Rough, Tense::Past)
+                .bold()
+                .cyan()
+                .render(color)
             )?;
-            c!("0", f, color);
 
-            // c!("31;1", f);
-            // f.write_fmt(format_args!(" [{}]", comment.value.like_count))?;
-            // c!("0", f);
+            write!(
+                f,
+                " [{}]",
+                comment.value.like_count.bold().red().render(color)
+            )?;
 
             f.write_str(":\n")?;
             f.write_str(ident)?;
 
-            f.write_str(&value.text.replace('\n', &format!("\n{ident}")))?;
+            f.write_str(
+                &format_text(
+                    value.text.trim(),
+                    Some(
+                        termsize::get().map_or(90, |ts| ts.cols)
+                            - u16::try_from(ident_count).expect("Should never overflow"),
+                    ),
+                )
+                .trim()
+                .replace('\n', &format!("\n{ident}")),
+            )?;
             f.write_str("\n")?;
 
             if comment.replies.is_empty() {
@@ -105,12 +104,12 @@ impl Comments {
 
         let mut f = String::new();
 
-        if !&self.vec.is_empty() {
-            let mut children = self.vec.clone();
+        if !&self.inner.is_empty() {
+            let mut children = self.inner.clone();
             children.sort_by(|a, b| b.value.like_count.cmp(&a.value.like_count));
 
             for child in children {
-                format(&child, &mut f, 0, color)?;
+                format(&child, &mut f, 0, use_color)?;
             }
         }
         Ok(f)
diff --git a/crates/yt/src/storage/db/video/comments/mod.rs b/crates/yt/src/storage/db/video/comments/mod.rs
new file mode 100644
index 0000000..d2249b3
--- /dev/null
+++ b/crates/yt/src/storage/db/video/comments/mod.rs
@@ -0,0 +1,187 @@
+use std::{iter, mem};
+
+use regex::{Captures, Regex};
+
+use crate::storage::db::video::comments::raw::{Parent, RawComment};
+
+pub(crate) mod display;
+pub(crate) mod raw;
+
+#[cfg(test)]
+mod tests;
+
+#[derive(Debug, Clone, PartialEq)]
+pub(crate) struct Comment {
+    value: RawComment,
+    replies: Vec<Self>,
+}
+
+#[derive(Debug, Default, PartialEq)]
+pub(crate) struct Comments {
+    inner: Vec<Comment>,
+}
+
+impl Comments {
+    pub(crate) fn from_raw(raw: Vec<RawComment>) -> Self {
+        let mut me = Self::default();
+
+        // Apply the parent -> child mapping yt provides us with.
+        for raw_comment in raw {
+            if let Parent::Id(id) = &raw_comment.parent {
+                me.insert(&(id.clone()), Comment::from(raw_comment));
+            } else {
+                me.inner.push(Comment::from(raw_comment));
+            }
+        }
+
+        {
+            // Sort the final comments chronologically.
+            // This ensures that replies are matched with the comment they actually replied to and
+            // not a later comment from the same author.
+            for comment in &mut me.inner {
+                comment
+                    .replies
+                    .sort_by_key(|comment| comment.value.timestamp);
+
+                for reply in &comment.replies {
+                    assert!(reply.replies.is_empty());
+                }
+            }
+        }
+
+        {
+            let find_reply_indicator =
+                Regex::new(r"\u{200b}?(@[^\t\s]+)\u{200b}?").expect("This is hardcoded");
+
+            // Try to re-construct the replies for the reply comments.
+            for comment in &mut me.inner {
+                let previous_replies = mem::take(&mut comment.replies);
+
+                let mut reply_tree = Comments::default();
+
+                for reply in previous_replies {
+                    // We try to reconstruct the parent child relation ship by looking (naively)
+                    // for a reply indicator. Currently, this is just the `@<some_name>`, as yt
+                    // seems to insert that by default if you press `reply-to` in their clients.
+                    //
+                    // This follows these steps:
+                    // - Does this reply have a “reply indicator”?
+                    // - If yes, try to resolve the indicator.
+                    // - If it is resolvable, add this reply to the [`Comment`] it resolved to.
+                    // - If not, keep the comment as reply.
+
+                    if let Some(reply_indicator_matches) =
+                        find_reply_indicator.captures(&reply.value.text.clone())
+                    {
+                        // We found a reply indicator.
+                        // First we traverse the current `reply_tree` in reversed order to find a
+                        // match, than we check if the reply indicator matches the reply tree root
+                        // and afterward we declare it unmatching and add it as toplevel.
+
+                        let reply_target_author = reply_indicator_matches
+                            .get(1)
+                            .expect("This should also exist")
+                            .as_str();
+
+                        if let Some(parent) = reply_tree.find_author_mut(reply_target_author) {
+                            parent
+                                .replies
+                                .push(comment_from_reply(reply, &reply_indicator_matches));
+                        } else if comment.value.author == reply_target_author {
+                            reply_tree
+                                .add_toplevel(comment_from_reply(reply, &reply_indicator_matches));
+                        } else {
+                            eprintln!(
+                                "Failed to find a parent for ('{}') both directly \
+                                    and via replies! The reply text was:\n'{}'\n",
+                                reply_target_author, reply.value.text
+                            );
+                            reply_tree.add_toplevel(reply);
+                        }
+                    } else {
+                        // The comment text did not contain a reply indicator, so add it as
+                        // toplevel.
+                        reply_tree.add_toplevel(reply);
+                    }
+                }
+
+                comment.replies = reply_tree.inner;
+            }
+        }
+
+        me
+    }
+
+    fn add_toplevel(&mut self, value: Comment) {
+        self.inner.push(value);
+    }
+
+    fn insert(&mut self, id: &str, value: Comment) {
+        let parent = self
+            .inner
+            .iter_mut()
+            .find(|c| c.value.id.id == id)
+            .expect("One of these should exist");
+
+        parent.replies.push(value);
+    }
+
+    fn find_author_mut(&mut self, reply_target_author: &str) -> Option<&mut Comment> {
+        fn perform_check<'a>(
+            comment: &'a mut Comment,
+            reply_target_author: &str,
+        ) -> Option<&'a mut Comment> {
+            let base_check = if comment.value.author == reply_target_author {
+                return Some(comment);
+            } else {
+                None
+            };
+
+            if comment.replies.is_empty() {
+                base_check
+            } else {
+                for reply in comment.replies.iter_mut().rev() {
+                    if let Some(out) = perform_check(reply, reply_target_author) {
+                        return Some(out);
+                    }
+                }
+
+                base_check
+            }
+        }
+
+        for comment in self.inner.iter_mut().rev() {
+            if let Some(output) = perform_check(comment, reply_target_author) {
+                return Some(output);
+            }
+        }
+
+        None
+    }
+}
+fn comment_from_reply(reply: Comment, reply_indicator_matches: &Captures<'_>) -> Comment {
+    Comment::from(RawComment {
+        text: {
+            // Remove the `@<some_name>` for the comment text.
+            let full_match = reply_indicator_matches
+                .get(0)
+                .expect("This will always exist");
+
+            let text = reply.value.text[0..full_match.start()].to_owned()
+                + &reply.value.text[full_match.end()..];
+
+            text.trim_matches(|c: char| c == '\u{200b}' || c == '\u{2060}' || c.is_whitespace())
+                .to_owned()
+        },
+        ..reply.value
+    })
+}
+
+impl From<RawComment> for Comment {
+    fn from(value: RawComment) -> Self {
+        Self {
+            value,
+            replies: vec![],
+        }
+    }
+}
diff --git a/crates/yt/src/comments/comment.rs b/crates/yt/src/storage/db/video/comments/raw.rs
index 30b8ea0..a79820a 100644
--- a/crates/yt/src/comments/comment.rs
+++ b/crates/yt/src/storage/db/video/comments/raw.rs
@@ -1,18 +1,22 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// 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 serde::{Deserialize, Deserializer, Serialize};
+use serde::{Deserialize, Deserializer};
 use url::Url;
 
-#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, PartialOrd, Ord)]
+#[derive(Debug, Deserialize, Clone, Eq, PartialEq, PartialOrd, Ord)]
+#[serde(from = "String")]
+#[serde(deny_unknown_fields)]
+pub(crate) struct Id {
+    pub(crate) id: String,
+}
+impl From<String> for Id {
+    fn from(value: String) -> Self {
+        Self {
+            // Take the last element if the string is split with dots, otherwise take the full id
+            id: value.split('.').next_back().unwrap_or(&value).to_owned(),
+        }
+    }
+}
+
+#[derive(Debug, Deserialize, Clone, Eq, PartialEq, PartialOrd, Ord)]
 #[serde(from = "String")]
 #[serde(deny_unknown_fields)]
 pub(crate) enum Parent {
@@ -30,24 +34,9 @@ impl From<String> for Parent {
     }
 }
 
-#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, PartialOrd, Ord)]
-#[serde(from = "String")]
-#[serde(deny_unknown_fields)]
-pub(crate) struct Id {
-    pub(crate) id: String,
-}
-impl From<String> for Id {
-    fn from(value: String) -> Self {
-        Self {
-            // Take the last element if the string is split with dots, otherwise take the full id
-            id: value.split('.').next_back().unwrap_or(&value).to_owned(),
-        }
-    }
-}
-
-#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, PartialOrd, Ord)]
+#[derive(Debug, Deserialize, Clone, Eq, PartialEq, PartialOrd, Ord)]
 #[allow(clippy::struct_excessive_bools)]
-pub(crate) struct Comment {
+pub(crate) struct RawComment {
     pub(crate) id: Id,
     pub(crate) text: String,
     #[serde(default = "zero")]
@@ -86,47 +75,3 @@ where
         Ok(false)
     }
 }
-
-#[derive(Debug, Clone)]
-#[allow(clippy::module_name_repetitions)]
-pub(crate) struct CommentExt {
-    pub(crate) value: Comment,
-    pub(crate) replies: Vec<CommentExt>,
-}
-
-#[derive(Debug, Default)]
-pub(crate) struct Comments {
-    pub(super) vec: Vec<CommentExt>,
-}
-
-impl Comments {
-    pub(crate) fn new() -> Self {
-        Self::default()
-    }
-    pub(crate) fn push(&mut self, value: CommentExt) {
-        self.vec.push(value);
-    }
-    pub(crate) fn insert(&mut self, key: &str, value: CommentExt) {
-        let parent = self
-            .vec
-            .iter_mut()
-            .filter(|c| c.value.id.id == key)
-            .next_back()
-            .expect("One of these should exist");
-        parent.push_reply(value);
-    }
-}
-impl CommentExt {
-    pub(crate) fn push_reply(&mut self, value: CommentExt) {
-        self.replies.push(value);
-    }
-}
-
-impl From<Comment> for CommentExt {
-    fn from(value: Comment) -> Self {
-        Self {
-            replies: vec![],
-            value,
-        }
-    }
-}
diff --git a/crates/yt/src/storage/db/video/comments/tests.rs b/crates/yt/src/storage/db/video/comments/tests.rs
new file mode 100644
index 0000000..138b1b6
--- /dev/null
+++ b/crates/yt/src/storage/db/video/comments/tests.rs
@@ -0,0 +1,219 @@
+use pretty_assertions::assert_eq;
+use url::Url;
+
+use crate::storage::db::video::comments::{
+    Comment, Comments, RawComment,
+    raw::{Id, Parent},
+};
+
+/// Generate both an [`expected`] and an [`input`] value from an expected comment expression.
+macro_rules! mk_comments {
+    () => {{
+        let input: Vec<RawComment> = vec![];
+        let expected: Comments = Comments {
+            inner: vec![],
+        };
+
+        (input, expected)
+    }};
+
+    (
+        $(
+            parent: $parent:expr, $actual_parent:ident,
+            (
+                @ $name:ident : $comment:literal
+                $(
+                    $reply_chain:tt
+                )*
+            )
+        )+
+    ) => {{
+        let (nested_input, _) = mk_comments!(
+            $(
+                $(
+                    parent: $parent, $name,
+                    $reply_chain
+                )*
+            )+
+        );
+
+        let mut input: Vec<RawComment> = vec![
+            $(
+                mk_comments!(@to_raw input $name $comment $parent, $actual_parent)
+            ),+
+        ];
+        input.extend(nested_input);
+
+        let expected: Comments = Comments {
+            inner: vec![
+                $(
+                    Comment {
+                        value: mk_comments!(@to_raw expected $name $comment $parent, $actual_parent),
+                        replies: {
+                            let (_, nested_expected) = mk_comments!(
+                                $(
+                                    parent: $parent, $name,
+                                    $reply_chain
+                                )*
+                            );
+
+                            nested_expected.inner
+                        },
+                    }
+                ),+
+            ]
+        };
+
+        (input, expected)
+    }};
+    (
+        $(
+            (
+                @ $name:ident : $comment:literal
+                $(
+                    $reply_chain:tt
+                )*
+            )
+        )+
+    ) => {{
+        let (nested_input, _) = mk_comments!(
+            $(
+                $(
+                    parent: mk_comments!(@mk_id $name $comment), $name,
+                    $reply_chain
+                )*
+            )+
+        );
+
+        let mut input: Vec<RawComment> = vec![
+            $(
+                mk_comments!(@to_raw input $name $comment)
+            ),+
+        ];
+        input.extend(nested_input);
+
+        let expected: Comments = Comments {
+            inner: vec![
+                $(
+                    Comment {
+                        value: mk_comments!(@to_raw expected $name $comment),
+                        replies: {
+                            let (_, nested_expected) = mk_comments!(
+                                $(
+                                    parent: mk_comments!(@mk_id $name $comment), $name,
+                                    $reply_chain
+                                )*
+                            );
+
+                            nested_expected.inner
+                        },
+                    }
+                ),+
+            ]
+        };
+
+        (input, expected)
+    }};
+
+    (@mk_id $name:ident $comment:literal) => {{
+        use std::hash::{Hash, Hasher};
+
+        let input = format!("{}{}", stringify!($name), $comment);
+
+        let mut digest = std::hash::DefaultHasher::new();
+        input.hash(&mut digest);
+        Id { id: digest.finish().to_string() }
+    }};
+
+    (@to_raw $state:ident $name:ident $comment:literal $($parent:expr, $actual_parent:ident)?) => {
+        RawComment {
+            id: mk_comments!(@mk_id $name $comment),
+            text: mk_comments!(@mk_text $state $comment $(, $actual_parent)?),
+            like_count: 0,
+            is_pinned: false,
+            author_id: stringify!($name).to_owned(),
+            author: format!("@{}", stringify!($name)),
+            author_is_verified: false,
+            author_thumbnail: Url::from_file_path("/dev/null").unwrap(),
+            parent: mk_comments!(@mk_parent $($parent)?),
+            edited: false,
+            timestamp: 0,
+            author_url: None,
+            author_is_uploader: false,
+            is_favorited: false,
+        }
+    };
+
+    (@mk_parent) => {
+        Parent::Root
+    };
+    (@mk_parent $parent:expr) => {
+        Parent::Id($parent.id)
+    };
+
+    (@mk_text input $text:expr) => {
+        $text.to_owned()
+    };
+    (@mk_text input $text:expr, $actual_parent:ident) => {
+        format!("@{} {}", stringify!($actual_parent), $text)
+    };
+    (@mk_text expected $text:expr $(, $_:tt)?) => {
+        $text.to_owned()
+    };
+}
+
+#[test]
+fn test_comments_toplevel() {
+    let (input, expected) = mk_comments!(
+        (@kant: "I think, that using the results of an action to determine morality is flawed.")
+        (@hume: "I think, that we should use our feeling for morality more.")
+        (@lock: "I think, that we should rely on the sum of happiness caused by an action to determine it's morality.")
+    );
+
+    assert_eq!(Comments::from_raw(input), expected);
+}
+
+#[test]
+fn test_comments_replies_1_level() {
+    let (input, expected) = mk_comments!(
+        (@hume: "I think, that we should use our feeling for morality more."
+            (@kant: "This is so wrong! I shall now dedicate my next 7? years to writing books that prove this.")
+            (@lock: "It feels not very applicable, no? We should focus on something that can be used in the court of law!"))
+    );
+
+    assert_eq!(
+        Comments::from_raw(input).render(true),
+        expected.render(true)
+    );
+}
+
+#[test]
+fn test_comments_replies_2_levels() {
+    let (input, expected) = mk_comments! {
+        (@singer: "We perform medical studies on animals; Children have lower or similar mental ability as these animals.."
+            (@singer: "Therefore, we should perform these studies on children instead, if we were to follow our own principals"
+                (@james: "This is ridiculous! I will not entertain this thought.")
+                (@singer: "Although one could also use this argument to argue for abortion _after_ birth.")))
+    };
+
+    assert_eq!(
+        Comments::from_raw(input).render(true),
+        expected.render(true)
+    );
+}
+
+#[test]
+fn test_comments_replies_3_levels() {
+    let (input, expected) = mk_comments! {
+        (@singer: "We perform medical studies on animals; Children have lower or similar mental ability as these animals.."
+            (@singer: "Therefore, we should perform these studies on children instead, if we were to follow our own principals"
+                (@james: "This is ridiculous! I will not entertain this thought."
+                    (@singer: "You know that I am not actually suggesting that? This is but a way to critizise the society"))
+                (@singer: "Although one could also use this argument to argue for abortion _after_ birth.")))
+    };
+
+    assert_eq!(
+        Comments::from_raw(input).render(true),
+        expected.render(true)
+    );
+}
diff --git a/crates/yt/src/storage/db/video.rs b/crates/yt/src/storage/db/video/mod.rs
index f1b8bb9..e768cec 100644
--- a/crates/yt/src/storage/db/video.rs
+++ b/crates/yt/src/storage/db/video/mod.rs
@@ -1,17 +1,18 @@
 use std::{fmt::Display, path::PathBuf, time::Duration};
 
 use chrono::{DateTime, Utc};
+use serde::{Deserialize, Serialize};
 use url::Url;
 
-use crate::{
-    select::selection_file::duration::MaybeDuration, storage::db::extractor_hash::ExtractorHash,
-};
+use crate::{select::duration::MaybeDuration, storage::db::extractor_hash::ExtractorHash};
+
+pub(crate) mod comments;
 
 macro_rules! video_from_record {
     ($record:expr) => {
         $crate::storage::db::video::Video {
             description: $record.description.clone(),
-            duration: $crate::select::selection_file::duration::MaybeDuration::from_maybe_secs_f64(
+            duration: $crate::select::duration::MaybeDuration::from_maybe_secs_f64(
                 $record.duration,
             ),
             extractor_hash: $crate::storage::db::extractor_hash::ExtractorHash::from_hash(
@@ -91,7 +92,7 @@ pub(crate) struct Video {
 }
 
 /// The priority of a [`Video`].
-#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Debug, Serialize, Deserialize, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 pub(crate) struct Priority {
     value: i64,
 }
@@ -156,7 +157,7 @@ impl Display for TimeStamp {
 /// Cache                       // yt cache
 ///     |
 /// Watched                     // yt watch
-#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
+#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
 pub(crate) enum VideoStatus {
     #[default]
     Pick,
diff --git a/crates/yt/src/update/mod.rs b/crates/yt/src/update/mod.rs
deleted file mode 100644
index 809289c..0000000
--- a/crates/yt/src/update/mod.rs
+++ /dev/null
@@ -1,211 +0,0 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// 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::{str::FromStr, time::Duration};
-
-use anyhow::{Context, Ok, Result};
-use chrono::{DateTime, Utc};
-use log::warn;
-use url::Url;
-use yt_dlp::{info_json::InfoJson, json_cast, json_get};
-
-use crate::{
-    app::App,
-    select::selection_file::duration::MaybeDuration,
-    storage::db::{
-        extractor_hash::ExtractorHash,
-        insert::Operations,
-        subscription::{Subscription, Subscriptions},
-        video::{Priority, TimeStamp, Video, VideoStatus},
-    },
-};
-
-mod updater;
-use updater::Updater;
-
-pub(crate) async fn update(
-    app: &App,
-    max_backlog: usize,
-    subscription_names_to_update: Vec<String>,
-) -> Result<()> {
-    let subscriptions = Subscriptions::get(app).await?;
-
-    let subs: Vec<Subscription> = if subscription_names_to_update.is_empty() {
-        subscriptions.0.into_values().collect()
-    } else {
-        subscriptions
-            .0
-            .into_values()
-            .filter(|sub| subscription_names_to_update.contains(&sub.name))
-            .collect()
-    };
-
-    // We can get away with not having to re-fetch the hashes every time, as the returned video
-    // should not contain duplicates.
-    let hashes = ExtractorHash::get_all(app).await?;
-
-    let updater = Updater::new(max_backlog, app.config.update.pool_size, hashes);
-    updater.update(app, subs).await?;
-
-    Ok(())
-}
-
-#[allow(clippy::too_many_lines)]
-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}'). \
-                    Expected `YYYY-MM-DD`, has the format changed?"
-        );
-        if let Some(date_string) = extended {
-            format!("{f}\nThe parsed '{date_string}' can't be turned to a valid UTC date.'")
-        } else {
-            f
-        }
-    }
-
-    let publish_date = if let Some(date) = &entry.get("upload_date") {
-        let date = json_cast!(date, as_str);
-
-        let year: u32 = date
-            .chars()
-            .take(4)
-            .collect::<String>()
-            .parse()
-            .with_context(|| fmt_context(date, None))?;
-        let month: u32 = date
-            .chars()
-            .skip(4)
-            .take(2)
-            .collect::<String>()
-            .parse()
-            .with_context(|| fmt_context(date, None))?;
-        let day: u32 = date
-            .chars()
-            .skip(4 + 2)
-            .take(2)
-            .collect::<String>()
-            .parse()
-            .with_context(|| fmt_context(date, None))?;
-
-        let date_string = format!("{year:04}-{month:02}-{day:02}T00:00:00Z");
-        Some(
-            DateTime::<Utc>::from_str(&date_string)
-                .with_context(|| fmt_context(date, Some(&date_string)))?
-                .timestamp(),
-        )
-    } else {
-        warn!(
-            "The video '{}' lacks it's upload date!",
-            json_get!(entry, "title", as_str)
-        );
-        None
-    };
-
-    let thumbnail_url = match (&entry.get("thumbnails"), &entry.get("thumbnail")) {
-        (None, None) => None,
-        (None, Some(thumbnail)) => Some(Url::from_str(json_cast!(thumbnail, as_str))?),
-
-        // TODO: The algorithm is not exactly the best <2024-05-28>
-        (Some(thumbnails), None) => {
-            if let Some(thumbnail) = json_cast!(thumbnails, as_array).first() {
-                Some(Url::from_str(json_get!(
-                    json_cast!(thumbnail, as_object),
-                    "url",
-                    as_str
-                ))?)
-            } else {
-                None
-            }
-        }
-        (Some(_), Some(thumnail)) => Some(Url::from_str(json_cast!(thumnail, as_str))?),
-    };
-
-    let url = {
-        let smug_url: Url = json_get!(entry, "webpage_url", as_str).parse()?;
-        // TODO(@bpeetz): We should probably add this? <2025-06-14>
-        // if '#__youtubedl_smuggle' not in smug_url:
-        //     return smug_url, default
-        // url, _, sdata = smug_url.rpartition('#')
-        // jsond = urllib.parse.parse_qs(sdata)['__youtubedl_smuggle'][0]
-        // data = json.loads(jsond)
-        // return url, data
-
-        smug_url
-    };
-
-    let extractor_hash = ExtractorHash::from_info_json(entry);
-
-    let subscription_name = if let Some(sub) = sub {
-        Some(sub.name.clone())
-    } else if let Some(uploader) = entry.get("uploader").map(|val| json_cast!(val, as_str)) {
-        if entry
-            .get("webpage_url_domain")
-            .map(|val| json_cast!(val, as_str))
-            == Some("youtube.com")
-        {
-            Some(format!("{uploader} - Videos"))
-        } else {
-            Some(uploader.to_owned())
-        }
-    } else {
-        None
-    };
-
-    let video = Video {
-        description: entry
-            .get("description")
-            .map(|val| json_cast!(val, as_str).to_owned()),
-        duration: MaybeDuration::from_maybe_secs_f64(
-            entry.get("duration").map(|val| json_cast!(val, as_f64)),
-        ),
-        extractor_hash,
-        last_status_change: TimeStamp::from_now(),
-        parent_subscription_name: subscription_name,
-        priority: Priority::default(),
-        publish_date: publish_date.map(TimeStamp::from_secs),
-        status: VideoStatus::Pick,
-        thumbnail_url,
-        title: json_get!(entry, "title", as_str).to_owned(),
-        url,
-        watch_progress: Duration::default(),
-        playback_speed: None,
-        subtitle_langs: None,
-    };
-    Ok(video)
-}
-
-async fn process_subscription(app: &App, sub: Subscription, entry: InfoJson) -> Result<()> {
-    let mut ops = Operations::new("Update: process subscription");
-    let video = video_entry_to_video(&entry, Some(&sub))
-        .context("Failed to parse search entry as Video")?;
-
-    let title = video.title.clone();
-    let url = video.url.clone();
-    let video = video.add(&mut ops).with_context(|| {
-        format!("Failed to add video to database: '{title}' (with url: '{url}')")
-    })?;
-
-    ops.commit(app).await.with_context(|| {
-        format!(
-            "Failed to add video to database: '{}' (with url: '{}')",
-            video.title, video.url
-        )
-    })?;
-    println!(
-        "{}",
-        &video
-            .to_line_display(app, None)
-            .await
-            .with_context(|| format!("Failed to format video: '{}'", video.title))?
-    );
-    Ok(())
-}
diff --git a/crates/yt/src/videos/display/mod.rs b/crates/yt/src/videos/display/mod.rs
deleted file mode 100644
index 54e98ed..0000000
--- a/crates/yt/src/videos/display/mod.rs
+++ /dev/null
@@ -1,241 +0,0 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// 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::db::video::{TimeStamp, Video, VideoStatus},
-};
-
-pub(crate) mod format_video;
-
-macro_rules! get {
-    ($value:expr, $key:ident, $name:expr, $code:tt) => {
-        if let Some(value) = &$value.$key {
-            $code(value)
-        } else {
-            concat!("[No ", $name, "]").to_owned()
-        }
-    };
-}
-
-fn maybe_add_color<F>(app: &App, input: String, mut color_fn: F) -> String
-where
-    F: FnMut(String) -> String,
-{
-    if app.config.global.display_colors {
-        color_fn(input)
-    } else {
-        input
-    }
-}
-impl Video {
-    #[must_use]
-    pub(crate) fn cache_path_fmt(&self, app: &App) -> String {
-        let cache_path = if let VideoStatus::Cached {
-            cache_path,
-            is_focused: _,
-        } = &self.status
-        {
-            cache_path.to_string_lossy().to_string()
-        } else {
-            "[No Cache Path]".to_owned()
-        };
-        maybe_add_color(app, cache_path, |v| v.blue().bold().to_string())
-    }
-
-    #[must_use]
-    pub(crate) fn description_fmt(&self) -> String {
-        get!(
-            self,
-            description,
-            "Description",
-            (|value: &str| value.to_owned())
-        )
-    }
-
-    #[must_use]
-    pub(crate) fn duration_fmt_no_color(&self) -> String {
-        self.duration.to_string()
-    }
-    #[must_use]
-    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(crate) fn watch_progress_fmt(&self, app: &App) -> String {
-        maybe_add_color(
-            app,
-            MaybeDuration::from_std(self.watch_progress).to_string(),
-            |v| v.cyan().bold().to_string(),
-        )
-    }
-
-    pub(crate) async fn extractor_hash_fmt_no_color(&self, app: &App) -> Result<String> {
-        let hash = self
-            .extractor_hash
-            .into_short_hash(app)
-            .await
-            .with_context(|| {
-                format!(
-                    "Failed to format extractor hash, whilst formatting video: '{}'",
-                    self.title
-                )
-            })?
-            .to_string();
-        Ok(hash)
-    }
-    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()
-        }))
-    }
-
-    #[must_use]
-    pub(crate) fn in_playlist_fmt(&self, app: &App) -> String {
-        let output = match &self.status {
-            VideoStatus::Pick
-            | VideoStatus::Watch
-            | VideoStatus::Watched
-            | VideoStatus::Drop
-            | VideoStatus::Dropped => "Not in the playlist",
-            VideoStatus::Cached { is_focused, .. } => {
-                if *is_focused {
-                    "In the playlist and focused"
-                } else {
-                    "In the playlist"
-                }
-            }
-        };
-        maybe_add_color(app, output.to_owned(), |v| v.yellow().italic().to_string())
-    }
-    #[must_use]
-    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(crate) fn parent_subscription_name_fmt_no_color(&self) -> String {
-        get!(
-            self,
-            parent_subscription_name,
-            "author",
-            (|sub: &str| sub.replace('"', "'"))
-        )
-    }
-    #[must_use]
-    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(crate) fn priority_fmt(&self) -> String {
-        self.priority.to_string()
-    }
-
-    #[must_use]
-    pub(crate) fn publish_date_fmt_no_color(&self) -> String {
-        get!(
-            self,
-            publish_date,
-            "release date",
-            (|date: &TimeStamp| date.to_string())
-        )
-    }
-    #[must_use]
-    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(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(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(crate) fn thumbnail_url_fmt(&self) -> String {
-        get!(
-            self,
-            thumbnail_url,
-            "thumbnail URL",
-            (|url: &Url| url.to_string())
-        )
-    }
-
-    #[must_use]
-    pub(crate) fn title_fmt_no_color(&self) -> String {
-        self.title.replace(['"', '„', '”', '“'], "'")
-    }
-    #[must_use]
-    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(crate) fn url_fmt_no_color(&self) -> String {
-        self.url.as_str().replace('"', "\\\"")
-    }
-    #[must_use]
-    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(crate) fn video_options_fmt_no_color(&self, app: &App) -> String {
-        let video_options = {
-            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}")
-        };
-        video_options
-    }
-
-    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/display/format_video.rs b/crates/yt/src/videos/format_video.rs
index 80ac5dd..9e86205 100644
--- a/crates/yt/src/videos/display/format_video.rs
+++ b/crates/yt/src/videos/format_video.rs
@@ -10,8 +10,9 @@
 // If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 
 use anyhow::Result;
+use colors::{Colorize, IntoCanvas};
 
-use crate::{app::App, comments::output::format_text, storage::db::video::Video};
+use crate::{app::App, output::format_text, storage::db::video::Video, videos::RenderWithApp};
 
 impl Video {
     pub(crate) async fn to_info_display(
@@ -19,21 +20,21 @@ impl Video {
         app: &App,
         format: Option<String>,
     ) -> Result<String> {
-        let cache_path = self.cache_path_fmt(app);
-        let description = self.description_fmt();
-        let duration = self.duration_fmt(app);
-        let extractor_hash = self.extractor_hash_fmt(app).await?;
-        let in_playlist = self.in_playlist_fmt(app);
-        let last_status_change = self.last_status_change_fmt(app);
-        let parent_subscription_name = self.parent_subscription_name_fmt(app);
-        let priority = self.priority_fmt();
-        let publish_date = self.publish_date_fmt(app);
-        let status = self.status_fmt(app);
-        let thumbnail_url = self.thumbnail_url_fmt();
-        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);
+        let cache_path = self.cache_path_fmt().to_string(app);
+        let description = self.description_fmt().to_string(app);
+        let duration = self.duration_fmt().to_string(app);
+        let extractor_hash = self.extractor_hash_fmt(app).await?.to_string(app);
+        let in_playlist = self.in_playlist_fmt().to_string(app);
+        let last_status_change = self.last_status_change_fmt().to_string(app);
+        let parent_subscription_name = self.parent_subscription_name_fmt().to_string(app);
+        let priority = self.priority_fmt().to_string(app);
+        let publish_date = self.publish_date_fmt().to_string(app);
+        let status = self.status_fmt().to_string(app);
+        let thumbnail_url = self.thumbnail_url_fmt().to_string(app);
+        let title = self.title_fmt().to_string(app);
+        let url = self.url_fmt().to_string(app);
+        let watch_progress = self.watch_progress_fmt().to_string(app);
+        let video_options = self.video_options_fmt(app).to_string(app);
 
         let watched_percentage_fmt = {
             if let Some(duration) = self.duration.as_secs() {
@@ -42,13 +43,15 @@ impl Video {
                     (self.watch_progress.as_secs() / duration) * 100
                 )
             } else {
-                format!(" {watch_progress}")
+                format!(" {}", watch_progress)
             }
-        };
+            .into_canvas()
+        }
+        .to_string(app);
 
         let options = video_options.to_string();
         let options = options.trim();
-        let description = format_text(description.to_string().as_str());
+        let description = format_text(description.to_string().as_str(), None);
 
         let string = if let Some(format) = format {
             format
@@ -91,13 +94,13 @@ impl Video {
         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 status = self.status_fmt().to_string(app);
+        let extractor_hash = self.extractor_hash_fmt(app).await?.to_string(app);
+        let title = self.title_fmt().to_string(app);
+        let publish_date = self.publish_date_fmt().to_string(app);
+        let parent_subscription_name = self.parent_subscription_name_fmt().to_string(app);
+        let duration = self.duration_fmt().to_string(app);
+        let url = self.url_fmt().to_string(app);
 
         let f = if let Some(format) = format {
             format
@@ -120,14 +123,14 @@ impl Video {
     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),
-            self.extractor_hash_fmt_no_color(app).await?,
-            self.title_fmt_no_color(),
-            self.publish_date_fmt_no_color(),
-            self.parent_subscription_name_fmt_no_color(),
-            self.duration_fmt_no_color(),
-            self.url_fmt_no_color(),
+            self.status_fmt().render(false),
+            self.video_options_fmt(app).render(false),
+            self.extractor_hash_fmt(app).await?.render(false),
+            self.title_fmt().render(false),
+            self.publish_date_fmt().render(false),
+            self.parent_subscription_name_fmt().render(false),
+            self.duration_fmt().render(false),
+            self.url_fmt().render(false),
             '\n'
         );
 
diff --git a/crates/yt/src/watch/playlist.rs b/crates/yt/src/watch/playlist.rs
deleted file mode 100644
index 7f1db2b..0000000
--- a/crates/yt/src/watch/playlist.rs
+++ /dev/null
@@ -1,111 +0,0 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// 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, path::Path};
-
-use crate::{
-    ansi_escape_codes::{cursor_up, erase_in_display_from_cursor},
-    app::App,
-    storage::{
-        db::{
-            playlist::Playlist,
-            video::{Video, VideoStatus},
-        },
-        notify::wait_for_db_write,
-    },
-};
-
-use anyhow::Result;
-use futures::{TryStreamExt, stream::FuturesOrdered};
-
-/// Extract the values of the [`VideoStatus::Cached`] value from a Video.
-fn cache_values(video: &Video) -> (&Path, bool) {
-    if let VideoStatus::Cached {
-        cache_path,
-        is_focused,
-    } = &video.status
-    {
-        (cache_path, *is_focused)
-    } else {
-        unreachable!("All of these videos should be cached");
-    }
-}
-
-/// # Panics
-/// Only if internal assertions fail.
-pub(crate) async fn playlist(app: &App, watch: bool) -> Result<()> {
-    let mut previous_output_length = 0;
-    loop {
-        let playlist = Playlist::create(app).await?.videos;
-
-        let output = playlist
-            .into_iter()
-            .map(|video| async move {
-                let mut output = String::new();
-
-                let (_, is_focused) = cache_values(&video);
-
-                if is_focused {
-                    output.push_str("🔻 ");
-                } else {
-                    output.push_str("  ");
-                }
-
-                output.push_str(&video.title_fmt(app));
-
-                output.push_str(" (");
-                output.push_str(&video.parent_subscription_name_fmt(app));
-                output.push(')');
-
-                output.push_str(" [");
-                output.push_str(&video.duration_fmt(app));
-
-                if is_focused {
-                    output.push_str(" (");
-                    if let Some(duration) = video.duration.as_secs() {
-                        let watch_progress: f64 = f64::from(
-                            u32::try_from(video.watch_progress.as_secs()).expect("No overflow"),
-                        );
-                        let duration = f64::from(u32::try_from(duration).expect("No overflow"));
-
-                        write!(output, "{:0.0}%", (watch_progress / duration) * 100.0)?;
-                    } else {
-                        write!(output, "{}", video.watch_progress_fmt(app))?;
-                    }
-
-                    output.push(')');
-                }
-                output.push(']');
-
-                output.push('\n');
-
-                Ok::<String, anyhow::Error>(output)
-            })
-            .collect::<FuturesOrdered<_>>()
-            .try_collect::<String>()
-            .await?;
-
-        // Delete the previous output
-        cursor_up(previous_output_length);
-        erase_in_display_from_cursor();
-
-        previous_output_length = output.chars().filter(|ch| *ch == '\n').count();
-
-        print!("{output}");
-
-        if !watch {
-            break;
-        }
-
-        wait_for_db_write(app).await?;
-    }
-
-    Ok(())
-}
diff --git a/crates/yt/src/yt_dlp/mod.rs b/crates/yt/src/yt_dlp/mod.rs
new file mode 100644
index 0000000..edf27e8
--- /dev/null
+++ b/crates/yt/src/yt_dlp/mod.rs
@@ -0,0 +1,249 @@
+use std::{str::FromStr, time::Duration};
+
+use anyhow::{Context, Result};
+use chrono::{DateTime, Utc};
+use futures::{FutureExt, future::BoxFuture};
+use log::{error, warn};
+use serde_json::json;
+use tokio::{fs, io};
+use url::Url;
+use yt_dlp::{YoutubeDL, info_json::InfoJson, json_cast, json_get, options::YoutubeDLOptions};
+
+use crate::{
+    app::App,
+    select::duration::MaybeDuration,
+    shared::bytes::Bytes,
+    storage::db::{
+        extractor_hash::ExtractorHash,
+        subscription::Subscription,
+        video::{Priority, TimeStamp, Video, VideoStatus},
+    },
+};
+
+pub(crate) fn yt_dlp_opts_updating(max_backlog: usize) -> Result<YoutubeDL> {
+    Ok(YoutubeDLOptions::new()
+        .set("playliststart", 1)
+        .set("playlistend", max_backlog)
+        .set("noplaylist", false)
+        .set(
+            "extractor_args",
+            json! {{"youtubetab": {"approximate_date": [""]}}},
+        )
+        // // TODO: This also removes unlisted and other stuff. Find a good way to remove the
+        // // members-only videos from the feed. <2025-04-17>
+        // .set("match-filter", "availability=public")
+        .build()?)
+}
+
+impl Video {
+    pub(crate) fn get_approx_size(&self) -> Result<u64> {
+        let yt_dlp = {
+            YoutubeDLOptions::new()
+                .set("prefer_free_formats", true)
+                .set("format", "bestvideo[height<=?1080]+bestaudio/best")
+                .set("fragment_retries", 10)
+                .set("retries", 10)
+                .set("getcomments", false)
+                .set("ignoreerrors", false)
+                .build()
+                .context("Failed to instanciate get approx size yt_dlp")
+        }?;
+
+        let result = yt_dlp
+            .extract_info(&self.url, false, true)
+            .with_context(|| format!("Failed to extract video information: '{}'", self.title))?;
+
+        let size = if let Some(val) = result.get("filesize") {
+            json_cast!(val, as_u64)
+        } else if let Some(serde_json::Value::Number(num)) = result.get("filesize_approx") {
+            // NOTE(@bpeetz): yt_dlp sets this value to `Null`, instead of omitting it when it
+            // can't calculate the approximate filesize.
+            // Thus, we have to check, that it is actually non-null, before we cast it. <2025-06-15>
+            json_cast!(num, as_u64)
+        } else if result.get("duration").is_some() && result.get("tbr").is_some() {
+            #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
+            let duration = json_get!(result, "duration", as_f64).ceil() as u64;
+
+            // TODO: yt_dlp gets this from the format
+            #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
+            let tbr = json_get!(result, "tbr", as_f64).ceil() as u64;
+
+            duration * tbr * (1000 / 8)
+        } else {
+            let hardcoded_default = Bytes::from_str("250 MiB").expect("This is hardcoded");
+            error!(
+                "Failed to find a filesize for video: {:?} (Using hardcoded value of {})",
+                self.title, hardcoded_default
+            );
+            hardcoded_default.as_u64()
+        };
+
+        Ok(size)
+    }
+}
+
+impl Video {
+    #[allow(clippy::too_many_lines)]
+    pub(crate) fn from_info_json(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}'). \
+                    Expected `YYYY-MM-DD`, has the format changed?"
+            );
+            if let Some(date_string) = extended {
+                format!("{f}\nThe parsed '{date_string}' can't be turned to a valid UTC date.'")
+            } else {
+                f
+            }
+        }
+
+        let publish_date = if let Some(date) = &entry.get("upload_date") {
+            let date = json_cast!(date, as_str);
+
+            let year: u32 = date
+                .chars()
+                .take(4)
+                .collect::<String>()
+                .parse()
+                .with_context(|| fmt_context(date, None))?;
+            let month: u32 = date
+                .chars()
+                .skip(4)
+                .take(2)
+                .collect::<String>()
+                .parse()
+                .with_context(|| fmt_context(date, None))?;
+            let day: u32 = date
+                .chars()
+                .skip(4 + 2)
+                .take(2)
+                .collect::<String>()
+                .parse()
+                .with_context(|| fmt_context(date, None))?;
+
+            let date_string = format!("{year:04}-{month:02}-{day:02}T00:00:00Z");
+            Some(
+                DateTime::<Utc>::from_str(&date_string)
+                    .with_context(|| fmt_context(date, Some(&date_string)))?
+                    .timestamp(),
+            )
+        } else {
+            warn!(
+                "The video '{}' lacks it's upload date!",
+                json_get!(entry, "title", as_str)
+            );
+            None
+        };
+
+        let thumbnail_url = match (&entry.get("thumbnails"), &entry.get("thumbnail")) {
+            (None, None) => None,
+            (None, Some(thumbnail)) => Some(Url::from_str(json_cast!(thumbnail, as_str))?),
+
+            // TODO: The algorithm is not exactly the best <2024-05-28>
+            (Some(thumbnails), None) => {
+                if let Some(thumbnail) = json_cast!(thumbnails, as_array).first() {
+                    Some(Url::from_str(json_get!(
+                        json_cast!(thumbnail, as_object),
+                        "url",
+                        as_str
+                    ))?)
+                } else {
+                    None
+                }
+            }
+            (Some(_), Some(thumnail)) => Some(Url::from_str(json_cast!(thumnail, as_str))?),
+        };
+
+        let url = {
+            let smug_url: Url = json_get!(entry, "webpage_url", as_str).parse()?;
+            // TODO(@bpeetz): We should probably add this? <2025-06-14>
+            // if '#__youtubedl_smuggle' not in smug_url:
+            //     return smug_url, default
+            // url, _, sdata = smug_url.rpartition('#')
+            // jsond = urllib.parse.parse_qs(sdata)['__youtubedl_smuggle'][0]
+            // data = json.loads(jsond)
+            // return url, data
+
+            smug_url
+        };
+
+        let extractor_hash = ExtractorHash::from_info_json(entry);
+
+        let subscription_name = if let Some(sub) = sub {
+            Some(sub.name.clone())
+        } else if let Some(uploader) = entry.get("uploader").map(|val| json_cast!(val, as_str)) {
+            if entry
+                .get("webpage_url_domain")
+                .map(|val| json_cast!(val, as_str))
+                == Some("youtube.com")
+            {
+                Some(format!("{uploader} - Videos"))
+            } else {
+                Some(uploader.to_owned())
+            }
+        } else {
+            None
+        };
+
+        let video = Video {
+            description: entry
+                .get("description")
+                .map(|val| json_cast!(val, as_str).to_owned()),
+            duration: MaybeDuration::from_maybe_secs_f64(
+                entry.get("duration").map(|val| json_cast!(val, as_f64)),
+            ),
+            extractor_hash,
+            last_status_change: TimeStamp::from_now(),
+            parent_subscription_name: subscription_name,
+            priority: Priority::default(),
+            publish_date: publish_date.map(TimeStamp::from_secs),
+            status: VideoStatus::Pick,
+            thumbnail_url,
+            title: json_get!(entry, "title", as_str).to_owned(),
+            url,
+            watch_progress: Duration::default(),
+            playback_speed: None,
+            subtitle_langs: None,
+        };
+        Ok(video)
+    }
+}
+
+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;
+            while let Some(entry) = dir.next_entry().await? {
+                let size = match entry.metadata().await? {
+                    data if data.is_dir() => {
+                        let path = entry.path();
+                        let read_dir = fs::read_dir(path).await?;
+
+                        dir_size(read_dir).await?.as_u64()
+                    }
+                    data => data.len(),
+                };
+                acc += size;
+            }
+            Ok(Bytes::new(acc))
+        }
+        .boxed()
+    }
+
+    let read_dir_result = match fs::read_dir(&app.config.paths.download_dir).await {
+        Ok(ok) => ok,
+        Err(err) => match err.kind() {
+            io::ErrorKind::NotFound => {
+                unreachable!("The download dir should always be created in the config finalizers.");
+            }
+            err => Err(io::Error::from(err)).with_context(|| {
+                format!(
+                    "Failed to get dir size of download dir at: '{}'",
+                    &app.config.paths.download_dir.display()
+                )
+            })?,
+        },
+    };
+
+    dir_size(read_dir_result).await
+}