Aplicación web (FastAPI + HTML/CSS/JS “vanilla”) para:
- Traducir subtítulos en
.srtcon distintos proveedores (OpenAI / DeepSeek / Groq / DeepL). - Procesar
.mkv: extrae la pista de subtítulos a SRT, la traduce, y vuelve a incrustarla en un MKV de salida.
La UI muestra logs en tiempo real y progreso mediante SSE (Server-Sent Events).
Nota sobre estáticos:
/templates/*sirve el frontend desdetemplates/(CSS/JS/HTML)./static/*es un alias por compatibilidad./assets/*se reserva para imágenes/iconos (por ejemplopage.webp,favicon.ico).
La imagen se publica automáticamente en GitHub Container Registry (GHCR):
ghcr.io/joseleelsuper/mkv-translator:latest(última versión)
Con Docker instalado, basta con descargar/crear un docker-compose.yml como este (usa :latest):
services:
mkv-translator:
image: ghcr.io/joseleelsuper/mkv-translator:latest
ports:
- "8000:8000"
environment:
- PORT=8000
- MKV_TRANSLATOR_DATA_DIR=/data
- MKV_TRANSLATOR_TEMPLATES_DIR=/app/templates
volumes:
- ${MKV_TRANSLATOR_DATA_HOST_DIR:-./data}:/data
- ${MKV_TRANSLATOR_TEMPLATES_HOST_DIR:-./templates}:/app/templates:ro
- ${MKV_TRANSLATOR_ASSETS_HOST_DIR:-./assets}:/app/assets:ro
restart: unless-stoppedY levantarlo:
docker compose pull
docker compose upNota: evita --build en este modo (eso es para construir desde código local).
Abre: http://127.0.0.1:8000
- En Docker/Compose, el ejemplo monta
./data(host) en/data(contenedor). - La app crea una carpeta por job dentro de ese directorio.
- Puedes cambiarlo apuntando
MKV_TRANSLATOR_DATA_DIRa otra ruta dentro del contenedor.
Puedes tener una carpeta “runtime” en cualquier sitio, por ejemplo:
C:\\mkv-translator-runtime\\docker-compose.ymldata\\(salidas por job)templates\\(copias detemplates/del repo)assets\\(favicon, imágenes…)
Y arrancar desde ahí con:
docker compose up -dSi el compose no está en la misma carpeta que templates/ y assets/, puedes usar rutas absolutas:
set MKV_TRANSLATOR_TEMPLATES_HOST_DIR=C:\\ruta\\a\\templates
set MKV_TRANSLATOR_ASSETS_HOST_DIR=C:\\ruta\\a\\assets
set MKV_TRANSLATOR_DATA_HOST_DIR=C:\\ruta\\a\\data
docker compose up -ddocker run --rm -p 8000:8000 ghcr.io/joseleelsuper/mkv-translator:latestRequisitos: tener Docker instalado.
Build de la imagen:
docker build -t mkv-translator:latest .Ejecutar:
docker run --rm -p 8000:8000 mkv-translator:latestAbre: http://127.0.0.1:8000
Notas:
- El contenedor ya incluye
ffmpegyffprobe, así que el procesamiento de.mkvfunciona dentro de Docker. - La app escucha en
0.0.0.0y usa${PORT:-8000}(por defecto 8000).
Requisitos: Docker Compose v2 (docker compose).
Levantar el servicio:
docker compose up --buildNota: este modo usa build local. Si quieres levantar sin clonar, usa el bloque de arriba (imagen :latest).
Parar:
docker compose downAbre: http://127.0.0.1:8000
Migrado de Vercel a Railway por limitaciones técnicas fundamentales:
- ❌ No incluye ffmpeg: Imposible procesar archivos
.mkvsin binarios del sistema - ❌ Límite de 250MB por función: Los binarios estáticos de ffmpeg (~154MB) hacen inviable incluirlos
- ❌ Jobs en memoria efímeros: Distintas requests van a instancias diferentes, rompiendo SSE
- ❌ Timeouts estrictos: No apto para traducciones largas (>60s)
- ✅ Soporte nativo de ffmpeg: Se instala con
apt-geten el Dockerfile - ✅ Sin límites de tamaño: Contenedores completos, no funciones limitadas
- ✅ Instancia persistente: Jobs + SSE funcionan correctamente
- ✅ Sin timeouts problemáticos: Procesa archivos grandes sin interrupciones
- ✅ Free tier generoso: 500 horas/mes + $5 crédito inicial
Conclusión: Railway es la plataforma ideal para apps que necesitan procesamiento de video/audio o cualquier dependencia del sistema.
- Python 3.11+ (probado con 3.12)
ffmpeg+ffprobe(instalados automáticamente en Railway)
En Linux (Debian/Ubuntu):
sudo apt update && sudo apt install -y ffmpegComprueba:
ffmpeg -version
ffprobe -versionpython -m venv .venv
. .venv/bin/activate
pip install -r requirements.txtpython -m uvicorn app.main:app --host 127.0.0.1 --port 8000Abre http://127.0.0.1:8000.
Este repo incluye configuración para desplegar en Vercel usando el runtime de Python:
api/index.py: entrypoint que exportaapp(FastAPI).vercel.json: rutas (catch-all) hacia la función serverless.
Pasos (resumen):
- Sube el repo a GitHub.
- En Vercel: New Project → importa el repo.
- Deploy.
Limitación: archivos MKV no funcionan en Vercel
- El runtime serverless de Vercel no incluye
ffmpeg/ffprobey el límite de 250MB por función hace inviable incluir binarios estáticos (~154MB solo ffmpeg). - Solución: Extrae los subtítulos localmente antes de subirlos:
Luego sube el archivo
ffmpeg -i video.mkv -map 0:s:0 subtitles.srt
.srta la app en Vercel.
La app está diseñada como proceso “largo” (jobs en memoria + SSE):
- El repositorio de jobs es en memoria. En Vercel, distintas requests pueden ir a instancias diferentes, por lo que el endpoint de eventos (
/api/jobs/{id}/events) podría no encontrar el job creado. - Los archivos y el estado son efímeros (no hay persistencia entre despliegues y no se debe confiar en el disco para almacenar resultados a largo plazo).
- Hay límites de duración por ejecución (configurable desde el Dashboard de Vercel, pero sigue siendo un entorno con timeouts).
Si necesitas que funcione de forma fiable en Vercel “en producción”, lo normal es:
- Persistir jobs/eventos en un store externo (por ejemplo Redis / Vercel KV).
- Subir entradas/salidas a almacenamiento (por ejemplo Vercel Blob / S3).
Si quieres, puedo adaptar el JobRepository y la cola de eventos para usar Vercel KV/Redis.
- Selecciona el proveedor.
- Introduce tu API key.
- La app intenta cargar la lista de modelos desde la API del proveedor.
- Si no puede, usa un fallback estático.
- En la UI, para proveedores LLM, el fallback se interpreta como “API key inválida” (DeepL es la excepción porque no expone catálogo de modelos).
- Sube un
.srto.mkv. - Inicia la traducción y sigue los logs/progreso.
- Descarga el resultado:
SRT Fuente(útil para depurar la extracción)SRT TraducidoMKV Traducido(solo si subiste un.mkv)
- La clave se envía al backend para llamar al proveedor durante el job.
- No se persiste en disco ni en una base de datos (los jobs son en memoria).
- Aun así, evita ejecutar la app en entornos compartidos si vas a usar claves reales.
mkv_translator/domain: parsing/ensamblado SRT y modeloJob(eventos/progreso)mkv_translator/application: puertos + servicios de orquestaciónmkv_translator/adapters:ffmpeg/*: extracción/mux de subtítulosllm/*: clientes de proveedores (OpenAI-compat / DeepL)web/*: FastAPI + rutas HTTP
templates/html|css|js: frontend vanilla
El “composition root” es app/main.py: cablea adaptadores y servicios.
- Subes un
.srto.mkvdesde la UI. - El backend crea un Job en memoria y lanza el trabajo en un thread.
- La UI se conecta a
/api/jobs/{id}/events(SSE) y va recibiendo:log: mensajes de estadoprogress: porcentajedone/error
- Descargas disponibles:
/api/jobs/{id}/download?kind=source_srt(SRT fuente)/api/jobs/{id}/download?kind=srt(SRT traducido)/api/jobs/{id}/download?kind=mkv(MKV traducido, solo si subiste MKV)
- Error al procesar MKV (
ffmpeg/ffprobe no está instalado): instalaffmpegy verifica que está enPATH. - "No subtitle streams found in MKV": el MKV no tiene pistas de subtítulos o están en un formato no detectable por
ffprobe.
- Build falla: Verifica que el
Dockerfileexiste en la raíz del proyecto - App no responde: Revisa los logs en Railway Dashboard → Deployments → Logs
- Puerto incorrecto: Railway asigna
$PORTautomáticamente, no lo configures manualmente
- Modelos no cargan: Verifica proveedor y API key
- Si el proveedor cae o limita el endpoint de modelos, el backend hará fallback estático
Si Railway no te funciona, estas plataformas también soportan ffmpeg:
- Fly.io: Similar a Railway, free tier con 3 VMs
- Render: Web Services con Docker, más tradicional
- VPS (Hetzner/DigitalOcean): Control total, desde €4.5/mes