diff options
Diffstat (limited to 'src/select/mod.rs')
-rw-r--r-- | src/select/mod.rs | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/src/select/mod.rs b/src/select/mod.rs new file mode 100644 index 0000000..6774ce6 --- /dev/null +++ b/src/select/mod.rs @@ -0,0 +1,184 @@ +// 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::{last_select, HELP_STR}, + storage::video_database::{getters::get_videos, VideoStatus}, +}; + +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) -> Result<()> { + let matching_videos = if done { + get_videos( + app, + &[ + VideoStatus::Pick, + // + VideoStatus::Watch, + VideoStatus::Cached, + VideoStatus::Watched, + // + VideoStatus::Drop, + VideoStatus::Dropped, + ], + 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.get(0) { + let _ = vid.to_select_file_display(app).await?; + } + + let lines: Vec<String> = join_all( + matching_videos + .iter() + .map(|vid| async { vid.to_select_file_display(app).await }) + .collect::<Vec<_>>(), + ) + .await + .into_iter() + .collect::<Result<Vec<String>>>()?; + + let temp_file = Builder::new() + .prefix("yt_video_select-") + .suffix(".yts") + .rand_bytes(6) + .tempfile() + .context("Failed to get tempfile")?; + + { + let mut edit_file = BufWriter::new(&temp_file); + + lines.iter().for_each(|line| { + edit_file + .write_all(line.as_bytes()) + .expect("This write should not fail"); + }); + + // edit_file.write_all(get_help().await?.as_bytes())?; + 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(), + last_select().context("Failed to get the persistent selection file 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 +// // jet 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) +// } |