diff options
Diffstat (limited to 'sys/nixpkgs/pkgs/yt/src/lib.rs')
-rw-r--r-- | sys/nixpkgs/pkgs/yt/src/lib.rs | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/sys/nixpkgs/pkgs/yt/src/lib.rs b/sys/nixpkgs/pkgs/yt/src/lib.rs new file mode 100644 index 00000000..a08b32db --- /dev/null +++ b/sys/nixpkgs/pkgs/yt/src/lib.rs @@ -0,0 +1,140 @@ +use anyhow::{bail, Context}; +use serde::Deserialize; +use url::Url; + +pub mod constants; +pub mod downloader; + +#[derive(Deserialize)] +pub struct YtccListData { + pub url: String, + pub title: String, + pub description: String, + pub publish_date: String, + pub watch_date: Option<String>, + pub duration: String, + pub thumbnail_url: String, + pub extractor_hash: String, + pub id: u32, + pub playlists: Vec<YtccPlaylistData>, +} +#[derive(Deserialize)] +pub struct YtccPlaylistData { + pub name: String, + pub url: String, + pub reverse: bool, +} + +pub enum LineCommand { + Pick, + Drop, + Watch, +} + +impl std::str::FromStr for LineCommand { + type Err = anyhow::Error; + fn from_str(v: &str) -> Result<Self, <Self as std::str::FromStr>::Err> { + match v { + "pick" | "p" => Ok(Self::Pick), + "drop" | "d" => Ok(Self::Drop), + "watch" | "w" => Ok(Self::Watch), + other => bail!("'{}' is not a recognized command!", other), + } + } +} + +pub struct Line { + pub cmd: LineCommand, + pub id: u32, + pub url: Url, +} + +/// We expect that each line is correctly formatted, and simply use default ones if they are not +impl From<&str> for Line { + fn from(v: &str) -> Self { + let buf: Vec<_> = v.split_whitespace().collect(); + let url: Url = Url::parse( + buf.last() + .expect("This should always exists") + .trim_matches('"'), + ) + .expect("This parsing should work,as the url is generated"); + + Line { + cmd: buf + .get(0) + .unwrap_or(&"pick") + .parse() + .unwrap_or(LineCommand::Pick), + id: buf.get(1).unwrap_or(&"0").parse().unwrap_or(0), + url, + } + } +} + +pub struct Duration { + time: u32, +} + +impl From<&str> for Duration { + fn from(v: &str) -> Self { + let buf: Vec<_> = v.split(':').take(2).collect(); + Self { + time: (buf[0] + .parse::<u32>() + .expect("Should be a number") + * 60) + + buf[1] + .parse::<u32>() + .expect("Should be a number"), + } + } +} + +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) - (((self.time % HOUR) % MINUTE) % SECOND); + + 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 crate::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()); + } +} + +pub fn ytcc_drop(id: u32) -> anyhow::Result<()> { + let mut ytcc = std::process::Command::new("ytcc"); + ytcc.args(["mark", &format!("{}", id)]); + if !ytcc.status().context("Failed to run ytcc")?.success() { + bail!("`ytcc mark {}` failed to execute", id) + } + Ok(()) +} |