1use std::{
4 fs,
5 path::Path,
6 process::Command,
7 sync::atomic::{AtomicBool, Ordering},
8 sync::Arc,
9 time::Duration,
10};
11
12use super::Result;
13
14pub fn extract_audio(
16 input: &Path,
17 output: &Path,
18 running: &Arc<AtomicBool>,
19) -> Result<()> {
20 let input = input.to_str().ok_or("Input invalid Unicode")?;
22 let output = output.to_str().ok_or("Output invalid Unicode")?;
23 let output = Command::new("ffmpeg")
25 .args(&[
26 "-y", "-i", input, "-vn", "-c:a", "aac", "-b:a", "192k", output,
27 ])
28 .output()?;
29 if !output.status.success() && running.load(Ordering::SeqCst) {
30 return Err("Failed extracting audio".into());
31 }
32
33 Ok(())
34}
35
36pub fn split_video(
38 input: &Path,
39 output_dir: &Path,
40 segment_length: Duration,
41 running: &Arc<AtomicBool>,
42) -> Result<()> {
43 let extension = input
45 .extension()
46 .ok_or("Unable to find extension")?
47 .to_str()
48 .ok_or("Unable to convert OsString extension")?;
49 let input = input.to_str().ok_or("Input invalid Unicode")?;
51 let mut output_dir = output_dir.to_path_buf();
52 output_dir.push(format!("chunk_%03d.{}", extension));
53 let output = output_dir.to_str().ok_or("Output invalid Unicode")?;
54 let output = Command::new("ffmpeg")
56 .args(&[
57 "-y",
58 "-i",
59 input,
60 "-an",
61 "-c",
62 "copy",
63 "-f",
64 "segment",
65 "-segment_time",
66 &segment_length.as_secs().to_string(),
67 output,
68 ])
69 .output()?;
70 if !output.status.success() && running.load(Ordering::SeqCst) {
71 return Err("Failed splitting video".into());
72 }
73
74 Ok(())
75}
76
77pub fn combine(
79 encoded_dir: &Path,
80 audio: &Path,
81 output: &Path,
82 running: &Arc<AtomicBool>,
83) -> Result<()> {
84 let mut chunks = fs::read_dir(&encoded_dir)?
86 .map(|res| res.and_then(|readdir| Ok(readdir.path())))
87 .map(|res| res.map_err(|e| e.into()))
88 .map(|res| res.and_then(|path| Ok(path.into_os_string())))
89 .map(|res| {
90 res.and_then(|os_string| {
91 os_string
92 .into_string()
93 .map_err(|_| "Failed OsString conversion".into())
94 })
95 })
96 .map(|res| res.and_then(|file| Ok(format!("file '{}'\n", file))))
97 .collect::<Result<Vec<String>>>()?;
98 chunks.sort();
100 let chunks = chunks.join("");
102 let mut file_list = encoded_dir.to_path_buf();
104 file_list.push("files.txt");
105 fs::write(&file_list, chunks)?;
106
107 let audio = audio.to_str().ok_or("Audio invalid Unicode")?;
109 let file_list = file_list.to_str().ok_or("File list invalid Unicode")?;
110 let output = output.to_str().ok_or("Output invalid Unicode")?;
111 let output = Command::new("ffmpeg")
113 .args(&[
114 "-y",
115 "-f",
116 "concat",
117 "-safe",
118 "0",
119 "-i",
120 file_list,
121 "-i",
122 audio,
123 "-c",
124 "copy",
125 "-movflags",
126 "+faststart",
127 output,
128 ])
129 .output()?;
130 if !output.status.success() && running.load(Ordering::SeqCst) {
131 return Err("Failed combining video".into());
132 }
133
134 Ok(())
135}