use std::process::Command; use serde_json; #[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] pub struct TranscriptionResult { pub words: Vec, pub segments: Vec, pub language: String, } #[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] pub struct Word { pub word: String, pub start: f64, pub end: f64, pub confidence: f64, } #[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] pub struct Segment { pub id: usize, pub start: f64, pub end: f64, pub text: String, pub words: Vec, } /// Transcribe audio file using Python faster-whisper pub fn transcribe_audio( file_path: &str, model_name: &str, language: Option<&str>, ) -> Result { // Path to Python venv and script let python_exe = crate::paths::python_exe(); let python_exe = python_exe.to_str().unwrap_or_default(); let script_path = crate::paths::root_script("transcribe.py"); let script_path = script_path.to_str().unwrap_or_default(); // Build command args let mut args = vec![script_path, file_path, model_name]; if let Some(lang) = language { args.push(lang); } // Run Python script with timeout let output = Command::new(python_exe) .args(&args) .output() .map_err(|e| format!("Failed to run Python script: {}", e))?; // Check for timeout or other errors if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); let stdout = String::from_utf8_lossy(&output.stdout); return Err(format!("Python script failed: {}\nStdout: {}\nStderr: {}", output.status, stdout, stderr)); } // Parse JSON output let stdout = String::from_utf8_lossy(&output.stdout); let result: TranscriptionResult = serde_json::from_str(&stdout.trim()) .map_err(|e| format!("Failed to parse JSON: {}", e))?; Ok(result) } /// Ensure model is available (faster-whisper handles this automatically) pub fn ensure_model_downloaded(_model_name: &str) -> Result { // faster-whisper downloads models on first use, so just return success Ok("Model ready".to_string()) }