1use log::{debug, error, info};
2use rayon::prelude::*;
3use std::fs::File;
4use std::io::Read;
5use std::path::Path;
6use std::process::Command;
7use walkdir::WalkDir;
8
9pub fn process_audio_files(folder: impl AsRef<Path>, speed: f32) -> std::io::Result<()> {
31 let folder = folder.as_ref();
32
33 let files: Vec<_> = WalkDir::new(folder)
35 .into_iter()
36 .filter_map(|e| e.ok())
37 .collect();
38
39 files.par_iter().try_for_each(|entry| {
41 let path = entry.path();
42 if !path.is_file() {
43 return Ok(());
44 }
45 let mut file = File::open(path)?;
47 let mut header = [0u8; 4];
48 if let Err(e) = file.read_exact(&mut header) {
49 error!("Error reading file header: {}", e);
50 return Err(e);
51 }
52 if &header != b"OggS" {
53 debug!("Skipping non-ogg file: {}", path.display());
54 return Ok(());
55 }
56
57 let output_file = path.with_file_name(format!(
58 "temp_{}",
59 path.file_name().unwrap().to_str().unwrap()
60 ));
61
62 info!("Processing {}...", path.display());
63
64 let status = Command::new("ffmpeg")
65 .args([
66 "-i",
67 path.to_str().unwrap(),
68 "-filter:a",
69 &format!("atempo={}", speed),
70 "-vn",
71 output_file.to_str().unwrap(),
72 "-y",
73 "-loglevel",
74 "error",
75 ])
76 .status();
77
78 if let Err(e) = status {
79 error!("Error processing {}: {}", path.display(), e);
80 return Err(e);
81 }
82
83 if status.unwrap().success() {
84 std::fs::rename(&output_file, path)?;
85 } else {
86 if output_file.exists() {
87 std::fs::remove_file(output_file)?;
88 }
89 error!("Error processing {}", path.display());
90 }
91 Ok(())
92 })
93}