auto-subtitle — Transcripción automática de audio/video a SubtitleCue
Un solo comando para obtener subtítulos: ingresa un archivo de audio/video y obtén dos productos: <nombre>.cues.json + <nombre>.srt. El contrato de datos está alineado palabra por palabra con SubtitleCue de video-motion: {start, end, text}, en segundos, con 3 decimales, texto en una sola línea (las oraciones largas ya están divididas) — el array subtitles puede ir directamente a la línea de tiempo de video-motion.
Paso 1 · Confirmar material e idioma
- Acepta mp4 / wav / flac (PyAV los decodifica directamente). Si hay error de decodificación → transcodificar primero:
ffmpeg -i in.mp4 -vn -ac 1 -ar 16000 out.wav - Confirmar el idioma y pasar
--lang:zh(predeterminado) /eny otros códigos de idioma de whisper. - Material que sea solo música/efectos de sonido NO necesita ejecutarse: VAD filtrará productos vacíos o extremadamente cortos (es una característica, no un error).
- Si el material tiene un texto conocido (guión / diálogos / texto original), guardarlo como UTF-8 — será la referencia para la verificación L2 en el Paso 3, para evitar desviaciones.
Paso 2 · Ejecutar la transcripción
El venv no se incluye en el repositorio (artefacto de construcción de ~284 MB, ya cubierto por .gitignore con .venv/). Por convención, la plantilla de la habilidad no incluye artefactos de construcción, igual que la plantilla no incluye node_modules, por lo que el venv se crea fuera del repositorio, se construye una vez y todos los proyectos lo reutilizan:
python -m venv "$env:USERPROFILE\.cache\vibe-studio\asr-venv"
& "$env:USERPROFILE\.cache\vibe-studio\asr-venv\Scripts\python.exe" -m pip install -r skills/auto-subtitle/templates/asr/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
cd skills/auto-subtitle/templates/asr
& "$env:USERPROFILE\.cache\vibe-studio\asr-venv\Scripts\python.exe" transcribe.py <input.mp4|wav> --outdir <directorio de salida>
- Modelo predeterminado:
large-v3-turbo(~1.6 GB, descarga automática la primera vez, calidad aceptable para chino); para verificación rápida agregar--model small(~484 MB). El modelo se almacena en caché en~/.cache/huggingface, descarga única y reutilización global. - Si la descarga del modelo falla, establecer
HF_ENDPOINTpara cambiar el origen: por defecto conexión directa oficial (verificado en agosto de 2026); alternativo como$env:HF_ENDPOINT="https://hf-mirror.com"(nota: con hub 1.x este mirror tiene problemas de compatibilidad con redirección 308, priorizar el oficial). - stdout: resumen en inglés de una línea
segments=N cues=M total_audio=... model=... lang=... out=...; exit 0 éxito, 1 fallo.
Paso 3 · Verificación en tres niveles (obligatorio, no se puede omitir)
L1 Nivel de programa: JSON debe poder cargarse con json.load; subtitles es un array y cada elemento tiene exactamente los campos start/end/text; start < end y no decreciente entre elementos; números con 3 decimales; el end del último elemento ≤ duración de ffprobe + 1s. Las líneas de marca de tiempo del SRT deben coincidir con HH:MM:SS,mmm --> HH:MM:SS,mmm, numeración continua desde 1.
L2 Precisión: si hay texto conocido → comparación con difflib.SequenceMatcher (ambos lados sin puntuación ni espacios, normalizados), similitud general ≥0.85, por frase ≥0.70 (para ventanas de cues contiguos concatenados tomar el máximo, cubriendo división/fusión). Sin texto conocido → escuchar muestras: inicio / medio / final.
L2.5 Línea de tiempo (verificación de rutina): La ventana de los subtítulos debe alinearse con la ventana temporal a nivel de palabra; no debe haber subtítulos durante el silencio entre oraciones — auditar el espacio entre cues adyacentes: entre oraciones debe haber un espacio de silencio real >1s (gap=0 dentro de una oración es normal); no debe haber fragmentos de menos de 0.3s; el start del primer cue debe alinearse con el inicio del habla, el end del último cue no debe cortar el silencio final. Internamente, el script usa marcas de tiempo a nivel de palabra (start de la primera palabra / end de la última) para definir la ventana; este paso es una verificación a nivel de producto.
L3 Conclusión: todos aprobados = ALL GREEN; si hay elementos en rojo → corregir la división / re-ejecutar con parámetros VAD y re-verificar; si 2 rondas consecutivas en rojo → detenerse e informar, no ajustar a ciegas.
Paso 4 · Entrega
- Informe: ruta absoluta de
cues.json+ número de cues + duración del audio + modelo/idioma + resultado de la verificación en tres niveles. - Integración con video-motion: el array
subtitlesse pega directamente ensrc/cues.tsdel proyecto video-motion encues.subtitlesy ya funciona, sin conversión de campos. - Explícitamente no se hace (para iteraciones posteriores): diarización de hablantes, exportación de marcas de tiempo a nivel de palabra, traducción, voz TTS.
Paso 5 · Registrar comando para re-ejecución
Al final del informe de entrega, incluir el comando completo para re-ejecutar (ruta de entrada / --lang / --outdir / --model), para garantizar que la próxima sesión u otra persona pueda reproducirlo exactamente. Los productos (cues.json / srt) se colocan en el área de salida (este repositorio: products/, entre repositorios: raíz del proyecto output/) sin incluirse en git; el comando es el activo.
Límites y trampas
- Consola GBK: el script imprime todo en inglés; al escribir archivos siempre usar explícitamente
encoding="utf-8", en JSONensure_ascii=False. - Reglas de división: si una línea tiene >20 caracteres, dividir en signos de puntuación (。!?;,、). Si no hay puntuación en la ventana, dividir de forma forzada asegurando que el último fragmento tenga al menos 3 caracteres (para evitar fragmentos de 1 carácter). Fragmentos <0.3s se fusionan con el anterior, sin superar el límite de 20 caracteres.
- Las marcas de tiempo a nivel de palabra son la única fuente correcta para la ventana de subtítulos: las marcas de tiempo a nivel de segmento se tragan el silencio entre oraciones, haciendo que los subtítulos aparezcan aproximadamente un segmento de silencio antes (ya corregido, no revertir
word_timestamps=False). - Inferencia int8 en CPU: para audio de 28s ≈ resultado en 1 minuto; para audio largo, estimar tiempo proporcionalmente. La primera ejecución con large-v3-turbo incluye descarga, dejar suficiente tiempo de espera.
- En reconocimiento de chino, los números suelen convertirse a arábigos ("doscientos milisegundos" → "200 milisegundos"), diferencia normal en comparación L2, no es error de reconocimiento.
Enlace de entrega
El SRT/cues final debe archivarse junto con la fuente de la pista de audio original y la versión del modelo. No entregar solo un mp4 con subtítulos quemados. Al entregar el video, usar media-qc para verificar la ventana de subtítulos contra la duración real; la precisión aún se verifica contra el texto conocido o mediante escucha de muestras. La descarga del modelo predeterminado y la inferencia local no reemplazan la voz humana grabada.