Skip to main content

ogg_batch_speedup/
lib.rs

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
9/// Process all audio files in the specified folder recursively with the given speed multiplier.
10///
11/// # Arguments
12///
13/// * `folder` - Path to the folder containing audio files
14/// * `speed` - Speed multiplier (e.g., 1.5 for 1.5x speed)
15///
16/// # Returns
17///
18/// * `Result<()>` - Ok(()) if successful, or an error if processing fails
19///
20/// # Example
21///
22/// ```no_run
23/// use std::path::Path;
24/// use ogg_batch_speedup::process_audio_files;
25///
26/// let folder = Path::new("path/to/audio/files");
27/// let speed = 1.5;
28/// process_audio_files(folder, speed).unwrap();
29/// ```
30pub fn process_audio_files(folder: impl AsRef<Path>, speed: f32) -> std::io::Result<()> {
31    let folder = folder.as_ref();
32
33    // Collect all files that need to be processed
34    let files: Vec<_> = WalkDir::new(folder)
35        .into_iter()
36        .filter_map(|e| e.ok())
37        .collect();
38
39    // Process all files in parallel
40    files.par_iter().try_for_each(|entry| {
41        let path = entry.path();
42        if !path.is_file() {
43            return Ok(());
44        }
45        // Check if file header indicates OGG file
46        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}