Files
Meisoftcoltd-comfyui-sequen…/tools.py
T
Meisoftcoltd e443ec7112 feat: rename ResTool32xLTX to ResTool64xLTX for IC-LoRA compatibility
- Renamed `ResTool32xLTX` to `ResTool64xLTX` and updated DIVISOR to 64 to prevent odd-dimension `ValueError`s when using latent downscale factors of 2.0 (IC-LoRA).
- Updated visual node name in `__init__.py` to accurately reflect the 64x divisor behavior to avoid technical debt.
- Updated documentation in `README.md` and `README_EN.md` to reflect the rename and explain the downscale compatibility logic.
2026-04-03 13:06:59 +00:00

129 lines
4.5 KiB
Python

import math
from . import register_node
# 🧠 Clase Base Invisible (El molde matemático por Megapíxeles)
class BaseResolutionTool:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"aspect_ratio": (["16:9", "9:16", "1:1", "4:3", "3:4", "21:9", "3:2", "2:3"],),
# 💡 Cambio clave: Dejamos de usar combo y pasamos a un campo INT libre
"base_resolution": ("INT", {"default": 1024, "min": 128, "max": 8192, "step": 8}),
}
}
RETURN_TYPES = ("INT", "INT", "STRING")
RETURN_NAMES = ("width", "height", "debug_info")
FUNCTION = "get_resolution"
CATEGORY = "🔁 Sequential Batcher/Tools"
def get_resolution(self, aspect_ratio, base_resolution):
div = self.DIVISOR
min_pixels = getattr(self, "MIN_PIXELS", 0)
longest_side = int(base_resolution) # Mantenemos el casting por seguridad
w_str, h_str = aspect_ratio.split(':')
w_ratio = float(w_str)
h_ratio = float(h_str)
ratio = w_ratio / h_ratio
print(f"\n{'='*50}")
print(f"🛠️ [DEBUG] NODO: Resolution Tool {div}x")
print(f" -> Petición Original: Lado mayor {longest_side}px | Ratio {aspect_ratio}")
# 1. Calcular resolución inicial teórica
if w_ratio >= h_ratio:
ideal_w = longest_side
ideal_h = ideal_w / ratio
else:
ideal_h = longest_side
ideal_w = ideal_h * ratio
current_pixels = ideal_w * ideal_h
# 2. 🛡️ Protección de Suelo por ÁREA TOTAL (Training Floor)
if current_pixels < min_pixels:
print(f" -> ⚠️ ALERTA: La resolución pedida ({int(current_pixels)} píxeles) es inferior al mínimo vital del modelo ({min_pixels} píxeles).")
print(f" -> 🛡️ Escalando proporcionalmente para evitar artefactos...")
# Factor de escala basado en área
scale_factor = math.sqrt(min_pixels / current_pixels)
ideal_w *= scale_factor
ideal_h *= scale_factor
# 3. Ajuste final de divisibilidad estricta
# Usamos round para asegurar que nos quedamos lo más cerca posible del área ideal
width = max(div, round(ideal_w / div) * div)
height = max(div, round(ideal_h / div) * div)
debug_msg = f"{width}x{height} (Div {div})"
print(f" -> 🎯 Resultado Final Seguro: Ancho {width} | Alto {height} | Píxeles totales: {width*height}")
print(f"{'='*50}\n")
return (width, height, debug_msg)
# 📦 Nodos Específicos Blindados por Píxeles
@register_node
class ResTool8x(BaseResolutionTool):
DIVISOR = 8
MIN_PIXELS = 262144 # SD1.5 (Equivale a 512x512)
@register_node
class ResTool16x(BaseResolutionTool):
DIVISOR = 16
MIN_PIXELS = 1048576 # SDXL (Equivale a 1024x1024)
@register_node
class ResTool32x(BaseResolutionTool):
DIVISOR = 32
MIN_PIXELS = 399360 # WanVideo (Equivale a 832x480)
@register_node
class ResTool64x(BaseResolutionTool):
DIVISOR = 64
MIN_PIXELS = 921600 # Hunyuan (Equivale a 1280x720)
@register_node
class ResTool64xLTX(BaseResolutionTool):
DIVISOR = 64
MIN_PIXELS = 393216 # LTX 2.3 (Equivale a 768x512)
@register_node
class AutoFPSLimiter:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"source_fps": ("FLOAT", {"forceInput": True}),
"target_max_fps": ("FLOAT", {"default": 30.0, "min": 1.0, "max": 120.0, "step": 1.0}),
}
}
RETURN_TYPES = ("INT", "FLOAT")
RETURN_NAMES = ("select_every_nth", "new_fps")
FUNCTION = "calculate_fps"
CATEGORY = "🔁 Sequential Batcher/Tools"
def calculate_fps(self, source_fps, target_max_fps):
import math
print(f"\n{'='*50}")
print(f"⏱️ [DEBUG] NODO: Auto FPS Limiter")
print(f" -> FPS Originales: {source_fps:.2f} | Meta Máxima: {target_max_fps:.2f}")
if source_fps <= target_max_fps:
nth = 1
new_fps = source_fps
print(f" -> ✅ Los FPS originales ya están dentro del límite.")
else:
# Calculamos el salto matemático (hacia arriba) para garantizar no superar la meta
nth = math.ceil(source_fps / target_max_fps)
new_fps = source_fps / nth
print(f" -> ✂️ Reducción necesaria. Procesando 1 de cada {nth} frames.")
print(f" -> 🎯 Resultado: {new_fps:.2f} FPS finales (Nth: {nth})")
print(f"{'='*50}\n")
return (nth, new_fps)