File size: 13,910 Bytes
501fd32
 
 
 
52e5575
501fd32
 
 
 
 
 
 
 
51363f0
99c6a62
 
501fd32
52e5575
 
ffc9fa5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aabed75
 
52e5575
ffc9fa5
d4ae81a
ffc9fa5
 
 
 
 
 
 
 
 
 
 
 
51363f0
ffc9fa5
 
 
 
 
 
 
52e5575
51363f0
ffc9fa5
aabed75
ffc9fa5
 
 
33ed0b3
aabed75
52e5575
 
 
 
 
 
 
 
 
33ed0b3
52e5575
 
33ed0b3
52e5575
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33ed0b3
52e5575
 
33ed0b3
501fd32
52e5575
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ffc9fa5
52e5575
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ffc9fa5
52e5575
ffc9fa5
 
 
 
 
 
 
659e451
52e5575
501fd32
 
 
52e5575
 
 
 
501fd32
33ed0b3
501fd32
52e5575
99c6a62
 
33ed0b3
52e5575
 
99c6a62
ffc9fa5
52e5575
 
 
ffc9fa5
 
 
52e5575
 
 
 
 
 
 
 
 
 
 
ffc9fa5
52e5575
ffc9fa5
 
 
 
 
 
 
52e5575
 
ffc9fa5
52e5575
ffc9fa5
 
 
 
 
 
 
 
 
 
52e5575
 
51363f0
52e5575
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501fd32
99c6a62
501fd32
52e5575
 
33ed0b3
52e5575
33ed0b3
 
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# app.py
#
# Copyright (C) August 4, 2025  Carlos Rodrigues dos Santos
#
# Versão 7.0.0 (Reactive UI & Planner-Composer Architecture)

import gradio as gr
import yaml
import logging
import os
import sys
import shutil

# --- 1. IMPORTAÇÃO DO FRAMEWORK E CONFIGURAÇÃO ---
import aduc_framework
from aduc_framework.types import PreProductionParams, ProductionParams

# Configuração de Tema Cinemático (cinematic_theme)
# (O código do tema permanece o mesmo, omitido para brevidade, mas deve estar aqui)
cinematic_theme = gr.themes.Base(
    primary_hue=gr.themes.colors.indigo,
    secondary_hue=gr.themes.colors.purple,
    neutral_hue=gr.themes.colors.slate,
    font=(gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"),
).set(
    body_background_fill="#111827", body_text_color="#E5E7EB",
    button_primary_background_fill="linear-gradient(90deg, #4F46E5, #8B5CF6)",
    button_primary_text_color="#FFFFFF", button_secondary_background_fill="#374151",
    button_secondary_border_color="#4B5563", button_secondary_text_color="#E5E7EB",
    block_background_fill="#1F2937", block_border_width="1px", block_border_color="#374151",
    block_label_background_fill="#374151", block_label_text_color="#E5E7EB",
    block_title_text_color="#FFFFFF", input_background_fill="#374151",
    input_border_color="#4B5563", input_placeholder_color="#9CA3AF",
)

# Configuração de Logging
# (O código de logging permanece o mesmo, omitido para brevidade, mas deve estar aqui)
LOG_FILE_PATH = "aduc_log.txt"
if os.path.exists(LOG_FILE_PATH): os.remove(LOG_FILE_PATH)
log_format = '%(asctime)s - %(levelname)s - [%(name)s:%(funcName)s] - %(message)s'
root_logger = logging.getLogger()
root_logger.setLevel(logging.INFO)
root_logger.handlers.clear()
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setFormatter(logging.Formatter(log_format))
root_logger.addHandler(stream_handler)
file_handler = logging.FileHandler(LOG_FILE_PATH, mode='w', encoding='utf-8')
file_handler.setFormatter(logging.Formatter(log_format))
root_logger.addHandler(file_handler)
logger = logging.getLogger(__name__)

# Inicialização do Aduc Framework
try:
    with open("config.yaml", 'r') as f: config = yaml.safe_load(f)
    WORKSPACE_DIR = config['application']['workspace_dir']
    aduc = aduc_framework.create_aduc_instance(workspace_dir=WORKSPACE_DIR)
    logger.info("Interface Gradio inicializada e conectada ao Aduc Framework.")
except Exception as e:
    logger.critical(f"ERRO CRÍTICO durante a inicialização: {e}", exc_info=True)
    # Bloco de erro do Gradio, se a inicialização falhar
    with gr.Blocks() as demo:
        gr.Markdown("# ERRO CRÍTICO NA INICIALIZAÇÃO")
        gr.Markdown("Não foi possível iniciar o Aduc Framework. Verifique os logs para mais detalhes.")
        gr.Textbox(value=str(e), label="Detalhes do Erro", lines=10)
    demo.launch()
    exit()

# --- 2. FUNÇÕES WRAPPER (UI <-> FRAMEWORK) ---

def run_pre_production_wrapper(prompt, num_scenes, ref_files, resolution_str, duration_per_fragment, progress=gr.Progress(track_tqdm=True)):
    """
    Wrapper reativo para a fase de pré-produção.
    Captura os eventos do Composer e atualiza a UI em tempo real.
    """
    if not ref_files:
        raise gr.Error("Por favor, forneça pelo menos uma imagem de referência.")

    target_resolution = int(resolution_str.split('x')[0])
    
    # Processa as imagens de referência
    ref_paths = [aduc.process_image_for_story(f.name, target_resolution, f"ref_processed_{i}.png") for i, f in enumerate(ref_files)]
    
    params = PreProductionParams(
        prompt=prompt,
        num_scenes=int(num_scenes),
        ref_paths=ref_paths,
        resolution=target_resolution,
        duration_per_fragment=duration_per_fragment
    )

    log_history = ""
    # Loop principal que consome o gerador do orquestrador
    for update in aduc.task_pre_production(params, progress_callback=progress):
        status = update.get("status")
        message = update.get("message", "")
        
        # Constrói o histórico de logs para a UI
        log_entry = f"[{status.upper()}] {message}\n"
        log_history += log_entry

        # Gera o dicionário de atualização para os componentes da UI
        yield {
            log_display_realtime: log_history,
            dna_display: update.get("dna_snapshot", gr.skip()),
        }

        if status == "error":
            raise gr.Error(f"Ocorreu um erro no backend: {message}")
        
        if status == "pre_production_complete":
            # Quando a pré-produção termina, habilita os próximos passos
            yield {
                log_display_realtime: log_history,
                dna_display: update.get("dna_snapshot"),
                keyframe_gallery: gr.update(value=update.get("keyframe_gallery", [])),
                step3_accordion: gr.update(visible=True, open=True)
            }
            # O gerador termina aqui para a pré-produção
            return


def run_production_wrapper(current_state_dict, trim_percent, handler_strength, dest_strength, guidance_scale, stg_scale, steps, progress=gr.Progress(track_tqdm=True)):
    """Wrapper para a geração do vídeo principal."""
    yield {final_video_output: gr.update(value=None, visible=True, label="🎬 Produzindo seu filme...")}
    
    # Esta função no orquestrador não é um gerador, então a chamamos diretamente.
    # Precisaremos do estado atual (DNA) que foi salvo no `generation_state_holder`.
    # AducDirector precisará de um método para carregar este estado.
    # Por agora, vamos simular passando o dicionário.
    # aduc.director.load_state_from_dict(current_state_dict) # Idealmente
    
    production_params = ProductionParams(
        trim_percent=int(trim_percent), handler_strength=handler_strength, 
        destination_convergence_strength=dest_strength, guidance_scale=guidance_scale, 
        stg_scale=stg_scale, inference_steps=int(steps)
    )
    
    # Assumindo que o `task_produce_original_movie` usa o estado já no `aduc.director`
    # (que foi atualizado pela pré-produção)
    final_video_path, latent_paths, updated_state = aduc.task_produce_original_movie(params=production_params, progress_callback=progress)
    
    yield {
        final_video_output: gr.update(value=final_video_path, label="✅ Filme Original Master"),
        step4_accordion: gr.update(visible=True, open=True),
        original_latents_paths_state: latent_paths,
        current_source_video_state: final_video_path,
        generation_state_holder: updated_state.model_dump(),
    }

# ... (Os wrappers de pós-produção permanecem os mesmos, pois já usam geradores) ...

def get_log_content():
    try:
        with open(LOG_FILE_PATH, "r", encoding="utf-8") as f:
            return f.read()
    except FileNotFoundError:
        return "Arquivo de log ainda não criado."

# --- 3. DEFINIÇÃO DA UI ---
with gr.Blocks(theme=cinematic_theme, css="style.css") as demo:
    # Componentes de Estado
    generation_state_holder = gr.State(value={})
    original_latents_paths_state = gr.State(value=[])
    current_source_video_state = gr.State(value=None)
    
    # Título
    gr.Markdown("<h1>ADUC-SDR 🎬 - O Diretor de Cinema IA</h1>")
    gr.Markdown("<p>Crie um filme completo com vídeo e áudio, orquestrado por uma equipe de IAs especialistas.</p>")

    # Etapa 1: Pré-Produção
    with gr.Accordion("Etapa 1: Planejamento (Pré-Produção)", open=True) as step1_accordion:
        prompt_input = gr.Textbox(label="Ideia Geral do Filme", value="Um leão majestoso caminha pela savana, senta-se e ruge para o sol poente.")
        with gr.Row():
            resolution_selector = gr.Radio(["512x512", "768x768", "1024x1024"], value="512x512", label="Resolução Base")
            num_scenes_slider = gr.Slider(minimum=2, maximum=10, value=3, step=1, label="Número de Cenas do Filme")
        ref_image_input = gr.File(label="Imagens de Referência (Material Bibliográfico)", file_count="multiple", file_types=["image"])
        duration_per_fragment_slider = gr.Slider(label="Duração Máxima de cada Ato (s)", minimum=2.0, maximum=10.0, value=4.0, step=0.1)
        start_planning_button = gr.Button("Iniciar Planejamento de Pré-Produção", variant="primary")

    # Accordion para mostrar o progresso do planejamento em tempo real
    with gr.Accordion("🧠 Diário do Planejador (Pré-Produção em Tempo Real)", open=False) as planning_log_accordion:
        log_display_realtime = gr.Textbox(label="Log da Conversa com IA", lines=15, interactive=False, autoscroll=True)
        dna_display = gr.JSON(label="DNA da Produção (em construção)")

    # Etapa 2: Produção
    with gr.Accordion("Etapa 2: Produção do Vídeo Original", open=False, visible=False) as step3_accordion:
        # ... (Sliders de produção permanecem os mesmos) ...
        trim_percent_slider = gr.Slider(minimum=10, maximum=90, value=50, step=5, label="Poda Causal (%)")
        handler_strength = gr.Slider(label="Força do Déjà-Vu", minimum=0.0, maximum=1.0, value=0.5, step=0.05)
        dest_strength = gr.Slider(label="Força da Âncora Final", minimum=0.0, maximum=1.0, value=0.75, step=0.05)
        guidance_scale_slider = gr.Slider(minimum=1.0, maximum=10.0, value=2.0, step=0.1, label="Escala de Orientação")
        stg_scale_slider = gr.Slider(minimum=0.0, maximum=1.0, value=0.025, step=0.005, label="Escala STG")
        inference_steps_slider = gr.Slider(minimum=10, maximum=50, value=20, step=1, label="Passos de Inferência")
        produce_original_button = gr.Button("🎬 Produzir Vídeo Original", variant="primary")

    # Etapa 3: Pós-Produção
    with gr.Accordion("Etapa 3: Pós-Produção (Opcional)", open=False, visible=False) as step4_accordion:
        # ... (Componentes de pós-produção permanecem os mesmos) ...
        gr.Markdown("Aplique melhorias ao filme. Cada etapa usa o resultado da anterior como fonte.")
        with gr.Accordion("A. Upscaler Latente 2x", open=True):
            upscaler_chunk_size_slider = gr.Slider(minimum=1, maximum=10, value=2, step=1, label="Fragmentos por Lote")
            run_upscaler_button = gr.Button("Executar Upscaler Latente", variant="secondary")
        with gr.Accordion("B. Masterização HD (SeedVR)", open=True):
            hd_steps_slider = gr.Slider(minimum=20, maximum=150, value=100, step=5, label="Passos de Inferência HD")
            run_hd_button = gr.Button("Executar Masterização HD", variant="secondary")
        with gr.Accordion("C. Geração de Áudio", open=True):
            audio_prompt_input = gr.Textbox(label="Prompt de Áudio Detalhado (Opcional)", lines=2, placeholder="Descreva os sons, efeitos e música.")
            run_audio_button = gr.Button("Gerar Áudio", variant="secondary")
            
    # Saídas Finais e Logs
    final_video_output = gr.Video(label="Filme Final (Resultado da Última Etapa)", visible=False, interactive=False)
    with gr.Accordion("🖼️ Galeria de Keyframes (Referência)", open=False) as keyframes_accordion:
        keyframe_gallery = gr.Gallery(label="Keyframes Gerados", visible=True, object_fit="contain", height="auto")
    with gr.Accordion("📝 Log de Sessão (Completo)", open=False) as log_accordion:
        log_display_full = gr.Textbox(label="Log da Sessão", lines=20, interactive=False, autoscroll=True)
        update_log_button = gr.Button("Atualizar Log Completo")

# --- 4. CONEXÕES DE EVENTOS ---

    # Conexão principal para a pré-produção reativa
    start_planning_button.click(
        fn=lambda: gr.update(open=True), outputs=[planning_log_accordion] # Abre o accordion de log
    ).then(
        fn=run_pre_production_wrapper, 
        inputs=[prompt_input, num_scenes_slider, ref_image_input, resolution_selector, duration_per_fragment_slider], 
        outputs=[log_display_realtime, dna_display, keyframe_gallery, step3_accordion]
    )

    # Conexões para as etapas seguintes
    produce_original_button.click(
        fn=run_production_wrapper, 
        inputs=[generation_state_holder, trim_percent_slider, handler_strength, dest_strength, guidance_scale_slider, stg_scale_slider, inference_steps_slider], 
        outputs=[final_video_output, step4_accordion, original_latents_paths_state, current_source_video_state, generation_state_holder]
    )
    
    run_upscaler_button.click(
        fn=aduc.task_run_latent_upscaler, # Chamando diretamente se já for um gerador
        inputs=[original_latents_paths_state, upscaler_chunk_size_slider], 
        outputs=[final_video_output] # Assumindo que o wrapper não é mais necessário
    ).then(
        fn=lambda video_path: video_path, inputs=[final_video_output], outputs=[current_source_video_state]
    )

    run_hd_button.click(
        fn=aduc.task_run_hd_mastering,
        inputs=[current_source_video_state, hd_steps_slider, prompt_input],
        outputs=[final_video_output]
    ).then(
        fn=lambda video_path: video_path, inputs=[final_video_output], outputs=[current_source_video_state]
    )

    run_audio_button.click(
        fn=aduc.task_run_audio_generation,
        inputs=[current_source_video_state, audio_prompt_input, prompt_input],
        outputs=[final_video_output]
    )

    # Conexão para atualizar o log completo
    update_log_button.click(fn=get_log_content, inputs=[], outputs=[log_display_full])
    
    # Atualiza o state holder com o DNA final quando a pré-produção termina
    dna_display.change(fn=lambda data: data, inputs=dna_display, outputs=generation_state_holder)


# --- 5. INICIALIZAÇÃO DA APLICAÇÃO ---
if __name__ == "__main__":
    if os.path.exists(WORKSPACE_DIR):
        shutil.rmtree(WORKSPACE_DIR)
    os.makedirs(WORKSPACE_DIR)
    
    logger.info("Aplicação Gradio iniciada. Lançando interface...")
    demo.queue().launch()