Spaces:
Sleeping
Sleeping
| """Subtitle generation: timed segments -> SRT/VTT text. | |
| Works against anything exposing `.segments` of objects with start/end/text | |
| (a Transcript or a Translation), keeping with the rule that subtitles are | |
| always derived data and never re-recorded from audio. | |
| """ | |
| from __future__ import annotations | |
| from typing import Iterable, Protocol | |
| class HasTimedText(Protocol): | |
| start: float | |
| end: float | |
| text: str | |
| def _format_srt_timestamp(seconds: float) -> str: | |
| seconds = max(0.0, seconds) | |
| hours = int(seconds // 3600) | |
| minutes = int((seconds % 3600) // 60) | |
| secs = int(seconds % 60) | |
| millis = int(round((seconds - int(seconds)) * 1000)) | |
| return f"{hours:02}:{minutes:02}:{secs:02},{millis:03}" | |
| def _format_vtt_timestamp(seconds: float) -> str: | |
| seconds = max(0.0, seconds) | |
| hours = int(seconds // 3600) | |
| minutes = int((seconds % 3600) // 60) | |
| secs = int(seconds % 60) | |
| millis = int(round((seconds - int(seconds)) * 1000)) | |
| return f"{hours:02}:{minutes:02}:{secs:02}.{millis:03}" | |
| def generate_srt(segments: Iterable[HasTimedText]) -> str: | |
| lines = [] | |
| for idx, segment in enumerate(segments, start=1): | |
| start = _format_srt_timestamp(segment.start) | |
| end = _format_srt_timestamp(segment.end) | |
| lines.append(f"{idx}\n{start} --> {end}\n{segment.text.strip()}\n") | |
| return "\n".join(lines) | |
| def generate_vtt(segments: Iterable[HasTimedText]) -> str: | |
| lines = ["WEBVTT\n"] | |
| for segment in segments: | |
| start = _format_vtt_timestamp(segment.start) | |
| end = _format_vtt_timestamp(segment.end) | |
| lines.append(f"{start} --> {end}\n{segment.text.strip()}\n") | |
| return "\n".join(lines) | |