this repo has no description
at main 142 lines 3.6 kB view raw
1use std::{ 2 fmt::{Debug, Display}, 3 path::PathBuf, 4 process::Command, 5 str::FromStr, 6}; 7 8use clap::Parser; 9 10#[derive(Debug)] 11#[non_exhaustive] 12enum ParamParseError { 13 AudioOption, 14} 15impl Display for ParamParseError { 16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 17 <Self as Debug>::fmt(self, f) 18 } 19} 20#[derive(Debug, Clone, Copy, clap::ValueEnum)] 21enum AudioOption { 22 Copy, 23 None, 24} 25 26impl FromStr for AudioOption { 27 type Err = ParamParseError; 28 29 fn from_str(s: &str) -> Result<Self, Self::Err> { 30 match s { 31 "c" | "none" | "None" => Ok(AudioOption::None), 32 "n" | "copy" | "Copy" => Ok(AudioOption::Copy), 33 _ => Err(ParamParseError::AudioOption), 34 } 35 } 36} 37 38#[derive(Debug, clap::Parser)] 39struct Params { 40 #[clap(short, long)] 41 output: PathBuf, 42 #[clap(short, long)] 43 input: PathBuf, 44 #[clap(short, long, default_value = "10.0")] 45 target: f32, 46 #[clap(short, long)] 47 scale: Option<f32>, 48 #[clap(short, long)] 49 framerate: Option<f32>, 50 #[clap(short, long, default_value = "copy")] 51 audio: AudioOption, 52} 53fn main() { 54 let params = Params::parse(); 55 let mut pass0 = Command::new("./ffprobe.exe"); 56 pass0.args( 57 [ 58 "-v", 59 "error", 60 "-hide_banner", 61 "-select_streams", 62 "v:0", 63 "-show_entries", 64 "format=duration", 65 "-of", 66 "default=noprint_wrappers=1:nokey=1", 67 "-i", 68 params.input.to_str().unwrap(), 69 ] 70 .iter(), 71 ); 72 let bitrate = pass0 73 .output() 74 .map(|o| { 75 let mut s = String::from_utf8(o.stdout).unwrap(); 76 let duration: f32 = s.trim().parse().expect("Couldn't parse ffprobe output"); 77 let bitrate = params.target * 1024.0 * 1024.0 * 8.0 / duration / 1000.0; 78 s = bitrate.to_string(); 79 s.push('k'); 80 s 81 }) 82 .expect("Couldn't run ffprobe"); 83 84 let mut args = vec![ 85 "-v", 86 "warning", 87 "-y", 88 "-i", 89 params.input.to_str().unwrap(), 90 "-c:v", 91 "libx264", 92 "-preset", 93 "medium", 94 "-b:v", 95 &bitrate, 96 "-f", 97 "mp4", 98 ]; 99 100 let mut scaling = String::from("scale=-1:"); 101 if let Some(scale) = params.scale { 102 scaling.push_str(&scale.to_string()); 103 args.extend(["-vf", &scaling].iter()); 104 } 105 106 match params.audio { 107 AudioOption::Copy => { 108 args.extend(["-c:a", "copy"].iter()); 109 } 110 AudioOption::None => { 111 args.push("-an"); 112 } 113 } 114 let fps: String; 115 if let Some(framerate) = params.framerate { 116 fps = framerate.to_string(); 117 args.extend(["-r", &fps].iter()); 118 } 119 let mut pass1 = Command::new("./ffmpeg.exe"); 120 pass1.args(args.iter()).arg("-pass").arg("1").arg("./junk"); 121 dbg!(pass1) 122 .spawn() 123 .expect("Couldn't run ffmpeg") 124 .wait() 125 .expect("Ffmpeg exited unexpectedly"); 126 127 let mut pass2 = Command::new("./ffmpeg.exe"); 128 pass2 129 .args(args) 130 .arg("-pass") 131 .arg("2") 132 .arg(params.output.to_str().unwrap()); 133 134 pass2 135 .spawn() 136 .expect("Couldn't run ffmpeg") 137 .wait() 138 .expect("Ffmpeg exited unexpectedly"); 139 for file in ["junk", "ffmpeg2pass-0.log", "ffmpeg2pass-0.log.mbtree"] { 140 std::fs::remove_file(file).expect("Failed to cleanup temporary files"); 141 } 142}