ffmpeg_light/
thumbnail.rs1use std::path::Path;
4
5use crate::command::{FfmpegBinaryPaths, FfmpegCommand};
6use crate::config::FfmpegLocator;
7use crate::error::Result;
8use crate::types::Time;
9
10#[derive(Clone, Debug)]
12pub enum ThumbnailFormat {
13 Png,
15 Jpeg,
17}
18
19impl ThumbnailFormat {
20 fn extension(&self) -> &'static str {
21 match self {
22 ThumbnailFormat::Png => "png",
23 ThumbnailFormat::Jpeg => "jpg",
24 }
25 }
26
27 fn ffmpeg_args(&self) -> &'static [&'static str] {
28 match self {
29 ThumbnailFormat::Png => &["-f", "image2"],
30 ThumbnailFormat::Jpeg => &["-f", "mjpeg"],
31 }
32 }
33}
34
35#[derive(Clone, Debug)]
37pub struct ThumbnailOptions {
38 time: Time,
39 width: Option<u32>,
40 height: Option<u32>,
41 format: ThumbnailFormat,
42}
43
44impl ThumbnailOptions {
45 pub fn new(time: Time) -> Self {
47 Self {
48 time,
49 width: None,
50 height: None,
51 format: ThumbnailFormat::Png,
52 }
53 }
54
55 pub fn time(&self) -> Time {
57 self.time
58 }
59
60 pub fn size(mut self, width: u32, height: u32) -> Self {
62 self.width = Some(width);
63 self.height = Some(height);
64 self
65 }
66
67 pub fn dimensions(&self) -> Option<(u32, u32)> {
69 match (self.width, self.height) {
70 (Some(w), Some(h)) => Some((w, h)),
71 _ => None,
72 }
73 }
74
75 pub fn format(mut self, format: ThumbnailFormat) -> Self {
77 self.format = format;
78 self
79 }
80
81 pub fn output_format(&self) -> &ThumbnailFormat {
83 &self.format
84 }
85}
86
87pub fn generate(
89 input: impl AsRef<Path>,
90 output: impl AsRef<Path>,
91 options: &ThumbnailOptions,
92) -> Result<()> {
93 let locator = FfmpegLocator::system()?;
94 generate_with_binaries(locator.binaries(), input, output, options)
95}
96
97pub fn generate_with_binaries(
99 binaries: &FfmpegBinaryPaths,
100 input: impl AsRef<Path>,
101 output: impl AsRef<Path>,
102 options: &ThumbnailOptions,
103) -> Result<()> {
104 if let Some(parent) = output.as_ref().parent() {
105 std::fs::create_dir_all(parent)?;
106 }
107
108 let mut cmd = FfmpegCommand::new(binaries.ffmpeg());
109 cmd.arg("-y");
110 cmd.arg("-ss").arg(options.time.to_ffmpeg_timestamp());
111 cmd.arg("-i").arg(input.as_ref());
112 cmd.arg("-vframes").arg("1");
113
114 if let (Some(width), Some(height)) = (options.width, options.height) {
115 cmd.arg("-vf").arg(format!("scale={width}:{height}"));
116 }
117
118 for arg in options.format.ffmpeg_args() {
119 cmd.arg(arg);
120 }
121
122 let mut output_path = output.as_ref().to_path_buf();
123 if output_path.extension().is_none() {
124 output_path.set_extension(options.format.extension());
125 }
126 cmd.arg(output_path);
127 cmd.run()
128}