Skip to main content

fancy_ffmpeg/
ffprobe.rs

1use std::{
2    path::Path,
3    process::{Command, ExitStatus},
4};
5
6use serde::{Deserialize, Serialize};
7use serde_with::{DisplayFromStr, serde_as};
8
9#[derive(Debug, Deserialize, Serialize)]
10pub struct FFProbe {
11    pub streams: Vec<Stream>,
12    pub format: Format,
13}
14
15#[derive(Debug, Deserialize, Serialize)]
16pub struct Stream {}
17
18#[serde_as]
19#[derive(Debug, Deserialize, Serialize)]
20pub struct Format {
21    #[serde_as(as = "DisplayFromStr")]
22    pub duration: f64,
23}
24
25#[derive(Debug, thiserror::Error)]
26pub enum FFProbeError {
27    #[error("{0}")]
28    Io(#[from] std::io::Error),
29    #[error("{0}")]
30    Json(#[from] serde_json::Error),
31    #[error("{0}")]
32    Process(ExitStatus),
33}
34
35const ARGS: &[&str] = &[
36    "-v",
37    "quiet",
38    "-print_format",
39    "json",
40    "-show_format",
41    "-show_streams",
42];
43
44pub fn ffprobe<P: AsRef<Path>>(path: P) -> Result<FFProbe, FFProbeError> {
45    let output = Command::new("ffprobe")
46        .args(ARGS)
47        .arg(path.as_ref())
48        .output()?;
49
50    if output.status.success() {
51        return serde_json::from_slice(&output.stdout).map_err(FFProbeError::Json);
52    }
53
54    Err(FFProbeError::Process(output.status))
55}
56
57#[cfg(feature = "tokio")]
58pub async fn async_ffprobe<P: AsRef<Path>>(path: P) -> Result<FFProbe, FFProbeError> {
59    let output = tokio::process::Command::new("ffprobe")
60        .args(ARGS)
61        .arg(path.as_ref())
62        .output()
63        .await?;
64
65    if output.status.success() {
66        return serde_json::from_slice(&output.stdout).map_err(FFProbeError::Json);
67    }
68
69    Err(FFProbeError::Process(output.status))
70}