sync.so lipsync – output
The output node that files your lipsync and hands you the frames
- output_path
- output_video
SyncLipsyncOutputNode is the clean-up crew for the sync-comfyui pack. The generate node already saves your finished video to output/sync_output_<timestamp>.mp4 in the ComfyUI directory - this node is where you decide it's actually yours: rename it, move it somewhere sensible, and load it back into ComfyUI as frames so you can preview or feed it downstream. It's an output node, which in ComfyUI-speak means it marks the end of the graph and makes the result show up properly in the UI rather than vanishing into the void.
It's the least glamorous node in the pack and, honestly, the most practical to keep in your workflow. The auto-named sync_output_1700000000.mp4 the main node produces is useless for finding anything a day later.
What you feed it
output_path(required, STRING) - wire this from the generate node's first output. It's the path to the video it downloaded.custom_video_path(optional) - a directory to copy the finished video into. Gets created if it doesn't exist.custom_video_name(optional) - the filename. If you don't include an extension,.mp4is appended automatically; if you include.movor.mkvor whatever, that's respected.
Outputs are the same output_path string passed through, plus output_video as a proper ComfyUI IMAGE - the video's frames, read back with OpenCV and normalized to 0–1 floats. That's the genuinely useful bit: it means the result of a paid cloud lipsync can drop into a VHS-compatible or any standard image/video pipeline for upscaling, color grading, or whatever else you want to do locally afterward.
How it works
Mechanically it's a passthrough with three jobs. First, if both optional fields are set and the output file exists, it copies the video to your custom location (via shutil.copy2, so the original in output/ stays). Then it loads the resulting file with OpenCV, converting each BGR frame to RGB and to float 0–1 tensors, and returns that as IMAGE. Finally it tells ComfyUI to render the video in the UI's output area. If anything goes wrong with loading - missing file, bad codec - it returns a 64×64 black placeholder tensor instead of failing loudly. That's a real gotcha: a black preview doesn't always mean a failed job, it can mean the file was unreachable.
Installing the pack
Same as every node in this pack - one install gets all five:
cd ComfyUI/custom_nodes
git clone https://github.com/synchronicity-labs/sync-comfyui
cd sync-comfyui
pip install -r requirements.txt
Restart ComfyUI, or use ComfyUI Manager and search for "sync-comfyui" / "sync". The output node leans on opencv-python-headless (that's where the video decoding comes from) plus torch/numpy, which you'll almost certainly already have.
Common issues
- Black or missing preview - the node couldn't open the video file. Since it swallows the failure and returns a zero tensor, check that
output_pathactually points at an existing file and that your copy target was writable. The console will have the real error. - File didn't move -
custom_video_pathandcustom_video_namemust both be set; if only one is, the copy is skipped and you get the original path back. - No video at all - the generate node failed before producing a file, so this node reports "No video output was generated." The root cause is upstream: check the main node's status and the console log.
That's the whole job. Wire the generate node's output_path in, pick a name you'll recognize next week, and the pack becomes far less annoying to use - plus you get frames out the other side for free, which is a nice bonus from a node that's really just a file manager with a ComfyUI costume on.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| output_path | STRING | — | |
| custom_video_pathopt | STRING | — | |
| custom_video_nameopt | STRING | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| output_path | STRING | — |
| output_video | IMAGE | — |