π΅ Music2Video Concat
Cut a list of clips into one synced film β without ever touching ffmpeg
- videos
- pipe
- audio
- video
- path
- duration
The main Music2Video node assembles a film only out of clips it rendered. If your clips come from anywhere else in the graph - an LTX or Wan subgraph, a local sampler, a load-from-disk node - that's what this node is for. You hand it a list of VIDEO clips and it cuts them into one finished film: one size, one frame rate, clips trimmed to their shots, and the soundtrack laid underneath. No generation, no API calls, no billing. One muxing pass with PyAV, and no ffmpeg binary anywhere. The README's framing is accurate: this is the "cutting clips you rendered yourself" node, and it's the difference between a pile of mp4s and something you'd actually play for someone.
How it works
Give it the pipe from the Music2Video node and it reads exactly one thing from it: the per-shot durations. Each clip is then trimmed, or its last frame held, to last exactly as long as its shot - and because the shot boundaries were snapped to the beat grid, every cut lands within half a frame of where the music said it should. Leave the pipe unwired and the clips keep whatever length they came back at. That's fine for a rough assembly, but the README puts it plainly: the film drifts out of sync with the track as the errors accumulate. Wire the pipe.
The soundtrack comes from audio_mode: source audio (your track alone - the usual choice), mix (track plus the clips' own audio, balanced by music_gain and clip_gain - the defaults put generated dialogue under the music rather than over it), video audio (only what the video models generated), or silent.
Inputs and outputs
Required: videos (the clips, in order - the node takes the whole list at once, not one execution per clip), audio_mode, music_gain, clip_gain, fit (pad/crop/stretch for odd-shaped clips), fps, width, height, crf, and filename_prefix. All the size and rate ones default to 0, meaning "take it from the clips." Optional: pipe (shot durations) and audio (the source track, normally the same AUDIO you fed the main node).
Outputs: video (the finished film, already written to disk), path (absolute), and duration (seconds).
Two details that save you real pain. Leave filename_prefix empty and a connected pipe names the film itself - it lands in that run's project folder under that run's name, next to the transcript and the clips. Type something and yours wins, subfolders included. And if a render failed, the clip list is shorter than the shot list; the node says so and falls back to the clips' own lengths rather than misaligning the whole film. It won't let you make a quiet mistake.
Install
Part of the same pack: ComfyUI Manager β search Music2Video β install, restart, or git clone https://github.com/lazniak/comfyui-music2video ComfyUI/custom_nodes/music2video. Requires ComfyUI β₯ 0.3.48. No extra dependencies - PyAV comes with the ComfyUI install it talks to.
Gotchas
The main one is the one the README leads with: wire the pipe. Without it you're just concatenating clips at their natural lengths, and for a music video the sync is the whole point. Second: in-memory clips get written to a temp file first and cleaned up after, so don't be confused when you see temp files flash by on a big render. And crf is the only re-encode the clips get - 20 is a good delivery default, 18 if you're archiving. Above 28, it shows.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| videos | VIDEO | The clips, in the order they should appear. Wire the 'videos' output of the Music2Video node, or the VIDEO output of whatever rendered them - this node takes a whole list at once rather than running per clip. A clip that is already a file on disk is read where it lies; anything still in memory is written to a temporary file first and removed afterwards. | |
| audio_mode | COMBO | source audio | What the film's soundtrack is. 'source audio' - the track from the 'audio' input, and nothing else. The usual choice for a music video. 'mix' - the track and the clips' own audio summed, balanced by 'music_gain' and 'clip_gain'. Use it when the clips carry dialogue or effects worth keeping under the music. 'video audio' - only what the video models generated, each clip's audio in its own slot, silence where a clip has none. 'silent' - no audio track at all. |
| music_gain | FLOAT | 1.000β4 | How loud the source track sits in the mix, as a linear multiplier - 1.0 is untouched, 0.5 is half amplitude (about -6 dB). Only used by 'mix'. The sum of both is clipped to full scale, so pushing both above 1.0 buys distortion, not loudness. |
| clip_gain | FLOAT | 0.500β4 | How loud the clips' own audio sits in the mix, as a linear multiplier. The default of 0.5 puts generated dialogue and effects under the music rather than over it. Only used by 'mix'. |
| fit | COMBO | pad | What happens to a clip whose aspect ratio differs from the film's. 'pad' letterboxes it, keeping the whole frame; 'crop' fills the frame and loses the edges; 'stretch' distorts it to fit. The film's own size is the most common size among the clips unless you override it. |
| fps | FLOAT | 00β120 | Frame rate of the finished film. 0 takes it from the clips (the highest sane rate among them), which is what you want unless a delivery spec says otherwise. Every clip is resampled onto this one grid, so a mixed-rate set of clips still cuts cleanly. |
| width | INT | 00β8192 | Width of the finished film, or 0 to take it from the clips. Set both this and 'height' to force a delivery size; odd numbers are rounded up, because H.264 cannot encode them. |
| height | INT | 00β8192 | Height of the finished film, or 0 to take it from the clips. |
| crf | INT | 200β51 | H.264 quality: lower is better and larger. 18 is visually lossless for most material, 20 is a good delivery default, above 28 shows. This is the only re-encode the clips get here. |
| filename_prefix | STRING | Start of the filename, written to ComfyUI/output/music2prompts as <prefix>_<date>-<time>_concat.mp4, so a re-run never overwrites the last one. Subfolders are allowed, as in any ComfyUI save node ('films/tour' writes into ComfyUI/output/films). Leave it empty and a connected 'pipe' names the film instead: it lands in that run's project folder under that run's name, next to the transcript and the clips ('final_video_name' on the pipe). Empty with no pipe falls back to 'music2video'. | |
| pipeopt | M2P_PIPE | The pipe from the Music2Video node, read for one thing only: the per-shot durations. Each clip is then trimmed, or held on its last frame, to last exactly as long as its shot, and every cut lands within half a frame of where the beat grid put it. Leave it unwired and the clips keep their own lengths - fine for a rough assembly, but the film drifts out of sync with the track as the errors accumulate. | |
| audioopt | AUDIO | The source track, normally the same AUDIO you fed the Music2Video node. Used when 'audio_mode' is 'source audio' or 'mix'; it is trimmed or padded with silence to the length of the finished film. Without it those two modes fall back to the clips' own audio and say so in the log. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| video | VIDEO | The finished film, ready for SaveVideo or a preview. It is already written to disk - saving it again only copies it somewhere else. |
| path | STRING | Where the file was written, absolute. |
| duration | FLOAT | Length of the finished film in seconds. |