diff options
author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-05-20 16:10:21 +0200 |
---|---|---|
committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-05-20 16:14:26 +0200 |
commit | 368cb6b0d25db2ae23be42ad51584de059997e51 (patch) | |
tree | 3282e45d3ebced63c8498a47e83a255c35de620b /pkgs/sources/yt/src/bin | |
parent | refactor(hm): Rename to `modules/home` (diff) | |
download | nixos-config-368cb6b0d25db2ae23be42ad51584de059997e51.zip |
refactor(sys): Modularize and move to `modules/system` or `pkgs`
Diffstat (limited to 'pkgs/sources/yt/src/bin')
-rw-r--r-- | pkgs/sources/yt/src/bin/yt/main.rs | 91 | ||||
-rw-r--r-- | pkgs/sources/yt/src/bin/ytc/args.rs | 26 | ||||
-rw-r--r-- | pkgs/sources/yt/src/bin/ytc/main.rs | 77 | ||||
-rw-r--r-- | pkgs/sources/yt/src/bin/yts/args.rs | 41 | ||||
-rw-r--r-- | pkgs/sources/yt/src/bin/yts/main.rs | 91 |
5 files changed, 326 insertions, 0 deletions
diff --git a/pkgs/sources/yt/src/bin/yt/main.rs b/pkgs/sources/yt/src/bin/yt/main.rs new file mode 100644 index 00000000..37348834 --- /dev/null +++ b/pkgs/sources/yt/src/bin/yt/main.rs @@ -0,0 +1,91 @@ +use anyhow::{bail, Context, Result}; +use std::{ + env, fs, + io::{BufRead, BufReader, BufWriter, Write}, + process::Command as StdCmd, +}; +use tempfile::Builder; +use yt::{ + constants::{last_select, HELP_STR}, + downloader::Downloader, + filter_line, YtccListData, +}; + +fn main() -> Result<()> { + cli_log::init_cli_log!(); + + let json_map = { + let mut ytcc = StdCmd::new("ytcc"); + ytcc.args([ + "--output", + "json", + "list", + "--order-by", + "publish_date", + "desc", + ]); + + serde_json::from_slice::<Vec<YtccListData>>( + &ytcc.output().context("Failed to json from ytcc")?.stdout, + ) + .context("Failed to deserialize json output")? + }; + + 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); + + json_map.iter().for_each(|line| { + let line = line.to_string(); + edit_file + .write_all(line.as_bytes()) + .expect("This write should not fail"); + }); + + edit_file.write_all(HELP_STR.as_bytes())?; + edit_file.flush().context("Failed to flush edit file")?; + + let mut nvim = StdCmd::new("nvim"); + nvim.arg(temp_file.path()); + let status = nvim.status().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 mut watching = Vec::new(); + let reader = BufReader::new(&read_file); + for line in reader.lines() { + let line = line.context("Failed to read line")?; + + if let Some(downloadable) = + filter_line(&line).with_context(|| format!("Failed to process line: '{}'", line))? + { + watching.push(downloadable); + } + } + + if watching.is_empty() { + return Ok(()); + } + + let downloader = Downloader::new(watching).context("Failed to construct downloader")?; + downloader + .consume() + .context("Failed to consume downloader")?; + + Ok(()) +} diff --git a/pkgs/sources/yt/src/bin/ytc/args.rs b/pkgs/sources/yt/src/bin/ytc/args.rs new file mode 100644 index 00000000..8b2d6a61 --- /dev/null +++ b/pkgs/sources/yt/src/bin/ytc/args.rs @@ -0,0 +1,26 @@ +use clap::{Parser, Subcommand}; +/// A helper for downloading and playing youtube videos +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +pub struct Args { + #[command(subcommand)] + /// The subcommand to execute + pub subcommand: Command, +} +#[derive(Subcommand, Debug)] +pub enum Command { + #[clap(value_parser)] + /// Work based of ytcc ids + Id { + #[clap(value_parser)] + /// A list of ids to play + ids: Vec<u32>, + }, + #[clap(value_parser)] + /// Work based of raw youtube urls + Url { + #[clap(value_parser)] + /// A list of urls to play + urls: Vec<String>, + }, +} diff --git a/pkgs/sources/yt/src/bin/ytc/main.rs b/pkgs/sources/yt/src/bin/ytc/main.rs new file mode 100644 index 00000000..b38157df --- /dev/null +++ b/pkgs/sources/yt/src/bin/ytc/main.rs @@ -0,0 +1,77 @@ +use std::{env, process::Command as StdCmd}; + +use anyhow::{bail, Context, Result}; +use clap::Parser; +use log::debug; +use url::Url; +use yt::{ + downloader::{Downloadable, Downloader}, + YtccListData, +}; + +use crate::args::{Args, Command}; + +mod args; + +fn main() -> Result<()> { + let args = Args::parse(); + cli_log::init_cli_log!(); + + let playspec: Vec<Downloadable> = match args.subcommand { + Command::Id { ids } => { + let mut output = Vec::with_capacity(ids.len()); + for id in ids { + debug!("Adding {}", id); + let mut ytcc = StdCmd::new("ytcc"); + ytcc.args([ + "--output", + "json", + "list", + "--watched", + "--unwatched", + "--attributes", + "url", + "--ids", + id.to_string().as_str(), + ]); + let json = serde_json::from_slice::<Vec<YtccListData>>( + &ytcc.output().context("Failed to get url from id")?.stdout, + ) + .context("Failed to deserialize json output")?; + + if json.is_empty() { + bail!("Could not find a video with id: {}", id); + } + assert_eq!(json.len(), 1); + let json = json.first().expect("Has only one element"); + + debug!("Id resolved to: '{}'", &json.url); + + output.push(Downloadable { + url: Url::parse(&json.url)?, + id: Some(json.id), + }) + } + output + } + Command::Url { urls } => { + let mut output = Vec::with_capacity(urls.len()); + for url in urls { + output.push(Downloadable { + url: Url::parse(&url).context("Failed to parse url")?, + id: None, + }) + } + output + } + }; + + debug!("Initializing downloader"); + let downloader = Downloader::new(playspec)?; + + downloader + .consume() + .context("Failed to consume downloader")?; + + Ok(()) +} diff --git a/pkgs/sources/yt/src/bin/yts/args.rs b/pkgs/sources/yt/src/bin/yts/args.rs new file mode 100644 index 00000000..56989421 --- /dev/null +++ b/pkgs/sources/yt/src/bin/yts/args.rs @@ -0,0 +1,41 @@ +use clap::{Parser, Subcommand}; +/// A helper for selecting which videos to download from ytcc to ytc +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +pub struct Args { + #[command(subcommand)] + /// subcommand to execute + pub subcommand: Option<Command>, +} + +#[derive(Subcommand, Debug)] +pub enum Command { + #[clap(value_parser)] + /// Which ordering to use + Order { + #[command(subcommand)] + command: OrderCommand, + }, +} + +#[derive(Subcommand, Debug)] +pub enum OrderCommand { + #[clap(value_parser)] + /// Order by date + #[group(required = true)] + Date { + #[arg(value_parser)] + /// Order descending + desc: bool, + #[clap(value_parser)] + /// Order ascending + asc: bool, + }, + #[clap(value_parser)] + /// Pass a raw SQL 'ORDER BY' value + Raw { + #[clap(value_parser)] + /// The raw value(s) to pass + value: Vec<String>, + }, +} diff --git a/pkgs/sources/yt/src/bin/yts/main.rs b/pkgs/sources/yt/src/bin/yts/main.rs new file mode 100644 index 00000000..7398db61 --- /dev/null +++ b/pkgs/sources/yt/src/bin/yts/main.rs @@ -0,0 +1,91 @@ +use anyhow::{bail, Context, Result}; +use clap::Parser; +use std::{ + env, + io::{BufRead, BufReader, Write}, + process::Command as StdCmd, +}; +use tempfile::NamedTempFile; +use yt::{constants::HELP_STR, filter_line, YtccListData}; + +use crate::args::{Args, Command, OrderCommand}; + +mod args; + +fn main() -> Result<()> { + let args = Args::parse(); + cli_log::init_cli_log!(); + + let ordering = match args.subcommand.unwrap_or(Command::Order { + command: OrderCommand::Date { + desc: true, + asc: false, + }, + }) { + Command::Order { command } => match command { + OrderCommand::Date { desc, asc } => { + if desc { + vec!["--order-by".into(), "publish_date".into(), "desc".into()] + } else if asc { + vec!["--order-by".into(), "publish_date".into(), "asc".into()] + } else { + vec!["--order-by".into(), "publish_date".into(), "desc".into()] + } + } + OrderCommand::Raw { value } => [vec!["--order-by".into()], value].concat(), + }, + }; + + let json_map = { + let mut ytcc = StdCmd::new("ytcc"); + ytcc.args(["--output", "json", "list"]); + ytcc.args(ordering); + + serde_json::from_slice::<Vec<YtccListData>>( + &ytcc.output().context("Failed to json from ytcc")?.stdout, + ) + .context("Failed to deserialize json output")? + }; + + let mut edit_file = NamedTempFile::new().context("Failed to get tempfile")?; + + json_map.iter().for_each(|line| { + let line = line.to_string(); + edit_file + .write_all(line.as_bytes()) + .expect("This write should not fail"); + }); + + write!(&edit_file, "{}", HELP_STR)?; + edit_file.flush().context("Failed to flush edit file")?; + + let read_file = edit_file.reopen()?; + + let mut nvim = StdCmd::new("nvim"); + nvim.arg(edit_file.path()); + + let status = nvim.status().context("Falied to run nvim")?; + if !status.success() { + bail!("Nvim exited with error status: {}", status) + } + + let mut watching = Vec::new(); + let reader = BufReader::new(&read_file); + for line in reader.lines() { + let line = line.context("Failed to read line")?; + + if let Some(downloadable) = + filter_line(&line).with_context(|| format!("Failed to process line: '{}'", line))? + { + watching.push(downloadable); + } + } + + let watching: String = watching + .iter() + .map(|d| d.to_string()) + .collect::<Vec<String>>() + .join("\n"); + println!("{}", &watching); + Ok(()) +} |