File size: 11,179 Bytes
6d12705
 
 
db47818
280cfe1
db47818
280cfe1
9ac7175
 
6d12705
9ac7175
 
280cfe1
6d12705
280cfe1
9ac7175
6d12705
280cfe1
 
6d12705
280cfe1
 
6d12705
280cfe1
 
6d12705
280cfe1
9ac7175
 
6d12705
9ac7175
 
280cfe1
 
6d12705
280cfe1
6d12705
280cfe1
 
8bbdce0
6d12705
 
8bbdce0
280cfe1
6d12705
8bbdce0
280cfe1
 
 
6d12705
280cfe1
 
 
9ac7175
6d12705
280cfe1
 
 
 
6d12705
 
 
280cfe1
9ac7175
6d12705
 
 
280cfe1
 
 
9ac7175
994d098
280cfe1
 
9ac7175
280cfe1
9ac7175
6d12705
280cfe1
9ac7175
280cfe1
 
6d12705
280cfe1
 
 
6d12705
 
280cfe1
 
6d12705
 
 
280cfe1
 
6d12705
 
280cfe1
 
6d12705
 
 
9ac7175
280cfe1
6d12705
280cfe1
9ac7175
280cfe1
6d12705
280cfe1
6d12705
280cfe1
6d12705
9ac7175
6d12705
 
280cfe1
 
 
 
6d12705
 
9ac7175
280cfe1
 
9ac7175
280cfe1
9ac7175
280cfe1
6d12705
 
7720807
280cfe1
 
 
 
6d12705
280cfe1
 
 
 
 
 
 
 
 
 
 
6d12705
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280cfe1
 
7720807
280cfe1
6d12705
280cfe1
6d12705
280cfe1
 
 
 
 
 
 
 
 
 
6d12705
 
280cfe1
 
6d12705
994d098
280cfe1
 
 
6d12705
db47818
280cfe1
 
6d12705
 
 
280cfe1
6d12705
280cfe1
 
 
 
 
 
6d12705
db47818
280cfe1
6d12705
280cfe1
9ac7175
280cfe1
 
 
 
 
6d12705
 
280cfe1
 
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# FILE: app.py
# DESCRIPTION: Final Gradio web interface for the ADUC-SDR Video Suite.
# Features a unified workflow, advanced LTX controls, and a clean, modular structure.

import gradio as gr
import traceback
import sys

# ==============================================================================
# --- IMPORTAÇÃO DOS SERVIÇOS DE BACKEND ---
# ==============================================================================

try:
    # A UI depende do VideoService para todas as operações LTX.
    from api.ltx_server_refactored_complete import video_generation_service
    
    # A importação do SeedVR permanece opcional.
    # from api.seedvr_server import SeedVRServer
    # seedvr_inference_server = SeedVRServer()
    seedvr_inference_server = None # Desativado para este exemplo
    print("Backend services imported successfully.")
except ImportError as e:
    print(f"FATAL ERROR: Could not import backend services. Details: {e}", file=sys.stderr)
    sys.exit(1)
except Exception as e:
    print(f"FATAL ERROR: An unexpected error occurred during backend initialization. Details: {e}", file=sys.stderr)
    sys.exit(1)

# ==============================================================================
# --- FUNÇÕES WRAPPER (PONTE ENTRE UI E BACKEND) ---
# ==============================================================================

def run_generate_base_video(
    generation_mode: str, prompt: str, neg_prompt: str, start_img: str, 
    height: int, width: int, duration: float,
    fp_guidance_preset: str, fp_guidance_scale_list: str, fp_stg_scale_list: str,
    fp_num_inference_steps: int, fp_skip_initial_steps: int, fp_skip_final_steps: int,
    progress=gr.Progress(track_tqdm=True)
) -> tuple:
    """
    Wrapper que coleta todos os dados da UI, os empacota e chama a função de geração
    unificada do backend.
    """
    try:
        print(f"[UI] Request received. Selected mode: {generation_mode}")
        
        initial_conditions = []
        if start_img:
            num_frames_estimate = int(duration * 24)
            items_list = [[start_img, 0, 1.0]]
            initial_conditions = video_generation_service.prepare_condition_items(
                items_list, height, width, num_frames_estimate
            )

        # Agrupa todas as configurações LTX em um único dicionário para o backend
        ltx_configs = {
            "guidance_preset": fp_guidance_preset,
            "guidance_scale_list": fp_guidance_scale_list,
            "stg_scale_list": fp_stg_scale_list,
            "num_inference_steps": fp_num_inference_steps,
            "skip_initial_inference_steps": fp_skip_initial_steps,
            "skip_final_inference_steps": fp_skip_final_steps,
        }

        video_path, tensor_path, final_seed = video_generation_service.generate_low_resolution(
            prompt=prompt,
            negative_prompt=neg_prompt,
            height=height, width=width, duration=duration,
            initial_conditions=initial_conditions,
            ltx_configs_override=ltx_configs
        )
        
        if not video_path:
            raise RuntimeError("Backend failed to return a valid video path.")

        new_state = {"low_res_video": video_path, "low_res_latents": tensor_path, "used_seed": final_seed}
        
        print(f"[UI] Base video generation successful. Seed used: {final_seed}, Path: {video_path}")
        return video_path, new_state, gr.update(visible=True)
        
    except Exception as e:
        error_message = f"❌ An error occurred during base generation:\n{e}"
        print(f"{error_message}\nDetails: {traceback.format_exc()}", file=sys.stderr)
        raise gr.Error(error_message)


def run_ltx_refinement(state: dict, prompt: str, neg_prompt: str, progress=gr.Progress(track_tqdm=True)) -> tuple:
    """Wrapper para chamar a função de refinamento/upscale do LTX."""
    if not state or not state.get("low_res_latents"):
        raise gr.Error("Error: Please generate a base video in Step 1 before refining.")
    # (A lógica desta função permanece a mesma)
    # ...
    return None, state


def run_seedvr_upscaling(state: dict, resolution: int, batch_size: int, fps: int, progress=gr.Progress(track_tqdm=True)) -> tuple:
    """Wrapper para chamar o serviço de upscale do SeedVR."""
    if not state or not state.get("low_res_video"):
        raise gr.Error("Error: Please generate a base video in Step 1 before upscaling.")
    # (A lógica desta função permanece a mesma)
    # ...
    return None, "Not implemented."

# ==============================================================================
# --- CONSTRUÇÃO DA INTERFACE GRADIO ---
# ==============================================================================

def build_ui():
    """Constrói a interface completa do Gradio."""
    
    with gr.Blocks(theme=gr.themes.Soft(primary_hue="indigo")) as demo:
        app_state = gr.State(value={"low_res_video": None, "low_res_latents": None, "used_seed": None})
        ui_components = {}

        gr.Markdown("# ADUC-SDR Video Suite - LTX Workflow", elem_id="main-title")
        
        with gr.Row():
            with gr.Column(scale=1):
                _build_generation_controls(ui_components)
            with gr.Column(scale=1):
                gr.Markdown("### Etapa 1: Vídeo Base Gerado")
                ui_components['low_res_video_output'] = gr.Video(label="O resultado aparecerá aqui", interactive=False)
        
        _build_postprod_controls(ui_components)
        _register_event_handlers(app_state, ui_components)

    return demo

def _build_generation_controls(ui: dict):
    """Constrói os componentes da UI para a Etapa 1: Geração."""
    gr.Markdown("### Configurações de Geração")
    
    ui['generation_mode'] = gr.Radio(
        label="Modo de Geração", 
        choices=["Simples (Prompt Único)", "Narrativa (Múltiplos Prompts)"],
        value="Narrativa (Múltiplos Prompts)", 
        info="Simples para uma ação contínua, Narrativa para uma sequência (uma cena por linha)."
    )
    ui['prompt'] = gr.Textbox(label="Prompt(s)", value="Um leão majestoso caminha pela savana\nEle sobe em uma grande pedra e olha o horizonte", lines=4)
    ui['neg_prompt'] = gr.Textbox(label="Negative Prompt", value="blurry, low quality, bad anatomy, deformed", lines=2)
    ui['start_image'] = gr.Image(label="Imagem de Início (Opcional)", type="filepath", sources=["upload"])
    
    with gr.Accordion("Parâmetros Principais", open=True):
        ui['duration'] = gr.Slider(label="Duração Total (s)", value=4, step=1, minimum=1, maximum=30)
        with gr.Row():
            ui['height'] = gr.Slider(label="Height", value=432, step=16, minimum=256, maximum=1024)
            ui['width'] = gr.Slider(label="Width", value=768, step=16, minimum=256, maximum=1024)

    with gr.Accordion("Opções Avançadas LTX", open=False):
        gr.Markdown("#### Configurações de Passos de Inferência (First Pass)")
        gr.Markdown("*Deixe o valor padrão (ex: 20) ou 0 para usar a configuração do `config.yaml`.*")
        
        ui['fp_num_inference_steps'] = gr.Slider(label="Número de Passos", minimum=0, maximum=100, step=1, value=20, info="Padrão LTX: 20.")
        ui['fp_skip_initial_steps'] = gr.Slider(label="Pular Passos Iniciais", minimum=0, maximum=100, step=1, value=0)
        ui['fp_skip_final_steps'] = gr.Slider(label="Pular Passos Finais", minimum=0, maximum=100, step=1, value=0)

        with gr.Tabs():
            with gr.TabItem("Configurações de Guiagem (First Pass)"):
                ui['fp_guidance_preset'] = gr.Dropdown(
                    label="Preset de Guiagem",
                    choices=["Padrão (Recomendado)", "Agressivo", "Suave", "Customizado"],
                    value="Padrão (Recomendado)", info="Controla o comportamento da guiagem durante a difusão."
                )
                with gr.Group(visible=False) as ui['custom_guidance_group']:
                    gr.Markdown("⚠️ Edite as listas em formato JSON. Ex: `[1.0, 2.5, 3.0]`")
                    ui['fp_guidance_scale_list'] = gr.Textbox(label="Lista de Guidance Scale", value="[1, 1, 6, 8, 6, 1, 1]")
                    ui['fp_stg_scale_list'] = gr.Textbox(label="Lista de STG Scale (Movimento)", value="[0, 0, 4, 4, 4, 2, 1]")
    
    ui['generate_low_btn'] = gr.Button("1. Gerar Vídeo Base", variant="primary")

def _build_postprod_controls(ui: dict):
    """Constrói os componentes da UI para a Etapa 2: Pós-Produção."""
    with gr.Group(visible=False) as ui['post_prod_group']:
        gr.Markdown("--- \n## Etapa 2: Pós-Produção")
        with gr.Tabs():
            with gr.TabItem("🚀 Upscaler de Textura (LTX)"):
                with gr.Row():
                    with gr.Column(scale=1):
                         gr.Markdown("Usa o prompt e a semente originais para refinar o vídeo, adicionando detalhes e texturas de alta qualidade.")
                         ui['ltx_refine_btn'] = gr.Button("2. Aplicar Refinamento LTX", variant="primary")
                    with gr.Column(scale=1):
                        ui['ltx_refined_video_output'] = gr.Video(label="Vídeo com Textura Refinada", interactive=False)
            
            with gr.TabItem("✨ Upscaler de Resolução (SeedVR)"):
                # (A UI do SeedVR permanece a mesma, desativada se o servidor não estiver disponível)
                pass

def _register_event_handlers(app_state: gr.State, ui: dict):
    """Registra todos os manipuladores de eventos do Gradio."""
    
    def toggle_custom_guidance(preset_choice: str) -> gr.update:
        return gr.update(visible=(preset_choice == "Customizado"))
    
    ui['fp_guidance_preset'].change(fn=toggle_custom_guidance, inputs=ui['fp_guidance_preset'], outputs=ui['custom_guidance_group'])

    gen_inputs = [
        ui['generation_mode'], ui['prompt'], ui['neg_prompt'], ui['start_image'],
        ui['height'], ui['width'], ui['duration'],
        ui['fp_guidance_preset'], ui['fp_guidance_scale_list'], ui['fp_stg_scale_list'],
        ui['fp_num_inference_steps'], ui['fp_skip_initial_steps'], ui['fp_skip_final_steps'],
    ]
    gen_outputs = [ui['low_res_video_output'], app_state, ui['post_prod_group']]
    ui['generate_low_btn'].click(fn=run_generate_base_video, inputs=gen_inputs, outputs=gen_outputs)

    refine_inputs = [app_state, ui['prompt'], ui['neg_prompt']]
    refine_outputs = [ui['ltx_refined_video_output'], app_state]
    ui['ltx_refine_btn'].click(fn=run_ltx_refinement, inputs=refine_inputs, outputs=refine_outputs)
    
    # (Handlers para o SeedVR, se ativados)

# ==============================================================================
# --- PONTO DE ENTRADA DA APLICAÇÃO ---
# ==============================================================================

if __name__ == "__main__":
    print("Building Gradio UI...")
    gradio_app = build_ui()
    print("Launching Gradio app...")
    gradio_app.queue().launch(
        server_name=os.getenv("GRADIO_SERVER_NAME", "0.0.0.0"), 
        server_port=int(os.getenv("GRADIO_SERVER_PORT", "7860")),
        show_error=True
    )