about summary refs log tree commit diff stats
path: root/crates
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-07-24 15:52:06 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-07-24 15:52:06 +0200
commit5ccf61730945e98f36a3e8621b22cfd3be4ab4eb (patch)
tree795946879f3601c1e6c829d8a09f85bea4f99636 /crates
parentrefactor(crates/yt/db/insert::Commitable): Make `Debug` a dependency (diff)
downloadyt-5ccf61730945e98f36a3e8621b22cfd3be4ab4eb.zip
feat(crates/yt/commands/show): Also provide thumbnail and info screen
Diffstat (limited to 'crates')
-rw-r--r--crates/yt/Cargo.toml1
-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/description/implm.rs16
-rw-r--r--crates/yt/src/commands/description/mod.rs6
-rw-r--r--crates/yt/src/commands/mod.rs29
-rw-r--r--crates/yt/src/commands/show/implm/mod.rs100
7 files changed, 112 insertions, 61 deletions
diff --git a/crates/yt/Cargo.toml b/crates/yt/Cargo.toml
index 12456fe..95f8270 100644
--- a/crates/yt/Cargo.toml
+++ b/crates/yt/Cargo.toml
@@ -50,6 +50,7 @@ url.workspace = true
 uu_fmt.workspace = true
 xdg = "3.0.0"
 yt_dlp.workspace = true
+reqwest = "0.12.22"
 
 [[bin]]
 name = "yt"
diff --git a/crates/yt/src/commands/comments/implm/mod.rs b/crates/yt/src/commands/comments/implm/mod.rs
deleted file mode 100644
index 1c02718..0000000
--- a/crates/yt/src/commands/comments/implm/mod.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-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
deleted file mode 100644
index d87c75d..0000000
--- a/crates/yt/src/commands/comments/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-use clap::Parser;
-
-mod implm;
-
-#[derive(Parser, Debug)]
-pub(crate) struct CommentsCommand {}
diff --git a/crates/yt/src/commands/description/implm.rs b/crates/yt/src/commands/description/implm.rs
deleted file mode 100644
index 7c39b1c..0000000
--- a/crates/yt/src/commands/description/implm.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-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
deleted file mode 100644
index b5b2a10..0000000
--- a/crates/yt/src/commands/description/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-use clap::Parser;
-
-mod implm;
-
-#[derive(Parser, Debug)]
-pub(crate) struct DescriptionCommand {}
diff --git a/crates/yt/src/commands/mod.rs b/crates/yt/src/commands/mod.rs
index a6aa2af..ee8b29c 100644
--- a/crates/yt/src/commands/mod.rs
+++ b/crates/yt/src/commands/mod.rs
@@ -7,10 +7,10 @@ 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::ConfigCommand, database::DatabaseCommand, download::DownloadCommand,
+        playlist::PlaylistCommand, select::SelectCommand, show::ShowCommand, status::StatusCommand,
+        subscriptions::SubscriptionCommand, update::UpdateCommand, videos::VideosCommand,
+        watch::WatchCommand,
     },
     config::Config,
     storage::db::subscription::Subscriptions,
@@ -18,13 +18,12 @@ use crate::{
 
 pub(crate) mod implm;
 
-mod comments;
 mod config;
 mod database;
-mod description;
 mod download;
 mod playlist;
 mod select;
+mod show;
 mod status;
 mod subscriptions;
 mod update;
@@ -33,12 +32,6 @@ 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)]
@@ -52,12 +45,6 @@ pub(crate) enum Command {
         cmd: DatabaseCommand,
     },
 
-    /// Display the description of the currently playing video
-    Description {
-        #[command(flatten)]
-        cmd: DescriptionCommand,
-    },
-
     /// Download and cache URLs
     Download {
         #[command(flatten)]
@@ -76,6 +63,12 @@ pub(crate) enum Command {
         cmd: Option<SelectCommand>,
     },
 
+    /// Show things about the currently playing video.
+    Show {
+        #[command(subcommand)]
+        cmd: ShowCommand,
+    },
+
     /// Show, which videos have been selected to be watched (and their cache status)
     Status {
         #[command(flatten)]
diff --git a/crates/yt/src/commands/show/implm/mod.rs b/crates/yt/src/commands/show/implm/mod.rs
new file mode 100644
index 0000000..158a25b
--- /dev/null
+++ b/crates/yt/src/commands/show/implm/mod.rs
@@ -0,0 +1,100 @@
+use std::{
+    fs::{self, OpenOptions},
+    io,
+    process::Command,
+};
+
+use crate::{
+    app::App,
+    commands::ShowCommand,
+    output::{display_fmt_and_less, display_less},
+    storage::db::video::Video,
+};
+
+use anyhow::{Context, Result, anyhow, bail};
+use tempfile::Builder;
+use tokio_util::bytes::Buf;
+
+impl ShowCommand {
+    pub(crate) async fn implm(&self, app: &App) -> Result<()> {
+        match self {
+            ShowCommand::Description {} => {
+                let description = Video::get_current_description(app).await?;
+
+                display_fmt_and_less(&description)?;
+            }
+            ShowCommand::Comments {} => {
+                let comments = Video::get_current_comments(app).await?;
+
+                display_less(comments.render(app.config.global.display_colors))?;
+            }
+            ShowCommand::Thumbnail {} => {
+                let video = Video::currently_focused(app).await?.ok_or(anyhow!(
+                    "You need to have a current video to display its info"
+                ))?;
+
+                if let Some(url) = video.thumbnail_url {
+                    let response = reqwest::get(url.clone())
+                        .await
+                        .with_context(|| format!("Failed to download thumbnail from url: {url}"))?;
+                    let response = response
+                        .error_for_status()
+                        .context("Failed to download thumbnail")?;
+
+                    let (tmp_path, mut tmp) = {
+                        let file = Builder::new().prefix("yt-thumbnail-download").tempfile()?;
+                        let (_, path) = file.keep()?;
+                        let new_file = OpenOptions::new()
+                            .write(true)
+                            .read(false)
+                            .create(false)
+                            .truncate(true)
+                            .open(&path)?;
+
+                        (path, new_file)
+                    };
+
+                    let mut content = response.bytes().await?.reader();
+                    io::copy(&mut content, &mut tmp)?;
+
+                    let status = Command::new(app.config.commands.image_show.first())
+                        .args(app.config.commands.image_show.tail())
+                        .arg(tmp_path.as_os_str())
+                        .status()
+                        .context("Failed to spawn image show command")?;
+
+                    if !status.success() {
+                        bail!(
+                            "{:?} failed with status: {}",
+                            &app.config.commands.image_show.join(" "),
+                            status
+                        );
+                    }
+
+                    fs::remove_file(&tmp_path).with_context(|| {
+                        format!(
+                            "Failed to cleanup downloaded thumbnail image at: {}",
+                            tmp_path.display()
+                        )
+                    })?;
+                } else {
+                    eprintln!("Current video does not have a thumbnail.");
+                }
+            }
+            ShowCommand::Info {} => {
+                let video = Video::currently_focused(app).await?.ok_or(anyhow!(
+                    "You need to have a current video to display its info"
+                ))?;
+
+                display_less(
+                    video
+                        .to_info_display(app, None)
+                        .await
+                        .context("Failed to format video")?,
+                )?;
+            }
+        }
+
+        Ok(())
+    }
+}