prototype video tab

Signed-off-by: Vladimir Mandic <mandic00@live.com>
pull/3845/head
Vladimir Mandic 2025-03-17 22:29:03 -04:00
parent 966eb3210a
commit d8044136b9
19 changed files with 220 additions and 79 deletions

27
TODO.md
View File

@ -4,28 +4,23 @@ Main ToDo list can be found at [GitHub projects](https://github.com/users/vladma
## Current
- <https://github.com/KohakuBlueleaf/z-tipo-extension/pull/73>
## Future Candidates
- Redesign postprocessing
- Flux NF4 loader: <https://github.com/huggingface/diffusers/issues/9996>
- IPAdapter negative: <https://github.com/huggingface/diffusers/discussions/7167>
- Control API enhance scripts compatibility
- CogView4
## Code TODO
- flux: loader for civitai nf4 models (fixme)
- hypertile: vae breaks when using non-standard sizes (fixme)
- install: enable ROCm for windows when available (fixme)
- lora make support quantized flux (fixme)
- lora: add other quantization types (fixme)
- model load: force-reloading entire model as loading transformers only leads to massive memory usage (fixme)
- model loader: implement model in-memory caching (fixme)
- modernui: monkey-patch for missing tabs.select event (fixme)
- processing: remove duplicate mask params (fixme)
- resize image: enable full VAE mode for resize-latent (fixme)
- sana: fails when quantized (fixme)
- support scripts via api (fixme)
- transformer from-single-file with quant (fixme)
- enable ROCm for windows when available
- resize image: enable full VAE mode for resize-latent
- infotext: handle using regex instead
- processing: remove duplicate mask params
- model loader: implement model in-memory caching
- hypertile: vae breaks when using non-standard sizes
- force-reloading entire model as loading transformers only leads to massive memory usage
- add other quantization types
- lora make support quantized flux
- control: support scripts via api
- modernui: monkey-patch for missing tabs.select event

View File

@ -88,8 +88,6 @@ svg.feather.feather-image, .feather .feather-image { display: none }
/* gradio elements overrides */
#div.gradio-container { overflow-x: hidden; }
#img2img_label_copy_to_img2img { font-weight: normal; }
#txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt { background-color: var(--background-color); box-shadow: 4px 4px 4px 0px #333333 !important; }
#txt2img_prompt > label > textarea, #txt2img_neg_prompt > label > textarea, #img2img_prompt > label > textarea, #img2img_neg_prompt > label > textarea { font-size: 1.1rem; }
#img2img_settings { min-width: calc(2 * var(--left-column)); max-width: calc(2 * var(--left-column)); background-color: #111111; padding-top: 16px; }
#interrogate, #deepbooru { margin: 0 0px 10px 0px; max-width: 80px; max-height: 80px; font-weight: normal; font-size: 0.95em; }
#quicksettings .gr-button-tool { font-size: 1.6rem; box-shadow: none; margin-left: -20px; margin-top: -2px; height: 2.4em; }

View File

@ -103,11 +103,6 @@ svg.feather.feather-image, .feather .feather-image { display: none }
/* gradio elements overrides */
#div.gradio-container { overflow-x: hidden; }
#img2img_label_copy_to_img2img { font-weight: normal; }
#txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt, #control_prompt, #control_neg_prompt { background-color: var(--background-color); box-shadow: none !important; }
#txt2img_prompt > label > textarea, #txt2img_neg_prompt > label > textarea, #img2img_prompt > label > textarea, #img2img_neg_prompt > label > textarea, #control_prompt > label > textarea, #control_neg_prompt > label > textarea { font-size: 1.0em; line-height: 1.4em; }
#txt2img_styles, #img2img_styles, #control_styles { padding: 0; }
#txt2img_styles_refresh, #img2img_styles_refresh, #control_styles_refresh { padding: 0; margin-top: 1em; }
#img2img_settings { min-width: calc(2 * var(--left-column)); max-width: calc(2 * var(--left-column)); background-color: var(--primary-950); padding-top: 16px; }
#interrogate, #deepbooru { margin: 0 0px 10px 0px; max-width: 80px; max-height: 80px; font-weight: normal; font-size: 0.95em; }
#quicksettings .gr-button-tool { font-size: 1.6rem; box-shadow: none; margin-left: -20px; margin-top: -2px; height: 2.4em; }

View File

@ -105,8 +105,6 @@ svg.feather.feather-image, .feather .feather-image { display: none }
/* gradio elements overrides */
#div.gradio-container { overflow-x: hidden; }
#img2img_label_copy_to_img2img { font-weight: normal; }
#txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt { background-color: var(--background-color); box-shadow: 4px 4px 4px 0px #333333 !important; }
#txt2img_prompt > label > textarea, #txt2img_neg_prompt > label > textarea, #img2img_prompt > label > textarea, #img2img_neg_prompt > label > textarea { font-size: 1.1rem; }
#img2img_settings { min-width: calc(2 * var(--left-column)); max-width: calc(2 * var(--left-column)); background-color: #111111; padding-top: 16px; }
#interrogate, #deepbooru { margin: 0 0px 10px 0px; max-width: 80px; max-height: 80px; font-weight: normal; font-size: 0.95em; }
#quicksettings .gr-button-tool { font-size: 1.6rem; box-shadow: none; margin-left: -20px; margin-top: -2px; height: 2.4em; }

View File

@ -142,11 +142,6 @@ svg.feather.feather-image, .feather .feather-image { display: none }
/* gradio elements overrides */
#div.gradio-container { overflow-x: hidden; }
#img2img_label_copy_to_img2img { font-weight: normal; }
#txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt, #control_prompt, #control_neg_prompt { background-color: var(--background-color); box-shadow: none !important; }
#txt2img_prompt > label > textarea, #txt2img_neg_prompt > label > textarea, #img2img_prompt > label > textarea, #img2img_neg_prompt > label > textarea, #control_prompt > label > textarea, #control_neg_prompt > label > textarea { font-size: 1.0em; line-height: 1.4em; }
#txt2img_styles, #img2img_styles, #control_styles { padding: 0; margin-top: 2px; }
#txt2img_styles_refresh, #img2img_styles_refresh, #control_styles_refresh { padding: 0; margin-top: 1em; }
#img2img_settings { min-width: calc(2 * var(--left-column)); max-width: calc(2 * var(--left-column)); background-color: var(--neutral-950); padding-top: 16px; }
#interrogate, #deepbooru { margin: 0 0px 10px 0px; max-width: 80px; max-height: 80px; font-weight: normal; font-size: 0.95em; }
#quicksettings .gr-button-tool { font-size: 1.6rem; box-shadow: none; margin-left: -20px; margin-top: -2px; height: 2.4em; }

View File

@ -101,8 +101,6 @@ button.selected {background: var(--button-primary-background-fill);}
/* gradio elements overrides */
#div.gradio-container { overflow-x: hidden; }
#img2img_label_copy_to_img2img { font-weight: normal; }
#txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt { background-color: var(--background-color); box-shadow: 4px 4px 4px 0px #333333 !important; }
#txt2img_prompt > label > textarea, #txt2img_neg_prompt > label > textarea, #img2img_prompt > label > textarea, #img2img_neg_prompt > label > textarea { font-size: 1.1rem; }
#img2img_settings { min-width: calc(2 * var(--left-column)); max-width: calc(2 * var(--left-column)); background-color: #111111; padding-top: 16px; }
#interrogate, #deepbooru { margin: 0 0px 10px 0px; max-width: 80px; max-height: 80px; font-weight: normal; font-size: 0.95em; }
#quicksettings .gr-button-tool { font-size: 1.6rem; box-shadow: none; margin-top: -2px; height: 2.4em; }

View File

@ -101,8 +101,6 @@ button.selected {background: var(--button-primary-background-fill);}
/* gradio elements overrides */
#div.gradio-container { overflow-x: hidden; }
#img2img_label_copy_to_img2img { font-weight: normal; }
#txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt { background-color: var(--background-color); box-shadow: 4px 4px 4px 0px #333333 !important; }
#txt2img_prompt > label > textarea, #txt2img_neg_prompt > label > textarea, #img2img_prompt > label > textarea, #img2img_neg_prompt > label > textarea { font-size: 1.1rem; }
#img2img_settings { min-width: calc(2 * var(--left-column)); max-width: calc(2 * var(--left-column)); background-color: #111111; padding-top: 16px; }
#interrogate, #deepbooru { margin: 0 0px 10px 0px; max-width: 80px; max-height: 80px; font-weight: normal; font-size: 0.95em; }
#quicksettings .gr-button-tool { font-size: 1.6rem; box-shadow: none; margin-top: -2px; height: 2.4em; }

View File

@ -101,10 +101,6 @@ svg.feather.feather-image, .feather .feather-image { display: none }
/* gradio elements overrides */
#div.gradio-container { overflow-x: hidden; }
#img2img_label_copy_to_img2img { font-weight: normal; }
#txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt, #control_prompt, #control_neg_prompt { background-color: var(--background-color); box-shadow: 4px 4px 4px 0px #333333 !important; }
#txt2img_prompt > label > textarea, #txt2img_neg_prompt > label > textarea, #img2img_prompt > label > textarea, #img2img_neg_prompt > label > textarea, #control_prompt > label > textarea, #control_neg_prompt > label > textarea { font-size: 1.0em; line-height: 1.4em; }
#txt2img_styles, #img2img_styles, #control_styles { padding: 0; }
#txt2img_styles_refresh, #img2img_styles_refresh, #control_styles_refresh { padding: 0; margin-top: 1em; }
#img2img_settings { min-width: calc(2 * var(--left-column)); max-width: calc(2 * var(--left-column)); background-color: #111111; padding-top: 16px; }
#interrogate, #deepbooru { margin: 0 0px 10px 0px; max-width: 80px; max-height: 80px; font-weight: normal; font-size: 0.95em; }
#quicksettings .gr-button-tool { font-size: 1.6rem; box-shadow: none; margin-left: -20px; margin-top: -2px; height: 2.4em; }

View File

@ -94,8 +94,6 @@ svg.feather.feather-image, .feather .feather-image { display: none }
/* gradio elements overrides */
#div.gradio-container { overflow-x: hidden; }
#img2img_label_copy_to_img2img { font-weight: normal; }
#txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt { background-color: var(--background-color); box-shadow: 4px 4px 4px 0px #333333 !important; }
#txt2img_prompt > label > textarea, #txt2img_neg_prompt > label > textarea, #img2img_prompt > label > textarea, #img2img_neg_prompt > label > textarea { font-size: 1.1rem; }
#img2img_settings { min-width: calc(2 * var(--left-column)); max-width: calc(2 * var(--left-column)); background-color: #111111; padding-top: 16px; }
#interrogate, #deepbooru { margin: 0 0px 10px 0px; max-width: 80px; max-height: 80px; font-weight: normal; font-size: 0.95em; }
#quicksettings .gr-button-tool { font-size: 1.6rem; box-shadow: none; margin-left: -20px; margin-top: -2px; height: 2.4em; }

View File

@ -101,8 +101,6 @@ button.selected {background: var(--button-primary-background-fill);}
/* gradio elements overrides */
#div.gradio-container { overflow-x: hidden; }
#img2img_label_copy_to_img2img { font-weight: normal; }
#txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt { background-color: var(--background-color); box-shadow: 4px 4px 4px 0px #333333 !important; }
#txt2img_prompt > label > textarea, #txt2img_neg_prompt > label > textarea, #img2img_prompt > label > textarea, #img2img_neg_prompt > label > textarea { font-size: 1.1rem; }
#img2img_settings { min-width: calc(2 * var(--left-column)); max-width: calc(2 * var(--left-column)); background-color: #111111; padding-top: 16px; }
#interrogate, #deepbooru { margin: 0 0px 10px 0px; max-width: 80px; max-height: 80px; font-weight: normal; font-size: 0.95em; }
#quicksettings .gr-button-tool { font-size: 1.6rem; box-shadow: none; margin-top: -2px; height: 2.4em; }

View File

@ -99,20 +99,20 @@ button.custom-button { border-radius: var(--button-large-radius); padding: var(-
#txt2img_gallery, #img2img_gallery { height: 50vh; }
#control-result { background: var(--button-secondary-background-fill); padding: 0.2em; }
#control-inputs { margin-top: 1em; }
#txt2img_prompt_container, #img2img_prompt_container, #control_prompt_container { margin-right: var(--layout-gap) }
#txt2img_prompt_container, #img2img_prompt_container, #control_prompt_container, #video_prompt_container { margin-right: var(--layout-gap) }
#txt2img_footer, #img2img_footer, #control_footer { height: fit-content; display: none; }
#txt2img_generate_box, #img2img_generate_box { gap: 0.5em; flex-wrap: unset; min-width: unset; width: 66.6%; }
#control_generate_box { gap: 0.5em; flex-wrap: unset; min-width: unset; width: 100%; }
#control_generate_box button:nth-child(1) { flex-grow: 2; }
#control_generate_box button:nth-child(2) { flex-grow: 1; }
#txt2img_actions_column, #img2img_actions_column, #control_actions_column { gap: 0.3em; height: fit-content; }
#txt2img_generate_box>button, #img2img_generate_box>button, #control_generate_box>button, #txt2img_enqueue, #img2img_enqueue, #txt2img_enqueue>button, #img2img_enqueue>button { min-height: 44px !important; max-height: 44px !important; line-height: 1em; white-space: break-spaces; min-width: unset; }
#txt2img_actions_column, #img2img_actions_column, #control_actions_column, #video_actions_column { gap: 0.3em; height: fit-content; }
#txt2img_generate_box>button, #img2img_generate_box>button, #control_generate_box>button, #video_generate_box>button, #txt2img_enqueue, #img2img_enqueue, #txt2img_enqueue>button, #img2img_enqueue>button { min-height: 44px !important; max-height: 44px !important; line-height: 1em; white-space: break-spaces; min-width: unset; }
#txt2img_enqueue_wrapper, #img2img_enqueue_wrapper, #control_enqueue_wrapper { min-width: unset !important; width: 31%; }
#txt2img_generate_line2, #img2img_generate_line2, #txt2img_tools, #img2img_tools, #control_generate_line2, #control_tools { display: flex; }
#txt2img_generate_line2>button, #img2img_generate_line2>button, #extras_generate_box>button, #control_generate_line2>button, #txt2img_tools>button, #img2img_tools>button, #control_tools>button { height: 2em; line-height: 0; font-size: var(--text-md);
min-width: unset; display: block !important; }
#txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt, #control_prompt, #control_neg_prompt { display: contents; }
#txt2img_actions_column, #img2img_actions_column, #control_actions { flex-flow: wrap; justify-content: space-between; }
#txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt, #control_prompt, #control_neg_prompt, #video_prompt, #video_neg_prompt { display: contents; }
#txt2img_actions_column, #img2img_actions_column, #control_actions, #video_actions { flex-flow: wrap; justify-content: space-between; }
.interrogate { position: absolute; right: 2.8em; top: 0.2em; max-width: fit-content; background: none !important; z-index: 50; font-size: 1.5em !important; }
.interrogate:hover { background: var(--button-primary-background-fill-hover) !important; }
@ -140,6 +140,11 @@ div#extras_scale_to_tab div.form { flex-direction: row; }
#txt2img_advanced_options, #img2img_advanced_options, #control_advanced_options { min-width: 100%; }
#txt2img_advanced_options .gradio-checkbox, #img2img_advanced_options .gradio-checkbox, #control_advanced_options .gradio-checkbox { min-width: unset !important; max-width: fit-content; }
#txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt, #control_prompt, #control_neg_prompt, #video_prompt, #video_neg_prompt { background-color: var(--background-color); box-shadow: none !important; }
#txt2img_prompt > label > textarea, #txt2img_neg_prompt > label > textarea, #img2img_prompt > label > textarea, #img2img_neg_prompt > label > textarea, #control_prompt > label > textarea, #control_neg_prompt > label > textarea, #video_prompt > label > textarea, #video_neg_prompt > label > textarea { font-size: 1.0em; line-height: 1.4em; }
#txt2img_styles, #img2img_styles, #control_styles, #video_styles { padding: 0; margin-top: 2px; }
#txt2img_styles_refresh, #img2img_styles_refresh, #control_styles_refresh, #video_styles_refresh { padding: 0; margin-top: 1em; }
/* settings */
#si-sparkline-memo, #si-sparkline-load { background-color: #111; }
#quicksettings { width: fit-content; }
@ -388,7 +393,7 @@ div:has(>#tab-gallery-folders) { flex-grow: 0 !important; background-color: var(
#txt2img_results, #extras_results, #txt2im g_footer p { text-wrap: wrap; max-width: 100% !important; } /* maintain side by side split on larger mobile displays for from text */
}
#scripts_alwayson_txt2img div, #scripts_alwayson_img2img div { max-width: 100%; }
#txt2img_prompt_container, #img2img_prompt_container, #control_prompt_container { resize: vertical !important; }
#txt2img_prompt_container, #img2img_prompt_container, #control_prompt_container, #video_prompt_container { resize: vertical !important; }
#txt2img_generate_box, #txt2img_enqueue_wrapper { min-width: 100% !important;} /* make generate and enqueue buttons take up the entire width of their rows. */
#img2img_toprow>div.gradio-column { flex-grow: 1 !important;} /*make interrogate buttons take up appropriate space. */
#img2img_actions_column { display: flex; min-width: fit-content !important; flex-direction: row;justify-content: space-evenly; align-items: center;}

View File

@ -101,8 +101,6 @@ button.selected {background: var(--button-primary-background-fill);}
/* gradio elements overrides */
#div.gradio-container { overflow-x: hidden; }
#img2img_label_copy_to_img2img { font-weight: normal; }
#txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt { background-color: var(--background-color); box-shadow: 4px 4px 4px 0px #333333 !important; }
#txt2img_prompt > label > textarea, #txt2img_neg_prompt > label > textarea, #img2img_prompt > label > textarea, #img2img_neg_prompt > label > textarea { font-size: 1.1rem; }
#img2img_settings { min-width: calc(2 * var(--left-column)); max-width: calc(2 * var(--left-column)); background-color: #111111; padding-top: 16px; }
#interrogate, #deepbooru { margin: 0 0px 10px 0px; max-width: 80px; max-height: 80px; font-weight: normal; font-size: 0.95em; }
#quicksettings .gr-button-tool { font-size: 1.6rem; box-shadow: none; margin-top: -2px; height: 2.4em; }

View File

@ -240,6 +240,18 @@ function submit_control(...args) {
return res;
}
function submit_video(...args) {
log('submitVideo');
clearGallery('video');
const id = randomId();
requestProgress(id, null, gradioApp().getElementById('video_gallery'));
const res = create_submit_args(args);
res[0] = id;
res[1] = window.submit_state;
window.submit_state = '';
return res;
}
function submit_postprocessing(...args) {
log('SubmitExtras');
clearGallery('extras');

View File

@ -146,6 +146,12 @@ def create_ui(startup_timer = None):
ui_control.create_ui()
timer.startup.record("ui-control")
with gr.Blocks(analytics_enabled=False) as video_interface:
if shared.native and shared.cmd_opts.experimental:
from modules import ui_video
ui_video.create_ui()
timer.startup.record("ui-video")
with gr.Blocks(analytics_enabled=False) as extras_interface:
from modules import ui_postprocessing
ui_postprocessing.create_ui()
@ -398,7 +404,10 @@ def create_ui(startup_timer = None):
interfaces = []
interfaces += [(txt2img_interface, "Text", "txt2img")]
interfaces += [(img2img_interface, "Image", "img2img")]
interfaces += [(control_interface, "Control", "control")] if control_interface is not None else []
if control_interface is not None:
interfaces += [(control_interface, "Control", "control")]
if video_interface is not None:
interfaces += [(video_interface, "Video", "video")]
interfaces += [(extras_interface, "Process", "process")]
interfaces += [(caption_interface, "Caption", "caption")]
interfaces += [(gallery_interface, "Gallery", "gallery")]

View File

@ -237,8 +237,8 @@ def interrogate_booru(image): # legacy function
return gr.update() if prompt is None else prompt
def create_output_panel(tabname, preview=True, prompt=None, height=None):
with gr.Column(variant='panel', elem_id=f"{tabname}_results"):
def create_output_panel(tabname, preview=True, prompt=None, height=None, transfer=True, scale=1):
with gr.Column(variant='panel', elem_id=f"{tabname}_results", scale=scale):
with gr.Group(elem_id=f"{tabname}_gallery_container"):
if tabname == "txt2img":
gr.HTML(value="", elem_id="main_info", visible=False, elem_classes=["main-info"])
@ -270,10 +270,13 @@ def create_output_panel(tabname, preview=True, prompt=None, height=None):
clip_files.click(fn=None, _js='clip_gallery_urls', inputs=[result_gallery], outputs=[])
save = gr.Button('Save', elem_id=f'save_{tabname}')
delete = gr.Button('Delete', elem_id=f'delete_{tabname}')
if not shared.native:
buttons = generation_parameters_copypaste.create_buttons(["img2img", "inpaint", "extras"])
if transfer:
if not shared.native:
buttons = generation_parameters_copypaste.create_buttons(["img2img", "inpaint", "extras"])
else:
buttons = generation_parameters_copypaste.create_buttons(["txt2img", "img2img", "control", "extras", "caption"])
else:
buttons = generation_parameters_copypaste.create_buttons(["txt2img", "img2img", "control", "extras", "caption"])
buttons = None
download_files = gr.File(None, file_count="multiple", interactive=False, show_label=False, visible=False, elem_id=f'download_files_{tabname}')
with gr.Group():
@ -309,17 +312,18 @@ def create_output_panel(tabname, preview=True, prompt=None, height=None):
else:
paste_field_names = []
debug(f'Paste field: tab={tabname} fields={paste_field_names}')
for paste_tabname, paste_button in buttons.items():
debug(f'Create output panel: source={tabname} target={paste_tabname} button={paste_button}')
bindings = generation_parameters_copypaste.ParamBinding(
paste_button=paste_button,
tabname=paste_tabname,
source_tabname=tabname,
source_image_component=result_gallery,
paste_field_names=paste_field_names,
source_text_component=prompt or generation_info
)
generation_parameters_copypaste.register_paste_params_button(bindings)
if buttons is not None:
for paste_tabname, paste_button in buttons.items():
debug(f'Create output panel: source={tabname} target={paste_tabname} button={paste_button}')
bindings = generation_parameters_copypaste.ParamBinding(
paste_button=paste_button,
tabname=paste_tabname,
source_tabname=tabname,
source_image_component=result_gallery,
paste_field_names=paste_field_names,
source_text_component=prompt or generation_info
)
generation_parameters_copypaste.register_paste_params_button(bindings)
return result_gallery, generation_info, html_info, html_info_formatted, html_log

View File

@ -4,7 +4,7 @@ from modules.ui_components import ToolButton
from modules.interrogate import interrogate
def create_toprow(is_img2img: bool = False, id_part: str = None):
def create_toprow(is_img2img: bool = False, id_part: str = None, negative_visible: bool = True, reprocess_visible: bool = True):
def apply_styles(prompt, prompt_neg, styles):
prompt = shared.prompt_styles.apply_styles_to_prompt(prompt, styles, wildcards=not shared.opts.extra_networks_apply_unparsed)
prompt_neg = shared.prompt_styles.apply_negative_styles_to_prompt(prompt_neg, styles, wildcards=not shared.opts.extra_networks_apply_unparsed)
@ -21,19 +21,20 @@ def create_toprow(is_img2img: bool = False, id_part: str = None):
with gr.Row():
with gr.Column(scale=80):
with gr.Row(elem_id=f"{id_part}_prompt_row"):
prompt = gr.Textbox(elem_id=f"{id_part}_prompt", label="Prompt", show_label=False, lines=3, placeholder="Prompt", elem_classes=["prompt"])
prompt = gr.Textbox(elem_id=f"{id_part}_prompt", label="Prompt", show_label=False, lines=3 if negative_visible else 5, placeholder="Prompt", elem_classes=["prompt"])
with gr.Row():
with gr.Column(scale=80):
with gr.Row(elem_id=f"{id_part}_negative_row"):
negative_prompt = gr.Textbox(elem_id=f"{id_part}_neg_prompt", label="Negative prompt", show_label=False, lines=3, placeholder="Negative prompt", elem_classes=["prompt"])
negative_prompt = gr.Textbox(elem_id=f"{id_part}_neg_prompt", label="Negative prompt", show_label=False, lines=3, placeholder="Negative prompt", elem_classes=["prompt"], visible=negative_visible)
with gr.Column(scale=1, elem_id=f"{id_part}_actions_column"):
with gr.Row(elem_id=f"{id_part}_generate_box"):
reprocess = []
submit = gr.Button('Generate', elem_id=f"{id_part}_generate", variant='primary')
reprocess.append(gr.Button('Reprocess', elem_id=f"{id_part}_reprocess", variant='primary', visible=True))
reprocess.append(gr.Button('Reprocess decode', elem_id=f"{id_part}_reprocess_decode", variant='primary', visible=False))
reprocess.append(gr.Button('Reprocess refine', elem_id=f"{id_part}_reprocess_refine", variant='primary', visible=False))
reprocess.append(gr.Button('Reprocess face', elem_id=f"{id_part}_reprocess_detail", variant='primary', visible=False))
if reprocess_visible:
reprocess.append(gr.Button('Reprocess', elem_id=f"{id_part}_reprocess", variant='primary', visible=True))
reprocess.append(gr.Button('Reprocess decode', elem_id=f"{id_part}_reprocess_decode", variant='primary', visible=False))
reprocess.append(gr.Button('Reprocess refine', elem_id=f"{id_part}_reprocess_refine", variant='primary', visible=False))
reprocess.append(gr.Button('Reprocess face', elem_id=f"{id_part}_reprocess_detail", variant='primary', visible=False))
with gr.Row(elem_id=f"{id_part}_generate_line2"):
interrupt = gr.Button('Stop', elem_id=f"{id_part}_interrupt")
interrupt.click(fn=lambda: shared.state.interrupt(), _js="requestInterrupt", inputs=[], outputs=[])
@ -79,9 +80,9 @@ def ar_change(ar, width, height):
return gr.update(), gr.update()
def create_resolution_inputs(tab):
width = gr.Slider(minimum=64, maximum=4096, step=8, label="Width", value=1024, elem_id=f"{tab}_width")
height = gr.Slider(minimum=64, maximum=4096, step=8, label="Height", value=1024, elem_id=f"{tab}_height")
def create_resolution_inputs(tab, default_width=1024, default_height=1024):
width = gr.Slider(minimum=64, maximum=4096, step=8, label="Width", value=default_width, elem_id=f"{tab}_width")
height = gr.Slider(minimum=64, maximum=4096, step=8, label="Height", value=default_height, elem_id=f"{tab}_height")
ar_list = ['AR'] + [x.strip() for x in shared.opts.aspect_ratios.split(',') if x.strip() != '']
ar_dropdown = gr.Dropdown(show_label=False, interactive=True, choices=ar_list, value=ar_list[0], elem_id=f"{tab}_ar", elem_classes=["ar-dropdown"])
for c in [ar_dropdown, width, height]:

View File

@ -1,6 +1,6 @@
import gradio as gr
from modules.call_queue import wrap_gradio_gpu_call, wrap_queued_call
from modules import timer, shared, ui_common, ui_sections, generation_parameters_copypaste, processing, processing_vae, devices
from modules import timer, shared, ui_common, ui_sections, generation_parameters_copypaste, processing, processing_vae, devices, images
from modules.ui_components import ToolButton # pylint: disable=unused-import
@ -23,7 +23,7 @@ def create_ui():
txt2img_prompt, txt2img_prompt_styles, txt2img_negative_prompt, txt2img_submit, txt2img_reprocess, txt2img_paste, txt2img_extra_networks_button, txt2img_token_counter, txt2img_token_button, txt2img_negative_token_counter, txt2img_negative_token_button = ui_sections.create_toprow(is_img2img=False, id_part="txt2img")
txt_prompt_img = gr.File(label="", elem_id="txt2img_prompt_image", file_count="single", type="binary", visible=False)
txt_prompt_img.change(fn=modules.images.image_data, inputs=[txt_prompt_img], outputs=[txt2img_prompt, txt_prompt_img])
txt_prompt_img.change(fn=images.image_data, inputs=[txt_prompt_img], outputs=[txt2img_prompt, txt_prompt_img])
with gr.Row(variant='compact', elem_id="txt2img_extra_networks", elem_classes=["extra_networks_root"], visible=False) as extra_networks_ui:
from modules import ui_extra_networks

130
modules/ui_video.py Normal file
View File

@ -0,0 +1,130 @@
# TODO hunyuanvideo: seed, scheduler, scheduler_shift, guidance_scale=1.0, true_cfg_scale=6.0, num_inference_steps=30, prompt_template, vae, offloading
# TODO modernui video tab
from dataclasses import dataclass
import gradio as gr
from modules import shared, images, ui_common, ui_sections, sd_models, call_queue, generation_parameters_copypaste
from modules.video_models import hunyuan
@dataclass
class Model():
name: str
repo: str
dit: str
MODELS = {
'None': [],
'Hunyuan Video': [
Model('None', None, None),
Model('Hunyuan Video T2V', 'hunyuanvideo-community/HunyuanVideo', None),
Model('Hunyuan Video I2V', 'hunyuanvideo-community/HunyuanVideo', 'hunyuanvideo-community/HunyuanVideo-I2V'), # https://github.com/huggingface/diffusers/pull/10983
Model('SkyReels Hunyuan T2V', 'hunyuanvideo-community/HunyuanVideo', 'Skywork/SkyReels-V1-Hunyuan-T2V'), # https://github.com/huggingface/diffusers/pull/10837
Model('SkyReels Hunyuan I2V', 'hunyuanvideo-community/HunyuanVideo', 'Skywork/SkyReels-V1-Hunyuan-I2V'),
Model('Fast Hunyuan T2V', 'hunyuanvideo-community/HunyuanVideo', 'hunyuan-video-t2v-720p/transformers/mp_rank_00_model_states.pt'), # https://github.com/hao-ai-lab/FastVideo/blob/8a77cf22c9b9e7f931f42bc4b35d21fd91d24e45/fastvideo/models/hunyuan/inference.py#L213
]
}
def engine_change(engine):
models = [model.name for model in MODELS.get(engine, [])]
return gr.update(choices=models, value=models[0] if len(models) > 0 else None)
def model_change(engine, model):
models = [model.name for model in MODELS.get(engine, [])]
selected = [m for m in MODELS[engine] if m.name == model][0] if len(models) > 0 else None
if selected:
if 'None' in selected.name:
sd_models.unload_model_weights()
msg = 'Video model unloaded'
elif 'Hunyuan' in selected.name:
msg = hunyuan.load(selected)
elif model != 'None':
msg = f'Video model not found: engine={engine} model={model}'
shared.log.error(msg)
else:
sd_models.unload_model_weights()
msg = 'Video model unloaded'
return [msg, gr.update(visible='I2V' in selected.name) if selected else gr.update(visible=False)]
def run_video(*args):
engine, model = args[2], args[3]
models = [model.name for model in MODELS.get(engine, [])]
selected = [m for m in MODELS[engine] if m.name == model][0] if len(models) > 0 else None
if selected and 'Hunyuan' in selected.name:
return hunyuan.generate(*args)
shared.log.error(f'Video model not found: args={args}')
return [], '', '', f'Video model not found: engine={engine} model={model}'
def create_ui():
shared.log.debug('UI initialize: txt2img')
with gr.Blocks(analytics_enabled=False) as _video_interface:
prompt, styles, _negative, generate, _reprocess, paste, _networks, _token_counter, _token_button, _token_counter_negative, _token_button_negative = ui_sections.create_toprow(is_img2img=False, id_part="video", negative_visible=False, reprocess_visible=False)
prompt_image = gr.File(label="", elem_id="video_prompt_image", file_count="single", type="binary", visible=False)
prompt_image.change(fn=images.image_data, inputs=[prompt_image], outputs=[prompt, prompt_image])
with gr.Row(elem_id="video_interface", equal_height=False):
with gr.Column(variant='compact', elem_id="video_settings", scale=1):
with gr.Row():
engine = gr.Dropdown(label='Engine', choices=list(MODELS), value='None')
model = gr.Dropdown(label='Model', choices=[''], value=None)
with gr.Row():
width, height = ui_sections.create_resolution_inputs('video', default_width=720, default_height=480)
with gr.Row():
frames = gr.Slider(label='Frames', minimum=1, maximum=1024, step=1, value=15)
with gr.Row():
with gr.Group(visible=False) as image_group:
gr.HTML("<br>&nbsp Init image")
image = gr.Image(elem_id="video_image", show_label=False, source="upload", interactive=True, type="pil", tool="select", image_mode="RGB", height=512)
with gr.Row():
save_frames = gr.Checkbox(label='Save image frames', value=False)
with gr.Row():
cc, duration, loop, pad, interpolate = ui_sections.create_video_inputs(tab='video')
override_settings = ui_common.create_override_inputs('video')
# output panel with 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)
# handle engine and model change
engine.change(fn=engine_change, inputs=[engine], outputs=[model])
model.change(fn=model_change, inputs=[engine, model], outputs=[html_log, image_group])
# handle restore fields
paste_fields = [
(prompt, "Prompt"),
# main
(width, "Size-1"),
(height, "Size-2"),
(frames, "Frames"),
]
generation_parameters_copypaste.add_paste_fields("txt2img", None, paste_fields, override_settings)
bindings = generation_parameters_copypaste.ParamBinding(paste_button=paste, tabname="video", source_text_component=prompt, source_image_component=None)
generation_parameters_copypaste.register_paste_params_button(bindings)
# hidden fields
task_id = gr.Textbox(visible=False, value='')
ui_state = gr.Textbox(visible=False, value='')
# generate args
video_args = [
task_id, ui_state,
engine, model,
prompt, styles,
width, height,
frames,
image,
save_frames,
cc, duration, loop, pad, interpolate,
]
# generate function
video_dict = dict(
fn=call_queue.wrap_gradio_gpu_call(run_video, extra_outputs=[None, '', ''], name='Video'),
_js="submit_video",
inputs=video_args,
outputs=[gallery, gen_info, html_info, html_log],
show_progress=False,
)
prompt.submit(**video_dict)
generate.click(**video_dict)

View File

@ -0,0 +1,13 @@
from modules import shared
def load(selected):
msg = f'Video load: model="{selected.name}" repo="{selected.repo}" dit="{selected.dit}"'
shared.log.info(msg)
return msg
def generate(*args, **kwargs):
# TODO hunyuanvideo: check if loaded
shared.log.debug(f'Video generate: args={args} kwargs={kwargs}')
return [], '', '', 'TBD'