This repository has no description
1use crate::{
2 Video,
3 ui::{Log, Pretty},
4 video::{encoders::vgv::VGVTranscodeMode, engine::EngineOutput},
5};
6use anyhow::Result;
7use std::path::PathBuf;
8
9pub mod ffmpeg;
10pub mod vgv;
11
12pub trait Encoder {
13 fn name(&self) -> String;
14 fn encode_frames(
15 &mut self,
16 outputs: Vec<EngineOutput>,
17 ) -> Result<std::ops::ControlFlow<()>>;
18 fn finish(&mut self) -> Result<()>;
19 fn finish_message(&self, time_elapsed: std::time::Duration) -> String;
20 fn progress_message(&self, current: u64, total: u64) -> String {
21 format!("{}/{} frames", current, total,)
22 }
23}
24
25impl<C: Default> Video<C> {
26 pub(crate) fn setup_encoder(
27 &mut self,
28 output_path: impl Into<PathBuf>,
29 ) -> Result<Box<dyn Encoder + Send>> {
30 let (width, height) =
31 self.initial_canvas.resolution_to_size_even(self.resolution);
32
33 let destination = output_path.into();
34 let pb = &self.progress_bars.encoding;
35
36 if destination.exists() {
37 std::fs::remove_file(&destination)?;
38 }
39
40 std::fs::create_dir_all(
41 &destination
42 .parent()
43 .expect("Given output file has no parent"),
44 )?;
45
46 Ok(match destination.full_extension() {
47 ".vgv.html" => {
48 self.progress_bars.encoding.log(
49 "Selecting",
50 &format!(
51 "VGV encoder with HTML transcoding as {} ends with .vgv.html",
52 destination.pretty(),
53 ),
54 );
55
56 Box::new(self.setup_vgv_encoder(
57 VGVTranscodeMode::ToHTML,
58 width as _,
59 height as _,
60 &self.initial_canvas,
61 destination,
62 )?)
63 }
64 ".vgv" => {
65 self.progress_bars.encoding.log(
66 "Selecting",
67 &format!(
68 "VGV encoder as {} ends with .vgv (use .vgv.html for HTML transcoding)",
69 destination.pretty(),
70 ),
71 );
72
73 Box::new(self.setup_vgv_encoder(
74 VGVTranscodeMode::None,
75 width as _,
76 height as _,
77 &self.initial_canvas,
78 destination,
79 )?)
80 }
81 _ => {
82 pb.log(
83 "Selecting",
84 &format!(
85 "FFMpeg encoder as {} ends with {}",
86 destination.pretty(),
87 destination.full_extension()
88 ),
89 );
90
91 self.initial_canvas.load_fonts()?;
92 Box::new(self.setup_ffmpeg_encoder(width, height, destination)?)
93 }
94 })
95 }
96}
97
98// Because .extension() sucks
99
100trait FullExtension {
101 fn full_extension(&self) -> &str;
102}
103
104impl FullExtension for PathBuf {
105 fn full_extension(&self) -> &str {
106 let filename = self
107 .file_name()
108 .and_then(|f| f.to_str())
109 .unwrap_or_default();
110 let parts: Vec<&str> = filename.split('.').collect();
111 if parts.len() <= 1 {
112 ""
113 } else {
114 &filename[filename.find('.').unwrap()..]
115 }
116 }
117}