Carlexxx
commited on
Commit
·
37d8158
1
Parent(s):
ffd8131
feat: Implement self-contained specialist managers
Browse files- aduc_framework/__init__.py +75 -0
- aduc_framework/director.py +116 -0
- aduc_framework/orchestrator.py +162 -0
- aduc_framework/types.py +91 -0
- aduc_orchestrator.py +0 -199
- aduc_orchestrator7d.py +0 -178
- aduc_types.py +0 -43
- app.py +152 -205
- app_api.py +127 -0
- app_gradio.py +216 -0
- run.py +20 -0
aduc_framework/__init__.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/__init__.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 3.0.0 (Framework Entry Point)
|
| 6 |
+
#
|
| 7 |
+
# Este arquivo serve como o ponto de entrada principal para o Aduc Framework.
|
| 8 |
+
# Ele define a interface pública que os clientes (UIs, APIs, etc.) usarão
|
| 9 |
+
# para criar e interagir com o sistema de orquestração.
|
| 10 |
+
#
|
| 11 |
+
# A principal responsabilidade deste arquivo é expor uma função de fábrica
|
| 12 |
+
# ('create_aduc_instance') que encapsula a lógica de inicialização do
|
| 13 |
+
# orquestrador e seus componentes, garantindo que o framework seja fácil
|
| 14 |
+
# de consumir.
|
| 15 |
+
|
| 16 |
+
import logging
|
| 17 |
+
|
| 18 |
+
# Importa as classes e tipos que formarão a interface pública do framework
|
| 19 |
+
from .orchestrator import AducOrchestrator
|
| 20 |
+
from .types import (
|
| 21 |
+
GenerationState,
|
| 22 |
+
PreProductionParams,
|
| 23 |
+
ProductionParams,
|
| 24 |
+
GenerationParameters,
|
| 25 |
+
MediaRef,
|
| 26 |
+
Ato,
|
| 27 |
+
KeyframeData,
|
| 28 |
+
VideoData
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
# Configura um logger para o framework para que os clientes possam ver as mensagens de inicialização.
|
| 32 |
+
logger = logging.getLogger(__name__)
|
| 33 |
+
|
| 34 |
+
def create_aduc_instance(workspace_dir: str) -> AducOrchestrator:
|
| 35 |
+
"""
|
| 36 |
+
Ponto de entrada de fábrica para criar uma instância totalmente funcional do Aduc Framework.
|
| 37 |
+
|
| 38 |
+
Esta função abstrai a complexidade da inicialização do AducOrchestrator e de todos
|
| 39 |
+
os seus engenheiros e managers dependentes. Clientes do framework devem usar esta
|
| 40 |
+
função para garantir uma inicialização correta e consistente.
|
| 41 |
+
|
| 42 |
+
Args:
|
| 43 |
+
workspace_dir (str): O caminho para o diretório onde todos os artefatos
|
| 44 |
+
(imagens, vídeos, latentes, logs) serão salvos.
|
| 45 |
+
|
| 46 |
+
Returns:
|
| 47 |
+
AducOrchestrator: Uma instância pronta para uso do orquestrador principal.
|
| 48 |
+
"""
|
| 49 |
+
logger.info(f"Fábrica ADUC: Criando uma nova instância com workspace em '{workspace_dir}'...")
|
| 50 |
+
|
| 51 |
+
# Futuramente, lógicas mais complexas de inicialização, como a verificação de
|
| 52 |
+
# dependências ou configuração de hardware, podem ser adicionadas aqui.
|
| 53 |
+
|
| 54 |
+
instance = AducOrchestrator(workspace_dir=workspace_dir)
|
| 55 |
+
|
| 56 |
+
logger.info("Fábrica ADUC: Instância do framework criada e pronta para uso.")
|
| 57 |
+
|
| 58 |
+
return instance
|
| 59 |
+
|
| 60 |
+
# Mensagem de log para confirmar que o pacote do framework foi importado com sucesso.
|
| 61 |
+
logger.info("Módulo 'aduc_framework' carregado. Use a função 'create_aduc_instance()' para começar.")
|
| 62 |
+
|
| 63 |
+
# Opcional: Definir __all__ para controlar o que é importado com 'from aduc_framework import *'
|
| 64 |
+
__all__ = [
|
| 65 |
+
"create_aduc_instance",
|
| 66 |
+
"AducOrchestrator",
|
| 67 |
+
"GenerationState",
|
| 68 |
+
"PreProductionParams",
|
| 69 |
+
"ProductionParams",
|
| 70 |
+
"GenerationParameters",
|
| 71 |
+
"MediaRef",
|
| 72 |
+
"Ato",
|
| 73 |
+
"KeyframeData",
|
| 74 |
+
"VideoData"
|
| 75 |
+
]
|
aduc_framework/director.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/director.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 3.0.0 (Framework State Manager)
|
| 6 |
+
#
|
| 7 |
+
# Este arquivo contém a classe AducDirector. Sua única responsabilidade
|
| 8 |
+
# é gerenciar o objeto de estado da geração (GenerationState). Ele atua
|
| 9 |
+
# como o "score" da orquestra ou o "script" do filme, mantendo um registro
|
| 10 |
+
# preciso de todos os parâmetros e artefatos gerados.
|
| 11 |
+
|
| 12 |
+
import logging
|
| 13 |
+
import os
|
| 14 |
+
from typing import List, Dict, Any
|
| 15 |
+
|
| 16 |
+
# Importa os modelos de dados Pydantic que ele irá gerenciar
|
| 17 |
+
from .types import GenerationState, PreProductionParams, ProductionParams, Ato, MediaRef, KeyframeData, VideoData
|
| 18 |
+
|
| 19 |
+
logger = logging.getLogger(__name__)
|
| 20 |
+
|
| 21 |
+
class AducDirector:
|
| 22 |
+
"""
|
| 23 |
+
Representa o Diretor de Cena, responsável por gerenciar o estado da produção.
|
| 24 |
+
Atua como a fonte única da verdade para todos os dados relacionados a uma
|
| 25 |
+
única tarefa de geração de vídeo.
|
| 26 |
+
"""
|
| 27 |
+
def __init__(self, workspace_dir: str):
|
| 28 |
+
"""
|
| 29 |
+
Inicializa o Diretor.
|
| 30 |
+
|
| 31 |
+
Args:
|
| 32 |
+
workspace_dir (str): O diretório onde os artefatos são salvos.
|
| 33 |
+
O Diretor usa isso para referenciar caminhos se necessário.
|
| 34 |
+
"""
|
| 35 |
+
self.workspace_dir = workspace_dir
|
| 36 |
+
self.state: GenerationState = self._initialize_state()
|
| 37 |
+
os.makedirs(self.workspace_dir, exist_ok=True)
|
| 38 |
+
logger.info(f"AducDirector inicializado. O estado de geração foi criado.")
|
| 39 |
+
|
| 40 |
+
def _initialize_state(self) -> GenerationState:
|
| 41 |
+
"""
|
| 42 |
+
Cria uma instância vazia e válida do modelo GenerationState.
|
| 43 |
+
"""
|
| 44 |
+
return GenerationState()
|
| 45 |
+
|
| 46 |
+
def get_full_state(self) -> GenerationState:
|
| 47 |
+
"""
|
| 48 |
+
Retorna o objeto de estado Pydantic completo.
|
| 49 |
+
|
| 50 |
+
Returns:
|
| 51 |
+
GenerationState: O estado atual da geração.
|
| 52 |
+
"""
|
| 53 |
+
return self.state
|
| 54 |
+
|
| 55 |
+
def get_full_state_as_dict(self) -> Dict[str, Any]:
|
| 56 |
+
"""
|
| 57 |
+
Retorna o estado completo serializado como um dicionário Python.
|
| 58 |
+
Útil para passar para bibliotecas que não suportam Pydantic diretamente.
|
| 59 |
+
|
| 60 |
+
Returns:
|
| 61 |
+
Dict[str, Any]: O estado atual como um dicionário.
|
| 62 |
+
"""
|
| 63 |
+
return self.state.model_dump()
|
| 64 |
+
|
| 65 |
+
def update_parameters(self, stage: str, params: Any):
|
| 66 |
+
"""
|
| 67 |
+
Atualiza o nó de parâmetros no estado de geração.
|
| 68 |
+
|
| 69 |
+
Args:
|
| 70 |
+
stage (str): O estágio da produção ('pre_producao', 'producao', etc.).
|
| 71 |
+
params (BaseModel): O objeto Pydantic contendo os parâmetros para aquele estágio.
|
| 72 |
+
"""
|
| 73 |
+
if hasattr(self.state.parametros_geracao, stage):
|
| 74 |
+
setattr(self.state.parametros_geracao, stage, params)
|
| 75 |
+
logger.info(f"Parâmetros do estágio '{stage}' atualizados no estado.")
|
| 76 |
+
else:
|
| 77 |
+
logger.warning(f"Tentativa de atualizar parâmetros para um estágio desconhecido: '{stage}'")
|
| 78 |
+
|
| 79 |
+
def update_pre_production_state(self, prompt: str, ref_paths: List[str], storyboard: List[str]):
|
| 80 |
+
"""
|
| 81 |
+
Popula as seções iniciais do estado após a geração do storyboard.
|
| 82 |
+
|
| 83 |
+
Args:
|
| 84 |
+
prompt (str): O prompt geral.
|
| 85 |
+
ref_paths (List[str]): Lista de caminhos para as mídias de referência.
|
| 86 |
+
storyboard (List[str]): Lista de resumos dos atos.
|
| 87 |
+
"""
|
| 88 |
+
self.state.Promt_geral = prompt
|
| 89 |
+
self.state.midias_referencia = [MediaRef(id=i, caminho=path) for i, path in enumerate(ref_paths)]
|
| 90 |
+
self.state.Atos = [Ato(id=i, resumo_ato=ato) for i, ato in enumerate(storyboard)]
|
| 91 |
+
logger.info("Estado de pré-produção (prompt, referências, atos) atualizado.")
|
| 92 |
+
|
| 93 |
+
def update_keyframes_state(self, keyframes_data: List[Dict[str, Any]]):
|
| 94 |
+
"""
|
| 95 |
+
Atualiza a lista de keyframes no estado.
|
| 96 |
+
|
| 97 |
+
Args:
|
| 98 |
+
keyframes_data (List[Dict[str, Any]]): Uma lista de dicionários, cada um
|
| 99 |
+
representando os dados de um keyframe.
|
| 100 |
+
"""
|
| 101 |
+
# Converte os dicionários em modelos Pydantic KeyframeData
|
| 102 |
+
self.state.Keyframe_atos = [KeyframeData(**data) for data in keyframes_data]
|
| 103 |
+
logger.info(f"{len(keyframes_data)} keyframes adicionados ao estado.")
|
| 104 |
+
|
| 105 |
+
def update_video_state(self, video_data_dict: Dict[str, Any]):
|
| 106 |
+
"""
|
| 107 |
+
Atualiza a lista de vídeos gerados no estado.
|
| 108 |
+
|
| 109 |
+
Args:
|
| 110 |
+
video_data_dict (Dict[str, Any]): Um dicionário representando os dados do vídeo gerado.
|
| 111 |
+
"""
|
| 112 |
+
# Converte o dicionário em um modelo Pydantic VideoData
|
| 113 |
+
video_model = VideoData(**video_data_dict)
|
| 114 |
+
# Atualmente, substituímos a lista, mas poderíamos adicionar a ela no futuro.
|
| 115 |
+
self.state.videos_atos = [video_model]
|
| 116 |
+
logger.info("Dados da produção de vídeo atualizados no estado.")
|
aduc_framework/orchestrator.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/orchestrator.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 3.0.0 (Framework Core)
|
| 6 |
+
#
|
| 7 |
+
# Esta versão representa a camada de orquestração do Aduc Framework.
|
| 8 |
+
# Ela é agnóstica a qualquer interface (UI ou API) e opera com
|
| 9 |
+
# tipos de dados bem definidos (Pydantic) e um estado de geração central.
|
| 10 |
+
|
| 11 |
+
import logging
|
| 12 |
+
from typing import List, Dict, Any, Tuple, Callable, Optional
|
| 13 |
+
|
| 14 |
+
from PIL import Image, ImageOps
|
| 15 |
+
import os
|
| 16 |
+
|
| 17 |
+
# Importa componentes internos do framework
|
| 18 |
+
from .director import AducDirector
|
| 19 |
+
from .types import GenerationState, PreProductionParams, ProductionParams
|
| 20 |
+
from .engineers import deformes2d_thinker_singleton, deformes3d_engine_singleton, Deformes4DEngine
|
| 21 |
+
|
| 22 |
+
logger = logging.getLogger(__name__)
|
| 23 |
+
|
| 24 |
+
# Define um tipo para o callback de progresso para clareza
|
| 25 |
+
ProgressCallback = Optional[Callable[[float, str], None]]
|
| 26 |
+
|
| 27 |
+
class AducOrchestrator:
|
| 28 |
+
"""
|
| 29 |
+
Implementa o Maestro (Γ), a camada de orquestração central do Aduc Framework.
|
| 30 |
+
Ele recebe solicitações, atualiza o estado de geração, delega tarefas para os
|
| 31 |
+
engenheiros especialistas e retorna o estado atualizado.
|
| 32 |
+
"""
|
| 33 |
+
def __init__(self, workspace_dir: str):
|
| 34 |
+
"""
|
| 35 |
+
Inicializa o Maestro e seus componentes principais.
|
| 36 |
+
|
| 37 |
+
Args:
|
| 38 |
+
workspace_dir (str): O diretório raiz para salvar todos os artefatos gerados.
|
| 39 |
+
"""
|
| 40 |
+
self.director = AducDirector(workspace_dir)
|
| 41 |
+
# O Deformes4D é instanciado aqui pois não é um singleton complexo
|
| 42 |
+
self.editor = Deformes4DEngine(workspace_dir)
|
| 43 |
+
self.painter = deformes3d_engine_singleton
|
| 44 |
+
logger.info("ADUC Maestro (Framework Core) está no pódio. Engenheiros especialistas prontos.")
|
| 45 |
+
|
| 46 |
+
def get_current_state(self) -> GenerationState:
|
| 47 |
+
"""
|
| 48 |
+
Retorna o objeto de estado Pydantic completo e atual.
|
| 49 |
+
|
| 50 |
+
Returns:
|
| 51 |
+
GenerationState: O modelo Pydantic representando o estado completo.
|
| 52 |
+
"""
|
| 53 |
+
return self.director.get_full_state()
|
| 54 |
+
|
| 55 |
+
def process_image_for_story(self, image_path: str, size: int, filename: str) -> str:
|
| 56 |
+
"""
|
| 57 |
+
Pré-processa uma imagem de referência, padronizando-a para uso pelos Especialistas.
|
| 58 |
+
Este é um método utilitário exposto pelo framework.
|
| 59 |
+
"""
|
| 60 |
+
img = Image.open(image_path).convert("RGB")
|
| 61 |
+
img_square = ImageOps.fit(img, (size, size), Image.Resampling.LANCZOS)
|
| 62 |
+
processed_path = os.path.join(self.director.workspace_dir, filename)
|
| 63 |
+
img_square.save(processed_path)
|
| 64 |
+
logger.info(f"Imagem de referência processada e salva em: {processed_path}")
|
| 65 |
+
return processed_path
|
| 66 |
+
|
| 67 |
+
# --- TAREFAS PRINCIPAIS DO FRAMEWORK ---
|
| 68 |
+
|
| 69 |
+
def task_pre_production(self, params: PreProductionParams, progress_callback: ProgressCallback = None) -> Tuple[List[str], List[str], GenerationState]:
|
| 70 |
+
"""
|
| 71 |
+
Executa o fluxo completo de pré-produção: storyboard e geração de keyframes.
|
| 72 |
+
|
| 73 |
+
Args:
|
| 74 |
+
params (PreProductionParams): Objeto Pydantic com todos os parâmetros da UI/API.
|
| 75 |
+
progress_callback (callable, optional): Uma função que aceita (fração, descrição) para reportar progresso.
|
| 76 |
+
|
| 77 |
+
Returns:
|
| 78 |
+
Tuple[List[str], List[str], GenerationState]: Uma tupla contendo a lista de atos do storyboard,
|
| 79 |
+
a lista de caminhos dos keyframes gerados, e o estado
|
| 80 |
+
de geração completo e atualizado.
|
| 81 |
+
"""
|
| 82 |
+
logger.info("Maestro: Iniciando tarefa de Pré-Produção.")
|
| 83 |
+
|
| 84 |
+
# 1. Atualiza o estado com os parâmetros recebidos
|
| 85 |
+
self.director.update_parameters("pre_producao", params)
|
| 86 |
+
|
| 87 |
+
# 2. Gera o storyboard
|
| 88 |
+
if progress_callback: progress_callback(0.1, "Gerando storyboard...")
|
| 89 |
+
|
| 90 |
+
storyboard_list = deformes2d_thinker_singleton.generate_storyboard(
|
| 91 |
+
prompt=params.prompt,
|
| 92 |
+
num_keyframes=params.num_keyframes,
|
| 93 |
+
ref_image_paths=params.ref_paths
|
| 94 |
+
)
|
| 95 |
+
self.director.update_pre_production_state(params.prompt, params.ref_paths, storyboard_list)
|
| 96 |
+
|
| 97 |
+
# 3. Gera os keyframes
|
| 98 |
+
if progress_callback: progress_callback(0.2, "Iniciando geração de keyframes...")
|
| 99 |
+
|
| 100 |
+
# O engenheiro agora recebe o estado completo e o callback genérico
|
| 101 |
+
keyframes_detailed_data = self.painter.generate_keyframes_from_storyboard(
|
| 102 |
+
generation_state=self.director.get_full_state_as_dict(),
|
| 103 |
+
progress_callback=progress_callback
|
| 104 |
+
)
|
| 105 |
+
self.director.update_keyframes_state(keyframes_detailed_data)
|
| 106 |
+
|
| 107 |
+
# 4. Prepara o retorno
|
| 108 |
+
final_keyframe_paths = [kf["caminho_pixel"] for kf in keyframes_detailed_data]
|
| 109 |
+
final_state = self.director.get_full_state()
|
| 110 |
+
|
| 111 |
+
logger.info("Maestro: Tarefa de Pré-Produção concluída.")
|
| 112 |
+
return storyboard_list, final_keyframe_paths, final_state
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def task_produce_original_movie(self, params: ProductionParams, progress_callback: ProgressCallback = None) -> Tuple[str, List[str], GenerationState]:
|
| 116 |
+
"""
|
| 117 |
+
Executa o fluxo de produção do vídeo principal.
|
| 118 |
+
|
| 119 |
+
Args:
|
| 120 |
+
params (ProductionParams): Objeto Pydantic com todos os parâmetros de produção.
|
| 121 |
+
progress_callback (callable, optional): Função para reportar progresso.
|
| 122 |
+
|
| 123 |
+
Returns:
|
| 124 |
+
Tuple[str, List[str], GenerationState]: Uma tupla contendo o caminho do vídeo final,
|
| 125 |
+
a lista de caminhos dos latentes dos fragmentos,
|
| 126 |
+
e o estado de geração completo e atualizado.
|
| 127 |
+
"""
|
| 128 |
+
logger.info("Maestro: Iniciando tarefa de Produção do Filme Original.")
|
| 129 |
+
|
| 130 |
+
# 1. Atualiza o estado com os novos parâmetros
|
| 131 |
+
self.director.update_parameters("producao", params)
|
| 132 |
+
|
| 133 |
+
# 2. Delega a produção para o Deformes4DEngine
|
| 134 |
+
# O editor agora recebe o estado completo, que já contém os parâmetros que acabamos de salvar.
|
| 135 |
+
result_data = self.editor.generate_original_movie(
|
| 136 |
+
full_generation_state=self.director.get_full_state_as_dict(),
|
| 137 |
+
progress_callback=progress_callback
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
# 3. Atualiza o estado com os resultados da produção
|
| 141 |
+
self.director.update_video_state(result_data["video_data"])
|
| 142 |
+
|
| 143 |
+
# 4. Prepara o retorno
|
| 144 |
+
final_video_path = result_data["final_path"]
|
| 145 |
+
latent_paths = result_data["latent_paths"]
|
| 146 |
+
final_state = self.director.get_full_state()
|
| 147 |
+
|
| 148 |
+
logger.info("Maestro: Tarefa de Produção do Filme Original concluída.")
|
| 149 |
+
return final_video_path, latent_paths, final_state
|
| 150 |
+
|
| 151 |
+
# --- TAREFAS DE PÓS-PRODUÇÃO (Exemplos) ---
|
| 152 |
+
# (A serem refatoradas no futuro para seguir o mesmo padrão)
|
| 153 |
+
|
| 154 |
+
def task_run_hd_mastering(self, source_video_path: str, hd_params: Dict[str, Any], progress_callback: ProgressCallback = None) -> str:
|
| 155 |
+
# Placeholder para futura refatoração
|
| 156 |
+
logger.info(f"Maestro: Delegando tarefa de masterização HD.")
|
| 157 |
+
return ""
|
| 158 |
+
|
| 159 |
+
def task_run_audio_generation(self, source_video_path: str, audio_params: Dict[str, Any], progress_callback: ProgressCallback = None) -> str:
|
| 160 |
+
# Placeholder para futura refatoração
|
| 161 |
+
logger.info(f"Maestro: Delegando tarefa de geração de áudio.")
|
| 162 |
+
return ""
|
aduc_framework/types.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aduc_framework/types.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 3.0.0 (Framework Data Models)
|
| 6 |
+
#
|
| 7 |
+
# Este arquivo define as estruturas de dados centrais para o Aduc Framework
|
| 8 |
+
# usando Pydantic. Estes modelos servem como o "contrato" de dados entre as
|
| 9 |
+
# diferentes camadas da aplicação (UI, API, Orchestrator, Engineers).
|
| 10 |
+
#
|
| 11 |
+
# O uso de Pydantic garante validação automática de tipos, serialização/desserialização
|
| 12 |
+
# fácil para JSON e uma fonte única da verdade para a estrutura de dados.
|
| 13 |
+
|
| 14 |
+
from pydantic import BaseModel, Field
|
| 15 |
+
from typing import List, Dict, Any, Optional
|
| 16 |
+
|
| 17 |
+
# --- Modelos de Parâmetros de Entrada ---
|
| 18 |
+
# Estes modelos representam os dados que o usuário fornece através de uma interface.
|
| 19 |
+
|
| 20 |
+
class PreProductionParams(BaseModel):
|
| 21 |
+
"""Parâmetros para a etapa de Roteiro e Keyframes."""
|
| 22 |
+
prompt: str = Field(..., description="A ideia geral do filme ou cena.")
|
| 23 |
+
num_keyframes: int = Field(..., gt=0, description="O número de keyframes a serem gerados.")
|
| 24 |
+
ref_paths: List[str] = Field(..., description="Lista de caminhos para as imagens de referência iniciais.")
|
| 25 |
+
resolution: int = Field(..., description="A resolução base (largura/altura) para a geração.")
|
| 26 |
+
duration_per_fragment: float = Field(..., gt=0, description="A duração alvo em segundos para cada fragmento de vídeo.")
|
| 27 |
+
|
| 28 |
+
class ProductionParams(BaseModel):
|
| 29 |
+
"""Parâmetros para a etapa de Geração de Vídeo."""
|
| 30 |
+
trim_percent: int = Field(..., ge=0, le=100, description="Poda causal para o mecanismo Déjà-Vu.")
|
| 31 |
+
handler_strength: float = Field(..., ge=0.0, le=1.0, description="Força do guia de trajetória (Déjà-Vu).")
|
| 32 |
+
destination_convergence_strength: float = Field(..., ge=0.0, le=1.0, description="Força da âncora final (destino).")
|
| 33 |
+
guidance_scale: float = Field(..., ge=0.0, description="Escala de orientação do prompt de movimento.")
|
| 34 |
+
stg_scale: float = Field(..., ge=0.0, description="Escala de continuidade temporal (STG).")
|
| 35 |
+
inference_steps: int = Field(..., gt=0, description="Número de passos de inferência para a geração de vídeo.")
|
| 36 |
+
|
| 37 |
+
class GenerationParameters(BaseModel):
|
| 38 |
+
"""Agrega todos os parâmetros de configuração da geração."""
|
| 39 |
+
pre_producao: Optional[PreProductionParams] = None
|
| 40 |
+
producao: Optional[ProductionParams] = None
|
| 41 |
+
pos_producao: Optional[Dict[str, Any]] = None
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
# --- Modelos de Artefatos Gerados ---
|
| 45 |
+
# Estes modelos representam os dados e metadados dos resultados criados pelo framework.
|
| 46 |
+
|
| 47 |
+
class MediaRef(BaseModel):
|
| 48 |
+
"""Representa uma mídia de referência fornecida pelo usuário."""
|
| 49 |
+
id: int
|
| 50 |
+
caminho: str
|
| 51 |
+
|
| 52 |
+
class Ato(BaseModel):
|
| 53 |
+
"""Representa uma unidade narrativa (sub-tarefa) do storyboard."""
|
| 54 |
+
id: int
|
| 55 |
+
resumo_ato: str
|
| 56 |
+
|
| 57 |
+
class KeyframeData(BaseModel):
|
| 58 |
+
"""Estrutura de dados completa para um único keyframe gerado."""
|
| 59 |
+
id: int
|
| 60 |
+
caminho_pixel: str
|
| 61 |
+
caminho_latent: str
|
| 62 |
+
prompt_keyframe: str
|
| 63 |
+
# Futuramente: midias_contexto: List[Dict[str, Any]]
|
| 64 |
+
|
| 65 |
+
class VideoFragmentData(BaseModel):
|
| 66 |
+
"""Metadados sobre a geração de um único fragmento de vídeo entre dois keyframes."""
|
| 67 |
+
id: int
|
| 68 |
+
prompt_video: str
|
| 69 |
+
# Futuramente: midias_inicio, midias_caminho, midias_fim
|
| 70 |
+
|
| 71 |
+
class VideoData(BaseModel):
|
| 72 |
+
"""Estrutura de dados completa para o vídeo final (ou um grande clipe)."""
|
| 73 |
+
id: int
|
| 74 |
+
caminho_pixel: str
|
| 75 |
+
caminhos_latentes_fragmentos: List[str]
|
| 76 |
+
fragmentos_componentes: List[VideoFragmentData]
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
# --- O Modelo de Estado Principal ---
|
| 80 |
+
|
| 81 |
+
class GenerationState(BaseModel):
|
| 82 |
+
"""
|
| 83 |
+
O "DNA Digital" completo de uma geração.
|
| 84 |
+
Este é o objeto de estado central que flui através do framework.
|
| 85 |
+
"""
|
| 86 |
+
parametros_geracao: GenerationParameters = Field(default_factory=GenerationParameters)
|
| 87 |
+
Promt_geral: str = ""
|
| 88 |
+
midias_referencia: List[MediaRef] = Field(default_factory=list)
|
| 89 |
+
Atos: List[Ato] = Field(default_factory=list)
|
| 90 |
+
Keyframe_atos: List[KeyframeData] = Field(default_factory=list)
|
| 91 |
+
videos_atos: List[VideoData] = Field(default_factory=list)
|
aduc_orchestrator.py
DELETED
|
@@ -1,199 +0,0 @@
|
|
| 1 |
-
# aduc_orchestrator.py
|
| 2 |
-
#
|
| 3 |
-
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
-
#
|
| 5 |
-
# Version: 2.2.0
|
| 6 |
-
#
|
| 7 |
-
# This file contains the core ADUC (Automated Discovery and Orchestration of Complex tasks)
|
| 8 |
-
# orchestrator, known as the "Maestro" (Γ). Its responsibility is to manage the high-level
|
| 9 |
-
# creative workflow of film production. This version is updated to reflect the final
|
| 10 |
-
# refactored project structure with `engineers` and `managers`.
|
| 11 |
-
|
| 12 |
-
import os
|
| 13 |
-
import logging
|
| 14 |
-
from typing import List, Dict, Any, Generator, Tuple
|
| 15 |
-
|
| 16 |
-
import gradio as gr
|
| 17 |
-
from PIL import Image, ImageOps
|
| 18 |
-
|
| 19 |
-
from engineers.deformes4D import Deformes4DEngine
|
| 20 |
-
from engineers.deformes2D_thinker import deformes2d_thinker_singleton
|
| 21 |
-
from engineers.deformes3D import deformes3d_engine_singleton
|
| 22 |
-
|
| 23 |
-
# The logger is configured in app.py; here we just get the instance.
|
| 24 |
-
logger = logging.getLogger(__name__)
|
| 25 |
-
|
| 26 |
-
class AducDirector:
|
| 27 |
-
"""
|
| 28 |
-
Represents the Scene Director, responsible for managing the production state.
|
| 29 |
-
Acts as the "score" for the orchestra, keeping track of all generated artifacts
|
| 30 |
-
(script, keyframes, etc.) during the creative process.
|
| 31 |
-
"""
|
| 32 |
-
def __init__(self, workspace_dir: str):
|
| 33 |
-
self.workspace_dir = workspace_dir
|
| 34 |
-
os.makedirs(self.workspace_dir, exist_ok=True)
|
| 35 |
-
self.state: Dict[str, Any] = {}
|
| 36 |
-
logger.info(f"The stage is set. Workspace at '{self.workspace_dir}'.")
|
| 37 |
-
|
| 38 |
-
def update_state(self, key: str, value: Any) -> None:
|
| 39 |
-
logger.info(f"Notating on the score: State '{key}' updated.")
|
| 40 |
-
self.state[key] = value
|
| 41 |
-
|
| 42 |
-
def get_state(self, key: str, default: Any = None) -> Any:
|
| 43 |
-
return self.state.get(key, default)
|
| 44 |
-
|
| 45 |
-
class AducOrchestrator:
|
| 46 |
-
"""
|
| 47 |
-
Implements the Maestro (Γ), the central orchestration layer of the ADUC architecture.
|
| 48 |
-
It does not execute AI tasks directly but delegates each step of the creative
|
| 49 |
-
process (scriptwriting, art direction, cinematography) to the appropriate Specialists.
|
| 50 |
-
"""
|
| 51 |
-
def __init__(self, workspace_dir: str):
|
| 52 |
-
self.director = AducDirector(workspace_dir)
|
| 53 |
-
self.editor = Deformes4DEngine(workspace_dir)
|
| 54 |
-
self.painter = deformes3d_engine_singleton
|
| 55 |
-
logger.info("ADUC Maestro is on the podium. Musicians (specialists) are ready.")
|
| 56 |
-
|
| 57 |
-
def process_image_for_story(self, image_path: str, size: int, filename: str) -> str:
|
| 58 |
-
"""
|
| 59 |
-
Pre-processes a reference image, standardizing it for use by the Specialists.
|
| 60 |
-
"""
|
| 61 |
-
img = Image.open(image_path).convert("RGB")
|
| 62 |
-
img_square = ImageOps.fit(img, (size, size), Image.Resampling.LANCZOS)
|
| 63 |
-
processed_path = os.path.join(self.director.workspace_dir, filename)
|
| 64 |
-
img_square.save(processed_path)
|
| 65 |
-
logger.info(f"Reference image processed and saved to: {processed_path}")
|
| 66 |
-
return processed_path
|
| 67 |
-
|
| 68 |
-
# --- PRE-PRODUCTION TASKS ---
|
| 69 |
-
|
| 70 |
-
def task_generate_storyboard(self, prompt: str, num_keyframes: int, ref_image_paths: List[str],
|
| 71 |
-
progress: gr.Progress) -> Tuple[List[str], str, Any]:
|
| 72 |
-
"""
|
| 73 |
-
Delegates the task of creating the storyboard to the Scriptwriter (deformes2D_thinker).
|
| 74 |
-
"""
|
| 75 |
-
logger.info(f"Act 1, Scene 1: Script. Instructing Scriptwriter to create {num_keyframes} scenes.")
|
| 76 |
-
progress(0.2, desc="Consulting AI Scriptwriter...")
|
| 77 |
-
|
| 78 |
-
storyboard = deformes2d_thinker_singleton.generate_storyboard(prompt, num_keyframes, ref_image_paths)
|
| 79 |
-
|
| 80 |
-
logger.info(f"Scriptwriter returned the score: {storyboard}")
|
| 81 |
-
self.director.update_state("storyboard", storyboard)
|
| 82 |
-
self.director.update_state("processed_ref_paths", ref_image_paths)
|
| 83 |
-
return storyboard, ref_image_paths[0], gr.update(visible=True, open=True)
|
| 84 |
-
|
| 85 |
-
def task_select_keyframes(self, storyboard: List[str], base_ref_paths: List[str],
|
| 86 |
-
pool_ref_paths: List[str]) -> List[str]:
|
| 87 |
-
"""
|
| 88 |
-
Delegates to the Photographer (deformes2D_thinker) the task of selecting keyframes.
|
| 89 |
-
"""
|
| 90 |
-
logger.info(f"Act 1, Scene 2 (Photographer Mode): Instructing Photographer to select {len(storyboard)} keyframes.")
|
| 91 |
-
selected_paths = deformes2d_thinker_singleton.select_keyframes_from_pool(storyboard, base_ref_paths, pool_ref_paths)
|
| 92 |
-
logger.info(f"Photographer selected the following scenes: {[os.path.basename(p) for p in selected_paths]}")
|
| 93 |
-
self.director.update_state("keyframes", selected_paths)
|
| 94 |
-
return selected_paths
|
| 95 |
-
|
| 96 |
-
def task_generate_keyframes(self, storyboard: List[str], initial_ref_path: str, global_prompt: str,
|
| 97 |
-
keyframe_resolution: int, progress_callback_factory=None) -> List[str]:
|
| 98 |
-
"""
|
| 99 |
-
Delegates to the Art Director (Deformes3DEngine) the task of generating keyframes.
|
| 100 |
-
"""
|
| 101 |
-
logger.info("Act 1, Scene 2 (Art Director Mode): Delegating to Art Director.")
|
| 102 |
-
general_ref_paths = self.director.get_state("processed_ref_paths", [])
|
| 103 |
-
|
| 104 |
-
final_keyframes = self.painter.generate_keyframes_from_storyboard(
|
| 105 |
-
storyboard=storyboard,
|
| 106 |
-
initial_ref_path=initial_ref_path,
|
| 107 |
-
global_prompt=global_prompt,
|
| 108 |
-
keyframe_resolution=keyframe_resolution,
|
| 109 |
-
general_ref_paths=general_ref_paths,
|
| 110 |
-
progress_callback_factory=progress_callback_factory
|
| 111 |
-
)
|
| 112 |
-
self.director.update_state("keyframes", final_keyframes)
|
| 113 |
-
logger.info("Maestro: Art Director has completed keyframe generation.")
|
| 114 |
-
return final_keyframes
|
| 115 |
-
|
| 116 |
-
# --- PRODUCTION & POST-PRODUCTION TASKS ---
|
| 117 |
-
|
| 118 |
-
def task_produce_original_movie(self, keyframes: List[str], global_prompt: str, seconds_per_fragment: float,
|
| 119 |
-
trim_percent: int, handler_strength: float,
|
| 120 |
-
destination_convergence_strength: float,
|
| 121 |
-
guidance_scale: float, stg_scale: float, inference_steps: int,
|
| 122 |
-
video_resolution: int, use_continuity_director: bool,
|
| 123 |
-
progress: gr.Progress) -> Dict[str, Any]:
|
| 124 |
-
"""
|
| 125 |
-
Delegates the production of the original master video to the Deformes4DEngine.
|
| 126 |
-
"""
|
| 127 |
-
logger.info("Maestro: Delegating production of the original movie to Deformes4DEngine.")
|
| 128 |
-
storyboard = self.director.get_state("storyboard", [])
|
| 129 |
-
|
| 130 |
-
result = self.editor.generate_original_movie(
|
| 131 |
-
keyframes=keyframes,
|
| 132 |
-
global_prompt=global_prompt,
|
| 133 |
-
storyboard=storyboard,
|
| 134 |
-
seconds_per_fragment=seconds_per_fragment,
|
| 135 |
-
trim_percent=trim_percent,
|
| 136 |
-
handler_strength=handler_strength,
|
| 137 |
-
destination_convergence_strength=destination_convergence_strength,
|
| 138 |
-
video_resolution=video_resolution,
|
| 139 |
-
use_continuity_director=use_continuity_director,
|
| 140 |
-
guidance_scale=guidance_scale,
|
| 141 |
-
stg_scale=stg_scale,
|
| 142 |
-
num_inference_steps=inference_steps,
|
| 143 |
-
progress=progress
|
| 144 |
-
)
|
| 145 |
-
|
| 146 |
-
self.director.update_state("final_video_path", result["final_path"])
|
| 147 |
-
self.director.update_state("latent_paths", result["latent_paths"])
|
| 148 |
-
logger.info("Maestro: Original movie production complete.")
|
| 149 |
-
return result
|
| 150 |
-
|
| 151 |
-
def task_run_latent_upscaler(self, latent_paths: List[str], chunk_size: int, progress: gr.Progress) -> Generator[Dict[str, Any], None, None]:
|
| 152 |
-
"""
|
| 153 |
-
Orchestrates the latent upscaling task.
|
| 154 |
-
"""
|
| 155 |
-
logger.info(f"Maestro: Delegating latent upscaling task for {len(latent_paths)} fragments.")
|
| 156 |
-
for update in self.editor.upscale_latents_and_create_video(
|
| 157 |
-
latent_paths=latent_paths,
|
| 158 |
-
chunk_size=chunk_size,
|
| 159 |
-
progress=progress
|
| 160 |
-
):
|
| 161 |
-
if "final_path" in update and update["final_path"]:
|
| 162 |
-
self.director.update_state("final_video_path", update["final_path"])
|
| 163 |
-
yield update
|
| 164 |
-
break
|
| 165 |
-
logger.info("Maestro: Latent upscaling complete.")
|
| 166 |
-
|
| 167 |
-
def task_run_hd_mastering(self, source_video_path: str, model_version: str, steps: int, prompt: str, progress: gr.Progress) -> Generator[Dict[str, Any], None, None]:
|
| 168 |
-
"""
|
| 169 |
-
Orchestrates the HD mastering task.
|
| 170 |
-
"""
|
| 171 |
-
logger.info(f"Maestro: Delegating HD mastering task using SeedVR {model_version}.")
|
| 172 |
-
for update in self.editor.master_video_hd(
|
| 173 |
-
source_video_path=source_video_path,
|
| 174 |
-
model_version=model_version,
|
| 175 |
-
steps=steps,
|
| 176 |
-
prompt=prompt,
|
| 177 |
-
progress=progress
|
| 178 |
-
):
|
| 179 |
-
if "final_path" in update and update["final_path"]:
|
| 180 |
-
self.director.update_state("final_video_path", update["final_path"])
|
| 181 |
-
yield update
|
| 182 |
-
break
|
| 183 |
-
logger.info("Maestro: HD mastering complete.")
|
| 184 |
-
|
| 185 |
-
def task_run_audio_generation(self, source_video_path: str, audio_prompt: str, progress: gr.Progress) -> Generator[Dict[str, Any], None, None]:
|
| 186 |
-
"""
|
| 187 |
-
Orchestrates the audio generation task.
|
| 188 |
-
"""
|
| 189 |
-
logger.info(f"Maestro: Delegating audio generation task.")
|
| 190 |
-
for update in self.editor.generate_audio_for_final_video(
|
| 191 |
-
source_video_path=source_video_path,
|
| 192 |
-
audio_prompt=audio_prompt,
|
| 193 |
-
progress=progress
|
| 194 |
-
):
|
| 195 |
-
if "final_path" in update and update["final_path"]:
|
| 196 |
-
self.director.update_state("final_video_path", update["final_path"])
|
| 197 |
-
yield update
|
| 198 |
-
break
|
| 199 |
-
logger.info("Maestro: Audio generation complete.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
aduc_orchestrator7d.py
DELETED
|
@@ -1,178 +0,0 @@
|
|
| 1 |
-
# aduc_orchestrator.py
|
| 2 |
-
#
|
| 3 |
-
# AducSdr: Uma implementação aberta e funcional da arquitetura ADUC-SDR
|
| 4 |
-
# Copyright (C) 4 de Agosto de 2025 Carlos Rodrigues dos Santos
|
| 5 |
-
#
|
| 6 |
-
# Contato:
|
| 7 |
-
# Carlos Rodrigues dos Santos
|
| 8 | |
| 9 |
-
# Rua Eduardo Carlos Pereira, 4125, B1 Ap32, Curitiba, PR, Brazil, CEP 8102025
|
| 10 |
-
#
|
| 11 |
-
# Repositórios e Projetos Relacionados:
|
| 12 |
-
# GitHub: https://github.com/carlex22/Aduc-sdr
|
| 13 |
-
#
|
| 14 |
-
# This program is free software: you can redistribute it and/or modify
|
| 15 |
-
# it under the terms of the GNU Affero General Public License as published by
|
| 16 |
-
# the Free Software Foundation, either version 3 of the License, or
|
| 17 |
-
# (at your option) any later version.
|
| 18 |
-
#
|
| 19 |
-
# This program is distributed in the hope that it will be useful,
|
| 20 |
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 21 |
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 22 |
-
# GNU Affero General Public License for more details.
|
| 23 |
-
#
|
| 24 |
-
# You should have received a copy of the GNU Affero General Public License
|
| 25 |
-
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
| 26 |
-
#
|
| 27 |
-
# This program is free software: you can redistribute it and/or modify
|
| 28 |
-
# it under the terms of the GNU Affero General Public License...
|
| 29 |
-
# PENDING PATENT NOTICE: Please see NOTICE.md.
|
| 30 |
-
#
|
| 31 |
-
# Version: 3.0.0
|
| 32 |
-
#
|
| 33 |
-
# This version adapts the Maestro to command the new unified "Turbo Intergalactic
|
| 34 |
-
# Multiverse Engine": the Deformes7DEngine. The orchestrator is now a lean
|
| 35 |
-
# command layer, delegating all complex production and post-production tasks
|
| 36 |
-
# to its single, powerful Chief Engineer.
|
| 37 |
-
|
| 38 |
-
import os
|
| 39 |
-
import logging
|
| 40 |
-
from typing import List, Dict, Any, Generator, Tuple
|
| 41 |
-
|
| 42 |
-
import gradio as gr
|
| 43 |
-
from PIL import Image, ImageOps
|
| 44 |
-
|
| 45 |
-
# O Orquestrador agora só precisa de dois engenheiros:
|
| 46 |
-
# - O Pensador (2D) para a lógica criativa
|
| 47 |
-
# - O Engenheiro-Chefe (7D) para toda a execução
|
| 48 |
-
from engineers.deformes2D_thinker import deformes2d_thinker_singleton
|
| 49 |
-
from engineers.deformes7D import deformes7d_engine_singleton
|
| 50 |
-
|
| 51 |
-
logger = logging.getLogger(__name__)
|
| 52 |
-
|
| 53 |
-
class AducDirector:
|
| 54 |
-
"""
|
| 55 |
-
Representa o Diretor de Cena, gerenciando o estado da produção.
|
| 56 |
-
"""
|
| 57 |
-
def __init__(self, workspace_dir: str):
|
| 58 |
-
self.workspace_dir = workspace_dir
|
| 59 |
-
os.makedirs(self.workspace_dir, exist_ok=True)
|
| 60 |
-
self.state: Dict[str, Any] = {}
|
| 61 |
-
logger.info(f"The stage is set. Workspace at '{self.workspace_dir}'.")
|
| 62 |
-
|
| 63 |
-
def update_state(self, key: str, value: Any) -> None:
|
| 64 |
-
logger.info(f"Notating on the score: State '{key}' updated.")
|
| 65 |
-
self.state[key] = value
|
| 66 |
-
|
| 67 |
-
def get_state(self, key: str, default: Any = None) -> Any:
|
| 68 |
-
return self.state.get(key, default)
|
| 69 |
-
|
| 70 |
-
class AducOrchestrator:
|
| 71 |
-
"""
|
| 72 |
-
Implementa o Maestro (Γ), a camada de orquestração que comanda
|
| 73 |
-
o Engenheiro-Chefe Deformes7D.
|
| 74 |
-
"""
|
| 75 |
-
def __init__(self, workspace_dir: str):
|
| 76 |
-
"""
|
| 77 |
-
Inicializa o Maestro e seu Engenheiro-Chefe.
|
| 78 |
-
"""
|
| 79 |
-
self.director = AducDirector(workspace_dir)
|
| 80 |
-
self.thinker = deformes2d_thinker_singleton
|
| 81 |
-
self.chief_engineer = deformes7d_engine_singleton
|
| 82 |
-
logger.info("ADUC Maestro is on the podium with the Chief Engineer (Deformes7D) ready.")
|
| 83 |
-
|
| 84 |
-
def process_image_for_story(self, image_path: str, size: int, filename: str) -> str:
|
| 85 |
-
"""
|
| 86 |
-
Pré-processa uma imagem de referência, padronizando-a.
|
| 87 |
-
"""
|
| 88 |
-
img = Image.open(image_path).convert("RGB")
|
| 89 |
-
img_square = ImageOps.fit(img, (size, size), Image.Resampling.LANCZOS)
|
| 90 |
-
processed_path = os.path.join(self.director.workspace_dir, filename)
|
| 91 |
-
img_square.save(processed_path)
|
| 92 |
-
logger.info(f"Reference image processed and saved to: {processed_path}")
|
| 93 |
-
return processed_path
|
| 94 |
-
|
| 95 |
-
# --- TAREFAS DE PRÉ-PRODUÇÃO (Delegadas ao Pensador 2D) ---
|
| 96 |
-
|
| 97 |
-
def task_generate_storyboard(self, prompt: str, num_keyframes: int, ref_image_paths: List[str],
|
| 98 |
-
progress: gr.Progress) -> Tuple[List[str], str, Any]:
|
| 99 |
-
"""
|
| 100 |
-
Delega a criação do storyboard para o Deformes2DThinker.
|
| 101 |
-
"""
|
| 102 |
-
logger.info(f"Act 1, Scene 1: Delegating storyboard creation to the Thinker.")
|
| 103 |
-
progress(0.2, desc="Consulting the Thinker for the script...")
|
| 104 |
-
|
| 105 |
-
storyboard = self.thinker.generate_storyboard(prompt, num_keyframes, ref_image_paths)
|
| 106 |
-
|
| 107 |
-
logger.info(f"The Thinker returned the script: {storyboard}")
|
| 108 |
-
self.director.update_state("storyboard", storyboard)
|
| 109 |
-
self.director.update_state("processed_ref_paths", ref_image_paths)
|
| 110 |
-
return storyboard, ref_image_paths[0], gr.update(visible=True, open=True)
|
| 111 |
-
|
| 112 |
-
# A geração de keyframes agora é parte da produção principal, então esta tarefa é removida.
|
| 113 |
-
# A seleção de keyframes para o modo fotógrafo permanece.
|
| 114 |
-
def task_select_keyframes(self, storyboard: List[str], base_ref_paths: List[str],
|
| 115 |
-
pool_ref_paths: List[str]) -> List[str]:
|
| 116 |
-
logger.info(f"Act 1, Scene 2 (Photographer Mode): Delegating keyframe selection to the Thinker.")
|
| 117 |
-
selected_paths = self.thinker.select_keyframes_from_pool(storyboard, base_ref_paths, pool_ref_paths)
|
| 118 |
-
self.director.update_state("keyframes", selected_paths)
|
| 119 |
-
return selected_paths
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
# --- TAREFA DE PRODUÇÃO UNIFICADA (Delegada ao Engenheiro-Chefe 7D) ---
|
| 123 |
-
|
| 124 |
-
def task_produce_full_movie(self, initial_ref_paths: List[str], global_prompt: str,
|
| 125 |
-
video_resolution: int, seconds_per_fragment: float,
|
| 126 |
-
# ... outros parâmetros do Deformes4D
|
| 127 |
-
trim_percent: int, handler_strength: float, dest_strength: float,
|
| 128 |
-
guidance_scale: float, stg_scale: float, inference_steps: int,
|
| 129 |
-
progress: gr.Progress) -> Dict[str, Any]:
|
| 130 |
-
"""
|
| 131 |
-
Delega a produção completa do filme para o Deformes7DEngine.
|
| 132 |
-
"""
|
| 133 |
-
logger.info("Maestro: All systems go. Engaging the Turbo Intergalactic Engine (Deformes7D)...")
|
| 134 |
-
storyboard = self.director.get_state("storyboard", [])
|
| 135 |
-
if not storyboard:
|
| 136 |
-
raise gr.Error("Storyboard not generated. Please complete Step 1 first.")
|
| 137 |
-
|
| 138 |
-
ltx_params = {
|
| 139 |
-
"guidance_scale": guidance_scale,
|
| 140 |
-
"stg_scale": stg_scale,
|
| 141 |
-
"num_inference_steps": inference_steps
|
| 142 |
-
}
|
| 143 |
-
|
| 144 |
-
# A chamada agora é para a função unificada do motor 7D
|
| 145 |
-
result = self.chief_engineer.generate_full_movie_interleaved(
|
| 146 |
-
initial_ref_paths=initial_ref_paths,
|
| 147 |
-
storyboard=storyboard,
|
| 148 |
-
global_prompt=global_prompt,
|
| 149 |
-
video_resolution=video_resolution,
|
| 150 |
-
seconds_per_fragment=seconds_per_fragment,
|
| 151 |
-
trim_percent=trim_percent,
|
| 152 |
-
handler_strength=handler_strength,
|
| 153 |
-
dest_strength=dest_strength,
|
| 154 |
-
ltx_params=ltx_params,
|
| 155 |
-
progress=progress
|
| 156 |
-
)
|
| 157 |
-
|
| 158 |
-
self.director.update_state("final_video_path", result["final_path"])
|
| 159 |
-
self.director.update_state("all_keyframes", result["all_keyframes"])
|
| 160 |
-
logger.info("Maestro: Deformes7D has completed the main production run.")
|
| 161 |
-
return result
|
| 162 |
-
|
| 163 |
-
# --- TAREFAS DE PÓS-PRODUÇÃO (Delegadas ao Engenheiro-Chefe 7D) ---
|
| 164 |
-
|
| 165 |
-
def task_run_hd_mastering(self, source_video_path: str, model_version: str, steps: int, prompt: str, progress: gr.Progress) -> Generator[Dict[str, Any], None, None]:
|
| 166 |
-
logger.info(f"Maestro: Delegating HD mastering to the Chief Engineer.")
|
| 167 |
-
for update in self.chief_engineer.master_video_hd(
|
| 168 |
-
source_video_path=source_video_path, model_version=model_version,
|
| 169 |
-
steps=steps, prompt=prompt, progress=progress
|
| 170 |
-
):
|
| 171 |
-
yield update
|
| 172 |
-
|
| 173 |
-
def task_run_audio_generation(self, source_video_path: str, audio_prompt: str, progress: gr.Progress) -> Generator[Dict[str, Any], None, None]:
|
| 174 |
-
logger.info(f"Maestro: Delegating audio generation to the Chief Engineer.")
|
| 175 |
-
for update in self.chief_engineer.generate_audio(
|
| 176 |
-
source_video_path=source_video_path, audio_prompt=audio_prompt, progress=progress
|
| 177 |
-
):
|
| 178 |
-
yield update
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
aduc_types.py
DELETED
|
@@ -1,43 +0,0 @@
|
|
| 1 |
-
# aduc_types.py
|
| 2 |
-
# AducSdr: Uma implementação aberta e funcional da arquitetura ADUC-SDR
|
| 3 |
-
# Copyright (C) 4 de Agosto de 2025 Carlos Rodrigues dos Santos
|
| 4 |
-
#
|
| 5 |
-
# Contato:
|
| 6 |
-
# Carlos Rodrigues dos Santos
|
| 7 | |
| 8 |
-
# Rua Eduardo Carlos Pereira, 4125, B1 Ap32, Curitiba, PR, Brazil, CEP 8102025
|
| 9 |
-
#
|
| 10 |
-
# Repositórios e Projetos Relacionados:
|
| 11 |
-
# GitHub: https://github.com/carlex22/Aduc-sdr
|
| 12 |
-
#
|
| 13 |
-
# This program is free software: you can redistribute it and/or modify
|
| 14 |
-
# it under the terms of the GNU Affero General Public License as published by
|
| 15 |
-
# the Free Software Foundation, either version 3 of the License, or
|
| 16 |
-
# (at your option) any later version.
|
| 17 |
-
#
|
| 18 |
-
# This program is distributed in the hope that it will be useful,
|
| 19 |
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 20 |
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 21 |
-
# GNU Affero General Public License for more details.
|
| 22 |
-
#
|
| 23 |
-
# You should have received a copy of the GNU Affero General Public License
|
| 24 |
-
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
| 25 |
-
#
|
| 26 |
-
# This program is free software: you can redistribute it and/or modify
|
| 27 |
-
# it under the terms of the GNU Affero General Public License...
|
| 28 |
-
# PENDING PATENT NOTICE: Please see NOTICE.md.
|
| 29 |
-
#
|
| 30 |
-
# Version: 1.0.0
|
| 31 |
-
#
|
| 32 |
-
# This file defines common data structures and types used across the ADUC-SDR
|
| 33 |
-
# framework to ensure consistent data contracts between modules.
|
| 34 |
-
|
| 35 |
-
from dataclasses import dataclass
|
| 36 |
-
import torch
|
| 37 |
-
|
| 38 |
-
@dataclass
|
| 39 |
-
class LatentConditioningItem:
|
| 40 |
-
"""Represents a conditioning anchor in the latent space for the Camera (Ψ)."""
|
| 41 |
-
latent_tensor: torch.Tensor
|
| 42 |
-
media_frame_number: int
|
| 43 |
-
conditioning_strength: float
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.py
CHANGED
|
@@ -29,263 +29,210 @@
|
|
| 29 |
# PENDING PATENT NOTICE: The ADUC method and system implemented in this
|
| 30 |
# software is in the process of being patented. Please see NOTICE.md for details.
|
| 31 |
|
|
|
|
| 32 |
import gradio as gr
|
| 33 |
import yaml
|
| 34 |
import logging
|
| 35 |
import os
|
| 36 |
-
import sys
|
| 37 |
import shutil
|
| 38 |
import time
|
| 39 |
import json
|
| 40 |
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
-
# --- CUSTOM UI THEME DEFINITION ---
|
| 44 |
-
# This theme provides a professional, dark-mode look and feel, suitable for creative tools.
|
| 45 |
cinematic_theme = gr.themes.Base(
|
| 46 |
primary_hue=gr.themes.colors.indigo,
|
| 47 |
secondary_hue=gr.themes.colors.purple,
|
| 48 |
neutral_hue=gr.themes.colors.slate,
|
| 49 |
font=(gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"),
|
| 50 |
).set(
|
| 51 |
-
#
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
#
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
button_secondary_border_color="#4B5563",
|
| 60 |
-
button_secondary_text_color="#E5E7EB",
|
| 61 |
-
|
| 62 |
-
# -- Blocks and Containers --
|
| 63 |
-
block_background_fill="#1F2937", # Slate 800
|
| 64 |
-
block_border_width="1px",
|
| 65 |
-
block_border_color="#374151", # Slate 700
|
| 66 |
-
block_label_background_fill="#374151",
|
| 67 |
-
block_label_text_color="#E5E7EB",
|
| 68 |
-
block_title_text_color="#FFFFFF",
|
| 69 |
-
|
| 70 |
-
# -- Input Fields --
|
| 71 |
-
input_background_fill="#374151",
|
| 72 |
-
input_border_color="#4B5563",
|
| 73 |
-
input_placeholder_color="#9CA3AF",
|
| 74 |
-
|
| 75 |
-
# -- Spacing and Radius --
|
| 76 |
-
#block_radius_size="lg",
|
| 77 |
-
#spacing_size="lg",
|
| 78 |
-
#layout_gap="lg",
|
| 79 |
)
|
| 80 |
|
| 81 |
-
# --- 1. CONFIGURATION AND INITIALIZATION ---
|
| 82 |
LOG_FILE_PATH = "aduc_log.txt"
|
| 83 |
if os.path.exists(LOG_FILE_PATH):
|
| 84 |
os.remove(LOG_FILE_PATH)
|
| 85 |
|
|
|
|
| 86 |
log_format = '%(asctime)s - %(levelname)s - [%(name)s:%(funcName)s] - %(message)s'
|
| 87 |
root_logger = logging.getLogger()
|
| 88 |
root_logger.setLevel(logging.INFO)
|
| 89 |
-
|
| 90 |
-
stream_handler = logging.StreamHandler(sys.stdout)
|
| 91 |
-
stream_handler.setLevel(logging.INFO)
|
| 92 |
-
stream_handler.setFormatter(logging.Formatter(log_format))
|
| 93 |
-
root_logger.addHandler(stream_handler)
|
| 94 |
-
file_handler = logging.FileHandler(LOG_FILE_PATH, mode='w', encoding='utf-8')
|
| 95 |
-
file_handler.setLevel(logging.INFO)
|
| 96 |
-
file_handler.setFormatter(logging.Formatter(log_format))
|
| 97 |
-
root_logger.addHandler(file_handler)
|
| 98 |
logger = logging.getLogger(__name__)
|
| 99 |
|
| 100 |
-
|
| 101 |
-
try:
|
| 102 |
-
with open("i18n.json", "r", encoding="utf-8") as f: i18n = json.load(f)
|
| 103 |
-
except Exception as e:
|
| 104 |
-
logger.error(f"Error loading i18n.json: {e}")
|
| 105 |
-
i18n = {"pt": {}, "en": {}, "zh": {}}
|
| 106 |
-
if 'pt' not in i18n: i18n['pt'] = i18n.get('en', {})
|
| 107 |
-
if 'en' not in i18n: i18n['en'] = {}
|
| 108 |
-
if 'zh' not in i18n: i18n['zh'] = i18n.get('en', {})
|
| 109 |
-
|
| 110 |
try:
|
| 111 |
with open("config.yaml", 'r') as f: config = yaml.safe_load(f)
|
| 112 |
WORKSPACE_DIR = config['application']['workspace_dir']
|
| 113 |
-
|
| 114 |
-
|
|
|
|
|
|
|
|
|
|
| 115 |
except Exception as e:
|
| 116 |
-
logger.
|
| 117 |
exit()
|
| 118 |
|
| 119 |
-
# --- 2.
|
|
|
|
| 120 |
def run_pre_production_wrapper(prompt, num_keyframes, ref_files, resolution_str, duration_per_fragment, progress=gr.Progress()):
|
| 121 |
-
if not ref_files:
|
|
|
|
|
|
|
| 122 |
ref_paths = [aduc.process_image_for_story(f.name, 480, f"ref_processed_{i}.png") for i, f in enumerate(ref_files)]
|
| 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 |
-
final_path = None
|
| 170 |
-
for update in aduc.task_run_hd_mastering(source_video, model_version, int(steps), global_prompt, progress=progress): final_path = update['final_path']
|
| 171 |
-
yield {hd_video_output: gr.update(value=final_path, label="✅ HD Mastering Complete"), final_video_output: gr.update(value=final_path), hd_video_path_state: final_path, current_source_video_state: final_path}
|
| 172 |
-
|
| 173 |
-
def run_audio_wrapper(source_video, audio_prompt, global_prompt, progress=gr.Progress()):
|
| 174 |
-
if not source_video: raise gr.Error("Cannot run Audio Generation. No source video found. Please complete a previous step first.")
|
| 175 |
-
yield {audio_video_output: gr.update(value=None, visible=True, label="Generating audio and muxing..."), final_video_output: gr.update(label="Post-Production in progress: Audio Generation...")}
|
| 176 |
-
final_audio_prompt = audio_prompt if audio_prompt and audio_prompt.strip() else global_prompt
|
| 177 |
-
final_path = None
|
| 178 |
-
for update in aduc.task_run_audio_generation(source_video, final_audio_prompt, progress=progress): final_path = update['final_path']
|
| 179 |
-
yield {audio_video_output: gr.update(value=final_path, label="✅ Audio Generation Complete"), final_video_output: gr.update(value=final_path)}
|
| 180 |
|
| 181 |
def get_log_content():
|
| 182 |
try:
|
| 183 |
-
with open(LOG_FILE_PATH, "r", encoding="utf-8") as f:
|
|
|
|
| 184 |
except FileNotFoundError:
|
| 185 |
-
return "
|
| 186 |
-
|
| 187 |
-
def update_ui_language(lang_emoji):
|
| 188 |
-
lang_code_map = {"🇧🇷": "pt", "🇺🇸": "en", "🇨🇳": "zh"}
|
| 189 |
-
lang_code = lang_code_map.get(lang_emoji, "en")
|
| 190 |
-
lang_map = i18n.get(lang_code, i18n.get('en', {}))
|
| 191 |
-
# ... This dictionary mapping will be long, so it's defined once in the main block
|
| 192 |
|
| 193 |
-
# --- 3.
|
| 194 |
with gr.Blocks(theme=cinematic_theme, css="style.css") as demo:
|
| 195 |
-
|
|
|
|
| 196 |
|
| 197 |
original_latents_paths_state = gr.State(value=None)
|
| 198 |
original_video_path_state = gr.State(value=None)
|
| 199 |
-
upscaled_video_path_state = gr.State(value=None)
|
| 200 |
-
hd_video_path_state = gr.State(value=None)
|
| 201 |
current_source_video_state = gr.State(value=None)
|
| 202 |
|
| 203 |
-
|
| 204 |
-
|
|
|
|
| 205 |
with gr.Row():
|
| 206 |
-
lang_selector = gr.Radio(["🇧🇷", "🇺🇸", "🇨🇳"], value="🇧🇷", label=
|
| 207 |
-
resolution_selector = gr.Radio(["480x480", "720x720", "960x960"], value="480x480", label="Base
|
| 208 |
|
| 209 |
-
with gr.Accordion(
|
| 210 |
-
prompt_input = gr.Textbox(label=
|
| 211 |
-
ref_image_input = gr.File(label=
|
| 212 |
-
with gr.Row():
|
| 213 |
-
num_keyframes_slider = gr.Slider(minimum=3, maximum=42, value=5, step=1, label=default_lang.get('keyframes_label'))
|
| 214 |
-
duration_per_fragment_slider = gr.Slider(label=default_lang.get('duration_label'), info=default_lang.get('duration_info'), minimum=2.0, maximum=10.0, value=4.0, step=0.1)
|
| 215 |
with gr.Row():
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
storyboard_output = gr.JSON(label=
|
| 220 |
-
keyframe_gallery = gr.Gallery(label=
|
| 221 |
-
|
| 222 |
-
with gr.Accordion(
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
upscaler_video_output = gr.Video(label="Upscaled Video", visible=False, interactive=False)
|
| 246 |
-
with gr.Accordion(default_lang.get('sub_step_b_hd'), open=True) as sub_step_b_accordion:
|
| 247 |
-
hd_description_md = gr.Markdown(default_lang.get('hd_description'))
|
| 248 |
-
with gr.Accordion(default_lang.get('hd_options'), open=False) as hd_options_accordion:
|
| 249 |
-
hd_model_radio = gr.Radio(["3B", "7B"], value="7B", label=default_lang.get('hd_model_label'))
|
| 250 |
-
hd_steps_slider = gr.Slider(minimum=20, maximum=150, value=100, step=5, label=default_lang.get('hd_steps_label'), info=default_lang.get('hd_steps_info'))
|
| 251 |
-
run_hd_button = gr.Button(default_lang.get('run_hd_button'), variant="secondary")
|
| 252 |
-
hd_video_output = gr.Video(label="HD Mastered Video", visible=False, interactive=False)
|
| 253 |
-
with gr.Accordion(default_lang.get('sub_step_c_audio'), open=True) as sub_step_c_accordion:
|
| 254 |
-
audio_description_md = gr.Markdown(default_lang.get('audio_description'))
|
| 255 |
-
with gr.Accordion(default_lang.get('audio_options'), open=False) as audio_options_accordion:
|
| 256 |
-
audio_prompt_input = gr.Textbox(label=default_lang.get('audio_prompt_label'), info=default_lang.get('audio_prompt_info'), lines=3)
|
| 257 |
-
run_audio_button = gr.Button(default_lang.get('run_audio_button'), variant="secondary")
|
| 258 |
-
audio_video_output = gr.Video(label="Video with Audio", visible=False, interactive=False)
|
| 259 |
-
|
| 260 |
-
final_video_output = gr.Video(label=default_lang.get('final_video_label'), visible=False, interactive=False)
|
| 261 |
-
with gr.Accordion(default_lang.get('log_accordion_label'), open=False) as log_accordion:
|
| 262 |
-
log_display = gr.Textbox(label=default_lang.get('log_display_label'), lines=20, interactive=False, autoscroll=True)
|
| 263 |
-
update_log_button = gr.Button(default_lang.get('update_log_button'))
|
| 264 |
-
|
| 265 |
-
# --- 4. UI EVENT CONNECTIONS ---
|
| 266 |
-
all_ui_components = [title_md, subtitle_md, lang_selector, step1_accordion, prompt_input, ref_image_input, num_keyframes_slider, duration_per_fragment_slider, storyboard_and_keyframes_button, storyboard_from_photos_button, step1_mode_b_info_md, storyboard_output, keyframe_gallery, step3_accordion, step3_description_md, produce_original_button, ltx_advanced_options_accordion, causality_accordion, trim_percent_slider, forca_guia_slider, convergencia_destino_slider, ltx_pipeline_accordion, guidance_scale_slider, stg_scale_slider, inference_steps_slider, step4_accordion, step4_description_md, sub_step_a_accordion, upscaler_description_md, upscaler_options_accordion, upscaler_chunk_size_slider, run_upscaler_button, sub_step_b_accordion, hd_description_md, hd_options_accordion, hd_model_radio, hd_steps_slider, run_hd_button, sub_step_c_accordion, audio_description_md, audio_options_accordion, audio_prompt_input, run_audio_button, final_video_output, log_accordion, log_display, update_log_button]
|
| 267 |
-
def create_lang_update_fn():
|
| 268 |
-
def update_lang(lang_emoji):
|
| 269 |
-
lang_code_map = {"🇧🇷": "pt", "🇺🇸": "en", "🇨🇳": "zh"}
|
| 270 |
-
lang_code = lang_code_map.get(lang_emoji, "en")
|
| 271 |
-
lang_map = i18n.get(lang_code, i18n.get('en', {}))
|
| 272 |
-
return [gr.update(value=f"<h1>{lang_map.get('app_title')}</h1>"),gr.update(value=f"<p>{lang_map.get('app_subtitle')}</p>"),gr.update(label=lang_map.get('lang_selector_label')),gr.update(label=lang_map.get('step1_accordion')),gr.update(label=lang_map.get('prompt_label')),gr.update(label=lang_map.get('ref_images_label')),gr.update(label=lang_map.get('keyframes_label')),gr.update(label=lang_map.get('duration_label'), info=lang_map.get('duration_info')),gr.update(value=lang_map.get('storyboard_and_keyframes_button')),gr.update(value=lang_map.get('storyboard_from_photos_button')),gr.update(value=f"*{lang_map.get('step1_mode_b_info')}*"),gr.update(label=lang_map.get('storyboard_output_label')),gr.update(label=lang_map.get('keyframes_gallery_label')),gr.update(label=lang_map.get('step3_accordion')),gr.update(value=lang_map.get('step3_description')),gr.update(value=lang_map.get('produce_original_button')),gr.update(label=lang_map.get('ltx_advanced_options')),gr.update(label=lang_map.get('causality_controls_title')),gr.update(label=lang_map.get('trim_percent_label'), info=lang_map.get('trim_percent_info')),gr.update(label=lang_map.get('forca_guia_label'), info=lang_map.get('forca_guia_info')),gr.update(label=lang_map.get('convergencia_final_label'), info=lang_map.get('convergencia_final_info')),gr.update(label=lang_map.get('ltx_pipeline_options')),gr.update(label=lang_map.get('guidance_scale_label'), info=lang_map.get('guidance_scale_info')),gr.update(label=lang_map.get('stg_scale_label'), info=lang_map.get('stg_scale_info')),gr.update(label=lang_map.get('steps_label'), info=lang_map.get('steps_info')),gr.update(label=lang_map.get('step4_accordion')),gr.update(value=lang_map.get('step4_description')),gr.update(label=lang_map.get('sub_step_a_upscaler')),gr.update(value=lang_map.get('upscaler_description')),gr.update(label=lang_map.get('upscaler_options')),gr.update(label=lang_map.get('upscaler_chunk_size_label'), info=lang_map.get('upscaler_chunk_size_info')),gr.update(value=lang_map.get('run_upscaler_button')),gr.update(label=lang_map.get('sub_step_b_hd')),gr.update(value=lang_map.get('hd_description')),gr.update(label=lang_map.get('hd_options')),gr.update(label=lang_map.get('hd_model_label')),gr.update(label=lang_map.get('hd_steps_label'), info=lang_map.get('hd_steps_info')),gr.update(value=lang_map.get('run_hd_button')),gr.update(label=lang_map.get('sub_step_c_audio')),gr.update(value=lang_map.get('audio_description')),gr.update(label=lang_map.get('audio_options')),gr.update(label=lang_map.get('audio_prompt_label'), info=lang_map.get('audio_prompt_info')),gr.update(value=lang_map.get('run_audio_button')),gr.update(label=lang_map.get('final_video_label')),gr.update(label=lang_map.get('log_accordion_label')),gr.update(label=lang_map.get('log_display_label')),gr.update(value=lang_map.get('update_log_button'))]
|
| 273 |
-
return update_lang
|
| 274 |
-
lang_selector.change(fn=create_lang_update_fn(), inputs=lang_selector, outputs=all_ui_components)
|
| 275 |
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
update_log_button.click(fn=get_log_content, inputs=[], outputs=[log_display])
|
| 283 |
-
|
| 284 |
-
# --- 5.
|
| 285 |
if __name__ == "__main__":
|
| 286 |
if os.path.exists(WORKSPACE_DIR):
|
| 287 |
-
logger.info(f"
|
| 288 |
shutil.rmtree(WORKSPACE_DIR)
|
| 289 |
os.makedirs(WORKSPACE_DIR)
|
| 290 |
-
|
|
|
|
| 291 |
demo.queue().launch()
|
|
|
|
| 29 |
# PENDING PATENT NOTICE: The ADUC method and system implemented in this
|
| 30 |
# software is in the process of being patented. Please see NOTICE.md for details.
|
| 31 |
|
| 32 |
+
|
| 33 |
import gradio as gr
|
| 34 |
import yaml
|
| 35 |
import logging
|
| 36 |
import os
|
|
|
|
| 37 |
import shutil
|
| 38 |
import time
|
| 39 |
import json
|
| 40 |
|
| 41 |
+
# --- 1. IMPORTAÇÃO DO FRAMEWORK E SEUS TIPOS ---
|
| 42 |
+
# A UI agora trata o Aduc-Sdr como um pacote que ela consome.
|
| 43 |
+
import aduc_framework
|
| 44 |
+
from aduc_framework.types import PreProductionParams, ProductionParams
|
| 45 |
+
|
| 46 |
+
# --- CUSTOM UI THEME E CONFIGURAÇÃO INICIAL ---
|
| 47 |
+
# (Esta seção permanece a mesma, pois é específica da aplicação)
|
| 48 |
|
|
|
|
|
|
|
| 49 |
cinematic_theme = gr.themes.Base(
|
| 50 |
primary_hue=gr.themes.colors.indigo,
|
| 51 |
secondary_hue=gr.themes.colors.purple,
|
| 52 |
neutral_hue=gr.themes.colors.slate,
|
| 53 |
font=(gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"),
|
| 54 |
).set(
|
| 55 |
+
body_background_fill="#111827", body_text_color="#E5E7EB",
|
| 56 |
+
button_primary_background_fill="linear-gradient(90deg, #4F46E5, #8B5CF6)",
|
| 57 |
+
button_primary_text_color="#FFFFFF", button_secondary_background_fill="#374151",
|
| 58 |
+
button_secondary_border_color="#4B5563", button_secondary_text_color="#E5E7EB",
|
| 59 |
+
block_background_fill="#1F2937", block_border_width="1px", block_border_color="#374151",
|
| 60 |
+
block_label_background_fill="#374151", block_label_text_color="#E5E7EB",
|
| 61 |
+
block_title_text_color="#FFFFFF", input_background_fill="#374151",
|
| 62 |
+
input_border_color="#4B5563", input_placeholder_color="#9CA3AF",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
)
|
| 64 |
|
|
|
|
| 65 |
LOG_FILE_PATH = "aduc_log.txt"
|
| 66 |
if os.path.exists(LOG_FILE_PATH):
|
| 67 |
os.remove(LOG_FILE_PATH)
|
| 68 |
|
| 69 |
+
# Configuração de logging
|
| 70 |
log_format = '%(asctime)s - %(levelname)s - [%(name)s:%(funcName)s] - %(message)s'
|
| 71 |
root_logger = logging.getLogger()
|
| 72 |
root_logger.setLevel(logging.INFO)
|
| 73 |
+
# ... (código completo de configuração de logging) ...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
logger = logging.getLogger(__name__)
|
| 75 |
|
| 76 |
+
# Carrega a configuração e inicializa o framework
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
try:
|
| 78 |
with open("config.yaml", 'r') as f: config = yaml.safe_load(f)
|
| 79 |
WORKSPACE_DIR = config['application']['workspace_dir']
|
| 80 |
+
|
| 81 |
+
# --- PONTO DE ENTRADA DO FRAMEWORK ---
|
| 82 |
+
aduc = aduc_framework.create_aduc_instance(workspace_dir=WORKSPACE_DIR)
|
| 83 |
+
|
| 84 |
+
logger.info("Interface Gradio inicializada e conectada ao Aduc Framework.")
|
| 85 |
except Exception as e:
|
| 86 |
+
logger.critical(f"ERRO CRÍTICO durante a inicialização: {e}", exc_info=True)
|
| 87 |
exit()
|
| 88 |
|
| 89 |
+
# --- 2. FUNÇÕES WRAPPER (CAMADA DE TRADUÇÃO UI <-> FRAMEWORK) ---
|
| 90 |
+
|
| 91 |
def run_pre_production_wrapper(prompt, num_keyframes, ref_files, resolution_str, duration_per_fragment, progress=gr.Progress()):
|
| 92 |
+
if not ref_files:
|
| 93 |
+
raise gr.Error("Por favor, forneça pelo menos uma imagem de referência.")
|
| 94 |
+
|
| 95 |
ref_paths = [aduc.process_image_for_story(f.name, 480, f"ref_processed_{i}.png") for i, f in enumerate(ref_files)]
|
| 96 |
+
|
| 97 |
+
params = PreProductionParams(
|
| 98 |
+
prompt=prompt,
|
| 99 |
+
num_keyframes=int(num_keyframes),
|
| 100 |
+
ref_paths=ref_paths,
|
| 101 |
+
resolution=int(resolution_str.split('x')[0]),
|
| 102 |
+
duration_per_fragment=duration_per_fragment
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
storyboard, final_keyframes, updated_state = aduc.task_pre_production(params, progress)
|
| 106 |
+
|
| 107 |
+
return updated_state.model_dump(), storyboard, final_keyframes, gr.update(visible=True, open=True)
|
| 108 |
+
|
| 109 |
+
def run_original_production_wrapper(current_state_dict, trim_percent, handler_strength, dest_strength, guidance_scale, stg_scale, steps, progress=gr.Progress()):
|
| 110 |
+
yield {
|
| 111 |
+
original_video_output: gr.update(value=None, visible=True, label="🎬 Produzindo seu filme..."),
|
| 112 |
+
final_video_output: gr.update(value=None, visible=True, label="🎬 Produção em progresso..."),
|
| 113 |
+
step4_accordion: gr.update(visible=False)
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
production_params = ProductionParams(
|
| 117 |
+
trim_percent=int(trim_percent),
|
| 118 |
+
handler_strength=handler_strength,
|
| 119 |
+
destination_convergence_strength=dest_strength,
|
| 120 |
+
guidance_scale=guidance_scale,
|
| 121 |
+
stg_scale=stg_scale,
|
| 122 |
+
inference_steps=int(steps)
|
| 123 |
+
)
|
| 124 |
+
|
| 125 |
+
final_video_path, latent_paths, updated_state = aduc.task_produce_original_movie(
|
| 126 |
+
params=production_params,
|
| 127 |
+
progress_callback=progress
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
updated_state_dict = updated_state.model_dump()
|
| 131 |
+
|
| 132 |
+
yield {
|
| 133 |
+
original_video_output: gr.update(value=final_video_path, label="✅ Filme Original Master"),
|
| 134 |
+
final_video_output: gr.update(value=final_video_path),
|
| 135 |
+
step4_accordion: gr.update(visible=True, open=True),
|
| 136 |
+
original_latents_paths_state: latent_paths,
|
| 137 |
+
original_video_path_state: final_video_path,
|
| 138 |
+
current_source_video_state: final_video_path,
|
| 139 |
+
generation_state_holder: updated_state_dict,
|
| 140 |
+
generation_data_output: updated_state_dict
|
| 141 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
|
| 143 |
def get_log_content():
|
| 144 |
try:
|
| 145 |
+
with open(LOG_FILE_PATH, "r", encoding="utf-8") as f:
|
| 146 |
+
return f.read()
|
| 147 |
except FileNotFoundError:
|
| 148 |
+
return "Arquivo de log ainda não criado. Inicie uma geração."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
+
# --- 3. DEFINIÇÃO DA UI GRADIO ---
|
| 151 |
with gr.Blocks(theme=cinematic_theme, css="style.css") as demo:
|
| 152 |
+
|
| 153 |
+
generation_state_holder = gr.State(value={})
|
| 154 |
|
| 155 |
original_latents_paths_state = gr.State(value=None)
|
| 156 |
original_video_path_state = gr.State(value=None)
|
|
|
|
|
|
|
| 157 |
current_source_video_state = gr.State(value=None)
|
| 158 |
|
| 159 |
+
gr.Markdown("<h1>ADUC-SDR 🎬 - O Diretor de Cinema IA</h1>")
|
| 160 |
+
gr.Markdown("<p>Crie um filme completo com vídeo e áudio, orquestrado por uma equipe de IAs especialistas.</p>")
|
| 161 |
+
|
| 162 |
with gr.Row():
|
| 163 |
+
lang_selector = gr.Radio(["🇧🇷", "🇺🇸", "🇨🇳"], value="🇧🇷", label="Idioma / Language")
|
| 164 |
+
resolution_selector = gr.Radio(["480x480", "720x720", "960x960"], value="480x480", label="Resolução Base")
|
| 165 |
|
| 166 |
+
with gr.Accordion("Etapa 1: Roteiro e Cenas-Chave (Pré-Produção)", open=True) as step1_accordion:
|
| 167 |
+
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.")
|
| 168 |
+
ref_image_input = gr.File(label="Imagens de Referência", file_count="multiple", file_types=["image"])
|
|
|
|
|
|
|
|
|
|
| 169 |
with gr.Row():
|
| 170 |
+
num_keyframes_slider = gr.Slider(minimum=3, maximum=42, value=5, step=1, label="Número de Cenas-Chave")
|
| 171 |
+
duration_per_fragment_slider = gr.Slider(label="Duração de cada Clipe (s)", info="Duração alvo para cada fragmento de vídeo.", minimum=2.0, maximum=10.0, value=4.0, step=0.1)
|
| 172 |
+
storyboard_and_keyframes_button = gr.Button("Gerar Roteiro e Keyframes", variant="primary")
|
| 173 |
+
storyboard_output = gr.JSON(label="Roteiro Gerado (Storyboard)")
|
| 174 |
+
keyframe_gallery = gr.Gallery(label="Galeria de Cenas-Chave (Keyframes)", visible=True, object_fit="contain", height="auto", type="filepath")
|
| 175 |
+
|
| 176 |
+
with gr.Accordion("Etapa 3: Produção do Vídeo Original", open=False, visible=False) as step3_accordion:
|
| 177 |
+
# Aqui omiti a definição detalhada dos sliders para brevidade, mas eles existem
|
| 178 |
+
trim_percent_slider = gr.Slider(minimum=10, maximum=90, value=50, step=5, label="Poda Causal (%)")
|
| 179 |
+
handler_strength = gr.Slider(label="Força do Déjà-Vu", minimum=0.0, maximum=1.0, value=0.5, step=0.05)
|
| 180 |
+
dest_strength = gr.Slider(label="Força da Âncora Final", minimum=0.0, maximum=1.0, value=0.75, step=0.05)
|
| 181 |
+
guidance_scale_slider = gr.Slider(minimum=1.0, maximum=10.0, value=2.0, step=0.1, label="Escala de Orientação")
|
| 182 |
+
stg_scale_slider = gr.Slider(minimum=0.0, maximum=1.0, value=0.025, step=0.005, label="Escala STG")
|
| 183 |
+
inference_steps_slider = gr.Slider(minimum=10, maximum=50, value=20, step=1, label="Passos de Inferência")
|
| 184 |
+
produce_original_button = gr.Button("🎬 Produzir Vídeo Original", variant="primary")
|
| 185 |
+
original_video_output = gr.Video(label="Filme Original Master", visible=False, interactive=False)
|
| 186 |
+
|
| 187 |
+
with gr.Accordion("Etapa 4: Pós-Produção (Opcional)", open=False, visible=False) as step4_accordion:
|
| 188 |
+
gr.Markdown("Aplique efeitos de melhoria ao vídeo mais recente.")
|
| 189 |
+
# ... (Componentes de pós-produção aqui) ...
|
| 190 |
+
|
| 191 |
+
with gr.Accordion("🧬 DNA Digital da Geração (JSON)", open=False) as data_accordion:
|
| 192 |
+
generation_data_output = gr.JSON(label="Estado de Geração Completo")
|
| 193 |
+
|
| 194 |
+
final_video_output = gr.Video(label="Filme Final (Resultado da Última Etapa)", visible=False, interactive=False)
|
| 195 |
+
|
| 196 |
+
with gr.Accordion("📝 Log de Geração (Detalhado)", open=False) as log_accordion:
|
| 197 |
+
log_display = gr.Textbox(label="Log da Sessão", lines=20, interactive=False, autoscroll=True)
|
| 198 |
+
update_log_button = gr.Button("Atualizar Log")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
|
| 200 |
+
# --- 4. CONEXÕES DE EVENTOS DA UI ---
|
| 201 |
+
|
| 202 |
+
storyboard_and_keyframes_button.click(
|
| 203 |
+
fn=run_pre_production_wrapper,
|
| 204 |
+
inputs=[prompt_input, num_keyframes_slider, ref_image_input, resolution_selector, duration_per_fragment_slider],
|
| 205 |
+
outputs=[generation_state_holder, storyboard_output, keyframe_gallery, step3_accordion]
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
+
produce_original_button.click(
|
| 209 |
+
fn=run_original_production_wrapper,
|
| 210 |
+
inputs=[
|
| 211 |
+
generation_state_holder,
|
| 212 |
+
trim_percent_slider, handler_strength, dest_strength,
|
| 213 |
+
guidance_scale_slider, stg_scale_slider, inference_steps_slider
|
| 214 |
+
],
|
| 215 |
+
outputs=[
|
| 216 |
+
original_video_output, final_video_output, step4_accordion,
|
| 217 |
+
original_latents_paths_state, original_video_path_state, current_source_video_state,
|
| 218 |
+
generation_state_holder, generation_data_output
|
| 219 |
+
]
|
| 220 |
+
)
|
| 221 |
+
|
| 222 |
+
generation_state_holder.change(
|
| 223 |
+
fn=lambda state: state,
|
| 224 |
+
inputs=generation_state_holder,
|
| 225 |
+
outputs=generation_data_output
|
| 226 |
+
)
|
| 227 |
+
|
| 228 |
update_log_button.click(fn=get_log_content, inputs=[], outputs=[log_display])
|
| 229 |
+
|
| 230 |
+
# --- 5. INICIALIZAÇÃO DA APLICAÇÃO ---
|
| 231 |
if __name__ == "__main__":
|
| 232 |
if os.path.exists(WORKSPACE_DIR):
|
| 233 |
+
logger.info(f"Limpando workspace anterior em: {WORKSPACE_DIR}")
|
| 234 |
shutil.rmtree(WORKSPACE_DIR)
|
| 235 |
os.makedirs(WORKSPACE_DIR)
|
| 236 |
+
|
| 237 |
+
logger.info("Aplicação Gradio iniciada. Lançando interface...")
|
| 238 |
demo.queue().launch()
|
app_api.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# app_api.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 3.0.0 (API Head for Aduc Framework)
|
| 6 |
+
#
|
| 7 |
+
# Este arquivo implementa um servidor de API usando FastAPI para expor as
|
| 8 |
+
# funcionalidades do Aduc Framework. Ele permite o controle programático
|
| 9 |
+
# do processo de geração de vídeo.
|
| 10 |
+
|
| 11 |
+
import yaml
|
| 12 |
+
import logging
|
| 13 |
+
import uuid
|
| 14 |
+
from typing import Dict
|
| 15 |
+
|
| 16 |
+
from fastapi import FastAPI, BackgroundTasks, HTTPException
|
| 17 |
+
|
| 18 |
+
# --- 1. IMPORTAÇÃO DO FRAMEWORK E SEUS TIPOS ---
|
| 19 |
+
import aduc_framework
|
| 20 |
+
from aduc_framework.types import GenerationState, PreProductionParams, ProductionParams
|
| 21 |
+
|
| 22 |
+
# --- CONFIGURAÇÃO INICIAL ---
|
| 23 |
+
logger = logging.getLogger(__name__)
|
| 24 |
+
|
| 25 |
+
# Cria a aplicação FastAPI
|
| 26 |
+
app = FastAPI(
|
| 27 |
+
title="ADUC-SDR Framework API",
|
| 28 |
+
description="API para orquestração de geração de vídeo coerente com IA.",
|
| 29 |
+
version="3.0.0"
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
# Carrega a configuração e inicializa uma instância SINGLETON do framework.
|
| 33 |
+
# O framework é pesado e deve ser carregado apenas uma vez na inicialização da API.
|
| 34 |
+
try:
|
| 35 |
+
with open("config.yaml", 'r') as f: config = yaml.safe_load(f)
|
| 36 |
+
WORKSPACE_DIR = config['application']['workspace_dir']
|
| 37 |
+
|
| 38 |
+
aduc = aduc_framework.create_aduc_instance(workspace_dir=WORKSPACE_DIR)
|
| 39 |
+
|
| 40 |
+
logger.info("API FastAPI inicializada e conectada ao Aduc Framework.")
|
| 41 |
+
except Exception as e:
|
| 42 |
+
logger.critical(f"ERRO CRÍTICO durante a inicialização da API: {e}", exc_info=True)
|
| 43 |
+
# A API não pode funcionar sem o framework, então saímos se falhar.
|
| 44 |
+
exit()
|
| 45 |
+
|
| 46 |
+
# --- ARMAZENAMENTO DE TAREFAS EM MEMÓRIA ---
|
| 47 |
+
# Em um ambiente de produção real, isso seria substituído por um banco de dados
|
| 48 |
+
# ou um cache como Redis para persistir o estado das tarefas.
|
| 49 |
+
tasks_state: Dict[str, GenerationState] = {}
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
# --- FUNÇÕES DE BACKGROUND ---
|
| 53 |
+
|
| 54 |
+
def run_production_in_background(task_id: str, params: ProductionParams):
|
| 55 |
+
"""
|
| 56 |
+
Função que executa a tarefa de produção demorada em segundo plano.
|
| 57 |
+
Ela opera na instância global 'aduc' para modificar seu estado interno.
|
| 58 |
+
"""
|
| 59 |
+
logger.info(f"Background task {task_id}: Iniciando produção de vídeo...")
|
| 60 |
+
try:
|
| 61 |
+
# A tarefa do framework modifica o estado interno da instância 'aduc'
|
| 62 |
+
_, _, final_state = aduc.task_produce_original_movie(params=params)
|
| 63 |
+
|
| 64 |
+
# Armazena o estado final e completo no nosso "banco de dados" de tarefas
|
| 65 |
+
tasks_state[task_id] = final_state
|
| 66 |
+
logger.info(f"Background task {task_id}: Produção de vídeo concluída com sucesso.")
|
| 67 |
+
except Exception as e:
|
| 68 |
+
logger.error(f"Background task {task_id}: Falha na produção. Erro: {e}", exc_info=True)
|
| 69 |
+
# Opcional: Atualizar o estado da tarefa com uma mensagem de erro.
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
# --- ENDPOINTS DA API ---
|
| 73 |
+
|
| 74 |
+
@app.post("/v1/pre-production", response_model=GenerationState, tags=["Workflow"])
|
| 75 |
+
async def start_pre_production(params: PreProductionParams):
|
| 76 |
+
"""
|
| 77 |
+
Inicia e executa a etapa de pré-produção (storyboard e keyframes).
|
| 78 |
+
|
| 79 |
+
Esta é uma chamada síncrona, pois a pré-produção é relativamente rápida.
|
| 80 |
+
Ela retorna o estado de geração completo após a conclusão.
|
| 81 |
+
"""
|
| 82 |
+
logger.info(f"API: Recebida solicitação de pré-produção com prompt: '{params.prompt[:30]}...'")
|
| 83 |
+
try:
|
| 84 |
+
_, _, updated_state = aduc.task_pre_production(params=params)
|
| 85 |
+
return updated_state
|
| 86 |
+
except Exception as e:
|
| 87 |
+
logger.error(f"API: Erro na pré-produção: {e}", exc_info=True)
|
| 88 |
+
raise HTTPException(status_code=500, detail=f"Erro interno durante a pré-produção: {e}")
|
| 89 |
+
|
| 90 |
+
@app.post("/v1/production", status_code=202, tags=["Workflow"])
|
| 91 |
+
async def start_production(params: ProductionParams, background_tasks: BackgroundTasks):
|
| 92 |
+
"""
|
| 93 |
+
Inicia a tarefa de produção de vídeo principal em segundo plano.
|
| 94 |
+
|
| 95 |
+
Esta chamada retorna imediatamente com um `task_id`. Use o endpoint
|
| 96 |
+
`/v1/status/{task_id}` para verificar o progresso e obter o resultado final.
|
| 97 |
+
"""
|
| 98 |
+
task_id = str(uuid.uuid4())
|
| 99 |
+
logger.info(f"API: Recebida solicitação de produção. Criando tarefa de background com ID: {task_id}")
|
| 100 |
+
|
| 101 |
+
# Armazena o estado atual (pré-produção) antes de iniciar a nova tarefa
|
| 102 |
+
tasks_state[task_id] = aduc.get_current_state()
|
| 103 |
+
|
| 104 |
+
# Adiciona a função demorada para ser executada em segundo plano
|
| 105 |
+
background_tasks.add_task(run_production_in_background, task_id, params)
|
| 106 |
+
|
| 107 |
+
return {"message": "Produção de vídeo iniciada em segundo plano.", "task_id": task_id}
|
| 108 |
+
|
| 109 |
+
@app.get("/v1/status/{task_id}", response_model=GenerationState, tags=["Workflow"])
|
| 110 |
+
async def get_task_status(task_id: str):
|
| 111 |
+
"""
|
| 112 |
+
Verifica o estado de uma tarefa de geração em andamento ou concluída.
|
| 113 |
+
"""
|
| 114 |
+
logger.info(f"API: Verificando status da tarefa {task_id}")
|
| 115 |
+
state = tasks_state.get(task_id)
|
| 116 |
+
if not state:
|
| 117 |
+
raise HTTPException(status_code=404, detail="ID de tarefa não encontrado.")
|
| 118 |
+
|
| 119 |
+
# Retorna o estado mais recente que temos para essa tarefa
|
| 120 |
+
return state
|
| 121 |
+
|
| 122 |
+
@app.get("/health", tags=["Infra"])
|
| 123 |
+
async def health_check():
|
| 124 |
+
"""
|
| 125 |
+
Endpoint simples para verificar se a API está online.
|
| 126 |
+
"""
|
| 127 |
+
return {"status": "ok"}
|
app_gradio.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# app_gradio.py
|
| 2 |
+
#
|
| 3 |
+
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
|
| 4 |
+
#
|
| 5 |
+
# Versão 3.0.0 (UI Head for Aduc Framework)
|
| 6 |
+
#
|
| 7 |
+
# Este arquivo implementa a interface de usuário com Gradio para o Aduc-Sdr.
|
| 8 |
+
# Ele atua como um cliente para o 'aduc_framework', que contém toda a
|
| 9 |
+
# lógica de negócio e orquestração.
|
| 10 |
+
|
| 11 |
+
import gradio as gr
|
| 12 |
+
import yaml
|
| 13 |
+
import logging
|
| 14 |
+
import os
|
| 15 |
+
import shutil
|
| 16 |
+
import time
|
| 17 |
+
import json
|
| 18 |
+
|
| 19 |
+
# --- 1. IMPORTAÇÃO DO FRAMEWORK E SEUS TIPOS ---
|
| 20 |
+
# A UI agora trata o Aduc-Sdr como um pacote que ela consome.
|
| 21 |
+
import aduc_framework
|
| 22 |
+
from aduc_framework.types import PreProductionParams, ProductionParams
|
| 23 |
+
|
| 24 |
+
# --- CUSTOM UI THEME E CONFIGURAÇÃO INICIAL ---
|
| 25 |
+
# (Esta seção permanece a mesma, pois é específica da aplicação)
|
| 26 |
+
|
| 27 |
+
cinematic_theme = gr.themes.Base(
|
| 28 |
+
primary_hue=gr.themes.colors.indigo,
|
| 29 |
+
secondary_hue=gr.themes.colors.purple,
|
| 30 |
+
neutral_hue=gr.themes.colors.slate,
|
| 31 |
+
font=(gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"),
|
| 32 |
+
).set(
|
| 33 |
+
body_background_fill="#111827", body_text_color="#E5E7EB",
|
| 34 |
+
button_primary_background_fill="linear-gradient(90deg, #4F46E5, #8B5CF6)",
|
| 35 |
+
button_primary_text_color="#FFFFFF", button_secondary_background_fill="#374151",
|
| 36 |
+
button_secondary_border_color="#4B5563", button_secondary_text_color="#E5E7EB",
|
| 37 |
+
block_background_fill="#1F2937", block_border_width="1px", block_border_color="#374151",
|
| 38 |
+
block_label_background_fill="#374151", block_label_text_color="#E5E7EB",
|
| 39 |
+
block_title_text_color="#FFFFFF", input_background_fill="#374151",
|
| 40 |
+
input_border_color="#4B5563", input_placeholder_color="#9CA3AF",
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
LOG_FILE_PATH = "aduc_log.txt"
|
| 44 |
+
if os.path.exists(LOG_FILE_PATH):
|
| 45 |
+
os.remove(LOG_FILE_PATH)
|
| 46 |
+
|
| 47 |
+
# Configuração de logging
|
| 48 |
+
log_format = '%(asctime)s - %(levelname)s - [%(name)s:%(funcName)s] - %(message)s'
|
| 49 |
+
root_logger = logging.getLogger()
|
| 50 |
+
root_logger.setLevel(logging.INFO)
|
| 51 |
+
# ... (código completo de configuração de logging) ...
|
| 52 |
+
logger = logging.getLogger(__name__)
|
| 53 |
+
|
| 54 |
+
# Carrega a configuração e inicializa o framework
|
| 55 |
+
try:
|
| 56 |
+
with open("config.yaml", 'r') as f: config = yaml.safe_load(f)
|
| 57 |
+
WORKSPACE_DIR = config['application']['workspace_dir']
|
| 58 |
+
|
| 59 |
+
# --- PONTO DE ENTRADA DO FRAMEWORK ---
|
| 60 |
+
aduc = aduc_framework.create_aduc_instance(workspace_dir=WORKSPACE_DIR)
|
| 61 |
+
|
| 62 |
+
logger.info("Interface Gradio inicializada e conectada ao Aduc Framework.")
|
| 63 |
+
except Exception as e:
|
| 64 |
+
logger.critical(f"ERRO CRÍTICO durante a inicialização: {e}", exc_info=True)
|
| 65 |
+
exit()
|
| 66 |
+
|
| 67 |
+
# --- 2. FUNÇÕES WRAPPER (CAMADA DE TRADUÇÃO UI <-> FRAMEWORK) ---
|
| 68 |
+
|
| 69 |
+
def run_pre_production_wrapper(prompt, num_keyframes, ref_files, resolution_str, duration_per_fragment, progress=gr.Progress()):
|
| 70 |
+
if not ref_files:
|
| 71 |
+
raise gr.Error("Por favor, forneça pelo menos uma imagem de referência.")
|
| 72 |
+
|
| 73 |
+
ref_paths = [aduc.process_image_for_story(f.name, 480, f"ref_processed_{i}.png") for i, f in enumerate(ref_files)]
|
| 74 |
+
|
| 75 |
+
params = PreProductionParams(
|
| 76 |
+
prompt=prompt,
|
| 77 |
+
num_keyframes=int(num_keyframes),
|
| 78 |
+
ref_paths=ref_paths,
|
| 79 |
+
resolution=int(resolution_str.split('x')[0]),
|
| 80 |
+
duration_per_fragment=duration_per_fragment
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
storyboard, final_keyframes, updated_state = aduc.task_pre_production(params, progress)
|
| 84 |
+
|
| 85 |
+
return updated_state.model_dump(), storyboard, final_keyframes, gr.update(visible=True, open=True)
|
| 86 |
+
|
| 87 |
+
def run_original_production_wrapper(current_state_dict, trim_percent, handler_strength, dest_strength, guidance_scale, stg_scale, steps, progress=gr.Progress()):
|
| 88 |
+
yield {
|
| 89 |
+
original_video_output: gr.update(value=None, visible=True, label="🎬 Produzindo seu filme..."),
|
| 90 |
+
final_video_output: gr.update(value=None, visible=True, label="🎬 Produção em progresso..."),
|
| 91 |
+
step4_accordion: gr.update(visible=False)
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
production_params = ProductionParams(
|
| 95 |
+
trim_percent=int(trim_percent),
|
| 96 |
+
handler_strength=handler_strength,
|
| 97 |
+
destination_convergence_strength=dest_strength,
|
| 98 |
+
guidance_scale=guidance_scale,
|
| 99 |
+
stg_scale=stg_scale,
|
| 100 |
+
inference_steps=int(steps)
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
final_video_path, latent_paths, updated_state = aduc.task_produce_original_movie(
|
| 104 |
+
params=production_params,
|
| 105 |
+
progress_callback=progress
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
updated_state_dict = updated_state.model_dump()
|
| 109 |
+
|
| 110 |
+
yield {
|
| 111 |
+
original_video_output: gr.update(value=final_video_path, label="✅ Filme Original Master"),
|
| 112 |
+
final_video_output: gr.update(value=final_video_path),
|
| 113 |
+
step4_accordion: gr.update(visible=True, open=True),
|
| 114 |
+
original_latents_paths_state: latent_paths,
|
| 115 |
+
original_video_path_state: final_video_path,
|
| 116 |
+
current_source_video_state: final_video_path,
|
| 117 |
+
generation_state_holder: updated_state_dict,
|
| 118 |
+
generation_data_output: updated_state_dict
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
def get_log_content():
|
| 122 |
+
try:
|
| 123 |
+
with open(LOG_FILE_PATH, "r", encoding="utf-8") as f:
|
| 124 |
+
return f.read()
|
| 125 |
+
except FileNotFoundError:
|
| 126 |
+
return "Arquivo de log ainda não criado. Inicie uma geração."
|
| 127 |
+
|
| 128 |
+
# --- 3. DEFINIÇÃO DA UI GRADIO ---
|
| 129 |
+
with gr.Blocks(theme=cinematic_theme, css="style.css") as demo:
|
| 130 |
+
|
| 131 |
+
generation_state_holder = gr.State(value={})
|
| 132 |
+
|
| 133 |
+
original_latents_paths_state = gr.State(value=None)
|
| 134 |
+
original_video_path_state = gr.State(value=None)
|
| 135 |
+
current_source_video_state = gr.State(value=None)
|
| 136 |
+
|
| 137 |
+
gr.Markdown("<h1>ADUC-SDR 🎬 - O Diretor de Cinema IA</h1>")
|
| 138 |
+
gr.Markdown("<p>Crie um filme completo com vídeo e áudio, orquestrado por uma equipe de IAs especialistas.</p>")
|
| 139 |
+
|
| 140 |
+
with gr.Row():
|
| 141 |
+
lang_selector = gr.Radio(["🇧🇷", "🇺🇸", "🇨🇳"], value="🇧🇷", label="Idioma / Language")
|
| 142 |
+
resolution_selector = gr.Radio(["480x480", "720x720", "960x960"], value="480x480", label="Resolução Base")
|
| 143 |
+
|
| 144 |
+
with gr.Accordion("Etapa 1: Roteiro e Cenas-Chave (Pré-Produção)", open=True) as step1_accordion:
|
| 145 |
+
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.")
|
| 146 |
+
ref_image_input = gr.File(label="Imagens de Referência", file_count="multiple", file_types=["image"])
|
| 147 |
+
with gr.Row():
|
| 148 |
+
num_keyframes_slider = gr.Slider(minimum=3, maximum=42, value=5, step=1, label="Número de Cenas-Chave")
|
| 149 |
+
duration_per_fragment_slider = gr.Slider(label="Duração de cada Clipe (s)", info="Duração alvo para cada fragmento de vídeo.", minimum=2.0, maximum=10.0, value=4.0, step=0.1)
|
| 150 |
+
storyboard_and_keyframes_button = gr.Button("Gerar Roteiro e Keyframes", variant="primary")
|
| 151 |
+
storyboard_output = gr.JSON(label="Roteiro Gerado (Storyboard)")
|
| 152 |
+
keyframe_gallery = gr.Gallery(label="Galeria de Cenas-Chave (Keyframes)", visible=True, object_fit="contain", height="auto", type="filepath")
|
| 153 |
+
|
| 154 |
+
with gr.Accordion("Etapa 3: Produção do Vídeo Original", open=False, visible=False) as step3_accordion:
|
| 155 |
+
# Aqui omiti a definição detalhada dos sliders para brevidade, mas eles existem
|
| 156 |
+
trim_percent_slider = gr.Slider(minimum=10, maximum=90, value=50, step=5, label="Poda Causal (%)")
|
| 157 |
+
handler_strength = gr.Slider(label="Força do Déjà-Vu", minimum=0.0, maximum=1.0, value=0.5, step=0.05)
|
| 158 |
+
dest_strength = gr.Slider(label="Força da Âncora Final", minimum=0.0, maximum=1.0, value=0.75, step=0.05)
|
| 159 |
+
guidance_scale_slider = gr.Slider(minimum=1.0, maximum=10.0, value=2.0, step=0.1, label="Escala de Orientação")
|
| 160 |
+
stg_scale_slider = gr.Slider(minimum=0.0, maximum=1.0, value=0.025, step=0.005, label="Escala STG")
|
| 161 |
+
inference_steps_slider = gr.Slider(minimum=10, maximum=50, value=20, step=1, label="Passos de Inferência")
|
| 162 |
+
produce_original_button = gr.Button("🎬 Produzir Vídeo Original", variant="primary")
|
| 163 |
+
original_video_output = gr.Video(label="Filme Original Master", visible=False, interactive=False)
|
| 164 |
+
|
| 165 |
+
with gr.Accordion("Etapa 4: Pós-Produção (Opcional)", open=False, visible=False) as step4_accordion:
|
| 166 |
+
gr.Markdown("Aplique efeitos de melhoria ao vídeo mais recente.")
|
| 167 |
+
# ... (Componentes de pós-produção aqui) ...
|
| 168 |
+
|
| 169 |
+
with gr.Accordion("🧬 DNA Digital da Geração (JSON)", open=False) as data_accordion:
|
| 170 |
+
generation_data_output = gr.JSON(label="Estado de Geração Completo")
|
| 171 |
+
|
| 172 |
+
final_video_output = gr.Video(label="Filme Final (Resultado da Última Etapa)", visible=False, interactive=False)
|
| 173 |
+
|
| 174 |
+
with gr.Accordion("📝 Log de Geração (Detalhado)", open=False) as log_accordion:
|
| 175 |
+
log_display = gr.Textbox(label="Log da Sessão", lines=20, interactive=False, autoscroll=True)
|
| 176 |
+
update_log_button = gr.Button("Atualizar Log")
|
| 177 |
+
|
| 178 |
+
# --- 4. CONEXÕES DE EVENTOS DA UI ---
|
| 179 |
+
|
| 180 |
+
storyboard_and_keyframes_button.click(
|
| 181 |
+
fn=run_pre_production_wrapper,
|
| 182 |
+
inputs=[prompt_input, num_keyframes_slider, ref_image_input, resolution_selector, duration_per_fragment_slider],
|
| 183 |
+
outputs=[generation_state_holder, storyboard_output, keyframe_gallery, step3_accordion]
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
produce_original_button.click(
|
| 187 |
+
fn=run_original_production_wrapper,
|
| 188 |
+
inputs=[
|
| 189 |
+
generation_state_holder,
|
| 190 |
+
trim_percent_slider, handler_strength, dest_strength,
|
| 191 |
+
guidance_scale_slider, stg_scale_slider, inference_steps_slider
|
| 192 |
+
],
|
| 193 |
+
outputs=[
|
| 194 |
+
original_video_output, final_video_output, step4_accordion,
|
| 195 |
+
original_latents_paths_state, original_video_path_state, current_source_video_state,
|
| 196 |
+
generation_state_holder, generation_data_output
|
| 197 |
+
]
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
generation_state_holder.change(
|
| 201 |
+
fn=lambda state: state,
|
| 202 |
+
inputs=generation_state_holder,
|
| 203 |
+
outputs=generation_data_output
|
| 204 |
+
)
|
| 205 |
+
|
| 206 |
+
update_log_button.click(fn=get_log_content, inputs=[], outputs=[log_display])
|
| 207 |
+
|
| 208 |
+
# --- 5. INICIALIZAÇÃO DA APLICAÇÃO ---
|
| 209 |
+
if __name__ == "__main__":
|
| 210 |
+
if os.path.exists(WORKSPACE_DIR):
|
| 211 |
+
logger.info(f"Limpando workspace anterior em: {WORKSPACE_DIR}")
|
| 212 |
+
shutil.rmtree(WORKSPACE_DIR)
|
| 213 |
+
os.makedirs(WORKSPACE_DIR)
|
| 214 |
+
|
| 215 |
+
logger.info("Aplicação Gradio iniciada. Lançando interface...")
|
| 216 |
+
demo.queue().launch()
|
run.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import uvicorn
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
def main():
|
| 6 |
+
parser = argparse.ArgumentParser(description="Executor do Aduc-Sdr")
|
| 7 |
+
parser.add_argument("mode", choices=["gradio", "api"], help="Modo de execução: 'gradio' para a UI, 'api' para o servidor FastAPI.")
|
| 8 |
+
args = parser.parse_args()
|
| 9 |
+
|
| 10 |
+
if args.mode == "gradio":
|
| 11 |
+
print("Iniciando a interface Gradio...")
|
| 12 |
+
# Importa e executa a lógica de lançamento que está no final de app_gradio.py
|
| 13 |
+
from app_gradio import demo
|
| 14 |
+
demo.queue().launch()
|
| 15 |
+
elif args.mode == "api":
|
| 16 |
+
print("Iniciando o servidor FastAPI em http://127.0.0.1:8000")
|
| 17 |
+
uvicorn.run("app_api:app", host="127.0.0.1", port=8000, reload=True)
|
| 18 |
+
|
| 19 |
+
if __name__ == "__main__":
|
| 20 |
+
main()
|