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