about summary refs log blame commit diff stats
path: root/src/storage/subscriptions.rs
blob: 22edd0865ac002bc22b452edd2de09249e15551c (plain) (tree)










































































































































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