File size: 6,414 Bytes
fb56537
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# aduc_framework/aduc_sdr.py
#
# Versão 16.0.0 (Maestro de Produção Integrado)
# - Implementa o método `task_produce_movie` para delegar a produção de vídeo
#   ao especialista `Planner5D`, ativando o pipeline de produção iterativo.
# - Atua como a ponte final entre a interface do usuário (UI) e o complexo
#   fluxo de trabalho de geração de vídeo cena a cena.
# - Utiliza o sistema de `yield from` para transmitir de forma eficiente as
#   atualizações de estado do `Planner5D` de volta para a UI.

import logging
import os
from typing import Generator

# Importa os especialistas de alto nível (Engenheiros)
from .engineers.composer_2D import composer_2d_singleton as Composer2D
from .engineers.planner_5D import planner_5d_singleton as Planner5D

# Importa as estruturas de dados (o DNA Digital)
from .types import GenerationState, PreProductionParams, ProductionParams, MediaRef
from .director import AducDirector

logger = logging.getLogger(__name__)

class AducSdr:
    """
    O Maestro do framework ADUC-SDR. Orquestra os especialistas (Engineers)
    e gerencia o fluxo de dados através do Diretor (estado persistente).
    """
    def __init__(self, workspace_root: str):
        self.workspace_root = workspace_root
        self.director: AducDirector | None = None
        self.composer_2d = Composer2D
        self.planner_5d = Planner5D
        logger.info("ADUC-SDR Maestro (Arquitetura V2) inicializado e pronto.")

    def load_project(self, project_name: str):
        """Carrega um projeto existente ou cria um novo."""
        project_path = os.path.join(self.workspace_root, project_name)
        self.director = AducDirector(project_path=project_path)
        logger.info(f"Projeto '{project_name}' carregado no Diretor.")

    def _ensure_project_loaded(self):
        """Garante que um projeto foi carregado antes de executar tarefas."""
        if not self.director:
            raise RuntimeError("Nenhum projeto foi carregado. Chame `aduc.load_project(project_name)` primeiro.")

    def get_current_state(self) -> GenerationState:
        """Retorna o estado completo e atual do projeto."""
        self._ensure_project_loaded()
        return self.director.get_full_state()

    def process_image_for_story(self, image_path: str, filename: str) -> str:
        """Processa uma imagem de referência para um formato padrão."""
        self._ensure_project_loaded()
        from PIL import Image
        size = 480; quality = 40
        img = Image.open(image_path).convert("RGB"); img.thumbnail((size, size), Image.Resampling.LANCZOS)
        background = Image.new('RGB', (size, size), (0, 0, 0)); img_w, img_h = img.size; offset = ((size - img_w) // 2, (size - img_h) // 2)
        background.paste(img, offset)
        processed_path = os.path.join(self.director.project_path, filename)
        background.save(processed_path, 'JPEG', quality=quality)
        return processed_path

    def _process_and_yield_updates(self, generator: Generator[GenerationState, None, None]):
        """
        Loop genérico para processar atualizações de um especialista, salvar o estado
        e repassar para a UI.
        """
        for updated_dna in generator:
            self.director.load_state_from_dict(updated_dna.model_dump())

            if self.director.state.should_checkpoint():
                checkpoint_dir = os.path.join(self.director.project_path, "checkpoints")
                path = self.director.state.create_checkpoint(checkpoint_dir)
                logger.info(f"Checkpoint do projeto salvo em: {path}")

            self.director.save_state()
            yield self.director.get_full_state()

    def task_run_story_and_keyframes(self, params: PreProductionParams) -> Generator[GenerationState, None, None]:
        """
        Orquestra a pré-produção (Fase 1), delegando ao Composer2D para criar o storyboard.
        """
        self._ensure_project_loaded()
        logger.info("Maestro: Iniciando Pré-Produção (Storyboard) com o Composer2D...")
        
        initial_state = self.director.get_full_state()
        initial_state.parametros_geracao.pre_producao = params
        initial_state.midias_referencia = [
            MediaRef(id=i, tag=f"<IMG{i}>", caminho=path)
            for i, path in enumerate(params.ref_paths)
        ]
        
        initial_state.texto_global_historia = None
        initial_state.ativos_catalogados = None
        initial_state.storyboard_producao = []
        initial_state.chat_history.append({
            "role": "Sistema",
            "content": f"Iniciando pré-produção. {len(params.ref_paths)} imagens de referência foram tageadas para uso pela IA."
        })

        pre_production_generator = self.composer_2d.compose_storyboard(initial_state)
        
        yield from self._process_and_yield_updates(pre_production_generator)
        logger.info("Maestro: Pré-Produção (Fase 1) concluída.")

    def task_produce_movie(self, params: ProductionParams) -> Generator[GenerationState, None, None]:
        """
        Orquestra a produção completa do filme (Fase 2), delegando ao Planner5D.
        """
        self._ensure_project_loaded()
        logger.info("Maestro: Iniciando Produção de Vídeo com o Planner5D...")

        # 1. Obter o estado atual, que já contém o storyboard da pré-produção.
        current_state = self.director.get_full_state()

        # 2. Atualizar o estado com os novos parâmetros de produção vindos da UI.
        if current_state.parametros_geracao:
            current_state.parametros_geracao.producao = params
        
        current_state.chat_history.append({
            "role": "Sistema",
            "content": f"Parâmetros de produção recebidos. O Diretor de Produção (Planner5D) está assumindo o controle."
        })
        
        # Salva os parâmetros no dna.json antes de iniciar o processo longo.
        self.director.save_state()

        # 3. Chamar o especialista Planner5D com o estado atualizado.
        # Ele agora irá controlar o loop de Deformes3D e Deformes4D.
        production_generator = self.planner_5d.produce_movie_by_scene(current_state)

        # 4. Processar as atualizações, salvar o estado e repassar para a UI em tempo real.
        yield from self._process_and_yield_updates(production_generator)

        logger.info("Maestro: Produção de vídeo (Fase 2) concluída.")