use serde_json::{json, Value}; use tauri::{AppHandle, Emitter}; use crate::sidecar::messages::IPCMessage; use crate::sidecar::sidecar; /// Start transcription of an audio file via the Python sidecar. #[tauri::command] pub fn transcribe_file( file_path: String, model: Option, device: Option, language: Option, ) -> Result { let manager = sidecar(); manager.ensure_running()?; let request_id = uuid::Uuid::new_v4().to_string(); let msg = IPCMessage::new( &request_id, "transcribe.start", json!({ "file": file_path, "model": model.unwrap_or_else(|| "base".to_string()), "device": device.unwrap_or_else(|| "cpu".to_string()), "compute_type": "int8", "language": language, }), ); let response = manager.send_and_receive(&msg)?; if response.msg_type == "error" { return Err(format!( "Transcription error: {}", response .payload .get("message") .and_then(|v| v.as_str()) .unwrap_or("unknown") )); } Ok(response.payload) } /// Download and validate the diarization model via the Python sidecar. #[tauri::command] pub fn download_diarize_model(hf_token: String) -> Result { let manager = sidecar(); manager.ensure_running()?; let request_id = uuid::Uuid::new_v4().to_string(); let msg = IPCMessage::new( &request_id, "diarize.download", json!({ "hf_token": hf_token, }), ); let response = manager.send_and_receive(&msg)?; if response.msg_type == "error" { return Ok(json!({ "ok": false, "error": response.payload.get("message").and_then(|v| v.as_str()).unwrap_or("unknown"), })); } Ok(json!({ "ok": true })) } /// Run the full transcription + diarization pipeline via the Python sidecar. #[tauri::command] pub async fn run_pipeline( app: AppHandle, file_path: String, model: Option, device: Option, language: Option, num_speakers: Option, min_speakers: Option, max_speakers: Option, skip_diarization: Option, hf_token: Option, ) -> Result { let manager = sidecar(); manager.ensure_running()?; let request_id = uuid::Uuid::new_v4().to_string(); let msg = IPCMessage::new( &request_id, "pipeline.start", json!({ "file": file_path, "model": model.unwrap_or_else(|| "base".to_string()), "device": device.unwrap_or_else(|| "cpu".to_string()), "compute_type": "int8", "language": language, "num_speakers": num_speakers, "min_speakers": min_speakers, "max_speakers": max_speakers, "skip_diarization": skip_diarization.unwrap_or(false), "hf_token": hf_token, }), ); // Run the blocking sidecar I/O on a separate thread so the async runtime // can deliver emitted events to the webview while processing is ongoing. let app_handle = app.clone(); tauri::async_runtime::spawn_blocking(move || { let response = manager.send_and_receive_with_progress(&msg, |msg| { let event_name = match msg.msg_type.as_str() { "pipeline.segment" => "pipeline-segment", "pipeline.speaker_update" => "pipeline-speaker-update", _ => "pipeline-progress", }; if let Err(e) = app_handle.emit(event_name, &msg.payload) { eprintln!("[sidecar-rs] Failed to emit {event_name}: {e}"); } })?; if response.msg_type == "error" { return Err(format!( "Pipeline error: {}", response .payload .get("message") .and_then(|v| v.as_str()) .unwrap_or("unknown") )); } Ok(response.payload) }) .await .map_err(|e| format!("Pipeline task failed: {e}"))? }