Files
Meisoftcoltd-comfyui-sequen…/loop.py
T
Meisoftcoltd 73581ac582 refactor: clean up repository to v1.0.0 and implement recursive self-queuing architecture
- Delete deprecated modules: `sequence.py`, `paths.py`, `batch.py`, `debug.py`.
- Clean `image.py`, retaining only SessionImageReceiver, SessionImageSender, and auxiliary function.
- Clean `video.py`, retaining only IncrementalVideoStitcher and WanFrameValidator.
- Clean `loop.py`, retaining only SequentialLoopStart and SequentialLoopTrigger.
- Rewrite `__init__.py` mapping explicitly to the 6 core components and bump version to 1.0.0.
2026-03-22 19:25:11 +00:00

103 lines
3.8 KiB
Python

import urllib.request
import json
from . import register_node
# Variable global para mantener el estado del bucle entre ejecuciones
global_loop_index = 0
@register_node
class SequentialLoopStart:
"""Inicia el bucle y provee el índice actual a los nodos de imagen y vídeo."""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"reset_loop": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("INT",)
RETURN_NAMES = ("current_loop_index",)
FUNCTION = "get_index"
CATEGORY = "🔁 Sequential Batcher/Loop"
@classmethod
def IS_CHANGED(cls, **kwargs):
return float("NaN") # Forzamos ejecución siempre
def get_index(self, reset_loop):
global global_loop_index
# Saneamiento de booleano por si viene de un text input
is_reset = False
if isinstance(reset_loop, str):
is_reset = reset_loop.strip().lower() in ['true', '1', 't', 'y', 'yes']
else:
is_reset = bool(reset_loop)
if is_reset:
global_loop_index = 0
print("🔄 [Sequential Batcher] Loop Start: Bucle reiniciado a 0 manualmente.")
print(f"🔄 [Sequential Batcher] Loop Start: Ejecutando ciclo {global_loop_index}.")
return (global_loop_index, )
@register_node
class SequentialLoopTrigger:
"""Se ejecuta al final del flujo. Incrementa el contador y auto-encola el siguiente ciclo si es necesario."""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"trigger_dependency": ("*", ), # Cable obligatorio para forzar que espere al final
"target_loops": ("INT", {"default": 4, "min": 1, "max": 1000, "step": 1}),
"port": ("INT", {"default": 8188, "min": 1000, "max": 9999, "step": 1}), # Puerto de ComfyUI (estándar 8188)
},
# MAGIA OCULTA: Pedimos a ComfyUI que nos pase el lienzo actual
"hidden": {
"prompt": "PROMPT",
"extra_pnginfo": "EXTRA_PNGINFO"
}
}
RETURN_TYPES = ()
RETURN_NAMES = ()
OUTPUT_NODE = True
FUNCTION = "trigger_next"
CATEGORY = "🔁 Sequential Batcher/Loop"
@classmethod
def IS_CHANGED(cls, **kwargs):
return float("NaN")
def trigger_next(self, trigger_dependency, target_loops, port, prompt=None, extra_pnginfo=None):
global global_loop_index
# 1. Incrementamos el contador
global_loop_index += 1
# 2. Comprobamos si debemos continuar
if global_loop_index < target_loops:
print(f"🚀 [Sequential Batcher] Loop Trigger: Ciclo {global_loop_index}/{target_loops}. Encolando automáticamente el siguiente lote...")
# 3. Preparamos la carga útil (payload) exactamente como la espera ComfyUI
p = {"prompt": prompt}
if extra_pnginfo:
p["extra_data"] = {"extra_pnginfo": extra_pnginfo}
data = json.dumps(p).encode('utf-8')
req = urllib.request.Request(f"http://127.0.0.1:{port}/prompt", data=data, headers={'Content-Type': 'application/json'})
# 4. Disparamos la petición a nuestro propio servidor con timeout de 5s para evitar cuelgues
try:
urllib.request.urlopen(req, timeout=5)
print("✅ [Sequential Batcher] Loop Trigger: Siguiente lote inyectado en la cola con éxito.")
except Exception as e:
print(f"❌ [Sequential Batcher] Error al auto-encolar: {e}")
else:
print(f"🏁 [Sequential Batcher] Loop Trigger: ¡Generación finalizada! Se han completado los {target_loops} ciclos.")
# Reseteamos automáticamente para la próxima vez que el usuario pulse Queue
global_loop_index = 0
return ()