about summary refs log blame commit diff stats
path: root/src/main.rs
blob: 7852aa0ba70ff65ecdaf7eb970a018df0a3c752b (plain) (tree)
1
2
3
4
5
6
7
8
9
10








                                                                          
                                               




                                                                          
              




































                                                                     
                                                                              
                                                      




                                                          


                                              

                                                       










                                                                                  










                                                                                            
                                                                                    











                                                                 
                                             
                                                              






                                                              















































                                                                                                 
// 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::{collections::HashMap, fs, sync::Arc};

use anyhow::{bail, Context, Result};
use app::App;
use cache::invalidate;
use clap::Parser;
use cli::{CacheCommand, CheckCommand, SelectCommand, SubscriptionCommand};
use log::info;
use select::cmds::handle_select_cmd;
use tokio::{
    fs::File,
    io::{stdin, BufReader},
};
use url::Url;
use yt_dlp::wrapper::info_json::InfoJson;

use crate::{cli::Command, storage::subscriptions::get_subscriptions};

pub mod app;
pub mod cli;

pub mod cache;
pub mod comments;
pub mod constants;
pub mod download;
pub mod select;
pub mod status;
pub mod storage;
pub mod subscribe;
pub mod update;
pub mod watch;

#[tokio::main]
async fn main() -> Result<()> {
    let args = cli::CliArgs::parse();
    stderrlog::new()
        .module(module_path!())
        .modules(&["yt_dlp".to_owned(), "libmpv2".to_owned()])
        .quiet(args.quiet)
        .show_module_names(false)
        .color(stderrlog::ColorChoice::Auto)
        .verbosity(args.verbosity as usize)
        .timestamp(stderrlog::Timestamp::Off)
        .init()
        .expect("Let's just hope that this does not panic");

    let app = App::new(args.db_path.unwrap_or(constants::database()?)).await?;

    match args.command.unwrap_or(Command::default()) {
        Command::Download {
            force,
            max_cache_size,
        } => {
            info!("max cache size: '{}'", max_cache_size);

            if force {
                invalidate(&app, true).await?;
            }

            download::Downloader::new()
                .consume(Arc::new(app), max_cache_size)
                .await?;
        }
        Command::Select { cmd } => {
            let cmd = cmd.unwrap_or(SelectCommand::default());

            match cmd {
                SelectCommand::File { done } => select::select(&app, done).await?,
                _ => handle_select_cmd(&app, cmd, None).await?,
            }
        }
        Command::Update {
            max_backlog,
            subscriptions,
        } => {
            let all_subs = get_subscriptions(&app).await?;

            for sub in &subscriptions {
                if let None = all_subs.0.get(sub) {
                    bail!(
                        "Your specified subscription to update '{}' is not a subscription!",
                        sub
                    )
                }
            }

            update::update(&app, max_backlog, subscriptions, args.verbosity).await?;
        }

        Command::Subscriptions { cmd } => match cmd {
            SubscriptionCommand::Add { name, url } => {
                subscribe::subscribe(&app, name, url)
                    .await
                    .context("Failed to add a subscription")?;
            }
            SubscriptionCommand::Remove { name } => {
                subscribe::unsubscribe(&app, name)
                    .await
                    .context("Failed to remove a subscription")?;
            }
            SubscriptionCommand::List {} => {
                let all_subs = get_subscriptions(&app).await?;

                for (key, val) in all_subs.0 {
                    println!("{}: '{}'", key, val.url);
                }
            }
            SubscriptionCommand::Export {} => {
                let all_subs = get_subscriptions(&app).await?;
                for val in all_subs.0.values() {
                    println!("{}", val.url);
                }
            }
            SubscriptionCommand::Import { file, force } => {
                if let Some(file) = file {
                    let f = File::open(file).await?;

                    subscribe::import(&app, BufReader::new(f), force).await?
                } else {
                    subscribe::import(&app, BufReader::new(stdin()), force).await?
                };
            }
        },

        Command::Watch {} => watch::watch(&app).await?,

        Command::Status {} => status::show(&app).await?,

        Command::Database { command } => match command {
            CacheCommand::Invalidate { hard } => cache::invalidate(&app, hard).await?,
            CacheCommand::Maintain { all } => cache::maintain(&app, all).await?,
        },

        Command::Check { command } => match command {
            CheckCommand::InfoJson { path } => {
                let string = fs::read_to_string(&path)
                    .with_context(|| format!("Failed to read '{}' to string!", path.display()))?;

                let _: InfoJson =
                    serde_json::from_str(&string).context("Failed to deserialize value")?;
            }
            CheckCommand::UpdateInfoJson { path } => {
                let string = fs::read_to_string(&path)
                    .with_context(|| format!("Failed to read '{}' to string!", path.display()))?;

                let _: HashMap<Url, InfoJson> =
                    serde_json::from_str(&string).context("Failed to deserialize value")?;
            }
        },
        Command::Comments {} => {
            comments::comments(&app).await?;
        }
        Command::Description {} => {
            todo!()
            // description::description(&app).await?;
        }
    }

    Ok(())
}