diff --git a/__pycache__/__init__.cpython-312.pyc b/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..89df6e4 Binary files /dev/null and b/__pycache__/__init__.cpython-312.pyc differ diff --git a/__pycache__/image.cpython-312.pyc b/__pycache__/image.cpython-312.pyc new file mode 100644 index 0000000..45127b9 Binary files /dev/null and b/__pycache__/image.cpython-312.pyc differ diff --git a/__pycache__/loop.cpython-312.pyc b/__pycache__/loop.cpython-312.pyc index ae70f83..eba9e68 100644 Binary files a/__pycache__/loop.cpython-312.pyc and b/__pycache__/loop.cpython-312.pyc differ diff --git a/__pycache__/video.cpython-312.pyc b/__pycache__/video.cpython-312.pyc index f69fb31..36f2065 100644 Binary files a/__pycache__/video.cpython-312.pyc and b/__pycache__/video.cpython-312.pyc differ diff --git a/image.py b/image.py index 0dc6e49..06db9dc 100644 --- a/image.py +++ b/image.py @@ -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") diff --git a/video.py b/video.py index 5e96eda..b6c857a 100644 --- a/video.py +++ b/video.py @@ -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: