Confucius4 Save Subtitle
A real .srt, with timings you should still check
- saved_path
- subtitle_text
- subtitle_json
- cue_count
From transcript to a file an editor will accept
Transcription gives you text. Video work wants a subtitle file - cue numbers, timestamps, line breaks, the exact flavor VLC, Premiere, DaVinci and ffmpeg all agree on. Confucius4 Save Subtitle is the terminal node that bridges those two: feed it the result_json that Confucius4 Transcribe (or the Live microphone node) hands out, pick srt or vtt, and it writes the file into ComfyUI/output while previewing it inside the node.
It earns its place next to Transcribe's own convenience srt output, which is deliberately dumb - it returns nothing rather than a half-broken file - whereas this node validates and tells you what it didn't like.
How the cues get made
The node parses result_json, walks the VAD/ASR segments, and turns each segment into one cue. That's the part to internalize, because the source comment above the builder says it plainly: segment timings are estimates, and the code refuses to subdivide a sentence's text across spans it invented. A long sentence that came through as one segment stays one cue - the node wraps it and flags it as too long instead of fabricating per-line timestamps.
Wrapping is display-only and respects CJK width: chinese_chars (default 16) sets the budget for wide characters - effectively 32 columns - and english_chars (default 42) for Latin text. Words stay intact, CJK wraps between characters, and trailing punctuation like 。 hangs past the limit rather than stranding a line.
The formats differ in more than the extension. SRT uses comma decimals (00:00:01,250); VTT uses dots, a WEBVTT header, and escaped text. So if your transcript contains angle brackets, use VTT - some players read <...> in an SRT as formatting tags.
Inputs that matter, and the output you wire
result_json is force-input, so you wire it from Transcribe (or a finished Live session - click Stop and finalize first). format is srt or vtt. prefix defaults to r2t2_subtitle and gets sanitized to [A-Za-z0-9_-], cut at 64 characters - use ASCII, a Chinese prefix won't survive.
Then the optional ones worth knowing:
offset_ms- shifts every cue once. Negative shifts clip cues at zero or drop them, with a warning. This is your fix when a video's audio track is delayed or you cut from a longer recording.chinese_chars/english_chars- line width, not timing. Lower them if your player chops off the second line.allow_partial- the transcript was truncated and you want the file anyway; off by default.whole_audio_draft- fallback for a transcript with no segments at all: one whole-audio cue, approximate timing. Only if segment collection was never on.
cue_count (INT) and saved_path (STRING) tell you whether it worked before you go hunting in the output folder - saved_path is empty when no file was written. subtitle_text is the file's contents; subtitle_json is the cue document with per-cue timestamps and warnings. The node also previews and offers a download link.
It almost always says "requires_review", and that's fine
The node reports a subtitle_status - complete, partial, requires_review or empty - plus a warnings list in the preview. Unless your timings came from real forced alignment, every cue is tagged approximate_segment_timing, so a normal run reports requires_review. It's the author telling you the truth: the cues are VAD-derived estimates that want a human pass. If you need genuine word-level sync - karaoke, lyrics, tight cuts - that's a different toolchain (WhisperX-style forced alignment is what people reach for).
Install
Same pack, Windows + NVIDIA only.
cd ComfyUI/custom_nodes
git clone https://github.com/T8mars/comfyui-confucius-r2t2-t8
cd comfyui-confucius-r2t2-t8
.\scripts\setup_worker_windows.ps1
Prerequisites: Python 3.12, Git, CMake, VS 2022 C++ build tools, CUDA Toolkit 12.8. The first run pulls and verifies about 2.19 GB of Q8 weights plus the audio projector, then compiles the llama.cpp CUDA backend - give it real time. Default target is RTX 50-series (arch 120); on an older card run setup_worker_windows.ps1 -SkipNativeBuild, then build_native_windows.ps1 -CudaArch 86. Restart ComfyUI afterward. Manager install works via publisher t8star once its registry review clears. Inference runs in its own Python 3.12 worker instead of pip-installing into your ComfyUI environment.
Already have a transcript JSON with segments? Skip ComfyUI and use the same builder:
python scripts/json_to_subtitle.py transcript.json --format srt --offset-ms 0
Where it goes wrong
You forgot subtitle_timings on Transcribe. Then result_json carries no segment text and the node stops with "Segment text is missing. Re-run transcription with subtitle_timings enabled." The transcript is fine; the timings just weren't collected. Same idea if you get "Only completed or finalized transcripts can become subtitles" - that's a cancelled run or a Live session you never finalized.
Where is my file? Outputs are named <prefix>_<hash-of-content>.<ext> in ComfyUI/output. Same content reuses the same name; a new offset or an edit makes a new file, and existing files are never overwritten. Five runs with five offsets means five files - by design.
saved_path is empty. No cues were produced, so nothing was written - usually honest silence, or an offset_ms negative enough to drop every cue. Check cue_count first; if it's a partial file you want, turn on allow_partial and re-run.
One last note: this node invalidates itself every run, so it notices output files you moved or deleted and regenerates them - while Transcribe stays cached and doesn't touch the model again.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| result_json | STRING | — | |
| format | COMBO | 2 options: srt, vtt | |
| prefix | STRING | r2t2_subtitle | — |
| offset_msopt | INT | 0-86400000–86400000 | Shift all cues once. Negative times are clipped or dropped with a warning. |
| chinese_charsopt | INT | 164–80 | — |
| english_charsopt | INT | 428–160 | — |
| allow_partialopt | BOOLEAN | false | — |
| whole_audio_draftopt | BOOLEAN | false | Explicit fallback for a single-pass transcript without segments. One whole-audio cue; approximate timing. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| saved_path | STRING | — |
| subtitle_text | STRING | — |
| subtitle_json | STRING | — |
| cue_count | INT | — |