about summary refs log tree commit diff stats
path: root/src/select
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-10-14 14:56:29 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-10-14 14:56:29 +0200
commit6c9286857ef8b314962b67f4a16a66e8c35531bc (patch)
tree9ced4485ec38b39f82cba258c06321a21c40000a /src/select
parentbuild(Cargo.toml): Add further lints (diff)
downloadyt-6c9286857ef8b314962b67f4a16a66e8c35531bc.tar.gz
yt-6c9286857ef8b314962b67f4a16a66e8c35531bc.zip
refactor(treewide): Combine the separate crates in one workspace
Diffstat (limited to 'src/select')
-rw-r--r--src/select/cmds.rs147
-rw-r--r--src/select/mod.rs173
-rw-r--r--src/select/selection_file/duration.rs111
-rw-r--r--src/select/selection_file/help.str12
-rw-r--r--src/select/selection_file/help.str.license9
-rw-r--r--src/select/selection_file/mod.rs34
6 files changed, 0 insertions, 486 deletions
diff --git a/src/select/cmds.rs b/src/select/cmds.rs
deleted file mode 100644
index 6e71607..0000000
--- a/src/select/cmds.rs
+++ /dev/null
@@ -1,147 +0,0 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 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::App,
-    cli::{SelectCommand, SharedSelectionCommandArgs},
-    download::download_options::download_opts,
-    storage::video_database::{
-        self,
-        getters::get_video_by_hash,
-        setters::{add_video, set_video_options, set_video_status},
-        VideoOptions, VideoStatus,
-    },
-    update::video_entry_to_video,
-    videos::display::format_video::FormatVideo,
-};
-
-use anyhow::{bail, Context, Result};
-use futures::future::join_all;
-use yt_dlp::wrapper::info_json::InfoType;
-
-pub async fn handle_select_cmd(
-    app: &App,
-    cmd: SelectCommand,
-    line_number: Option<i64>,
-) -> Result<()> {
-    match cmd {
-        SelectCommand::Pick { shared } => {
-            handle_status_change(app, shared, line_number, VideoStatus::Pick).await?;
-        }
-        SelectCommand::Drop { shared } => {
-            handle_status_change(app, shared, line_number, VideoStatus::Drop).await?;
-        }
-        SelectCommand::Watched { shared } => {
-            handle_status_change(app, shared, line_number, VideoStatus::Watched).await?;
-        }
-        SelectCommand::Add { urls } => {
-            for url in urls {
-                let opts = download_opts(
-                    &app,
-                    video_database::YtDlpOptions {
-                        subtitle_langs: "".to_owned(),
-                    },
-                );
-                let entry = yt_dlp::extract_info(&opts, &url, false, true)
-                    .await
-                    .with_context(|| format!("Failed to fetch entry for url: '{}'", url))?;
-
-                async fn add_entry(
-                    app: &App,
-                    entry: yt_dlp::wrapper::info_json::InfoJson,
-                ) -> Result<()> {
-                    let video = video_entry_to_video(entry, None)?;
-                    println!(
-                        "{}",
-                        (&video.to_formatted_video(app).await?.colorize()).to_line_display()
-                    );
-                    add_video(app, video).await?;
-
-                    Ok(())
-                }
-
-                match entry._type {
-                    Some(InfoType::Video) => {
-                        add_entry(&app, entry).await?;
-                    }
-                    Some(InfoType::Playlist) => {
-                        if let Some(mut entries) = entry.entries {
-                            if !entries.is_empty() {
-                                // Pre-warm the cache
-                                add_entry(app, entries.remove(0)).await?;
-
-                                let futures: Vec<_> = entries
-                                    .into_iter()
-                                    .map(|entry| add_entry(&app, entry))
-                                    .collect();
-
-                                join_all(futures).await.into_iter().collect::<Result<_>>()?;
-                            }
-                        } else {
-                            bail!("Your playlist does not seem to have any entries!")
-                        }
-                    }
-                    other => bail!(
-                        "Your URL should point to a video or a playlist, but points to a '{:#?}'",
-                        other
-                    ),
-                }
-            }
-        }
-        SelectCommand::Watch { shared } => {
-            let hash = shared.hash.clone().realize(app).await?;
-
-            let video = get_video_by_hash(app, &hash).await?;
-            if video.cache_path.is_some() {
-                handle_status_change(app, shared, line_number, VideoStatus::Cached).await?;
-            } else {
-                handle_status_change(app, shared, line_number, VideoStatus::Watch).await?;
-            }
-        }
-
-        SelectCommand::Url { shared } => {
-            let mut firefox = std::process::Command::new("firefox");
-            firefox.args(["-P", "timesinks.youtube"]);
-            firefox.arg(shared.url.as_str());
-            let _handle = firefox.spawn().context("Failed to run firefox")?;
-        }
-        SelectCommand::File { .. } => unreachable!("This should have been filtered out"),
-    }
-    Ok(())
-}
-
-async fn handle_status_change(
-    app: &App,
-    shared: SharedSelectionCommandArgs,
-    line_number: Option<i64>,
-    new_status: VideoStatus,
-) -> Result<()> {
-    let hash = shared.hash.realize(app).await?;
-    let video_options = VideoOptions::new(
-        shared
-            .subtitle_langs
-            .unwrap_or(app.config.select.subtitle_langs.clone()),
-        shared.speed.unwrap_or(app.config.select.playback_speed),
-    );
-    let priority = compute_priority(line_number, shared.priority);
-
-    set_video_status(app, &hash, new_status, priority).await?;
-    set_video_options(app, &hash, &video_options).await?;
-
-    Ok(())
-}
-
-fn compute_priority(line_number: Option<i64>, priority: Option<i64>) -> Option<i64> {
-    if let Some(pri) = priority {
-        Some(pri)
-    } else {
-        line_number
-    }
-}
diff --git a/src/select/mod.rs b/src/select/mod.rs
deleted file mode 100644
index ca7a203..0000000
--- a/src/select/mod.rs
+++ /dev/null
@@ -1,173 +0,0 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 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::{
-    env::{self},
-    fs,
-    io::{BufRead, Write},
-    io::{BufReader, BufWriter},
-};
-
-use crate::{
-    app::App,
-    cli::CliArgs,
-    constants::HELP_STR,
-    storage::video_database::{getters::get_videos, VideoStatus},
-    videos::display::format_video::FormatVideo,
-};
-
-use anyhow::{bail, Context, Result};
-use clap::Parser;
-use cmds::handle_select_cmd;
-use futures::future::join_all;
-use selection_file::process_line;
-use tempfile::Builder;
-use tokio::process::Command;
-
-pub mod cmds;
-pub mod selection_file;
-
-pub async fn select(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 = if done {
-            get_videos(app, VideoStatus::ALL, None).await?
-        } else {
-            get_videos(
-                app,
-                &[
-                    VideoStatus::Pick,
-                    //
-                    VideoStatus::Watch,
-                    VideoStatus::Cached,
-                ],
-                None,
-            )
-            .await?
-        };
-
-        // Warmup 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) = matching_videos.first() {
-            let _ = vid.to_formatted_video(app).await?;
-        }
-
-        let mut edit_file = BufWriter::new(&temp_file);
-
-        join_all(
-            matching_videos
-                .into_iter()
-                .map(|vid| async { vid.to_formatted_video_owned(app).await })
-                .collect::<Vec<_>>(),
-        )
-        .await
-        .into_iter()
-        .try_for_each(|line| -> Result<()> {
-            let formatted_line = (&line?).to_select_file_display();
-
-            edit_file
-                .write_all(formatted_line.as_bytes())
-                .expect("This write should not fail");
-
-            Ok(())
-        })?;
-
-        edit_file.write_all(HELP_STR.as_bytes())?;
-        edit_file.flush().context("Failed to flush edit file")?;
-    };
-
-    {
-        let editor = env::var("EDITOR").unwrap_or("nvim".to_owned());
-
-        let mut nvim = Command::new(editor);
-        nvim.arg(temp_file.path());
-        let status = nvim.status().await.context("Falied to run nvim")?;
-        if !status.success() {
-            bail!("nvim exited with error status: {}", status)
-        }
-    }
-
-    let read_file = temp_file.reopen()?;
-    fs::copy(temp_file.path(), &app.config.paths.last_selection_path)
-        .context("Failed to persist selection file")?;
-
-    let reader = BufReader::new(&read_file);
-
-    let mut line_number = 0;
-    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(|val| val.as_str()));
-
-            let args = CliArgs::parse_from(arg_line);
-
-            let cmd = if let crate::cli::Command::Select { cmd } =
-                args.command.expect("This will be some")
-            {
-                cmd
-            } else {
-                unreachable!("This is checked in the `filter_line` function")
-            };
-
-            handle_select_cmd(
-                app,
-                cmd.expect("This value should always be some here"),
-                Some(line_number),
-            )
-            .await?
-        }
-    }
-
-    Ok(())
-}
-
-// // 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 with out the extra exec <2024-08-20>
-// async fn get_help() -> Result<String> {
-//     let binary_name = current_exe()?;
-//     let cmd = Command::new(binary_name)
-//         .args(&["select", "--help"])
-//         .output()
-//         .await?;
-//
-//     assert_eq!(cmd.status.code(), Some(0));
-//
-//     let output = String::from_utf8(cmd.stdout).expect("Our help output was not utf8?");
-//
-//     let out = output
-//         .lines()
-//         .map(|line| format!("# {}\n", line))
-//         .collect::<String>();
-//
-//     debug!("Returning help: '{}'", &out);
-//
-//     Ok(out)
-// }
diff --git a/src/select/selection_file/duration.rs b/src/select/selection_file/duration.rs
deleted file mode 100644
index a38981c..0000000
--- a/src/select/selection_file/duration.rs
+++ /dev/null
@@ -1,111 +0,0 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 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};
-
-#[derive(Copy, Clone, Debug)]
-pub struct Duration {
-    time: u32,
-}
-
-impl FromStr for Duration {
-    type Err = anyhow::Error;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        fn parse_num(str: &str, suffix: char) -> Result<u32> {
-            str.strip_suffix(suffix)
-                .with_context(|| {
-                    format!("Failed to strip suffix '{}' of number: '{}'", suffix, str)
-                })?
-                .parse::<u32>()
-                .with_context(|| format!("Failed to parse '{}'", suffix))
-        }
-
-        if s == "[No Duration]" {
-            return Ok(Self { time: 0 });
-        }
-
-        let buf: Vec<_> = s.split(' ').collect();
-
-        let hours;
-        let minutes;
-        let seconds;
-
-        assert_eq!(buf.len(), 2, "Other lengths should not happen");
-
-        if buf[0].ends_with('h') {
-            hours = parse_num(buf[0], 'h')?;
-            minutes = parse_num(buf[1], 'm')?;
-            seconds = 0;
-        } else if buf[0].ends_with('m') {
-            hours = 0;
-            minutes = parse_num(buf[0], 'm')?;
-            seconds = parse_num(buf[1], 's')?;
-        } else {
-            unreachable!(
-                "The first part always ends with 'h' or 'm', but was: {:#?}",
-                buf
-            )
-        }
-
-        Ok(Self {
-            time: (hours * 60 * 60) + (minutes * 60) + seconds,
-        })
-    }
-}
-
-impl From<Option<f64>> for Duration {
-    fn from(value: Option<f64>) -> Self {
-        Self {
-            time: value.unwrap_or(0.0).ceil() as u32,
-        }
-    }
-}
-
-impl std::fmt::Display for Duration {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
-        const SECOND: u32 = 1;
-        const MINUTE: u32 = 60 * SECOND;
-        const HOUR: u32 = 60 * MINUTE;
-
-        let base_hour = self.time - (self.time % HOUR);
-        let base_min = (self.time % HOUR) - ((self.time % HOUR) % MINUTE);
-        let base_sec = (self.time % HOUR) % MINUTE;
-
-        let h = base_hour / HOUR;
-        let m = base_min / MINUTE;
-        let s = base_sec / SECOND;
-
-        if self.time == 0 {
-            write!(f, "[No Duration]")
-        } else if h > 0 {
-            write!(f, "{h}h {m}m")
-        } else {
-            write!(f, "{m}m {s}s")
-        }
-    }
-}
-#[cfg(test)]
-mod test {
-    use super::Duration;
-
-    #[test]
-    fn test_display_duration_1h() {
-        let dur = Duration { time: 60 * 60 };
-        assert_eq!("1h 0m".to_owned(), dur.to_string());
-    }
-    #[test]
-    fn test_display_duration_30min() {
-        let dur = Duration { time: 60 * 30 };
-        assert_eq!("30m 0s".to_owned(), dur.to_string());
-    }
-}
diff --git a/src/select/selection_file/help.str b/src/select/selection_file/help.str
deleted file mode 100644
index eb76ce5..0000000
--- a/src/select/selection_file/help.str
+++ /dev/null
@@ -1,12 +0,0 @@
-# Commands:
-#   w,  watch    [-p,-s,-l]   Mark the video given by the hash to be watched
-#   wd, watched  [-p,-s,-l]   Mark the video given by the hash as already watched
-#   d,  drop     [-p,-s,-l]   Mark the video given by the hash to be dropped
-#   u,  url      [-p,-s,-l]   Open the video URL in Firefox's `timesinks.youtube` profile
-#   p,  pick     [-p,-s,-l]   Reset the videos status to 'Pick'
-#   a,  add      URL          Add a video, defined by the URL
-#
-# See `yt select <cmd_name> --help` for more help.
-#
-# These lines can be re-ordered; they are executed from top to bottom.
-# vim: filetype=yts conceallevel=2 concealcursor=nc colorcolumn=
diff --git a/src/select/selection_file/help.str.license b/src/select/selection_file/help.str.license
deleted file mode 100644
index d4d410f..0000000
--- a/src/select/selection_file/help.str.license
+++ /dev/null
@@ -1,9 +0,0 @@
-yt - A fully featured command line YouTube client
-
-Copyright (C) 2024 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>.
diff --git a/src/select/selection_file/mod.rs b/src/select/selection_file/mod.rs
deleted file mode 100644
index 45809fa..0000000
--- a/src/select/selection_file/mod.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2024 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::{Context, Result};
-use trinitry::Trinitry;
-
-pub mod duration;
-
-pub 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 {
-        // pick 2195db "CouchRecherche? Gunnar und Han von STRG_F sind #mitfunkzuhause" "2020-04-01" "STRG_F - Live" "[1h 5m]" "https://www.youtube.com/watch?v=C8UXOaoMrXY"
-
-        let tri =
-            Trinitry::new(line).with_context(|| format!("Failed to parse line '{}'", line))?;
-
-        let mut vec = Vec::with_capacity(tri.arguments().len() + 1);
-        vec.push(tri.command().to_owned());
-        vec.extend(tri.arguments().to_vec());
-
-        Ok(Some(vec))
-    }
-}