refactor(video): per-engine self-contained tab layout

Each engine tab (Generic, FramePack, LTX) now owns its own size, seed,
frame count, and init imagery. The previously shared Size and Inputs
subtabs are removed; persistent encoding settings move to a single
Output subtab constructed first so engine tabs can consume its widgets.
FramePack drops its local fps/interpolate sliders and consumes the
shared Output widgets; LTX drops the redundant shared init_image
parameter and keeps only its own ltx_init_image, with run_ltx's
condition_image fallback removed in turn.
This commit is contained in:
CalamitousFelicitousness
2026-04-19 04:38:47 +01:00
parent 19b930a221
commit 44759f5f92
5 changed files with 62 additions and 64 deletions
+4 -3
View File
@@ -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, init_image, last_image, 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):
with gr.Row():
with gr.Column(variant='compact', elem_id="framepack_settings", elem_classes=['settings-column'], scale=1):
with gr.Row():
@@ -22,11 +22,12 @@ def create_ui(prompt, negative, styles, _overrides, init_image, last_image, mp4_
with gr.Row():
resolution = gr.Slider(label="FP resolution", minimum=240, maximum=1088, value=640, step=16)
duration = gr.Slider(label="FP duration", minimum=1, maximum=120, value=4, step=0.1)
mp4_fps = gr.Slider(label="FP target FPS", minimum=1, maximum=60, value=24, step=1)
mp4_interpolate = gr.Slider(label="FP interpolation", minimum=0, maximum=10, value=0, step=1)
with gr.Row():
section_html = gr.HTML(show_label=False, elem_id="framepack_section_html")
with gr.Accordion(label="Inputs", open=False):
with gr.Row():
init_image = gr.Image(label='FP init image', elem_id='framepack_init_image', type='pil', image_mode='RGB', width=256, height=256)
last_image = gr.Image(label='FP last image', elem_id='framepack_last_image', type='pil', image_mode='RGB', width=256, height=256)
with gr.Row():
start_weight = gr.Slider(label="FP init strength", value=1.0, minimum=0.0, maximum=2.0, step=0.05, elem_id="framepack_start_weight")
end_weight = gr.Slider(label="FP end strength", value=1.0, minimum=0.0, maximum=2.0, step=0.05, elem_id="framepack_end_weight")
+6 -9
View File
@@ -122,7 +122,6 @@ def run_ltx(task_id,
refine_strength: float,
condition_strength: float,
ltx_init_image,
condition_image,
condition_last,
condition_files,
condition_video,
@@ -213,15 +212,13 @@ def run_ltx(task_id,
from modules.video_models import models_def, video_overrides
selected = next((m for m in models_def.models.get(engine, []) if m.name == model), None)
effective_init_image = ltx_init_image if ltx_init_image is not None else condition_image
if caps.is_i2v and caps.repo_cls_name in ('LTXImageToVideoPipeline', 'LTX2ImageToVideoPipeline') and effective_init_image is None:
if caps.is_i2v and caps.repo_cls_name in ('LTXImageToVideoPipeline', 'LTX2ImageToVideoPipeline') and ltx_init_image is None:
yield from abort('No input image provided. Please upload or select an image.', ok=True)
return
condition_images = []
if effective_init_image is not None:
condition_images.append(effective_init_image)
if ltx_init_image is not None:
condition_images.append(ltx_init_image)
if condition_last is not None:
condition_images.append(condition_last)
conditions = []
@@ -254,7 +251,7 @@ def run_ltx(task_id,
frames=get_frames(frames),
cfg_scale=float(guidance_scale) if guidance_scale is not None and guidance_scale > 0 else caps.default_cfg,
denoising_strength=float(condition_strength) if condition_strength is not None else 1.0,
init_image=effective_init_image,
init_image=ltx_init_image,
vae_type='Default',
vae_tile_frames=16,
)
@@ -279,9 +276,9 @@ def run_ltx(task_id,
if caps.supports_multi_condition and conditions:
p.task_args['conditions'] = conditions
if caps.is_i2v and caps.repo_cls_name in ('LTXImageToVideoPipeline', 'LTX2ImageToVideoPipeline') and effective_init_image is not None:
if caps.is_i2v and caps.repo_cls_name in ('LTXImageToVideoPipeline', 'LTX2ImageToVideoPipeline') and ltx_init_image is not None:
from modules import images
p.task_args['image'] = images.resize_image(resize_mode=2, im=effective_init_image, width=p.width, height=p.height, upscaler_name=None, output_type='pil')
p.task_args['image'] = images.resize_image(resize_mode=2, im=ltx_init_image, width=p.width, height=p.height, upscaler_name=None, output_type='pil')
if caps.family == '2.x' and caps.is_distilled:
from diffusers.pipelines.ltx2.utils import DISTILLED_SIGMA_VALUES
+13 -3
View File
@@ -1,6 +1,7 @@
import os
import gradio as gr
from modules import ui_sections
from modules import ui_sections, ui_symbols
from modules.ui_components import ToolButton
from modules.logger import log
from modules.video_models.models_def import models
from modules.ltx import ltx_process, ltx_capabilities
@@ -48,7 +49,7 @@ def _model_change(model_name: str):
)
def create_ui(prompt, negative, styles, overrides, init_image, _init_strength, last_image, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, width, height, frames, seed):
def create_ui(prompt, negative, styles, overrides, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf):
with gr.Row():
with gr.Column(variant='compact', elem_id="ltx_settings", elem_classes=['settings-column'], scale=1):
with gr.Row():
@@ -56,10 +57,19 @@ def create_ui(prompt, negative, styles, overrides, init_image, _init_strength, l
with gr.Row():
ltx_models = [m.name for m in models['LTX Video']] if 'LTX Video' in models else ['None']
model = gr.Dropdown(label='LTX model', choices=ltx_models, value=ltx_models[0], elem_id="ltx_model")
with gr.Accordion(open=False, label='Size', elem_id='ltx_size_accordion'):
width, height = ui_sections.create_resolution_inputs('ltx', default_width=832, default_height=480)
with gr.Row():
frames = gr.Slider(label='Frames', minimum=1, maximum=1024, step=1, value=121, elem_id='ltx_frames')
seed = gr.Number(label='Initial seed', value=-1, elem_id='ltx_seed', container=True)
random_seed = ToolButton(ui_symbols.random, elem_id='ltx_seed_random')
random_seed.click(fn=lambda: -1, show_progress='hidden', inputs=[], outputs=[seed])
input_media_accordion = gr.Accordion(open=False, label="Input media", elem_id='ltx_input_media_accordion', visible=False)
with input_media_accordion:
ltx_init_image = gr.Image(label='Image', elem_id='ltx_init_image', type='pil', image_mode='RGB', width=256, height=256)
ltx_condition_strength = gr.Slider(label='LTX input strength', minimum=0.0, maximum=1.0, step=0.05, value=1.0, elem_id='ltx_condition_strength')
with gr.Row():
last_image = gr.Image(label='Last image', elem_id='ltx_last_image', type='pil', image_mode='RGB', width=256, height=256)
multi_condition_group = gr.Group(visible=False)
with multi_condition_group:
gr.Markdown('**Prefix conditioning**: supply a video or gallery to anchor the opening frames', elem_id='ltx_prefix_conditioning_label')
@@ -137,7 +147,7 @@ def create_ui(prompt, negative, styles, overrides, init_image, _init_strength, l
seed,
upsample_enable, upsample_ratio,
refine_enable, refine_strength,
ltx_condition_strength, ltx_init_image, init_image, last_image, condition_files, condition_video, condition_video_frames, condition_video_skip,
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,
audio_enable,
+14 -11
View File
@@ -28,24 +28,27 @@ def create_ui():
with gr.Row(elem_id="video_interface", equal_height=False):
with gr.Tabs(elem_classes=['video-tabs'], elem_id='video-tabs'):
overrides = ui_common.create_override_inputs('video')
with gr.Tab('Size', id='video-size-tab') as _video_size_tab:
from modules.video_models import video_ui
width, height, frames, seed, reuse_seed = video_ui.create_ui_size()
with gr.Tab('Inputs', id='video-inputs-tab') as _video_inputs_tab:
from modules.video_models import video_ui
init_image, init_strength, last_image = video_ui.create_ui_inputs()
with gr.Tab('Video Output', id='video-outputs-tab') as _video_outputs_tab:
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()
with gr.Tab('Models', id='video-core-tab') as video_core_tab:
with gr.Tab('Generic', id='video-core-tab') as video_core_tab:
from modules.video_models import video_ui
engine, model, steps, sampler_index = video_ui.create_ui(prompt, negative, styles, overrides, init_image, init_strength, last_image, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, width, height, frames, seed, reuse_seed)
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,
)
with gr.Tab('FramePack', id='framepack-tab') as framepack_tab:
from modules.framepack import framepack_ui
framepack_ui.create_ui(prompt, negative, styles, overrides, init_image, last_image, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf)
framepack_ui.create_ui(
prompt, negative, styles, overrides,
mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf,
)
with gr.Tab('LTX', id='ltx-tab') as ltx_tab:
from modules.ltx import ltx_ui
ltx_ui.create_ui(prompt, negative, styles, overrides, init_image, init_strength, last_image, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, width, height, frames, seed)
ltx_ui.create_ui(
prompt, negative, styles, overrides,
mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf,
)
paste_fields = [
(prompt, "Prompt"), # cannot add more fields as they are not defined yet
+25 -38
View File
@@ -86,17 +86,6 @@ def run_video(*args):
return video_utils.queue_err(f'model not found: engine="{engine}" model="{model}"')
def create_ui_inputs():
with gr.Row():
with gr.Column(variant='compact', elem_id="video_inputs", elem_classes=['settings-column'], scale=1):
init_strength = gr.Slider(label='Init strength', minimum=0.0, maximum=1.0, step=0.01, value=0.8, elem_id="video_denoising_strength")
gr.HTML("<br>&nbsp Init image")
init_image = gr.Image(elem_id="video_image", show_label=False, type="pil", image_mode="RGB", width=256, height=256)
gr.HTML("<br>&nbsp Last image")
last_image = gr.Image(elem_id="video_last", show_label=False, type="pil", image_mode="RGB", width=256, height=256)
return init_image, init_strength, last_image
def create_ui_outputs():
with gr.Row():
with gr.Column(variant='compact', elem_id="video_outputs", elem_classes=['settings-column'], scale=1):
@@ -105,31 +94,17 @@ def create_ui_outputs():
mp4_interpolate = gr.Slider(label="Video interpolation", minimum=0, maximum=10, value=0, step=1)
with gr.Row():
mp4_codec = gr.Dropdown(label="Video codec", choices=['none', 'libx264'], value='libx264', type='value')
ui_common.create_refresh_button(mp4_codec, video_utils.get_codecs, elem_id="framepack_mp4_codec_refresh")
mp4_ext = gr.Textbox(label="Video format", value='mp4', elem_id="framepack_mp4_ext")
mp4_opt = gr.Textbox(label="Video options", value='crf:16', elem_id="framepack_mp4_opt")
ui_common.create_refresh_button(mp4_codec, video_utils.get_codecs, elem_id="video_mp4_codec_refresh")
mp4_ext = gr.Textbox(label="Video format", value='mp4', elem_id="video_mp4_ext")
mp4_opt = gr.Textbox(label="Video options", value='crf:16', elem_id="video_mp4_opt")
with gr.Row():
mp4_video = gr.Checkbox(label='Video save video', value=True, elem_id="framepack_mp4_video")
mp4_frames = gr.Checkbox(label='Video save frames', value=False, elem_id="framepack_mp4_frames")
mp4_sf = gr.Checkbox(label='Video save safetensors', value=False, elem_id="framepack_mp4_sf")
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
def create_ui_size():
with gr.Row():
with gr.Column(variant='compact', elem_id="video_size", elem_classes=['settings-column'], scale=1):
with gr.Row():
width, height = ui_sections.create_resolution_inputs('video', default_width=832, default_height=480)
with gr.Row():
frames = gr.Slider(label='Frames', minimum=1, maximum=1024, step=1, value=17, elem_id="video_frames")
seed = gr.Number(label='Initial seed', value=-1, elem_id="video_seed", container=True)
random_seed = ToolButton(ui_symbols.random, elem_id="video_seed_random")
reuse_seed = ToolButton(ui_symbols.reuse, elem_id="video_seed_reuse")
random_seed.click(fn=lambda: -1, show_progress='hidden', inputs=[], outputs=[seed])
return width, height, frames, seed, reuse_seed
def create_ui(prompt, negative, styles, overrides, init_image, init_strength, last_image, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, width, height, frames, seed, reuse_seed):
def create_ui(prompt, negative, styles, overrides, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf):
with gr.Row():
with gr.Column(variant='compact', elem_id="video_settings", elem_classes=['settings-column'], scale=1):
with gr.Row():
@@ -138,7 +113,7 @@ def create_ui(prompt, negative, styles, overrides, init_image, init_strength, la
engine = gr.Dropdown(label='Video engine', choices=visible_engines(), value='None', elem_id="video_engine")
model = gr.Dropdown(label='Video model', choices=[''], value='None', elem_id="video_model")
btn_load = ToolButton(ui_symbols.loading, elem_id="video_model_load")
with gr.Row():
with gr.Accordion(open=True, label="Model", elem_id='video_model_accordion'):
url = gr.HTML(label='Model URL', elem_id='video_model_url', value='<br><br>')
with gr.Accordion(open=False, label="Parameters", elem_id='video_parameters_accordion'):
steps, sampler_index = ui_sections.create_sampler_and_steps_selection(None, "video", default_steps=50)
@@ -148,6 +123,21 @@ def create_ui(prompt, negative, styles, overrides, init_image, init_strength, la
with gr.Row():
guidance_scale = gr.Slider(label='Guidance scale', minimum=-1.0, maximum=14.0, step=0.1, value=-1.0, elem_id="video_guidance_scale")
guidance_true = gr.Slider(label='True guidance', minimum=-1.0, maximum=14.0, step=0.1, value=-1.0, elem_id="video_guidance_true")
with gr.Accordion(open=False, label="Size", elem_id='video_size_accordion'):
with gr.Row():
width, height = ui_sections.create_resolution_inputs('video', default_width=832, default_height=480)
with gr.Row():
frames = gr.Slider(label='Frames', minimum=1, maximum=1024, step=1, value=17, elem_id="video_frames")
seed = gr.Number(label='Initial seed', value=-1, elem_id="video_seed", container=True)
random_seed = ToolButton(ui_symbols.random, elem_id="video_seed_random")
reuse_seed = ToolButton(ui_symbols.reuse, elem_id="video_seed_reuse")
random_seed.click(fn=lambda: -1, show_progress='hidden', inputs=[], outputs=[seed])
with gr.Accordion(open=False, label="Inputs", elem_id='video_inputs_accordion'):
init_strength = gr.Slider(label='Init strength', minimum=0.0, maximum=1.0, step=0.01, value=0.8, elem_id="video_denoising_strength")
gr.HTML("<br>&nbsp Init image")
init_image = gr.Image(elem_id="video_image", show_label=False, type="pil", image_mode="RGB", width=256, height=256)
gr.HTML("<br>&nbsp Last image")
last_image = gr.Image(elem_id="video_last", show_label=False, type="pil", image_mode="RGB", width=256, height=256)
with gr.Accordion(open=False, label="Decode", elem_id='video_decode_accordion'):
with gr.Row():
vae_type = gr.Dropdown(label='VAE decode', choices=['Default', 'Tiny', 'Remote', 'Upscale'], value='Default', elem_id="video_vae_type")
@@ -163,18 +153,15 @@ def create_ui(prompt, negative, styles, overrides, init_image, init_strength, la
with gr.Tab('Frames', id='out-gallery'):
gallery, gen_info, html_info, _html_info_formatted, html_log = ui_common.create_output_panel("video", prompt=prompt, preview=False, transfer=False, scale=2)
# connect reuse seed button
ui_common.connect_reuse_seed(seed, reuse_seed, gen_info, is_subseed=False)
# handle engine and model change
engine.change(fn=engine_change, inputs=[engine], outputs=[model])
model.change(fn=model_change, inputs=[engine, model], outputs=[url])
btn_load.click(fn=model_load, inputs=[engine, model], outputs=[html_log])
# hidden fields
task_id = gr.Textbox(visible=False, value='')
ui_state = gr.Textbox(visible=False, value='')
state_inputs = [task_id, ui_state]
# generate args
video_inputs = [
engine, model,
prompt, negative, styles,
@@ -206,4 +193,4 @@ def create_ui(prompt, negative, styles, overrides, init_image, init_strength, la
show_progress='hidden',
)
generate.click(**video_dict)
return [engine, model, steps, sampler_index]
return engine, model, steps, sampler_index, width, height, frames, seed