diff --git a/src/overlay/asr.rs b/src/overlay/asr.rs new file mode 100644 index 0000000..59fe268 --- /dev/null +++ b/src/overlay/asr.rs @@ -0,0 +1,59 @@ +//! ASR subtitle loader and manager + +use anyhow::{Context, Result}; +use serde::Deserialize; +use std::path::Path; + +#[derive(Debug, Clone, Deserialize)] +pub struct AsrSegment { + pub start: f64, + pub end: f64, + pub text: String, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct AsrData { + pub language: Option, + pub language_probability: Option, + pub segments: Vec, +} + +#[derive(Debug, Clone)] +pub struct AsrLoader { + data: AsrData, +} + +impl AsrLoader { + pub fn load(path: &Path) -> Result { + let content = std::fs::read_to_string(path) + .with_context(|| format!("Failed to read ASR file: {:?}", path))?; + + let data: AsrData = serde_json::from_str(&content) + .with_context(|| "Failed to parse ASR JSON")?; + + Ok(Self { data }) + } + + pub fn get_segment_at(&self, time_ms: f64) -> Option<&AsrSegment> { + let time_sec = time_ms / 1000.0; + self.data.segments.iter().find(|seg| { + time_sec >= seg.start && time_sec < seg.end + }) + } + + pub fn get_text_at(&self, time_ms: f64) -> Option { + self.get_segment_at(time_ms).map(|seg| seg.text.clone()) + } + + pub fn get_all_segments(&self) -> &[AsrSegment] { + &self.data.segments + } + + pub fn segment_count(&self) -> usize { + self.data.segments.len() + } + + pub fn duration(&self) -> f64 { + self.data.segments.last().map(|s| s.end).unwrap_or(0.0) + } +}