Get chunks from Scene Detection
Cut on Scene Changes, Not on Frame 81
- full_video
- boundary_frames
- full_audio
- chunkOfVideo
- chunkOfAudio
Every long-form video workflow in ComfyUI hits the same wall: the model generates a few seconds at a time, so you chop the source up, run each piece through your V2V or upscale pass, and stitch it back. The chopping is where it gets ugly. Slice at a fixed 81 frames and you'll cut a person in half mid-sentence, then wonder why the join glitches.
TKTransitionDetectorChunk - "Get chunks from Scene Detection" in the menu - is the second half of a better answer. Its sibling TKTransitionDetector ("Video Transition Detector") watches the whole clip for hard cuts and dissolve ends, and emits a list of boundary frames. This node takes that list plus the full video, and hands you back one segment: the frames between two boundaries, plus the matching slice of the audio. You're cutting where the video already cuts, so seams land on the edit instead of in the middle of someone's face.
How it works
The detector does the analysis once; this node is bookkeeping on top of it. Segments are half-open ranges: segment 0 is frame 0 up to the first boundary, segment 1 runs from the first boundary to the second, and the last runs to the final frame. cut and dissolve_end boundaries are real scene changes, forced ones are where the detector split an over-long scene evenly to hit your chunk_secs target, and an end marker just closes the last segment rather than opening a new one.
Two implementation details are worth knowing because they explain the behavior you'll see.
First, the node sets INPUT_IS_LIST = True. That's the opposite of the usual list semantics in ComfyUI, where a list input makes the node execute once per item. Here the whole boundary list arrives in a single call, and you pick which segment you want with the index widget. If you were expecting a magic fan-out that processes all chunks, that isn't what this is - it's a selector.
Second, the boundary socket is a wildcard: ints, dicts with frame/type, a JSON string, the detector's tuple - all normalised into segment starts, so boundaries from elsewhere usually work too.
Audio is sliced by time, not frame count - start / fps * sample_rate through end / fps * sample_rate. Don't wire full_audio at all and the node doesn't fail; it returns silent 44.1 kHz stereo sized to the chunk, so the output is always safe to connect.
Inputs and outputs
Five sockets, one of them optional:
full_video- the complete IMAGE batch, not the chunk. Every call re-slices this same tensor.boundary_frames- the list fromTKTransitionDetector. Wildcard type, so it connects to whatever that node emits.index- which segment you want, 0-based. Drive this from a loop, an increment, or by hand while you audition chunks.fps- must match the fps the detector analysed at. See the gotcha below; this one bites.full_audio(optional) - the track to slice. Leave it off and you get silence rather than a red node.
Outputs are chunkOfVideo (an IMAGE batch of just that segment) and chunkOfAudio (an AUDIO dict). Video goes into your V2V or upscale branch; audio goes wherever the original track was going - TKAudioToFPSMatcher if your target model runs at a different fps, TKMergeAudioList or TKVideoAudioFuse at the end when you're putting the pieces back together.
Installing it
Manager first: search "Handy Nodes" in ComfyUI Manager and install from there. Manually:
cd ComfyUI/custom_nodes
git clone https://github.com/trashkollector/TKNodes
Restart ComfyUI and the node shows up under the TKNodes category, not under video - the whole pack lands in one flat category, which is why it's easy to miss.
No model downloads. The pack's requirements.txt pulls torch/torchvision/torchaudio (all >= floors, so nothing gets downgraded), numpy, pydub and sherpa-onnx - the last two are for its speaker nodes and come along regardless. pydub needs FFmpeg on your PATH; that's the author's own note in the file, and it's the usual reason a fresh install's video nodes do nothing useful.
Where people get burned
The fps defaults disagree. TKTransitionDetector defaults to 25, this node to 24. Leave both alone and boundary frame 300 is 12.0s to one and 12.5s to the other: the video slice is right, the audio is half a second late, and the drift grows with every index. Set fps explicitly on both.
Out-of-range index doesn't crash. It prints a line to the console and clamps to the last segment. Convenient while you're wiring, awful in a render queue: a loop that runs one too many times gives you a duplicated final chunk rather than an error you'd notice. Pair it with TKTransitionScenesCount and loop 0 .. last_index.
Boundaries aren't valid frame counts. A cut lands wherever the picture changed - not 4n+1, not whatever your LTX or Wan checkpoint wants. Snap each chunk after you pull it (TKSnapFrames is in the same pack) or the sampler will tell you about it, less politely.
The pack moves fast. The detector's own docstring documents an output reordering - boundary_frames used to sit on a different slot - so a workflow copied from an older screenshot may be feeding a report string into this node's boundary socket. It won't complain; it'll just return segment 0 forever. Check the slot.
ModuleNotFoundError: No module named 'cv2'. The detector's frame analysis uses OpenCV, and opencv is imported by the pack but isn't in its requirements.txt. If your ComfyUI install doesn't already have it:
cd ComfyUI
python -m pip install opencv-python-headless
That's the whole node. Not glamorous, but it's the difference between "chunk at 81 frames and fix it in the edit" and "chunk where the film already cuts."
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| index | INT | 00–100000 | — |
| fps | FLOAT | 24.000.01–1000 | — |
| full_video | IMAGE | — | |
| boundary_frames | * | — | |
| full_audioopt | AUDIO | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| chunkOfVideo | IMAGE | — |
| chunkOfAudio | AUDIO | — |