// yt - A fully featured command line YouTube client // // Copyright (C) 2024 Benedikt Peetz // 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 . use std::{collections::HashMap, process::Stdio, str::FromStr}; use anyhow::{Context, Ok, Result}; use chrono::{DateTime, Utc}; use log::{error, info, warn}; use tokio::{ io::{AsyncBufReadExt, BufReader}, process::Command, }; use url::Url; use yt_dlp::{unsmuggle_url, wrapper::info_json::InfoJson}; use crate::{ app::App, storage::{ subscriptions::{get_subscriptions, Subscription}, video_database::{ extractor_hash::ExtractorHash, getters::get_all_hashes, setters::add_video, Video, VideoStatus, }, }, }; pub async fn update( app: &App, max_backlog: u32, subs_to_update: Vec, verbosity: u8, ) -> Result<()> { let subscriptions = get_subscriptions(app).await?; let mut back_subs: HashMap = HashMap::new(); let logging = verbosity > 0; let log_level = match verbosity { // 0 => 50, // logging.CRITICAL 0 => 40, // logging.ERROR 1 => 30, // logging.WARNING 2 => 20, // logging.INFO 3.. => 10, // logging.DEBUG }; info!("Passing log_level {} to the update script", log_level); let mut urls: Vec = vec![]; for (name, sub) in subscriptions.0 { if subs_to_update.contains(&name) || subs_to_update.is_empty() { urls.push(sub.url.to_string()); back_subs.insert(sub.url.clone(), sub); } else { info!( "Not updating subscription '{}' as it was not specified", name ); } } // We can get away with not having to re-fetch the hashes every time, as the returned video // should not contain duplicates. let hashes = get_all_hashes(app).await?; let mut child = Command::new("raw_update.py") .arg(max_backlog.to_string()) .arg(urls.len().to_string()) .arg(log_level.to_string()) .args(&urls) .args(hashes.iter().map(|haz| haz.to_string()).collect::>()) .stdout(Stdio::piped()) .stderr(if logging { Stdio::inherit() } else { Stdio::null() }) .stdin(Stdio::null()) .spawn() .context("Failed to call python3 update_raw")?; let mut out = BufReader::new( child .stdout .take() .expect("Should be able to take child stdout"), ) .lines(); while let Some(line) = out.next_line().await? { // use tokio::{fs::File, io::AsyncWriteExt}; // let mut output = File::create("output.json").await?; // output.write(line.as_bytes()).await?; // output.flush().await?; // output.sync_all().await?; // drop(output); let output_json: HashMap = serde_json::from_str(&line).expect("This should be valid json"); for (url, value) in output_json { let sub = back_subs.get(&url).expect("This was stored before"); process_subscription(app, sub, value, &hashes).await? } } let out = child.wait().await?; if !out.success() { error!( "The update_raw.py invokation failed (exit code: {}).", out.code() .map(|f| f.to_string()) .unwrap_or("".to_owned()) ) } Ok(()) } pub fn video_entry_to_video(entry: InfoJson, sub: Option<&Subscription>) -> Result