#![warn(missing_docs)]
#![warn(clippy::all)]
#![warn(clippy::pedantic)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::must_use_candidate)]
pub mod builder;
pub mod format;
pub mod parsers;
pub mod types;
pub use builder::FFprobeBuilder;
pub use format::{EscapeMode, OutputFormat, StringValidation, WriterOptions};
pub use types::{
ChapterInfo, ErrorInfo, FormatInfo, FrameInfo, IntervalPosition, PacketInfo, ProbeResult,
ProbeSection, ProgramInfo, ReadInterval, StreamInfo,
};
pub use ffmpeg_common::{
get_version, Capabilities, Duration, Error, LogLevel, MediaPath, Result, StreamSpecifier,
StreamType, Version,
};
pub mod prelude {
pub use crate::{
FFprobeBuilder, OutputFormat, ProbeResult, StreamInfo,
format::presets,
};
pub use ffmpeg_common::{Duration, MediaPath, Result, StreamSpecifier, StreamType};
}
pub async fn probe(path: impl Into<MediaPath>) -> Result<ProbeResult> {
FFprobeBuilder::probe(path).run().await
}
pub async fn probe_format(path: impl Into<MediaPath>) -> Result<ProbeResult> {
FFprobeBuilder::probe_format(path).run().await
}
pub async fn probe_streams(path: impl Into<MediaPath>) -> Result<ProbeResult> {
FFprobeBuilder::probe_streams(path).run().await
}
pub async fn capabilities() -> Result<Capabilities> {
Capabilities::detect("ffprobe").await
}
pub async fn is_available() -> bool {
ffmpeg_common::process::find_executable("ffprobe").is_ok()
}
pub async fn version() -> Result<Version> {
get_version("ffprobe").await
}
pub mod helpers {
use super::*;
pub async fn get_dimensions(path: impl Into<MediaPath>) -> Result<Option<(u32, u32)>> {
let result = probe_streams(path).await?;
Ok(result.primary_video_stream().unwrap().resolution())
}
pub async fn get_duration(path: impl Into<MediaPath>) -> Result<Option<f64>> {
let result = probe_format(path).await?;
Ok(result.duration())
}
pub async fn get_format(path: impl Into<MediaPath>) -> Result<Option<String>> {
let result = probe_format(path).await?;
Ok(result.format_name().map(String::from))
}
pub async fn get_video_codec(path: impl Into<MediaPath>) -> Result<Option<String>> {
let result = probe_streams(path).await?;
Ok(result
.primary_video_stream()
.unwrap()
.codec_name
.clone())
}
pub async fn get_audio_codec(path: impl Into<MediaPath>) -> Result<Option<String>> {
let result = probe_streams(path).await?;
Ok(result
.primary_audio_stream()
.unwrap()
.codec_name
.clone())
}
pub async fn has_video(path: impl Into<MediaPath>) -> Result<bool> {
let result = probe_streams(path).await?;
Ok(!result.video_streams().is_empty())
}
pub async fn has_audio(path: impl Into<MediaPath>) -> Result<bool> {
let result = probe_streams(path).await?;
Ok(!result.audio_streams().is_empty())
}
pub async fn has_subtitles(path: impl Into<MediaPath>) -> Result<bool> {
let result = probe_streams(path).await?;
Ok(!result.subtitle_streams().is_empty())
}
pub async fn get_metadata(path: impl Into<MediaPath>) -> Result<std::collections::HashMap<String, String>> {
let result = probe_format(path).await?;
Ok(result
.format
.map(|f| f.tags)
.unwrap_or_default())
}
pub async fn get_stream_count(path: impl Into<MediaPath>) -> Result<usize> {
let result = probe_streams(path).await?;
Ok(result.streams.len())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
#[test]
fn test_builder_creation() {
let builder = FFprobeBuilder::probe("test.mp4");
let command = builder.command().unwrap();
assert!(command.contains("ffprobe"));
assert!(command.contains("test.mp4"));
assert!(command.contains("-show_format"));
assert!(command.contains("-show_streams"));
}
#[test]
fn test_output_format_selection() {
let builder = FFprobeBuilder::probe("test.mp4")
.output_format(OutputFormat::Xml);
let command = builder.command().unwrap();
assert!(command.contains("-print_format xml"));
}
#[test]
fn test_stream_selection() {
let builder = FFprobeBuilder::probe_stream(
"test.mp4",
StreamSpecifier::Type(StreamType::Audio),
);
let command = builder.command().unwrap();
assert!(command.contains("-select_streams a"));
}
}