Skip to content

Commit

Permalink
fix: resolve ffprobe path correctly (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
nathanbabcock authored Oct 13, 2023
1 parent 36a4174 commit 9821b4d
Show file tree
Hide file tree
Showing 3 changed files with 426 additions and 417 deletions.
187 changes: 97 additions & 90 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,90 +1,97 @@
use std::error::Error as StdError;
use std::fmt::{Display, Formatter};
use std::io;
use std::result::Result as StdResult;
use std::str::Utf8Error;

/// Shorthand alias for `Result<T, Error>` using `ffmpeg_sidecar` error type.
pub type Result<T> = StdResult<T, Error>;

/// A generic error type for the `ffmpeg-sidecar` crate.
#[derive(Debug)]
pub struct Error {
pub message: String,
pub source: Option<Box<dyn StdError + 'static>>,
}

impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}

impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.source.as_deref()
}
}

impl Error {
/// Wrap any standard Error into a library Error.
/// Similar to [`anyhow`](https://github.com/dtolnay/anyhow/blob/master/src/error.rs#L88).
pub fn from_std<E>(e: E) -> Self
where
E: StdError + 'static,
{
Error {
message: e.to_string(),
source: Some(Box::new(e)),
}
}

/// Wrap any Display into a library Error.
pub fn from_display<E>(e: E) -> Self
where
E: Display,
{
Error {
message: e.to_string(),
source: None,
}
}

/// Create an error message from a string.
pub fn msg<S: AsRef<str>>(message: S) -> Self {
Error {
message: message.as_ref().to_string(),
source: None,
}
}
}

impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::from_std(e)
}
}

impl From<Utf8Error> for Error {
fn from(e: Utf8Error) -> Self {
Error::from_std(e)
}
}

impl From<&str> for Error {
fn from(e: &str) -> Self {
Error::from_display(e)
}
}

impl From<String> for Error {
fn from(e: String) -> Self {
Error::from_display(e)
}
}

impl From<()> for Error {
fn from(_: ()) -> Self {
Error::from_display("empty error")
}
}
use std::error::Error as StdError;
use std::fmt::{Display, Formatter};
use std::io;
use std::result::Result as StdResult;
use std::str::Utf8Error;
use std::string::FromUtf8Error;

/// Shorthand alias for `Result<T, Error>` using `ffmpeg_sidecar` error type.
pub type Result<T> = StdResult<T, Error>;

/// A generic error type for the `ffmpeg-sidecar` crate.
#[derive(Debug)]
pub struct Error {
pub message: String,
pub source: Option<Box<dyn StdError + 'static>>,
}

impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}

impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.source.as_deref()
}
}

impl Error {
/// Wrap any standard Error into a library Error.
/// Similar to [`anyhow`](https://github.com/dtolnay/anyhow/blob/master/src/error.rs#L88).
pub fn from_std<E>(e: E) -> Self
where
E: StdError + 'static,
{
Error {
message: e.to_string(),
source: Some(Box::new(e)),
}
}

/// Wrap any Display into a library Error.
pub fn from_display<E>(e: E) -> Self
where
E: Display,
{
Error {
message: e.to_string(),
source: None,
}
}

/// Create an error message from a string.
pub fn msg<S: AsRef<str>>(message: S) -> Self {
Error {
message: message.as_ref().to_string(),
source: None,
}
}
}

impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::from_std(e)
}
}

impl From<Utf8Error> for Error {
fn from(e: Utf8Error) -> Self {
Error::from_std(e)
}
}

impl From<FromUtf8Error> for Error {
fn from(e: FromUtf8Error) -> Self {
Error::from_std(e)
}
}

impl From<&str> for Error {
fn from(e: &str) -> Self {
Error::from_display(e)
}
}

impl From<String> for Error {
fn from(e: String) -> Self {
Error::from_display(e)
}
}

impl From<()> for Error {
fn from(_: ()) -> Self {
Error::from_display("empty error")
}
}
49 changes: 22 additions & 27 deletions src/ffprobe.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
use crate::{
error::{Error, Result},
event::FfmpegEvent,
log_parser::FfmpegLogParser,
paths::sidecar_path,
};
use std::{ffi::OsStr, path::PathBuf};
use crate::error::{Error, Result};
use std::{env::current_exe, ffi::OsStr, path::PathBuf};
use std::{
path::Path,
process::{Command, Stdio},
Expand All @@ -15,7 +10,7 @@ use std::{
/// distributions include FFprobe.
pub fn ffprobe_path() -> PathBuf {
let default = Path::new("ffprobe").to_path_buf();
match sidecar_path() {
match ffprobe_sidecar_path() {
Ok(sidecar_path) => match sidecar_path.exists() {
true => sidecar_path,
false => default,
Expand All @@ -24,6 +19,21 @@ pub fn ffprobe_path() -> PathBuf {
}
}

/// The (expected) path to an FFmpeg binary adjacent to the Rust binary.
///
/// The extension between platforms, with Windows using `.exe`, while Mac and
/// Linux have no extension.
pub fn ffprobe_sidecar_path() -> Result<PathBuf> {
let mut path = current_exe()?
.parent()
.ok_or("Can't get parent of current_exe")?
.join("ffprobe");
if cfg!(windows) {
path.set_extension("exe");
}
Ok(path)
}

/// Alias for `ffprobe -version`, parsing the version number and returning it.
pub fn ffprobe_version() -> Result<String> {
ffprobe_version_with_path(ffprobe_path())
Expand All @@ -32,26 +42,11 @@ pub fn ffprobe_version() -> Result<String> {
/// 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 output = Command::new(&path).arg("-version").output()?;

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"))
// note:version parsing is not implemented for ffprobe

String::from_utf8(output.stdout).map_err(Error::from)
}

/// Verify whether ffprobe is installed on the system. This will return true if
Expand Down
Loading

0 comments on commit 9821b4d

Please sign in to comment.