diff options
Diffstat (limited to 'yt/src/storage/subscriptions.rs')
-rw-r--r-- | yt/src/storage/subscriptions.rs | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/yt/src/storage/subscriptions.rs b/yt/src/storage/subscriptions.rs new file mode 100644 index 0000000..22edd08 --- /dev/null +++ b/yt/src/storage/subscriptions.rs @@ -0,0 +1,140 @@ +// 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>. + +//! Handle subscriptions + +use std::collections::HashMap; + +use anyhow::Result; +use log::debug; +use serde_json::{json, Value}; +use sqlx::query; +use url::Url; +use yt_dlp::wrapper::info_json::InfoType; + +use crate::app::App; + +#[derive(Clone, Debug)] +pub struct Subscription { + /// The human readable name of this subscription + pub name: String, + + /// The URL this subscription subscribes to + pub url: Url, +} + +impl Subscription { + pub fn new(name: String, url: Url) -> Self { + Self { name, url } + } +} + +/// Check whether an URL could be used as a subscription URL +pub async fn check_url(url: &Url) -> Result<bool> { + let yt_opts = match json!( { + "playliststart": 1, + "playlistend": 10, + "noplaylist": false, + "extract_flat": "in_playlist", + }) { + Value::Object(map) => map, + _ => unreachable!("This is hardcoded"), + }; + + let info = yt_dlp::extract_info(&yt_opts, url, false, false).await?; + + debug!("{:#?}", info); + + Ok(info._type == Some(InfoType::Playlist)) +} + +#[derive(Default)] +pub struct Subscriptions(pub(crate) HashMap<String, Subscription>); + +pub async fn remove_all_subscriptions(app: &App) -> Result<()> { + query!( + " + DELETE FROM subscriptions; + ", + ) + .execute(&app.database) + .await?; + + Ok(()) +} + +/// Get a list of subscriptions +pub async fn get_subscriptions(app: &App) -> Result<Subscriptions> { + let raw_subs = query!( + " + SELECT * + FROM subscriptions; + " + ) + .fetch_all(&app.database) + .await?; + + let subscriptions: HashMap<String, Subscription> = raw_subs + .into_iter() + .map(|sub| { + ( + sub.name.clone(), + Subscription::new( + sub.name, + Url::parse(&sub.url).expect("This should be valid"), + ), + ) + }) + .collect(); + + Ok(Subscriptions(subscriptions)) +} + +pub async fn add_subscription(app: &App, sub: &Subscription) -> Result<()> { + let url = sub.url.to_string(); + + query!( + " + INSERT INTO subscriptions ( + name, + url + ) VALUES (?, ?); + ", + sub.name, + url + ) + .execute(&app.database) + .await?; + + println!("Subscribed to '{}' at '{}'", sub.name, sub.url); + Ok(()) +} + +pub async fn remove_subscription(app: &App, sub: &Subscription) -> Result<()> { + let output = query!( + " + DELETE FROM subscriptions + WHERE name = ? + ", + sub.name, + ) + .execute(&app.database) + .await?; + + assert_eq!( + output.rows_affected(), + 1, + "The remove subscriptino query did effect more (or less) than one row. This is a bug." + ); + + println!("Unsubscribed from '{}' at '{}'", sub.name, sub.url); + + Ok(()) +} |