use std::{
path::{Path, PathBuf},
process::Command,
};
use anyhow::{anyhow, bail, Result};
use super::{get_stdout, Preset};
pub(crate) struct File {
pub(crate) path: PathBuf,
pub(crate) duration: Option<u32>,
pub(crate) title_hint: String,
}
impl File {
pub(crate) fn new(path: impl Into<PathBuf>) -> Result<Self> {
let path = path.into();
if !path.is_file() {
bail!("Path {} is not a file", path.display())
}
let duration = File::duration(&path).ok();
let title_hint = File::title_hint(&path)?;
Ok(Self {
path,
duration,
title_hint,
})
}
#[allow(clippy::cast_possible_truncation)]
fn duration(path: &Path) -> Result<u32> {
let output = Command::new("ffprobe")
.args(
"-v error -show_entries format=duration -print_format \
default=noprint_wrappers=1:nokey=1"
.split_whitespace(),
)
.arg(path)
.output()?;
Ok(get_stdout(output)?
.trim()
.split('.')
.next()
.ok_or_else(|| anyhow!("no chunk before period"))?
.parse::<u32>()?)
}
pub(crate) fn rip(&self, preset: &Preset) -> Result<PathBuf> {
let outfile = &self.title_hint;
Command::new("HandBrakeCLI")
.arg("--preset")
.arg(preset.to_string())
.arg("--input")
.arg(&self.path)
.arg("--output")
.arg(outfile)
.output()?;
Ok(outfile.into())
}
fn title_hint(path: impl AsRef<Path>) -> Result<String> {
if let Some(stem) =
path.as_ref().file_stem().and_then(|stem| stem.to_str())
{
Ok(stem.to_string())
} else {
bail!("Unable to get stem from path: {}", path.as_ref().display())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_title() {
assert_eq!(File::new("/etc/passwd").unwrap().title_hint, "passwd");
assert!(File::new("/etc/").is_err());
}
}