# Definition du workflow AnimateDiff via l'API ComfyUI
print("\n--- WORKFLOW ANIMATEDIFF ---")
print("=" * 45)
def build_animatediff_workflow(
prompt: str,
negative: str,
width: int,
height: int,
frames: int,
steps: int,
cfg: float,
seed: int
) -> Dict[str, Any]:
"""
Construit un workflow AnimateDiff au format ComfyUI API.
Le workflow suit la chaine :
CheckpointLoader -> CLIPTextEncode (pos/neg) -> AnimateDiffLoader
-> ApplyAnimateDiff -> UseEvolvedSampling -> KSampler
-> VAEDecode -> VHS_VideoCombine (ou SaveImage)
Args:
prompt: Description textuelle positive
negative: Prompt negatif
width, height: Resolution video
frames: Nombre de frames
steps: Etapes de debruitage
cfg: CFG scale
seed: Graine de generation
Returns:
Dict au format ComfyUI prompt API
"""
workflow = {
# Node 1 : Charger le modele SD 1.5
"1": {
"class_type": "CheckpointLoaderSimple",
"inputs": {
"ckpt_name": "sd-v1-5-pruned-emaonly.ckpt"
}
},
# Node 2 : Encodage du prompt positif
"2": {
"class_type": "CLIPTextEncode",
"inputs": {
"text": prompt,
"clip": ["1", 1] # Sortie CLIP du checkpoint
}
},
# Node 3 : Encodage du prompt negatif
"3": {
"class_type": "CLIPTextEncode",
"inputs": {
"text": negative,
"clip": ["1", 1]
}
},
# Node 4 : Charger le motion module AnimateDiff
"4": {
"class_type": "ADE_LoadAnimateDiffModel",
"inputs": {
"model_name": "v3_sd15_mm.ckpt"
}
},
# Node 5 : Appliquer le motion module (sortie M_MODELS)
"5": {
"class_type": "ADE_ApplyAnimateDiffModelSimple",
"inputs": {
"motion_model": ["4", 0]
}
},
# Node 10 : Injecter le motion model dans le modele de base
# (M_MODELS + MODEL -> MODEL exploitable par le KSampler)
"10": {
"class_type": "ADE_UseEvolvedSampling",
"inputs": {
"model": ["1", 0], # Sortie MODEL du checkpoint
"m_models": ["5", 0], # Sortie M_MODELS de l'ApplyAnimateDiff
"beta_schedule": "autoselect"
}
},
# Node 6 : Latent vide (batch de frames)
"6": {
"class_type": "EmptyLatentImage",
"inputs": {
"width": width,
"height": height,
"batch_size": frames
}
},
# Node 7 : KSampler
"7": {
"class_type": "KSampler",
"inputs": {
"model": ["10", 0], # Modele avec AnimateDiff (via UseEvolvedSampling)
"positive": ["2", 0],
"negative": ["3", 0],
"latent_image": ["6", 0],
"seed": seed,
"steps": steps,
"cfg": cfg,
"sampler_name": "euler",
"scheduler": "normal",
"denoise": 1.0
}
},
# Node 8 : Decodage VAE
"8": {
"class_type": "VAEDecode",
"inputs": {
"samples": ["7", 0],
"vae": ["1", 2] # Sortie VAE du checkpoint
}
}
}
# Node 9 : Sortie video (VideoHelperSuite ou SaveImage)
if enable_video_helper:
workflow["9"] = {
"class_type": "VHS_VideoCombine",
"inputs": {
"images": ["8", 0],
"frame_rate": fps_output,
"loop_count": 0,
"filename_prefix": "animatediff",
"format": "video/h264-mp4",
"pingpong": False,
"save_output": True
}
}
else:
workflow["9"] = {
"class_type": "SaveImage",
"inputs": {
"images": ["8", 0],
"filename_prefix": "animatediff_frame"
}
}
return workflow
# Construire le workflow
workflow = build_animatediff_workflow(
prompt=prompt_text,
negative=negative_prompt,
width=width,
height=height,
frames=num_frames,
steps=steps,
cfg=cfg_scale,
seed=seed_value
)
print(f"Workflow construit : {len(workflow)} nodes")
print(f"\nNodes du workflow :")
for node_id, node_data in workflow.items():
print(f" [{node_id}] {node_data['class_type']}")
# Sauvegarder le workflow JSON
workflow_path = OUTPUT_DIR / "animatediff_workflow.json"
with open(workflow_path, 'w', encoding='utf-8') as f:
json.dump({"prompt": workflow}, f, indent=2)
print(f"\nWorkflow sauvegarde : {workflow_path.name}")