aboutsummaryrefslogtreecommitdiffstats
path: root/src/select/selection_file
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-08-21 10:49:23 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-08-21 11:28:43 +0200
commit1debeb77f7986de1b659dcfdc442de6415e1d9f5 (patch)
tree4df3e7c3f6a2d1ec116e4088c5ace7f143a8b05f /src/select/selection_file
downloadyt-1debeb77f7986de1b659dcfdc442de6415e1d9f5.zip
chore: Initial Commit
This repository was migrated out of my nixos-config.
Diffstat (limited to 'src/select/selection_file')
-rw-r--r--src/select/selection_file/display.rs103
-rw-r--r--src/select/selection_file/duration.rs102
-rw-r--r--src/select/selection_file/help.str10
-rw-r--r--src/select/selection_file/help.str.license9
-rw-r--r--src/select/selection_file/mod.rs35
5 files changed, 259 insertions, 0 deletions
diff --git a/src/select/selection_file/display.rs b/src/select/selection_file/display.rs
new file mode 100644
index 0000000..12d128c
--- /dev/null
+++ b/src/select/selection_file/display.rs
@@ -0,0 +1,103 @@
+// 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::fmt::Write;
+
+use anyhow::Result;
+use chrono::DateTime;
+use log::debug;
+
+use crate::{
+ app::App,
+ select::selection_file::duration::Duration,
+ storage::video_database::{getters::get_video_opts, Video},
+};
+
+macro_rules! c {
+ ($color:expr, $format:expr) => {
+ format!("\x1b[{}m{}\x1b[0m", $color, $format)
+ };
+}
+
+impl Video {
+ pub async fn to_select_file_display(&self, app: &App) -> Result<String> {
+ let mut f = String::new();
+
+ let opts = get_video_opts(app, &self.extractor_hash)
+ .await?
+ .to_cli_flags();
+ let opts_white = if !opts.is_empty() { " " } else { "" };
+
+ let publish_date = if let Some(date) = self.publish_date {
+ DateTime::from_timestamp(date, 0)
+ .expect("This should not fail")
+ .format("%Y-%m-%d")
+ .to_string()
+ } else {
+ "[No release date]".to_owned()
+ };
+
+ let parent_subscription_name = if let Some(sub) = &self.parent_subscription_name {
+ sub.replace('"', "'")
+ } else {
+ "[No author]".to_owned()
+ };
+
+ debug!("Formatting video for selection file: {}", self.title);
+ write!(
+ f,
+ r#"{}{}{} {} "{}" "{}" "{}" "{}" "{}"{}"#,
+ self.status.as_command(),
+ opts_white,
+ opts,
+ self.extractor_hash.into_short_hash(app).await?,
+ self.title.replace(['"', '„', '”'], "'"),
+ publish_date,
+ parent_subscription_name,
+ Duration::from(self.duration),
+ self.url.as_str().replace('"', "\\\""),
+ "\n"
+ )?;
+
+ Ok(f)
+ }
+
+ pub fn to_color_display(&self) -> String {
+ let mut f = String::new();
+
+ let publish_date = if let Some(date) = self.publish_date {
+ DateTime::from_timestamp(date, 0)
+ .expect("This should not fail")
+ .format("%Y-%m-%d")
+ .to_string()
+ } else {
+ "[No release date]".to_owned()
+ };
+
+ let parent_subscription_name = if let Some(sub) = &self.parent_subscription_name {
+ sub.replace('"', "'")
+ } else {
+ "[No author]".to_owned()
+ };
+
+ write!(
+ f,
+ r#"{} {} {} {} {}"#,
+ c!("31;1", self.status.as_command()),
+ c!("32;1", self.title.replace(['"', '„', '”'], "'")),
+ c!("37;1", publish_date),
+ c!("34;1", parent_subscription_name),
+ c!("35;1", Duration::from(self.duration)),
+ )
+ .expect("This write should always work");
+
+ f
+ }
+}
diff --git a/src/select/selection_file/duration.rs b/src/select/selection_file/duration.rs
new file mode 100644
index 0000000..4224ead
--- /dev/null
+++ b/src/select/selection_file/duration.rs
@@ -0,0 +1,102 @@
+// 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)
+ .expect("it has a 'h' suffix")
+ .parse::<u32>()
+ .context("Failed to parse hours")
+ }
+
+ 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'")
+ }
+
+ 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
new file mode 100644
index 0000000..6e296f6
--- /dev/null
+++ b/src/select/selection_file/help.str
@@ -0,0 +1,10 @@
+# Commands:
+# w, watch [-p,-s,-l] Mark the video given by the hash to be watched
+# d, drop Mark the video given by the hash to be dropped
+# u, url Open the video URL in Firefox's `timesinks.youtube` profile
+# p, pick Reset the videos status to 'Pick'
+#
+# 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
new file mode 100644
index 0000000..d4d410f
--- /dev/null
+++ b/src/select/selection_file/help.str.license
@@ -0,0 +1,9 @@
+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
new file mode 100644
index 0000000..bdb0866
--- /dev/null
+++ b/src/select/selection_file/mod.rs
@@ -0,0 +1,35 @@
+// 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 display;
+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().into_iter());
+
+ Ok(Some(vec))
+ }
+}