Communitygithub.com

ThomasHMD/subtitles

Corrige la exportación JSON de transcripciones/captions de Premiere Pro (con muchos errores tipográficos, especialmente en nombres propios) transcribiendo el vídeo nuevamente de forma local con whisper.cpp, y reconstruyendo un JSON en el formato exacto de Premiere (reimportable tal cual) con nombres corregidos desde un guión de referencia, puntuación y mayúsculas restauradas, títulos de obras entre comillas, y sincronización de palabras realineada sobre el audio mediante alineación forzada.

¿Qué es subtitles?

Esta habilidad para Claude (o agentes compatibles) permite corregir transcripciones automáticas generadas por Premiere Pro, que suelen contener errores tipográficos graves, especialmente en nombres propios (por ejemplo, el nombre de un fotógrafo dividido en dos palabras sin relación). El flujo de trabajo transcribe el vídeo de forma local usando whisper.cpp, mucho más preciso, y luego reconstruye un archivo JSON en el formato exacto de Premiere (listo para reimportar), aplicando correcciones de nombres basadas en un guión de referencia, restaurando puntuación y mayúsculas, añadiendo comillas a títulos de obras, y realineando las marcas de tiempo de cada palabra sobre el audio mediante alineación forzada (forced alignment), ya que los timings de whisper suelen desviarse. Está diseñado para usuarios de Premiere Pro que necesitan subtítulos/captions precisos y profesionales, sin depender de servicios en la nube. El proceso se divide en pasos claros: inspección del formato original, transcripción local, corrección manual de nombres (con un archivo `corrections.json`), generación del JSON corregido, y realineado temporal final. Incluye mecanismos para detectar y corregir alucinaciones comunes de whisper (como líneas finales inventadas o repeticiones de palabras con duración cero) y problemas recurrentes como números decimales duplicados o signos de puntuación aislados que se pierden. Soporta múltiples idiomas mediante archivos de configuración (`languages/<code>.json`) y requiere una configuración inicial única con `whisper-cli`, `ffmpeg`, y el modelo `ggml-large-v3-turbo`.

Compatible con✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ThomasHMD/premiere-rough-cut/tree/HEAD/skills/subtitles

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

subtitles: corregir captions de Premiere Pro

Configuración única: ver INSTALL.md en la raíz del kit. Con el plugin, la línea de configuración es bash "${CLAUDE_PLUGIN_ROOT}/install.sh" --data "${CLAUDE_PLUGIN_DATA}"; con una instalación manual (las cuatro carpetas skills/ copiadas una al lado de la otra en ~/.claude/skills/), ejecute bash install.sh desde la raíz del kit. Los scripts viven en la carpeta scripts/ de esta habilidad. Esta habilidad apunta a la otra como ../rough-cut/....

Qué hace la habilidad

Premiere genera una transcripción automática (exportación .json) que a menudo está llena de errores tipográficos, sobre todo en nombres propios (el nombre de un fotógrafo convertido en dos palabras sin relación). whisper.cpp transcribe mucho mejor. Esta habilidad transcribe el vídeo nuevamente de forma local, luego reconstruye un .json en el formato exacto de Premiere (reimportable tal cual): nombres corregidos con la ayuda de un guión de referencia, puntuación y mayúsculas restauradas, títulos de obras entre comillas. Finalmente realinea los tiempos sobre el audio mediante alineación forzada (los marcas de tiempo de whisper se desvían).

  • SÍ: transcripción local, corrección de nombres, puntuación y mayúsculas, comillas alrededor de títulos, realineación de tiempos sobre el audio (alineación forzada), un formato idéntico al de Premiere, listo para reimportar.
  • NO: cortar o edición preliminar del vídeo (eso es rough-cut), traducir, resincronizar un archivo de captions que ya está bien, subtítulos .srt/.vtt (el formato aquí es el JSON nativo de Premiere).

Entradas esperadas

  1. El vídeo exportado de Premiere (.mp4/.mov): contiene el audio a transcribir.
  2. El .json de captions exportado de Premiere: proporciona el formato de destino (claves, UUID del speaker, idioma) a clonar.
  3. El guión de referencia del vídeo, en cualquier forma legible (texto plano, Markdown, un Google Doc, una página de Notion, un texto pegado): la autoridad para la ortografía de los nombres propios (personas, obras, lugares, marcas). Si no hay, pídalo, o corrija solo los errores tipográficos obvios y mantenga la ortografía de whisper para cualquier nombre incierto.

Idioma

Tipografía (espaciado alrededor de la puntuación), letras acentuadas, conjuntos de puntuación, marcas de comillas y normalizaciones ortográficas viven en languages/<code>.json (fr y en están incluidos), nunca en el código. El idioma se elige mediante --lang <code>, si no el idioma del JSON de Premiere, si no el registrado en la transcripción de whisper, si no en. Cómo funcionan los archivos y cómo agregar un idioma: ../rough-cut/references/languages.md (mismo mecanismo; esta habilidad tiene su propia carpeta languages/).

Requisitos (una vez por máquina)

whisper-cli + ffmpeg + el modelo ~/Models/whisper/ggml-large-v3-turbo.bin. Ver ../rough-cut/references/whisper-setup.md (configuración compartida). Verificar: which whisper-cli ffmpeg && ls ~/Models/whisper/.

Alineación forzada (realineación de tiempos sobre el audio, paso 6b):

pip3 install --break-system-packages ctc-forced-aligner torch torchaudio unidecode

Un modelo ONNX de aproximadamente 1,2 GB se descarga en la primera ejecución (caché ~/ctc_forced_aligner/). Verificar: python3 -c "import ctc_forced_aligner".


Flujo de trabajo

Los comandos scripts/... a continuación asumen que está en la carpeta de la habilidad. El directorio de trabajo se puede restablecer entre dos llamadas al shell: ya sea haciendo cd a la carpeta de la habilidad una vez, o anteponiendo a cada script su ruta absoluta. Los archivos de trabajo (audio, transcripción, correcciones) van en una carpeta temporal fuera de cualquier carpeta sincronizada.

1. Examinar el vídeo y el formato de entrada

PRIMERO: tomar una instantánea del original. Premiere puede REESCRIBIR el .json de entrada en una re-exportación, y es la única verdad de sincronización para verificar los tiempos en el paso 7:

cp "<Reel.json>" /tmp/subs_original_premiere.json
ffprobe -v error -show_entries format=duration:stream=codec_type,r_frame_rate \
  -of default=noprint_wrappers=1 "<video>"

Anote la duración (la última palabra debe caer dentro de ella) y los fps. Luego inspeccione el JSON de Premiere de entrada para confirmar el formato:

python3 scripts/inspect_format.py "<Reel.json>"

Imprime las claves (top/segment/word), el UUID del speaker, el idioma, el número de segmentos y el texto actual segmento por segmento (= la lista de errores tipográficos a corregir). El formato esperado está documentado en references/premiere-format.md: léalo si la inspección muestra claves inusuales.

2. Transcripción local (whisper.cpp)

python3 scripts/extract_audio.py "<video>" /tmp/subs_audio.wav
python3 scripts/transcribe.py /tmp/subs_audio.wav /tmp/subs_transcript.json --model large-v3-turbo --language <code>

Aproximadamente 8 s para 3 min en Apple Silicon. La transcripción tiene segmentos con text (puntuado, con mayúsculas/minúsculas) y words con marcas de tiempo.

2b. Si whisper NO puntuó (entrega continua): volver a puntuar

En monólogos rápidos, whisper a veces devuelve un flujo sin puntuación ni mayúsculas (los segmentos se rompen en medio de una oración, sin ./,). Síntoma claro: build_subs.py reporta eos: 0. Sin puntuación, los subtítulos son ilegibles. Verificación rápida: python3 scripts/words_of.py /tmp/subs_transcript.json 0 1 2 --plain.

Si es así, vuelva a puntuar usted mismo (es una tarea de LLM, no de whisper): reescriba el text de cada segmento con puntuación y mayúsculas de oración, manteniendo las palabras de whisper sin cambios: agregue SOLO puntuación y mayúsculas/minúsculas; las correcciones léxicas (nombres, acrónimos) van en corrections.json. La puntuación SIEMPRE unida a la palabra (so?, no so ?): un signo precedido de un espacio se convierte en un token aislado que la alineación elimina silenciosamente (una vez se perdieron 114 ?/! a la vez). build_subs.py restaura el espaciado del idioma (para francés, un espacio de no separación) por sí mismo. Coloque el resultado en un JSON {index: "texto puntuado"}, luego:

python3 scripts/repunctuate.py --transcript /tmp/subs_transcript.json --punct /tmp/punct.json --output /tmp/subs_transcript_punct.json

El script se niega si agregó, eliminó o cambió una palabra (muestra el diff): eso es lo que mantiene la alineación palabra-tiempo intacta. Tenga cuidado con el flujo entre segmentos: un segmento que continúa una oración comienza en minúscula, uno que abre una oración lleva mayúscula. Use esta transcripción puntuada para el resto (--transcript en el paso 6).

También elimine cualquier alucinación de final de clip: en el silencio final, whisper a menudo inventa una línea como un crédito de subtítulo o "Gracias por ver" (las líneas típicas de cada idioma se enumeran bajo whisper_hallucinations en languages/<code>.json). Elimínela con una seq_rule de N a 0. Otra alucinación: segmentos consecutivos con texto idéntico cuyas palabras tienen una duración casi nula (start==end), repeticiones inventadas sobre un silencio ("here we go" x3). Detección: segmentos i e i+1 con el mismo texto y palabras de duración 0. Solución: elimínelos de la transcripción antes de la construcción (del segs[i:j]).

3. Leer la transcripción de whisper y el guión de referencia

Imprima la transcripción segmento por segmento (text) y compárela con el texto de Premiere de entrada (paso 1). whisper ya corrige muchas cosas, pero mantiene errores tipográficos en los nombres. Lea el guión de referencia para obtener la ortografía canónica de los nombres propios y de los títulos de las obras que cita.

4. Construir corrections.json (el núcleo inteligente)

Aquí es donde se decide la calidad. Construya un archivo de correcciones comparando lo que whisper escuchó con los nombres del guión. Inspeccione la división palabra por palabra de los segmentos afectados para escribir reglas exactas:

python3 scripts/words_of.py /tmp/subs_transcript.json 2 9 38   # tokens indexados de los segmentos a examinar

La salida indexa cada palabra (9:Smyth 10:Jones): eso es lo que le permite escribir reglas exactas sin adivinar la tokenización (cuántos tokens ocupa un nombre, guiones conservados, etc.).

Formato de corrections.json (todas las claves opcionales):

{
  "quote_char": "\"",
  "normalize_cotes": true,
  "seq_rules": [
    [["jon","smyth"], ["John","Smith"]],
    [["train","park"],    ["Trent","Parke"]],
    [["arthus","bertrand"], ["Arthus-Bertrand"]]
  ],
  "word_rules": {"macdum": "Magnum", "scholler": "Schoeller"},
  "titles": [
    {"match": ["the","decisive","moment"], "canonical": ["The","Decisive","Moment"]},
    {"match": ["errance"], "canonical": ["Errance"], "sep_comma": true}
  ]
}
  • seq_rules: nombres de varias palabras, o de 1 a N / de N a 1. La coincidencia se realiza sobre las palabras en minúsculas, sin puntuación. Úselo para cualquier nombre que abarque 2 o más palabras de whisper, o cuando el número de palabras cambie. Mayúsculas/minúsculas: las palabras de destino se usan LITERALMENTE: escriba las mayúsculas/minúsculas finales que desee, incluida la posición en la oración (["Hoy","nosotros",...] si la palabra abre la oración, no ["hoy",...]). Misma longitud fuente/destino: la puntuación (comas internas) y los tiempos se conservan por índice; longitudes diferentes: los tiempos se redistribuyen y solo sobrevive la puntuación de la primera/última palabra (las comas internas de las palabras reemplazadas se pierden: relea esos segmentos en el paso 7). Eliminación: un destino vacío [["uh"], []] elimina la palabra (resérvelo para ruidos aislados reales que whisper duplicó; para una palabra mal transcrita, corríjala en lugar de eliminarla, o perderá parte de una oración).
  • word_rules: un nombre de una palabra, mayúsculas/minúsculas forzadas (clave en minúsculas al valor exacto).
  • titles: obras citadas (libros, series, fotografías tituladas) obtienen comillas automáticas (apertura en la primera palabra, cierre en la última) más un canonical para las mayúsculas/minúsculas correctas del título. sep_comma: true agrega una coma después del título (útil cuando varios títulos se suceden). Ponga obras entre comillas, no nombres de personas o marcas.
  • normalize_cotes: aplica las normalizaciones ortográficas del archivo de idioma (normalize_words), para variantes que whisper escribe de manera inconsistente. Desactivado a menos que lo configure.
  • french_typography: el interruptor para el espaciado tipográfico del idioma (el nombre es histórico; se aplica a cualquier idioma en uso). El valor predeterminado se toma del archivo de idioma: para francés, un espacio de no separación (U+00A0) antes de los signos de puntuación dobles y la comilla de cierre, y después de la comilla de apertura; para inglés, ninguno. Establezca false solo si desea puntuación adjunta. Cuando vuelva a puntuar en el paso 2b, la puntuación DEBE estar adjunta (Flop:); build_subs.py restaura el espaciado por sí mismo.

5. Resolver incertidumbres CON el usuario (nunca inventar)

whisper a veces produce una palabra no nombre ambigua o ininteligible. No adivine en silencio. Enumere estos puntos con su código de tiempo y pregunte al usuario qué se dice realmente, ofreciendo una hipótesis si el contexto o el guión sugieren una. Distinga:

  • Corregir directamente: nombres propios confirmados por el guión; errores tipográficos obvios y ciertos.
  • Preguntar primero: palabras no nombre ambiguas, o cualquier corrección que cambie el significado (por ejemplo, whisper "quien siente" contra el guión "sin": una inversión de significado, márquelo incluso si el guión lo resuelve).

No reescriba el estilo hablado (jerga, frases de relleno, repeticiones): los subtítulos deben seguir el audio.

Qué escribir en el archivo mientras espera la respuesta (no necesita bloquear la generación): produzca una primera versión que aplique todas las correcciones seguras más su mejor hipótesis para las palabras ambiguas comunes (y márquelas), pero mantenga la ortografía original de whisper para un NOMBRE incierto que no esté en el guión: nunca invente un nombre propio. Entregue esa versión con la lista de puntos a validar (códigos de tiempo), luego regenerar después de la respuesta actualizando corrections.json. Ese es el flujo normal: generar, presentar con las preguntas, refinar.

6. Generar el archivo corregido

python3 scripts/build_subs.py \
  --transcript /tmp/subs_transcript.json \
  --original "<Reel.json>" \
  --corrections /tmp/corrections.json \
  --output "<folder>/<name>_corrige.json" [--lang <code>]

El script clona el formato del original (UUID del speaker, idioma, claves ordenadas, UTF-8 minimizado), restaura puntuación y mayúsculas/minúsculas, aplica las correcciones, agrega las comillas, recalcula eos e imprime un informe de validación.

Escriba <name>_corrige.json JUNTO AL original, nunca sobrescriba la exportación original (es la fuente del usuario). El sufijo _corrige es un nombre de archivo en el que confían los scripts: manténgalo.

6a. Verificar las 2 trampas recurrentes de build_subs.py (cada vez)

Dos errores vistos en muchos vídeos, a verificar después de CADA construcción:

  1. Números decimales duplicados. whisper divide 6.6 en 2 tokens (6 + .6; igualmente 1,33, o 4 AM en 4 + AM); la reinyección de puntuación los duplica en la salida (1.4 .4, 6.6. .6), y seq_rules coinciden mal (la normalización no elimina un ./, interno). Detección: grep en la salida final los patrones \d[.,]?\s+[.,]\d. Solución: cirugía en la transcripción ANTES de la construcción: fusionar los 2 tokens en uno ({'text':'1.4','start':a.start,'end':b.end,...}; esquema de palabra de whisper = text/start/end/confidence, NO duration), luego reconstruir.
  2. Puntuación aislada de final de oración eliminada. Un ?/!/: presente en el text del segmento de whisper pero sin un token de palabra propio desaparece (¿Cómo lo hicimos ? pierde su ?). Detección: compare count('?') (y !, :) entre el texto de whisper y la salida. Solución: edite posteriormente el JSON de salida: vuelva a agregar ? (con el espaciado del idioma) al token anterior.

Si se detecta la trampa 1, corrija la transcripción y reconstruya (paso 6). Si es la trampa 2, parche la salida directamente.

6b. Realinear los tiempos sobre el audio: alineación forzada (SIEMPRE)

Los tiempos de las palabras de whisper SE DESVÍAN (medido hasta 7 s antes, no lineal) con monólogos rápidos y de múltiples tomas: whisper aplana los silencios entre tomas, por lo que la voz llega DESPUÉS del subtítulo. Queja típica: "cuando aparece el 'Ah' ya escucho la siguiente oración". DTW (-dtw) no cambia nada (mismos desplazamientos), y una deformación temporal sobre anclas dispersas distorsiona las zonas que ya estaban bien. El texto de whisper es bueno, solo sus tiempos son incorrectos. Solución: alineación forzada con ctc-forced-aligner:

python3 scripts/realign_timings.py \
  --subs "<folder>/<name>_corrige.json" \
  --audio /tmp/subs_audio.wav \
  --output "<folder>/<name>_corrige_alignee.json" \
  --lang <code>

La salida tiene un sufijo _alignee (añadido al archivo de entrada, antes de .json). El script produce un informe de desplazamiento (/tmp/realign_report.json) que muestra las correcciones hechas. La alineación forzada se ejecuta SÓLO en este paso final, sobre el JSON ya corregido (puntuación, mayúsculas/minúsculas, nombres). NO la ejecute sobre la transcripción sin procesar: los tiempos de corrección + puntuación deben estar finalizados primero.

7. Validar el resultado

Compare el JSON generado con el original (instantánea del paso 1) en Premiere: impórtelos uno al lado del otro y reproduzca. Verifique:

  • Cronometraje: cada palabra aparece cuando se dice (sin desfase).
  • Puntuación: los subtítulos tienen sentido (puntos, comas, signos de interrogación).
  • Nombres: la ortografía coincide con el guión de referencia.
  • Comillas: los títulos de obras (libros, series, fotografías) están entre comillas.
  • Sin alucinaciones: sin líneas finales inventadas ni repeticiones.

Si se detecta un error tipográfico: actualice corrections.json y regenere (paso 6).

8. Reimportar en Premiere

En Premiere, elimine los captions existentes de la secuencia. Luego:

  • Archivo → Importar → seleccione el archivo <name>_corrige_alignee.json.
  • Arrástrelo a la línea de tiempo de captions.
  • Verifique que los captions aparezcan en los momentos correctos con el texto correcto.

9. Limpieza

Elimine los archivos temporales:

rm -rf /tmp/subs_*

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

Skills relacionados