Repository navigation
feat(cli): let transcribe --to write srt or vtt straight from a media file - #5215
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
Edit accuracy: accurate 2061 (base branch 2061), smooth 1582 of thoseThe gate passes. Quarantined, measured but not gated (0) |
…transcript - Refuse an --output that resolves to the input file or the transcript.json the run writes, before the engine starts. - When the caption file cannot be written after transcribing, say the transcript was saved and name the caption path and reason, instead of reporting a failed transcription. - Docs: a one-run export groups cues for languages without spaces; exporting the saved transcript later may split them differently.
7b9aeb6 to
978fa25
Compare
- The overwrite check compares files, not path text: an existing target is matched by device and inode, a new one by its real folder and its name ignoring letter case. Links, folder aliases such as /var and /private/var, and other letter case are all refused. - The check also covers the default caption path, so exporting transcript.srt to srt no longer rewrites its own input. - A trailing -o with no path fails before transcribing. - One constant names transcript.json for the engines, the import and the check.
978fa25 to
d5ae7e5
Compare
- A caption path that is a link is followed to where the write would land, so a link to the transcript.json the run is about to create is refused before transcribing. - A test pins the folder letter-case check on a case-insensitive disk, and says which branch ran.
…writing it - Right before the caption write, by when the transcript exists, a destination that is the same file as the input or transcript.json is refused. The OS resolves every link, relative link and alias, so the hand-written link following is gone. - The run keeps the transcript and says the caption file could not be written and why. An export whose caption write fails now says so too, instead of crashing. - The check before transcribing stays as an early refusal for the common cases.
- Some network shares report inode 0 for every file; such a pair is compared by folder and name instead of being called the same file. The check before transcribing and the one at write time now share one comparison. - The link-through-a-file tests accept ENOENT as well as ENOTDIR, the code Windows reports.
f5ab35d to
7d63790
Compare
- When either file reports inode 0 and both exist, compare their fully resolved real paths, so a link to the media or the transcript is still refused. The folder and name compare stays for a path that does not exist yet. - Tests zero the inode on every path, on the caption path only, or on the protected file only.
somanshreddy
left a comment
There was a problem hiding this comment.
Approving at 5242dcbb. I found no blockers. It merges cleanly with main.
Checked:
- Flow:
--toon media now transcribes, writestranscript.json, then writes the sidecar.- Import and export share
writeCaptionSidecar. - Media words are passed as word-level (
phraseLevelSource=false), so CJK grouping follows the transcriber, not the spacing heuristic. --optionalskips stay exit 0 without a sidecar.
- Import and export share
- No-overwrite guard: two layers, both on the path that writes.
- An early
overwriteProblemrefuses before decoding. If a file doesn't exist yet, it compares the real parent folder plus the basename, case-insensitively. - The write-time
sameFileruns aftertranscript.jsonexists, so the OS resolves links and aliases bydev/ino, with arealpathfallback when a share reports inode 0. - The default export path onto its own input (
transcript.srt --to srt) is now refused too.
- An early
- Failure after transcription: exits 1 with the "Transcript saved…, but the caption file … could not be written" message, through
setCommandExitCode, socli.tsstill owns the exit code.
Tests: transcribe.test.ts passes 63/63. Mutants: 7/7 caught:
- write-time check removed;
- early check removed;
- inode-0 fallback forced false;
- case-sensitive basename;
- media treated as phrase-level;
- exit code not set;
- trailing-slash
--outputcheck removed.
CI: no required check is red (4 pass, 4 pending).
Nit: when the destination doesn't exist yet, the basename comparison is case-insensitive on every OS. On a case-sensitive Linux disk, -o Transcript.json beside the run's transcript.json is refused even though they would be different files. That fails the safe way and it's a corner case, so it's fine as is. Limiting it to case-insensitive volumes would just be a refinement.
What changed
hyperframes transcribe video.mp4 --to srtused to stop with "--to can only export from transcript files. Run transcribe first.", so getting captions from a recording took two commands. Now--to srtor--to vtton an audio or video file transcribes it and writes the sidecar in the same run, next totranscript.json(or at--output).--toon a.json,.srtor.vttstill exports instead of importing, with the same cue rules.--preserve-cuesand--outputbehave the same.--jsonthe run still prints one result line; it gainsformatandoutputPathwhen a sidecar was written.--outputfails at once, before any decoding, so a typo does not cost a long transcription. That covers a missing folder ("Output folder not found") and an--outputthat is a folder or ends in a slash ("--output is a folder", naming a file path to use instead). A caption path that would land on the input file or on thetranscript.jsonthe run writes is refused too ("The caption file would overwrite ..."). The check compares files, not path text: right before the caption write, by when the transcript exists, a destination that is the same file as the input ortranscript.jsonis refused, so the operating system resolves every link, relative link, folder alias (/varand/private/var) and letter case. An early check refuses the common cases before transcribing. When a disk reports inode 0 (some network shares), existing files are compared by their fully resolved paths instead, so links are still caught and two different files are never called the same. It also covers the default path, sotranscribe transcript.srt --to srtno longer rewrites its own input. A trailing-owith no path fails ("--output needs a file path").transcript.jsonlater still guesses from spacing, so it may split those cues differently; the docs say so.--towrites an empty sidecar (an empty SRT, a header-only VTT). Exporting an empty transcript file still fails with "No words found".--optional, a run skipped because whisper-cpp is unavailable writes no sidecar and exits 0. Before,--toon media always failed with exit 1.--helpexamples show the new form.Validation
--to vtt --jsonwritestranscript.json, a VTT that matches the formatter's output, and exactly one stdout result naming both; a missing--outputfolder fails before transcription starts; silent audio with--to srtwrites an empty sidecar. The first two fail on the previous code. New tests also cover an--outputthat is a folder, ends in a slash or sits under a file, an--outputthat names the input ortranscript.jsondirectly, through a link, through a linked folder, by its real path or in other letter case, an export whose default path is its own input, a trailing-o, a caption file that cannot be written after transcribing, and CJK grouping. Each new test fails when its fix is removed. Links to the transcript the run is about to create (direct, and relative inside a linked folder) keep the transcript and report that the caption file could not be written. All 63 transcribe tests pass three runs in a row. On macOS, the built-from-source CLI refuses each alias (case variant,/privatepath, link, trailing-o, export onto its own input) and leaves the files untouched.transcribe hello.wav --to srt --jsonexits 0, prints one result line, and writes three correct cues ("Write HTML.", "Render video.", "Built for agents.").transcript.jsonis named by one constant, used by both engines, the import, init and the check.No visible change
CLI only; no Studio or player code changes.