use std::path::Path;
use crate::command::{FfmpegBinaryPaths, FfmpegCommand};
use crate::config::FfmpegLocator;
use crate::error::Result;
use crate::types::Time;
#[derive(Clone, Debug)]
pub enum ThumbnailFormat {
Png,
Jpeg,
}
impl ThumbnailFormat {
fn extension(&self) -> &'static str {
match self {
ThumbnailFormat::Png => "png",
ThumbnailFormat::Jpeg => "jpg",
}
}
fn ffmpeg_args(&self) -> &'static [&'static str] {
match self {
ThumbnailFormat::Png => &["-f", "image2"],
ThumbnailFormat::Jpeg => &["-f", "mjpeg"],
}
}
}
#[derive(Clone, Debug)]
pub struct ThumbnailOptions {
time: Time,
width: Option<u32>,
height: Option<u32>,
format: ThumbnailFormat,
}
impl ThumbnailOptions {
pub fn new(time: Time) -> Self {
Self {
time,
width: None,
height: None,
format: ThumbnailFormat::Png,
}
}
pub fn time(&self) -> Time {
self.time
}
pub fn size(mut self, width: u32, height: u32) -> Self {
self.width = Some(width);
self.height = Some(height);
self
}
pub fn dimensions(&self) -> Option<(u32, u32)> {
match (self.width, self.height) {
(Some(w), Some(h)) => Some((w, h)),
_ => None,
}
}
pub fn format(mut self, format: ThumbnailFormat) -> Self {
self.format = format;
self
}
pub fn output_format(&self) -> &ThumbnailFormat {
&self.format
}
}
pub fn generate(
input: impl AsRef<Path>,
output: impl AsRef<Path>,
options: &ThumbnailOptions,
) -> Result<()> {
let locator = FfmpegLocator::system()?;
generate_with_binaries(locator.binaries(), input, output, options)
}
pub fn generate_with_binaries(
binaries: &FfmpegBinaryPaths,
input: impl AsRef<Path>,
output: impl AsRef<Path>,
options: &ThumbnailOptions,
) -> Result<()> {
if let Some(parent) = output.as_ref().parent() {
std::fs::create_dir_all(parent)?;
}
let mut cmd = FfmpegCommand::new(binaries.ffmpeg());
cmd.arg("-y");
cmd.arg("-ss").arg(options.time.to_ffmpeg_timestamp());
cmd.arg("-i").arg(input.as_ref());
cmd.arg("-vframes").arg("1");
if let (Some(width), Some(height)) = (options.width, options.height) {
cmd.arg("-vf").arg(format!("scale={width}:{height}"));
}
for arg in options.format.ffmpeg_args() {
cmd.arg(arg);
}
let mut output_path = output.as_ref().to_path_buf();
if output_path.extension().is_none() {
output_path.set_extension(options.format.extension());
}
cmd.arg(output_path);
cmd.run()
}