use crate::models::{CookieSource, DependencyStatus}; use std::process::Command; pub fn check_tool_exists(name: &str) -> Option { let output = Command::new("which").arg(name).output().ok()?; if output.status.success() { let version_output = Command::new(name).arg("--version").output().ok()?; let version = String::from_utf8_lossy(&version_output.stdout) .lines() .next() .unwrap_or("") .trim() .to_string(); Some(version) } else { None } } pub fn check_js_runtime() -> Option { check_tool_exists("deno").or_else(|| check_tool_exists("node")) } pub fn check_pot_plugin(cookie_source: &CookieSource) -> bool { let mut command = Command::new("yt-dlp"); command .arg("-v") .arg("--simulate") .args(cookie_source.to_ytdlp_args()) .arg("https://www.youtube.com/watch?v=dQw4w9WgXcQ"); let output = command.output(); match output { Ok(out) => { let stderr = String::from_utf8_lossy(&out.stderr); stderr.contains("GetPOT") } Err(_) => false, } } pub fn get_all_dependency_statuses(cookie_source: &CookieSource) -> Vec { let ffmpeg = check_tool_exists("ffmpeg"); let ytdlp = check_tool_exists("yt-dlp"); let js_runtime = check_js_runtime(); let pot_plugin = if ytdlp.is_some() { check_pot_plugin(cookie_source) } else { false }; vec![ DependencyStatus { name: "ffmpeg".to_string(), installed: ffmpeg.is_some(), version: ffmpeg, install_command: "brew install ffmpeg".to_string(), }, DependencyStatus { name: "yt-dlp".to_string(), installed: ytdlp.is_some(), version: ytdlp, install_command: "brew install yt-dlp".to_string(), }, DependencyStatus { name: "JavaScript runtime (deno/node)".to_string(), installed: js_runtime.is_some(), version: js_runtime, install_command: "brew install deno".to_string(), }, DependencyStatus { name: "bgutil-ytdlp-pot-provider".to_string(), installed: pot_plugin, version: None, install_command: "python3 -m pip install -U bgutil-ytdlp-pot-provider".to_string(), }, ] } pub fn get_install_args(dep_name: &str) -> Result, String> { match dep_name { "ffmpeg" => Ok(vec![ "brew".into(), "install".into(), "ffmpeg".into(), ]), "yt-dlp" => Ok(vec![ "brew".into(), "install".into(), "yt-dlp".into(), ]), "JavaScript runtime (deno/node)" => Ok(vec![ "brew".into(), "install".into(), "deno".into(), ]), "bgutil-ytdlp-pot-provider" => Ok(vec![ "python3".into(), "-m".into(), "pip".into(), "install".into(), "-U".into(), "bgutil-ytdlp-pot-provider".into(), ]), _ => Err(format!("Unknown dependency: {dep_name}")), } } #[cfg(test)] mod tests { use super::*; use crate::models::CookieSource; #[test] fn test_get_install_args_ffmpeg() { let args = get_install_args("ffmpeg").unwrap(); assert_eq!(args, vec!["brew", "install", "ffmpeg"]); } #[test] fn test_get_install_args_ytdlp() { let args = get_install_args("yt-dlp").unwrap(); assert_eq!(args, vec!["brew", "install", "yt-dlp"]); } #[test] fn test_get_install_args_pot_provider() { let args = get_install_args("bgutil-ytdlp-pot-provider").unwrap(); assert_eq!(args, vec!["python3", "-m", "pip", "install", "-U", "bgutil-ytdlp-pot-provider"]); } #[test] fn test_get_install_args_unknown() { assert!(get_install_args("unknown-tool").is_err()); } #[test] fn test_dependency_statuses_has_four_entries() { let statuses = get_all_dependency_statuses(&CookieSource::default()); assert_eq!(statuses.len(), 4); assert_eq!(statuses[0].name, "ffmpeg"); assert_eq!(statuses[1].name, "yt-dlp"); assert_eq!(statuses[2].name, "JavaScript runtime (deno/node)"); assert_eq!(statuses[3].name, "bgutil-ytdlp-pot-provider"); } }