File size: 7,236 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
138
139
140
141
142
143
# aduc_framework/engineers/planner_5D.py
#
# Versão 1.1.0 (Diretor de Produção Alinhado)
# Copyright (C) August 4, 2025  Carlos Rodrigues dos Santos
#
# - Versão finalizada e alinhada com os especialistas Deformes v12+ e VaeManager v2.
# - Cria as Ordens de Serviço (Jobs) usando as definições mais recentes de `types.py`.
# - Delega os cálculos técnicos (ex: poda de frames) para os especialistas,
#   focando-se na orquestração de alto nível e no gerenciamento do loop de feedback visual.

import logging
import os
from typing import Generator, Dict, Any

# Importa os especialistas que serão dirigidos
from ..engineers.deformes3D import deformes3d_engine_singleton as Deformes3D
from ..engineers.deformes4D import deformes4d_engine_singleton as Deformes4D
from ..tools.video_encode_tool import video_encode_tool_singleton as VideoTool

# Importa as estruturas de dados (o "DNA" e as "Ordens de Serviço")
from ..types import (
    GenerationState,
    KeyframeGenerationJob,
    VideoGenerationJob,
    KeyframeData,
    VideoData
)

logger = logging.getLogger(__name__)

class Planner5D:
    """
    Atua como o Diretor de Produção. Orquestra a geração de keyframes e vídeos
    cena a cena, implementando um loop de feedback para garantir continuidade visual.
    """
    def __init__(self):
        self.dna: GenerationState | None = None
        self.workspace: str | None = None
        self.production_params: Dict[str, Any] = {}
        self.pre_prod_params: Dict[str, Any] = {}

    def produce_movie_by_scene(self, dna: GenerationState) -> Generator[GenerationState, None, None]:
        """
        Ponto de entrada para a produção do filme. Executa o loop de produção cena a cena.
        """
        self.dna = dna
        self.workspace = self.dna.workspace_dir

        if not (self.dna.parametros_geracao and self.dna.parametros_geracao.pre_producao and self.dna.parametros_geracao.producao):
             raise ValueError("Parâmetros de pré-produção ou produção ausentes no DNA. O processo não pode continuar.")
        
        self.pre_prod_params = self.dna.parametros_geracao.pre_producao.model_dump()
        self.production_params = self.dna.parametros_geracao.producao.model_dump()

        Deformes3D.initialize(self.workspace)
        Deformes4D.initialize(self.workspace)

        all_final_scene_clips = []
        last_scene_final_frame_path = None

        self.dna.chat_history.append({"role": "Planner5D", "content": "Ok, storyboard recebido. Iniciando a produção do filme, cena por cena..."})
        yield self.dna

        for scene_idx, scene in enumerate(self.dna.storyboard_producao):
            scene_title = scene.titulo_cena or f"Cena {scene.id_cena}"
            logger.info(f"--- PLANNER 5D: Iniciando Cena {scene.id_cena}: '{scene_title}' ---")
            self.dna.chat_history.append({"role": "Planner5D", "content": f"Preparando para filmar a Cena {scene.id_cena}: '{scene_title}'..."})
            yield self.dna

            # ETAPA 1: GERAÇÃO DE KEYFRAMES
            reference_paths = [ref.caminho for ref in self.dna.midias_referencia if ref.caminho]
            ref_id_to_path_map = {i: path for i, path in enumerate(reference_paths)}

            keyframe_job = KeyframeGenerationJob(
                storyboard=[ato.NARRATIVA_VISUAL for ato in scene.atos],
                global_prompt=self.dna.texto_global_historia or "",
                ref_image_paths=reference_paths,
                ref_id_to_path_map=ref_id_to_path_map,
                available_ref_ids=list(ref_id_to_path_map.keys()),
                keyframe_prefix=f"s{scene.id_cena:02d}",
                params=self.pre_prod_params
            )
            generated_keyframes_data = Deformes3D.generate_keyframes_from_job(keyframe_job)
            self.dna.storyboard_producao[scene_idx].keyframes = [KeyframeData(**kf) for kf in generated_keyframes_data]
            self.dna.chat_history.append({"role": "Planner5D", "content": f"Cena {scene.id_cena}: {len(generated_keyframes_data)} keyframes criados."})
            yield self.dna

            
        for scene_idx, scene in enumerate(self.dna.storyboard_producao):
            scene_title = scene.titulo_cena or f"Cena {scene.id_cena}"
            logger.info(f"--- PLANNER 5D: Iniciando Gravação de Cena  {scene.id_cena}: '{scene_title}' ---")
            self.dna.chat_history.append({"role": "Planner5D", "content": f"Filmando a Cena {scene.id_cena}: '{scene_title}'..."})
            yield self.dna
            
            # ETAPA 2: GERAÇÃO DO VÍDEO
            if len(generated_keyframes_data) < 1:
                logger.warning(f"Cena {scene.id_cena} tem menos de 1 keyframes. Pulando geração de vídeo.")
                self.dna.chat_history.append({"role": "Planner5D", "content": f"AVISO: Cena {scene.id_cena} não pôde ser filmada (keyframes insuficientes)."})
                yield self.dna
                continue

            self.dna.chat_history.append({"role": "Planner5D", "content": f"Cena {scene.id_cena}: Luz, câmera, ação! Gerando o clipe de vídeo..."})
            yield self.dna

            # Criando a Ordem de Serviço para Deformes4D com os parâmetros de alto nível
            video_job = VideoGenerationJob(
                scene_id=scene.id_cena,
                global_prompt=self.dna.texto_global_historia or "",
                storyboard=[ato.NARRATIVA_VISUAL for ato in scene.atos],
                keyframe_paths=[kf['caminho_pixel'] for kf in generated_keyframes_data],
                **self.pre_prod_params,
                **self.production_params
            )

            video_result = Deformes4D.generate_movie_clip_from_job(video_job)
            scene_clip_path = video_result.get("final_path")

            if scene_clip_path:
                all_final_scene_clips.append(scene_clip_path)
                video_data = video_result.get("video_data", {})
                self.dna.storyboard_producao[scene_idx].video_gerado = VideoData(**video_data)
                self.dna.caminho_filme_final_bruto = scene_clip_path
                self.dna.chat_history.append({"role": "Planner5D", "content": f"Cena {scene.id_cena} filmada! O resultado será usado como ponto de partida para a próxima cena."})
                yield self.dna

        # ETAPA FINAL: MONTAGEM DO FILME
        if not all_final_scene_clips:
            self.dna.chat_history.append({"role": "Planner5D", "content": "Produção falhou. Nenhum clipe de vídeo foi gerado."})
            yield self.dna
            return

        self.dna.chat_history.append({"role": "Planner5D", "content": "Todas as cenas foram filmadas. Iniciando a montagem final do filme..."})
        yield self.dna
        
        final_movie_path = os.path.join(self.workspace, "filme_final.mp4")
        final_movie_path = VideoTool.concatenate_videos(all_final_scene_clips, final_movie_path, self.workspace)
        
        self.dna.caminho_filme_final_bruto = final_movie_path
        self.dna.chat_history.append({"role": "Maestro", "content": "Produção concluída! O filme final está pronto para ser assistido."})
        yield self.dna

# --- Instância Singleton ---
planner_5d_singleton = Planner5D()