v1.10.13: Fix Ditto Sampler interruption issue
This commit is contained in:
@@ -1175,6 +1175,11 @@ B: 太神奇了!那我们快去生成试试吧!
|
||||
|
||||
## Changelog
|
||||
|
||||
### [1.10.13] - 2026-02-02
|
||||
|
||||
- **Ditto Sampler**: 修复了采样过程中无法正常响应 ComfyUI 中断/取消信号的问题。
|
||||
- 为所有工作线程增加了超时检测和状态轮询,支持在长任务执行期间即时退出。
|
||||
|
||||
### [1.10.12] - 2026-02-02
|
||||
|
||||
- **Debug & Utilities**: 新增 **Text Debug Splicer** 节点。
|
||||
|
||||
+13
-2
@@ -5,6 +5,7 @@ import numpy as np
|
||||
from PIL import Image
|
||||
import folder_paths
|
||||
import logging
|
||||
import comfy.model_management # Added for interruption check
|
||||
|
||||
|
||||
# Add Ditto library to path
|
||||
@@ -972,8 +973,18 @@ class AIIA_DittoSampler:
|
||||
aud_feat = master_sdk.wav2feat.wav2feat(audio_np)
|
||||
master_sdk.audio2motion_queue.put(aud_feat)
|
||||
|
||||
# 5. Wait for completion
|
||||
# master_sdk.close() joins threads.
|
||||
# 5. Wait for completion with Interruption Check
|
||||
# Replace blocking master_sdk.close() with polling
|
||||
import time
|
||||
while any(t.is_alive() for t in master_sdk.thread_list):
|
||||
if comfy.model_management.should_stop():
|
||||
logger.info("[Ditto] Interruption detected. Stopping workers...")
|
||||
master_sdk.stop_event.set()
|
||||
# Flush queues to unblock workers waiting on put
|
||||
# (Though we already added timeout in SDK, this is faster)
|
||||
break
|
||||
time.sleep(0.1)
|
||||
|
||||
master_sdk.close()
|
||||
except Exception as e:
|
||||
logging.error(f"Error during Ditto inference: {e}")
|
||||
|
||||
@@ -295,13 +295,24 @@ class StreamSDK:
|
||||
except queue.Empty:
|
||||
continue
|
||||
if item is None:
|
||||
self.writer_queue.put(None)
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
self.writer_queue.put(None, timeout=1)
|
||||
break
|
||||
except queue.Full:
|
||||
continue
|
||||
break
|
||||
frame_idx, render_img = item
|
||||
frame_rgb = self.source_info["img_rgb_lst"][frame_idx]
|
||||
M_c2o = self.source_info["M_c2o_lst"][frame_idx]
|
||||
res_frame_rgb = self.putback(frame_rgb, render_img, M_c2o)
|
||||
self.writer_queue.put(res_frame_rgb)
|
||||
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
self.writer_queue.put(res_frame_rgb, timeout=1)
|
||||
break
|
||||
except queue.Full:
|
||||
continue
|
||||
|
||||
def decode_f3d_worker(self):
|
||||
try:
|
||||
@@ -317,11 +328,22 @@ class StreamSDK:
|
||||
except queue.Empty:
|
||||
continue
|
||||
if item is None:
|
||||
self.putback_queue.put(None)
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
self.putback_queue.put(None, timeout=1)
|
||||
break
|
||||
except queue.Full:
|
||||
continue
|
||||
break
|
||||
frame_idx, f_3d = item
|
||||
render_img = self.decode_f3d(f_3d)
|
||||
self.putback_queue.put([frame_idx, render_img])
|
||||
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
self.putback_queue.put([frame_idx, render_img], timeout=1)
|
||||
break
|
||||
except queue.Full:
|
||||
continue
|
||||
|
||||
def warp_f3d_worker(self):
|
||||
try:
|
||||
@@ -337,12 +359,23 @@ class StreamSDK:
|
||||
except queue.Empty:
|
||||
continue
|
||||
if item is None:
|
||||
self.decode_f3d_queue.put(None)
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
self.decode_f3d_queue.put(None, timeout=1)
|
||||
break
|
||||
except queue.Full:
|
||||
continue
|
||||
break
|
||||
frame_idx, x_s, x_d = item
|
||||
f_s = self.source_info["f_s_lst"][frame_idx]
|
||||
f_3d = self.warp_f3d(f_s, x_s, x_d)
|
||||
self.decode_f3d_queue.put([frame_idx, f_3d])
|
||||
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
self.decode_f3d_queue.put([frame_idx, f_3d], timeout=1)
|
||||
break
|
||||
except queue.Full:
|
||||
continue
|
||||
|
||||
def motion_stitch_worker(self):
|
||||
try:
|
||||
@@ -358,13 +391,24 @@ class StreamSDK:
|
||||
except queue.Empty:
|
||||
continue
|
||||
if item is None:
|
||||
self.warp_f3d_queue.put(None)
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
self.warp_f3d_queue.put(None, timeout=1)
|
||||
break
|
||||
except queue.Full:
|
||||
continue
|
||||
break
|
||||
|
||||
frame_idx, x_d_info, ctrl_kwargs = item
|
||||
x_s_info = self.source_info["x_s_info_lst"][frame_idx]
|
||||
x_s, x_d = self.motion_stitch(x_s_info, x_d_info, **ctrl_kwargs)
|
||||
self.warp_f3d_queue.put([frame_idx, x_s, x_d])
|
||||
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
self.warp_f3d_queue.put([frame_idx, x_s, x_d], timeout=1)
|
||||
break
|
||||
except queue.Full:
|
||||
continue
|
||||
|
||||
def audio2motion_worker(self):
|
||||
try:
|
||||
@@ -401,6 +445,8 @@ class StreamSDK:
|
||||
min_silence_for_reset = 30 # ~1.2s @ 25fps. Avoid resetting on standard sentence pauses.
|
||||
|
||||
while idx < num_frames:
|
||||
if self.stop_event.is_set():
|
||||
break
|
||||
pbar.update()
|
||||
|
||||
vad_timeline = getattr(self, "vad_timeline", None)
|
||||
@@ -515,7 +561,12 @@ class StreamSDK:
|
||||
|
||||
break
|
||||
|
||||
self.motion_stitch_queue.put(None)
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
self.motion_stitch_queue.put(None, timeout=1)
|
||||
break
|
||||
except queue.Full:
|
||||
continue
|
||||
|
||||
|
||||
def _audio2motion_worker(self):
|
||||
@@ -615,14 +666,30 @@ class StreamSDK:
|
||||
if is_end:
|
||||
break
|
||||
|
||||
self.motion_stitch_queue.put(None)
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
self.motion_stitch_queue.put(None, timeout=1)
|
||||
break
|
||||
except queue.Full:
|
||||
continue
|
||||
|
||||
def close(self):
|
||||
# flush frames
|
||||
self.audio2motion_queue.put(None)
|
||||
# flush frames - using non-blocking put as it might be called during interruption
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
self.audio2motion_queue.put(None, timeout=1)
|
||||
break
|
||||
except queue.Full:
|
||||
continue
|
||||
|
||||
# Wait for worker threads to finish
|
||||
for thread in self.thread_list:
|
||||
thread.join()
|
||||
while thread.is_alive():
|
||||
thread.join(timeout=0.1)
|
||||
# Check ComfyUI interruption if needed (caller should handle this usually)
|
||||
# but we just want to ensure we don't block forever if stop_event is set elsewhere
|
||||
if self.stop_event.is_set() and not thread.is_alive():
|
||||
break
|
||||
|
||||
try:
|
||||
self.writer.close()
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "aiia"
|
||||
description = "The Ultimate AI Audio/Video toolkit for ComfyUI. Features an enhanced Ditto (with optimizations that outperform official demos and other SOTA talking head models in lip-sync accuracy and natural motion), EchoMimic V3 & FLOAT, VibeVoice & CosyVoice 3.0 (Zero-Shot Voice Cloning), Multi-Role Podcast Generation, and a powerful Media Browser."
|
||||
version = "1.10.12"
|
||||
version = "1.10.13"
|
||||
license = {file = "LICENSE"}
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
|
||||
Reference in New Issue
Block a user