diff --git a/README.md b/README.md index 253228c..9fe23bd 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,10 @@ WanGP supports the Wan (and derived models), Hunyuan Video and LTV Video models ## 🔥 Latest News!! +* May 26 2025: 👋 Wan 2.1GP v5.3 : Happy with a Video generation and want to do more generations using the same settings but you can't remember what you did or you find it to hard to copy / paste one per one each setting from the file metadata ? Rejoice ! There are now multiple ways to turn this tedious process into a one click task: + - Select one Video recently generated in the Video Gallery and click *Use Selected Video Settings* + - Click *Drop File Here* and select a Video you saved somewhere, if the settings metadata have been saved with the Video you will be able to extract them automatically + - Click *Export Settings to File* to save on your harddrive the current settings. You will be able to use them later again by clicking *Drop File Here* and select this time a Settings json file * May 23 2025: 👋 Wan 2.1GP v5.21 : Improvements for Vace: better transitions between Sliding Windows,Support for Image masks in Matanyone, new Extend Video for Vace, different types of automated background removal * May 20 2025: 👋 Wan 2.1GP v5.2 : Added support for Wan CausVid which is a distilled Wan model that can generate nice looking videos in only 4 to 12 steps. The great thing is that Kijai (Kudos to him !) has created a CausVid Lora that can be combined with any existing Wan t2v model 14B like Wan Vace 14B. diff --git a/ltx_video/pipelines/pipeline_ltx_video.py b/ltx_video/pipelines/pipeline_ltx_video.py index ff61072..38ff702 100644 --- a/ltx_video/pipelines/pipeline_ltx_video.py +++ b/ltx_video/pipelines/pipeline_ltx_video.py @@ -1113,7 +1113,7 @@ class LTXVideoPipeline(DiffusionPipeline): ) latent_model_input = ( - torch.cat([latents] * num_conds) if num_conds > 1 else latents + torch.cat([latents] * num_conds) if num_conds > 1 else latents ) latent_model_input = self.scheduler.scale_model_input( latent_model_input, t diff --git a/wan/text2video.py b/wan/text2video.py index 84ee0f5..f7ef46d 100644 --- a/wan/text2video.py +++ b/wan/text2video.py @@ -124,7 +124,7 @@ class WanT2V: reactive = [i * m + 0 * (1 - m) for i, m in zip(frames, masks)] inactive = self.vae.encode(inactive, tile_size = tile_size) self.toto = inactive[0].clone() - if overlapped_latents != None : + if overlapped_latents != None : # inactive[0][:, 0:1] = self.vae.encode([frames[0][:, 0:1]], tile_size = tile_size)[0] # redundant inactive[0][:, 1:overlapped_latents.shape[1] + 1] = overlapped_latents @@ -303,6 +303,7 @@ class WanT2V: overlapped_latents = None, return_latent_slice = None, overlap_noise = 0, + conditioning_latents_size = 0, model_filename = None, **bbargs ): @@ -445,8 +446,9 @@ class WanT2V: if vace: ref_images_count = len(input_ref_images[0]) if input_ref_images != None and input_ref_images[0] != None else 0 kwargs.update({'vace_context' : z, 'vace_context_scale' : context_scale}) - if overlapped_latents != None: + if overlapped_latents != None : overlapped_latents_size = overlapped_latents.shape[1] + 1 + # overlapped_latents_size = 3 z_reactive = [ zz[0:16, 0:overlapped_latents_size + ref_images_count].clone() for zz in z] @@ -456,22 +458,33 @@ class WanT2V: self.model.compute_teacache_threshold(self.model.teacache_start_step, timesteps, self.model.teacache_multiplier) if callback != None: callback(-1, None, True) + prev = 50/1000 for i, t in enumerate(tqdm(timesteps)): - if overlapped_latents != None: + + timestep = [t] + if overlapped_latents != None : # overlap_noise_factor = overlap_noise *(i/(len(timesteps)-1)) / 1000 overlap_noise_factor = overlap_noise / 1000 + # overlap_noise_factor = (1000-t )/ 1000 # overlap_noise / 1000 + # latent_noise_factor = 1 #max(min(1, (t - overlap_noise) / 1000 ),0) latent_noise_factor = t / 1000 for zz, zz_r, ll in zip(z, z_reactive, [latents]): pass - zz[0:16, ref_images_count:overlapped_latents_size + ref_images_count] = zz_r[:, ref_images_count:] * (1.0 - overlap_noise_factor) + torch.randn_like(zz_r[:, ref_images_count:] ) * overlap_noise_factor - ll[:, 0:overlapped_latents_size + ref_images_count] = zz_r * (1.0 - latent_noise_factor) + torch.randn_like(zz_r ) * latent_noise_factor + # zz[0:16, ref_images_count:overlapped_latents_size + ref_images_count] = zz_r[:, ref_images_count:] * (1.0 - overlap_noise_factor) + torch.randn_like(zz_r[:, ref_images_count:] ) * overlap_noise_factor + # ll[:, 0:overlapped_latents_size + ref_images_count] = zz_r * (1.0 - latent_noise_factor) + torch.randn_like(zz_r ) * latent_noise_factor + + if conditioning_latents_size > 0 and overlap_noise > 0: + pass + overlap_noise_factor = overlap_noise / 1000 + latents[:, conditioning_latents_size + ref_images_count:] = latents[:, conditioning_latents_size + ref_images_count:] * (1.0 - overlap_noise_factor) + torch.randn_like(latents[:, conditioning_latents_size + ref_images_count:]) * overlap_noise_factor + timestep = [torch.tensor([t.item()] * (conditioning_latents_size + ref_images_count) + [t.item() - overlap_noise]*(len(timesteps) - conditioning_latents_size - ref_images_count))] + if target_camera != None: latent_model_input = torch.cat([latents, source_latents], dim=1) else: latent_model_input = latents kwargs["slg_layers"] = slg_layers if int(slg_start * sampling_steps) <= i < int(slg_end * sampling_steps) else None - timestep = [t] offload.set_step_no_for_lora(self.model, i) timestep = torch.stack(timestep) kwargs["current_step"] = i diff --git a/wgp.py b/wgp.py index 2e61bec..ee12754 100644 --- a/wgp.py +++ b/wgp.py @@ -43,6 +43,7 @@ AUTOSAVE_FILENAME = "queue.zip" PROMPT_VARS_MAX = 10 target_mmgp_version = "3.4.7" +WanGP_version = "5.3" prompt_enhancer_image_caption_model, prompt_enhancer_image_caption_processor, prompt_enhancer_llm_model, prompt_enhancer_llm_tokenizer = None, None, None, None from importlib.metadata import version @@ -434,6 +435,7 @@ def process_prompt_and_add_tasks(state, model_choice): "prompt" : single_prompt, "image_start": start, "image_end" : end, + "video_source": None, } inputs.update(extra_inputs) add_video_task(**inputs) @@ -1535,7 +1537,7 @@ wan_choices_i2v=["ckpts/wan2.1_image2video_480p_14B_mbf16.safetensors", "ckpts/w "ckpts/wan2.1_fantasy_speaking_14B_bf16.safetensors"] ltxv_choices= ["ckpts/ltxv_0.9.7_13B_dev_bf16.safetensors", "ckpts/ltxv_0.9.7_13B_dev_quanto_bf16_int8.safetensors", "ckpts/ltxv_0.9.7_13B_distilled_lora128_bf16.safetensors"] -hunyuan_choices= ["ckpts/hunyuan_video_720_bf16.safetensors", "ckpts/hunyuan_video_720_quanto_int8.safetensors", "ckpts/hunyuan_video_i2v_720_bf16.safetensors", "ckpts/hunyuan_video_i2v_720_quanto_int8v2.safetensors", +hunyuan_choices= ["ckpts/hunyuan_video_720_bf16.safetensors", "ckpts/hunyuan_video_720_quanto_int8.safetensors", "ckpts/hunyuan_video_i2v_720_bf16v2.safetensors", "ckpts/hunyuan_video_i2v_720_quanto_int8v2.safetensors", "ckpts/hunyuan_video_custom_720_bf16.safetensors", "ckpts/hunyuan_video_custom_720_quanto_bf16_int8.safetensors" ] transformer_choices = wan_choices_t2v + wan_choices_i2v + ltxv_choices + hunyuan_choices @@ -2323,13 +2325,13 @@ def apply_changes( state, transformer_dtype_policy = server_config["transformer_dtype_policy"] text_encoder_quantization = server_config["text_encoder_quantization"] transformer_types = server_config["transformer_types"] - state["model_filename"] = get_model_filename(get_model_type(state["model_filename"]), transformer_quantization, transformer_dtype_policy) - + model_filename = get_model_filename(get_model_type(state["model_filename"]), transformer_quantization, transformer_dtype_policy) + state["model_filename"] = model_filename if all(change in ["attention_mode", "vae_config", "boost", "save_path", "metadata_type", "clear_file_list", "fit_canvas"] for change in changes ): model_choice = gr.Dropdown() else: reload_needed = True - model_choice = generate_dropdown_model_list() + model_choice = generate_dropdown_model_list(model_filename) header = generate_header(state["model_filename"], compile=compile, attention_mode= attention_mode) return "
The new configuration has been succesfully applied
", header, model_choice, gr.Row(visible= server_config["enhancer_enabled"] == 1) @@ -2699,6 +2701,8 @@ def generate_video( torch.set_grad_enabled(False) file_list = gen["file_list"] + file_settings_list = gen["file_settings_list"] + prompt_no = gen["prompt_no"] fit_canvas = server_config.get("fit_canvas", 0) @@ -2796,7 +2800,8 @@ def generate_video( seed = None if seed == -1 else seed # negative_prompt = "" # not applicable in the inference image2video = test_class_i2v(model_filename) - enable_RIFLEx = RIFLEx_setting == 0 and video_length > (6* 16) or RIFLEx_setting == 1 + current_video_length = video_length + enable_RIFLEx = RIFLEx_setting == 0 and current_video_length > (6* 16) or RIFLEx_setting == 1 # VAE Tiling device_mem_capacity = torch.cuda.get_device_properties(None).total_memory / 1048576 @@ -2849,7 +2854,7 @@ def generate_video( source_video = None target_camera = None if "recam" in model_filename: - source_video = preprocess_video("", width=width, height=height,video_in=video_source, max_frames= video_length, start_frame = 0, fit_canvas= fit_canvas == 1) + source_video = preprocess_video("", width=width, height=height,video_in=video_source, max_frames= current_video_length, start_frame = 0, fit_canvas= fit_canvas == 1) target_camera = model_mode audio_proj_split = None @@ -2859,8 +2864,8 @@ def generate_video( from fantasytalking.infer import parse_audio import librosa duration = librosa.get_duration(path=audio_guide) - video_length = min(int(fps * duration // 4) * 4 + 5, video_length) - audio_proj_split, audio_context_lens = parse_audio(audio_guide, num_frames= video_length, fps= fps, device= processing_device ) + current_video_length = min(int(fps * duration // 4) * 4 + 5, current_video_length) + audio_proj_split, audio_context_lens = parse_audio(audio_guide, num_frames= current_video_length, fps= fps, device= processing_device ) audio_scale = 1.0 import random @@ -2884,19 +2889,19 @@ def generate_video( else: reuse_frames = 0 if (diffusion_forcing or ltxv) and source_video != None: - video_length += sliding_window_overlap - sliding_window = (vace or diffusion_forcing or ltxv) and video_length > sliding_window_size + current_video_length += sliding_window_overlap + sliding_window = (vace or diffusion_forcing or ltxv) and current_video_length > sliding_window_size discard_last_frames = sliding_window_discard_last_frames - default_max_frames_to_generate = video_length + default_max_frames_to_generate = current_video_length if sliding_window: - left_after_first_window = video_length - sliding_window_size + discard_last_frames + left_after_first_window = current_video_length - sliding_window_size + discard_last_frames initial_total_windows= 1 + math.ceil(left_after_first_window / (sliding_window_size - discard_last_frames - reuse_frames)) - video_length = sliding_window_size + current_video_length = sliding_window_size else: initial_total_windows = 1 - first_window_video_length = video_length + first_window_video_length = current_video_length original_prompts = prompts.copy() gen["sliding_window"] = sliding_window while not abort: @@ -2917,7 +2922,7 @@ def generate_video( window_no = 0 extra_windows = 0 guide_start_frame = 0 - video_length = first_window_video_length + current_video_length = first_window_video_length gen["extra_windows"] = 0 gen["total_windows"] = 1 gen["window_no"] = 1 @@ -2967,7 +2972,7 @@ def generate_video( num_frames_generated -= reuse_frames if (max_frames_to_generate - prefix_video_frames_count - num_frames_generated) < latent_size: break - video_length = min(sliding_window_size, ((max_frames_to_generate - num_frames_generated - prefix_video_frames_count + reuse_frames + discard_last_frames) // latent_size) * latent_size + 1 ) + current_video_length = min(sliding_window_size, ((max_frames_to_generate - num_frames_generated - prefix_video_frames_count + reuse_frames + discard_last_frames) // latent_size) * latent_size + 1 ) total_windows = initial_total_windows + extra_windows gen["total_windows"] = total_windows @@ -3015,11 +3020,11 @@ def generate_video( if preprocess_type != None : send_cmd("progress", progress_args) - video_guide_copy = preprocess_video(preprocess_type, width=width, height=height,video_in=video_guide, max_frames= video_length if window_no == 1 else video_length - reuse_frames, start_frame = guide_start_frame, fit_canvas = fit_canvas, target_fps = fps) + video_guide_copy = preprocess_video(preprocess_type, width=width, height=height,video_in=video_guide, max_frames= current_video_length if window_no == 1 else current_video_length - reuse_frames, start_frame = guide_start_frame, fit_canvas = fit_canvas, target_fps = fps) keep_frames_parsed, error = parse_keep_frames_video_guide(keep_frames_video_guide, max_frames_to_generate) if len(error) > 0: raise gr.Error(f"invalid keep frames {keep_frames_video_guide}") - keep_frames_parsed = keep_frames_parsed[guide_start_frame: guide_start_frame + video_length] + keep_frames_parsed = keep_frames_parsed[guide_start_frame: guide_start_frame + current_video_length] if window_no == 1: image_size = (height, width) # default frame dimensions until it is set by video_src (if there is any) @@ -3028,13 +3033,18 @@ def generate_video( src_video, src_mask, src_ref_images = wan_model.prepare_source([video_guide_copy], [video_mask_copy ], [image_refs_copy], - video_length, image_size = image_size, device ="cpu", + current_video_length, image_size = image_size, device ="cpu", original_video= "O" in video_prompt_type, keep_frames=keep_frames_parsed, start_frame = guide_start_frame, pre_src_video = [pre_video_guide], fit_into_canvas = fit_canvas ) + if window_no == 1: + conditioning_latents_size = ( (prefix_video_frames_count-1) // latent_size) + 1 if prefix_video_frames_count > 0 else 0 + else: + conditioning_latents_size = ( (reuse_frames-1) // latent_size) + 1 + status = get_latest_status(state) gen["progress_status"] = status gen["progress_phase"] = ("Encoding Prompt", -1 ) @@ -3062,7 +3072,7 @@ def generate_video( input_masks = src_mask, input_video= pre_video_guide if diffusion_forcing or ltxv else source_video, target_camera= target_camera, - frame_num=(video_length // latent_size)* latent_size + 1, + frame_num=(current_video_length // latent_size)* latent_size + 1, height = height, width = width, fit_into_canvas = fit_canvas == 1, @@ -3092,6 +3102,7 @@ def generate_video( overlapped_latents = overlapped_latents, return_latent_slice= return_latent_slice, overlap_noise = sliding_window_overlap_noise, + conditioning_latents_size = conditioning_latents_size, model_filename = model_filename, ) except Exception as e: @@ -3160,7 +3171,7 @@ def generate_video( if gen.get("extra_windows",0) > 0: sliding_window = True if sliding_window : - guide_start_frame += video_length + guide_start_frame += current_video_length if discard_last_frames > 0: sample = sample[: , :-discard_last_frames] guide_start_frame -= discard_last_frames @@ -3259,7 +3270,7 @@ def generate_video( inputs.pop("task") configs = prepare_inputs_dict("metadata", inputs) configs["prompt"] = "\n".join(original_prompts) - if prompt_enhancer_image_caption_model != None: + if prompt_enhancer_image_caption_model != None and prompt_enhancer !=None and len(prompt_enhancer)>0: configs["enhanced_prompt"] = "\n".join(prompts) configs["generation_time"] = round(end_time-start_time) metadata_choice = server_config.get("metadata_type","metadata") @@ -3274,6 +3285,8 @@ def generate_video( print(f"New video saved to Path: "+video_path) file_list.append(video_path) + file_settings_list.append(configs) + send_cmd("output") seed += 1 @@ -3520,6 +3533,7 @@ def process_tasks(state): gen = get_gen_info(state) clear_file_list = server_config.get("clear_file_list", 0) file_list = gen.get("file_list", []) + file_settings_list = gen.get("file_settings_list", []) if clear_file_list > 0: file_list_current_size = len(file_list) keep_file_from = max(file_list_current_size - clear_file_list, 0) @@ -3527,11 +3541,13 @@ def process_tasks(state): choice = gen.get("selected",0) choice = max(choice- files_removed, 0) file_list = file_list[ keep_file_from: ] + file_settings_list = file_settings_list[ keep_file_from: ] else: file_list = [] choice = 0 gen["selected"] = choice gen["file_list"] = file_list + gen["file_settings_list"] = file_settings_list start_time = time.time() @@ -4005,12 +4021,12 @@ def prepare_inputs_dict(target, inputs ): if target == "state": return inputs - unsaved_params = ["image_start", "image_end", "image_refs", "video_guide", "video_source", "video_mask", "audio_guide", "embedded_guidance_scale"] + unsaved_params = ["image_start", "image_end", "image_refs", "video_guide", "video_source", "video_mask", "audio_guide"] for k in unsaved_params: inputs.pop(k) model_filename = state["model_filename"] - inputs["type"] = "WanGP by DeepBeepMeep - " + get_model_name(model_filename) + inputs["type"] = f"WanGP v{WanGP_version} by DeepBeepMeep - " + get_model_name(model_filename) if target == "settings": return inputs @@ -4021,10 +4037,10 @@ def prepare_inputs_dict(target, inputs ): if not server_config.get("enhancer_enabled", 0) == 1: inputs.pop("prompt_enhancer") - if not "recam" in model_filename or not "diffusion_forcing" in model_filename: + if not "recam" in model_filename and not "diffusion_forcing" in model_filename: inputs.pop("model_mode") - if not "Vace" in model_filename or not "phantom" in model_filename or not "hunyuan_video_custom" in model_filename: + if not "Vace" in model_filename and not "phantom" in model_filename and not "hunyuan_video_custom" in model_filename: unsaved_params = ["keep_frames_video_guide", "video_prompt_type", "remove_background_images_ref"] for k in unsaved_params: inputs.pop(k) @@ -4035,7 +4051,7 @@ def prepare_inputs_dict(target, inputs ): inputs.pop(k) - if not "Vace" in model_filename or "diffusion_forcing" in model_filename or "ltxv" in model_filename: + if not "Vace" in model_filename and not "diffusion_forcing" in model_filename and not "ltxv" in model_filename: unsaved_params = [ "sliding_window_size", "sliding_window_overlap", "sliding_window_overlap_noise", "sliding_window_discard_last_frames"] for k in unsaved_params: inputs.pop(k) @@ -4043,6 +4059,8 @@ def prepare_inputs_dict(target, inputs ): if not "fantasy" in model_filename: inputs.pop("audio_guidance_scale") + if not "hunyuan" in model_filename: + inputs.pop("embedded_guidance_scale") if target == "metadata": inputs = {k: v for k,v in inputs.items() if v != None } @@ -4055,7 +4073,85 @@ def get_function_arguments(func, locals): for k in args_names: kwargs[k] = locals[k] return kwargs - + +def export_settings(state): + model_filename = state["model_filename"] + model_type = get_model_type(model_filename) + settings = state[model_type] + settings["state"] = state + settings = prepare_inputs_dict("metadata", settings) + settings["model_filename"] = model_filename + text = json.dumps(settings, indent=4) + text_base64 = base64.b64encode(text.encode('utf8')).decode('utf-8') + return text_base64 + +def use_video_settings(state, files): + gen = get_gen_info(state) + choice = gen.get("selected",-1) + file_list = gen.get("file_list", None) + if file_list !=None and choice >=0 and len(file_list)>0: + file_settings_list = gen["file_settings_list"] + configs = file_settings_list[choice] + model_filename = configs["model_filename"] + model_type = get_model_type(model_filename) + defaults = state.get(model_type, None) + defaults = get_default_settings(model_filename) if defaults == None else defaults + defaults.update(configs) + current_model_filename = state["model_filename"] + prompt = configs.get("prompt", "") + state[model_type] = defaults + gr.Info(f"Settings Loaded from Video with prompt '{prompt[:100]}'") + if model_type == get_model_type(current_model_filename): + return gr.update(), str(time.time()) + else: + return generate_dropdown_model_list(model_filename), gr.update() + else: + gr.Info(f"No Video is Selected") + + return gr.update(), gr.update() + +def load_settings_from_file(state, file_path): + gen = get_gen_info(state) + if file_path==None: + return gr.update(), gr.update(), None + + configs = None + if file_path.endswith(".json"): + try: + with open(file_path, 'r', encoding='utf-8') as f: + configs = json.load(f) + except: + pass + else: + from mutagen.mp4 import MP4 + tags = None + try: + file = MP4(file_path) + tags = file.tags['©cmt'][0] + except: + pass + if tags != None: + configs = json.loads(tags) + if configs == None: + gr.Info("File not supported") + return gr.update(), gr.update(), None + + prompt = configs.get("prompt", "") + current_model_filename = state["model_filename"] + model_filename = configs["model_filename"] + model_type = get_model_type(model_filename) + defaults = state.get(model_type, None) + defaults = get_default_settings(model_filename) if defaults == None else defaults + defaults.update(configs) + state[model_type]= defaults + if tags != None: + gr.Info(f"Settings Loaded from Video generated with prompt '{prompt[:100]}'") + else: + gr.Info(f"Settings Loaded from Settings file with prompt '{prompt[:100]}'") + if model_type == get_model_type(current_model_filename): + return gr.update(), str(time.time()), None + else: + return generate_dropdown_model_list(model_filename), gr.update(), None def save_inputs( target, @@ -4420,7 +4516,7 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non if ltxv: model_mode = gr.Dropdown( choices=[ - ], + ], value=None, visible= False ) else: @@ -4470,7 +4566,7 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non columns=[3], rows=[1], object_fit="contain", height="auto", selected_index=0, interactive= True, visible="E" in image_prompt_type_value, value= ui_defaults.get("image_end", None)) video_source = gr.Video(visible=False) - model_mode = gr.Dropdown(visible=False) + model_mode = gr.Dropdown(value=None, visible=False) keep_frames_video_source = gr.Text(visible=False) with gr.Column(visible= vace or phantom or hunyuan_video_custom) as video_prompt_column: @@ -4784,14 +4880,19 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non with gr.Row(): save_settings_btn = gr.Button("Set Settings as Default", visible = not args.lock_config) - + export_settings_from_file_btn = gr.Button("Export Settings to File", visible = not args.lock_config) + use_video_settings_btn = gr.Button("Use Selected Video Settings", visible = not args.lock_config) + with gr.Row(): + settings_file = gr.File(height=41,label="Load Settings From Video / Json") + settings_base64_output = gr.Text(interactive= False, visible=False, value = "") if not update_form: with gr.Column(): gen_status = gr.Text(interactive= False, label = "Status") status_trigger = gr.Text(interactive= False, visible=False) output = gr.Gallery( label="Generated videos", show_label=False, elem_id="gallery" , columns=[3], rows=[1], object_fit="contain", height=450, selected_index=0, interactive= False) output_trigger = gr.Text(interactive= False, visible=False) - + refresh_form_trigger = gr.Text(interactive= False, visible=False) + generate_btn = gr.Button("Generate") add_to_queue_btn = gr.Button("Add New Prompt To Queue", visible = False) @@ -4891,6 +4992,8 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non gen["status_display"] = True return time.time() + start_quit_timer_js, cancel_quit_timer_js, trigger_zip_download_js, trigger_settings_download_js = get_js() + status_trigger.change(refresh_status_async, inputs= [state] , outputs= [gen_status], show_progress_on= [gen_status]) output_trigger.change(refresh_gallery, @@ -4907,8 +5010,49 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non locals_dict = locals() gen_inputs = [locals_dict[k] for k in inputs_names] + [state] save_settings_btn.click( fn=validate_wizard_prompt, inputs =[state, wizard_prompt_activated_var, wizard_variables_var, prompt, wizard_prompt, *prompt_vars] , outputs= [prompt]).then( - save_inputs, inputs =[target_settings] + gen_inputs, outputs = []) + save_inputs, inputs =[target_settings] + gen_inputs, outputs = []) + use_video_settings_btn.click(fn=validate_wizard_prompt, + inputs= [state, wizard_prompt_activated_var, wizard_variables_var, prompt, wizard_prompt, *prompt_vars] , + outputs= [prompt] + ).then(fn=save_inputs, + inputs =[target_state] + gen_inputs, + outputs= None + ).then( fn=use_video_settings, inputs =[state, output] , outputs= [model_choice, refresh_form_trigger]) + + export_settings_from_file_btn.click(fn=validate_wizard_prompt, + inputs= [state, wizard_prompt_activated_var, wizard_variables_var, prompt, wizard_prompt, *prompt_vars] , + outputs= [prompt] + ).then(fn=save_inputs, + inputs =[target_state] + gen_inputs, + outputs= None + ).then(fn=export_settings, + inputs =[state], + outputs= [settings_base64_output] + ).then( + fn=None, + inputs=[settings_base64_output], + outputs=None, + js=trigger_settings_download_js + ) + + + settings_file.upload(fn=validate_wizard_prompt, + inputs= [state, wizard_prompt_activated_var, wizard_variables_var, prompt, wizard_prompt, *prompt_vars] , + outputs= [prompt] + ).then(fn=save_inputs, + inputs =[target_state] + gen_inputs, + outputs= None + ).then(fn=load_settings_from_file, inputs =[state, settings_file] , outputs= [model_choice, refresh_form_trigger, settings_file]) + + + refresh_form_trigger.change(fn= fill_inputs, + inputs=[state], + outputs=gen_inputs + extra_inputs + ).then(fn=validate_wizard_prompt, + inputs= [state, wizard_prompt_activated_var, wizard_variables_var, prompt, wizard_prompt, *prompt_vars], + outputs= [prompt] + ) model_choice.change(fn=validate_wizard_prompt, inputs= [state, wizard_prompt_activated_var, wizard_variables_var, prompt, wizard_prompt, *prompt_vars] , @@ -4916,10 +5060,10 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non ).then(fn=save_inputs, inputs =[target_state] + gen_inputs, outputs= None - ).then(fn= change_model, + ).then(fn= change_model, inputs=[state, model_choice], outputs= [header] - ).then(fn= fill_inputs, + ).then(fn= fill_inputs, inputs=[state], outputs=gen_inputs + extra_inputs ).then(fn= preload_model_when_switching, @@ -4988,7 +5132,6 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non ) - start_quit_timer_js, cancel_quit_timer_js, trigger_zip_download_js = get_timer_js() single_hidden_trigger_btn.click( fn=show_countdown_info_from_state, @@ -5348,9 +5491,9 @@ def generate_info_tab(): gr.Markdown("Please note that if your turn on compilation, the first denoising step of the first video generation will be slow due to the compilation. Therefore all your tests should be done with compilation turned off.") -def generate_dropdown_model_list(): +def generate_dropdown_model_list(model_filename): dropdown_types= transformer_types if len(transformer_types) > 0 else model_types - current_model_type = get_model_type(transformer_filename) + current_model_type = get_model_type(model_filename) if current_model_type not in dropdown_types: dropdown_types.append(current_model_type) model_list = [] @@ -5385,7 +5528,7 @@ def select_tab(tab_state, evt:gr.SelectData): tab_state["tab_no"] = new_tab_no return gr.Tabs() -def get_timer_js(): +def get_js(): start_quit_timer_js = """ () => { function findAndClickGradioButton(elemId) { @@ -5454,9 +5597,41 @@ def get_timer_js(): } } """ - return start_quit_timer_js, cancel_quit_timer_js, trigger_zip_download_js -def create_demo(): + trigger_settings_download_js = """ + (base64String) => { + if (!base64String) { + console.log("No base64 settings data received, skipping download."); + return; + } + try { + const byteCharacters = atob(base64String); + const byteNumbers = new Array(byteCharacters.length); + for (let i = 0; i < byteCharacters.length; i++) { + byteNumbers[i] = byteCharacters.charCodeAt(i); + } + const byteArray = new Uint8Array(byteNumbers); + const blob = new Blob([byteArray], { type: 'application/text' }); + + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.style.display = 'none'; + a.href = url; + a.download = 'settings.json'; + document.body.appendChild(a); + a.click(); + + window.URL.revokeObjectURL(url); + document.body.removeChild(a); + console.log("settings download triggered."); + } catch (e) { + console.error("Error processing base64 data or triggering download:", e); + } + } + """ + return start_quit_timer_js, cancel_quit_timer_js, trigger_zip_download_js, trigger_settings_download_js + +def create_ui(): global vmc_event_handler css = """ #model_list{ @@ -5699,7 +5874,7 @@ def create_demo(): theme = gr.themes.Soft(font=["Verdana"], primary_hue="sky", neutral_hue="slate", text_size="md") with gr.Blocks(css=css, theme=theme, title= "WanGP") as main: - gr.Markdown("

WanGP v5.21 by DeepBeepMeep ") # (Updates)

") + gr.Markdown(f"

WanGP v{WanGP_version} by DeepBeepMeep ") # (Updates)

") global model_list tab_state = gr.State({ "tab_no":0 }) @@ -5712,7 +5887,7 @@ def create_demo(): model_choice = gr.Dropdown(visible=False, value= get_model_type(transformer_filename)) else: gr.Markdown("
") - model_choice = generate_dropdown_model_list() + model_choice = generate_dropdown_model_list(transformer_filename) gr.Markdown("
") with gr.Row(): header = gr.Markdown(generate_header(transformer_filename, compile, attention_mode), visible= True) @@ -5753,7 +5928,7 @@ if __name__ == "__main__": server_name = "0.0.0.0" if len(server_name) == 0: server_name = os.getenv("SERVER_NAME", "localhost") - demo = create_demo() + demo = create_ui() if args.open_browser: import webbrowser if server_name.startswith("http"):