Files
Meisoftcoltd-comfyui-sequen…/image.py
T
Meisoftcoltd e87cad300c feat: add explicit input validations to key nodes
- `loop.py`: Added check for `trigger_dependency` in `SequentialLoopTrigger.trigger_next`.
- `image.py`: Added checks for `generated_images` and `current_loop_index` in `SessionImageSender.set_image`.
- `video.py`: Added list-aware checks for `trigger` and `current_loop_index` in `IncrementalVideoStitcher.stitch_incremental` due to `INPUT_IS_LIST = True`.

All validations throw a `ValueError` with clear Spanish instructions when inputs are missing to provide immediate user feedback on the ComfyUI canvas.
2026-03-23 20:49:22 +00:00

118 lines
4.0 KiB
Python

import os
import time
import random
import numpy as np
import torch
from PIL import Image
import folder_paths
from . import register_node
global_session_image = None
def tensor_to_temp_image(tensor_image, prefix="session_img"):
temp_dir = folder_paths.get_temp_directory()
filename = f"{prefix}_{random.randint(10000, 99999)}.png"
filepath = os.path.join(temp_dir, filename)
img_array = 255. * tensor_image[0].cpu().numpy()
img = Image.fromarray(np.clip(img_array, 0, 255).astype(np.uint8))
img.save(filepath)
return {"filename": filename, "subfolder": "", "type": "temp"}
@register_node
class SessionImageReceiver:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"initial_image": ("IMAGE",),
"current_loop_index": ("INT", {"default": 0, "min": 0, "max": 10000}),
},
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("current_image",)
OUTPUT_NODE = True
FUNCTION = "get_image"
CATEGORY = "🔁 Sequential Batcher/Image"
@classmethod
def IS_CHANGED(cls, **kwargs):
return time.time()
def get_image(self, initial_image, current_loop_index):
global global_session_image
loop_idx = current_loop_index[0] if isinstance(current_loop_index, list) else current_loop_index
is_first = (loop_idx == 0)
print(f"\n{'='*50}")
print(f"📥 [DEBUG] NODO: Image Receiver")
print(f" -> Ciclo actual detectado: {loop_idx}")
if is_first or global_session_image is None:
global_session_image = initial_image.clone().cpu()
print(f" -> 🆕 Iniciando sesión con la imagen ORIGINAL.")
selected = initial_image
else:
print(f" -> ♻️ Usando el fotograma rescatado de la RAM.")
selected = global_session_image
print(f" -> 🖼️ Tensor shape: {selected.shape}")
print(f"{'='*50}\n")
ui_image = tensor_to_temp_image(selected, "receiver")
return {"ui": {"images": [ui_image]}, "result": (selected, )}
@register_node
class SessionImageSender:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"generated_images": ("IMAGE",),
"current_loop_index": ("INT", {"default": 0, "min": 0, "max": 10000}),
},
}
RETURN_TYPES = ()
RETURN_NAMES = ()
OUTPUT_NODE = True
FUNCTION = "set_image"
CATEGORY = "🔁 Sequential Batcher/Image"
@classmethod
def IS_CHANGED(cls, **kwargs):
return time.time()
def set_image(self, generated_images, current_loop_index):
if generated_images is None:
raise ValueError("❌ ERROR CRÍTICO: El nodo 'Session Image Sender' no está recibiendo imágenes. Conecta la salida de tu VAE Decode o Sampler.")
if current_loop_index is None:
raise ValueError("❌ ERROR CRÍTICO: El nodo 'Session Image Sender' necesita el cable de 'current_loop_index' desde el Loop Start para nombrar los archivos.")
global global_session_image
loop_idx = current_loop_index[0] if isinstance(current_loop_index, list) else current_loop_index
print(f"\n{'='*50}")
print(f"📤 [DEBUG] NODO: Image Sender")
print(f" -> Ciclo actual: {loop_idx} | Frames recibidos: {generated_images.shape[0]}")
last_frame = generated_images[-1:].clone().cpu()
global_session_image = last_frame
# Guardar disco progresivo
out_dir = folder_paths.get_output_directory()
filename = f"keyframe_{loop_idx:03d}.png"
filepath = os.path.join(out_dir, filename)
img_array = 255. * last_frame[0].numpy()
img = Image.fromarray(np.clip(img_array, 0, 255).astype(np.uint8))
img.save(filepath)
print(f" -> 💾 Keyframe guardado físicamente: {filename}")
print(f" -> 🧠 RAM asegurada para el próximo ciclo.")
print(f"{'='*50}\n")
ui_image = tensor_to_temp_image(last_frame, "sender")
return {"ui": {"images": [ui_image]}}