Skip to main content

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}