aether_renderer_core/ffmpeg/
gif.rs1use std::fs;
2use std::process::Command;
3
4pub fn render_gif(
6 input_pattern: &str,
7 output: &str,
8 fps: u32,
9 fade_filter: Option<&str>,
10 verbose_ffmpeg: bool,
11) -> Result<(), String> {
12 let palette_path = "palette.png";
13
14 let mut palette_args: Vec<String> = Vec::new();
15 if input_pattern.contains('*') {
16 palette_args.push("-pattern_type".into());
17 palette_args.push("glob".into());
18 }
19 palette_args.push("-i".into());
20 palette_args.push(input_pattern.into());
21 palette_args.push("-vf".into());
22 palette_args.push("fps=30,scale=640:-1:flags=lanczos,palettegen".into());
23 palette_args.push("-y".into());
24 palette_args.push(palette_path.into());
25
26 let palette_status = match {
27 let mut cmd = Command::new("ffmpeg");
28 if !verbose_ffmpeg {
29 cmd.args(["-loglevel", "warning"]);
30 }
31 cmd.args(&palette_args).status()
32 } {
33 Ok(s) => s,
34 Err(e) => {
35 if e.kind() == std::io::ErrorKind::NotFound {
36 return Err(
37 "❌ ffmpeg not found. Please install ffmpeg and ensure it is in your PATH."
38 .into(),
39 );
40 } else {
41 return Err(format!("❌ Failed to execute ffmpeg: {}", e));
42 }
43 }
44 };
45
46 if !palette_status.success() {
47 return Err("❌ Failed to generate palette".into());
48 }
49
50 let mut gif_filter = String::from("fps=30,scale=640:-1:flags=lanczos");
51 if let Some(filter) = fade_filter {
52 if !filter.is_empty() {
53 gif_filter.push(',');
54 gif_filter.push_str(filter);
55 }
56 }
57 let mut gif_args: Vec<String> = vec!["-framerate".into(), fps.to_string()];
58 if input_pattern.contains('*') {
59 gif_args.push("-pattern_type".into());
60 gif_args.push("glob".into());
61 }
62 gif_args.push("-i".into());
63 gif_args.push(input_pattern.into());
64 gif_args.push("-i".into());
65 gif_args.push(palette_path.into());
66 gif_args.push("-lavfi".into());
67 gif_args.push(format!("{} [x]; [x][1:v] paletteuse", gif_filter));
68 gif_args.push("-y".into());
69 gif_args.push(output.into());
70
71 let gif_status = match {
72 let mut cmd = Command::new("ffmpeg");
73 if !verbose_ffmpeg {
74 cmd.args(["-loglevel", "warning"]);
75 }
76 cmd.args(&gif_args).status()
77 } {
78 Ok(s) => s,
79 Err(e) => {
80 if e.kind() == std::io::ErrorKind::NotFound {
81 return Err(
82 "❌ ffmpeg not found. Please install ffmpeg and ensure it is in your PATH."
83 .into(),
84 );
85 } else {
86 return Err(format!("❌ Failed to execute ffmpeg: {}", e));
87 }
88 }
89 };
90
91 fs::remove_file(palette_path)
92 .unwrap_or_else(|e| eprintln!("⚠️ Failed to remove palette file: {}", e));
93
94 if gif_status.success() {
95 println!("✅ GIF exported: {}", output);
96 Ok(())
97 } else {
98 Err("❌ Failed to export GIF".into())
99 }
100}