# app_wan.py (v1.5.0 - com yield para atualizações em tempo real) import os import gradio as gr import numpy as np from PIL import Image # === Constantes da UI === MAX_SEED = np.iinfo(np.int32).max MIN_TOTAL_FRAMES = 9 MAX_TOTAL_FRAMES = 81 STEP_TOTAL_FRAMES = 4 # Garante 4n+1 (9, 13, 17...) MIN_HANDLER_FRAME = 17 # Garante 8n+1 e buffer inicial (2*8+1) MAX_HANDLER_FRAME = 73 # Garante buffer final (9*8+1) STEP_HANDLER_FRAME = 8 # Garante 8n+1 # === Importa o serviço de geração (manager) === from aduc_framework.managers.wan_manager import WanManager wan_manager = WanManager() # === Wrapper da UI para o Serviço (agora é um GERADOR) === def ui_generate_video( # --- Inputs da Aba 1: Image-to-Video --- convergent_img_i2v, handler_img_i2v, handler_frame_slider, handler_weight_slider, causal_img_i2v, total_frames_slider, causal_weight_slider, # --- Inputs da Aba 2: Video Extender --- convergent_img_v2v, causal_video_upload, # --- Inputs Comuns --- prompt, negative_prompt, fps, resolution, steps, guidance_scale, guidance_scale_2, seed, randomize_seed, progress=gr.Progress(track_tqdm=True), ): # Determina o modo de operação e a imagem convergente correta is_v2v_mode = causal_video_upload is not None convergent_img = convergent_img_v2v if is_v2v_mode else convergent_img_i2v # Validação de entradas essenciais if convergent_img is None: raise gr.Error("A 'Convergent Image (Start)' é obrigatória em ambos os modos.") if not is_v2v_mode and causal_img_i2v is None: raise gr.Error("A 'Causal Image (End)' é obrigatória no modo Image-to-Video.") # Itera sobre os resultados parciais produzidos pelo manager # A última iteração conterá o resultado final completo. for result in wan_manager.generate_video( convergent_img=convergent_img, causal_video_path=causal_video_upload, causal_img=causal_img_i2v, handler_img=handler_img_i2v, total_frames=int(total_frames_slider), handler_frame=int(handler_frame_slider), causal_weight=float(causal_weight_slider), handler_weight=float(handler_weight_slider), prompt=prompt, negative_prompt=negative_prompt, fps=int(fps), resolution=resolution, steps=int(steps), guidance_scale=float(guidance_scale), guidance_scale_2=float(guidance_scale_2), seed=int(seed), randomize_seed=bool(randomize_seed), ): # result é uma tupla: (video_path, seed, lista_de_videos_de_passo) final_video_path, current_seed, denoising_videos = result # Cria um dicionário de atualização para o Gradio. # A cada passo, atualizamos a galeria com a lista de vídeos. # No último passo, o vídeo principal e a seed também são atualizados. update_dict = { output_video: gr.update(value=final_video_path), seed_input: gr.update(value=current_seed), steps_video_gallery: gr.update(value=denoising_videos) } # Produz (yield) a atualização para a UI do Gradio yield update_dict # === Interface Gradio === css = ''' .fillable{max-width: 1100px !important} .dark .progress-text {color: white} #general_items{margin-top: 2em} ''' with gr.Blocks(theme=gr.themes.Glass(), css=css) as app: gr.Markdown("# Wan 2.2 Aduca-SDR") with gr.Row(elem_id="general_items"): with gr.Column(scale=2): with gr.Tabs() as tabs: with gr.TabItem("Image-to-Video", id="i2v_tab"): with gr.Row(): with gr.Column(): convergent_image_i2v = gr.Image(type="pil", label="Convergent Img (Start)", sources=["upload", "clipboard"]) with gr.Column(): handler_image_i2v = gr.Image(type="pil", label="Handler Img (Middle)", sources=["upload", "clipboard"]) handler_frame_slider = gr.Slider( minimum=MIN_HANDLER_FRAME, maximum=MAX_HANDLER_FRAME, step=STEP_HANDLER_FRAME, value=25, label="Handler Frame" ) handler_weight_slider = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, value=1.0, label="Handler Weight") with gr.Column(): causal_image_i2v = gr.Image(type="pil", label="Causal Img (End)", sources=["upload", "clipboard"]) total_frames_slider = gr.Slider( minimum=MIN_TOTAL_FRAMES, maximum=MAX_TOTAL_FRAMES, step=STEP_TOTAL_FRAMES, value=33, label="Total Frames" ) causal_weight_slider = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, value=1.0, label="Causal Weight") with gr.TabItem("Video Extender", id="v2v_tab"): with gr.Row(): with gr.Column(): convergent_image_v2v = gr.Image(type="pil", label="Convergent Img (Start)", sources=["upload", "clipboard"]) gr.Markdown("

A imagem para iniciar a nova seção do vídeo.

") with gr.Column(): causal_video_upload = gr.Video(label="Causal Video", sources=["upload"]) gr.Markdown("

O vídeo a ser estendido.

") gr.Markdown("---") prompt = gr.Textbox( label="Prompt", info="Descreva a nova cena ou transição. Ex: 'the scene changes to a futuristic city at night'." ) with gr.Accordion("Advanced Settings", open=False): with gr.Row(): resolution_selector = gr.Dropdown( choices=["480x832", "832x480", "240x416", "416x240", "720x1280", "1280x720", "512x512"], value="480x832", label="Resolution (H x W)", info="No modo Video Extender, o vídeo será conformado para esta resolução." ) fps_selector = gr.Dropdown(choices=[8, 16, 24], value=16, label="FPS", info="O vídeo será conformado para este FPS.") negative_prompt_input = gr.Textbox( label="Negative Prompt", value=wan_manager.default_negative_prompt, lines=3 ) steps_slider = gr.Slider(minimum=1, maximum=30, step=1, value=8, label="Inference Steps") guidance_scale_input = gr.Slider( minimum=0.0, maximum=10.0, step=0.5, value=1.0, label="Guidance Scale (High Noise)" ) guidance_scale_2_input = gr.Slider( minimum=0.0, maximum=10.0, step=0.5, value=1.0, label="Guidance Scale (Low Noise)" ) with gr.Row(): seed_input = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=42, interactive=True) randomize_seed_checkbox = gr.Checkbox(label="Randomize Seed", value=True) generate_button = gr.Button("Generate Video", variant="primary") with gr.Column(scale=1): output_video = gr.Video(label="Generated Video", autoplay=True) with gr.Accordion("Denoising Process Visuals", open=True): gr.Markdown("Evolução do vídeo completo em cada passo do Denoising.") steps_video_gallery = gr.Gallery( label="Denoising Steps", object_fit="contain", height="auto", columns=4, ) ui_inputs = [ convergent_image_i2v, handler_image_i2v, handler_frame_slider, handler_weight_slider, causal_image_i2v, total_frames_slider, causal_weight_slider, convergent_image_v2v, causal_video_upload, prompt, negative_prompt_input, fps_selector, resolution_selector, steps_slider, guidance_scale_input, guidance_scale_2_input, seed_input, randomize_seed_checkbox, ] ui_outputs = [output_video, seed_input, steps_video_gallery] generate_button.click(fn=ui_generate_video, inputs=ui_inputs, outputs=ui_outputs) if __name__ == "__main__": app.launch(server_name="0.0.0.0", server_port=7860, show_error=True)