Communitygithub.com

businessbaum/vedit

Genera archivos ASS de subtítulos y los incrusta en el renderizado, con soporte para alemán, presets configurables y verificación de fuentes.

¿Qué es vedit?

Esta skill genera archivos de subtítulos ASS a partir de transcripciones palabra por palabra y los incrusta en el vídeo final mediante libass. Está diseñada específicamente para manejar las complejidades del alemán, como los sustantivos compuestos largos (ej. 'Geschwindigkeitsbegrenzung'), utilizando un presupuesto de ancho de píxel en lugar de un límite de caracteres para evitar desbordamientos. Soporta múltiples presets de estilo (vertical, YouTube, etc.), verifica que las fuentes sean estáticas y de un solo peso para evitar fallos silenciosos de libass, y realiza una comprobación de calidad (QC) que compara hashes para asegurar que la fuente cargada se renderiza correctamente. Funciona con proyectos de vídeo existentes y requiere permisos para acceder a la carpeta de fuentes y al archivo ASS generado.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/businessbaum/vedit/tree/HEAD/.agents/skills/captions

Preguntar en tu IA favorita

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

Documentación

captions

Genera un archivo de subtítulos ASS a partir de la transcripción palabra por palabra y lo incrusta en el renderizado. Argumentos: slug de $project, $preset un nombre de presets/captions/*.json. Solicitud completa en $ARGUMENTS. Un marcador $ vacío significa que la skill se activó automáticamente en lugar de ser invocada por barra: toma el proyecto de PIPELINE_STATE en el contexto de la sesión e indica cuál seleccionaste. Los nombres de verbo siguen el contrato de compilación; python engine/ve.py --help prevalece.

Dos reglas específicas del alemán que determinan si esto funciona

1. Nunca agrupar por max_chars

Los sustantivos compuestos alemanes son tokens únicos e indivisibles que superan cualquier límite de caracteres. Geschwindigkeitsbegrenzung tiene 26 caracteres y ocupa aproximadamente el 60% de un fotograma de 1920 a 90 px. Una regla de max_chars: 22 no puede dividirlo, por lo que desborda el fotograma o el agrupador emite silenciosamente una línea que prometió no emitir.

Agrupar por un presupuesto de ancho de píxel en su lugar, con una regla de reducción automática para una sola palabra que supere el presupuesto por sí misma. estimate_text_width_px() es la encargada de la medición; group_words() es la encargada del presupuesto. Los detalles están en docs/fonts.md y en el docstring del módulo engine/vedit/captions.py.

Este es el error de subtítulos en alemán más común y es completamente evitable.

2. La fuente debe ser un archivo estático de un solo peso, nombrado por su nombre exacto nameID 1

Las fuentes variables no se pueden seleccionar por peso desde ASS. InterVariable.ttf se renderiza byte-idéntico a Arial a través de libass - mismo md5 - mientras registra que cargó el archivo. Salida 0, sin advertencia. Style: D,Inter,...,Bold=-1 obtiene DirectWrite Arial y nadie se da cuenta hasta que la exportación está en YouTube.

Usar únicamente estas, con la cadena exacta de Fontname y el valor de Bold:

archivoASS FontnameBold
Inter-Regular.ttfInter0
Inter-Bold.ttfInter-1
Inter-SemiBold.ttfInter SemiBold0
Inter-ExtraBold.ttfInter ExtraBold0
Montserrat-Bold.ttfMontserrat-1
Montserrat-ExtraBold.ttfMontserrat ExtraBold0

assets/fonts/ contiene solo fuentes. libass carga cada archivo que encuentra allí y da error en cualquier otra cosa: un README.md suelto produce Error opening memory font 'README.md'. Ese directorio tiene un permiso de ask en la configuración precisamente por esta razón; si un documento pertenece junto con las fuentes, va en docs/fonts.md.

Paso 1 - elegir un preset

presets/captions/*.json contiene el estilo, el presupuesto de agrupación, la posición y el comportamiento de resaltado. Elegir por output.platform, no por preferencia:

  • Las plataformas verticales quieren texto grande, centrado, de 1 a 3 palabras, alto en el área segura - el tercio inferior está debajo de la propia interfaz de la plataforma.
  • El contenido largo de YouTube quiere dos líneas, ancladas abajo, con un tamaño que un espectador de móvil pueda leer.

Confirmar la elección con el usuario antes de generar y mencionar qué cambia.

Paso 2 - generar el ASS

python engine/ve.py captions $project --preset $preset

Dos requisitos estructurales, ambos fallan silenciosamente si se violan:

  • La línea V4+ Style: Format: de 23 campos (Name .. Encoding, la propia lista de campos de libass - el campo 24, AlphaLevel, es SSA v4 y no ASS V4+) y la línea Format: de 10 campos de Eventos deben ser canónicas, y la fila debajo de cada una debe tener exactamente esa cantidad de columnas. Una discrepancia en el recuento hace que libass salga con 0, anteponga una coma extra en el texto renderizado y elimine silenciosamente el contorno y la negrita. Reproducido con un Format de 5 campos contra un Dialogue de 6 campos. captions.validate_ass() verifica ambos recuentos antes de cualquier incrustación; no es opcional y burn_in() se niega en caso de fallo.
  • El archivo debe ser UTF-8. Los diacríticos alemanes viajan correctamente a través de libass cuando lo es - Größe Straße Übermäßig äöüÄÖÜß fue verificado de principio a fin.

El archivo secundario se escribe en projects/<slug>/out/captions.ass y se referencia desde captions.sidecar_path.

El tiempo se obtiene de la misma lista de palabras de la que provienen los cortes, mapeada a través de las posiciones de la línea de tiempo en _computed. Por lo tanto, los subtítulos se invalidan con cualquier cambio de corte: regenerar después de volver a cortar en lugar de parchear.

Paso 3 - corregir el texto, no la transcripción

Los números son el problema predecible. --suppress_numerals es obligatorio en la transcripción, por lo que la transcripción dice zwanzig donde el hablante dijo "20". Corregirlo en captions.overrides[] como {word_index, text}, nunca editando la transcripción: la transcripción es el ancla para cada rango de palabras en el proyecto y reescribirla desincroniza los cortes.

Lo mismo para nombres propios, nombres de productos y abreviaciones tipo GmbH. Recopilarlos en una sola pasada escaneando palabras con un score bajo: la puntuación de alineación es inútil como señal de relleno, pero es una buena señal de "el alineador no estaba seguro de qué era esta palabra", que es exactamente lo que parece un nombre propio.

Paso 4 - incrustar

python engine/ve.py render $project

La incrustación ocurre en el multiplexado final, una vez, sobre el vídeo concatenado - nunca por segmento. Una incrustación por segmento vuelve a renderizar el mismo subtítulo en cada límite y coloca una línea parcial a cada lado de un corte.

El escape de las rutas subtitles= y fontsdir= es exacto y no obvio. Barras inclinadas hacia adelante en todas partes, y el valor entrecomillado con dos puntos de unidad escapados individualmente:

subtitles='C\:/Users/me/.../out/captions.ass':fontsdir='C\:/Users/me/.../assets/fonts'

escape_filter_path() en vedit/ffmpeg.py es la encargada de esto y devuelve la cadena incluyendo sus comillas - no añadir las propias. Las comillas, no la profundidad de anidamiento, es lo que decide: la forma simple con un solo escape falla con No option name near '/Users/...', y solo la forma entrecomillada sobrevive a una coma, punto y coma o corchete en la ruta, todos los cuales son legales en Windows. escape_filter_path_nested() devuelve la variante sin comillas con doble escape C\\:/... para cualquiera que la necesite, con esa limitación documentada.

Todo pasa a través de una lista argv desde Python. La misma ruta construida en Git Bash se convierte en C\;C:\Program Files\Git\Users\... - reproducido en vivo, no teórico.

Establecer captions.enabled y captions.burn_in, y establecer stage a "captions".

Paso 5 - demostrar que la fuente realmente se cargó

La verificación de calidad (e) es obligatoria y esta es la única forma de detectar una fuente de respaldo silenciosa. Renderizar un fotograma de subtítulo con el estilo del proyecto y otro con un estilo de control Arial, y afirmar que los hashes difieren. Hashes idénticos significan que libass recurrió al respaldo y la exportación se enviará con el tipo de letra incorrecto.

Luego también confirmar: recuentos de campos en las líneas Format:, ninguna línea que exceda el presupuesto de píxeles, y ningún subtítulo que cruce un límite de segmento dejando un fragmento.

Modos de fallo

SíntomaCausaSolución
Los subtítulos parecen ArialFuente variable, o Fontname incorrectoArchivo estático + cadena exacta nameID 1; ejecutar QC (e)
Coma extra en el texto, sin contornoLínea Format: mal formadaEmitir líneas canónicas de 24/10 campos
Error opening memory font 'README.md'Archivo que no es de fuente en assets/fonts/Solo fuentes en ese directorio
Una palabra larga se sale del fotogramaAgrupado por max_charsPresupuesto de ancho de píxel más reducción automática de una sola palabra
No option name near '/Users/...'Escape simple de fontsdirDuplicar la barra invertida antes de los dos puntos
Los subtítulos escriben números--suppress_numerals, según lo diseñadoEntradas en captions.overrides
Los subtítulos se desincronizan después de un recorteASS obsoletoRegenerar; nunca parchear tiempos manualmente

Individual skills in this repo

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

businessbaum/vedit

This skill should be used when the user asks for mechanical tightening of material that is already selected - "mach den Rohschnitt", "bau den Rohschnitt", "schneid die Ähms raus", "nimm die Füllwörter raus", "entferne die Pausen", "kürz die Pausen", "schneid die Versprecher raus", "mach den Radioschnitt", "straff das", "build the rough cut", "cut the fillers", "remove the ums", "tighten the pauses", "trim the dead air", "cut the retakes", "radio edit". Turns kept story blocks into enabled segments in project.json. Does not decide what the video is about - that is cut-story.

businessbaum/vedit

This skill should be used when the user asks to decide what the video is actually about before any mechanical cutting - "bau die Story", "was soll ins Video", "was kann raus", "welche Themen bleiben", "kürz das auf 6 Minuten", "mach daraus ein kurzes Video", "wo ist der Hook", "sortier die Themen", "worum geht es hier eigentlich", "build the story", "decide what stays", "structure this", "cut this down to N minutes", "find the hook", "reorder the topics". This is the CONTENT-selection pass that writes the "story" block in project.json; it runs after transcribe and before cut-rough.

businessbaum/vedit

This skill should be used when the user asks for the finished video file - "exportier das", "render das fertig", "mach das final", "renderte das Video", "mach den Endexport", "bau das Video zusammen", "mach es fertig für YouTube", "gib mir die MP4", "export the final", "render the final cut", "make the master", "export for TikTok", "give me the mp4". Covers segment rendering with the cache, guarded concat, the one-pass audio, loudness normalisation and the single mux.

Skills relacionados