Files
vid-repair/vid-repair-core/src/scan/ffprobe.rs
2025-12-31 22:07:42 -05:00

84 lines
2.5 KiB
Rust

use std::path::Path;
use std::process::Command;
use anyhow::{Context, Result};
use serde_json::Value;
use super::types::{ProbeData, StreamInfo};
pub fn run_ffprobe(path: &Path, ffprobe_path: &str) -> Result<ProbeData> {
let output = Command::new(ffprobe_path)
.arg("-v")
.arg("error")
.arg("-print_format")
.arg("json")
.arg("-show_format")
.arg("-show_streams")
.arg(path)
.output()
.with_context(|| format!("Failed to run ffprobe on {}", path.display()))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!(
"ffprobe failed for {}: {}",
path.display(),
stderr.trim()
);
}
let raw: Value = serde_json::from_slice(&output.stdout)
.with_context(|| format!("Failed to parse ffprobe output for {}", path.display()))?;
let format_name = raw
.get("format")
.and_then(|fmt| fmt.get("format_name"))
.and_then(|v| v.as_str())
.map(|s| s.to_string());
let duration = raw
.get("format")
.and_then(|fmt| fmt.get("duration"))
.and_then(|v| v.as_str())
.and_then(|s| s.parse::<f64>().ok());
let streams = raw
.get("streams")
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.map(|stream| StreamInfo {
codec_type: stream
.get("codec_type")
.and_then(|v| v.as_str())
.map(|s| s.to_string()),
codec_name: stream
.get("codec_name")
.and_then(|v| v.as_str())
.map(|s| s.to_string()),
width: stream.get("width").and_then(|v| v.as_u64()).map(|v| v as u32),
height: stream
.get("height")
.and_then(|v| v.as_u64())
.map(|v| v as u32),
sample_rate: stream
.get("sample_rate")
.and_then(|v| v.as_str())
.map(|s| s.to_string()),
channels: stream
.get("channels")
.and_then(|v| v.as_u64())
.map(|v| v as u32),
})
.collect::<Vec<_>>()
})
.unwrap_or_default();
Ok(ProbeData {
format_name,
duration,
streams,
raw,
})
}