Load Video Path (C2C)
The loader for files that aren't in your input folder
- vae
- IMAGE
- frame_count
- audio
- video_info
- video
- mask
- info
Every video widget in ComfyUI lists files from ComfyUI/input/. That's fine for experiments and infuriating for actual work, where the plate lives on the NAS, on an external drive, or anywhere other than the one folder the widget can see. This node takes a path string instead, and it takes an image sequence or a sequence pattern too - so one node covers D:/plates/interview.mov and /mnt/nas/shot_010/####.exr.
Same output set as its sibling: IMAGE (or LATENT if you wire a VAE), frame_count, audio, video_info in VideoHelperSuite format, the lazy C2C_VIDEO handle, and mask.
What you type and why it validates
video is a plain string and the node checks it before the run: if there's nothing at that path, you get a message naming the absolute path it tried - with the quote-stripping applied, so a pasted "D:\plates\clip.mov" works. Sequences are detected by extension (EXR, HDR, PNG, JPG, TIFF, DPX) or by pattern (####, %04d, $F4, [1-10]), so a folder of frames and a shot_####.exr string are both legitimate inputs.
Everything else matches Load Video (C2C) - force_rate (0 keeps source), custom_width / custom_height (0 derives one from the other), frame_load_cap, skip_first_frames, select_every_nth, and the format preset that rounds frames onto a model's grid and snaps dimensions to its required multiple. If a clip came out shorter than you selected and you didn't ask for it, format is the first thing to check.
The extra widget here is sequence_fps. A video file carries its own frame rate; a folder of EXR frames does not. This is how you tell the loader what the sequence's rate is - 24 by default - and it matters the moment anything downstream cares about time rather than just frame order. Audio has no meaning for a sequence, so the audio output stays empty there.
The lazy decode, and why it's not just a gimmick
The node reads the container or sequence header first and returns frame_count plus a C2C_VIDEO handle without touching pixels. Actual decoding happens only if the IMAGE/LATENT output is wired to something, and then only for the frames your skip/nth/cap selection kept. The batch is also checked against free system memory before it's allocated, and if it doesn't fit you get a refusal with the numbers and a suggested frame cap rather than an out-of-memory death halfway through a queue.
That's the difference between a loader you can put at the top of a graph and one you're scared of.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Code2Collapse/ComfyUI-CustomNodePacks.git
Or "CustomNodePacks" in ComfyUI Manager, then restart. Decoding is via PyAV (av), which modern ComfyUI builds already include; if it's missing, pip install av in ComfyUI's own Python environment. No model downloads. Don't blanket-install the pack's requirements.txt over a working install - opencv-python, scipy, safetensors are the real hard requirements and ComfyUI already provides torch, numpy, torchvision and Pillow.
Where people get burned
Absolute paths are absolute, and ComfyUI may be running in a container, a WSL distro, or on a different machine from the one you're browsing on. /mnt/nas/... on your desktop isn't the same filesystem the server sees. When the validation says nothing's there, believe it and check from inside the server's environment.
Second: an EXR sequence is read as-is, and the pack's own pipeline treats images as normal 0β1 floats downstream. If you're loading 16-bit linear plates and expecting ACES-correct colour, you want the pack's colour-science nodes in the chain rather than assuming the loader did the transform.
Third: this is the loader the Image Mask Editor can show frame-by-frame without a run, along with the input-folder version - which is a genuinely nice pair for rotoscoping, since it means you paint masks on the real frames instead of on a stale preview.
Inputs (26)
| Name | Type | Default | Description |
|---|---|---|---|
| video | STRING | Path to a video file or image sequence. | |
| force_rate | FLOAT | 00β240 | Output FPS; 0 keeps source. |
| custom_width | INT | 00β16384 | Output width; 0 keeps source or derives from height. |
| custom_height | INT | 00β16384 | Output height; 0 keeps source or derives from width. |
| frame_load_cap | INT | 00β1000000 | Max frames; 0 = no cap. |
| skip_first_frames | INT | 00β1000000 | Skip this many frames first. |
| select_every_nth | INT | 11β100000 | Keep every Nth frame. |
| vaeopt | VAE | Encode to LATENT instead of IMAGE. | |
| formatopt | COMBO | None | Model preset (frame grid / dim multiple). |
| sequence_fpsopt | FLOAT | 24.001β240 | FPS for image sequences only. |
| crop_xopt | FLOAT | 0.0000β1 | Crop, as a fraction of the frame (x, y, width, height; 0, 0, 1, 1 = no crop). Set it with the Crop tool in the player; custom_width / custom_height then resize the cropped frame. |
| crop_yopt | FLOAT | 0.0000β1 | Crop, as a fraction of the frame (x, y, width, height; 0, 0, 1, 1 = no crop). Set it with the Crop tool in the player; custom_width / custom_height then resize the cropped frame. |
| crop_wopt | FLOAT | 1.0000β1 | Crop, as a fraction of the frame (x, y, width, height; 0, 0, 1, 1 = no crop). Set it with the Crop tool in the player; custom_width / custom_height then resize the cropped frame. |
| crop_hopt | FLOAT | 1.0000β1 | Crop, as a fraction of the frame (x, y, width, height; 0, 0, 1, 1 = no crop). Set it with the Crop tool in the player; custom_width / custom_height then resize the cropped frame. |
| decoderopt | COMBO | auto | auto: the fast path only where it is faster with the same colour - measured, that is the exact reader on the tested machine. fast: ffmpeg decodes 8-bit 4:2:0 video (the same frames; on an NVIDIA GPU the colour conversion runs there, mean 0.5/255 from the exact reader). exact: the frame-accurate PyAV reader (10-bit, HDR, alpha, ProRes and image sequences always use it). |
| seconds_capopt | FLOAT | 0.00β86400 | Load at most this many seconds after skip_first_frames (0 = no limit), at the force_rate rate when set. Works with frame_load_cap: the smaller one wins, and a format preset then snaps the count. |
| resolutionopt | COMBO | custom | Output size by its LONGEST side, keeping the aspect: 2160p is 3840, 1080p is 1920. custom uses custom_width / custom_height (0 = keep the source size or follow the aspect). source ignores them. |
| divisible_byopt | COMBO | 1 | Round both output dimensions to a multiple of this. A format preset or a VAE may raise it. |
| audio_when_missingopt | COMBO | silence | When the file has no audio: silence outputs a silent track as long as the loaded frames (what the loader always did, so downstream nodes keep working); none outputs nothing. |
| handlesopt | COMBO | off | Handles = extra frames for a video model to settle on, added here and trimmed off again by Save Video (C2C) (it reads them from video_info). fit: pad the clip UP to the model's frame grid (handle_target = frames you want out, 0 = this clip's length). add: put handle_frames on, rounded onto the grid. Repeated edge frames, never black; the audio moves with them. |
| handle_framesopt | INT | 80β400 | add: frames to put on (side both: on EACH end). With a grid, 0 = just enough to land on it. |
| handle_targetopt | INT | 00β1000000 | fit: how many frames you want out after the trim. 0 = this clip's own length. |
| handle_sideopt | COMBO | head | Which end gets the handles. head is where a video model settles; both splits them. |
| handle_gridopt | COMBO | from format | The model's frame grid: 'from format' uses the format preset above (MiniMax H3 17n+5, Wan 4n+1, LTXV 8n+1 ...), so there is one grid, not two; or pick one here. |
| handle_custom_stepopt | INT | 41β1000 | custom grid: step (frames = step * n + plus). |
| handle_custom_plusopt | INT | 10β1000 | custom grid: plus. |
Outputs (7)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE,LATENT | β |
| frame_count | INT | β |
| audio | AUDIO | β |
| video_info | VHS_VIDEOINFO | β |
| video | C2C_VIDEO | β |
| mask | MASK | β |
| info | STRING | β |