diff --git a/packages/cli/src/whisper/normalize.test.ts b/packages/cli/src/whisper/normalize.test.ts index 8ee305a615..9391eabbbe 100644 --- a/packages/cli/src/whisper/normalize.test.ts +++ b/packages/cli/src/whisper/normalize.test.ts @@ -103,8 +103,8 @@ describe("loadTranscript", () => { const { words, format } = loadTranscript(path); expect(format).toBe("whisper-cpp"); expect(words).toEqual([ - { text: "Hello,", start: 0, end: 0.55 }, - { text: "world.", start: 0.6, end: 1.25 }, + { text: "Hello,", start: 0, end: 0.55, id: "w0" }, + { text: "world.", start: 0.6, end: 1.25, id: "w1" }, ]); }); @@ -142,8 +142,8 @@ describe("loadTranscript", () => { const { words, format } = loadTranscript(path); expect(format).toBe("openai"); expect(words).toEqual([ - { text: "Hello", start: 0, end: 0.5 }, - { text: "world", start: 0.6, end: 1.2 }, + { text: "Hello", start: 0, end: 0.5, id: "w0" }, + { text: "world", start: 0.6, end: 1.2, id: "w1" }, ]); }); @@ -205,7 +205,7 @@ Short format expect(words[0]?.text).toBe("Bold and italic"); }); - it("passes through normalized word arrays", () => { + it("assigns w{index} ids to normalized word arrays", () => { const input = [ { text: "Hello", start: 0.0, end: 0.5 }, { text: "world", start: 0.6, end: 1.2 }, @@ -214,10 +214,21 @@ Short format const { words, format } = loadTranscript(path); expect(format).toBe("words-json"); expect(words).toEqual([ - { text: "Hello", start: 0, end: 0.5, id: "" }, - { text: "world", start: 0.6, end: 1.2, id: "" }, + { text: "Hello", start: 0, end: 0.5, id: "w0" }, + { text: "world", start: 0.6, end: 1.2, id: "w1" }, ]); }); + + it("preserves existing ids and repairs empty-string ids from legacy files", () => { + const input = [ + { text: "Hello", start: 0.0, end: 0.5, id: "keep-me" }, + { text: "world", start: 0.6, end: 1.2, id: "" }, + { text: "again", start: 1.3, end: 1.8 }, + ]; + const path = tmpFile("legacy.json", JSON.stringify(input)); + const { words } = loadTranscript(path); + expect(words.map((w) => w.id)).toEqual(["keep-me", "w1", "w2"]); + }); }); describe("caption formatting", () => { @@ -328,9 +339,9 @@ describe("whisper-cpp contraction merging", () => { ); const { words } = loadTranscript(path); expect(words).toEqual([ - { text: "I", start: 0, end: 0.2 }, - { text: "didn't", start: 0.2, end: 0.7 }, - { text: "know", start: 0.7, end: 1 }, + { text: "I", start: 0, end: 0.2, id: "w0" }, + { text: "didn't", start: 0.2, end: 0.7, id: "w1" }, + { text: "know", start: 0.7, end: 1, id: "w2" }, ]); }); diff --git a/packages/cli/src/whisper/normalize.ts b/packages/cli/src/whisper/normalize.ts index 2599d8ab9b..9cdb601e7f 100644 --- a/packages/cli/src/whisper/normalize.ts +++ b/packages/cli/src/whisper/normalize.ts @@ -462,17 +462,21 @@ export function loadTranscript(filePath: string): { words: Word[]; format: Trans const parsed = JSON.parse(content); const format = detectJsonFormat(parsed); - const words = + // JSON parsers never set id — assign w{index} like the srt/vtt branches above + // so caption overrides always have a stable key. `||` (not `??`) also repairs + // the empty-string ids older CLIs wrote to words-json transcript files. + const words = ( format === "whisper-cpp" ? parseWhisperCpp(parsed) : format === "openai" ? parseOpenAI(parsed) : (parsed as Word[]).map((w) => ({ - id: w.id ?? "", + id: w.id, text: w.text.trim(), start: round3(w.start), end: round3(w.end), - })); + })) + ).map((w, i) => ({ ...w, id: w.id || `w${i}` })); return { words, format }; }