use std::path::Path;
use std::process::{Command, Stdio};
use std::env;
use std::fs::File;
use std::io::Write;
use std::process;
use rust_readmdict::readmdict::Mdd;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
eprintln!("Usage: cargo run --example play_audio <mdd_file>");
eprintln!("Play audio files from MDD files");
process::exit(1);
}
let mdd_file = &args[1];
if !Path::new(mdd_file).exists() {
eprintln!("Error: MDD file '{}' not found", mdd_file);
process::exit(1);
}
match load_and_play_audio(mdd_file) {
Ok(_) => {},
Err(e) => {
eprintln!("Error: {}", e);
if e.to_string().to_lowercase().contains("lzo") {
eprintln!("\nThis error is likely related to missing LZO compression support.");
eprintln!("Please install the LZO library.");
}
process::exit(1);
}
}
}
fn load_and_play_audio(mdd_file: &str) -> Result<(), Box<dyn std::error::Error>> {
println!("Loading MDD file: {}", mdd_file);
let mdd = Mdd::new(mdd_file, None)?;
let mut audio_files = Vec::new();
let mdd_items = mdd.items()?;
for (filename, content) in mdd_items {
let filename_str = String::from_utf8_lossy(&filename);
let filename_lower = filename_str.to_lowercase();
if filename_lower.ends_with(".mp3") ||
filename_lower.ends_with(".wav") ||
filename_lower.ends_with(".ogg") ||
filename_lower.ends_with(".m4a") {
audio_files.push((filename_str.to_string(), content));
}
}
println!("Found {} audio files", audio_files.len());
if audio_files.is_empty() {
println!("No audio files found in the MDD file.");
return Ok(());
}
let files_to_play = &audio_files[..audio_files.len().min(10)];
println!("\nPlaying {} audio files sequentially...", files_to_play.len());
for (i, (filename, content)) in files_to_play.iter().enumerate() {
println!("\n[{}/{}] {} ({} bytes)", i + 1, files_to_play.len(), filename, content.len());
match play_audio_file(content, filename) {
Ok(_) => println!("Successfully played {}", filename),
Err(e) => {
eprintln!("Failed to play {}: {}", filename, e);
}
}
}
println!("\nFinished playing all audio files.");
Ok(())
}
fn play_audio_file(audio_data: &[u8], filename: &str) -> Result<(), String> {
use std::env;
let temp_path = create_temp_audio_file(audio_data, filename)?;
println!("Playing {}...", filename);
let result = match env::consts::OS {
"macos" => {
Command::new("afplay")
.arg(&temp_path)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map_err(|e| format!("Failed to run afplay: {}", e))
},
"linux" => {
let players = ["mpg123", "mpv", "vlc", "mplayer"];
let mut last_error = None;
for player in &players {
match Command::new(player)
.arg(&temp_path)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
{
Ok(status) if status.success() => {
last_error = None;
break;
}
Ok(_) => {
last_error = Some(format!("Player {} exited with non-zero status", player));
}
Err(_) => {
last_error = Some(format!("Player {} not found", player));
}
}
}
match last_error {
None => return Ok(()),
Some(_) => return Err(format!("No suitable audio player found. Tried: {}", players.join(", ")))
}
},
"windows" => {
Command::new("cmd")
.args(&["/C", "start", ""])
.arg(&temp_path)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map_err(|e| format!("Failed to start audio player: {}", e))
},
_ => {
Err(format!("Unsupported platform: {}", env::consts::OS))
}
};
let _ = std::fs::remove_file(&temp_path);
result.map(|_| ()).map_err(|e| e)
}
fn create_temp_audio_file(audio_data: &[u8], filename: &str) -> Result<String, String> {
use std::env;
let extension = Path::new(filename)
.extension()
.and_then(|ext| ext.to_str())
.unwrap_or("mp3");
let temp_dir = env::temp_dir();
let temp_filename = format!("rust_audio_{}.{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos(),
extension);
let temp_path = temp_dir.join(temp_filename);
let mut file = File::create(&temp_path)
.map_err(|e| format!("Failed to create temporary file: {}", e))?;
file.write_all(audio_data)
.map_err(|e| format!("Failed to write audio data: {}", e))?;
Ok(temp_path.to_string_lossy().to_string())
}