this repo has no description
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}