SRT Generator
Transcription is half the job — this node makes it a subtitle file
- srt_content
SRT Generator is the node you chain after the Audio Transcription (Real-time) node when raw Whisper text isn't enough - when you actually want timed, numbered subtitles you can burn into a video or hand to a streaming overlay. Raw transcription gives you words; this node gives you entries with HH:MM:SS,mmm start/end times. It's from livepeer/ComfyUI-Stream-Pack, the pack the Livepeer team built around ComfyStream, so it's tuned for the streaming case where transcription arrives in chunks rather than as one finished file.
It does one thing and does it without any extra models or downloads: it parses whatever text it's handed, figures out timing, and returns a STRING of subtitle entries. Feed it the json_segments or json_words output from AudioTranscriptionNode and it'll reconstruct timing from the Whisper timestamps. Feed it plain text and it'll give you a single subtitle with a default 3-second duration. Both work; you just get much better timing with the JSON formats.
How it works
Under the hood it's a parser plus a timer. It tries to parse transcription_data as JSON - a list of segments (text + start/end) or words (word + start/end) - and if that fails, it falls back to treating the whole string as one 3-second segment. Then it formats each entry's times to SRT's HH:MM:SS,mmm style and numbers them. Two timing modes, controlled by use_absolute_time:
false(default) - times are relative to the segment Whisper gave you, so a chunk transcribed at 0–3 s stays at 0–3 s. Right for per-chunk streaming where each transcription is its own island.true- it offsets everything bysegment_start_time, so you can build one continuous timeline across chunks (you feed it the stream-relative start of each segment as it arrives).
There's also minimum_duration (default 1 s): any subtitle shorter than that gets stretched to it, so captions don't flash by too fast to read. And it handles the AudioTranscriptionNode's __WARMUP_SENTINEL__ by passing it through untouched so the rest of the pipeline can detect the warmup and filter it.
The inputs that matter
transcription_data- feed it theSTRINGoutput of AudioTranscriptionNode (ideallyjson_segments). This is the one you'll always connect.use_absolute_time+segment_start_time- flip totrueand supply the current stream position if you want one continuous subtitle timeline; leave both alone for per-chunk streaming.minimum_duration- bump it up (2–3 s) if captions feel too flashy.segment_duration- the fallback duration used when there's no timing info at all.
Output: one STRING named srt_content.
One gotcha worth knowing
The node's display name says "SRT Generator," and the output is called srt_content, but read the source before you assume: it actually emits a JSON array of subtitle entries ([{"id":1,"start":"00:00:00,000","end":"00:00:03,000","text":"..."}]) rather than literal .srt block text with sequential 1\n00:00:00,000 --> ... lines. If you're pointing it at a player or an encoder that expects a real .srt file, you'll need one tiny conversion step (parse the JSON, join the entries) - or save the raw string and transform it outside the graph. It's a real quirk, not a bug; knowing it saves you a confusing ten minutes.
Installing
It ships in ComfyUI-Stream-Pack, so you install the pack once - ComfyUI Manager → search "ComfyUI-Stream-Pack", or:
cd ComfyUI/custom_nodes
git clone https://github.com/livepeer/ComfyUI-Stream-Pack
# restart ComfyUI
This node itself is pure Python - no models, no extra downloads. The heavy install cost lives in the pack's faster_whisper/opencv-contrib-python/mediapipe dependencies (installed on first load), which you're pulling anyway if you use its sibling nodes.
Where people get burned
- Empty input is fine, but not silent. If
transcription_datais too short, it returns an empty string - harmless for the graph, but a bit confusing if you're debugging and see nothing. - Segment IDs keep counting up. The subtitle counter is instance state, so IDs increment across calls rather than resetting per batch. Doesn't matter for playback (players ignore IDs), but it looks odd in raw output.
- Timing quality is inherited. If you feed it plain text with no Whisper timestamps, every entry gets the same 3-second default - feed it
json_segmentsfor real timing.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| transcription_data | STRING | Transcription text or JSON with timing data from AudioTranscriptionNode | |
| segment_start_time | FLOAT | 0.0000–86400 | Start time of this segment in seconds (for absolute timing) |
| segment_duration | FLOAT | 3.00.1–60 | Duration of this segment in seconds |
| use_absolute_time | BOOLEAN | false | Use absolute timing (True) or segment-relative timing (False) |
| minimum_duration | FLOAT | 1.00.1–10 | Minimum subtitle duration in seconds |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| srt_content | STRING | — |