Spaces:
Sleeping
Sleeping
| """Transcription service: Audio -> canonical Transcript. | |
| This is the only place that runs Whisper. It always uses Whisper's | |
| "transcribe" task, never "translate" -- translation is handled as a | |
| separate, text-based step (see services/translation.py) operating on the | |
| Transcript this produces. That split is the core v1.0 architecture | |
| decision: there is exactly one path from audio to text, and everything | |
| else branches off the resulting Transcript. | |
| """ | |
| from __future__ import annotations | |
| from typing import Optional | |
| from faster_whisper import WhisperModel | |
| from models.transcript import Segment, Transcript | |
| # ISO 639-1 code -> display name. Used both for the "Source Language" | |
| # dropdown and for labelling detected languages in the results dashboard. | |
| SUPPORTED_LANGUAGES: dict[str, str] = { | |
| "fr": "French", | |
| "en": "English", | |
| "de": "German", | |
| "fa": "Persian", | |
| "es": "Spanish", | |
| } | |
| class TranscriptionService: | |
| """Thin wrapper around a faster-whisper model. | |
| Deliberately has no knowledge of translation, subtitles, or the UI -- | |
| it only knows how to turn audio into a Transcript. | |
| """ | |
| def __init__( | |
| self, | |
| model_size: str = "base", | |
| device: str = "cpu", | |
| compute_type: str = "int8", | |
| download_root: str = "/tmp/whisper_models", | |
| ) -> None: | |
| self._model = WhisperModel( | |
| model_size, | |
| device=device, | |
| compute_type=compute_type, | |
| download_root=download_root, | |
| ) | |
| def transcribe( | |
| self, | |
| audio_path: str, | |
| source_filename: str, | |
| language: Optional[str] = None, | |
| window_start: Optional[float] = None, | |
| window_end: Optional[float] = None, | |
| beam_size: int = 5, | |
| ) -> Transcript: | |
| """Run speech-to-text and return the canonical Transcript. | |
| `language` is an ISO 639-1 code, or None for auto-detect (the | |
| "Auto Detect" dropdown option). | |
| """ | |
| segments_iter, info = self._model.transcribe( | |
| audio_path, | |
| task="transcribe", | |
| language=language, | |
| beam_size=beam_size, | |
| ) | |
| segments = [ | |
| Segment(index=i, start=seg.start, end=seg.end, text=seg.text.strip()) | |
| for i, seg in enumerate(segments_iter, start=1) | |
| ] | |
| duration = segments[-1].end if segments else 0.0 | |
| return Transcript( | |
| source_filename=source_filename, | |
| language=info.language, | |
| language_probability=info.language_probability, | |
| duration=duration, | |
| segments=segments, | |
| window_start=window_start, | |
| window_end=window_end, | |
| ) | |