// 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(())
}