Skip to content

transcribe never assigns word id on the JSON path, so per-word caption overrides have nothing to key on #3442

Description

@rnstev26

Summary

media-use/audio/references/transcribe.md:43 states the word id (w0, w1, …) "is added during normalization for stable references in caption overrides."

For the JSON transcripts the CLI itself produces, it never is. Per-word caption overrides have nothing to key on.

Environment

  • hyperframes 0.8.10, installed globally via npm
  • macOS 15, Apple M5 Pro
  • whisper-cpp small.en via Homebrew whisper-cli
  • Independently reproduced on WSL2 / Ubuntu

Measured

hyperframes transcribe probe.wav --model small.en on 1.7 s of speech:

[
  { "text": "The",         "start": 0.01, "end": 0.16 },
  { "text": "pipeline",    "start": 0.16, "end": 0.6  },
  { "text": "is",          "start": 0.6,  "end": 0.68 },
  { "text": "proven",      "start": 0.82, "end": 1.04 },
  { "text": "end-to-end.", "start": 1.04, "end": 1.74 }
]

Zero id fields across all 5 words.

Where it is and is not assigned

loadTranscript (dist/cli.js:97787) branches on file type:

input id assigned?
.srt yes — id: w.id ?? "w" + i
.vtt yes — id: w.id ?? "w" + i
JSON, whisper-cpp shape noparseWhisperCpp emits {text,start,end} only
JSON, openai shape noparseOpenAI emits {text,start,end} only
JSON, flat array id: w.id ?? "" — empty string, not "w" + i

The flat-array branch is the one that looks like the documented normalization and is not: a missing id becomes "", so every word ends up sharing the same empty key.

Since the branch is chosen by file format rather than by caller, going through the skill-driven flow instead of the CLI does not change the result — it is the same loadTranscript.

Workaround (verified, no code change)

Round-trip through SRT with --preserve-cues on both legs:

hyperframes transcribe audio.wav --model small.en                          # -> transcript.json, no ids
hyperframes transcribe transcript.json --to srt -o w.srt --preserve-cues
hyperframes transcribe w.srt --preserve-cues                               # -> transcript.json WITH ids

Result: 5 entries, word-level timings preserved, id w0w4 present.

--preserve-cues is required on the export leg too. Without it the export groups all 5 words into a single cue, and the word granularity is gone before the id assignment ever runs — the re-import then yields one entry, id: "w0", text "The pipeline is proven end-to-end.".

The CLI help describes --preserve-cues purely in import terms ("Keep each transcript entry as its own caption cue"), which gives no hint that it is load-bearing on export.

Suggested fixes

  1. Assign "w" + i in parseWhisperCpp / parseOpenAI, or in the flat-array branch of loadTranscript instead of defaulting to "".
  2. Correct transcribe.md:43 — as written it promises ids on the path that never produces them.
  3. Document --preserve-cues as applying to export as well as import.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions