aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cli.rs4
-rw-r--r--src/select/cmds.rs60
-rw-r--r--src/select/selection_file/display.rs5
-rw-r--r--src/select/selection_file/help.str1
-rw-r--r--src/update/mod.rs66
5 files changed, 109 insertions, 27 deletions
diff --git a/src/cli.rs b/src/cli.rs
index 51809c0..d19586e 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -238,6 +238,10 @@ pub enum SelectCommand {
use_last_selection: bool,
},
+ /// Add a video to the database
+ #[command(visible_alias = "a")]
+ Add { urls: Vec<Url> },
+
/// Mark the video given by the hash to be watched
#[command(visible_alias = "w")]
Watch {
diff --git a/src/select/cmds.rs b/src/select/cmds.rs
index 3a7a800..b45cc48 100644
--- a/src/select/cmds.rs
+++ b/src/select/cmds.rs
@@ -11,14 +11,19 @@
use crate::{
app::App,
cli::{SelectCommand, SharedSelectionCommandArgs},
+ download::download_options::download_opts,
storage::video_database::{
+ self,
getters::get_video_by_hash,
- setters::{set_video_options, set_video_status},
+ setters::{add_video, set_video_options, set_video_status},
VideoOptions, VideoStatus,
},
+ update::video_entry_to_video,
};
-use anyhow::{Context, Result};
+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,
@@ -35,6 +40,57 @@ pub async fn handle_select_cmd(
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_color_display(app).await?);
+ 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?;
diff --git a/src/select/selection_file/display.rs b/src/select/selection_file/display.rs
index 8ff6a15..0714015 100644
--- a/src/select/selection_file/display.rs
+++ b/src/select/selection_file/display.rs
@@ -10,7 +10,7 @@
use std::fmt::Write;
-use anyhow::Result;
+use anyhow::{Context, Result};
use chrono::DateTime;
use log::debug;
@@ -31,7 +31,8 @@ impl Video {
let mut f = String::new();
let opts = get_video_opts(app, &self.extractor_hash)
- .await?
+ .await
+ .with_context(|| format!("Failed to get video options for video: '{}'", self.title))?
.to_cli_flags(app);
let opts_white = if !opts.is_empty() { " " } else { "" };
diff --git a/src/select/selection_file/help.str b/src/select/selection_file/help.str
index f3ad2f2..eb76ce5 100644
--- a/src/select/selection_file/help.str
+++ b/src/select/selection_file/help.str
@@ -4,6 +4,7 @@
# 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.
#
diff --git a/src/update/mod.rs b/src/update/mod.rs
index ce0a7e5..ce3a7f9 100644
--- a/src/update/mod.rs
+++ b/src/update/mod.rs
@@ -120,12 +120,7 @@ pub async fn update(
Ok(())
}
-async fn process_subscription(
- app: &App,
- sub: &Subscription,
- entry: InfoJson,
- hashes: &[blake3::Hash],
-) -> Result<()> {
+pub fn video_entry_to_video(entry: InfoJson, sub: Option<&Subscription>) -> Result<Video> {
macro_rules! unwrap_option {
($option:expr) => {
match $option {
@@ -197,26 +192,51 @@ async fn process_subscription(
let extractor_hash = blake3::hash(unwrap_option!(entry.id).as_bytes());
- if hashes.contains(&extractor_hash) {
+ let subscription_name = if let Some(sub) = sub {
+ Some(sub.name.clone())
+ } else {
+ if let Some(uploader) = entry.uploader {
+ if entry.webpage_url_domain == Some("youtube.com".to_owned()) {
+ Some(format!("{} - Videos", uploader))
+ } else {
+ Some(uploader.clone())
+ }
+ } else {
+ None
+ }
+ };
+
+ let video = Video {
+ cache_path: None,
+ description: entry.description.clone(),
+ duration: entry.duration,
+ extractor_hash: ExtractorHash::from_hash(extractor_hash),
+ last_status_change: Utc::now().timestamp(),
+ parent_subscription_name: subscription_name,
+ priority: 0,
+ publish_date,
+ status: VideoStatus::Pick,
+ status_change: false,
+ thumbnail_url,
+ title: unwrap_option!(entry.title.clone()),
+ url,
+ };
+ Ok(video)
+}
+
+async fn process_subscription(
+ app: &App,
+ sub: &Subscription,
+ entry: InfoJson,
+ hashes: &[blake3::Hash],
+) -> Result<()> {
+ let video =
+ video_entry_to_video(entry, Some(sub)).context("Failed to parse search entry as Video")?;
+
+ if hashes.contains(&video.extractor_hash.hash()) {
// We already stored the video information
unreachable!("The python update script should have never provided us a duplicated video");
} else {
- let video = Video {
- cache_path: None,
- description: entry.description.clone(),
- duration: entry.duration,
- extractor_hash: ExtractorHash::from_hash(extractor_hash),
- last_status_change: Utc::now().timestamp(),
- parent_subscription_name: Some(sub.name.clone()),
- priority: 0,
- publish_date,
- status: VideoStatus::Pick,
- status_change: false,
- thumbnail_url,
- title: unwrap_option!(entry.title.clone()),
- url,
- };
-
println!("{}", video.to_color_display(app).await?);
add_video(app, video).await?;
Ok(())