From ba4436916d249cc3dbdd85a8b74df68468320e3f Mon Sep 17 00:00:00 2001 From: CalamitousFelicitousness Date: Mon, 27 Apr 2026 01:32:30 +0100 Subject: [PATCH] fix(control): prevent duplicate save_video and unmask video module shadow When a video script (animatediff, text2video, image2video, stablevideodiffusion) runs via Control tab, both the script's save and control_run's end-of-run save fired. The latter crashed silently because the local `video` cv2 capture name at control_run:584 shadowed the modules.video import, so the duplicate was hidden and the gallery video link never propagated. - alias import as video_module to bypass the shadow - p.video_saved marker set by each script - control_run skips its end-of-run save when the marker is set --- modules/control/run.py | 5 +++-- scripts/animatediff.py | 1 + scripts/image2video.py | 1 + scripts/stablevideodiffusion.py | 1 + scripts/text2video.py | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/control/run.py b/modules/control/run.py index 90baacb71..c201367f5 100644 --- a/modules/control/run.py +++ b/modules/control/run.py @@ -19,6 +19,7 @@ from modules.ui_common import infotext_to_html from modules.api import script from modules.generation_parameters_copypaste import create_override_settings_dict from modules.paths import resolve_output_path +from modules import video as video_module # alias avoids shadow by local `video` cv2 capture name at control_run:584 debug = os.environ.get('SD_CONTROL_DEBUG', None) is not None @@ -795,9 +796,9 @@ def control_run(state: str = '', # pylint: disable=keyword-arg-before-vararg image_txt = '' p.init_images = output_images # may be used for hires - if video_type != 'None' and isinstance(output_images, list) and 'video' in p.ops: + if video_type != 'None' and isinstance(output_images, list) and 'video' in p.ops and not getattr(p, 'video_saved', False): p.do_not_save_grid = True # pylint: disable=attribute-defined-outside-init - output_filename = video.save_video(p, filename=None, images=output_images, video_type=video_type, duration=video_duration, loop=video_loop, pad=video_pad, interpolate=video_interpolate, sync=True) + output_filename = video_module.save_video(p, filename=None, images=output_images, video_type=video_type, duration=video_duration, loop=video_loop, pad=video_pad, interpolate=video_interpolate, sync=True) if shared.opts.gradio_skip_video: output_filename = '' image_txt = f'| Frames {len(output_images)} | Size {output_images[0].width}x{output_images[0].height}' diff --git a/scripts/animatediff.py b/scripts/animatediff.py index 1800a7218..bb7570488 100644 --- a/scripts/animatediff.py +++ b/scripts/animatediff.py @@ -268,3 +268,4 @@ class AnimateDiffScript(scripts_manager.Script): if video_type != 'None': log.debug(f'AnimateDiff video: type={video_type} duration={duration} loop={gif_loop} pad={mp4_pad} interpolate={mp4_interpolate}') save_video(p, filename=None, images=processed.images, video_type=video_type, duration=duration, loop=gif_loop, pad=mp4_pad, interpolate=mp4_interpolate) + p.video_saved = True diff --git a/scripts/image2video.py b/scripts/image2video.py index 4041db5f8..05f82a037 100644 --- a/scripts/image2video.py +++ b/scripts/image2video.py @@ -112,4 +112,5 @@ class VGenI2VScript(scripts_manager.Script): shared.sd_model = orig_pipeline if video_type != 'None' and processed is not None: video.save_video(p, filename=None, images=processed.images, video_type=video_type, duration=duration, loop=gif_loop, pad=mp4_pad, interpolate=mp4_interpolate) + p.video_saved = True return processed diff --git a/scripts/stablevideodiffusion.py b/scripts/stablevideodiffusion.py index 3fa2a5cc2..6e023dd46 100644 --- a/scripts/stablevideodiffusion.py +++ b/scripts/stablevideodiffusion.py @@ -123,4 +123,5 @@ class SVDScript(scripts_manager.Script): processed = processing.process_images(p) if video_type != 'None': video.save_video(p, filename=None, images=processed.images, video_type=video_type, duration=duration, loop=gif_loop, pad=mp4_pad, interpolate=mp4_interpolate) + p.video_saved = True return processed diff --git a/scripts/text2video.py b/scripts/text2video.py index eaa737ac0..a3726f63d 100644 --- a/scripts/text2video.py +++ b/scripts/text2video.py @@ -91,4 +91,5 @@ class ModelScopeScript(scripts_manager.Script): if video_type != 'None': video.save_video(p, filename=None, images=processed.images, video_type=video_type, duration=duration, loop=gif_loop, pad=mp4_pad, interpolate=mp4_interpolate) + p.video_saved = True return processed