Video to Seamless Loop Converter
Upload a video clip to generate a seamless, looping video output.
查看工作流内部
源模板的只读预览。在 Cloud 中打开,即可查看节点并进行修改。
Loading the graph
Open full-size workflow preview- 输出
- 视频
- 作者
- ComfyUI
从代码中调用该模型
以下请求使用与体验区相同的预设图和设置。每次提交都可能开始一次付费运行;如果提交结果未知,请勿自动重试。
What you need
- Runs on
- Comfy Cloud
- 密钥
- COMFY_API_KEY
- 你的文件
- 由代码根据链接上传
需要付费的 Cloud 方案和额度。
# Python 3.10+: pip install comfy-sdk==0.4.0
import os
from comfy_sdk import Comfy
api_key = os.environ.get("COMFY_API_KEY", "").strip()
if not api_key: raise RuntimeError("Set COMFY_API_KEY")
os.environ.setdefault("COMFY_BASE_URL", "https://cloud.comfy.org")
client = Comfy(api_key=api_key)
workflow = client.workflows.from_json({
"158": {
"class_type": "VHS_LoadVideo",
"inputs": {
"custom_height": 0,
"custom_width": 0,
"force_rate": 16,
"format": "Wan",
"frame_load_cap": 0,
"select_every_nth": 1,
"skip_first_frames": 0,
"video": "UPLOADED_video_FILENAME"
},
"_meta": {
"title": "Load Video (Upload) 🎥🅥🅗🅢"
}
},
"176": {
"class_type": "ImageBatch",
"inputs": {
"image1": [
"229:160",
0
],
"image2": [
"177",
0
]
},
"_meta": {
"title": "Batch Images (DEPRECATED)"
}
},
"177": {
"class_type": "ImageFromBatch",
"inputs": {
"batch_index": [
"229:228",
0
],
"image": [
"225:8",
0
],
"length": [
"229:203",
0
]
},
"_meta": {
"title": "Get Image from Batch"
}
},
"250": {
"class_type": "CreateVideo",
"inputs": {
"audio": [
"158",
2
],
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": 16,
"images": [
"176",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"251": {
"class_type": "SaveVideo",
"inputs": {
"filename_prefix": "video/ComfyUI",
"format": "auto",
"format.codec": "auto",
"video": [
"250",
0
]
},
"_meta": {
"title": "Save Video"
}
},
"225:159": {
"class_type": "ReverseImageBatch",
"inputs": {
"images": [
"229:160",
0
]
},
"_meta": {
"title": "Reverse Image Batch"
}
},
"225:162": {
"class_type": "ImageFromBatch",
"inputs": {
"batch_index": 0,
"image": [
"225:159",
0
],
"length": 1
},
"_meta": {
"title": "Get Image from Batch"
}
},
"225:182": {
"class_type": "OpenAIChatNode",
"inputs": {
"images": [
"225:162",
0
],
"model": "gpt-5-mini",
"persist_context": False,
"prompt": "Analyze the image closely and describe it with high visual fidelity.\n\nWrite a focused prompt targeting roughly 100 tokens. Use dense, specific, cinematic language with minimal fluff. Return exactly one paragraph, 1-2 sentences only. No titles, no labels, no bullet points, no numbering, no explanations, no quotation marks, no markdown, and no extra text before or after the prompt.\n\nOutput prompt only."
},
"_meta": {
"title": "OpenAI ChatGPT"
}
},
"225:183": {
"class_type": "UNETLoader",
"inputs": {
"unet_name": "wan2.1_vace_14B_fp16.safetensors",
"weight_dtype": "default"
},
"_meta": {
"title": "Load Diffusion Model"
}
},
"225:188": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "Wan21_T2V_14B_lightx2v_cfg_step_distill_lora_rank32.safetensors",
"model": [
"225:183",
0
],
"strength_model": 0.7
},
"_meta": {
"title": "Load LoRA"
}
},
"225:201": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"225:182",
0
]
},
"_meta": {
"title": "Preview as Text"
}
},
"225:244": {
"class_type": "UNetTemporalAttentionMultiply",
"inputs": {
"cross_structural": 1.2,
"cross_temporal": 1.3,
"model": [
"225:188",
0
],
"self_structural": 1,
"self_temporal": 1
},
"_meta": {
"title": "UNetTemporalAttentionMultiply"
}
},
"225:3": {
"class_type": "KSampler",
"inputs": {
"cfg": 1,
"denoise": 1,
"latent_image": [
"225:83",
2
],
"model": [
"225:244",
0
],
"negative": [
"225:83",
1
],
"positive": [
"225:83",
0
],
"sampler_name": "euler",
"scheduler": "simple",
"seed": 276217540434315,
"steps": 8
},
"_meta": {
"title": "KSampler"
}
},
"225:38": {
"class_type": "CLIPLoader",
"inputs": {
"clip_name": "umt5_xxl_fp8_e4m3fn_scaled.safetensors",
"device": "default",
"type": "wan"
},
"_meta": {
"title": "Load CLIP"
}
},
"225:39": {
"class_type": "VAELoader",
"inputs": {
"vae_name": "wan_2.1_vae.safetensors"
},
"_meta": {
"title": "Load VAE"
}
},
"225:6": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"225:38",
0
],
"text": [
"225:182",
0
]
},
"_meta": {
"title": "CLIP Text Encode (Positive Prompt)"
}
},
"225:7": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"225:38",
0
],
"text": ""
},
"_meta": {
"title": "CLIP Text Encode (Negative Prompt)"
}
},
"225:8": {
"class_type": "VAEDecode",
"inputs": {
"samples": [
"225:3",
0
],
"vae": [
"225:39",
0
]
},
"_meta": {
"title": "VAE Decode"
}
},
"225:83": {
"class_type": "WanVaceToVideo",
"inputs": {
"batch_size": 1,
"control_masks": [
"229:217",
0
],
"control_video": [
"229:184",
0
],
"height": [
"229:160",
2
],
"length": [
"229:200",
0
],
"negative": [
"225:7",
0
],
"positive": [
"225:6",
0
],
"strength": 1,
"vae": [
"225:39",
0
],
"width": [
"229:160",
1
]
},
"_meta": {
"title": "WanVaceToVideo"
}
},
"229:144": {
"class_type": "VHS_DuplicateImages",
"inputs": {
"images": [
"229:197",
0
],
"multiply_by": [
"229:203",
0
]
},
"_meta": {
"title": "Repeat Images 🎥🅥🅗🅢"
}
},
"229:157": {
"class_type": "ImageFromBatch",
"inputs": {
"batch_index": 0,
"image": [
"229:160",
0
],
"length": [
"229:228",
0
]
},
"_meta": {
"title": "Get Image from Batch"
}
},
"229:160": {
"class_type": "ImageResize+",
"inputs": {
"condition": "always",
"height": 720,
"image": [
"158",
0
],
"interpolation": "lanczos",
"method": "keep proportion",
"multiple_of": 32,
"width": 720
},
"_meta": {
"title": "🔧 Image Resize"
}
},
"229:163": {
"class_type": "ImageFromBatch",
"inputs": {
"batch_index": 0,
"image": [
"229:236",
0
],
"length": [
"229:228",
0
]
},
"_meta": {
"title": "Get Image from Batch"
}
},
"229:184": {
"class_type": "BatchImagesNode",
"inputs": {
"images.image0": [
"229:237",
0
],
"images.image1": [
"229:144",
0
],
"images.image2": [
"229:157",
0
]
},
"_meta": {
"title": "Batch Images"
}
},
"229:197": {
"class_type": "LayerUtility: ColorImage V2",
"inputs": {
"color": "#7F7F7F",
"custom_height": [
"229:160",
2
],
"custom_width": [
"229:160",
1
],
"size": "custom"
},
"_meta": {
"title": "LayerUtility: ColorImage V2"
}
},
"229:200": {
"class_type": "PrimitiveInt",
"inputs": {
"value": 121
},
"_meta": {
"title": "Length"
}
},
"229:203": {
"class_type": "SimpleMath+",
"inputs": {
"a": [
"229:200",
0
],
"b": 0,
"value": "a - (16 * 2)"
},
"_meta": {
"title": "🔧 Simple Math"
}
},
"229:213": {
"class_type": "VHS_DuplicateImages",
"inputs": {
"images": [
"229:216",
0
],
"multiply_by": [
"229:203",
0
]
},
"_meta": {
"title": "Repeat Images 🎥🅥🅗🅢"
}
},
"229:214": {
"class_type": "BatchImagesNode",
"inputs": {
"images.image0": [
"229:224",
0
],
"images.image1": [
"229:213",
0
],
"images.image2": [
"229:224",
0
]
},
"_meta": {
"title": "Batch Images"
}
},
"229:216": {
"class_type": "LayerUtility: ColorImage V2",
"inputs": {
"color": "#ffffff",
"custom_height": [
"229:160",
2
],
"custom_width": [
"229:160",
1
],
"size": "custom"
},
"_meta": {
"title": "LayerUtility: ColorImage V2"
}
},
"229:217": {
"class_type": "ImageToMask",
"inputs": {
"channel": "red",
"image": [
"229:214",
0
]
},
"_meta": {
"title": "Convert Image to Mask"
}
},
"229:224": {
"class_type": "VHS_DuplicateImages",
"inputs": {
"images": [
"229:235",
0
],
"multiply_by": [
"229:228",
0
]
},
"_meta": {
"title": "Repeat Images 🎥🅥🅗🅢"
}
},
"229:228": {
"class_type": "PrimitiveInt",
"inputs": {
"value": 16
},
"_meta": {
"title": "Length"
}
},
"229:235": {
"class_type": "LayerUtility: ColorImage V2",
"inputs": {
"color": "#000000",
"custom_height": [
"229:160",
2
],
"custom_width": [
"229:160",
1
],
"size": "custom"
},
"_meta": {
"title": "LayerUtility: ColorImage V2"
}
},
"229:236": {
"class_type": "ReverseImageBatch",
"inputs": {
"images": [
"229:160",
0
]
},
"_meta": {
"title": "Reverse Image Batch"
}
},
"229:237": {
"class_type": "ReverseImageBatch",
"inputs": {
"images": [
"229:163",
0
]
},
"_meta": {
"title": "Reverse Image Batch"
}
}
})
workflow.set_input("158", "video", client.assets.from_url("https://comfy.org/catalog-media/34cf531f-9d9b-496e-b151-9d9eb1884ad4/6e9246b2-b8a7-468a-9bdc-ff766d66abf5"))
# run() also passes api_key to the partner nodes in this workflow.
job = client.run(workflow, api_key=api_key)
for output in job.get_outputs("251"):
output.to_file(output.name)试试一个示例
关于此工作流
The 'Video to Seamless Loop Converter' workflow is designed to transform your video clips into seamless, looping videos. This is particularly useful for creating engaging content for social media, digital art installations, or any application where continuous playback is desired. The workflow leverages the Wan2.1 VACE model, which excels in video content analysis and enhancement, ensuring that the transition between the end and the start of the video is smooth and imperceptible.
Technically, the workflow begins with the VHS_LoadVideo node, which imports the video into the system. The video frames are processed using a series of nodes, including ImageBatch and ImageFromBatch, which manage the frame data efficiently. The custom nodes from comfyui-kjnodes and comfyui-videohelpersuite are employed to create and fill empty frames, ensuring a seamless transition. Finally, the CreateVideo and SaveVideo nodes compile the processed frames into a continuous loop, ready for export and use.
常见问答
The workflow supports common video formats such as MP4, AVI, and MOV. Ensure your video is in one of these formats for optimal results.
The loop length is flexible and can be set according to your needs. However, shorter loops may require more precise frame alignment for seamless transitions.
This workflow is designed for pre-recorded video clips. For live feeds, additional real-time processing capabilities would be required.
The workflow aims to maintain the original video quality while ensuring a seamless loop. However, some minor quality adjustments may occur during processing.