This commit is contained in:
Andras Schmelczer 2026-06-22 22:04:20 +01:00
parent 6c6780fc60
commit f7e0814a38
8 changed files with 206 additions and 92 deletions

View file

@ -35,3 +35,38 @@ class NarrationLog {
}
export const narrationLog = new NarrationLog();
function formatVttTimestamp(ms: number): string {
const clamped = Math.max(0, Math.round(ms));
const totalSeconds = Math.floor(clamped / 1000);
const millis = clamped % 1000;
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
const pad = (n: number, width: number): string =>
n.toString().padStart(width, '0');
return `${pad(hours, 2)}:${pad(minutes, 2)}:${pad(seconds, 2)}.${pad(millis, 3)}`;
}
/**
* Render narration cues as a WebVTT document.
*
* Pure helper: takes cues (already ordered by the caller) and produces a
* caption sidecar string. Cue text is trimmed and any internal blank lines
* are collapsed so a single cue stays a single VTT block.
*/
export function cuesToVtt(cues: NarrationCue[]): string {
let out = 'WEBVTT\n\n';
for (const cue of cues) {
const start = formatVttTimestamp(cue.videoTimeMs);
const end = formatVttTimestamp(cue.videoTimeMs + cue.durationMs);
const text = cue.text
.split('\n')
.map((line) => line.trim())
.filter((line) => line.length > 0)
.join('\n')
.trim();
out += `${start} --> ${end}\n${text}\n\n`;
}
return out;
}