From 11096bc0aaebaefd0b774963a2c323b82f522fe7 Mon Sep 17 00:00:00 2001 From: Ryan Meador <1176600+ryanmeador@users.noreply.github.com> Date: Tue, 2 Jun 2026 23:43:58 -0400 Subject: [PATCH] feat(video): add thumbnail save toggle --- modules/framepack/framepack_api.py | 2 ++ modules/framepack/framepack_ui.py | 4 ++-- modules/framepack/framepack_worker.py | 4 +++- modules/framepack/framepack_wrappers.py | 4 ++-- modules/ltx/ltx_process.py | 2 ++ modules/ltx/ltx_ui.py | 4 ++-- modules/ui_video.py | 8 ++++---- modules/video_models/video_run.py | 3 ++- modules/video_models/video_save.py | 7 ++++--- modules/video_models/video_ui.py | 7 ++++--- 10 files changed, 27 insertions(+), 18 deletions(-) diff --git a/modules/framepack/framepack_api.py b/modules/framepack/framepack_api.py index 9283eaaec..99efdcfb1 100644 --- a/modules/framepack/framepack_api.py +++ b/modules/framepack/framepack_api.py @@ -33,6 +33,7 @@ class ReqFramepack(BaseModel): mp4_sf: bool | None = Field(default=False, title="Save SafeTensors", description="Save SafeTensors for the video") mp4_video: bool | None = Field(default=True, title="Save Video", description="Save video") mp4_frames: bool | None = Field(default=False, title="Save Frames", description="Save frames for the video") + mp4_thumb: bool | None = Field(default=True, title="Save Thumbnail", description="Save thumbnail for the video") mp4_opt: str | None = Field(default="crf:16", title="Options", description="Options for the video codec") mp4_ext: str | None = Field(default="mp4", title="Format", description="Format for the video") mp4_interpolate: int | None = Field(default=0, title="Interpolation", description="Interpolation for the video") @@ -109,6 +110,7 @@ def framepack_post(request: ReqFramepack): mp4_sf=request.mp4_sf, mp4_video=request.mp4_video, mp4_frames=request.mp4_frames, + mp4_thumb=request.mp4_thumb, mp4_opt=request.mp4_opt, mp4_ext=request.mp4_ext, mp4_interpolate=request.mp4_interpolate, diff --git a/modules/framepack/framepack_ui.py b/modules/framepack/framepack_ui.py index 93b57863a..92029085a 100644 --- a/modules/framepack/framepack_ui.py +++ b/modules/framepack/framepack_ui.py @@ -12,7 +12,7 @@ def change_sections(duration, mp4_fps, mp4_interpolate, latent_ws, variant): return gr.update(value=f'Target video: {num_frames} frames in {num_sections} sections'), gr.update(lines=max(2, 2*num_sections//3)) -def create_ui(prompt, negative, styles, _overrides, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf): +def create_ui(prompt, negative, styles, _overrides, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, mp4_thumb): with gr.Row(): with gr.Column(variant='compact', elem_id="framepack_settings", elem_classes=['settings-column'], scale=1): with gr.Row(): @@ -106,7 +106,7 @@ def create_ui(prompt, negative, styles, _overrides, mp4_fps, mp4_interpolate, mp cfg_scale, cfg_distilled, cfg_rescale, shift, use_teacache, use_cfgzero, use_preview, - mp4_fps, mp4_codec, mp4_sf, mp4_video, mp4_frames, mp4_opt, mp4_ext, mp4_interpolate, + mp4_fps, mp4_codec, mp4_sf, mp4_video, mp4_frames, mp4_thumb, mp4_opt, mp4_ext, mp4_interpolate, attention, vae_type, variant, vlm_enhance, vlm_model, vlm_system_prompt, ] diff --git a/modules/framepack/framepack_worker.py b/modules/framepack/framepack_worker.py index 09b62b6cb..0504de761 100644 --- a/modules/framepack/framepack_worker.py +++ b/modules/framepack/framepack_worker.py @@ -41,7 +41,7 @@ def worker( cfg_scale, cfg_distilled, cfg_rescale, shift, use_teacache, use_cfgzero, use_preview, - mp4_fps, mp4_codec, mp4_sf, mp4_video, mp4_frames, mp4_opt, mp4_ext, mp4_interpolate, + mp4_fps, mp4_codec, mp4_sf, mp4_video, mp4_frames, mp4_thumb, mp4_opt, mp4_ext, mp4_interpolate, vae_type, variant, metadata: dict | None = None, @@ -343,6 +343,7 @@ def worker( mp4_sf=mp4_sf, mp4_video=mp4_video, mp4_frames=mp4_frames, + mp4_thumb=mp4_thumb, mp4_interpolate=0, pbar=pbar, stream=stream, @@ -364,6 +365,7 @@ def worker( mp4_sf=mp4_sf, mp4_video=mp4_video, mp4_frames=mp4_frames, + mp4_thumb=mp4_thumb, mp4_interpolate=0, pbar=pbar, stream=stream, diff --git a/modules/framepack/framepack_wrappers.py b/modules/framepack/framepack_wrappers.py index b9a5e5592..0e3c546ad 100644 --- a/modules/framepack/framepack_wrappers.py +++ b/modules/framepack/framepack_wrappers.py @@ -114,7 +114,7 @@ def unload_model(): yield gr.update(), gr.update(), 'Model unloaded' -def run_framepack(task_id, _ui_state, init_image, end_image, start_weight, end_weight, vision_weight, prompt, system_prompt, optimized_prompt, section_prompt, negative_prompt, styles, seed, resolution, duration, latent_ws, steps, cfg_scale, cfg_distilled, cfg_rescale, shift, use_teacache, use_cfgzero, use_preview, mp4_fps, mp4_codec, mp4_sf, mp4_video, mp4_frames, mp4_opt, mp4_ext, mp4_interpolate, attention, vae_type, variant, vlm_enhance, vlm_model, vlm_system_prompt): +def run_framepack(task_id, _ui_state, init_image, end_image, start_weight, end_weight, vision_weight, prompt, system_prompt, optimized_prompt, section_prompt, negative_prompt, styles, seed, resolution, duration, latent_ws, steps, cfg_scale, cfg_distilled, cfg_rescale, shift, use_teacache, use_cfgzero, use_preview, mp4_fps, mp4_codec, mp4_sf, mp4_video, mp4_frames, mp4_thumb, mp4_opt, mp4_ext, mp4_interpolate, attention, vae_type, variant, vlm_enhance, vlm_model, vlm_system_prompt): variant = variant or 'bi-directional' if init_image is None: init_image = np.zeros((resolution, resolution, 3), dtype=np.uint8) @@ -181,7 +181,7 @@ def run_framepack(task_id, _ui_state, init_image, end_image, start_weight, end_w cfg_scale, cfg_distilled, cfg_rescale, shift, use_teacache, use_cfgzero, use_preview, - mp4_fps, mp4_codec, mp4_sf, mp4_video, mp4_frames, mp4_opt, mp4_ext, mp4_interpolate, + mp4_fps, mp4_codec, mp4_sf, mp4_video, mp4_frames, mp4_thumb, mp4_opt, mp4_ext, mp4_interpolate, vae_type, variant, ) diff --git a/modules/ltx/ltx_process.py b/modules/ltx/ltx_process.py index acd2220e3..0acd6d3e0 100644 --- a/modules/ltx/ltx_process.py +++ b/modules/ltx/ltx_process.py @@ -148,6 +148,7 @@ def run_ltx(task_id, mp4_video: bool, mp4_frames: bool, mp4_sf: bool, + mp4_thumb: bool, audio_enable: bool, _overrides, ): @@ -643,6 +644,7 @@ def run_ltx(task_id, mp4_sf=mp4_sf, mp4_video=mp4_video, mp4_frames=mp4_frames, + mp4_thumb=mp4_thumb, mp4_interpolate=mp4_interpolate, aac_sample_rate=aac_sample_rate, metadata={}, diff --git a/modules/ltx/ltx_ui.py b/modules/ltx/ltx_ui.py index 44ea0db48..e5b2f1582 100644 --- a/modules/ltx/ltx_ui.py +++ b/modules/ltx/ltx_ui.py @@ -53,7 +53,7 @@ def _model_change(model_name: str): ) -def create_ui(prompt, negative, styles, overrides, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf): +def create_ui(prompt, negative, styles, overrides, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, mp4_thumb): with gr.Row(): with gr.Column(variant='compact', elem_id="ltx_settings", elem_classes=['settings-column'], scale=1): with gr.Row(): @@ -153,7 +153,7 @@ def create_ui(prompt, negative, styles, overrides, mp4_fps, mp4_interpolate, mp4 refine_enable, refine_strength, ltx_condition_strength, ltx_init_image, last_image, condition_files, condition_video, condition_video_frames, condition_video_skip, decode_timestep, image_cond_noise_scale, - mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, + mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, mp4_thumb, audio_enable, overrides, ] diff --git a/modules/ui_video.py b/modules/ui_video.py index 6caa70d0a..910befcc8 100644 --- a/modules/ui_video.py +++ b/modules/ui_video.py @@ -30,24 +30,24 @@ def create_ui(): overrides = ui_common.create_override_inputs('video') with gr.Tab('Output', id='video-outputs-tab') as _video_outputs_tab: from modules.video_models import video_ui - mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf = video_ui.create_ui_outputs() + mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, mp4_thumb = video_ui.create_ui_outputs() with gr.Tab('Generic', id='video-core-tab') as video_core_tab: from modules.video_models import video_ui engine, model, steps, sampler_index, width, height, frames, seed = video_ui.create_ui( prompt, negative, styles, overrides, - mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, + mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, mp4_thumb, ) with gr.Tab('FramePack', id='framepack-tab') as framepack_tab: from modules.framepack import framepack_ui framepack_ui.create_ui( prompt, negative, styles, overrides, - mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, + mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, mp4_thumb, ) with gr.Tab('LTX', id='ltx-tab') as ltx_tab: from modules.ltx import ltx_ui ltx_ui.create_ui( prompt, negative, styles, overrides, - mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, + mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, mp4_thumb, ) paste_fields = [ diff --git a/modules/video_models/video_run.py b/modules/video_models/video_run.py index 25d7d30fe..b23fd98e3 100644 --- a/modules/video_models/video_run.py +++ b/modules/video_models/video_run.py @@ -11,7 +11,7 @@ debug = log.trace if os.environ.get('SD_VIDEO_DEBUG', None) is not None else lam def generate(*args, **kwargs): - task_id, ui_state, engine, model, prompt, negative, styles, width, height, frames, steps, sampler_index, sampler_shift, dynamic_shift, seed, guidance_scale, guidance_true, init_image, init_strength, last_image, vae_type, vae_tile_frames, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, vlm_enhance, vlm_model, vlm_system_prompt, override_settings = args + task_id, ui_state, engine, model, prompt, negative, styles, width, height, frames, steps, sampler_index, sampler_shift, dynamic_shift, seed, guidance_scale, guidance_true, init_image, init_strength, last_image, vae_type, vae_tile_frames, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, mp4_thumb, vlm_enhance, vlm_model, vlm_system_prompt, override_settings = args if engine is None or model is None or engine == 'None' or model == 'None': return video_utils.queue_err('model not selected') @@ -183,6 +183,7 @@ def generate(*args, **kwargs): mp4_sf=mp4_sf, mp4_video=mp4_video, mp4_frames=mp4_frames, + mp4_thumb=mp4_thumb, mp4_interpolate=mp4_interpolate, metadata={}, ) diff --git a/modules/video_models/video_save.py b/modules/video_models/video_save.py index f63ae5d3b..5a5abc652 100644 --- a/modules/video_models/video_save.py +++ b/modules/video_models/video_save.py @@ -36,7 +36,7 @@ def save_params(p, filename: str | None = None): if p is None: dct = {} else: - # sampler_index, sampler_shift, dynamic_shift, guidance_scale, guidance_true, init_image, init_strength, last_image, vae_type, vae_tile_frames, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, vlm_enhance, vlm_model, vlm_system_prompt, override_settings = args + # sampler_index, sampler_shift, dynamic_shift, guidance_scale, guidance_true, init_image, init_strength, last_image, vae_type, vae_tile_frames, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, mp4_thumb, vlm_enhance, vlm_model, vlm_system_prompt, override_settings = args dct = { "Prompt": p.prompt, "Negative prompt": p.negative_prompt, @@ -227,6 +227,7 @@ def save_video( mp4_sf: bool = False, # save safetensors mp4_video: bool = True, # save video mp4_frames: bool = False, # save frames + mp4_thumb: bool = True, # save thumbnail mp4_interpolate: int = 0, # rife interpolation aac_sample_rate: int = 24000, # audio sample rate stream=None, # async progress reporting stream @@ -251,7 +252,7 @@ def save_video( except Exception as e: log.error(f'Video output: file="{output_video}" write error {e}') errors.display(e, 'video') - thumb = save_thumbnail(output_video) + thumb = save_thumbnail(output_video) if mp4_thumb else None return 0, output_video, thumb if pixels is None: @@ -322,5 +323,5 @@ def save_video( log.error(f'Video save: raw={size} {e}') errors.display(e, 'video') timer.process.add('save', time.time()-t_save) - thumb = save_thumbnail(output_video) if output_video is not None else None + thumb = save_thumbnail(output_video) if mp4_thumb and output_video is not None else None return t, output_video, thumb diff --git a/modules/video_models/video_ui.py b/modules/video_models/video_ui.py index baab34bd1..a546ba45e 100644 --- a/modules/video_models/video_ui.py +++ b/modules/video_models/video_ui.py @@ -101,10 +101,11 @@ def create_ui_outputs(): mp4_video = gr.Checkbox(label='Video save video', value=True, elem_id="video_mp4_video") mp4_frames = gr.Checkbox(label='Video save frames', value=False, elem_id="video_mp4_frames") mp4_sf = gr.Checkbox(label='Video save safetensors', value=False, elem_id="video_mp4_sf") - return mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf + mp4_thumb = gr.Checkbox(label='Video save thumbnail', value=True, elem_id="video_mp4_thumb") + return mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, mp4_thumb -def create_ui(prompt, negative, styles, overrides, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf): +def create_ui(prompt, negative, styles, overrides, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, mp4_thumb): with gr.Row(): with gr.Column(variant='compact', elem_id="video_settings", elem_classes=['settings-column'], scale=1): with gr.Row(): @@ -172,7 +173,7 @@ def create_ui(prompt, negative, styles, overrides, mp4_fps, mp4_interpolate, mp4 guidance_scale, guidance_true, init_image, init_strength, last_image, vae_type, vae_tile_frames, - mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, + mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, mp4_thumb, vlm_enhance, vlm_model, vlm_system_prompt, overrides, ]