use std::{
path::PathBuf,
process::{Command, Stdio},
};
use tracing::debug;
use crate::Error;
#[derive(Debug)]
pub struct AspectRatio(pub u8, pub u8);
#[derive(Debug)]
pub struct Resolution(pub u16, pub u16);
#[derive(Debug)]
pub struct MediaData {
pub resolution: Option<Resolution>,
pub aspect_ratio: Option<AspectRatio>,
pub duration: Option<f32>,
pub old_kbit_rate: Option<u32>,
}
pub fn get_metadata(path: &PathBuf) -> Result<MediaData, Error> {
let args = [
"-v",
"error",
"-select_streams",
"v:0",
"-show_entries",
"stream=width,height,display_aspect_ratio,bit_rate,duration",
"-of",
"csv=s=,:p=0",
];
let ffprobe = Command::new("ffprobe")
.args(args)
.arg(path)
.stderr(Stdio::piped())
.output()?;
if !ffprobe.status.success() {
return Err(Error::Sys(
"Ffprobe failed running. Is it installed?".to_owned(),
));
};
let text = std::str::from_utf8(&ffprobe.stdout)?;
let mem = text.split(',').collect::<Vec<_>>();
debug!("{:?}", mem);
let width = mem.first().and_then(|v| v.parse::<u16>().ok());
let height = mem.get(1).and_then(|v| v.parse::<u16>().ok());
let aspect_ratio = mem.get(2).and_then(|a| {
a.split_once(':')
.map(|u| (u.0.parse::<u8>().ok(), u.1.parse::<u8>().ok()))
.and_then(|u| u.0.zip(u.1))
.map(|u| AspectRatio(u.0, u.1))
});
let old_kbit_rate = mem
.get(3)
.and_then(|v| v.parse::<u32>().ok().map(|v| v / 1000));
let resolution = width.zip(height).map(|s| Resolution(s.0, s.1));
let duration = match mem.get(4).and_then(|v| v.trim().parse::<f32>().ok()) {
Some(d) => Some(d),
None => {
let metadata = get_attribute_from_meta("duration", path).unwrap_or("".to_owned());
let res = metadata.parse::<f32>().ok();
if let Some(d) = res {
Some(d)
} else {
if !metadata.contains(":") {
None
} else {
let mut res = 0.;
let mut iter = metadata.split(':').rev();
let secs = iter.next().map(|n| n.parse::<f32>().ok()).flatten();
let mins = iter
.next()
.map(|n| n.parse::<f32>().ok().map(|m| m * 60.))
.flatten();
let hrs = iter
.next()
.map(|n| n.parse::<f32>().ok().map(|h| h * 3600.))
.flatten();
let days = iter
.next()
.map(|n| n.parse::<f32>().ok().map(|d| d * 24. * 3600.))
.flatten();
if let Some(s) = secs {
res = res + s
};
if let Some(m) = mins {
res = res + m
};
if let Some(h) = hrs {
res = res + h
};
if let Some(d) = days {
res = res + d
};
Some(res)
}
}
}
};
Ok(MediaData {
aspect_ratio,
duration,
resolution,
old_kbit_rate,
})
}
pub fn get_attribute_from_meta(attr: &str, path: &PathBuf) -> Option<String> {
let ffprobe = Command::new("ffprobe")
.args([
"-v",
"error",
"-select_streams",
"v:0",
"-show_entries",
&format!("stream={attr}:stream_args={attr}"),
"-of",
"csv=s=,:p=0",
])
.arg(path)
.stderr(Stdio::piped())
.output()
.ok()?;
if !ffprobe.status.success() {
return None;
};
std::str::from_utf8(&ffprobe.stdout)
.ok()
.map(|v| v.to_string())
}