AI2Go Multi Video Loader
Several video clips in one node — and the 'free' path that doesn't decode a single frame
- count
- video_1
- audio_1
- video_2
- audio_2
- video_3
- audio_3
- video_4
- audio_4
- video_5
- audio_5
- video_6
- audio_6
- video_7
- audio_7
- video_8
- audio_8
Video is where ComfyUI gets genuinely annoying. Stock Load Video is one clip at a time, and the moment your workflow needs two clips feeding two different branches - a V2V pass on clip A while clip B goes down a different path, or a multi-clip stitch - you're back to loader sprawl plus a dozen reroute noodles. The AI2Go Multi Video Loader does for video what its siblings do for image and audio: drop up to eight files onto one node and each gets its own video_N and audio_N socket pair.
The interesting part isn't the sockets, though. It's what the node doesn't do unless you ask.
The free path: force_rate = 0
The force_rate input defaults to 0, and 0 is the cheap route: every clip is returned lazily as a VideoFromFile object without decoding a single frame. No frame reads, no memory spike - you're just handing downstream a handle to the file. That's a genuinely nice touch for multi-clip work, because the alternative (decode eight clips up front) can eat VRAM and seconds for zero benefit when you're only going to re-encode later anyway.
Set force_rate to any other value (it goes up to 240) and the semantics flip to match VideoHelperSuite: the node decodes each clip and drops or duplicates frames so every clip runs at that rate - 24, 30, 60 - while preserving real running time. A 10-second clip stays 10 seconds; you're changing playback rate, not duration. That costs time and memory, because it has to actually decode frames to retime them. It also handles the NTSC weirdness properly: a 29.97 fps clip forced to a clean rate keeps its true duration instead of drifting (the code normalizes to a 2997/1001-style rational rather than a float artifact).
Rule of thumb: leave it at 0 unless a downstream node genuinely needs a uniform frame rate. The retime is there when you need it, but it's not free.
Audio comes along
Each row also emits audio_N, decoded separately with PyAV - so you get the clip's audio even on the lazy force_rate = 0 path, where video frames were never touched. A clip with no (decodable) audio track outputs None on that row's audio_N socket, exactly like an unplugged OPTIONAL input. Fine for optional consumers; a REQUIRED input downstream will fail. (The loader is also smarter than it used to be about tricky audio: it picks the last audio stream FFmpeg can actually decode, so files with an undecodable first track - iPhone spatial-audio, say - don't blow up.)
Inputs and outputs
files_json- hidden, front-end managed; don't edit it.force_rate- the one you'll actually touch, default0.output_slots-autoby default; pin to a fixed count to keep sockets (and wires) stable while you swap files.
Outputs: count plus video_N / audio_N per file (8 max). count = enabled rows actually emitted. The off-toggle behaves like the rest of the pack: a switched-off row keeps its socket slot but outputs None - safe for OPTIONAL inputs, a hard failure for REQUIRED ones. And if you reorder or remove rows, check the status-line warning: sockets shift, wires move with them.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Little-God1983/ComfyUI-AI2Go-Utils
Restart ComfyUI (or Manager → search "ComfyUI-AI2Go-Utils"). No extra dependencies - PyAV and Pillow ship with ComfyUI. The usual caveat from the README applies: this repo is frozen at v1.6.1; the author moved development to ComfyUI-IntoTheLatent-Utils under new ITL* node names, so this pack is for keeping existing workflows alive, not for new features.
Common issues
- "not found in the input folder - re-add it" - the source clip moved or was deleted from
input/. Re-drop. audio_Nis None on a clip that clearly has sound - that track may use a codec FFmpeg can't decode; the node logs which file failed, so check the console.- Slow loads / high memory after you set
force_rate- that's expected, it's decoding every clip. Set it back to0if you don't need uniform rates.
For multi-clip video work it's the loader worth reaching for: the lazy path is the honest reason to pick it over wiring up N stock loaders, and the retime is a bonus when you need it.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| files_json | STRING | [] | Authoritative file list as JSON. Hidden in the UI and kept in sync by the front-end — drop files onto the node instead of editing this. |
| force_rate | FLOAT | 00–240 | 0 = off — every clip keeps its own frame rate, and video_N is returned lazily without decoding a single frame (free). Any other value drops or duplicates frames so all clips run at that rate while keeping their real running time (a 10s clip stays 10s). Forcing a rate has to decode the clip's frames to do this, so it costs time and memory — 0 stays free. |
| output_slots | COMBO | auto | How many output sockets to show. 'auto' follows the number of loaded files, so sockets appear and disappear as you edit the list. Pick a fixed number to keep the sockets (and your wires) in place while you swap files around — extra sockets with no file behind them output nothing, so don't wire more than you load. |
Outputs (17)
| Name | Type | Description |
|---|---|---|
| count | INT | — |
| video_1 | VIDEO | — |
| audio_1 | AUDIO | — |
| video_2 | VIDEO | — |
| audio_2 | AUDIO | — |
| video_3 | VIDEO | — |
| audio_3 | AUDIO | — |
| video_4 | VIDEO | — |
| audio_4 | AUDIO | — |
| video_5 | VIDEO | — |
| audio_5 | AUDIO | — |
| video_6 | VIDEO | — |
| audio_6 | AUDIO | — |
| video_7 | VIDEO | — |
| audio_7 | AUDIO | — |
| video_8 | VIDEO | — |
| audio_8 | AUDIO | — |