File size: 12,296 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# aduc_framework/engineers/deformes4D.py
#
# Versão 16.0.0 (Integração de Lógica ADUC-SDR Canônica)
# Copyright (C) August 4, 2025  Carlos Rodrigues dos Santos
#
# - Refatora a lógica de geração de vídeo monolítica para o padrão de
#   Especialista ADUC, operando dentro da classe Deformes4DEngine.
# - Integra o núcleo lógico de Poda Causal, Eco e Déjà-Vu (linhas 187-315
#   do arquivo original) de forma intacta.
# - Substitui as chamadas de baixo nível por delegações aos managers
#   especializados (VaeManager, LtxManager, VideoEncodeTool).
# - O método principal agora aceita um `VideoGenerationJob` do Planner5D.

import os
import time
import torch
import logging
from PIL import Image
import gc
from typing import Dict, Any

# Importa os managers e especialistas da arquitetura ADUC
from ..managers.ltx_manager import ltx_manager_singleton
from ..managers.vae_manager import vae_manager_singleton
from ..tools.video_encode_tool import video_encode_tool_singleton
from ..types import LatentConditioningItem, VideoGenerationJob
from ..engineers.composer import composer_singleton as Composer # Usado para decisões cinematográficas

logger = logging.getLogger(__name__)

class Deformes4DEngine:
    """
    Implementa a Câmera (Ψ) e o Destilador (Δ) da arquitetura ADUC-SDR.
    É responsável pela execução da geração de fragmentos de vídeo e pela
    extração dos contextos causais (Eco e Déjà-Vu).
    """
    def __init__(self):
        self.workspace_dir: str | None = None
        self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
        logger.info("Especialista Deformes4D (Executor ADUC-SDR: Câmera Ψ e Destilador Δ) inicializado.")

    def initialize(self, workspace_dir: str):
        if self.workspace_dir and self.workspace_dir == workspace_dir:
            return
        self.workspace_dir = workspace_dir
        os.makedirs(self.workspace_dir, exist_ok=True)
        logger.info(f"Executor de Cena 4D inicializado com workspace: {self.workspace_dir}.")

    def _quantize_to_multiple(self, n, m):
        if m == 0: return n
        quantized = int(round(n / m) * m)
        return m if n > 0 and quantized == 0 else quantized

    # O método principal que o Planner5D chamará
    def generate_movie_clip_from_job(self, job: VideoGenerationJob) -> Dict[str, Any]:
        if not self.workspace_dir:
            raise RuntimeError("Deformes4DEngine não foi inicializado.")

        # Extrai os parâmetros da Ordem de Serviço
        keyframes = job.keyframe_paths
        global_prompt = job.global_prompt
        storyboard = job.storyboard
        seconds_per_fragment = job.duration_per_fragment
        trim_percent = job.trim_percent
        handler_strength = job.handler_strength
        destination_convergence_strength = job.destination_convergence_strength
        video_resolution = job.resolution
        
        # Este método agora encapsula a lógica do seu 'generate_full_movie'
        return self._execute_generation_logic(
            keyframes, global_prompt, storyboard, seconds_per_fragment,
            trim_percent, handler_strength, destination_convergence_strength,
            video_resolution
        )

    def _execute_generation_logic(self, keyframes: list, global_prompt: str, storyboard: list, 
                                  seconds_per_fragment: float, trim_percent: int,
                                  handler_strength: float, destination_convergence_strength: float, 
                                  video_resolution: int):

        #####(atenção Gemini .. nao apagar ou modificar nada apartir dessa linha) #####
        # manter o conteudo original da linha 187 ate 315 
        
        # 1. Definição dos Parâmetros da Geração com base na Tese
        FPS = 32
        FRAMES_PER_LATENT_CHUNK = 8
        ECO_LATENT_CHUNKS = 2
        
        total_frames_brutos = self._quantize_to_multiple(int(seconds_per_fragment * FPS), FRAMES_PER_LATENT_CHUNK)
        total_latents_brutos = total_frames_brutos // FRAMES_PER_LATENT_CHUNK

        frames_a_podar = 15 #self._quantize_to_multiple(int(total_frames_brutos * (trim_percent / 100)), FRAMES_PER_LATENT_CHUNK)
        latents_a_podar = frames_a_podar // FRAMES_PER_LATENT_CHUNK

        if total_latents_brutos <= latents_a_podar:
            #raise gr.Error(f"A porcentagem de poda ({trim_percent}%) é muito alta. Reduza-a ou aumente a duração.")
            raise ValueError(f"A porcentagem de poda ({trim_percent}%) é muito alta. Reduza-a ou aumente a duração.")

        DEJAVU_FRAME_TARGET = 23
        DESTINATION_FRAME_TARGET = total_frames_brutos -8
        
        logger.info("--- CONFIGURAÇÃO DA GERAÇÃO ADUC-SDR ---")
        logger.info(f"Total de Latents por Geração Exploratória (V_bruto): {total_latents_brutos} ({total_frames_brutos} frames)")
        logger.info(f"Latents a serem descartados (Poda Causal): {latents_a_podar} ({frames_a_podar} frames)")
        logger.info(f"Chunks Latentes do Eco Causal (C): {ECO_LATENT_CHUNKS}")
        logger.info(f"Frame alvo do Déjà-Vu (D): {DEJAVU_FRAME_TARGET}")
        logger.info(f"Frame alvo do Destino (K): {DESTINATION_FRAME_TARGET}")
        logger.info("------------------------------------------")

        # 2. Inicialização do Estado
        base_ltx_params = {"guidance_scale": 1.0, "stg_scale": 0.005, "rescaling_scale": 0.05, "num_inference_steps": 4, "image_cond_noise_scale": 0.05}
        keyframe_paths = [item[0] if isinstance(item, tuple) else item for item in keyframes]
        video_clips_paths, story_history = [], ""
        target_resolution_tuple = (video_resolution, video_resolution) 
        
        eco_latent_for_next_loop = None
        dejavu_latent_for_next_loop = None
        
        if len(keyframe_paths) < 1:
            #raise gr.Error(f"A geração requer no mínimo 2 keyframes. Você forneceu {len(keyframe_paths)}.")
            raise ValueError(f"A geração requer no mínimo 2 keyframes. Você forneceu {len(keyframe_paths)}.")
        
        num_transitions_to_generate = len(keyframe_paths) -1
        
        # 3. Loop Principal de Geração de Fragmentos
        for i in range(num_transitions_to_generate):
            fragment_index = i + 1
            logger.info(f"--- INICIANDO FRAGMENTO {fragment_index}/{num_transitions_to_generate} ---")
            
            # 3.1. Consulta ao Maestro (Γ) para obter a intenção (Pᵢ)
            start_keyframe_path = keyframe_paths[i]
            
            if i+1 < num_transitions_to_generate:
                destination_keyframe_path = keyframe_paths[i + 1]
            else:
                destination_keyframe_path = keyframe_paths[i]
        

            decision = Composer.execute_cognitive_task(
                task_id="TASK_08_IMPROVISE_CINEMATIC_PROMPT_ATO",
                template_data={"current_act_narrative": storyboard[i]},
                images=[Image.open(start_keyframe_path), Image.open(destination_keyframe_path)]
            )
            motion_prompt = decision.strip()
            story_history += f"\n- Ato {fragment_index}: {motion_prompt}"

            # 3.2. Montagem das Âncoras para a Fórmula Canônica Ψ({C, D, K}, P)
            conditioning_items = []
            logger.info("  [Ψ.1] Montando âncoras causais...")
            
            if eco_latent_for_next_loop is None:
               logger.info("    - Primeiro fragmento: Usando Keyframe inicial como âncora de partida.")
               img_start = Image.open(start_keyframe_path).convert("RGB")
               img_dest = Image.open(destination_keyframe_path).convert("RGB")
               start_latent, dest_latent = vae_manager_singleton.encode_batch([img_start, img_dest], target_resolution_tuple)
               conditioning_items.append(LatentConditioningItem(start_latent, 0, 1.0))
            else:
               logger.info("    - Âncora 1: Eco Causal (C) - Herança do passado.")
               conditioning_items.append(LatentConditioningItem(eco_latent_for_next_loop, 0, 1.0))
               logger.info("    - Âncora 2: Déjà-Vu (D) - Memória de um futuro idealizado.")
               conditioning_items.append(LatentConditioningItem(dejavu_latent_for_next_loop, 23, 0.5))
               img_dest = Image.open(destination_keyframe_path).convert("RGB")
               dest_latent = vae_manager_singleton.encode_batch([img_dest], target_resolution_tuple)[0]
            
            logger.info("    - Âncora 3: Destino (K) - Âncora geométrica/narrativa.")
            conditioning_items.append(LatentConditioningItem(dest_latent, DESTINATION_FRAME_TARGET, 1.0))

            # 3.3. Execução da Câmera (Ψ): Geração Exploratória para criar V_bruto
            logger.info(f"  [Ψ.2] Câmera (Ψ) executando a geração exploratória de {total_latents_brutos} chunks latentes...")
            current_ltx_params = {**base_ltx_params, "motion_prompt": motion_prompt}
            latents_brutos, _ = ltx_manager_singleton.generate_latent_fragment(
                height=video_resolution, width=video_resolution,
                conditioning_items_data=conditioning_items,
                video_total_frames=total_frames_brutos+1,
                **current_ltx_params
            )
            
            # 3.4. Execução do Destilador (Δ): Implementação do Ciclo de Poda Causal
            logger.info(f"  [Δ] Shape do tensor bruto para vídeo final: {latents_brutos.shape}")


            #latents_video = latents_brutos[:, :, :-1, :, :]
            
            
            # --- Workaround empírico preservado ---
            eco_latent_for_next_loop = latents_brutos[:, :, -5:-2, :, :].clone()
            dejavu_latent_for_next_loop = latents_brutos[:, :, -1:, :, :].clone()
            

            latents_video = latents_brutos[:, :, :-4, :, :].clone()

            logger.info(f"  [Δ] Shape do tensor video para vídeo final: {latents_video.shape}")
         
            
            #if i+1 < num_transitions_to_generate:
            #latents_video = latents_video[:, :, :-4, :, :]
            #    logger.info(f"@@@@@@@@@@# Nao é ULTIMO poda -1")
            #    logger.info(f"  [Δ] -1 Shape do tensor video para vídeo final: {latents_video.shape}")
         

            #if i > 0:
            #    latents_video = latents_video[:, :, 2:, :, :]
            #    logger.info(f"@@@@@@@@@@# nao é o primeiro poda 1")
            #    logger.info(f"  [Δ] 1 Shape do tensor video para vídeo final: {latents_video.shape}")
         
            
            
            logger.info(f"  [Δ] Shape do tensor para vídeo final: {latents_video.shape}")
            logger.info(f"    - (Δ.1) Déjà-Vu (D) destilado. Shape: {dejavu_latent_for_next_loop.shape if dejavu_latent_for_next_loop is not None else 'N/A'}")
            logger.info(f"    - (Δ.2) Eco Causal (C) extraído. Shape: {eco_latent_for_next_loop.shape if eco_latent_for_next_loop is not None else 'N/A'}")

            # 3.5. Renderização e Armazenamento do Fragmento Final
            base_name = f"fragment_{fragment_index}_{int(time.time())}"
            pixel_tensor = vae_manager_singleton.decode(latents_video)
            video_path = os.path.join(self.workspace_dir, f"{base_name}.mp4")
            video_encode_tool_singleton.save_video_from_tensor(pixel_tensor, video_path, fps=FPS)
            video_clips_paths.append(video_path)
            logger.info(f"--- FRAGMENTO {fragment_index} FINALIZADO E SALVO EM: {video_path} ---")

        # 4. Montagem Final do Filme
        final_movie_path = os.path.join(self.workspace_dir, f"final_movie_silent_{int(time.time())}.mp4")
        final_movie_path = video_encode_tool_singleton.concatenate_videos(video_clips_paths, final_movie_path, self.workspace_dir)
        
        #####(atenção Gemini .. nao apagar ou modificar nada acima dessa linha) #####
    
        logger.info(f"Filme completo salvo em: {final_movie_path}")
        # Retorna o resultado no formato esperado pelo Planner5D
        return {
            "final_path": final_movie_path,
            "video_data": {"id": 0, "caminho_pixel": final_movie_path} # ID 0 para o filme completo
        }

# --- Instância Singleton ---
deformes4d_engine_singleton = Deformes4DEngine()