File size: 8,147 Bytes
1b56622
 
fb56537
5b259dd
1b56622
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67b292e
 
1b56622
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7f72576
ccf9c7e
dea92b2
1b56622
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151


import gradio as gr
from PIL import Image
import os
import imageio

# Importa a instância do nosso serviço
# O modelo será carregado quando este módulo for importado
from video_service import video_generation_service

# --- FUNÇÕES DE AJUDA PARA A UI (não relacionadas ao modelo) ---

TARGET_FIXED_SIDE = 768
MIN_DIM_SLIDER = 256
MAX_IMAGE_SIZE = 1280

def calculate_new_dimensions(orig_w, orig_h):
    if orig_w == 0 or orig_h == 0: return int(TARGET_FIXED_SIDE), int(TARGET_FIXED_SIDE)
    if orig_w >= orig_h:
        new_h, aspect_ratio = TARGET_FIXED_SIDE, orig_w / orig_h
        new_w = round((new_h * aspect_ratio) / 32) * 32
        new_w = max(MIN_DIM_SLIDER, min(new_w, MAX_IMAGE_SIZE))
        new_h = max(MIN_DIM_SLIDER, min(new_h, MAX_IMAGE_SIZE)) 
    else:
        new_w, aspect_ratio = TARGET_FIXED_SIDE, orig_h / orig_w
        new_h = round((new_w * aspect_ratio) / 32) * 32
        new_h = max(MIN_DIM_SLIDER, min(new_h, MAX_IMAGE_SIZE))
        new_w = max(MIN_DIM_SLIDER, min(new_w, MAX_IMAGE_SIZE))
    return int(new_h), int(new_w)

def handle_media_upload_for_dims(filepath, current_h, current_w):
    if not filepath or not os.path.exists(str(filepath)): return gr.update(value=current_h), gr.update(value=current_w)
    try:
        if str(filepath).lower().endswith(('.png', '.jpg', '.jpeg', '.webp')):
            with Image.open(filepath) as img:
                orig_w, orig_h = img.size
        else: # Assumir que é um vídeo
            with imageio.get_reader(filepath) as reader:
                meta = reader.get_meta_data()
                orig_w, orig_h = meta.get('size', (current_w, current_h))
        new_h, new_w = calculate_new_dimensions(orig_w, orig_h)
        return gr.update(value=new_h), gr.update(value=new_w)
    except Exception as e:
        print(f"Erro ao processar mídia para dimensões: {e}")
        return gr.update(value=current_h), gr.update(value=current_w)

# --- FUNÇÃO WRAPPER PARA CHAMAR O SERVIÇO A PARTIR DO GRADIO ---

def gradio_generate_wrapper(prompt, negative_prompt, input_image, input_video,
                            height, width, mode, duration, frames_to_use,
                            seed, randomize_seed, guidance_scale, improve_texture,
                            progress=gr.Progress(track_tqdm=True)):
    """
    Esta função atua como uma ponte entre a interface Gradio e o nosso VideoService.
    """
    try:
        # Define a função de callback para a barra de progresso do Gradio
        def progress_handler(step, total_steps):
            progress(step / total_steps, desc="Salvando vídeo...")

        output_path, used_seed = video_generation_service.generate(
            prompt=prompt,
            negative_prompt=negative_prompt,
            input_image_filepath=input_image,
            input_video_filepath=input_video,
            height=int(height),
            width=int(width),
            mode=mode,
            duration=float(duration),
            frames_to_use=int(frames_to_use),
            seed=int(seed),
            randomize_seed=bool(randomize_seed),
            guidance_scale=float(guidance_scale),
            improve_texture=bool(improve_texture),
            progress_callback=progress_handler # Passamos o handler para o serviço
        )
        return output_path, used_seed
    except ValueError as e:
        # Captura erros de validação do serviço e os exibe na UI
        raise gr.Error(str(e))
    except Exception as e:
        # Captura outros erros inesperados
        print(f"Erro inesperado na geração: {e}")
        raise gr.Error("Ocorreu um erro inesperado. Verifique os logs.")


# --- DEFINIÇÃO DA INTERFACE GRADIO ---

css = "#col-container { margin: 0 auto; max-width: 900px; }"
with gr.Blocks(css=css) as demo:
    gr.Markdown("# LTX Video 0.9.8 13B Distilled")
    gr.Markdown("Geração de vídeo rápida e de alta qualidade.")

    with gr.Row():
        with gr.Column():
            # ... (Layout das abas e componentes exatamente como antes) ...
            with gr.Tab("image-to-video") as image_tab:
                video_i_hidden = gr.Textbox(label="video_i", visible=False, value=None)
                image_i2v = gr.Image(label="Input Image", type="filepath", sources=["upload", "webcam", "clipboard"])
                i2v_prompt = gr.Textbox(label="Prompt", value="The creature from the image starts to move", lines=3)
                i2v_button = gr.Button("Generate Image-to-Video", variant="primary")
            with gr.Tab("text-to-video") as text_tab:
                image_n_hidden = gr.Textbox(label="image_n", visible=False, value=None)
                video_n_hidden = gr.Textbox(label="video_n", visible=False, value=None)
                t2v_prompt = gr.Textbox(label="Prompt", value="A majestic dragon flying over a medieval castle", lines=3)
                t2v_button = gr.Button("Generate Text-to-Video", variant="primary")
            with gr.Tab("video-to-video", visible=True) as video_tab:
                image_v_hidden = gr.Textbox(label="image_v", visible=False, value=None)
                video_v2v = gr.Video(label="Input Video", sources=["upload", "webcam"])
                frames_to_use = gr.Slider(label="Frames to use from input video", minimum=9, maximum=257, value=9, step=8, info="Must be N*8+1.")
                v2v_prompt = gr.Textbox(label="Prompt", value="Change the style to cinematic anime", lines=3)
                v2v_button = gr.Button("Generate Video-to-Video", variant="primary")

            duration_input = gr.Slider(label="Video Duration (seconds)", minimum=0.3, maximum=8.5, value=2, step=0.1)
            improve_texture = gr.Checkbox(label="Improve Texture (multi-scale)", value=True, visible=True)

        with gr.Column():
            output_video = gr.Video(label="Generated Video", interactive=False)

    with gr.Accordion("Advanced settings", open=False):
        mode = gr.Dropdown(["text-to-video", "image-to-video", "video-to-video"], label="task", value="image-to-video", visible=False)
        negative_prompt_input = gr.Textbox(label="Negative Prompt", value="worst quality, inconsistent motion, blurry, jittery, distorted", lines=2)
        with gr.Row():
            seed_input = gr.Number(label="Seed", value=42, precision=0)
            randomize_seed_input = gr.Checkbox(label="Randomize Seed", value=True)
        guidance_scale_input = gr.Slider(label="Guidance Scale (CFG)", minimum=1.0, maximum=10.0, value=3.0, step=0.1)
        with gr.Row():
            height_input = gr.Slider(label="Height", value=512, step=32, minimum=MIN_DIM_SLIDER, maximum=MAX_IMAGE_SIZE)
            width_input = gr.Slider(label="Width", value=704, step=32, minimum=MIN_DIM_SLIDER, maximum=MAX_IMAGE_SIZE)

    # --- LÓGICA DE EVENTOS DA UI ---

    image_i2v.upload(fn=handle_media_upload_for_dims, inputs=[image_i2v, height_input, width_input], outputs=[height_input, width_input])
    video_v2v.upload(fn=handle_media_upload_for_dims, inputs=[video_v2v, height_input, width_input], outputs=[height_input, width_input])

    image_tab.select(fn=lambda: "image-to-video", outputs=[mode])
    text_tab.select(fn=lambda: "text-to-video", outputs=[mode])
    video_tab.select(fn=lambda: "video-to-video", outputs=[mode])
    
    common_inputs = [negative_prompt_input, height_input, width_input, mode, duration_input, frames_to_use, seed_input, randomize_seed_input, guidance_scale_input, improve_texture]
    common_outputs = [output_video, seed_input]
    
    t2v_button.click(fn=gradio_generate_wrapper, inputs=[t2v_prompt, *common_inputs[:1], image_n_hidden, video_n_hidden, *common_inputs[1:]], outputs=common_outputs, api_name="text_to_video")
    i2v_button.click(fn=gradio_generate_wrapper, inputs=[i2v_prompt, *common_inputs[:1], image_i2v, video_i_hidden, *common_inputs[1:]], outputs=common_outputs, api_name="image_to_video")
    v2v_button.click(fn=gradio_generate_wrapper, inputs=[v2v_prompt, *common_inputs[:1], image_v_hidden, video_v2v, *common_inputs[1:]], outputs=common_outputs, api_name="video_to_video")


if __name__ == "__main__":
    demo.queue().launch(debug=True, share=False)