From ba8d65d780610b084cc5895c136ff5ed0067d3c7 Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Thu, 8 Oct 2026 18:10:49 +0800 Subject: [PATCH] fix(cli): retain WebVTT timestamps before cue settings --- packages/cli/src/whisper/normalize.test.ts | 18 ++++++++++++++++++ packages/cli/src/whisper/normalize.ts | 5 +++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/whisper/normalize.test.ts b/packages/cli/src/whisper/normalize.test.ts index 969bbff3b0a..7aa187fc800 100644 --- a/packages/cli/src/whisper/normalize.test.ts +++ b/packages/cli/src/whisper/normalize.test.ts @@ -83,6 +83,24 @@ describe("detectFormat", () => { }); describe("loadTranscript", () => { + it.each([ + ["00:00:01.000", "00:00:03.500", "align:start"], + ["00:01.000", "00:03.500", "align:start"], + ["00:00:01.000", "00:00:03.500", "line:90% position:50%,center size:80% align:center"], + ["00:01.000", "00:03.500", "\tvertical:rl\tline:0"], + ["00:00:01.000", "00:00:03.500", "region:captions"], + ])("retains VTT end time before cue settings: %s --> %s %s", (start, end, settings) => { + const source = `WEBVTT\n\nfirst\n${start} --> ${end} ${settings}\nFirst phrase\n\nsecond\n00:04.000 --> 00:06.000\nSecond phrase\n`; + const { words } = loadTranscript(tmpFile("settings.vtt", source)); + expect(words).toEqual([ + { text: "First phrase", start: 1, end: 3.5, id: "w0" }, + { text: "Second phrase", start: 4, end: 6, id: "w1" }, + ]); + expect(formatSrt(words, { preGrouped: true })).toBe( + "1\n00:00:01,000 --> 00:00:03,500\nFirst phrase\n\n2\n00:00:04,000 --> 00:00:06,000\nSecond phrase\n", + ); + }); + it("reads an empty word list as a transcript with no words", () => { expect(loadTranscript(tmpFile("transcript.json", "[]"))).toEqual({ words: [], diff --git a/packages/cli/src/whisper/normalize.ts b/packages/cli/src/whisper/normalize.ts index 6683d634af2..9ddbc9ebbe7 100644 --- a/packages/cli/src/whisper/normalize.ts +++ b/packages/cli/src/whisper/normalize.ts @@ -266,8 +266,9 @@ function parseSrtTimestamp(ts: string): number { /** Parse VTT timestamp: 00:01:23.456 or 01:23.456 → seconds */ function parseVttTimestamp(ts: string): number { - const parts = ts.split(":"); - if (parts.length === 3) return parseSrtTimestamp(ts); + const timestamp = ts.split(/[ \t]+/, 1)[0] ?? ""; + const parts = timestamp.split(":"); + if (parts.length === 3) return parseSrtTimestamp(timestamp); // MM:SS.mmm if (parts.length === 2) { const [min, secMs] = parts;