unbundle/probe.rs
1//! Lightweight media file probing.
2//!
3//! [`MediaProbe`] extracts metadata from a media file without keeping the
4//! demuxer open. This is useful for quickly inspecting many files (e.g. in a
5//! directory listing) without the cost of retaining an FFmpeg input context
6//! per file.
7//!
8//! For full extraction capabilities, use
9//! [`MediaFile::open`](crate::MediaFile::open) instead.
10
11use std::path::Path;
12
13use crate::error::UnbundleError;
14use crate::metadata::MediaMetadata;
15use crate::unbundle::MediaFile;
16
17/// Lightweight media file probe.
18///
19/// Opens the file, extracts metadata, and immediately closes the demuxer.
20/// The resulting [`MediaMetadata`] is identical to what
21/// [`MediaFile::metadata`](crate::MediaFile::metadata) returns, but
22/// without keeping the file open for extraction.
23///
24/// # Example
25///
26/// ```no_run
27/// use unbundle::{MediaProbe, UnbundleError};
28///
29/// let metadata = MediaProbe::probe("input.mp4")?;
30/// println!("Duration: {:?}, format: {}", metadata.duration, metadata.format);
31/// if let Some(video) = &metadata.video {
32/// println!("Video: {}x{} @ {} fps", video.width, video.height, video.frames_per_second);
33/// }
34/// # Ok::<(), UnbundleError>(())
35/// ```
36pub struct MediaProbe;
37
38impl MediaProbe {
39 /// Probe a media file and return its metadata.
40 ///
41 /// Opens the file, extracts all available metadata (video, audio,
42 /// subtitle streams, chapters), and closes the demuxer. The returned
43 /// [`MediaMetadata`] is owned and fully independent of any file handle.
44 ///
45 /// # Errors
46 ///
47 /// Returns [`UnbundleError::FileOpen`] if the file cannot be opened or
48 /// recognised as a media file.
49 ///
50 /// # Example
51 ///
52 /// ```no_run
53 /// use unbundle::{MediaProbe, UnbundleError};
54 ///
55 /// let metadata = MediaProbe::probe("video.mkv")?;
56 /// println!("{:?}", metadata);
57 /// # Ok::<(), UnbundleError>(())
58 /// ```
59 pub fn probe<P: AsRef<Path>>(path: P) -> Result<MediaMetadata, UnbundleError> {
60 log::debug!("Probing media file: {}", path.as_ref().display());
61 let unbundler = MediaFile::open(path)?;
62 Ok(unbundler.metadata.clone())
63 }
64
65 /// Probe multiple media files and return their metadata.
66 ///
67 /// Files that cannot be probed produce an `Err` entry in the result
68 /// vector rather than aborting the entire batch.
69 ///
70 /// # Example
71 ///
72 /// ```no_run
73 /// use unbundle::MediaProbe;
74 ///
75 /// let results = MediaProbe::probe_many(&["a.mp4", "b.mkv", "c.avi"]);
76 /// for result in &results {
77 /// match result {
78 /// Ok(meta) => println!("{}: {:?}", meta.format, meta.duration),
79 /// Err(err) => eprintln!("Error: {err}"),
80 /// }
81 /// }
82 /// ```
83 pub fn probe_many<P: AsRef<Path>>(paths: &[P]) -> Vec<Result<MediaMetadata, UnbundleError>> {
84 paths.iter().map(|path| Self::probe(path)).collect()
85 }
86}