# 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({
"1": {
"class_type": "GetVideoComponents",
"inputs": {
"video": [
"2",
0
]
},
"_meta": {
"title": "Get Video Components"
}
},
"2": {
"class_type": "LoadVideo",
"inputs": {
"file": "UPLOADED_video_FILENAME"
},
"_meta": {
"title": "Load Video"
}
},
"28": {
"class_type": "VHS_SelectEveryNthImage",
"inputs": {
"images": [
"1",
0
],
"select_every_nth": [
"157",
0
],
"skip_first_images": 0
},
"_meta": {
"title": "Select Every Nth Image 🎥🅥🅗🅢"
}
},
"104": {
"class_type": "ImpactImageBatchToImageList",
"inputs": {
"image": [
"28",
0
]
},
"_meta": {
"title": "Image Batch to Image List"
}
},
"116": {
"class_type": "FL_PromptMulti",
"inputs": {
"name_prefix": "prompt_",
"negative_text": "",
"positive_text": [
"130",
0
]
},
"_meta": {
"title": "FL Prompt Multi"
}
},
"130": {
"class_type": "GeminiNode",
"inputs": {
"model": "gemini-3-1-flash-lite",
"prompt": [
"173",
0
],
"seed": 131495485131261,
"system_prompt": [
"150",
0
]
},
"_meta": {
"title": "Google Gemini"
}
},
"147": {
"class_type": "CreateVideo",
"inputs": {
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": 8,
"images": [
"165",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"148": {
"class_type": "SaveVideo",
"inputs": {
"filename_prefix": "video/ComfyUI",
"format": "mp4",
"format.codec": "h264",
"video": [
"147",
0
]
},
"_meta": {
"title": "Save Video"
}
},
"150": {
"class_type": "PrimitiveStringMultiline",
"inputs": {
"value": "Return exactly the requested number of newline-separated prompts.\nNo bullets, numbering, JSON, quotes, or commentary.\n\nEach line is a style-transfer prompt for the corresponding source video frame.\nDo not invent subjects or scene details.\nRefer to the image only as “the source frame”.\n\nEach prompt must preserve source frame depth, outlined edges composition and geometry.\n\nEach prompt must be 35 to 55 words.\n"
},
"_meta": {
"title": "System Prompt"
}
},
"155": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"28",
1
]
},
"_meta": {
"title": "Int to String"
}
},
"157": {
"class_type": "PrimitiveInt",
"inputs": {
"value": 4
},
"_meta": {
"title": "Select Every 'Nth' Image"
}
},
"165": {
"class_type": "ImageListToImageBatch",
"inputs": {
"images": [
"146:144",
0
]
},
"_meta": {
"title": "Image List to Image Batch"
}
},
"168": {
"class_type": "RegexReplace",
"inputs": {
"case_insensitive": True,
"count": 0,
"dotall": False,
"multiline": False,
"regex_pattern": "{COUNT}",
"replace": [
"155",
0
],
"string": "Create exactly {COUNT} style-transfer prompts, one per line. Use “the source frame” instead of describing the subject. Assign treatments strictly by prompt number using this repeating cycle:\n\n{FRAME_PROMPTS}\n\nRepeat the cycle after the final prompt."
},
"_meta": {
"title": "Replace Text (Regex)"
}
},
"170": {
"class_type": "RegexReplace",
"inputs": {
"case_insensitive": True,
"count": 0,
"dotall": False,
"multiline": False,
"regex_pattern": "{FRAME_PROMPTS}",
"replace": [
"172",
0
],
"string": [
"168",
0
]
},
"_meta": {
"title": "Replace Text (Regex)"
}
},
"172": {
"class_type": "PrimitiveStringMultiline",
"inputs": {
"value": "Each Prompt # should appear 1x before incrementing to the next Prompt #.\n\ntheme: hand-painted gouache animation cels\nPrompt 1: flat opaque gouache fills, visible bristle strokes, warm cream paper showing at the edges, soft pencil underdrawing, muted teal and coral palette.\nPrompt 2: thick ink outlines with slight wobble, chalky pastel washes, dry-brush texture in the sky, sun-faded yellows, tiny paint speckles.\nPrompt 3: layered cut-paper shapes, gentle drop shadows between layers, matte gouache surface, deep ultramarine shadows, sparse white highlights.\nPrompt 4: loose watercolour bleed under the gouache, warm ochre light, soft blooming edges, granulated pigment in the darker areas.\nPrompt 5: bold limited palette of rust, sage and navy, waxy crayon grain over flat colour, rough torn-paper edges, slightly misregistered layers.\nPrompt 6: dusky lilac evening tones, glowing peach highlights, smudged charcoal accents, visible canvas weave, painterly sparkle on the water."
},
"_meta": {
"title": "Frame Prompts"
}
},
"173": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"170",
0
]
},
"_meta": {
"title": "Preview Prompt"
}
},
"146:123": {
"class_type": "ReferenceLatent",
"inputs": {
"conditioning": [
"146:140",
0
],
"latent": [
"146:143",
0
]
},
"_meta": {
"title": "Set Reference Latent"
}
},
"146:135": {
"class_type": "UNETLoader",
"inputs": {
"unet_name": "flux-2-klein-4b-fp8.safetensors",
"weight_dtype": "default"
},
"_meta": {
"title": "Load Diffusion Model"
}
},
"146:136": {
"class_type": "CLIPLoader",
"inputs": {
"clip_name": "qwen_3_4b.safetensors",
"device": "default",
"type": "flux2"
},
"_meta": {
"title": "Load CLIP"
}
},
"146:137": {
"class_type": "VAELoader",
"inputs": {
"vae_name": "flux2-vae.safetensors"
},
"_meta": {
"title": "Load VAE"
}
},
"146:138": {
"class_type": "EmptyFlux2LatentImage",
"inputs": {
"batch_size": 1,
"height": [
"146:99",
1
],
"width": [
"146:99",
0
]
},
"_meta": {
"title": "Empty Flux 2 Latent"
}
},
"146:139": {
"class_type": "ImageScaleToTotalPixels",
"inputs": {
"image": [
"104",
0
],
"megapixels": 1,
"resolution_steps": 1,
"upscale_method": "nearest-exact"
},
"_meta": {
"title": "Scale Image to Total Pixels"
}
},
"146:140": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"146:136",
0
],
"text": [
"116",
0
]
},
"_meta": {
"title": "CLIP Text Encode (Positive Prompt)"
}
},
"146:141": {
"class_type": "ConditioningZeroOut",
"inputs": {
"conditioning": [
"146:140",
0
]
},
"_meta": {
"title": "Conditioning Zero Out"
}
},
"146:142": {
"class_type": "ReferenceLatent",
"inputs": {
"conditioning": [
"146:141",
0
],
"latent": [
"146:143",
0
]
},
"_meta": {
"title": "Set Reference Latent"
}
},
"146:143": {
"class_type": "VAEEncode",
"inputs": {
"pixels": [
"146:139",
0
],
"vae": [
"146:137",
0
]
},
"_meta": {
"title": "VAE Encode"
}
},
"146:144": {
"class_type": "VAEDecode",
"inputs": {
"samples": [
"146:64",
0
],
"vae": [
"146:137",
0
]
},
"_meta": {
"title": "VAE Decode"
}
},
"146:145": {
"class_type": "RandomNoise",
"inputs": {
"noise_seed": 300402371121536
},
"_meta": {
"title": "RandomNoise"
}
},
"146:61": {
"class_type": "KSamplerSelect",
"inputs": {
"sampler_name": "euler"
},
"_meta": {
"title": "KSamplerSelect"
}
},
"146:62": {
"class_type": "Flux2Scheduler",
"inputs": {
"height": [
"146:99",
1
],
"steps": 4,
"width": [
"146:99",
0
]
},
"_meta": {
"title": "Flux2Scheduler"
}
},
"146:63": {
"class_type": "CFGGuider",
"inputs": {
"cfg": 1,
"model": [
"146:135",
0
],
"negative": [
"146:142",
0
],
"positive": [
"146:123",
0
]
},
"_meta": {
"title": "CFG Guider"
}
},
"146:64": {
"class_type": "SamplerCustomAdvanced",
"inputs": {
"guider": [
"146:63",
0
],
"latent_image": [
"146:138",
0
],
"noise": [
"146:145",
0
],
"sampler": [
"146:61",
0
],
"sigmas": [
"146:62",
0
]
},
"_meta": {
"title": "SamplerCustomAdvanced"
}
},
"146:99": {
"class_type": "GetImageSize",
"inputs": {
"image": [
"146:139",
0
]
},
"_meta": {
"title": "Get Image Size"
}
}
})
workflow.set_input("2", "file", client.assets.from_url("https://comfy.org/catalog-media/3c68d8e2-08ce-417c-bfe6-4c67a357c7c6/c7a288d9-bb67-4b98-b8e0-7e16dfc7086e"))
# 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("148"):
output.to_file(output.name)