fix: bypass keyframe save and output full tensor on final cycle

- Updated `SessionImageSender` to conditionally save keyframe only when not in the final cycle.
- Updated `IncrementalVideoStitcher` to return full concatenated tensor when in the final cycle, avoiding dummy frame output and blocking downstream nodes.
- Expose `IS_FINAL_CYCLE` boolean from `IncrementalVideoStitcher` to UI for downstream conditionals.
This commit is contained in:
Meisoftcoltd
2026-04-05 21:32:14 +00:00
parent 8a0ceca136
commit bd6016f559
6 changed files with 39 additions and 24 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+16 -7
View File
@@ -159,21 +159,30 @@ class SessionImageSender:
loop.global_accumulated_frames += advanced_original_frames
source_total = getattr(loop, 'global_source_frame_count', 1)
# 🚀 NUEVO: Detectar si es el ciclo final o un ciclo único
is_final_cycle = loop.global_accumulated_frames >= source_total
print(f" -> ✂️ Tensor truncado a {frames_accepted} frames válidos.")
print(f" -> 📈 Timeline avanzado a {loop.global_accumulated_frames} / {source_total}")
last_frame = valid_images[-1:].clone().cpu()
global_session_image = last_frame
out_dir = folder_paths.get_output_directory()
filename = f"keyframe_{loop_idx:03d}.png"
filepath = os.path.join(out_dir, filename)
# 🛡️ Solo guardamos el keyframe si habrá un próximo ciclo
if not is_final_cycle:
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)
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 seguro guardado: {filename}")
else:
print(f" -> 🚀 Ciclo final/único detectado. Omitiendo guardado de keyframe.")
print(f" -> 💾 Keyframe seguro guardado: {filename}")
print(f"{'='*50}\n")
ui_image = tensor_to_temp_image(last_frame, "sender")
+23 -17
View File
@@ -306,8 +306,8 @@ class IncrementalVideoStitcher:
}
}
RETURN_TYPES = ("IMAGE", "AUDIO")
RETURN_NAMES = ("ALL_IMAGES", "AUDIO_OUT")
RETURN_TYPES = ("IMAGE", "AUDIO", "BOOLEAN")
RETURN_NAMES = ("ALL_IMAGES", "AUDIO_OUT", "IS_FINAL_CYCLE")
FUNCTION = "stitch"
CATEGORY = "🔁 Sequential Batcher/Video"
@@ -322,25 +322,31 @@ class IncrementalVideoStitcher:
torch.save(images.cpu(), path)
print(f"🎞️ [Stitcher] Lote {current_loop_index} guardado en disco ({images.shape[0]} frames).")
is_final = loop.global_accumulated_frames >= loop.global_source_frame_count
source_total = getattr(loop, 'global_source_frame_count', 1)
is_final_cycle = loop.global_accumulated_frames >= source_total
if not is_final:
preview_frame = images[0:1]
return (preview_frame, None)
if is_final_cycle:
print(f" -> 🏁 Generación completa. Liberando tensor total al flujo para procesos finales.")
print(f"📦 [Stitcher] Ensamblando todos los lotes de vídeo...")
all_tensors = []
for i in range(current_loop_index + 1):
p = os.path.join(cache_dir, f"batch_{i:04d}.pt")
if os.path.exists(p):
all_tensors.append(torch.load(p))
try: os.remove(p)
except: pass
print(f"📦 [Stitcher] Ensamblando todos los lotes de vídeo...")
all_tensors = []
for i in range(current_loop_index + 1):
p = os.path.join(cache_dir, f"batch_{i:04d}.pt")
if os.path.exists(p):
all_tensors.append(torch.load(p))
try: os.remove(p)
except: pass
final_tensor = torch.cat(all_tensors, dim=0)
print(f"✅ [Stitcher] Vídeo completado: {final_tensor.shape[0]} frames ensamblados.")
final_images = torch.cat(all_tensors, dim=0)
print(f"✅ [Stitcher] Vídeo completado: {final_images.shape[0]} frames ensamblados.")
# 🚀 Retornamos True al final
return (final_tensor, audio, True)
else:
print(f" -> ⏳ Ciclo intermedio. Soltando 1 frame dummy...")
dummy_frame = images[-1:].clone()
return (final_images, audio)
# 🚀 Retornamos False al final
return (dummy_frame, None, False)
@register_node
class AutoLoopCalculatorWan: