use std::process::Command;
use which::which;
pub struct DependencyChecker;
#[derive(Debug, Clone)]
pub struct DependencyStatus {
pub name: String,
pub found: bool,
pub version: Option<String>,
pub path: Option<String>,
}
impl DependencyChecker {
pub fn check_ffmpeg() -> DependencyStatus {
match which("ffmpeg") {
Ok(path) => {
let version = Self::get_ffmpeg_version();
DependencyStatus {
name: "ffmpeg".to_string(),
found: true,
version,
path: Some(path.display().to_string()),
}
}
Err(_) => DependencyStatus {
name: "ffmpeg".to_string(),
found: false,
version: None,
path: None,
},
}
}
pub fn check_atomic_parsley() -> DependencyStatus {
match which("AtomicParsley") {
Ok(path) => {
let version = Self::get_atomic_parsley_version();
DependencyStatus {
name: "AtomicParsley".to_string(),
found: true,
version,
path: Some(path.display().to_string()),
}
}
Err(_) => DependencyStatus {
name: "AtomicParsley".to_string(),
found: false,
version: None,
path: None,
},
}
}
pub fn check_mp4box() -> DependencyStatus {
match which("MP4Box") {
Ok(path) => {
let version = Self::get_mp4box_version();
DependencyStatus {
name: "MP4Box".to_string(),
found: true,
version,
path: Some(path.display().to_string()),
}
}
Err(_) => DependencyStatus {
name: "MP4Box".to_string(),
found: false,
version: None,
path: None,
},
}
}
pub fn check_all() -> Vec<DependencyStatus> {
vec![
Self::check_ffmpeg(),
Self::check_atomic_parsley(),
Self::check_mp4box(),
]
}
pub fn all_dependencies_met() -> bool {
Self::check_all().iter().all(|dep| dep.found)
}
fn get_ffmpeg_version() -> Option<String> {
let output = Command::new("ffmpeg")
.arg("-version")
.output()
.ok()?;
let stdout = String::from_utf8_lossy(&output.stdout);
stdout
.lines()
.next()
.and_then(|line| line.split_whitespace().nth(2))
.map(|s| s.to_string())
}
fn get_atomic_parsley_version() -> Option<String> {
let output = Command::new("AtomicParsley")
.arg("--version")
.output()
.ok()?;
let stdout = String::from_utf8_lossy(&output.stdout);
stdout
.lines()
.find(|line| line.contains("version"))
.and_then(|line| line.split_whitespace().last())
.map(|s| s.to_string())
}
fn get_mp4box_version() -> Option<String> {
let output = Command::new("MP4Box")
.arg("-version")
.output()
.ok()?;
let stdout = String::from_utf8_lossy(&output.stdout);
stdout
.lines()
.next()
.and_then(|line| line.split_whitespace().find(|s| s.contains("version")))
.map(|s| s.to_string())
}
pub fn check_aac_at_support() -> bool {
let output = Command::new("ffmpeg")
.args(&["-encoders"])
.output();
if let Ok(output) = output {
let stdout = String::from_utf8_lossy(&output.stdout);
stdout.contains("aac_at")
} else {
false
}
}
pub fn get_available_encoders() -> Vec<String> {
crate::audio::EncoderDetector::get_available_encoders()
.into_iter()
.map(|e| e.name().to_string())
.collect()
}
pub fn get_selected_encoder() -> String {
crate::audio::get_encoder().name().to_string()
}
}
impl std::fmt::Display for DependencyStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.found {
write!(f, "✓ {}", self.name)?;
if let Some(ref version) = self.version {
write!(f, " ({})", version)?;
}
if let Some(ref path) = self.path {
write!(f, "\n Path: {}", path)?;
}
Ok(())
} else {
write!(f, "✗ {} - NOT FOUND", self.name)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_check_dependencies() {
let deps = DependencyChecker::check_all();
assert_eq!(deps.len(), 3);
let ffmpeg = deps.iter().find(|d| d.name == "ffmpeg");
assert!(ffmpeg.is_some());
}
}