Spaces:
Paused
Paused
Update api/ltx_server.py
Browse files- api/ltx_server.py +95 -404
api/ltx_server.py
CHANGED
|
@@ -396,55 +396,22 @@ class VideoService:
|
|
| 396 |
return out
|
| 397 |
|
| 398 |
|
| 399 |
-
def
|
| 400 |
-
|
| 401 |
-
Divide um tensor de latentes em `quantidade` partes e retorna uma lista de clones.
|
| 402 |
-
|
| 403 |
-
Args:
|
| 404 |
-
latents_brutos: tensor [B, C, T, H, W]
|
| 405 |
-
quantidade: número de partes que queremos dividir
|
| 406 |
|
| 407 |
-
Returns:
|
| 408 |
-
List[Tensor]: lista de `quantidade` partes, cada uma cloneada
|
| 409 |
-
"""
|
| 410 |
-
total = latents_brutos.shape[2] # dimensão temporal
|
| 411 |
-
partes = []
|
| 412 |
-
|
| 413 |
-
if quantidade <= 1 or quantidade > total:
|
| 414 |
-
return [latents_brutos.clone()]
|
| 415 |
-
|
| 416 |
-
# calcular tamanho aproximado de cada parte
|
| 417 |
-
step = total // quantidade
|
| 418 |
-
overlap = 0 # sobreposição mínima de 1 frame entre partes
|
| 419 |
-
|
| 420 |
-
for i in range(quantidade):
|
| 421 |
-
start = i * step
|
| 422 |
-
end = start + step
|
| 423 |
-
if i == quantidade - 1:
|
| 424 |
-
end = total # última parte vai até o final
|
| 425 |
-
else:
|
| 426 |
-
end += overlap # sobreposição
|
| 427 |
-
parte = latents_brutos[:, :, start-1:end+1, :, :].clone()
|
| 428 |
-
partes.append(parte)
|
| 429 |
-
|
| 430 |
-
return partes
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
def dividir_latentes(latents_brutos):
|
| 434 |
-
total = latents_brutos.shape[2] # dimensão temporal (latentes)
|
| 435 |
-
|
| 436 |
if total % 2 == 1: # ÍMPAR
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
|
|
|
|
|
|
| 440 |
else: # PAR
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
return primeira, segunda
|
| 447 |
-
|
| 448 |
|
| 449 |
def _concat_mp4s_no_reencode(self, mp4_list: List[str], out_path: str):
|
| 450 |
"""
|
|
@@ -472,7 +439,6 @@ class VideoService:
|
|
| 472 |
pass
|
| 473 |
|
| 474 |
|
| 475 |
-
|
| 476 |
def generate(
|
| 477 |
self,
|
| 478 |
prompt,
|
|
@@ -575,41 +541,50 @@ class VideoService:
|
|
| 575 |
print(f"[DEBUG] media_items shape={tuple(media.shape)}")
|
| 576 |
|
| 577 |
latents = None
|
| 578 |
-
|
| 579 |
-
try:
|
| 580 |
-
ctx = torch.autocast(device_type="cuda", dtype=self.runtime_autocast_dtype) if self.device == "cuda" else contextlib.nullcontext()
|
| 581 |
|
|
|
|
| 582 |
if improve_texture:
|
| 583 |
if not self.latent_upsampler:
|
| 584 |
raise ValueError("Upscaler espacial não carregado.")
|
| 585 |
|
|
|
|
|
|
|
| 586 |
# --- PASSO 1: GERAÇÃO DE LATENTES EM BAIXA RESOLUÇÃO ---
|
| 587 |
print("[DEBUG] Multi-escala: Iniciando Passo 1 (geração de latentes base).")
|
| 588 |
|
| 589 |
first_pass_args = self.config.get("first_pass", {}).copy()
|
| 590 |
first_pass_kwargs = call_kwargs.copy()
|
|
|
|
|
|
|
| 591 |
first_pass_kwargs.update({
|
| 592 |
-
"guidance_scale":
|
| 593 |
"stg_scale": first_pass_args.get("stg_scale"),
|
| 594 |
"rescaling_scale": first_pass_args.get("rescaling_scale"),
|
| 595 |
"skip_block_list": first_pass_args.get("skip_block_list"),
|
|
|
|
|
|
|
| 596 |
})
|
| 597 |
-
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
first_pass_kwargs["guidance_timesteps"] = schedule
|
| 601 |
-
|
| 602 |
downscale_factor = self.config.get("downscale_factor", 2)
|
| 603 |
original_height = first_pass_kwargs["height"]
|
| 604 |
original_width = first_pass_kwargs["width"]
|
| 605 |
divisor = 24
|
| 606 |
-
|
| 607 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 608 |
height_p1 = round(target_height_p1 / divisor) * divisor
|
| 609 |
if height_p1 == 0: height_p1 = divisor
|
| 610 |
first_pass_kwargs["height"] = height_p1
|
| 611 |
|
| 612 |
-
target_width_p1 = original_width // downscale_factor
|
| 613 |
width_p1 = round(target_width_p1 / divisor) * divisor
|
| 614 |
if width_p1 == 0: width_p1 = divisor
|
| 615 |
first_pass_kwargs["width"] = width_p1
|
|
@@ -622,7 +597,7 @@ class VideoService:
|
|
| 622 |
latents_low_res = first_pass_result.images
|
| 623 |
log_tensor_info(latents_low_res, "Latentes (Passo 1)")
|
| 624 |
|
| 625 |
-
del first_pass_result
|
| 626 |
gc.collect()
|
| 627 |
if self.device == "cuda": torch.cuda.empty_cache()
|
| 628 |
|
|
@@ -641,48 +616,40 @@ class VideoService:
|
|
| 641 |
second_pass_args = self.config.get("second_pass", {}).copy()
|
| 642 |
second_pass_kwargs = call_kwargs.copy()
|
| 643 |
|
| 644 |
-
|
| 645 |
-
width_p2 = width_p1 * 2
|
| 646 |
-
second_pass_kwargs["height"] = height_p2
|
| 647 |
-
second_pass_kwargs["width"] = width_p2
|
| 648 |
-
print(f"[DEBUG] Passo 2: Dimensões definidas para {height_p2}x{width_p2} para corresponder ao upscale.")
|
| 649 |
-
|
| 650 |
second_pass_kwargs.update({
|
| 651 |
-
"guidance_scale":
|
| 652 |
"stg_scale": second_pass_args.get("stg_scale"),
|
| 653 |
"rescaling_scale": second_pass_args.get("rescaling_scale"),
|
| 654 |
"skip_block_list": second_pass_args.get("skip_block_list"),
|
|
|
|
|
|
|
| 655 |
})
|
|
|
|
| 656 |
|
| 657 |
-
|
| 658 |
-
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
self.pipeline.scheduler.set_timesteps(num_steps_passo2_total, device=self.device)
|
| 666 |
-
todos_os_timesteps_p2 = self.pipeline.scheduler.timesteps
|
| 667 |
-
|
| 668 |
-
ponto_de_corte = int(len(todos_os_timesteps_p2) * (1.0 - strength_p2))
|
| 669 |
-
timesteps_para_refinamento = todos_os_timesteps_p2[ponto_de_corte:]
|
| 670 |
-
print(f"[DEBUG] Passo 2: Calculando {len(timesteps_para_refinamento)} timesteps manuais (strength ≈ {strength_p2})")
|
| 671 |
-
|
| 672 |
-
second_pass_kwargs["timesteps"] = timesteps_para_refinamento
|
| 673 |
|
| 674 |
-
|
| 675 |
-
|
| 676 |
second_pass_kwargs["latents"] = latents_high_res
|
| 677 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 678 |
with ctx:
|
| 679 |
second_pass_result = self.pipeline(**second_pass_kwargs)
|
| 680 |
|
| 681 |
latents = second_pass_result.images
|
| 682 |
log_tensor_info(latents, "Latentes Finais (Passo 2)")
|
| 683 |
-
|
| 684 |
-
|
| 685 |
-
|
| 686 |
single_pass_kwargs = call_kwargs.copy()
|
| 687 |
first_pass_config = self.config.get("first_pass", {})
|
| 688 |
single_pass_kwargs.update(
|
|
@@ -702,22 +669,30 @@ class VideoService:
|
|
| 702 |
print(f"[DEBUG] Single-pass: timesteps_len={len(schedule) if schedule else 0}")
|
| 703 |
|
| 704 |
print("\n[INFO] Executando pipeline de etapa única...")
|
|
|
|
|
|
|
| 705 |
with ctx:
|
| 706 |
result = self.pipeline(**single_pass_kwargs)
|
| 707 |
-
|
| 708 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 709 |
print(f"[DEBUG] Latentes (single-pass): shape={tuple(latents.shape)}")
|
| 710 |
|
| 711 |
-
#
|
| 712 |
|
| 713 |
latents_cpu = latents.detach().to("cpu", non_blocking=True)
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
| 718 |
-
|
| 719 |
-
pass
|
| 720 |
|
|
|
|
| 721 |
lat_a, lat_b = self._dividir_latentes(latents_cpu)
|
| 722 |
lat_a1, lat_a2 = self._dividir_latentes(lat_a)
|
| 723 |
lat_b1, lat_b2 = self._dividir_latentes(lat_b)
|
|
@@ -730,19 +705,22 @@ class VideoService:
|
|
| 730 |
partes_mp4 = []
|
| 731 |
par = 0
|
| 732 |
|
| 733 |
-
for
|
| 734 |
-
print(f"[DEBUG] Partição {par}: {tuple(
|
|
|
|
| 735 |
par = par + 1
|
| 736 |
output_video_path = os.path.join(temp_dir, f"output_{used_seed}_{par}.mp4")
|
|
|
|
| 737 |
|
| 738 |
print("[DEBUG] Decodificando bloco de latentes com VAE → tensor de pixels...")
|
|
|
|
| 739 |
pixel_tensor = vae_manager_singleton.decode(
|
| 740 |
-
|
| 741 |
decode_timestep=float(self.config.get("decode_timestep", 0.05))
|
| 742 |
)
|
| 743 |
log_tensor_info(pixel_tensor, "Pixel tensor (VAE saída)")
|
| 744 |
|
| 745 |
-
print("[DEBUG] Codificando MP4 a partir do tensor de pixels...")
|
| 746 |
video_encode_tool_singleton.save_video_from_tensor(
|
| 747 |
pixel_tensor,
|
| 748 |
output_video_path,
|
|
@@ -756,7 +734,9 @@ class VideoService:
|
|
| 756 |
final_output_path = candidate
|
| 757 |
print(f"[DEBUG] MP4 parte {par} movido para {final_output_path}")
|
| 758 |
partes_mp4.append(final_output_path)
|
|
|
|
| 759 |
except Exception as e:
|
|
|
|
| 760 |
print(f"[DEBUG] Falha no move; usando tmp como final: {e}")
|
| 761 |
|
| 762 |
final_concat = os.path.join(results_dir, f"concat_fim_{used_seed}.mp4")
|
|
@@ -765,320 +745,31 @@ class VideoService:
|
|
| 765 |
self._log_gpu_memory("Fim da Geração")
|
| 766 |
return final_concat, used_seed
|
| 767 |
|
| 768 |
-
except Exception as e:
|
| 769 |
-
print("[DEBUG] EXCEÇÃO NA GERAÇÃO:")
|
| 770 |
-
print("".join(traceback.format_exception(type(e), e, e.__traceback__)))
|
| 771 |
-
raise
|
| 772 |
-
|
| 773 |
-
# ltx_server.py
|
| 774 |
-
|
| 775 |
-
def generate(
|
| 776 |
-
self,
|
| 777 |
-
prompt,
|
| 778 |
-
negative_prompt,
|
| 779 |
-
mode="text-to-video",
|
| 780 |
-
start_image_filepath=None,
|
| 781 |
-
middle_image_filepath=None,
|
| 782 |
-
middle_frame_number=None,
|
| 783 |
-
middle_image_weight=1.0,
|
| 784 |
-
end_image_filepath=None,
|
| 785 |
-
end_image_weight=1.0,
|
| 786 |
-
input_video_filepath=None,
|
| 787 |
-
height=512,
|
| 788 |
-
width=704,
|
| 789 |
-
duration=2.0,
|
| 790 |
-
frames_to_use=9,
|
| 791 |
-
seed=42,
|
| 792 |
-
randomize_seed=True,
|
| 793 |
-
guidance_scale=3.0, # Valor de referência/fallback
|
| 794 |
-
improve_texture=True,
|
| 795 |
-
progress_callback=None,
|
| 796 |
-
external_decode=True,
|
| 797 |
-
):
|
| 798 |
-
t_all = time.perf_counter()
|
| 799 |
-
print(f"[DEBUG] generate() begin mode={mode} external_decode={external_decode} improve_texture={improve_texture}")
|
| 800 |
-
if self.device == "cuda":
|
| 801 |
-
torch.cuda.empty_cache(); torch.cuda.reset_peak_memory_stats()
|
| 802 |
-
self._log_gpu_memory("Início da Geração")
|
| 803 |
-
|
| 804 |
-
if mode == "image-to-video" and not start_image_filepath:
|
| 805 |
-
raise ValueError("A imagem de início é obrigatória para o modo image-to-video")
|
| 806 |
-
if mode == "video-to-video" and not input_video_filepath:
|
| 807 |
-
raise ValueError("O vídeo de entrada é obrigatório para o modo video-to-video")
|
| 808 |
-
|
| 809 |
-
used_seed = random.randint(0, 2**32 - 1) if randomize_seed else int(seed)
|
| 810 |
-
seed_everething(used_seed); print(f"[DEBUG] Seed usado: {used_seed}")
|
| 811 |
-
|
| 812 |
-
FPS = 24.0; MAX_NUM_FRAMES = 2570
|
| 813 |
-
target_frames_rounded = round(duration * FPS)
|
| 814 |
-
n_val = round((float(target_frames_rounded) - 1.0) / 8.0)
|
| 815 |
-
actual_num_frames = max(9, min(MAX_NUM_FRAMES, int(n_val * 8 + 1)))
|
| 816 |
-
print(f"[DEBUG] Frames alvo: {actual_num_frames} (dur={duration}s @ {FPS}fps)")
|
| 817 |
-
|
| 818 |
-
height_padded = ((height - 1) // 32 + 1) * 32
|
| 819 |
-
width_padded = ((width - 1) // 32 + 1) * 32
|
| 820 |
-
padding_values = calculate_padding(height, width, height_padded, width_padded)
|
| 821 |
-
print(f"[DEBUG] Dimensões: ({height},{width}) -> pad ({height_padded},{width_padded}); padding={padding_values}")
|
| 822 |
-
|
| 823 |
-
generator = torch.Generator(device=self.device).manual_seed(used_seed)
|
| 824 |
-
conditioning_items = []
|
| 825 |
-
|
| 826 |
-
if mode == "image-to-video":
|
| 827 |
-
start_tensor = self._prepare_conditioning_tensor(start_image_filepath, height, width, padding_values)
|
| 828 |
-
conditioning_items.append(ConditioningItem(start_tensor, 0, 1.0))
|
| 829 |
-
if middle_image_filepath and middle_frame_number is not None:
|
| 830 |
-
middle_tensor = self._prepare_conditioning_tensor(middle_image_filepath, height, width, padding_values)
|
| 831 |
-
safe_middle_frame = max(0, min(int(middle_frame_number), actual_num_frames - 1))
|
| 832 |
-
conditioning_items.append(ConditioningItem(middle_tensor, safe_middle_frame, float(middle_image_weight)))
|
| 833 |
-
if end_image_filepath:
|
| 834 |
-
end_tensor = self._prepare_conditioning_tensor(end_image_filepath, height, width, padding_values)
|
| 835 |
-
last_frame_index = actual_num_frames - 1
|
| 836 |
-
conditioning_items.append(ConditioningItem(end_tensor, last_frame_index, float(end_image_weight)))
|
| 837 |
-
print(f"[DEBUG] Conditioning items: {len(conditioning_items)}")
|
| 838 |
-
|
| 839 |
-
# --- LÓGICA DE CONVERSÃO DO STG_MODE ---
|
| 840 |
-
stg_mode_str = self.config.get("stg_mode", "attention_values")
|
| 841 |
-
stg_mode_map = {
|
| 842 |
-
"attention_values": "AttentionValues",
|
| 843 |
-
"attention_skip": "AttentionSkip",
|
| 844 |
-
"residual": "Residual",
|
| 845 |
-
"transformer_block": "TransformerBlock"
|
| 846 |
-
}
|
| 847 |
-
stg_mode_enum_key = stg_mode_map.get(stg_mode_str.lower(), "AttentionValues")
|
| 848 |
-
# --- FIM DA LÓGICA DE CONVERSÃO ---
|
| 849 |
-
|
| 850 |
-
call_kwargs = {
|
| 851 |
-
"prompt": prompt,
|
| 852 |
-
"negative_prompt": negative_prompt,
|
| 853 |
-
"height": height_padded,
|
| 854 |
-
"width": width_padded,
|
| 855 |
-
"num_frames": actual_num_frames,
|
| 856 |
-
"frame_rate": int(FPS),
|
| 857 |
-
"generator": generator,
|
| 858 |
-
"output_type": "latent",
|
| 859 |
-
"conditioning_items": conditioning_items if conditioning_items else None,
|
| 860 |
-
"media_items": None,
|
| 861 |
-
"decode_timestep": self.config.get("decode_timestep"),
|
| 862 |
-
"decode_noise_scale": self.config.get("decode_noise_scale"),
|
| 863 |
-
"stochastic_sampling": self.config.get("stochastic_sampling"),
|
| 864 |
-
"image_cond_noise_scale": self.config.get("image_cond_noise_scale", 0.01),
|
| 865 |
-
"is_video": True,
|
| 866 |
-
"vae_per_channel_normalize": self.config.get("vae_per_channel_normalize", True),
|
| 867 |
-
"mixed_precision": (self.config.get("precision") == "mixed_precision"),
|
| 868 |
-
"offload_to_cpu": False,
|
| 869 |
-
"enhance_prompt": False,
|
| 870 |
-
"skip_layer_strategy": SkipLayerStrategy[stg_mode_enum_key],
|
| 871 |
-
}
|
| 872 |
-
print(f"[DEBUG] output_type={call_kwargs['output_type']} skip_layer_strategy={call_kwargs['skip_layer_strategy']}")
|
| 873 |
-
|
| 874 |
-
if mode == "video-to-video":
|
| 875 |
-
media = load_media_file(
|
| 876 |
-
media_path=input_video_filepath,
|
| 877 |
-
height=height,
|
| 878 |
-
width=width,
|
| 879 |
-
max_frames=int(frames_to_use),
|
| 880 |
-
padding=padding_values,
|
| 881 |
-
).to(self.device)
|
| 882 |
-
call_kwargs["media_items"] = media
|
| 883 |
-
print(f"[DEBUG] media_items shape={tuple(media.shape)}")
|
| 884 |
-
|
| 885 |
-
latents = None
|
| 886 |
-
|
| 887 |
-
try:
|
| 888 |
-
ctx = torch.autocast(device_type="cuda", dtype=self.runtime_autocast_dtype) if self.device == "cuda" else contextlib.nullcontext()
|
| 889 |
-
|
| 890 |
-
if improve_texture:
|
| 891 |
-
if not self.latent_upsampler:
|
| 892 |
-
raise ValueError("Upscaler espacial não carregado.")
|
| 893 |
-
|
| 894 |
-
# --- PASSO 1: GERAÇ��O DE LATENTES EM BAIXA RESOLUÇÃO ---
|
| 895 |
-
print("[DEBUG] Multi-escala: Iniciando Passo 1 (geração de latentes base).")
|
| 896 |
-
|
| 897 |
-
first_pass_args = self.config.get("first_pass", {}).copy()
|
| 898 |
-
first_pass_kwargs = call_kwargs.copy()
|
| 899 |
-
|
| 900 |
-
first_pass_kwargs.update({
|
| 901 |
-
"guidance_scale": first_pass_args.get("guidance_scale", guidance_scale),
|
| 902 |
-
"stg_scale": first_pass_args.get("stg_scale"),
|
| 903 |
-
"rescaling_scale": first_pass_args.get("rescaling_scale"),
|
| 904 |
-
"skip_block_list": first_pass_args.get("skip_block_list"),
|
| 905 |
-
"guidance_timesteps": first_pass_args.get("guidance_timesteps"),
|
| 906 |
-
"timesteps": first_pass_args.get("timesteps")
|
| 907 |
-
})
|
| 908 |
-
print(f"[DEBUG] Passo 1: Parâmetros do config carregados: guidance_scale={first_pass_kwargs['guidance_scale']}, stg_scale={first_pass_kwargs['stg_scale']}")
|
| 909 |
-
|
| 910 |
-
downscale_factor = self.config.get("downscale_factor", 2)
|
| 911 |
-
original_height = first_pass_kwargs["height"]
|
| 912 |
-
original_width = first_pass_kwargs["width"]
|
| 913 |
-
divisor = 24
|
| 914 |
-
target_height_p1 = original_height // downscale_factor
|
| 915 |
-
height_p1 = round(target_height_p1 / divisor) * divisor
|
| 916 |
-
if height_p1 == 0: height_p1 = divisor
|
| 917 |
-
first_pass_kwargs["height"] = height_p1
|
| 918 |
-
target_width_p1 = original_width // downscale_factor
|
| 919 |
-
width_p1 = round(target_width_p1 / divisor) * divisor
|
| 920 |
-
if width_p1 == 0: width_p1 = divisor
|
| 921 |
-
first_pass_kwargs["width"] = width_p1
|
| 922 |
-
print(f"[DEBUG] Passo 1: Dimensões reduzidas e ajustadas para {height_p1}x{width_p1}")
|
| 923 |
-
|
| 924 |
-
with ctx:
|
| 925 |
-
first_pass_result = self.pipeline(**first_pass_kwargs)
|
| 926 |
-
|
| 927 |
-
latents_low_res = first_pass_result.images
|
| 928 |
-
log_tensor_info(latents_low_res, "Latentes (Passo 1)")
|
| 929 |
-
|
| 930 |
-
del first_pass_result
|
| 931 |
-
gc.collect()
|
| 932 |
-
if self.device == "cuda": torch.cuda.empty_cache()
|
| 933 |
-
|
| 934 |
-
# --- PASSO INTERMEDIÁRIO: UPSCALE DOS LATENTES ---
|
| 935 |
-
print("[DEBUG] Multi-escala: Fazendo upscale dos latentes com latent_upsampler.")
|
| 936 |
-
with ctx:
|
| 937 |
-
latents_high_res = self.latent_upsampler(latents_low_res)
|
| 938 |
-
|
| 939 |
-
log_tensor_info(latents_high_res, "Latentes (Pós-Upscale)")
|
| 940 |
-
del latents_low_res
|
| 941 |
-
gc.collect()
|
| 942 |
-
if self.device == "cuda": torch.cuda.empty_cache()
|
| 943 |
-
|
| 944 |
-
# --- PASSO 2: REFINAMENTO EM ALTA RESOLUÇÃO ---
|
| 945 |
-
print("[DEBUG] Multi-escala: Iniciando Passo 2 (refinamento em alta resolução).")
|
| 946 |
-
second_pass_args = self.config.get("second_pass", {}).copy()
|
| 947 |
-
second_pass_kwargs = call_kwargs.copy()
|
| 948 |
-
|
| 949 |
-
second_pass_kwargs.update({
|
| 950 |
-
"guidance_scale": second_pass_args.get("guidance_scale", guidance_scale),
|
| 951 |
-
"stg_scale": second_pass_args.get("stg_scale"),
|
| 952 |
-
"rescaling_scale": second_pass_args.get("rescaling_scale"),
|
| 953 |
-
"skip_block_list": second_pass_args.get("skip_block_list"),
|
| 954 |
-
"guidance_timesteps": second_pass_args.get("guidance_timesteps"),
|
| 955 |
-
"timesteps": second_pass_args.get("timesteps")
|
| 956 |
-
})
|
| 957 |
-
print(f"[DEBUG] Passo 2: Parâmetros do config carregados: guidance_scale={second_pass_kwargs['guidance_scale']}, stg_scale={second_pass_kwargs['stg_scale']}")
|
| 958 |
-
|
| 959 |
-
height_p2 = height_p1 * 2
|
| 960 |
-
width_p2 = width_p1 * 2
|
| 961 |
-
second_pass_kwargs["height"] = height_p2
|
| 962 |
-
second_pass_kwargs["width"] = width_p2
|
| 963 |
-
print(f"[DEBUG] Passo 2: Dimensões definidas para {height_p2}x{width_p2}")
|
| 964 |
-
|
| 965 |
-
second_pass_kwargs["latents"] = latents_high_res
|
| 966 |
-
|
| 967 |
-
with ctx:
|
| 968 |
-
second_pass_result = self.pipeline(**second_pass_kwargs)
|
| 969 |
-
|
| 970 |
-
latents = second_pass_result.images
|
| 971 |
-
log_tensor_info(latents, "Latentes Finais (Passo 2)")
|
| 972 |
-
|
| 973 |
-
else:
|
| 974 |
-
# --- PASSO ÚNICO (SINGLE-PASS) ---
|
| 975 |
-
single_pass_kwargs = call_kwargs.copy()
|
| 976 |
-
|
| 977 |
-
single_pass_kwargs.update({
|
| 978 |
-
"guidance_scale": self.config.get("guidance_scale", guidance_scale),
|
| 979 |
-
"stg_scale": self.config.get("stg_scale"),
|
| 980 |
-
"rescaling_scale": self.config.get("rescaling_scale"),
|
| 981 |
-
"skip_block_list": self.config.get("skip_block_list"),
|
| 982 |
-
"guidance_timesteps": self.config.get("guidance_timesteps"),
|
| 983 |
-
"timesteps": self.config.get("timesteps"),
|
| 984 |
-
"num_inference_steps": self.config.get("num_inference_steps", 20)
|
| 985 |
-
})
|
| 986 |
-
|
| 987 |
-
print("\n[INFO] Executando pipeline de etapa única...")
|
| 988 |
-
with ctx:
|
| 989 |
-
result = self.pipeline(**single_pass_kwargs)
|
| 990 |
-
|
| 991 |
-
latents = result.images
|
| 992 |
-
print(f"[DEBUG] Latentes (single-pass): shape={tuple(latents.shape)}")
|
| 993 |
-
|
| 994 |
-
# --- DECODIFICAÇÃO E CODIFICAÇÃO DE VÍDEO FINAL ---
|
| 995 |
-
latents_cpu = latents.detach().to("cpu", non_blocking=True)
|
| 996 |
-
if self.device == "cuda":
|
| 997 |
-
torch.cuda.empty_cache()
|
| 998 |
-
try: torch.cuda.ipc_collect()
|
| 999 |
-
except Exception: pass
|
| 1000 |
-
|
| 1001 |
-
lat_a, lat_b = self._dividir_latentes(latents_cpu)
|
| 1002 |
-
if lat_a is not None:
|
| 1003 |
-
lat_a1, lat_a2 = self._dividir_latentes(lat_a)
|
| 1004 |
-
else:
|
| 1005 |
-
lat_a1, lat_a2 = None, None
|
| 1006 |
-
if lat_b is not None:
|
| 1007 |
-
lat_b1, lat_b2 = self._dividir_latentes(lat_b)
|
| 1008 |
-
else:
|
| 1009 |
-
lat_b1, lat_b2 = None, None
|
| 1010 |
-
|
| 1011 |
-
latents_parts = [p for p in [lat_a1, lat_a2, lat_b1, lat_b2] if p is not None and p.shape[2] > 1]
|
| 1012 |
-
if not latents_parts:
|
| 1013 |
-
latents_parts = [latents_cpu]
|
| 1014 |
|
| 1015 |
-
temp_dir = tempfile.mkdtemp(prefix="ltxv_"); self._register_tmp_dir(temp_dir)
|
| 1016 |
-
results_dir = "/app/output"; os.makedirs(results_dir, exist_ok=True)
|
| 1017 |
-
partes_mp4 = []
|
| 1018 |
-
par = 0
|
| 1019 |
-
|
| 1020 |
-
for part in latents_parts:
|
| 1021 |
-
par += 1
|
| 1022 |
-
print(f"[DEBUG] Partição {par}: {tuple(part.shape)}")
|
| 1023 |
-
output_video_path = os.path.join(temp_dir, f"output_{used_seed}_{par}.mp4")
|
| 1024 |
-
|
| 1025 |
-
print("[DEBUG] Decodificando bloco de latentes com VAE → tensor de pixels...")
|
| 1026 |
-
pixel_tensor = vae_manager_singleton.decode(
|
| 1027 |
-
part.to(self.device, non_blocking=True),
|
| 1028 |
-
decode_timestep=float(self.config.get("decode_timestep", 0.05))
|
| 1029 |
-
)
|
| 1030 |
-
log_tensor_info(pixel_tensor, "Pixel tensor (VAE saída)")
|
| 1031 |
-
|
| 1032 |
-
print("[DEBUG] Codificando MP4 a partir do tensor de pixels...")
|
| 1033 |
-
video_encode_tool_singleton.save_video_from_tensor(
|
| 1034 |
-
pixel_tensor,
|
| 1035 |
-
output_video_path,
|
| 1036 |
-
fps=call_kwargs["frame_rate"],
|
| 1037 |
-
progress_callback=progress_callback
|
| 1038 |
-
)
|
| 1039 |
-
|
| 1040 |
-
candidate = os.path.join(results_dir, f"output_par_{par}.mp4")
|
| 1041 |
-
try:
|
| 1042 |
-
shutil.move(output_video_path, candidate)
|
| 1043 |
-
print(f"[DEBUG] MP4 parte {par} movido para {candidate}")
|
| 1044 |
-
partes_mp4.append(candidate)
|
| 1045 |
-
except Exception as e:
|
| 1046 |
-
print(f"[DEBUG] Falha no move; usando tmp como final: {e}")
|
| 1047 |
-
partes_mp4.append(output_video_path)
|
| 1048 |
-
|
| 1049 |
-
final_concat = os.path.join(results_dir, f"concat_fim_{used_seed}.mp4")
|
| 1050 |
-
if partes_mp4:
|
| 1051 |
-
if len(partes_mp4) == 1:
|
| 1052 |
-
shutil.move(partes_mp4[0], final_concat)
|
| 1053 |
-
print(f"[DEBUG] Apenas uma parte, movida para {final_concat}")
|
| 1054 |
-
else:
|
| 1055 |
-
self._concat_mp4s_no_reencode(partes_mp4, final_concat)
|
| 1056 |
-
else:
|
| 1057 |
-
print("[WARN] Nenhuma parte de vídeo foi gerada para concatenar.")
|
| 1058 |
-
return None, used_seed
|
| 1059 |
-
|
| 1060 |
-
self._log_gpu_memory("Fim da Geração")
|
| 1061 |
-
return final_concat, used_seed
|
| 1062 |
-
|
| 1063 |
except Exception as e:
|
| 1064 |
print("[DEBUG] EXCEÇÃO NA GERAÇÃO:")
|
| 1065 |
print("".join(traceback.format_exception(type(e), e, e.__traceback__)))
|
| 1066 |
raise
|
| 1067 |
finally:
|
| 1068 |
try:
|
| 1069 |
-
del latents
|
| 1070 |
-
except
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1071 |
pass
|
| 1072 |
-
except Exception as e:
|
| 1073 |
-
print(f"[DEBUG] Erro na limpeza de variáveis: {e}")
|
| 1074 |
|
| 1075 |
gc.collect()
|
| 1076 |
-
|
| 1077 |
-
|
| 1078 |
torch.cuda.empty_cache()
|
| 1079 |
-
|
| 1080 |
-
|
| 1081 |
-
|
|
|
|
|
|
|
|
|
|
| 1082 |
|
| 1083 |
try:
|
| 1084 |
self.finalize(keep_paths=[])
|
|
@@ -1086,4 +777,4 @@ class VideoService:
|
|
| 1086 |
print(f"[DEBUG] finalize() no finally falhou: {e}")
|
| 1087 |
|
| 1088 |
print("Criando instância do VideoService. O carregamento do modelo começará agora...")
|
| 1089 |
-
video_generation_service = VideoService()
|
|
|
|
| 396 |
return out
|
| 397 |
|
| 398 |
|
| 399 |
+
def _dividir_latentes(self, latents_brutos):
|
| 400 |
+
total = latents_brutos.shape[2] # dimensão temporal (número de latentes)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 402 |
if total % 2 == 1: # ÍMPAR
|
| 403 |
+
# Ex: 11 → primeira 0..5, segunda 5..10
|
| 404 |
+
cut = total // 2
|
| 405 |
+
primeira = latents_brutos[:, :, :cut+1, :, :].clone()
|
| 406 |
+
segunda = latents_brutos[:, :, cut:, :, :].clone()
|
| 407 |
+
|
| 408 |
else: # PAR
|
| 409 |
+
# Ex: 12 → primeira 0..5, segunda 5..11
|
| 410 |
+
cut = total // 2
|
| 411 |
+
primeira = latents_brutos[:, :, :cut, :, :].clone()
|
| 412 |
+
segunda = latents_brutos[:, :, cut-1:, :, :].clone()
|
| 413 |
+
|
| 414 |
return primeira, segunda
|
|
|
|
| 415 |
|
| 416 |
def _concat_mp4s_no_reencode(self, mp4_list: List[str], out_path: str):
|
| 417 |
"""
|
|
|
|
| 439 |
pass
|
| 440 |
|
| 441 |
|
|
|
|
| 442 |
def generate(
|
| 443 |
self,
|
| 444 |
prompt,
|
|
|
|
| 541 |
print(f"[DEBUG] media_items shape={tuple(media.shape)}")
|
| 542 |
|
| 543 |
latents = None
|
| 544 |
+
multi_scale_pipeline = None
|
|
|
|
|
|
|
| 545 |
|
| 546 |
+
try:
|
| 547 |
if improve_texture:
|
| 548 |
if not self.latent_upsampler:
|
| 549 |
raise ValueError("Upscaler espacial não carregado.")
|
| 550 |
|
| 551 |
+
# --- INÍCIO DA IMPLEMENTAÇÃO LIMPA DOS 3 PASSOS ---
|
| 552 |
+
|
| 553 |
# --- PASSO 1: GERAÇÃO DE LATENTES EM BAIXA RESOLUÇÃO ---
|
| 554 |
print("[DEBUG] Multi-escala: Iniciando Passo 1 (geração de latentes base).")
|
| 555 |
|
| 556 |
first_pass_args = self.config.get("first_pass", {}).copy()
|
| 557 |
first_pass_kwargs = call_kwargs.copy()
|
| 558 |
+
|
| 559 |
+
# Carrega os parâmetros do config, incluindo listas de timesteps e guidance
|
| 560 |
first_pass_kwargs.update({
|
| 561 |
+
"guidance_scale": first_pass_args.get("guidance_scale", guidance_scale),
|
| 562 |
"stg_scale": first_pass_args.get("stg_scale"),
|
| 563 |
"rescaling_scale": first_pass_args.get("rescaling_scale"),
|
| 564 |
"skip_block_list": first_pass_args.get("skip_block_list"),
|
| 565 |
+
"guidance_timesteps": first_pass_args.get("guidance_timesteps"),
|
| 566 |
+
"timesteps": first_pass_args.get("timesteps")
|
| 567 |
})
|
| 568 |
+
print(f"[DEBUG] Passo 1: Parâmetros do config carregados.")
|
| 569 |
+
|
| 570 |
+
# Calcula as dimensões de baixa resolução
|
|
|
|
|
|
|
| 571 |
downscale_factor = self.config.get("downscale_factor", 2)
|
| 572 |
original_height = first_pass_kwargs["height"]
|
| 573 |
original_width = first_pass_kwargs["width"]
|
| 574 |
divisor = 24
|
| 575 |
+
|
| 576 |
+
# Para downscale_factor < 1 (ex: 0.666), a lógica é multiplicar
|
| 577 |
+
if downscale_factor < 1.0:
|
| 578 |
+
target_height_p1 = original_height * downscale_factor
|
| 579 |
+
target_width_p1 = original_width * downscale_factor
|
| 580 |
+
else: # Para downscale_factor >= 1, a lógica é dividir
|
| 581 |
+
target_height_p1 = original_height // downscale_factor
|
| 582 |
+
target_width_p1 = original_width // downscale_factor
|
| 583 |
+
|
| 584 |
height_p1 = round(target_height_p1 / divisor) * divisor
|
| 585 |
if height_p1 == 0: height_p1 = divisor
|
| 586 |
first_pass_kwargs["height"] = height_p1
|
| 587 |
|
|
|
|
| 588 |
width_p1 = round(target_width_p1 / divisor) * divisor
|
| 589 |
if width_p1 == 0: width_p1 = divisor
|
| 590 |
first_pass_kwargs["width"] = width_p1
|
|
|
|
| 597 |
latents_low_res = first_pass_result.images
|
| 598 |
log_tensor_info(latents_low_res, "Latentes (Passo 1)")
|
| 599 |
|
| 600 |
+
del first_pass_result, first_pass_kwargs
|
| 601 |
gc.collect()
|
| 602 |
if self.device == "cuda": torch.cuda.empty_cache()
|
| 603 |
|
|
|
|
| 616 |
second_pass_args = self.config.get("second_pass", {}).copy()
|
| 617 |
second_pass_kwargs = call_kwargs.copy()
|
| 618 |
|
| 619 |
+
# Carrega os parâmetros do config para o segundo passo
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 620 |
second_pass_kwargs.update({
|
| 621 |
+
"guidance_scale": second_pass_args.get("guidance_scale", guidance_scale),
|
| 622 |
"stg_scale": second_pass_args.get("stg_scale"),
|
| 623 |
"rescaling_scale": second_pass_args.get("rescaling_scale"),
|
| 624 |
"skip_block_list": second_pass_args.get("skip_block_list"),
|
| 625 |
+
"guidance_timesteps": second_pass_args.get("guidance_timesteps"),
|
| 626 |
+
"timesteps": second_pass_args.get("timesteps")
|
| 627 |
})
|
| 628 |
+
print(f"[DEBUG] Passo 2: Parâmetros do config carregados.")
|
| 629 |
|
| 630 |
+
# Define as dimensões de alta resolução com base no upscale
|
| 631 |
+
# O upsampler espacial dobra a resolução, então multiplicamos por 2
|
| 632 |
+
height_p2 = height_p1 * 2
|
| 633 |
+
width_p2 = width_p1 * 2
|
| 634 |
+
second_pass_kwargs["height"] = height_p2
|
| 635 |
+
second_pass_kwargs["width"] = width_p2
|
| 636 |
+
print(f"[DEBUG] Passo 2: Dimensões definidas para {height_p2}x{width_p2}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 637 |
|
| 638 |
+
# A entrada para o refinamento são os latentes que sofreram upscale
|
|
|
|
| 639 |
second_pass_kwargs["latents"] = latents_high_res
|
| 640 |
|
| 641 |
+
# Garante que 'strength' não seja passado, pois estamos controlando via timesteps
|
| 642 |
+
if "strength" in second_pass_kwargs:
|
| 643 |
+
del second_pass_kwargs["strength"]
|
| 644 |
+
|
| 645 |
with ctx:
|
| 646 |
second_pass_result = self.pipeline(**second_pass_kwargs)
|
| 647 |
|
| 648 |
latents = second_pass_result.images
|
| 649 |
log_tensor_info(latents, "Latentes Finais (Passo 2)")
|
| 650 |
+
|
| 651 |
+
|
| 652 |
+
else:
|
| 653 |
single_pass_kwargs = call_kwargs.copy()
|
| 654 |
first_pass_config = self.config.get("first_pass", {})
|
| 655 |
single_pass_kwargs.update(
|
|
|
|
| 669 |
print(f"[DEBUG] Single-pass: timesteps_len={len(schedule) if schedule else 0}")
|
| 670 |
|
| 671 |
print("\n[INFO] Executando pipeline de etapa única...")
|
| 672 |
+
t_sp = time.perf_counter()
|
| 673 |
+
ctx = torch.autocast(device_type="cuda", dtype=self.runtime_autocast_dtype) if self.device == "cuda" else contextlib.nullcontext()
|
| 674 |
with ctx:
|
| 675 |
result = self.pipeline(**single_pass_kwargs)
|
| 676 |
+
print(f"[DEBUG] single-pass tempo={time.perf_counter()-t_sp:.3f}s")
|
| 677 |
+
|
| 678 |
+
if hasattr(result, "latents"):
|
| 679 |
+
latents = result.latents
|
| 680 |
+
elif hasattr(result, "images") and isinstance(result.images, torch.Tensor):
|
| 681 |
+
latents = result.images
|
| 682 |
+
else:
|
| 683 |
+
latents = result
|
| 684 |
print(f"[DEBUG] Latentes (single-pass): shape={tuple(latents.shape)}")
|
| 685 |
|
| 686 |
+
# Staging e escrita MP4 (simples: VAE → pixels → MP4)
|
| 687 |
|
| 688 |
latents_cpu = latents.detach().to("cpu", non_blocking=True)
|
| 689 |
+
torch.cuda.empty_cache()
|
| 690 |
+
try:
|
| 691 |
+
torch.cuda.ipc_collect()
|
| 692 |
+
except Exception:
|
| 693 |
+
pass
|
|
|
|
| 694 |
|
| 695 |
+
# 2) Divide em duas partes
|
| 696 |
lat_a, lat_b = self._dividir_latentes(latents_cpu)
|
| 697 |
lat_a1, lat_a2 = self._dividir_latentes(lat_a)
|
| 698 |
lat_b1, lat_b2 = self._dividir_latentes(lat_b)
|
|
|
|
| 705 |
partes_mp4 = []
|
| 706 |
par = 0
|
| 707 |
|
| 708 |
+
for latents in latents_parts:
|
| 709 |
+
print(f"[DEBUG] Partição {par}: {tuple(latents.shape)}")
|
| 710 |
+
|
| 711 |
par = par + 1
|
| 712 |
output_video_path = os.path.join(temp_dir, f"output_{used_seed}_{par}.mp4")
|
| 713 |
+
final_output_path = None
|
| 714 |
|
| 715 |
print("[DEBUG] Decodificando bloco de latentes com VAE → tensor de pixels...")
|
| 716 |
+
# Usar manager com timestep por item; previne target_shape e rota NoneType.decode
|
| 717 |
pixel_tensor = vae_manager_singleton.decode(
|
| 718 |
+
latents.to(self.device, non_blocking=True),
|
| 719 |
decode_timestep=float(self.config.get("decode_timestep", 0.05))
|
| 720 |
)
|
| 721 |
log_tensor_info(pixel_tensor, "Pixel tensor (VAE saída)")
|
| 722 |
|
| 723 |
+
print("[DEBUG] Codificando MP4 a partir do tensor de pixels (bloco inteiro)...")
|
| 724 |
video_encode_tool_singleton.save_video_from_tensor(
|
| 725 |
pixel_tensor,
|
| 726 |
output_video_path,
|
|
|
|
| 734 |
final_output_path = candidate
|
| 735 |
print(f"[DEBUG] MP4 parte {par} movido para {final_output_path}")
|
| 736 |
partes_mp4.append(final_output_path)
|
| 737 |
+
|
| 738 |
except Exception as e:
|
| 739 |
+
final_output_path = output_video_path
|
| 740 |
print(f"[DEBUG] Falha no move; usando tmp como final: {e}")
|
| 741 |
|
| 742 |
final_concat = os.path.join(results_dir, f"concat_fim_{used_seed}.mp4")
|
|
|
|
| 745 |
self._log_gpu_memory("Fim da Geração")
|
| 746 |
return final_concat, used_seed
|
| 747 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 748 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 749 |
except Exception as e:
|
| 750 |
print("[DEBUG] EXCEÇÃO NA GERAÇÃO:")
|
| 751 |
print("".join(traceback.format_exception(type(e), e, e.__traceback__)))
|
| 752 |
raise
|
| 753 |
finally:
|
| 754 |
try:
|
| 755 |
+
del latents
|
| 756 |
+
except Exception:
|
| 757 |
+
pass
|
| 758 |
+
try:
|
| 759 |
+
del multi_scale_pipeline
|
| 760 |
+
except Exception:
|
| 761 |
pass
|
|
|
|
|
|
|
| 762 |
|
| 763 |
gc.collect()
|
| 764 |
+
try:
|
| 765 |
+
if self.device == "cuda":
|
| 766 |
torch.cuda.empty_cache()
|
| 767 |
+
try:
|
| 768 |
+
torch.cuda.ipc_collect()
|
| 769 |
+
except Exception:
|
| 770 |
+
pass
|
| 771 |
+
except Exception as e:
|
| 772 |
+
print(f"[DEBUG] Limpeza GPU no finally falhou: {e}")
|
| 773 |
|
| 774 |
try:
|
| 775 |
self.finalize(keep_paths=[])
|
|
|
|
| 777 |
print(f"[DEBUG] finalize() no finally falhou: {e}")
|
| 778 |
|
| 779 |
print("Criando instância do VideoService. O carregamento do modelo começará agora...")
|
| 780 |
+
video_generation_service = VideoService()
|