use std::path::Path;
use std::process::Command;
use std::time::Duration;
use error::FfProbeError;
#[cfg(feature = "streams")]
mod attachment_stream;
#[cfg(feature = "streams")]
mod audio_stream;
#[cfg(feature = "chapters")]
mod chapter;
mod config;
#[cfg(feature = "streams")]
mod data_stream;
#[cfg(feature = "streams")]
mod disposition;
pub mod error;
mod ffprobe;
#[cfg(feature = "format")]
mod format;
mod ratio;
#[cfg(feature = "streams")]
mod streams;
#[cfg(feature = "streams")]
mod subtitle_stream;
#[cfg(feature = "streams")]
mod video_stream;
#[cfg(feature = "streams")]
pub use attachment_stream::AttachmentStream;
#[cfg(feature = "streams")]
pub use attachment_stream::AttachmentTags;
#[cfg(feature = "streams")]
pub use audio_stream::AudioStream;
#[cfg(feature = "streams")]
pub use audio_stream::AudioTags;
#[cfg(feature = "chapters")]
pub use chapter::Chapter;
#[cfg(feature = "chapters")]
pub use chapter::ChapterTags;
pub use config::Config;
#[cfg(feature = "streams")]
pub use data_stream::DataStream;
#[cfg(feature = "streams")]
pub use data_stream::DataTags;
#[cfg(feature = "streams")]
pub use disposition::Disposition;
pub use ffprobe::FfProbe;
#[cfg(feature = "format")]
pub use format::Format;
#[cfg(feature = "format")]
pub use format::FormatTags;
pub use ratio::Ratio;
use serde::Deserialize;
use serde::Deserializer;
#[cfg(feature = "streams")]
pub use streams::SideData;
#[cfg(feature = "streams")]
pub use streams::Stream;
#[cfg(feature = "streams")]
pub use streams::StreamKinds;
#[cfg(feature = "streams")]
pub use streams::StreamTags;
#[cfg(feature = "streams")]
pub use subtitle_stream::SubtititleTags;
#[cfg(feature = "streams")]
pub use subtitle_stream::SubtitleStream;
#[cfg(feature = "streams")]
pub use video_stream::VideoStream;
#[cfg(feature = "streams")]
pub use video_stream::VideoTags;
pub fn ffprobe(path: impl AsRef<Path>) -> Result<FfProbe, FfProbeError> {
ffprobe_config(Config::new(), path)
}
pub fn ffprobe_config(config: Config, path: impl AsRef<Path>) -> Result<FfProbe, FfProbeError> {
let path = path.as_ref();
let mut cmd = Command::new(config.ffprobe_bin);
cmd.args(["-v", "quiet", "-print_format", "json"]);
#[cfg(feature = "chapters")]
cmd.arg("-show_chapters");
#[cfg(feature = "format")]
cmd.arg("-show_format");
#[cfg(feature = "streams")]
cmd.arg("-show_streams");
if config.count_frames {
cmd.arg("-count_frames");
}
cmd.arg(path);
let out = cmd.output().map_err(FfProbeError::Io)?;
if !out.status.success() {
return Err(FfProbeError::Status(out));
}
serde_json::from_slice::<FfProbe>(&out.stdout).map_err(FfProbeError::Deserialize)
}
#[cfg(feature = "async")]
pub async fn ffprobe_async(path: impl AsRef<std::path::Path>) -> Result<FfProbe, FfProbeError> {
ffprobe_async_config(Config::new(), path).await
}
#[cfg(feature = "async")]
pub async fn ffprobe_async_config(
config: Config,
path: impl AsRef<Path>,
) -> Result<FfProbe, FfProbeError> {
let mut cmd = tokio::process::Command::new("ffprobe");
let path = path.as_ref();
cmd.args(["-v", "quiet", "-print_format", "json"]);
#[cfg(feature = "chapters")]
cmd.arg("-show_chapters");
#[cfg(feature = "format")]
cmd.arg("-show_format");
#[cfg(feature = "streams")]
cmd.arg("-show_streams");
if config.count_frames {
cmd.arg("-count_frames");
}
cmd.arg(path);
let out = cmd.output().await.map_err(FfProbeError::Io)?;
if !out.status.success() {
return Err(FfProbeError::Status(out));
}
serde_json::from_slice::<FfProbe>(&out.stdout).map_err(FfProbeError::Deserialize)
}
pub fn option_string_to_duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
where
D: Deserializer<'de>,
{
let s: Option<String> = Option::deserialize(deserializer)?;
match s {
Some(s) => s
.parse::<f64>()
.map(|v| v.max(0.0))
.map(Duration::from_secs_f64)
.map(Some)
.map_err(serde::de::Error::custom),
None => Ok(None),
}
}