File size: 10,833 Bytes
7c81ca5
 
da887e7
 
7c81ca5
 
 
 
 
 
da887e7
7c81ca5
 
 
 
 
dda2256
da887e7
 
dda2256
da887e7
 
aa98d8e
7c81ca5
 
dda2256
7c81ca5
dda2256
 
7c81ca5
 
 
 
 
 
 
da887e7
 
7c81ca5
 
da887e7
7c81ca5
 
da887e7
 
 
7c81ca5
da887e7
7c81ca5
da887e7
7c81ca5
 
 
 
da887e7
7c81ca5
 
da887e7
 
 
 
 
 
 
 
 
 
 
7c81ca5
 
da887e7
 
7c81ca5
da887e7
 
 
 
7c81ca5
 
 
da887e7
7c81ca5
 
 
 
 
 
da887e7
7c81ca5
 
d509302
7c81ca5
 
 
 
 
 
d509302
7c81ca5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
da887e7
7c81ca5
da887e7
7c81ca5
 
 
da887e7
7c81ca5
 
 
 
 
 
 
 
da887e7
 
 
 
7c81ca5
 
 
 
 
da887e7
 
7c81ca5
 
da887e7
 
 
 
 
 
7c81ca5
da887e7
7c81ca5
 
da887e7
7c81ca5
 
 
 
d509302
7c81ca5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
da887e7
7c81ca5
 
da887e7
7c81ca5
 
 
da887e7
7c81ca5
 
 
 
 
 
 
 
 
 
 
72155bf
3e6ea16
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
# FILE: app.py
# DESCRIPTION: Final Gradio web interface for the ADUC-SDR Video Suite.
# This version is refactored to use the central LtxAducOrchestrator, simplifying the UI logic
# and making it a pure client of the backend services.

import gradio as gr
import traceback
import sys
import os
import logging
from PIL import Image

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

#try:
    # --- MUDANÇA PRINCIPAL: Importamos apenas o ORQUESTRADOR ---
    # O orquestrador é agora nosso único ponto de entrada para a geração de vídeo.
from api.ltx.ltx_aduc_orchestrator import ltx_aduc_orchestrator
    
    # O SeedVR (upscaler de resolução) ainda é um serviço separado que pode ser chamado após a geração.
from api.seedvr.seedvr_aduc_pipeline import seed_aduc_pipeline
    
    # Nosso decorador de logging para depuração
from utils.debug_utils import log_function_io
    
logging.info("All backend services (Orchestrator, SeedVR) and debug utils imported successfully.")



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

@log_function_io
def run_orchestrated_generation(
    prompt: str, start_img_path: 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,
    progress=gr.Progress(track_tqdm=True)
) -> tuple:
    """
    Função wrapper simplificada que coleta dados da UI e chama o orquestrador principal.
    """
    try:
        logging.info("[UI] Request received. Submitting job to the main orchestrator...")
        
        # Monta o dicionário de configurações avançadas LTX a partir da UI
        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
        }

        # Carrega a imagem inicial para um objeto PIL, que é o que o orquestrador espera.
        initial_image_pil = Image.open(start_img_path).convert("RGB") if start_img_path else None

        # --- CHAMADA ÚNICA E LIMPA PARA O ORQUESTRADOR ---
        video_path = ltx_aduc_orchestrator(
            prompt=prompt,
            initial_image=initial_image_pil,
            height=height,
            width=width,
            duration_in_seconds=duration,
            ltx_configs=ltx_configs
        )
        
        if not video_path:
            raise RuntimeError("Orchestrator failed to return a valid video path. Check backend logs for details.")

        logging.info(f"[UI] Orchestrator job successful. Video path: {video_path}")
        
        # O estado agora pode ser mais simples, apenas guardando o caminho do vídeo gerado para o próximo passo (SeedVR).
        new_state = {"low_res_video": video_path}
        return video_path, new_state, gr.update(visible=True)
        
    except Exception as e:
        error_message = f"❌ An error occurred during the orchestrated generation:\n{e}"
        logging.error(f"{error_message}\nDetails: {traceback.format_exc()}", exc_info=True)
        raise gr.Error(error_message)


@log_function_io
def run_seedvr_upscaling(state: dict, seed: int, resolution: int, batch_size: int, fps: int, progress=gr.Progress(track_tqdm=True)) -> tuple:
    """Wrapper para o upscale de resolução SeedVR. Esta função permanece a mesma."""
    if not state or not state.get("low_res_video"):
        raise gr.Error("Error: Please generate a base video in Step 1 before upscaling.")
    if not seed_aduc_pipeline:
        raise gr.Error("Error: The SeedVR upscaling server is not available.")
    
    try:
        logging.info(f"[UI] Requesting SeedVR upscaling for video: {state.get('low_res_video')}")
        def progress_wrapper(p, desc=""): progress(p, desc=desc)
        
        output_filepath = seed_aduc_pipeline.run_inference(
            file_path=state["low_res_video"], seed=int(seed), resolution=int(resolution),
            batch_size=int(batch_size), fps=float(fps), progress=progress_wrapper
        )
        
        status_message = f"✅ Upscaling complete!\nSaved to: {output_filepath}"
        logging.info(f"[UI] SeedVR upscaling successful. Path: {output_filepath}")
        return gr.update(value=output_filepath), gr.update(value=status_message)
    except Exception as e:
        error_message = f"❌ An error occurred during SeedVR Upscaling:\n{e}"
        logging.error(f"{error_message}\nDetails: {traceback.format_exc()}", exc_info=True)
        return None, gr.update(value=error_message)

# ==============================================================================
# --- 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})
        ui_components = {}
        gr.Markdown("# ADUC-SDR Video Suite - Orchestrated 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 pelo Orquestrador")
                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['prompt'] = gr.Textbox(label="Prompt(s) de Geração", 
                              info="Escreva sua história. Cada nova linha será tratada como uma nova cena.",
                              value="Um leão majestoso caminha pela savana\nEle sobe em uma grande pedra e olha o horizonte", 
                              lines=4)
    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="Altura", value=432, step=8, minimum=256, maximum=1024)
            ui['width'] = gr.Slider(label="Largura", value=768, step=8, minimum=256, maximum=1024)

    with gr.Accordion("Opções Avançadas LTX", open=False):
        ui['fp_num_inference_steps'] = gr.Slider(label="Número de Passos", minimum=1, maximum=100, step=1, value=20)
        ui['fp_guidance_preset'] = gr.Dropdown(label="Preset de Guiagem", choices=["Padrão (Recomendado)", "Customizado"], value="Padrão (Recomendado)")
        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 via Orquestrador", variant="primary")

def _build_postprod_controls(ui: dict):
    """Constrói os componentes da UI para a Etapa 2: Pós-Produção (Upscaling)."""
    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 Resolução (SeedVR)"):
                is_seedvr_available = seed_aduc_pipeline is not None
                if not is_seedvr_available:
                    gr.Markdown("🔴 **AVISO: O serviço SeedVR não está disponível.**")
                with gr.Row():
                    with gr.Column(scale=1):
                        ui['seedvr_seed'] = gr.Slider(minimum=0, maximum=999999, value=42, step=1, label="Seed")
                        ui['seedvr_resolution'] = gr.Slider(minimum=720, maximum=2160, value=1080, step=8, label="Resolução Vertical Alvo")
                        ui['seedvr_batch_size'] = gr.Slider(minimum=1, maximum=16, value=4, step=1, label="Batch Size por GPU")
                        ui['seedvr_fps'] = gr.Number(label="FPS de Saída (0 = original)", value=0)
                        ui['run_seedvr_btn'] = gr.Button("2. Iniciar Upscaling SeedVR", variant="primary", interactive=is_seedvr_available)
                    with gr.Column(scale=1):
                        ui['seedvr_video_output'] = gr.Video(label="Vídeo com Upscale SeedVR", interactive=False)
                        ui['seedvr_status_box'] = gr.Textbox(label="Status do SeedVR", value="Aguardando...", lines=3, interactive=False)

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['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'],
    ]
    gen_outputs = [ui['low_res_video_output'], app_state, ui['post_prod_group']]
    
    ui['generate_low_btn'].click(fn=run_orchestrated_generation, inputs=gen_inputs, outputs=gen_outputs)
    
    if 'run_seedvr_btn' in ui and ui['run_seedvr_btn'].interactive:
        seedvr_inputs = [app_state, ui['seedvr_seed'], ui['seedvr_resolution'], ui['seedvr_batch_size'], ui['seedvr_fps']]
        seedvr_outputs = [ui['seedvr_video_output'], ui['seedvr_status_box']]
        ui['run_seedvr_btn'].click(fn=run_seedvr_upscaling, inputs=seedvr_inputs, outputs=seedvr_outputs)

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

if __name__ == "__main__":
    demo = build_ui()
    demo.queue().launch(server_name="0.0.0.0", server_port=7860, debug=True, show_error=True)