Repository: nathanbabcock/ffmpeg-sidecar (language: rust)

Issue:
add support for ffprobe
Seems like this downloads `ffmpeg` and `ffprobe` and correctly places both the files after extraction. But there are no equivalent functions for `ffprobe`. Would it be possible to add these?

* `ffprobe_version()`
* `ffprobe_path()`
* `FfprobeCommand`


Discussion / hints:

Fix patch:
diff --git a/examples/ffprobe.rs b/examples/ffprobe.rs
new file mode 100644
index 0000000..b80d56e
--- /dev/null
+++ b/examples/ffprobe.rs
@@ -0,0 +1,11 @@
+use ffmpeg_sidecar::{download::auto_download, ffprobe::ffprobe_version};
+
+fn main() {
+  // Download ffprobe from a configured source.
+  // Note that not all distributions include ffprobe in their bundle.
+  auto_download().unwrap();
+
+  // Try running the executable and printing the version number.
+  let version = ffprobe_version().unwrap();
+  println!("ffprobe version: {}", version);
+}
diff --git a/src/ffprobe.rs b/src/ffprobe.rs
new file mode 100644
index 0000000..040d53c
--- /dev/null
+++ b/src/ffprobe.rs
@@ -0,0 +1,68 @@
+use crate::{
+  error::{Error, Result},
+  event::FfmpegEvent,
+  log_parser::FfmpegLogParser,
+  paths::sidecar_path,
+};
+use std::{ffi::OsStr, path::PathBuf};
+use std::{
+  path::Path,
+  process::{Command, Stdio},
+};
+
+/// Returns the path of the downloaded FFprobe executable, or falls back to
+/// assuming its installed in the system path. Note that not all FFmpeg
+/// distributions include FFprobe.
+pub fn ffprobe_path() -> PathBuf {
+  let default = Path::new("ffprobe").to_path_buf();
+  match sidecar_path() {
+    Ok(sidecar_path) => match sidecar_path.exists() {
+      true => sidecar_path,
+      false => default,
+    },
+    Err(_) => default,
+  }
+}
+
+/// Alias for `ffprobe -version`, parsing the version number and returning it.
+pub fn ffprobe_version() -> Result<String> {
+  ffprobe_version_with_path(ffprobe_path())
+}
+
+/// Lower level variant of `ffprobe_version` that exposes a customized the path
+/// to the ffmpeg binary.
+pub fn ffprobe_version_with_path<S: AsRef<OsStr>>(path: S) -> Result<String> {
+  let mut cmd = Command::new(&path)
+    .arg("-version")
+    .stdout(Stdio::piped()) // not stderr when calling `-version`
+    .spawn()?;
+  let stdout = cmd.stdout.take().ok_or("No standard output channel")?;
+  let mut parser = FfmpegLogParser::new(stdout);
+
+  let mut version: Option<String> = None;
+  while let Ok(event) = parser.parse_next_event() {
+    match event {
+      FfmpegEvent::ParsedVersion(v) => version = Some(v.version),
+      FfmpegEvent::LogEOF => break,
+      _ => {}
+    }
+  }
+  let exit_status = cmd.wait()?;
+  if !exit_status.success() {
+    return Err(Error::msg("ffprobe -version exited with non-zero status"));
+  }
+  version.ok_or_else(|| Error::msg("Failed to parse ffprobe version"))
+}
+
+/// Verify whether ffprobe is installed on the system. This will return true if
+/// there is an ffprobe binary in the PATH, or in the same directory as the Rust
+/// executable.
+pub fn ffprobe_is_installed() -> bool {
+  Command::new(ffprobe_path())
+    .arg("-version")
+    .stderr(Stdio::null())
+    .stdout(Stdio::null())
+    .status()
+    .map(|s| s.success())
+    .unwrap_or_else(|_| false)
+}
diff --git a/src/lib.rs b/src/lib.rs
index e7e4feb..b8d09e7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,49 +1,50 @@
-//! Wrap a standalone FFmpeg binary in an intuitive Iterator interface.
-//!
-//! ## Example
-//!
-//! ```rust
-//! use ffmpeg_sidecar::{command::FfmpegCommand, error::Result, event::FfmpegEvent};
-//!
-//! fn main() -> Result<()> {
-//!   FfmpegCommand::new() // <- Builder API like `std::process::Command`
-//!     .testsrc()  // <- Discoverable aliases for FFmpeg args
-//!     .rawvideo() // <- Convenient argument presets
-//!     .spawn()?   // <- Uses an ordinary `std::process::Child`
-//!     .iter()?    // <- Iterator over all log messages and video output
-//!     .for_each(|event: FfmpegEvent| {
-//!       match event {
-//!         FfmpegEvent::OutputFrame(frame) => {
-//!           println!("frame: {}x{}", frame.width, frame.height);
-//!           let _pixels: Vec<u8> = frame.data; // <- raw RGB pixels! 🎨
-//!         }
-//!         FfmpegEvent::Progress(progress) => {
-//!           eprintln!("Current speed: {}x", progress.speed); // <- parsed progress updates
-//!         }
-//!         FfmpegEvent::Log(_level, msg) => {
-//!           eprintln!("[ffmpeg] {}", msg); // <- granular log message from stderr
-//!         }
-//!         _ => {}
-//!       }
-//!     });
-//!   Ok(())
-//! }
-//! ```
-//!
-
-#[cfg(test)]
-mod test;
-
-pub mod child;
-pub mod comma_iter;
-pub mod command;
-pub mod download;
-pub mod error;
-pub mod event;
-pub mod iter;
-pub mod log_parser;
-pub mod metadata;
-pub mod paths;
-pub mod pix_fmt;
-pub mod read_until_any;
-pub mod version;
+//! Wrap a standalone FFmpeg binary in an intuitive Iterator interface.
+//!
+//! ## Example
+//!
+//! ```rust
+//! use ffmpeg_sidecar::{command::FfmpegCommand, error::Result, event::FfmpegEvent};
+//!
+//! fn main() -> Result<()> {
+//!   FfmpegCommand::new() // <- Builder API like `std::process::Command`
+//!     .testsrc()  // <- Discoverable aliases for FFmpeg args
+//!     .rawvideo() // <- Convenient argument presets
+//!     .spawn()?   // <- Uses an ordinary `std::process::Child`
+//!     .iter()?    // <- Iterator over all log messages and video output
+//!     .for_each(|event: FfmpegEvent| {
+//!       match event {
+//!         FfmpegEvent::OutputFrame(frame) => {
+//!           println!("frame: {}x{}", frame.width, frame.height);
+//!           let _pixels: Vec<u8> = frame.data; // <- raw RGB pixels! 🎨
+//!         }
+//!         FfmpegEvent::Progress(progress) => {
+//!           eprintln!("Current speed: {}x", progress.speed); // <- parsed progress updates
+//!         }
+//!         FfmpegEvent::Log(_level, msg) => {
+//!           eprintln!("[ffmpeg] {}", msg); // <- granular log message from stderr
+//!         }
+//!         _ => {}
+//!       }
+//!     });
+//!   Ok(())
+//! }
+//! ```
+//!
+
+#[cfg(test)]
+mod test;
+
+pub mod child;
+pub mod comma_iter;
+pub mod command;
+pub mod download;
+pub mod error;
+pub mod event;
+pub mod ffprobe;
+pub mod iter;
+pub mod log_parser;
+pub mod metadata;
+pub mod paths;
+pub mod pix_fmt;
+pub mod read_until_any;
+pub mod version;


Test patch: