use crate::config::SpeechConfig;
use crate::feedback::FeedbackSink;
use crate::{init_all_models, RealTimeTranscriber, TranscriptionMessage};
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::broadcast;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Transcript {
pub text: String,
pub session_id: Option<String>,
pub model: String,
pub backend: crate::BackendType,
}
impl Transcript {
fn from_message(message: TranscriptionMessage, config: &SpeechConfig) -> Self {
Self {
text: message.text,
session_id: message.session_id,
model: config.general_config.model.clone(),
backend: config.backend_config.backend,
}
}
}
pub struct SpeechEngine {
transcriber: RealTimeTranscriber,
transcript_rx: broadcast::Receiver<TranscriptionMessage>,
config: SpeechConfig,
active_session_id: Option<String>,
transcript_timeout: Duration,
}
impl SpeechEngine {
pub async fn new(config: SpeechConfig) -> Result<Self, anyhow::Error> {
Self::with_feedback(config, None).await
}
pub async fn with_feedback(
mut config: SpeechConfig,
feedback_sink: Option<Arc<dyn FeedbackSink>>,
) -> Result<Self, anyhow::Error> {
config.general_config.transcription_mode = "manual".to_string();
let (model_path, _) = init_all_models(
Some(&config.general_config.model),
config.backend_config.backend,
&config.backend_config.quantization_level,
)
.await?;
Self::from_model_path(model_path, config, feedback_sink)
}
pub fn from_model_path(
model_path: PathBuf,
mut config: SpeechConfig,
feedback_sink: Option<Arc<dyn FeedbackSink>>,
) -> Result<Self, anyhow::Error> {
config.general_config.transcription_mode = "manual".to_string();
let mut transcriber = RealTimeTranscriber::new(model_path, config.clone(), feedback_sink)?;
let transcript_rx = transcriber.get_transcript_rx();
transcriber.start()?;
Ok(Self {
transcriber,
transcript_rx,
config,
active_session_id: None,
transcript_timeout: Duration::from_secs(60),
})
}
pub fn set_transcript_timeout(&mut self, timeout: Duration) {
self.transcript_timeout = timeout;
}
pub async fn start_session(&mut self) -> Result<String, anyhow::Error> {
let session_id = self.transcriber.start_manual_session().await?;
self.active_session_id = Some(session_id.clone());
Ok(session_id)
}
pub async fn stop_and_transcribe(&mut self) -> Result<Transcript, anyhow::Error> {
let expected_session_id = self.active_session_id.take();
self.transcriber.stop_manual_session().await?;
let timeout = self.transcript_timeout;
let config = self.config.clone();
let transcript_rx = &mut self.transcript_rx;
tokio::time::timeout(timeout, async move {
loop {
let message = transcript_rx.recv().await.map_err(|e| {
anyhow::anyhow!("Transcript channel closed before final transcript: {}", e)
})?;
if expected_session_id.is_none() || message.session_id == expected_session_id {
return Ok(Transcript::from_message(message, &config));
}
}
})
.await
.map_err(|_| anyhow::anyhow!("Timed out waiting for final transcript"))?
}
pub async fn cancel_session(&mut self) -> Result<(), anyhow::Error> {
self.active_session_id = None;
self.transcriber.cancel_manual_session().await
}
pub fn transcript_messages(&self) -> broadcast::Receiver<TranscriptionMessage> {
self.transcriber.get_transcript_rx()
}
pub fn transcriber(&self) -> &RealTimeTranscriber {
&self.transcriber
}
pub fn transcriber_mut(&mut self) -> &mut RealTimeTranscriber {
&mut self.transcriber
}
pub async fn shutdown(&mut self) -> Result<(), anyhow::Error> {
self.transcriber.shutdown().await
}
}