File size: 13,352 Bytes
501fd32
 
 
 
659e451
501fd32
659e451
33ed0b3
 
501fd32
 
 
 
 
 
 
 
 
 
659e451
99c6a62
 
501fd32
33ed0b3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501fd32
33ed0b3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
659e451
501fd32
33ed0b3
 
 
 
 
 
 
 
 
 
a871b6c
 
33ed0b3
 
 
 
 
 
 
 
501fd32
99c6a62
33ed0b3
 
99c6a62
659e451
 
 
 
 
33ed0b3
659e451
 
 
 
 
33ed0b3
659e451
33ed0b3
99c6a62
 
659e451
 
 
33ed0b3
659e451
501fd32
99c6a62
659e451
33ed0b3
659e451
33ed0b3
99c6a62
 
659e451
 
 
33ed0b3
659e451
501fd32
 
659e451
33ed0b3
659e451
501fd32
33ed0b3
99c6a62
 
659e451
33ed0b3
501fd32
 
 
33ed0b3
 
501fd32
33ed0b3
501fd32
33ed0b3
99c6a62
 
33ed0b3
501fd32
99c6a62
 
 
 
 
659e451
 
 
33ed0b3
501fd32
659e451
 
99c6a62
33ed0b3
659e451
33ed0b3
 
 
 
 
 
99c6a62
 
659e451
 
99c6a62
 
 
 
 
659e451
99c6a62
33ed0b3
99c6a62
 
33ed0b3
659e451
 
33ed0b3
 
659e451
99c6a62
659e451
99c6a62
 
 
 
 
501fd32
33ed0b3
 
659e451
33ed0b3
659e451
33ed0b3
 
 
659e451
 
33ed0b3
501fd32
99c6a62
501fd32
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
# app.py
#
# Copyright (C) August 4, 2025  Carlos Rodrigues dos Santos
#
# Versão 6.0.0 (Clean, Interactive & Consolidated UI)
#
# Esta versão refatora completamente a interface do usuário para uma experiência
# de coluna única, com um único player de vídeo, streaming de resultados e
# um visualizador de logs em tempo real, alinhado com a arquitetura do Diretor Autônomo.

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

# --- 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 = 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
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)
    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_keyframes, ref_files, resolution_str, duration_per_fragment, progress=gr.Progress()):
    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])
    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_keyframes=int(num_keyframes), ref_paths=ref_paths, resolution=target_resolution, duration_per_fragment=duration_per_fragment)
    
    final_result = {}
    for update in aduc.task_pre_production(params, progress):
        yield {
            generation_state_holder: update.get("updated_state", gr.skip()),
            storyboard_output: update.get("storyboard", gr.skip()),
            keyframe_gallery: gr.update(value=update.get("final_keyframes", [])),
        }
        final_result = update

    yield {
        generation_state_holder: final_result.get("updated_state"),
        step3_accordion: gr.update(visible=True, open=True)
    }

def run_original_production_wrapper(current_state_dict, trim_percent, handler_strength, dest_strength, guidance_scale, stg_scale, steps, progress=gr.Progress()):
    yield {final_video_output: gr.update(value=None, visible=True, label="🎬 Produzindo seu filme...")}
    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))
    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(),
    }

def run_upscaler_wrapper(source_video, latent_paths, chunk_size, progress=gr.Progress()):
    if not source_video or not latent_paths: raise gr.Error("Fonte de vídeo ou latentes originais não encontrados para o Upscaler.")
    yield {final_video_output: gr.update(label="Pós-Produção: Upscaler Latente...")}
    
    final_path = source_video
    for update in aduc.task_run_latent_upscaler(latent_paths, int(chunk_size), progress):
        if "final_path" in update: final_path = update['final_path']
        
    yield {
        final_video_output: gr.update(value=final_path, label="✅ Upscale Latente Concluído"),
        current_source_video_state: final_path
    }

def run_hd_wrapper(source_video, steps, global_prompt, progress=gr.Progress()):
    if not source_video: raise gr.Error("Fonte de vídeo não encontrada para a Masterização HD.")
    yield {final_video_output: gr.update(label="Pós-Produção: Masterização HD...")}

    final_path = source_video
    for update in aduc.task_run_hd_mastering(source_video, int(steps), global_prompt, progress):
        if "final_path" in update: final_path = update['final_path']
        
    yield {
        final_video_output: gr.update(value=final_path, label="✅ Masterização HD Concluída"),
        current_source_video_state: final_path
    }

def run_audio_wrapper(source_video, audio_prompt, global_prompt, progress=gr.Progress()):
    if not source_video: raise gr.Error("Fonte de vídeo não encontrada para a Geração de Áudio.")
    yield {final_video_output: gr.update(label="Pós-Produção: Geração de Áudio...")}
    
    final_audio_prompt = audio_prompt if audio_prompt and audio_prompt.strip() else global_prompt
    final_path = source_video
    for update in aduc.task_run_audio_generation(source_video, final_audio_prompt, progress):
        if "final_path" in update: final_path = update['final_path']
        
    yield {final_video_output: gr.update(value=final_path, label="✅ Filme Final com Áudio")}

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:
    # Estados para gerenciar os caminhos dos vídeos nos bastidores
    generation_state_holder = gr.State(value={})
    original_latents_paths_state = gr.State(value=[])
    current_source_video_state = gr.State(value=None)

    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>")

    with gr.Accordion("Etapa 1: Roteiro e Cenas-Chave (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():
            lang_selector = gr.Radio(["🇧🇷", "🇺🇸", "🇨🇳"], value="🇧🇷", label="Idioma / Language")
            resolution_selector = gr.Radio(["512x512", "768x768", "1024x1024"], value="512x512", label="Resolução Base")
        ref_image_input = gr.File(label="Grupo de Imagens do Usuário", file_count="multiple", file_types=["image"])
        with gr.Row():
            num_keyframes_slider = gr.Slider(minimum=2, maximum=42, value=4, step=2, label="Número de Cenas-Chave (Par)")
            duration_per_fragment_slider = gr.Slider(label="Duração de cada Clipe (s)", minimum=2.0, maximum=10.0, value=4.0, step=0.1)
        storyboard_and_keyframes_button = gr.Button("Gerar Roteiro e Keyframes", variant="primary")

    with gr.Accordion("Etapa 2: Produção do Vídeo Original", open=False, visible=False) as step3_accordion:
        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")
        
    with gr.Accordion("Etapa 3: Pós-Produção (Opcional)", open=False, visible=False) as step4_accordion:
        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")

    # --- RESULTADOS CONSOLIDADOS ---
    final_video_output = gr.Video(label="Filme Final (Resultado da Última Etapa)", visible=False, interactive=False)
    
    with gr.Accordion("Grupo das Keyframes", open=False) as keyframes_accordion:
        keyframe_gallery = gr.Gallery(label="Keyframes Gerados", visible=True, object_fit="contain", height="auto", type="filepath")
        
    with gr.Accordion("🧬 DNA Digital da Geração (JSON)", open=False) as data_accordion:
        storyboard_output = gr.JSON(label="Roteiro Gerado (Storyboard)")
        generation_data_output = gr.JSON(label="Estado de Geração Completo")

    with gr.Accordion("📝 Log de Geração (Detalhado)", open=False) as log_accordion:
        log_display = gr.Textbox(label="Log da Sessão", lines=20, interactive=False, autoscroll=True)
        update_log_button = gr.Button("Atualizar Log")

    # --- 4. CONEXÕES DE EVENTOS ---
    storyboard_and_keyframes_button.click(fn=run_pre_production_wrapper, inputs=[prompt_input, num_keyframes_slider, ref_image_input, resolution_selector, duration_per_fragment_slider], outputs=[generation_state_holder, storyboard_output, keyframe_gallery, step3_accordion])
    
    produce_original_button.click(fn=run_original_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=run_upscaler_wrapper, inputs=[current_source_video_state, original_latents_paths_state, upscaler_chunk_size_slider], outputs=[final_video_output, current_source_video_state])
    run_hd_button.click(fn=run_hd_wrapper, inputs=[current_source_video_state, hd_steps_slider, prompt_input], outputs=[final_video_output, current_source_video_state])
    run_audio_button.click(fn=run_audio_wrapper, inputs=[current_source_video_state, audio_prompt_input, prompt_input], outputs=[final_video_output])
    
    generation_state_holder.change(fn=lambda state: state, inputs=generation_state_holder, outputs=generation_data_output)
    update_log_button.click(fn=get_log_content, inputs=[], outputs=[log_display])

# --- 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()