159 lines
5.0 KiB
Python

#!/usr/bin/env python3
"""CLI mínimo que expone el orquestador principal.
Este módulo proporciona la función `main()` que construye los adaptadores
por defecto e invoca `PipelineOrchestrator.run(...)`. Está diseñado para
reemplazar el antiguo `run_full_pipeline.py` como punto de entrada.
"""
from __future__ import annotations
import argparse
import glob
import os
import shutil
import sys
import tempfile
from whisper_project.usecases.orchestrator import PipelineOrchestrator
from whisper_project.infra.kokoro_adapter import KokoroHttpClient
def main():
p = argparse.ArgumentParser()
p.add_argument("--video", required=True)
p.add_argument("--srt", help="SRT de entrada (opcional)")
p.add_argument(
"--kokoro-endpoint",
required=False,
default="https://kokoro.example/api/synthesize",
help=(
"Endpoint HTTP de Kokoro (por defecto: "
"https://kokoro.example/api/synthesize)"
),
)
p.add_argument("--kokoro-key", required=False)
p.add_argument("--voice", default="em_alex")
p.add_argument("--kokoro-model", default="model")
p.add_argument("--whisper-model", default="base")
p.add_argument(
"--translate-method",
choices=[
"local",
"gemini",
"argos",
"none",
],
default="local",
)
p.add_argument(
"--gemini-key",
default=None,
help=(
"API key para Gemini (si eliges "
"--translate-method=gemini)"
),
)
p.add_argument("--mix", action="store_true")
p.add_argument("--mix-background-volume", type=float, default=0.2)
p.add_argument("--keep-chunks", action="store_true")
p.add_argument("--keep-temp", action="store_true")
p.add_argument(
"--dry-run",
action="store_true",
help="Simular pasos sin ejecutar",
)
args = p.parse_args()
video = os.path.abspath(args.video)
if not os.path.exists(video):
print("Vídeo no encontrado:", video, file=sys.stderr)
sys.exit(2)
workdir = tempfile.mkdtemp(prefix="full_pipeline_")
try:
# construir cliente Kokoro HTTP nativo e inyectarlo en el orquestador
kokoro_client = KokoroHttpClient(
args.kokoro_endpoint,
api_key=args.kokoro_key,
voice=args.voice,
model=args.kokoro_model,
)
orchestrator = PipelineOrchestrator(
kokoro_endpoint=args.kokoro_endpoint,
kokoro_key=args.kokoro_key,
voice=args.voice,
kokoro_model=args.kokoro_model,
tts_client=kokoro_client,
)
result = orchestrator.run(
video=video,
srt=args.srt,
workdir=workdir,
translate_method=args.translate_method,
gemini_api_key=args.gemini_key,
whisper_model=args.whisper_model,
mix=args.mix,
mix_background_volume=args.mix_background_volume,
keep_chunks=args.keep_chunks,
dry_run=args.dry_run,
)
# Si no es dry-run, crear una subcarpeta por proyecto en output/
# (output/<basename-of-video>) y mover allí los artefactos generados.
final_path = None
if (
not args.dry_run
and result
and getattr(result, "burned_video", None)
):
base = os.path.splitext(os.path.basename(video))[0]
project_out = os.path.join(os.getcwd(), "output", base)
try:
os.makedirs(project_out, exist_ok=True)
except Exception:
pass
# Mover el vídeo principal
src = result.burned_video
dest = os.path.join(project_out, os.path.basename(src))
try:
if os.path.abspath(src) != os.path.abspath(dest):
shutil.move(src, dest)
final_path = dest
except Exception:
final_path = src
# También mover otros artefactos que empiecen por el basename
try:
pattern = os.path.join(os.getcwd(), f"{base}*")
for p in glob.glob(pattern):
# no mover el archivo fuente ya movido
if os.path.abspath(p) == os.path.abspath(final_path):
continue
# mover sólo ficheros regulares
try:
if os.path.isfile(p):
shutil.move(p, os.path.join(project_out, os.path.basename(p)))
except Exception:
pass
except Exception:
pass
else:
# En dry-run o sin resultado, no movemos nada
final_path = getattr(result, "burned_video", None)
print("Flujo completado. Vídeo final:", final_path)
finally:
if not args.keep_temp:
try:
shutil.rmtree(workdir)
except Exception:
pass
if __name__ == "__main__":
main()