Arquitectura
Translay son tres procesos en el mismo equipo: el servidor en Python (vt/), el proceso de voz con su propio entorno y la interfaz (web/), dentro de la ventana de la app de escritorio (desktop/) o en un navegador. No hay base de datos ni servicios externos: todo vive en archivos JSON y WAV dentro de data/ y de cada biblioteca (ver Datos en disco).
Procesos
Sección titulada «Procesos»App de escritorio
Sección titulada «App de escritorio»desktop/ es una app de Tauri 2 (Rust) que muestra la misma interfaz web en una ventana propia:
- Al abrir, carga su página de inicio (
desktop/ui/), que usa la fuente, los colores y los botones deweb/(los copiadesktop/scripts/prepare.mjs). - La primera vez, o si una versión nueva cambia los paquetes, instala los entornos con el
uvincluido:uv pip sync --require-hashesdedesktop/locks/server.txtsobre el Python 3.12 incluido, y dedesktop/locks/voice.txt(torch para CUDA 12.8) sobre el Python 3.11, si la GPU tiene al menos ~4 GB.syncinstala exactamente el lock, sin resolver dependencias en tu equipo. El avance sale de lo que crece la caché de paquetes. - Arranca
run.pysin ventana con--data-dir(la carpeta elegida),--port(8765 o el siguiente libre) yTRANSLAY_VENVSapuntando a sus entornos, con ffmpeg y Node.js incluidos al frente delPATH. El proceso va dentro de un job object de Windows que cierra todo el árbol (voz, yt-dlp, ffmpeg) cuando la app sale, aunque se cierre de golpe. - Cuando el servidor escribe
Listo: http://127.0.0.1:<puerto>, le da a ese origen exacto permiso para los controles de la ventana y los comandos de la app, y navega a la página.web/js/desktop.jsconvierte el encabezado en barra de título y agrega Preferencias › App de escritorio. - Si la salida del servidor se cierra (todos sus procesos terminaron) sin que la app lo pidiera, vuelve a su página con las últimas líneas. «Reiniciar ahora» de Rendimiento no cuenta: el servidor nuevo hereda la salida y sigue en el mismo job.
Servidor
Sección titulada «Servidor»python run.py llama a vt.app.main(), que:
- Carga Whisper (
large-v3-turbopor defecto) y lo calienta con un segundo de silencio; luego el traductor (opus-big) con una frase de prueba. - Abre las bibliotecas (
vt/vaults.py) y registra en cada una los videos de YouTube que ya estén en su carpetayoutube/. - Crea el cliente de voz en modo perezoso: el proceso de voz arranca la primera vez que se necesita.
- Devuelve Whisper y el traductor a la RAM: el servidor arranca en reposo (ver GPU y reposo).
- Levanta la cola (
Processor), las exportaciones (Exporter), el acceso (Auth), los webhooks y la API, y sirve todo conAppServeren127.0.0.1:8765.
Todo corre en un solo bucle asyncio. El trabajo pesado va a hilos:
| Hilo | Qué hace |
|---|---|
gpu (ThreadPoolExecutor, 1 hilo) |
Reconocimiento y traducción. |
tts (ThreadPoolExecutor, 1 hilo) |
Pedidos de voz al proceso de voz. |
Cola (Processor._loop) |
Procesa los videos guardados, uno a la vez. |
Descargas (Processor._download_loop) |
Baja videos de YouTube con yt-dlp, sin GPU. |
Exportaciones (Exporter._loop) |
Mezcla y empaqueta con ffmpeg, en la CPU. |
Reposo (Resources._watch) |
Revisa cada 2 s la inactividad y duerme los modelos. |
| Miniaturas al arrancar | Completa miniatura, canal y categorías de videos ya descargados, con 3 s entre consultas. |
Proceso de voz
Sección titulada «Proceso de voz»vt/tts.py (TTSClient) lanza vt/tts_worker.py con el Python de .venvs/<motor>/ (Scripts/python.exe en Windows, bin/python en Linux). Así el motor de voz tiene su propio torch y sus dependencias sin tocar las del servidor.
La comunicación usa vt/ipc.py: cada mensaje es una trama [u32 largo JSON][JSON UTF-8][u32 largo binario][binario] por stdin/stdout. El proceso reserva stdout para las tramas y manda a stderr todo lo que impriman las librerías; el servidor lo muestra con el prefijo [voz].
| Operación | Respuesta |
|---|---|
| (al arrancar) | {"ok": true, "sr": …} cuando el modelo cargó y se calentó. |
synth (text, lang, ref, ref_text, params) |
{"ok", "sr"} + audio float32. |
offload / onload |
Pasa el modelo (y DeepFilterNet) a la RAM o de vuelta a la GPU. |
quit |
Termina el proceso. |
TTSClient atiende un pedido a la vez; si el proceso no está vivo, lo arranca antes de sintetizar.
Navegador
Sección titulada «Navegador»La interfaz no usa framework ni compilación: web/index.html carga los CSS y los JS en orden (ver Interfaz). Se conecta por WebSocket a /ws (con ?lib=<biblioteca> si la página es /b/<biblioteca>) y se reconecta sola al segundo si se corta. Videos, audios y voces se piden por HTTP con soporte de Range, para poder saltar dentro del video.
Módulos de vt/
Sección titulada «Módulos de vt/»| Módulo | Responsabilidad |
|---|---|
app.py |
Punto de entrada: argumentos, carga de modelos, rutas HTTP y arranque. |
httpws.py |
Servidor HTTP estático (con Range) y WebSocket RFC 6455 sobre asyncio, sin dependencias. |
session.py |
Una sesión por pestaña: protocolo WebSocket, permisos, procesamiento en vivo y cola de voz de la pestaña. |
processor.py |
Cola del servidor: procesa videos guardados y descarga los de YouTube. Estado en data/queue.json. |
vad.py |
Detector de voz por energía con piso de ruido adaptativo. |
asr.py |
Whisper (faster-whisper sobre CTranslate2): audio → palabras con tiempos. |
streaming.py |
Transcripción en streaming con acuerdo local: confirma palabras y cierra frases. |
translate.py |
Traductores sobre CTranslate2 (Opus-MT, NLLB, MADLAD) con la misma interfaz. |
textproc.py |
Texto antes y después de traducir: oraciones, modismos, forma y convenciones latinoamericanas. |
glossary.py |
Glosario de cada biblioteca: términos que no se traducen o con traducción fija. |
speakers.py |
Diarización en línea: huella de voz (WeSpeaker en ONNX) y referencias de cada persona. |
dub.py |
Doblaje por frase: persona, referencia, síntesis, ajuste de duración y sonoridad. |
tts.py |
Cliente del proceso de voz. |
tts_worker.py |
Proceso de voz: OmniVoice (y Chatterbox o VoxCPM, para comparar). |
enhance_df.py |
DeepFilterNet3 para limpiar referencias ruidosas (corre en el proceso de voz). |
ipc.py |
Tramas binarias por tuberías entre procesos. |
audiofx.py |
Audio propio en numpy: re-muestreo, WSOLA, sonoridad, compuerta espectral, YIN, ruido de prueba, WAV. |
repair_refs.py |
Reparación de referencias de voz recortadas por versiones anteriores. |
resources.py |
Modo reposo y turnos en la GPU (FairLock). |
gpu.py |
DLL de CUDA en Windows y límite del caché de CTranslate2. |
store.py |
Transcripción de cada video (transcripts/<video>.json). |
library.py |
Videos y colecciones de una biblioteca (library.json). |
vaults.py |
Varias bibliotecas, cada una en su carpeta; rutas de sus archivos. |
prefs.py |
Esquema y valores de las preferencias (data/prefs.json). |
youtube.py |
Descargas con yt-dlp, miniaturas, canales y categorías. |
cookies.py |
Importa la sesión de YouTube (navegador o cookies.txt). |
exporter.py |
Exporta video y audio traducidos u originales, subtítulos y transcripción. |
auth.py |
Acceso: dueño, bibliotecas privadas, con contraseña o públicas, y claves de API. |
api.py |
API HTTP v1, inicio de sesión y guardián de archivos (make_guard). |
events.py |
Catálogo de eventos y su emisión. |
webhooks.py |
Envío firmado de eventos a otras apps, con reintentos. |
Recorrido de un video
Sección titulada «Recorrido de un video»Un enlace de YouTube, de punta a punta:
- Importar. El navegador manda
youtube,playlistoyt_batchpor WebSocket. La sesión registra el video en la biblioteca (sinmediatodavía) y lo agrega a la cola. - Descargar. El hilo de descargas de
Processorlo baja con yt-dlp a<biblioteca>/youtube/<id>.mp4y<id>.m4a, con su miniatura y datos, y completa la entrada delibrary.json. - Ponerlo en turno. El hilo de la cola toma el primer trabajo descargado; si alguien está viendo un video, ese va primero y empieza desde su punto de reproducción.
- Decodificar. PyAV pasa el audio a mono de 16 kHz. Se lee en bloques de 3 s.
- Reconocer.
StreamingTranscriberacumula los bloques, consulta a Whisper y entrega frases cerradas (Segment) con su audio original. - Traducir. Cada frase pasa por
textproc.pyy Opus-MT con el glosario de la biblioteca.Transcript.add_segmentla guarda. - Identificar a la persona.
Dubber.identifycalcula la huella de voz, asigna la persona y mejora su referencia;Dubber.relabelcorrige frases cortas anteriores. - Doblar. La frase va al hilo
tts: OmniVoice clona la voz de la persona, se ajusta la duración y la sonoridad, y el WAV queda en<biblioteca>/audio/<video>/<frase>.wav. - Avisar. Cada paso emite un mensaje
bg(segment,translations,dub,covered) que el servidor reenvía solo a las pestañas que tienen ese video abierto. - Reproducir. El navegador baja cada voz a medida que se acerca y la hace sonar en el inicio de su frase, con el original atenuado debajo.
Un archivo del equipo sigue el mismo camino con dos diferencias: su identificador es una huella del contenido (SHA-256 del tamaño, el primer MB y el último MB, recortado a 32 caracteres) y, mientras el servidor no tiene su copia, es el navegador el que envía el audio por delante de la reproducción. Ver Pipeline.