Video nodes report outputs to /history (OUTPUT_NODE + ui), batch upload for list-image inputs, ComfyChair templates

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019D2FYwPstKRfT5kh9M2kMS
This commit is contained in:
Pharma-Lobby
2026-07-12 17:00:00 +00:00
co-authored by Claude Fable 5
parent d70274517e
commit b307fc93a3
7 changed files with 250 additions and 9 deletions
+17 -9
View File
@@ -84,18 +84,20 @@ def _to_audio(data: bytes) -> dict:
return {"waveform": waveform[None,], "sample_rate": sr}
def _save_video(data: bytes, model_id: str) -> str:
"""Video in den ComfyUI-Output-Ordner schreiben, Pfad zurückgeben."""
def _save_video(data: bytes, model_id: str):
"""Video in den ComfyUI-Output-Ordner schreiben → (Pfad, ui-Metadaten für /history)."""
try:
import folder_paths
out_dir = Path(folder_paths.get_output_directory())
except Exception:
out_dir = Path("output")
out_dir.mkdir(parents=True, exist_ok=True)
subfolder = "video"
(out_dir / subfolder).mkdir(parents=True, exist_ok=True)
slug = model_id.replace("/", "_")
path = out_dir / f"replicate_{slug}_{int(time.time())}.mp4"
path.write_bytes(data)
return str(path)
filename = f"replicate_{slug}_{int(time.time())}.mp4"
(out_dir / subfolder / filename).write_bytes(data)
meta = {"filename": filename, "subfolder": subfolder, "type": "output"}
return str(out_dir / subfolder / filename), meta
def _first_output(result):
@@ -145,8 +147,11 @@ def _build_arguments(model_def: dict, kwargs: dict) -> dict:
continue
kind = entry["kind"]
if kind == "image":
uri = _image_to_uri(value)
args[name] = [uri] if entry.get("is_list") else uri
if entry.get("is_list"):
# Batch-Bilder einzeln hochladen (z.B. Qwen Edit Plus: Source + Referenzen)
args[name] = [_image_to_uri(value[i:i + 1]) for i in range(value.shape[0])]
else:
args[name] = _image_to_uri(value)
elif kind == "audio":
args[name] = _audio_to_uri(value)
elif kind == "combo":
@@ -180,6 +185,7 @@ def _make_node(model_def: dict):
FUNCTION = "run"
RETURN_TYPES = return_types
RETURN_NAMES = return_names
OUTPUT_NODE = output_kind == "video"
@classmethod
def INPUT_TYPES(cls):
@@ -201,7 +207,9 @@ def _make_node(model_def: dict):
return (_to_image_tensor(data),)
if output_kind == "audio":
return (_to_audio(data),)
return (_save_video(data, model_def["id"]),)
path, meta = _save_video(data, model_def["id"])
# ui-Eintrag → Video erscheint in /history und ist via /view abrufbar (Client-Apps)
return {"ui": {"images": [meta], "animated": (True,)}, "result": (path,)}
ReplicateModelNode.__name__ = "Replicate_" + model_def["id"].replace("/", "_").replace("-", "_").replace(".", "_")
return ReplicateModelNode
+34
View File
@@ -0,0 +1,34 @@
{
"name": "LTX-2 Distilled (Replicate)",
"description": "Image-to-Video mit synchronem Audio via lightricks/ltx-2-distilled. Video landet in output/video/, Node ist Output-Node.",
"defaults": {
"num_frames": 121,
"seed": 0
},
"nodes": {
"1": {
"class_type": "LoadImage",
"inputs": {
"image": "{{image_filename}}"
},
"_meta": {
"title": "Load Image"
}
},
"2": {
"class_type": "Replicate_lightricks_ltx_2_distilled",
"inputs": {
"prompt": "{{positive_prompt}}",
"image": [
"1",
0
],
"num_frames": "{{num_frames}}",
"seed": "{{seed}}"
},
"_meta": {
"title": "LTX-2 Distilled (Replicate)"
}
}
}
}
@@ -0,0 +1,47 @@
{
"name": "Qwen Image Edit Plus (Replicate)",
"description": "Bild-Editing via qwen/qwen-image-edit-plus. Referenzbilder: per ImageBatch an den image-Input hängen.",
"defaults": {
"aspect_ratio": "match_input_image",
"seed": 0
},
"nodes": {
"1": {
"class_type": "LoadImage",
"inputs": {
"image": "uploaded_image.png [input]"
},
"_meta": {
"title": "Load Image (Source)"
}
},
"2": {
"class_type": "Replicate_qwen_qwen_image_edit_plus",
"inputs": {
"image": [
"1",
0
],
"prompt": "{{positive_prompt}}",
"aspect_ratio": "{{aspect_ratio}}",
"seed": "{{seed}}"
},
"_meta": {
"title": "Qwen Image Edit Plus (Replicate)"
}
},
"3": {
"class_type": "SaveImage",
"inputs": {
"filename_prefix": "ComfyChair",
"images": [
"2",
0
]
},
"_meta": {
"title": "Save Image"
}
}
}
}
+38
View File
@@ -0,0 +1,38 @@
{
"name": "Wan 2.2 I2V Fast (Replicate)",
"description": "Image-to-Video via wan-video/wan-2.2-i2v-fast (Lightning-destilliert von Pruna)",
"defaults": {
"resolution": "480p",
"num_frames": 81,
"frame_rate": 24,
"seed": 0
},
"nodes": {
"1": {
"class_type": "LoadImage",
"inputs": {
"image": "{{image_filename}}"
},
"_meta": {
"title": "Load Image"
}
},
"2": {
"class_type": "Replicate_wan_video_wan_2_2_i2v_fast",
"inputs": {
"prompt": "{{positive_prompt}}",
"image": [
"1",
0
],
"resolution": "{{resolution}}",
"num_frames": "{{num_frames}}",
"frames_per_second": "{{frame_rate}}",
"seed": "{{seed}}"
},
"_meta": {
"title": "Wan 2.2 I2V Fast (Replicate)"
}
}
}
}
+27
View File
@@ -0,0 +1,27 @@
{
"name": "Wan 2.2 T2V Fast (Replicate)",
"description": "Text-to-Video via wan-video/wan-2.2-t2v-fast (Lightning-destilliert von Pruna)",
"defaults": {
"aspect_ratio": "9:16",
"resolution": "480p",
"num_frames": 81,
"frame_rate": 24,
"seed": 0
},
"nodes": {
"1": {
"class_type": "Replicate_wan_video_wan_2_2_t2v_fast",
"inputs": {
"prompt": "{{positive_prompt}}",
"aspect_ratio": "{{aspect_ratio}}",
"resolution": "{{resolution}}",
"num_frames": "{{num_frames}}",
"frames_per_second": "{{frame_rate}}",
"seed": "{{seed}}"
},
"_meta": {
"title": "Wan 2.2 T2V Fast (Replicate)"
}
}
}
}
+38
View File
@@ -0,0 +1,38 @@
{
"name": "Z-Image Turbo (Replicate)",
"description": "Text-to-Image via prunaai/z-image-turbo auf Replicate",
"defaults": {
"width": 832,
"height": 1216,
"steps": 9,
"seed": 0
},
"nodes": {
"1": {
"class_type": "Replicate_prunaai_z_image_turbo",
"inputs": {
"prompt": "{{positive_prompt}}",
"width": "{{width}}",
"height": "{{height}}",
"num_inference_steps": "{{steps}}",
"seed": "{{seed}}"
},
"_meta": {
"title": "Z-Image Turbo (Replicate)"
}
},
"2": {
"class_type": "SaveImage",
"inputs": {
"filename_prefix": "ComfyChair",
"images": [
"1",
0
]
},
"_meta": {
"title": "Save Image"
}
}
}
}
@@ -0,0 +1,49 @@
{
"name": "Z-Image Turbo I2I (Replicate)",
"description": "Image-to-Image via prunaai/z-image-turbo-img2img (statt Inpaint: strength steuert Nähe zum Original)",
"defaults": {
"strength": 0.6,
"steps": 9,
"seed": 0
},
"nodes": {
"1": {
"class_type": "LoadImage",
"inputs": {
"image": "uploaded_image.png [input]"
},
"_meta": {
"title": "Load Image"
}
},
"2": {
"class_type": "Replicate_prunaai_z_image_turbo_img2img",
"inputs": {
"prompt": "{{positive_prompt}}",
"image": [
"1",
0
],
"strength": "{{strength}}",
"num_inference_steps": "{{steps}}",
"seed": "{{seed}}"
},
"_meta": {
"title": "Z-Image Turbo I2I (Replicate)"
}
},
"3": {
"class_type": "SaveImage",
"inputs": {
"filename_prefix": "ComfyChair",
"images": [
"2",
0
]
},
"_meta": {
"title": "Save Image"
}
}
}
}