/video-subtitle — Herramienta de Subtítulos para Video
Transcribe el audio de un video a subtítulos SRT usando un modelo local de Whisper y luego los incrusta en el video.
Invocable por el usuario
Cuando el usuario escribe /video-subtitle, ejecuta esta habilidad.
Prerrequisitos
Esta habilidad requiere dos herramientas. Verifica la disponibilidad antes de proceder:
1. mlx-whisper (Apple Silicon) o openai-whisper
Detecta cuál está disponible:
which mlx_whisper whisper 2>/dev/null
Si no se encuentra ninguno, guía al usuario para instalar uno:
- Mac con Apple Silicon (recomendado):
pip install mlx-whisper - Cualquier plataforma:
pip install openai-whisper
2. ffmpeg con libass
Verifica el soporte de libass:
ffmpeg -filters 2>&1 | grep subtitles
Si no hay salida (falta libass), el filtro subtitles no está disponible:
- macOS:
brew install ffmpeg-full(solo keg, usa/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg) - Linux:
sudo apt install ffmpeg(normalmente incluye libass) - Alternativa: Si el usuario tiene ffmpeg normal sin libass, incrusta el SRT como pista de subtítulos en lugar de quemarlo.
Selección del modelo Whisper
Descubre los modelos en caché:
ls ~/.cache/huggingface/hub/ 2>/dev/null | grep whisper
Prefiere el modelo más grande disponible. Si no hay ninguno en caché, usa:
- mlx-whisper:
mlx-community/whisper-large-v3-mlx - openai-whisper:
large-v3
Modos
/video-subtitle ruta/al/video.mov→ Pipeline completo (transcribir + corregir + quemar)/video-subtitle srt ruta/al/video.mov→ Solo SRT (transcribir, sin quemar)/video-subtitle burn ruta/al/video.mov ruta/al/sub.srt→ Solo quemar (usar SRT existente)/video-subtitle(sin argumentos) → Interactivo, pedir la ruta del video
Pipeline Completo
Paso 1: Verificar video
Si no se proporciona ruta, preguntar con AskUserQuestion. Confirmar que el archivo existe y leer metadatos:
ffmpeg -i "<ruta_video>" 2>&1 | grep -E "Duration|Stream.*(Video|Audio)"
Informar al usuario sobre duración, resolución y tamaño del archivo.
Paso 2: Extraer audio
ffmpeg -i "<ruta_video>" -vn -acodec pcm_s16le -ar 16000 -ac 1 "<directorio_salida>/<nombre_base>_audio.wav" -y
WAV mono a 16kHz es el formato de entrada óptimo para Whisper.
Paso 3: Confirmar idioma
Preguntar al usuario con AskUserQuestion. Opciones comunes:
| Idioma | Flag |
|---|---|
| Chino | --language zh |
| Inglés | --language en |
| Japonés | --language ja |
| Detección automática | omitir --language |
Paso 4: Transcribir
mlx-whisper:
mlx_whisper --model "<modelo>" --language <idioma> --output-format srt --output-dir "<directorio_salida>" "<audio_wav>"
openai-whisper:
whisper "<audio_wav>" --model large-v3 --language <idioma> --output_format srt --output_dir "<directorio_salida>"
Decir al usuario: "Transcribiendo, por favor espera..."
Paso 5: Limpiar SRT
Leer el SRT generado y corregir:
- Eliminar alucinaciones: Borrar entradas con marcas de tiempo de duración cero (ej.
00:00:57,640 --> 00:00:57,640) o texto vacío - Corregir nombres propios: Whisper suele reconocer mal marcas y términos técnicos — usar contexto de la conversación para corregir
- Unir fragmentos: Combinar entradas más cortas de 0.5s que estén semánticamente conectadas
- Renumerar: Ejecutar este fragmento de Python para renumerar todas las entradas:
python3 -c "
import re
with open('<ruta_srt>', 'r') as f:
content = f.read()
blocks = re.split(r'\n\n+', content.strip())
out = []
for i, block in enumerate(blocks, 1):
lines = block.strip().split('\n')
if len(lines) >= 2:
lines[0] = str(i)
out.append('\n'.join(lines))
with open('<ruta_srt>', 'w') as f:
f.write('\n\n'.join(out) + '\n')
"
Presentar el SRT corregido al usuario y preguntar si se necesitan más ediciones antes de quemar.
Paso 6: Quemar subtítulos
Determinar el binario ffmpeg correcto (preferir ffmpeg-full si ffmpeg normal carece de libass):
FFMPEG="ffmpeg"
if ! ffmpeg -filters 2>&1 | grep -q subtitles; then
if [ -x /opt/homebrew/opt/ffmpeg-full/bin/ffmpeg ]; then
FFMPEG="/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg"
fi
fi
Luego quemar:
$FFMPEG \
-i "<ruta_video>" \
-vf "subtitles=<ruta_srt>:force_style='FontSize=24,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,Outline=2,MarginV=30'" \
-c:v libx264 -preset medium -crf 23 \
-c:a aac -b:a 128k \
-pix_fmt yuv420p \
"<directorio_salida>/<nombre_base>_subtitled.mp4" -y
Opciones de force_style:
| Parámetro | Valor por defecto | Descripción |
|---|---|---|
| FontSize | 24 | Tamaño de fuente |
| PrimaryColour | &H00FFFFFF | Texto blanco |
| OutlineColour | &H00000000 | Contorno negro |
| Outline | 2 | Grosor del contorno |
| MarginV | 30 | Margen inferior |
Importante: La ruta del archivo SRT no debe contener caracteres especiales que confundan el analizador de filtros de ffmpeg. Usar rutas relativas o enlaces simbólicos si es necesario.
Paso 7: Finalizado
Informar al usuario sobre la ruta y el tamaño del archivo de salida. Sugerir open <ruta> para previsualizar. Recordar al usuario que el archivo intermedio _audio.wav puede ser eliminado.
Solución de problemas
| Problema | Causa | Solución |
|---|---|---|
No such filter: 'subtitles' | ffmpeg sin libass | Usar ffmpeg-full o instalar libass |
No such filter: 'drawtext' | ffmpeg sin libfreetype | Usar ffmpeg-full |
| Entradas SRT duplicadas/vacías | Alucinación de Whisper | Limpiar en el Paso 5 |
| Caracteres chinos se muestran como cuadros | Falta fuente CJK | macOS: fallback automático a PingFang. Linux: apt install fonts-noto-cjk |
Error parsing filterchain | Caracteres especiales en la ruta del SRT | Usar ruta relativa, evitar espacios |
Reglas de calidad
- Transcripción fiel: Solo transcribir lo que realmente se dijo — no añadir ni reescribir el significado
- Corrección de nombres propios: Corregir errores comunes de Whisper, pero confirmar con el usuario
- Ritmo de subtítulos: Máximo dos líneas por entrada, máximo ~20 caracteres CJK por línea
- Limpieza: Recordar al usuario eliminar archivos intermedios (_audio.wav) al finalizar