aboutsummaryrefslogtreecommitdiffstats
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.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))
- }
-}