Carlexxx commited on
Commit
b7222c6
·
verified ·
1 Parent(s): ffa9bff

Delete app-v1.py

Browse files
Files changed (1) hide show
  1. app-v1.py +0 -301
app-v1.py DELETED
@@ -1,301 +0,0 @@
1
- # --- app.py (O Painel de Controle do Maestro - Depuração Focada) ---
2
- # By Carlex & Gemini & DreamO
3
-
4
- # ... (importações e inicializações inalteradas) ...
5
- import gradio as gr
6
- import torch
7
- import os
8
- import yaml
9
- from PIL import Image
10
- import shutil
11
- import gc
12
- import subprocess
13
- import math
14
- import google.generativeai as genai
15
- import numpy as np
16
- import imageio
17
- from pathlib import Path
18
- import huggingface_hub
19
- import json
20
-
21
- from inference import create_ltx_video_pipeline, load_image_to_tensor_with_resize_and_crop, seed_everething, calculate_padding
22
- from ltx_video.pipelines.pipeline_ltx_video import ConditioningItem
23
- from dreamo_helpers import dreamo_generator_singleton
24
-
25
- # ... (configurações e constantes inalteradas) ...
26
- config_file_path = "configs/ltxv-13b-0.9.8-distilled.yaml"
27
- with open(config_file_path, "r") as file:
28
- PIPELINE_CONFIG_YAML = yaml.safe_load(file)
29
-
30
- LTX_REPO = "Lightricks/LTX-Video"
31
- models_dir = "downloaded_models_gradio_cpu_init"
32
- Path(models_dir).mkdir(parents=True, exist_ok=True)
33
- WORKSPACE_DIR = "aduc_workspace"
34
- GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
35
-
36
- VIDEO_WIDTH = 720
37
- VIDEO_HEIGHT = 720
38
- VIDEO_FPS = 24
39
- VIDEO_DURATION_SECONDS = 4
40
- VIDEO_TOTAL_FRAMES = VIDEO_DURATION_SECONDS * VIDEO_FPS
41
-
42
- print("Baixando e criando pipelines LTX na CPU...")
43
- distilled_model_actual_path = huggingface_hub.hf_hub_download(repo_id=LTX_REPO, filename=PIPELINE_CONFIG_YAML["checkpoint_path"], local_dir=models_dir, local_dir_use_symlinks=False)
44
- pipeline_instance = create_ltx_video_pipeline(ckpt_path=distilled_model_actual_path, precision=PIPELINE_CONFIG_YAML["precision"], text_encoder_model_name_or_path=PIPELINE_CONFIG_YAML["text_encoder_model_name_or_path"], sampler=PIPELINE_CONFIG_YAML["sampler"], device='cpu')
45
- print("Modelos LTX prontos (na CPU).")
46
-
47
- # --- Ato 3: As Partituras dos Músicos (Funções) ---
48
-
49
- # ... (get_storyboard_from_director e run_keyframe_generation inalterados) ...
50
- def get_storyboard_from_director(num_fragments: int, prompt: str, initial_image_path: str, progress=gr.Progress()):
51
- progress(0.5, desc="[Diretor Gemini] Criando o storyboard...")
52
- if not initial_image_path: raise gr.Error("Por favor, forneça uma imagem de referência inicial.")
53
- if not GEMINI_API_KEY: raise gr.Error("Chave da API Gemini não configurada!")
54
- genai.configure(api_key=GEMINI_API_KEY)
55
- try:
56
- script_dir = os.path.dirname(os.path.abspath(__file__))
57
- prompt_file_path = os.path.join(script_dir, "prompts", "director_storyboard_v2.txt")
58
- with open(prompt_file_path, "r", encoding="utf-8") as f: template = f.read()
59
- except FileNotFoundError: raise gr.Error(f"Arquivo de prompt não encontrado em '{prompt_file_path}'!")
60
- director_prompt = template.format(user_prompt=prompt, num_fragments=int(num_fragments))
61
- model = genai.GenerativeModel('gemini-2.5-flash')
62
- img = Image.open(initial_image_path)
63
- response = model.generate_content([director_prompt, img])
64
- try:
65
- cleaned_response = response.text.strip().replace("```json", "").replace("```", "")
66
- if not cleaned_response: raise ValueError("A resposta do Gemini estava vazia após a limpeza.")
67
- storyboard_data = json.loads(cleaned_response)
68
- return storyboard_data.get("storyboard", [])
69
- except (json.JSONDecodeError, ValueError) as e:
70
- raise gr.Error(f"O Diretor retornou uma resposta inválida. Erro: {e}. Resposta Bruta: '{response.text}'")
71
-
72
- def run_keyframe_generation(storyboard, ref_img_path_1, ref_img_path_2, ref_task_1, ref_task_2):
73
- if not storyboard: raise gr.Error("Nenhum roteiro para gerar imagens-chave.")
74
- if not ref_img_path_1: raise gr.Error("A Referência 1 é obrigatória.")
75
-
76
- with Image.open(ref_img_path_1) as img:
77
- width, height = img.size
78
- width = (width // 32) * 32
79
- height = (height // 32) * 32
80
-
81
- keyframe_paths, log_history = [], ""
82
- try:
83
- dreamo_generator_singleton.to_gpu()
84
- for i, prompt in enumerate(storyboard):
85
- log_message = f"Pintando Cena {i+1}/{len(storyboard)} com DreamO ({width}x{height})..."
86
- log_history += log_message + "\n"
87
- yield {keyframe_log_output: gr.update(value=log_history)}
88
- output_path = os.path.join(WORKSPACE_DIR, f"keyframe_image_{i+1}.png")
89
- image = dreamo_generator_singleton.generate_image_with_gpu_management(
90
- ref_image1_np=np.array(Image.open(ref_img_path_1).convert("RGB")) if ref_img_path_1 else None,
91
- ref_image2_np=np.array(Image.open(ref_img_path_2).convert("RGB")) if ref_img_path_2 else None,
92
- ref_task1=ref_task_1, ref_task2=ref_task_2,
93
- prompt=prompt, width=width, height=height
94
- )
95
- image.save(output_path)
96
- keyframe_paths.append(output_path)
97
- log_message = f"Cena {i+1} pintada."
98
- log_history += log_message + "\n"
99
- yield {keyframe_log_output: gr.update(value=log_history), keyframe_gallery_output: gr.update(value=keyframe_paths), keyframe_images_state: keyframe_paths}
100
- finally:
101
- dreamo_generator_singleton.to_cpu()
102
-
103
- log_history += "\nPintura de todas as cenas concluída!"
104
- yield {keyframe_log_output: gr.update(value=log_history)}
105
-
106
- def run_ltx_animation(current_fragment_index, motion_prompt, conditioning_items_data, width, height, seed, cfg, progress=gr.Progress()):
107
- # ... (código inalterado)
108
- progress(0, desc=f"[Animador LTX] Gerando Cena {current_fragment_index}...")
109
- output_path = os.path.join(WORKSPACE_DIR, f"fragment_{current_fragment_index}.mp4")
110
- target_device = 'cuda' if torch.cuda.is_available() else 'cpu'
111
- try:
112
- pipeline_instance.to(target_device)
113
- conditioning_items = []
114
- for (path, start_frame, strength) in conditioning_items_data:
115
- tensor = load_image_to_tensor_with_resize_and_crop(path, height, width)
116
- conditioning_items.append(ConditioningItem(tensor.to(target_device), start_frame, strength))
117
- n_val = round((float(VIDEO_TOTAL_FRAMES) - 1.0) / 8.0)
118
- actual_num_frames = int(n_val * 8 + 1)
119
- padded_h, padded_w = ((height - 1) // 32 + 1) * 32, ((width - 1) // 32 + 1) * 32
120
- padding_vals = calculate_padding(height, width, padded_h, padded_w)
121
- for cond_item in conditioning_items: cond_item.media_item = torch.nn.functional.pad(cond_item.media_item, padding_vals)
122
- timesteps = PIPELINE_CONFIG_YAML.get("first_pass", {}).get("timesteps")
123
- kwargs = {"prompt": motion_prompt, "negative_prompt": "blurry, distorted, bad quality, artifacts", "height": padded_h, "width": padded_w, "num_frames": actual_num_frames, "frame_rate": VIDEO_FPS, "generator": torch.Generator(device=target_device).manual_seed(int(seed) + current_fragment_index), "output_type": "pt", "guidance_scale": float(cfg), "timesteps": timesteps, "conditioning_items": conditioning_items, "vae_per_channel_normalize": True, "decode_timestep": PIPELINE_CONFIG_YAML["decode_timestep"], "decode_noise_scale": PIPELINE_CONFIG_YAML["decode_noise_scale"], "stochastic_sampling": PIPELINE_CONFIG_YAML["stochastic_sampling"], "image_cond_noise_scale": 0.15, "is_video": True, "mixed_precision": (PIPELINE_CONFIG_YAML["precision"] == "mixed_precision"), "offload_to_cpu": False, "enhance_prompt": False}
124
- result_tensor = pipeline_instance(**kwargs).images
125
- pad_l, pad_r, pad_t, pad_b = padding_vals; slice_h, slice_w = (-pad_b if pad_b > 0 else None), (-pad_r if pad_r > 0 else None)
126
- cropped_tensor = result_tensor[:, :, :VIDEO_TOTAL_FRAMES, pad_t:slice_h, pad_l:slice_w];
127
- video_np = (cropped_tensor[0].permute(1, 2, 3, 0).cpu().float().numpy() * 255).astype(np.uint8)
128
- with imageio.get_writer(output_path, fps=VIDEO_FPS, codec='libx264', quality=8) as writer:
129
- for i, frame in enumerate(video_np): progress(i / len(video_np), desc=f"Renderizando frame {i+1}/{len(video_np)}..."); writer.append_data(frame)
130
- return output_path
131
- finally:
132
- pipeline_instance.to('cpu'); gc.collect()
133
- if torch.cuda.is_available(): torch.cuda.empty_cache()
134
-
135
- # <<<< FUNÇÃO DE PRODUÇÃO SIMPLIFICADA PARA DEPURAÇÃO >>>>
136
- def run_full_video_production(storyboard, keyframe_image_paths, seed, cfg):
137
- if not storyboard or not keyframe_image_paths: raise gr.Error("Roteiro e/ou imagens-chave estão faltando.")
138
- if len(storyboard) != len(keyframe_image_paths): raise gr.Error("A contagem de prompts do roteiro e imagens-chave não coincide.")
139
-
140
- with Image.open(keyframe_image_paths[0]) as img:
141
- width, height = img.size
142
-
143
- video_fragments, log_history = [], ""
144
- num_keyframes = len(keyframe_image_paths)
145
-
146
- n_val = round((float(VIDEO_TOTAL_FRAMES) - 1.0) / 8.0)
147
- actual_num_frames = int(n_val * 8 + 1)
148
- end_frame_index = actual_num_frames - 1
149
-
150
- for i in range(num_keyframes - 1):
151
- # ... (lógica de interpolação inalterada)
152
- motion_prompt = storyboard[i]
153
- start_image_path = keyframe_image_paths[i]
154
- end_image_path = keyframe_image_paths[i+1]
155
- log_message = f"Preparando Cena de Interpolação {i+1}/{num_keyframes}..."
156
- log_history += log_message + "\n"
157
- yield {video_production_log_output: gr.update(value=log_history), fragment_list_state: video_fragments}
158
- conditioning_items_data = [(start_image_path, 0, 1.0), (end_image_path, end_frame_index, 1.0)]
159
- log_message = f" -> De: {os.path.basename(start_image_path)} | Para: {os.path.basename(end_image_path)}"
160
- log_history += log_message + "\n"
161
- yield {video_production_log_output: gr.update(value=log_history), fragment_list_state: video_fragments}
162
- fragment_path = run_ltx_animation(i + 1, motion_prompt, conditioning_items_data, width, height, seed, cfg)
163
- video_fragments.append(fragment_path)
164
- log_message = f"Cena {i+1} concluída."
165
- log_history += log_message + "\n"
166
- yield {video_production_log_output: gr.update(value=log_history), fragment_list_state: video_fragments}
167
-
168
- if num_keyframes > 0:
169
- # ... (lógica da cena final inalterada)
170
- last_scene_index = num_keyframes - 1
171
- last_motion_prompt = storyboard[last_scene_index]
172
- last_image_path = keyframe_image_paths[last_scene_index]
173
- log_message = f"Preparando Cena Final (Animação Livre) {num_keyframes}/{num_keyframes}..."
174
- log_history += log_message + "\n"
175
- yield {video_production_log_output: gr.update(value=log_history), fragment_list_state: video_fragments}
176
- conditioning_items_data = [(last_image_path, 0, 1.0)]
177
- log_message = f" -> Ponto de Partida: {os.path.basename(last_image_path)}"
178
- log_history += log_message + "\n"
179
- yield {video_production_log_output: gr.update(value=log_history), fragment_list_state: video_fragments}
180
- fragment_path = run_ltx_animation(last_scene_index + 1, last_motion_prompt, conditioning_items_data, width, height, seed, cfg)
181
- video_fragments.append(fragment_path)
182
- log_message = f"Cena Final concluída."
183
- log_history += log_message + "\n"
184
- yield {video_production_log_output: gr.update(value=log_history), fragment_list_state: video_fragments}
185
-
186
- log_history += "\nProdução de todas as cenas de vídeo concluída!"
187
- yield {video_production_log_output: gr.update(value=log_history), fragment_list_state: video_fragments}
188
-
189
- def concatenate_masterpiece(fragment_paths: list, progress=gr.Progress()):
190
- # ... (código inalterado)
191
- progress(0.5, desc="Montando a obra-prima final..."); list_file_path, final_output_path = os.path.join(WORKSPACE_DIR, "concat_list.txt"), os.path.join(WORKSPACE_DIR, "obra_prima_final.mp4")
192
- with open(list_file_path, "w") as f:
193
- for path in fragment_paths: f.write(f"file '{os.path.abspath(path)}'\n")
194
- command = f"ffmpeg -y -f concat -safe 0 -i {list_file_path} -c copy {final_output_path}"
195
- try: subprocess.run(command, shell=True, check=True, capture_output=True, text=True); return final_output_path
196
- except subprocess.CalledProcessError as e: raise gr.Error(f"FFmpeg falhou ao unir os vídeos: {e.stderr}")
197
-
198
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
199
- # ... (UI inalterada)
200
- gr.Markdown("# LTX Video - Storyboard em Vídeo (ADUC-SDR)\n*By Carlex & Gemini & DreamO*")
201
- storyboard_state = gr.State([])
202
- keyframe_images_state = gr.State([])
203
- fragment_list_state = gr.State([])
204
- if os.path.exists(WORKSPACE_DIR): shutil.rmtree(WORKSPACE_DIR)
205
- os.makedirs(WORKSPACE_DIR)
206
-
207
- with gr.Tabs():
208
- with gr.TabItem("ETAPA 1: O DIRETOR (Roteiro Visual)"):
209
- # ... (UI inalterada)
210
- with gr.Row():
211
- with gr.Column():
212
- num_fragments_input = gr.Slider(2, 10, 4, step=1, label="Número de Cenas")
213
- prompt_input = gr.Textbox(label="Ideia Geral (Prompt)")
214
- image_input = gr.Image(type="filepath", label="Imagem de Referência Principal")
215
- director_button = gr.Button("▶️ 1. Gerar Roteiro Visual", variant="primary")
216
- with gr.Column():
217
- storyboard_to_show = gr.JSON(label="Roteiro Gerado (para visualização)")
218
- with gr.TabItem("ETAPA 2: O PINTOR (Imagens-Chave)"):
219
- # ... (UI inalterada)
220
- with gr.Row():
221
- with gr.Column(scale=2):
222
- gr.Markdown("### Controles do Pintor (DreamO)")
223
- with gr.Row():
224
- ref_image_1_input = gr.Image(label="Referência 1 (Principal)", type="filepath")
225
- ref_image_2_input = gr.Image(label="Referência 2 (Opcional, para composição)", type="filepath")
226
- with gr.Row():
227
- ref_task_1_input = gr.Dropdown(choices=["ip", "id", "style"], value="ip", label="Tarefa para Referência 1")
228
- ref_task_2_input = gr.Dropdown(choices=["ip", "id", "style"], value="ip", label="Tarefa para Referência 2")
229
- photographer_button = gr.Button("▶️ 2. Pintar Imagens-Chave", variant="primary")
230
- keyframe_log_output = gr.Textbox(label="Diário de Bordo do Pintor", lines=5, interactive=False)
231
- with gr.Column(scale=1):
232
- keyframe_gallery_output = gr.Gallery(label="Imagens-Chave Pintadas", object_fit="contain", height="auto", type="filepath")
233
- with gr.TabItem("ETAPA 3: A PRODUÇÃO (Gerar Cenas em Vídeo)"):
234
- # ... (UI inalterada)
235
- gr.Markdown(f"Gere o vídeo interpolando entre as imagens-chave. A resolução será a mesma da sua imagem de referência. Cada clipe terá **{VIDEO_DURATION_SECONDS} segundos a {VIDEO_FPS} FPS**.")
236
- with gr.Row():
237
- with gr.Column():
238
- keyframes_to_render = gr.Gallery(label="Imagens-Chave para Animar", object_fit="contain", height="auto", interactive=False)
239
- animator_button = gr.Button("▶️ 3. Produzir Cenas em Vídeo", variant="primary", interactive=False)
240
- video_production_log_output = gr.Textbox(label="Diário de Bordo da Produção", lines=10, interactive=False)
241
- with gr.Column():
242
- # <<<< REMOVIDO PARA DEPURAÇÃO >>>>
243
- # fragment_gallery_output = gr.Gallery(label="Cenas Produzidas (Vídeos)", object_fit="contain", height="auto")
244
- gr.Markdown("A galeria de vídeos foi desativada para depuração. Verifique o resultado na Etapa 4.")
245
- with gr.Row():
246
- seed_number = gr.Number(42, label="Seed")
247
- cfg_slider = gr.Slider(1.0, 10.0, 2.5, step=0.1, label="CFG")
248
- with gr.TabItem("ETAPA 4: PÓS-PRODUÇÃO"):
249
- # ... (UI inalterada)
250
- with gr.Row():
251
- with gr.Column():
252
- editor_button = gr.Button("▶️ 4. Concatenar Vídeo Final", variant="primary")
253
- final_fragments_display = gr.JSON(label="Fragmentos a Concatenar")
254
- with gr.Column():
255
- final_video_output = gr.Video(label="A Obra-Prima Final")
256
-
257
- # --- Ato 5: A Regência (Lógica de Conexão dos Botões) ---
258
- def director_success(storyboard_list, img_path):
259
- # ... (lógica inalterada)
260
- if not storyboard_list: raise gr.Error("O storyboard está vazio ou em formato inválido.")
261
- return {storyboard_state: storyboard_list, storyboard_to_show: gr.update(value=storyboard_list), ref_image_1_input: gr.update(value=img_path)}
262
-
263
- director_button.click(
264
- fn=get_storyboard_from_director,
265
- inputs=[num_fragments_input, prompt_input, image_input],
266
- outputs=[storyboard_state]
267
- ).then(
268
- fn=director_success,
269
- inputs=[storyboard_state, image_input],
270
- outputs=[storyboard_state, storyboard_to_show, ref_image_1_input]
271
- )
272
-
273
- photographer_button.click(
274
- fn=run_keyframe_generation,
275
- inputs=[storyboard_state, ref_image_1_input, ref_image_2_input, ref_task_1_input, ref_task_2_input],
276
- outputs=[keyframe_log_output, keyframe_gallery_output, keyframe_images_state]
277
- ).then(
278
- lambda paths: {keyframes_to_render: gr.update(value=paths), animator_button: gr.update(interactive=True)},
279
- inputs=[keyframe_images_state],
280
- outputs=[keyframes_to_render, animator_button]
281
- )
282
-
283
- # <<<< CHAMADA DE CLICK SIMPLIFICADA PARA DEPURAÇÃO >>>>
284
- animator_button.click(
285
- fn=run_full_video_production,
286
- inputs=[storyboard_state, keyframe_images_state, seed_number, cfg_slider],
287
- outputs=[video_production_log_output, fragment_list_state]
288
- ).then(
289
- lambda paths: gr.update(value=paths),
290
- inputs=[fragment_list_state],
291
- outputs=[final_fragments_display]
292
- )
293
-
294
- editor_button.click(
295
- fn=concatenate_masterpiece,
296
- inputs=[fragment_list_state],
297
- outputs=[final_video_output]
298
- )
299
-
300
- if __name__ == "__main__":
301
- demo.queue().launch(server_name="0.0.0.0", share=True)