Eueuiaa commited on
Commit
39769b3
·
verified ·
1 Parent(s): a281339

Upload ltx_server.py

Browse files
Files changed (1) hide show
  1. api/ltx_server.py +96 -186
api/ltx_server.py CHANGED
@@ -8,15 +8,19 @@ warnings.filterwarnings("ignore", category=UserWarning)
8
  warnings.filterwarnings("ignore", category=FutureWarning)
9
  warnings.filterwarnings("ignore", message=".*")
10
 
11
- from huggingface_hub import logging, hf_hub_download
12
 
13
  logging.set_verbosity_error()
14
  logging.set_verbosity_warning()
15
  logging.set_verbosity_info()
16
  logging.set_verbosity_debug()
 
 
17
  LTXV_DEBUG=1
18
  LTXV_FRAME_LOG_EVERY=8
19
 
 
 
20
  # --- 1. IMPORTAÇÕES ---
21
  import os, subprocess, shlex, tempfile
22
  import torch
@@ -104,6 +108,8 @@ def _query_gpu_processes_via_nvidiasmi(device_index: int) -> List[Dict]:
104
  continue
105
  return results
106
 
 
 
107
  def calculate_new_dimensions(orig_w, orig_h, divisor=8):
108
  """
109
  Calcula novas dimensões mantendo a proporção, garantindo que ambos os
@@ -138,6 +144,7 @@ def calculate_new_dimensions(orig_w, orig_h, divisor=8):
138
  print(f"[Dimension Calc] Original: {orig_w}x{orig_h} -> Calculado: {new_w:.0f}x{new_h:.0f} -> Final (divisível por {divisor}): {final_w}x{final_h}")
139
  return final_h, final_w # Retorna (altura, largura)
140
 
 
141
  def handle_media_upload_for_dims(filepath, current_h, current_w):
142
  """
143
  Esta função agora usará o novo cálculo robusto.
@@ -211,6 +218,7 @@ def add_deps_to_path():
211
  add_deps_to_path()
212
 
213
  # --- 3. IMPORTAÇÕES ESPECÍFICAS DO MODELO ---
 
214
  from ltx_video.pipelines.pipeline_ltx_video import ConditioningItem, LTXMultiScalePipeline
215
  from ltx_video.utils.skip_layer_strategy import SkipLayerStrategy
216
  from ltx_video.models.autoencoders.vae_encode import un_normalize_latents, normalize_latents
@@ -233,6 +241,10 @@ def log_tensor_info(tensor, name="Tensor"):
233
  pass
234
  print("------------------------------------------\n")
235
 
 
 
 
 
236
  # --- 5. CLASSE PRINCIPAL DO SERVIÇO ---
237
  class VideoService:
238
  def __init__(self):
@@ -354,80 +366,6 @@ class VideoService:
354
  return yaml.safe_load(file)
355
 
356
  def _load_models(self):
357
- """
358
- Carrega os modelos de forma inteligente:
359
- 1. Tenta resolver o caminho do cache local (rápido, sem rede).
360
- 2. Se o arquivo não for encontrado localmente, baixa como fallback.
361
- Garante que o serviço possa iniciar mesmo que o setup.py não tenha sido executado.
362
- """
363
- t0 = time.perf_counter()
364
- LTX_REPO = "Lightricks/LTX-Video"
365
-
366
- print("[DEBUG] Resolvendo caminhos dos modelos de forma inteligente...")
367
-
368
- # --- Função Auxiliar para Carregamento Inteligente ---
369
- def get_or_download_model(repo_id, filename, description):
370
- try:
371
- # hf_hub_download é a ferramenta certa aqui. Ela verifica o cache PRIMEIRO.
372
- # Se o arquivo estiver no cache, retorna o caminho instantaneamente (após uma verificação rápida de metadados).
373
- # Se não estiver no cache, ela o baixa.
374
- print(f"[DEBUG] Verificando {description}: {filename}...")
375
- model_path = hf_hub_download(
376
- repo_id=repo_id,
377
- filename=filename,
378
- # Forçar o uso de um cache específico se necessário
379
- cache_dir=os.getenv("HF_HOME_CACHE"),
380
- token=os.getenv("HF_TOKEN")
381
- )
382
- print(f"[DEBUG] Caminho do {description} resolvido com sucesso.")
383
- return model_path
384
- except Exception as e:
385
- print("\n" + "="*80)
386
- print(f"[ERRO CRÍTICO] Falha ao obter o modelo '{filename}'.")
387
- print(f"Detalhe do erro: {e}")
388
- print("Verifique sua conexão com a internet ou o estado do cache do Hugging Face.")
389
- print("="*80 + "\n")
390
- sys.exit(1)
391
-
392
- # --- Checkpoint Principal ---
393
- checkpoint_filename = self.config["checkpoint_path"]
394
- distilled_model_path = get_or_download_model(
395
- LTX_REPO, checkpoint_filename, "checkpoint principal"
396
- )
397
- self.config["checkpoint_path"] = distilled_model_path
398
-
399
- # --- Upscaler Espacial ---
400
- upscaler_filename = self.config["spatial_upscaler_model_path"]
401
- spatial_upscaler_path = get_or_download_model(
402
- LTX_REPO, upscaler_filename, "upscaler espacial"
403
- )
404
- self.config["spatial_upscaler_model_path"] = spatial_upscaler_path
405
-
406
- # --- Construção dos Pipelines ---
407
- print("\n[DEBUG] Construindo pipeline a partir dos caminhos resolvidos...")
408
- pipeline = create_ltx_video_pipeline(
409
- ckpt_path=self.config["checkpoint_path"],
410
- precision=self.config["precision"],
411
- text_encoder_model_name_or_path=self.config["text_encoder_model_name_or_path"],
412
- sampler=self.config["sampler"],
413
- device="cpu",
414
- enhance_prompt=False,
415
- prompt_enhancer_image_caption_model_name_or_path=self.config["prompt_enhancer_image_caption_model_name_or_path"],
416
- prompt_enhancer_llm_model_name_or_path=self.config["prompt_enhancer_llm_model_name_or_path"],
417
- )
418
- print("[DEBUG] Pipeline pronto.")
419
-
420
- latent_upsampler = None
421
- if self.config.get("spatial_upscaler_model_path"):
422
- print("[DEBUG] Construindo latent_upsampler...")
423
- latent_upsampler = create_latent_upsampler(self.config["spatial_upscaler_model_path"], device="cpu")
424
- print("[DEBUG] Upsampler pronto.")
425
-
426
- print(f"[DEBUG] _load_models() tempo total={time.perf_counter()-t0:.3f}s")
427
- return pipeline, latent_upsampler
428
-
429
-
430
- def _load_models_old(self):
431
  t0 = time.perf_counter()
432
  LTX_REPO = "Lightricks/LTX-Video"
433
  print("[DEBUG] Baixando checkpoint principal...")
@@ -497,6 +435,8 @@ class VideoService:
497
  pass
498
  print(f"[DEBUG] FP8→BF16: params_promoted={p_cnt}, buffers_promoted={b_cnt}")
499
 
 
 
500
  @torch.no_grad()
501
  def _upsample_latents_internal(self, latents: torch.Tensor) -> torch.Tensor:
502
  """
@@ -517,6 +457,7 @@ class VideoService:
517
  return upsampled_latents
518
 
519
 
 
520
  def _apply_precision_policy(self):
521
  prec = str(self.config.get("precision", "")).lower()
522
  self.runtime_autocast_dtype = torch.float32
@@ -571,32 +512,28 @@ class VideoService:
571
 
572
  n_chunks = (sum_latent) // num_latente_por_chunk
573
  steps = sum_latent//n_chunks
574
- print("==========PODA CAUSAL[start:stop-1]==========")
575
  print(f"[DEBUG] TOTAL LATENTES = {sum_latent}")
576
  print(f"[DEBUG] LATENTES min por chunk = {num_latente_por_chunk}")
577
  print(f"[DEBUG] Número de chunks = {n_chunks}")
578
  if n_chunks > 1:
579
  i=0
580
  while i < n_chunks:
581
- if i>0:
582
- dow=0
583
- else:
584
- dow=0
585
  start = (num_latente_por_chunk*i)
586
- end = (start+num_latente_por_chunk+(overlap))
587
  if i+1 < n_chunks:
588
- chunk = latents_brutos[:, :, start-(dow):end, :, :].clone().detach()
589
- print(f"[DEBUG] chunk{i+1}[:, :, {start-dow}:{end}, :, :] = {chunk.shape[2]}")
590
  else:
591
- chunk = latents_brutos[:, :, start-(dow):, :, :].clone().detach()
592
- print(f"[DEBUG] chunk{i+1}[:, :, {start-(dow)}:, :, :] = {chunk.shape[2]}")
593
  chunks.append(chunk)
594
  i+=1
595
  else:
596
  print(f"[DEBUG] numero chunks minimo ")
597
  print(f"[DEBUG] latents_brutos[:, :, :, :, :] = {latents_brutos.shape[2]}")
598
  chunks.append(latents_brutos)
599
- print("\n\n================PODA CAUSAL=================")
600
  return chunks
601
 
602
  def _get_total_frames(self, video_path: str) -> int:
@@ -625,16 +562,10 @@ class VideoService:
625
  video_fade_ini = None
626
  nova_lista = []
627
 
628
- if crossfade_frames == 0:
629
- print("\n\n[DEBUG] CROSSFADE_FRAMES=0 Ship concatenation causal")
630
- return video_paths
631
-
632
-
633
- print("\n\n===========CONCATECAO CAUSAL=============")
634
 
635
  print(f"[DEBUG] Iniciando pipeline com {total_partes} vídeos e {poda} frames de crossfade")
636
-
637
-
638
  for i in range(total_partes):
639
  base = video_paths[i]
640
 
@@ -693,7 +624,7 @@ class VideoService:
693
 
694
  nova_lista.append(video_podado)
695
  print(f"[DEBUG] Video podado {i+1} adicionado {self._get_total_frames(video_podado)} frames ✅")
696
-
697
 
698
 
699
  print("===========CONCATECAO CAUSAL=============")
@@ -809,7 +740,8 @@ class VideoService:
809
  "enhance_prompt": False,
810
  "skip_layer_strategy": SkipLayerStrategy.AttentionValues,
811
  }
812
-
 
813
  latents = None
814
  latents_list = []
815
  results_dir = "/app/output"; os.makedirs(results_dir, exist_ok=True)
@@ -821,9 +753,7 @@ class VideoService:
821
  if improve_texture:
822
  if not self.latent_upsampler:
823
  raise ValueError("Upscaler espacial não carregado, mas 'improve_texture' está ativo.")
824
-
825
- first_pass_kwargs = call_kwargs.copy()
826
-
827
  # --- ETAPA 1: GERAÇÃO BASE (FIRST PASS) ---
828
  print("\n--- INICIANDO ETAPA 1: GERAÇÃO BASE (FIRST PASS) ---")
829
  t_pass1 = time.perf_counter()
@@ -832,21 +762,23 @@ class VideoService:
832
  downscale_factor = self.config.get("downscale_factor", 0.6666666)
833
  vae_scale_factor = self.pipeline.vae_scale_factor # Geralmente 8
834
 
 
 
835
  x_width = int(width_padded * downscale_factor)
836
  downscaled_width = x_width - (x_width % vae_scale_factor)
837
  x_height = int(height_padded * downscale_factor)
838
  downscaled_height = x_height - (x_height % vae_scale_factor)
839
  print(f"[DEBUG] First Pass Dims: Original Pad ({width_padded}x{height_padded}) -> Downscaled ({downscaled_width}x{downscaled_height})")
 
840
 
841
- first_pass_kwargs.update({
842
- **first_pass_config
843
- })
844
-
845
  first_pass_kwargs.update({
846
  "output_type": "latent",
847
  "width": downscaled_width,
848
  "height": downscaled_height,
849
  "guidance_scale": float(guidance_scale),
 
850
  })
851
 
852
  print(f"[DEBUG] First Pass: Gerando em {downscaled_width}x{downscaled_height}...")
@@ -872,44 +804,38 @@ class VideoService:
872
  except Exception:
873
  pass
874
 
875
- latents_parts_up = self._dividir_latentes_por_tamanho(latents_cpu_up,40,0)
876
-
877
- print("\n\n--- INICIANDO ETAPA 3: REFINAMENTO DE TEXTURA (SECOND PASS) ---")
878
-
879
- cc = 1
880
  for latents in latents_parts_up:
881
-
882
- t_pass2 = time.perf_counter()
883
- print("\n\n#########################################")
884
  # # --- ETAPA 3: REFINAMENTO DE TEXTURA (SECOND PASS) ---
885
- print(f"\n--- INICIANDO ETAPA 3/{cc} ")
886
-
887
- first_pass_kwargs = call_kwargs.copy()
888
-
889
  second_pass_config = self.config.get("second_pass", {}).copy()
890
-
 
891
  second_pass_width = downscaled_width * 2
892
  second_pass_height = downscaled_height * 2
893
  print(f"[DEBUG] Second Pass Dims: Target ({second_pass_width}x{second_pass_height})")
894
-
 
 
895
  num_latent_frames_part = latents.shape[2]
896
- log_tensor_info(latents, "Latentes input (Pre-Pós-Second Pass)")
897
-
898
  vae_temporal_scale = self.pipeline.video_scale_factor # Geralmente 4 ou 8
899
  num_pixel_frames_part = ((num_latent_frames_part - 1) * vae_temporal_scale) + 1
900
  print(f"[DEBUG] Parte: {num_latent_frames_part - 1} latentes -> {num_pixel_frames_part} frames de pixel (alvo)")
901
-
902
- second_pass_kwargs.update({
903
- **second_pass_config
904
- })
905
-
906
  second_pass_kwargs.update({
907
  "output_type": "latent",
908
  "width": second_pass_width,
909
  "height": second_pass_height,
910
- "num_frames": num_pixel_frames_part,
911
- "latents": latents, # O tensor upscaled
912
  "guidance_scale": float(guidance_scale),
 
913
  })
914
 
915
  print(f"[DEBUG] Second Pass: Refinando em {width_padded}x{height_padded}...")
@@ -918,10 +844,6 @@ class VideoService:
918
  print(f"[DEBUG] Second part Pass concluída em {time.perf_counter() - t_pass2:.2f}s")
919
 
920
  latents_list.append(final_latents)
921
- cc+=1
922
- print("#########################################")
923
-
924
- print("\n\n--- FIM ETAPA 3: REFINAMENTO DE TEXTURA (SECOND PASS) ---")
925
 
926
  else: # Geração de etapa única
927
  print("\n--- INICIANDO GERAÇÃO DE ETAPA ÚNICA ---")
@@ -931,74 +853,62 @@ class VideoService:
931
  single_pass_kwargs["guidance_scale"] = float(guidance_scale)
932
  single_pass_kwargs["output_type"] = "latent"
933
 
934
- # Remove keys that might conflict or are not used in single pass / handled by above
935
- #single_pass_kwargs.pop("num_inference_steps", None)
936
- #single_pass_kwargs.pop("first_pass", None)
937
- #single_pass_kwargs.pop("second_pass", None)
938
- #single_pass_kwargs.pop("downscale_factor", None)
939
-
940
  latents = self.pipeline(**single_pass_kwargs).images
941
  log_tensor_info(latents, "Latentes Finais (Etapa Única)")
942
  print(f"[DEBUG] Etapa única concluída em {time.perf_counter() - t_single:.2f}s")
943
 
944
  latents_list.append(latents)
945
-
946
  # --- ETAPA FINAL: DECODIFICAÇÃO E CODIFICAÇÃO MP4 ---
947
  print("\n--- INICIANDO ETAPA FINAL: DECODIFICAÇÃO E MONTAGEM ---")
948
 
949
- temp_dir = tempfile.mkdtemp(prefix="ltxv_"); self._register_tmp_dir(temp_dir)
950
- results_dir = "/app/output"; os.makedirs(results_dir, exist_ok=True)
951
-
952
-
 
 
 
 
 
 
 
 
953
  partes_mp4 = []
954
  par = 0
955
-
956
- for latents_vae in latents_list:
957
-
958
- latents_cpu_vae = latents_vae.detach().to("cpu", non_blocking=True)
959
- torch.cuda.empty_cache()
960
- try:
961
- torch.cuda.ipc_collect()
962
- except Exception:
963
- pass
964
-
965
- latents_parts_vae = self._dividir_latentes_por_tamanho(latents_cpu_vae,4,1)
966
 
967
- for latents in latents_parts_vae:
968
- print(f"[DEBUG] Partição {par}: {tuple(latents.shape)}")
969
-
970
- par = par + 1
971
- output_video_path = os.path.join(temp_dir, f"output_{used_seed}_{par}.mp4")
972
- final_output_path = None
973
-
974
- print("[DEBUG] Decodificando bloco de latentes com VAE → tensor de pixels...")
975
- # Usar manager com timestep por item; previne target_shape e rota NoneType.decode
976
- pixel_tensor = vae_manager_singleton.decode(
977
- latents.to(self.device, non_blocking=True),
978
- decode_timestep=float(self.config.get("decode_timestep", 0.05))
979
- )
980
- log_tensor_info(pixel_tensor, "Pixel tensor (VAE saída)")
981
-
982
- print("[DEBUG] Codificando MP4 a partir do tensor de pixels (bloco inteiro)...")
983
- video_encode_tool_singleton.save_video_from_tensor(
984
- pixel_tensor,
985
- output_video_path,
986
- fps=call_kwargs["frame_rate"],
987
- progress_callback=progress_callback
988
- )
989
-
990
- candidate = os.path.join(results_dir, f"output_par_{par}.mp4")
991
- try:
992
- shutil.move(output_video_path, candidate)
993
- final_output_path = candidate
994
- print(f"[DEBUG] MP4 parte {par} movido para {final_output_path}")
995
- partes_mp4.append(final_output_path)
996
-
997
- except Exception as e:
998
- final_output_path = output_video_path
999
- print(f"[DEBUG] Falha no move; usando tmp como final: {e}")
1000
 
1001
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1002
  total_partes = len(partes_mp4)
1003
  if (total_partes>1):
1004
  final_vid = os.path.join(results_dir, f"concat_fim_{used_seed}.mp4")
 
8
  warnings.filterwarnings("ignore", category=FutureWarning)
9
  warnings.filterwarnings("ignore", message=".*")
10
 
11
+ from huggingface_hub import logging
12
 
13
  logging.set_verbosity_error()
14
  logging.set_verbosity_warning()
15
  logging.set_verbosity_info()
16
  logging.set_verbosity_debug()
17
+
18
+
19
  LTXV_DEBUG=1
20
  LTXV_FRAME_LOG_EVERY=8
21
 
22
+
23
+
24
  # --- 1. IMPORTAÇÕES ---
25
  import os, subprocess, shlex, tempfile
26
  import torch
 
108
  continue
109
  return results
110
 
111
+
112
+
113
  def calculate_new_dimensions(orig_w, orig_h, divisor=8):
114
  """
115
  Calcula novas dimensões mantendo a proporção, garantindo que ambos os
 
144
  print(f"[Dimension Calc] Original: {orig_w}x{orig_h} -> Calculado: {new_w:.0f}x{new_h:.0f} -> Final (divisível por {divisor}): {final_w}x{final_h}")
145
  return final_h, final_w # Retorna (altura, largura)
146
 
147
+
148
  def handle_media_upload_for_dims(filepath, current_h, current_w):
149
  """
150
  Esta função agora usará o novo cálculo robusto.
 
218
  add_deps_to_path()
219
 
220
  # --- 3. IMPORTAÇÕES ESPECÍFICAS DO MODELO ---
221
+
222
  from ltx_video.pipelines.pipeline_ltx_video import ConditioningItem, LTXMultiScalePipeline
223
  from ltx_video.utils.skip_layer_strategy import SkipLayerStrategy
224
  from ltx_video.models.autoencoders.vae_encode import un_normalize_latents, normalize_latents
 
241
  pass
242
  print("------------------------------------------\n")
243
 
244
+
245
+
246
+
247
+
248
  # --- 5. CLASSE PRINCIPAL DO SERVIÇO ---
249
  class VideoService:
250
  def __init__(self):
 
366
  return yaml.safe_load(file)
367
 
368
  def _load_models(self):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  t0 = time.perf_counter()
370
  LTX_REPO = "Lightricks/LTX-Video"
371
  print("[DEBUG] Baixando checkpoint principal...")
 
435
  pass
436
  print(f"[DEBUG] FP8→BF16: params_promoted={p_cnt}, buffers_promoted={b_cnt}")
437
 
438
+
439
+
440
  @torch.no_grad()
441
  def _upsample_latents_internal(self, latents: torch.Tensor) -> torch.Tensor:
442
  """
 
457
  return upsampled_latents
458
 
459
 
460
+
461
  def _apply_precision_policy(self):
462
  prec = str(self.config.get("precision", "")).lower()
463
  self.runtime_autocast_dtype = torch.float32
 
512
 
513
  n_chunks = (sum_latent) // num_latente_por_chunk
514
  steps = sum_latent//n_chunks
515
+ print("================PODA CAUSAL=================")
516
  print(f"[DEBUG] TOTAL LATENTES = {sum_latent}")
517
  print(f"[DEBUG] LATENTES min por chunk = {num_latente_por_chunk}")
518
  print(f"[DEBUG] Número de chunks = {n_chunks}")
519
  if n_chunks > 1:
520
  i=0
521
  while i < n_chunks:
 
 
 
 
522
  start = (num_latente_por_chunk*i)
523
+ end = (start+num_latente_por_chunk+overlap)
524
  if i+1 < n_chunks:
525
+ chunk = latents_brutos[:, :, start:end, :, :].clone().detach()
526
+ print(f"[DEBUG] chunk{i+1}[:, :, {start}:{end}, :, :] = {chunk.shape[2]}")
527
  else:
528
+ chunk = latents_brutos[:, :, start:, :, :].clone().detach()
529
+ print(f"[DEBUG] chunk{i+1}[:, :, {start}:, :, :] = {chunk.shape[2]}")
530
  chunks.append(chunk)
531
  i+=1
532
  else:
533
  print(f"[DEBUG] numero chunks minimo ")
534
  print(f"[DEBUG] latents_brutos[:, :, :, :, :] = {latents_brutos.shape[2]}")
535
  chunks.append(latents_brutos)
536
+ print("================PODA CAUSAL=================")
537
  return chunks
538
 
539
  def _get_total_frames(self, video_path: str) -> int:
 
562
  video_fade_ini = None
563
  nova_lista = []
564
 
565
+ print("===========CONCATECAO CAUSAL=============")
 
 
 
 
 
566
 
567
  print(f"[DEBUG] Iniciando pipeline com {total_partes} vídeos e {poda} frames de crossfade")
568
+
 
569
  for i in range(total_partes):
570
  base = video_paths[i]
571
 
 
624
 
625
  nova_lista.append(video_podado)
626
  print(f"[DEBUG] Video podado {i+1} adicionado {self._get_total_frames(video_podado)} frames ✅")
627
+
628
 
629
 
630
  print("===========CONCATECAO CAUSAL=============")
 
740
  "enhance_prompt": False,
741
  "skip_layer_strategy": SkipLayerStrategy.AttentionValues,
742
  }
743
+ print(f"[DEBUG] output_type={call_kwargs['output_type']} skip_layer_strategy={call_kwargs['skip_layer_strategy']}")
744
+
745
  latents = None
746
  latents_list = []
747
  results_dir = "/app/output"; os.makedirs(results_dir, exist_ok=True)
 
753
  if improve_texture:
754
  if not self.latent_upsampler:
755
  raise ValueError("Upscaler espacial não carregado, mas 'improve_texture' está ativo.")
756
+
 
 
757
  # --- ETAPA 1: GERAÇÃO BASE (FIRST PASS) ---
758
  print("\n--- INICIANDO ETAPA 1: GERAÇÃO BASE (FIRST PASS) ---")
759
  t_pass1 = time.perf_counter()
 
762
  downscale_factor = self.config.get("downscale_factor", 0.6666666)
763
  vae_scale_factor = self.pipeline.vae_scale_factor # Geralmente 8
764
 
765
+ # --- <INÍCIO DA LÓGICA DE CÁLCULO EXATA> ---
766
+ # Replica a fórmula da LTXMultiScalePipeline
767
  x_width = int(width_padded * downscale_factor)
768
  downscaled_width = x_width - (x_width % vae_scale_factor)
769
  x_height = int(height_padded * downscale_factor)
770
  downscaled_height = x_height - (x_height % vae_scale_factor)
771
  print(f"[DEBUG] First Pass Dims: Original Pad ({width_padded}x{height_padded}) -> Downscaled ({downscaled_width}x{downscaled_height})")
772
+ # --- <FIM DA LÓGICA DE CÁLCULO EXATA> ---
773
 
774
+ first_pass_kwargs = call_kwargs.copy()
775
+
 
 
776
  first_pass_kwargs.update({
777
  "output_type": "latent",
778
  "width": downscaled_width,
779
  "height": downscaled_height,
780
  "guidance_scale": float(guidance_scale),
781
+ **first_pass_config
782
  })
783
 
784
  print(f"[DEBUG] First Pass: Gerando em {downscaled_width}x{downscaled_height}...")
 
804
  except Exception:
805
  pass
806
 
807
+ latents_parts_up = self._dividir_latentes_por_tamanho(latents_cpu_up,15,1)
808
+
 
 
 
809
  for latents in latents_parts_up:
810
+
 
 
811
  # # --- ETAPA 3: REFINAMENTO DE TEXTURA (SECOND PASS) ---
812
+ print("\n--- INICIANDO ETAPA 3: REFINAMENTO DE TEXTURA (SECOND PASS) ---")
813
+
 
 
814
  second_pass_config = self.config.get("second_pass", {}).copy()
815
+ # --- <INÍCIO DA LÓGICA DE CÁLCULO EXATA PARA SECOND PASS> ---
816
+ # Usa as dimensões da primeira passagem dobradas, como na pipeline original
817
  second_pass_width = downscaled_width * 2
818
  second_pass_height = downscaled_height * 2
819
  print(f"[DEBUG] Second Pass Dims: Target ({second_pass_width}x{second_pass_height})")
820
+ # --- <FIM DA LÓGICA DE CÁLCULO EXATA> ---
821
+ t_pass2 = time.perf_counter()
822
+
823
  num_latent_frames_part = latents.shape[2]
824
+
825
+
826
  vae_temporal_scale = self.pipeline.video_scale_factor # Geralmente 4 ou 8
827
  num_pixel_frames_part = ((num_latent_frames_part - 1) * vae_temporal_scale) + 1
828
  print(f"[DEBUG] Parte: {num_latent_frames_part - 1} latentes -> {num_pixel_frames_part} frames de pixel (alvo)")
829
+
830
+ second_pass_kwargs = call_kwargs.copy()
 
 
 
831
  second_pass_kwargs.update({
832
  "output_type": "latent",
833
  "width": second_pass_width,
834
  "height": second_pass_height,
835
+ #"num_frames": num_pixel_frames_part,
836
+ "latents": upsampled_latents, # O tensor upscaled
837
  "guidance_scale": float(guidance_scale),
838
+ **second_pass_config
839
  })
840
 
841
  print(f"[DEBUG] Second Pass: Refinando em {width_padded}x{height_padded}...")
 
844
  print(f"[DEBUG] Second part Pass concluída em {time.perf_counter() - t_pass2:.2f}s")
845
 
846
  latents_list.append(final_latents)
 
 
 
 
847
 
848
  else: # Geração de etapa única
849
  print("\n--- INICIANDO GERAÇÃO DE ETAPA ÚNICA ---")
 
853
  single_pass_kwargs["guidance_scale"] = float(guidance_scale)
854
  single_pass_kwargs["output_type"] = "latent"
855
 
 
 
 
 
 
 
856
  latents = self.pipeline(**single_pass_kwargs).images
857
  log_tensor_info(latents, "Latentes Finais (Etapa Única)")
858
  print(f"[DEBUG] Etapa única concluída em {time.perf_counter() - t_single:.2f}s")
859
 
860
  latents_list.append(latents)
861
+
862
  # --- ETAPA FINAL: DECODIFICAÇÃO E CODIFICAÇÃO MP4 ---
863
  print("\n--- INICIANDO ETAPA FINAL: DECODIFICAÇÃO E MONTAGEM ---")
864
 
865
+ #latents_cpu = latents.detach().to("cpu", non_blocking=True)
866
+ #torch.cuda.empty_cache()
867
+ #try:
868
+ # torch.cuda.ipc_collect()
869
+ #except Exception:
870
+ # pass
871
+
872
+ latents_parts = []
873
+ for latents in latents_list:
874
+ latents_parts.append(self._dividir_latentes_por_tamanho(latents,15,1))
875
+
876
+
877
  partes_mp4 = []
878
  par = 0
879
+ for latents in latents_parts:
 
 
 
 
 
 
 
 
 
 
880
 
881
+ par = par + 1
882
+ output_video_path = os.path.join(results_dir, f"output_{used_seed}_{par}.mp4")
883
+ final_output_path = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
884
 
885
+ print("[DEBUG] Decodificando bloco de latentes com VAE {par} → tensor de pixels...")
886
+ # Usar manager com timestep por item; previne target_shape e rota NoneType.decode
887
+ pixel_tensor = vae_manager_singleton.decode(
888
+ latents.to(self.device, non_blocking=True),
889
+ decode_timestep=float(self.config.get("decode_timestep", 0.05))
890
+ )
891
+ log_tensor_info(pixel_tensor, "Pixel tensor (VAE saída)")
892
+
893
+ print("[DEBUG] Codificando MP4 a partir do tensor de pixels (bloco inteiro)...")
894
+ video_encode_tool_singleton.save_video_from_tensor(
895
+ pixel_tensor,
896
+ output_video_path,
897
+ fps=call_kwargs["frame_rate"],
898
+ progress_callback=progress_callback
899
+ )
900
+
901
+ candidate = os.path.join(results_dir, f"output_par_{par}.mp4")
902
+ try:
903
+ shutil.move(output_video_path, candidate)
904
+ final_output_path = candidate
905
+ print(f"[DEBUG] MP4 parte {par} movido para {final_output_path}")
906
+ partes_mp4.append(final_output_path)
907
+
908
+ except Exception as e:
909
+ final_output_path = output_video_path
910
+ print(f"[DEBUG] Falha no move; usando tmp como final: {e}")
911
+
912
  total_partes = len(partes_mp4)
913
  if (total_partes>1):
914
  final_vid = os.path.join(results_dir, f"concat_fim_{used_seed}.mp4")