EuuIia commited on
Commit
fe0bcf4
·
verified ·
1 Parent(s): 90d1500

Update api/ltx_server.py

Browse files
Files changed (1) hide show
  1. api/ltx_server.py +115 -2
api/ltx_server.py CHANGED
@@ -435,9 +435,123 @@ class VideoService:
435
  print("================PODA CAUSAL=================")
436
  return chunks
437
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438
 
439
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
  def _gerar_lista_com_transicoes(self, pasta: str, video_paths: list[str], crossfade_frames: int = 8) -> list[str]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441
  """
442
  Gera uma nova lista de vídeos aplicando transições suaves (crossfade de N frames)
443
  entre cada par de vídeos da lista original.
@@ -530,8 +644,7 @@ class VideoService:
530
  print(f"[DEBUG] Nova lista final de {len(nova_lista)} arquivos criada.")
531
  return nova_lista
532
 
533
-
534
- def _concat_mp4s_no_reencode(self, mp4_list: List[str], out_path: str):
535
  """
536
  Concatena múltiplos MP4s sem reencode usando o demuxer do ffmpeg.
537
  ATENÇÃO: todos os arquivos precisam ter mesmo codec, fps, resolução etc.
 
435
  print("================PODA CAUSAL=================")
436
  return chunks
437
 
438
+ def _concat_mp4s_no_reencode(self, mp4_list: List[str], out_path: str):
439
+ """
440
+ Concatena múltiplos MP4s sem reencode usando o demuxer do ffmpeg.
441
+ - Silencia logs do ffmpeg.
442
+ - Valida cada item e mostra metadados antes de concatenar.
443
+ """
444
+ if not mp4_list or len(mp4_list) < 2:
445
+ raise ValueError("Forneça pelo menos dois arquivos MP4 para concatenar.")
446
+
447
+ valid_list = []
448
+ for mp4 in mp4_list:
449
+ if not os.path.exists(mp4):
450
+ print(f"⚠️ Arquivo não encontrado: {mp4}")
451
+ continue
452
+ info = self._get_video_info(mp4)
453
+ if info:
454
+ valid_list.append(mp4)
455
+
456
+ if len(valid_list) < 2:
457
+ raise RuntimeError("Menos de dois vídeos válidos para concatenar.")
458
+
459
+ with tempfile.NamedTemporaryFile("w", delete=False, suffix=".txt") as f:
460
+ for mp4 in valid_list:
461
+ f.write(f"file '{os.path.abspath(mp4)}'\n")
462
+ list_path = f.name
463
+
464
+ cmd = f"ffmpeg -hide_banner -loglevel warning -y -f concat -safe 0 -i {list_path} -c copy {out_path}"
465
+ print(f"[DEBUG] Concat: {cmd}")
466
+
467
+ try:
468
+ subprocess.check_call(shlex.split(cmd))
469
+ print(f"✅ Concatenação concluída: {out_path}")
470
+ self._get_video_info(out_path)
471
+ finally:
472
+ try:
473
+ os.remove(list_path)
474
+ except Exception:
475
+ pass
476
 
477
 
478
+ def _get_video_info(self, path: str) -> dict:
479
+ """Retorna metadados essenciais do vídeo (DNA)."""
480
+ cmd = f"ffprobe -v error -select_streams v:0 -show_entries " \
481
+ f"stream=codec_name,width,height,avg_frame_rate,duration -of json {shlex.quote(path)}"
482
+ try:
483
+ result = subprocess.run(shlex.split(cmd), capture_output=True, text=True, check=True)
484
+ data = json.loads(result.stdout)
485
+ stream = data.get("streams", [{}])[0]
486
+ fps_str = stream.get("avg_frame_rate", "0/1")
487
+ try:
488
+ num, den = map(float, fps_str.split("/"))
489
+ fps = num / den if den != 0 else 0
490
+ except Exception:
491
+ fps = 0
492
+ info = {
493
+ "arquivo": os.path.basename(path),
494
+ "codec": stream.get("codec_name"),
495
+ "resolução": f"{stream.get('width')}x{stream.get('height')}",
496
+ "fps": round(fps, 2),
497
+ "duração": float(stream.get("duration", 0))
498
+ }
499
+ print(f"🧬 [DNA] {info}")
500
+ return info
501
+ except subprocess.CalledProcessError:
502
+ print(f"⚠️ Falha ao ler metadados de {path}")
503
+ return {}
504
+
505
  def _gerar_lista_com_transicoes(self, pasta: str, video_paths: list[str], crossfade_frames: int = 8) -> list[str]:
506
+ """
507
+ Gera uma nova lista de vídeos aplicando transições suaves (crossfade de N frames)
508
+ entre cada par de vídeos da lista original.
509
+ Sanitiza logs e valida cada saída antes de adicioná-la à lista final.
510
+ """
511
+ if len(video_paths) < 2:
512
+ print("⚠️ Lista de vídeos muito curta, nada a mesclar.")
513
+ return video_paths
514
+
515
+ nova_lista = []
516
+ for i in range(len(video_paths) - 1):
517
+ v1 = video_paths[i]
518
+ v2 = video_paths[i + 1]
519
+ out = os.path.join(pasta, f"transicao_{i+1}.mp4")
520
+
521
+ # Comando FFmpeg com crossfade simples (exemplo usando blend)
522
+ cmd = (
523
+ f"ffmpeg -hide_banner -loglevel error -y "
524
+ f"-i {shlex.quote(v1)} -i {shlex.quote(v2)} "
525
+ f"-filter_complex "
526
+ f"\"[0:v][1:v]blend=all_expr='A*(1-T/{crossfade_frames})+B*(T/{crossfade_frames})',"
527
+ f"format=yuv420p\" "
528
+ f"-an {shlex.quote(out)}"
529
+ )
530
+
531
+ print(f"[DEBUG] Gerando transição {i+1}: {cmd}")
532
+ try:
533
+ subprocess.run(shlex.split(cmd), check=True)
534
+ except subprocess.CalledProcessError as e:
535
+ print(f"❌ Erro na transição {i+1}: {e}")
536
+ continue
537
+
538
+ # Verifica se o arquivo foi criado
539
+ if not os.path.exists(out) or os.path.getsize(out) == 0:
540
+ print(f"⚠️ Transição {i+1} falhou (arquivo vazio ou ausente).")
541
+ continue
542
+
543
+ # Mostra o DNA do vídeo gerado antes de adicioná-lo
544
+ dna = self._get_video_info(out)
545
+ if not dna or dna["duração"] == 0:
546
+ print(f"⚠️ Arquivo corrompido: {out}")
547
+ continue
548
+
549
+ nova_lista.append(out)
550
+
551
+ print(f"✅ Nova lista de vídeos pronta: {nova_lista}")
552
+ return nova_lista
553
+
554
+ def _gerar_lista_com_transicoes1(self, pasta: str, video_paths: list[str], crossfade_frames: int = 8) -> list[str]:
555
  """
556
  Gera uma nova lista de vídeos aplicando transições suaves (crossfade de N frames)
557
  entre cada par de vídeos da lista original.
 
644
  print(f"[DEBUG] Nova lista final de {len(nova_lista)} arquivos criada.")
645
  return nova_lista
646
 
647
+ def _concat_mp4s_no_reencode1(self, mp4_list: List[str], out_path: str):
 
648
  """
649
  Concatena múltiplos MP4s sem reencode usando o demuxer do ffmpeg.
650
  ATENÇÃO: todos os arquivos precisam ter mesmo codec, fps, resolução etc.