Replaced loop.py and image.py, and updated video.py to fix the 'reverse cache' issue by injecting the new index directly into the ComfyUI API JSON payload. Added large debug banners in Spanish, PNG keyframe extraction, and progressive MP4 saving.
114 lines
4.0 KiB
Python
114 lines
4.0 KiB
Python
import random
|
|
import time
|
|
import urllib.request
|
|
import json
|
|
from . import register_node
|
|
|
|
global_loop_index = 0
|
|
|
|
@register_node
|
|
class SequentialLoopStart:
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"reset_loop": ("BOOLEAN", {"default": False}),
|
|
"loop_idx": ("INT", {"default": 0, "min": 0, "max": 10000}), # Inyectado por el Trigger
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("INT",)
|
|
RETURN_NAMES = ("current_loop_index",)
|
|
FUNCTION = "get_index"
|
|
CATEGORY = "🔁 Sequential Batcher/Loop"
|
|
|
|
@classmethod
|
|
def IS_CHANGED(cls, **kwargs):
|
|
return time.time()
|
|
|
|
def get_index(self, reset_loop, loop_idx):
|
|
global global_loop_index
|
|
|
|
print(f"\n{'='*50}")
|
|
print(f"🚀 [DEBUG] NODO: Loop Start")
|
|
print(f" -> Input loop_idx (UI/Trigger): {loop_idx}")
|
|
print(f" -> Input reset_loop: {reset_loop}")
|
|
|
|
is_reset = str(reset_loop).lower() in ['true', '1', 't', 'y']
|
|
if is_reset:
|
|
global_loop_index = 0
|
|
print(" -> 🔄 Bucle reiniciado a 0 manualmente.")
|
|
else:
|
|
global_loop_index = loop_idx
|
|
|
|
print(f" -> 📤 OUTPUT current_loop_index: {global_loop_index}")
|
|
print(f"{'='*50}\n")
|
|
|
|
return (global_loop_index, )
|
|
|
|
@register_node
|
|
class SequentialLoopTrigger:
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"trigger_dependency": ("*", ),
|
|
"target_loops": ("INT", {"default": 4, "min": 1, "max": 1000}),
|
|
"port": ("INT", {"default": 8188, "min": 1000, "max": 9999}),
|
|
},
|
|
"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 time.time()
|
|
|
|
def trigger_next(self, trigger_dependency, target_loops, port, prompt=None, extra_pnginfo=None):
|
|
global global_loop_index
|
|
next_loop = global_loop_index + 1
|
|
|
|
print(f"\n{'='*50}")
|
|
print(f"🎯 [DEBUG] NODO: Loop Trigger")
|
|
print(f" -> Ciclo terminado: {global_loop_index} | Target: {target_loops}")
|
|
|
|
if next_loop < target_loops:
|
|
print(f" -> ⚙️ Preparando Ciclo {next_loop}...")
|
|
if prompt is not None:
|
|
m_seeds = 0
|
|
for node_id, node_data in prompt.items():
|
|
inputs = node_data.get("inputs", {})
|
|
# Mutar Semillas
|
|
for key in ["seed", "noise_seed"]:
|
|
if key in inputs and isinstance(inputs[key], (int, float)):
|
|
inputs[key] = random.randint(1, 0xffffffff)
|
|
m_seeds += 1
|
|
# 💥 INYECCIÓN ANTI-CACHÉ: Forzar a Loop Start a leer el nuevo índice
|
|
if node_data.get("class_type") == "SequentialLoopStart":
|
|
inputs["loop_idx"] = next_loop
|
|
inputs["reset_loop"] = False
|
|
print(f" -> 💉 Inyectado loop_idx={next_loop} en LoopStart (Nodo {node_id})")
|
|
print(f" -> 🎲 Semillas mutadas: {m_seeds}")
|
|
|
|
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'})
|
|
try:
|
|
urllib.request.urlopen(req, timeout=5)
|
|
print(f" -> ✅ Ciclo {next_loop} inyectado en la cola.")
|
|
except Exception as e:
|
|
print(f" -> ❌ Error HTTP: {e}")
|
|
else:
|
|
print(f" -> 🏁 ¡Generación finalizada! ({target_loops} ciclos)")
|
|
global_loop_index = 0
|
|
|
|
print(f"{'='*50}\n")
|
|
return ()
|