use super::prompt::{start_background_prompt, PromptResult};
use super::recorder::{PostProcessConfig, RecordingResult};
use crate::cli::utils::target_file;
use crate::cli::utils::{print_tree_list, HumanReadable};
use crate::core::generators::{check_for_mp4, generate_gif, generate_mp4};
use crate::core::post_processing::{
post_process_effects, post_process_screenshots, PostProcessingOptions,
};
use crate::core::utils::{DEFAULT_EXT, MOVIE_EXT};
use crate::Result;
use std::borrow::Borrow;
use std::path::PathBuf;
use std::time::{Duration, Instant};
pub struct OutputConfig {
pub output_path: PathBuf,
pub generate_gif: bool,
pub generate_video: bool,
pub quiet: bool,
pub post_process: PostProcessConfig,
}
pub struct OutputGenerator {
result: RecordingResult,
config: OutputConfig,
}
impl OutputGenerator {
pub fn new(result: RecordingResult, config: OutputConfig) -> Self {
Self { result, config }
}
pub fn process(self) -> Result<Duration> {
println!();
println!("🎆 Applying effects (might take a bit)");
super::tips::show_tip();
let post_opts = self.build_post_processing_options();
self.apply_effects(&post_opts);
let target = target_file(self.config.output_path.to_str().unwrap_or("t-rec"));
self.process_screenshots(&target, &post_opts);
println!();
let mut total_time = Duration::default();
let video_prompt = if !self.config.generate_video && !self.config.quiet {
start_background_prompt("🎬 Also generate MP4 video?", 15)
} else {
None
};
if self.config.generate_gif {
let start = Instant::now();
generate_gif(
&self.result.time_codes.lock().unwrap(),
self.result.tempdir.lock().unwrap().borrow(),
&format!("{}.{}", target, DEFAULT_EXT),
Some(self.config.post_process.start_delay).filter(|d| !d.is_zero()),
Some(self.config.post_process.end_delay).filter(|d| !d.is_zero()),
)?;
total_time += start.elapsed();
}
let should_generate_video = if self.config.generate_video {
true
} else if let Some(prompt) = video_prompt {
match prompt.wait() {
PromptResult::Yes => {
check_for_mp4()?;
true
}
PromptResult::No | PromptResult::Timeout => false,
}
} else {
false
};
if should_generate_video {
let start = Instant::now();
generate_mp4(
&self.result.time_codes.lock().unwrap(),
self.result.tempdir.lock().unwrap().borrow(),
&format!("{}.{}", target, MOVIE_EXT),
self.config.post_process.fps,
)?;
total_time += start.elapsed();
}
println!("Time: {}", total_time.as_human_readable());
Ok(total_time)
}
fn build_post_processing_options(&self) -> PostProcessingOptions<'_> {
if let Some((ref wallpaper, padding)) = self.config.post_process.wallpaper {
PostProcessingOptions::new(
self.config.post_process.decor,
&self.config.post_process.bg_color,
)
.with_wallpaper(wallpaper, padding)
} else {
PostProcessingOptions::new(
self.config.post_process.decor,
&self.config.post_process.bg_color,
)
}
}
fn apply_effects(&self, opts: &PostProcessingOptions) {
let temp_path = self.result.tempdir.lock().unwrap().path().to_path_buf();
let codes = self.result.time_codes.lock().unwrap();
let frame_files: Vec<_> = codes
.iter()
.map(|tc| {
temp_path.join(crate::core::utils::file_name_for(
tc,
crate::core::utils::IMG_EXT,
))
})
.collect();
post_process_effects(&frame_files, opts);
}
fn process_screenshots(&self, target: &str, opts: &PostProcessingOptions) {
if !self.result.screenshots.is_empty() {
println!();
println!(
"📸 Processing {} screenshot(s)...",
self.result.screenshots.len()
);
let saved_screenshots =
post_process_screenshots(&self.result.screenshots, target, opts);
if !saved_screenshots.is_empty() {
println!(" Screenshots saved:");
print_tree_list(&saved_screenshots);
}
}
}
}