use std::path::Path;
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct MediaMeta {
pub width: Option<u32>,
pub height: Option<u32>,
pub duration_secs: Option<f64>,
}
impl MediaMeta {
pub fn dimensions(&self) -> Option<(u32, u32)> {
match (self.width, self.height) {
(Some(w), Some(h)) if w > 0 && h > 0 => Some((w, h)),
_ => None,
}
}
}
pub fn probe_media_meta(path: &Path) -> Option<MediaMeta> {
let ext = path
.extension()
.and_then(|e| e.to_str())
.map(|s| s.to_ascii_lowercase())?;
#[cfg(feature = "video")]
if crate::mime_resolve::is_video_ext(&ext) {
return probe_video(path);
}
#[cfg(feature = "audio")]
if crate::mime_resolve::is_audio_ext(&ext) {
return probe_audio(path);
}
if matches!(ext.as_str(), "psd" | "psb") {
return probe_psd_header(path);
}
#[cfg(feature = "pdf")]
if ext == "ai" {
return probe_ai_page_size(path);
}
probe_image_dimensions(path)
}
#[cfg(feature = "video")]
fn probe_video(path: &Path) -> Option<MediaMeta> {
let probe = crate::decode::ffmpeg_probe::probe_options_for_video(path);
let (dimensions, duration_secs) = crate::decode::ffmpeg_decode::probe_video_meta(path, probe);
let (width, height) = dimensions?;
Some(MediaMeta {
width: Some(width),
height: Some(height),
duration_secs,
})
}
#[cfg(feature = "audio")]
fn probe_audio(path: &Path) -> Option<MediaMeta> {
use lofty::file::AudioFile;
let tagged = lofty::read_from_path(path).ok()?;
let duration = tagged.properties().duration();
if duration.is_zero() {
return None;
}
Some(MediaMeta {
width: None,
height: None,
duration_secs: Some(duration.as_secs_f64()),
})
}
fn probe_psd_header(path: &Path) -> Option<MediaMeta> {
use std::io::Read;
let mut file = std::fs::File::open(path).ok()?;
let mut header = [0u8; 22];
file.read_exact(&mut header).ok()?;
if &header[0..4] != b"8BPS" {
return None;
}
let height = u32::from_be_bytes(header[14..18].try_into().ok()?);
let width = u32::from_be_bytes(header[18..22].try_into().ok()?);
Some(MediaMeta {
width: Some(width),
height: Some(height),
duration_secs: None,
})
}
#[cfg(feature = "pdf")]
fn probe_ai_page_size(path: &Path) -> Option<MediaMeta> {
let (width, height) = crate::thumbs::pdf::probe_page_size(path)?;
Some(MediaMeta {
width: Some(width),
height: Some(height),
duration_secs: None,
})
}
fn probe_image_dimensions(path: &Path) -> Option<MediaMeta> {
let (width, height) = image::ImageReader::open(path)
.ok()?
.into_dimensions()
.ok()?;
Some(MediaMeta {
width: Some(width),
height: Some(height),
duration_secs: None,
})
}