overlays: gráficos sincronizados con la voz, colocados en Premiere
Dos habilidades, un taller
Aquí (overlays): colocar y editar | Taller (motion-design): crear |
|---|---|
transcripción, línea de tiempo del .prproj, plan escrito como anclajes de frases | el ÚNICO proyecto Remotion: motion-design/src/, composiciones de superposiciones en src/illustration/ (BrandedVideo, LowerThirdTrack, Screencast, Scene, FondAnime) |
obtención de recursos: fotos, b-rolls, GIFs (gifs.py), capturas de web, tarjetas de correo (mail-cards.py) | public/: fuentes, scenes/<video>/ (medios de escenas), sfx/ (sonidos) |
grabación de demos de sitios (screencast-record.py) | tema (src/illustration/theme.ts), paleta de sonidos (reference/sound-palette.md, scripts/make-sfx.py, scripts/son.py) |
render-segments.py, screencast-render.py, montage-tella.py: iniciar el render DENTRO del taller, luego colocar todo en el XML de Premiere | música, mezcla |
Las dos carpetas deben ser vecinas (<skills>/overlays y <skills>/motion-design): los scripts encuentran el taller a través de ../motion-design. Un nuevo componente de escena se escribe en motion-design/src/illustration/scenes/; un nuevo sonido va en las recetas de síntesis del taller. Instalación (una vez por máquina, instalación del plugin): bash "${CLAUDE_PLUGIN_ROOT}/install.sh" --data "${CLAUDE_PLUGIN_DATA}". A mano, con las cuatro carpetas de habilidades copiadas una al lado de la otra: bash install.sh desde la raíz del kit. Después de una actualización del plugin, el enlace node_modules del taller desaparece: vuelve a ejecutar la línea de configuración. Detalles: INSTALL.md del kit.
Regla de oro editorial (configuración predeterminada, adaptarla al canal)
El video se sostiene por la voz. Los gráficos no vuelven a contar lo que se dice: anclan lo que de otro modo solo se sentiría (un nombre propio, una cita, un lugar, un título, un número), o MUESTRAN de lo que se habla (un documento, un sitio, un diagrama).
Dos niveles de vestimenta:
- ligero: 1 premium por capítulo + 1 tercio inferior cada 45 a 60 s, fuera de la intro (0-30 s) y la conclusión;
- denso (demo, video de investigación o tutorial): escenas animadas sobre el 60 al 70% de la imagen, cada escena sincronizada con las palabras, sin escena sin movimiento durante más de 3 s. El talking-head mantiene su lugar.
Pregunta por el nivel deseado si no se menciona. No animar por defecto: la intro (los espectadores deciden si se quedan), una anécdota íntima (la animación mata la emoción), una pregunta que genera suspenso, la conclusión y el llamado a la acción, una lista de adjetivos.
Una desviación del guion dicha en cámara (un número, un término, una aproximación) se corrige EN PANTALLA: el corte bruto lo marca en su informe, el plan pone el valor correcto sobre la voz, sin pedir una nueva toma.
Regla cero: la línea de tiempo de referencia es la del proyecto de Premiere, nunca la del corte bruto
El orden es SIEMPRE: corte bruto (rough-cut) -> el editor revisa y cambia la edición en Premiere -> superposiciones. Entre ambos, los cortes se mueven, los clips se eliminan, la duración cambia: los tiempos calculados en el corte bruto terminan con 30 s a 1 min de desviación y los elementos terminan después del final del video.
Parada forzosa después del corte bruto, incluso si el usuario dijo "corte bruto y luego superposiciones, depende de ti": entregar el XML del corte bruto, luego esperar hasta que el usuario diga que lo validó en Premiere. Antes de eso, ningún plan, ni tiempos, ni colocación. Solo lo que no depende de tiempos puede continuar (obtención de imágenes y GIFs).
python3 scripts/timeline-from-prproj.py "/ruta/clip.MOV" --list # qué secuencias contienen el clip
python3 scripts/timeline-from-prproj.py "/ruta/clip.MOV" --sequence "Derush_v1"
# -> <clip>_transcription.json (palabras en tiempo de SECUENCIA) + <clip>_timeline.json (mapa clip -> secuencia)
Lee los clips del metraje en la secuencia elegida (ticks = 254016000000/s) y transfiere las palabras de <clip>_Derush_vN_work/words_premiere.json (la transcripción del proyecto, ya realineada por el corte bruto). Varias secuencias contienen el clip: se niega a adivinar (código 2), preguntar cuál. Varios clips en una secuencia: pasarlos todos, en orden (A.MOV B.MOV --sequence "..."); cada entrada de línea de tiempo y cada palabra lleva media, y las salidas toman el nombre del primer clip. Una palabra cortada al ras se mantiene si COMIENZA en el clip (al menos 80 ms antes del corte); una palabra verdaderamente cortada rompe el anclaje que la apunta: mover el anclaje a la palabra anterior + offset. Verificar que el último elemento colocado se mantenga antes del final real de la secuencia.
Nunca reutilizar un cuts_final.json o un mapa del corte bruto. Sin un proyecto de Premiere (clip solo): python3 scripts/transcribe.py "/ruta/clip.mp4" (whisper.cpp local, aproximadamente 5 min para 15 min de metraje en Apple Silicon).
Cuando el usuario devuelve la edición, ejecutar primero la comparación: los cambios que hicieron en Premiere son la retroalimentación más confiable. Comparar su secuencia con el XML del corte bruto, convertir cada discrepancia recurrente en una corrección, una verificación o una regla, y solo entonces escribir el plan de superposiciones. Cuando el usuario valida la edición final, hacer lo mismo con el XML de superposiciones entregado: lo que eliminaron, movieron (más de 2 fotogramas), redimensionaron o agregaron (su propio metraje pegado en V1 no cuenta) se convierte en una corrección, una verificación o una regla. Nada eliminado o movido significa que el método de esa entrega es correcto: mantenerlo.
Pipeline
0. (Opcional) b-rolls
Una carpeta Brolls/ junto al clip, archivos nombrados NN_description_K.mp4 (número, descripción, variante):
python3 scripts/match-brolls.py "/ruta/clip.mp4" # -> <clip>_broll-script.json para completar
Para cada b-roll: la variante (file), el tiempo (timecode MM:SS, verificado en la transcripción), durationSec (null = duración nativa). Colocado en V2. Ejecutarlo ANTES de las demos de sitios (--force sobrescribe el archivo). Mirar una hoja de contactos de las variantes antes de elegir (3 fotogramas por variante): un nombre de archivo a menudo miente ("street-photography-portrait" era una toma de drone de una ciudad de noche). Nunca poner un b-roll en una frase que el hablante no muestra literalmente, y ningún sonido de superposición mientras se reproduce un b-roll.
0 bis. (Opcional) fotos de ilustración
Para un video que cita personas, obras, libros, lugares:
- Manifiesto a partir del guion del video: entidades en orden de aparición ->
illustrations-manifest.json:El guion de referencia puede estar en cualquier formato (texto, Markdown, un documento): da la ortografía correcta y las cosas a mostrar.{"version": 1, "dest": "Illustrations", "items": [ {"n": 1, "categorie": "reference", "nom": "Nombre Apellido", "sujet": "portrait", "url": "https://...", "fallback_urls": [], "note": "fuente: ..."}]} - URLs: fuentes gratuitas primero (Wikimedia Commons, sitios oficiales, museos de acceso abierto), luego
python3 scripts/serp_images.py "Nombre Título" --num 5 --min-width 1200(Google Images a través de SerpAPI, claveSERPAPI_API_KEY). Verificar que la imagen sea la correcta (mirar la miniatura, no solo el título de la página; las páginas de lotes de subasta son una trampa). Verificar los derechos si el video se vende. - Descarga:
python3 scripts/source-illustrations.py illustrations-manifest.json->Illustrations/NN-category-name-subject.ext(bytes verificados, ancho medido; salir 1 si faltan URLs). - Tiempos:
python3 scripts/match-photos.py "/ruta/clip.mp4"-><clip>_photos-script.json, tiempos prellenados por una búsqueda difusa del nombre en la transcripción (el reconocimiento de voz distorsiona los nombres propios). SIEMPRE volver a verificar cada tiempo manualmente contra la transcripción (la coincidencia difusa puede tomar una mención tardía en lugar de la primera);enabled: falseelimina una foto. Las fotos van en V3, con prioridad sobre los b-rolls (un b-roll que se superpone a una foto se elimina del XML y se registra).
Tratamiento de fotos: nunca recortar (relación de aspecto nativa, una vertical se mantiene vertical); una foto se asienta sobre un fondo liso con un margen en los cuatro lados (aproximadamente 86% del fotograma), no un contenedor a pantalla completa que toque los bordes superior e inferior. No .webp en el manifiesto (Premiere lo lee como un medio de longitud cero en XML: convertir con sips -s format jpeg). Una foto de menos de 800 px de ancho se verá suave: render-segments.py imprime una advertencia BAJA RESOLUCIÓN, listarlas antes de entregar. La portada de un libro mostrada por una escena es la imagen visual del editor tomada de la web (su sitio, un mockup HD), nunca una imagen extraída del metraje del propio hablante.
Cuando el hablante MUESTRA un objeto, nada lo cubre. Escanear la transcripción en busca de "aquí", "como puedes ver aquí", "este", "mira" cerca de la mención de una obra: el hablante está sosteniendo el libro o la cámara frente al lente. Sin foto a pantalla completa en esa ventana (un editor eliminó cada una de esas portadas en una edición entregada); la portada va antes o después de la demostración, o no va.
0 ter. GIFs y memes (un básico, no una opción)
Un GIF cada 2 a 4 min, en momentos de estado de ánimo o autocrítica ("esto empuja muy fuerte", "me lo estoy inventando todo"), nunca en la intro, nunca para ilustrar una frase técnica literalmente. Una escena gif (superposición, marco blanco, bucle) que llega con la palabra, con su efecto de sonido.
python3 scripts/gifs.py chercher "that's a bug" --planche gifs-sheet.jpg # lista + hoja de contactos numerada
python3 scripts/gifs.py prendre <id> --nom bug --public <video> # fuente MP4 + datos de la escena
Mirar la hoja de contactos ANTES de elegir (un título de GIPHY no dice lo que se muestra; buscar en inglés a menudo funciona mejor). prendre descarga el MP4 (nunca el .gif) en Illustrations/gifs/, mantiene gifs.json (fuente de cada GIF), lo copia a motion-design/public/scenes/<video>/gifs/ e imprime los data de la escena gif (src, srcW, srcH, loopSec). Clave GIPHY_API_KEY (gratuita). Un GIF a menudo reutiliza una obra protegida: en un video que se vende, abstenerse.
1. Frecuencia de fotogramas y formato del clip (CRÍTICO)
ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate,width,height -of csv=p=0 "/ruta/clip.mp4"
render-segments.py lee la frecuencia y el tamaño del clip cuando el JSON no los proporciona. Un JSON a 30 en un clip a 60 desplaza todo por un factor de 2. A 29.97, n fotogramas de secuencia duran n x 1.001 / 30 s, y cada render debe cubrir su clip (tailFrames y el componente TenirFin se encargan de ello). Un clip se escribe en la frecuencia de su MEDIO: Premiere lee <in> a 29.97 (la frecuencia del <file>), no en el <rate> del clip; el script escribe in = round(t x fps x 1000/1001) con <ntsc>TRUE.
Vertical 9:16 (reel): mismo pipeline, mismo JSON. El tercio inferior detecta vertical y se mueve hacia arriba sobre la zona de subtítulos; las fotos permanecen enteras (una banda en el fondo); un b-roll 16:9 obtiene una escala que llena el ancho. Reglas de reel (de la comparación de entregas con la edición final): fotos de 2 a 3 s (predeterminado 2.5), tercio inferior tan largo como su foto de retrato asociada y comenzando con ella, sin b-roll de stock a menos que se solicite, sin portada mientras el hablante muestra el objeto. Siempre renderizar UN tercio inferior como imagen fija y componerlo en un marco gris para verificar el diseño vertical antes de la serie.
2. Leer antes de proponer
Leer la transcripción COMPLETA. Detectar: nombres de personas (tercio inferior "nom"), títulos de obras ("oeuvre"), lugares ("lieu"), términos o movimientos ("term"), citas fuertes (premium quote-full), anuncios de una parte (section-title o tarjeta de capítulo), números impactantes (escena stat), documentos, intercambios y sitios de los que se habla (escenas de prueba, demos). Cada nombre, obra o libro citado se MUESTRA en algún lugar (una escena colgante, una foto, una tarjeta) o se escribe la razón por la que no. Leer el contenido de cada diagrama en las PROPIAS palabras del hablante (volver a leer en la transcripción), nunca una interpretación: un diagrama incorrecto (un perfil colocado en el nivel incorrecto de un embudo) proviene de parafrasear.
3. Cada premium se concibe en una oración
"Lo que quiero ver: la frase grande, en cursiva, una regla de acento a la izquierda, la atribución pequeña." Si la descripción es "texto que aparece", no es un premium: tercio inferior, o nada.
4. Catálogo base
| Tipo | Componente / tipo | Pista | Datos | Duración |
|---|---|---|---|---|
motion (premium a pantalla completa) | quote-full | V5 opaca | text, author?, source? | 10-14 s |
lower-third | lower-third-nom | V6 alfa | name, years?, tag? | 6 s |
lower-third | lower-third-oeuvre | V6 | title, author, year? | 6 s |
lower-third | lower-third-lieu | V6 | place, date | 6 s |
lower-third | lower-third-term | V6 | term, subtitle? | 6 s |
section-title | V6 | eyebrow?, title | 4 s | |
scene | ver "Escenas animadas" | V5 / V6 / V7 | data, cues | sigue a la voz |
Un nombre y su obra citados dentro de 10 s: un solo tercio inferior "nom" con la obra en tag. Una cita del propio hablante: quote-full sin author (su voz firma). Las animaciones terminan con un fundido automático en los últimos 18 fotogramas.
5. Tiempos hasta la palabra (CRÍTICO)
Nunca una estimación: buscar la palabra en <clip>_transcription.json y tomar su start. El elemento aparece al inicio de la palabra (un tercio inferior puede precederla como máximo 0.5 s; sincronizar al inicio de la ORACIÓN se lee como un anuncio que llega temprano). render-segments.py rechaza un segmento sin un tiempo válido y verifica cada tiempo contra la transcripción (OK / WARN / UNKNOWN). También verificar la frecuencia de fotogramas en el JSON (paso 1).
6. Presentar el plan antes de escribir
| # | Tiempo | Dur | Tipo | Componente | Por qué |
|---|---|---|---|---|---|
| 1 | 01:24 | 6 s | lower-third | lower-third-nom | 1ª mención de Nombre Apellido |
| 2 | 02:14 | 12 s | motion | quote-full | cita clave de la parte 1 |
Esperar la aprobación antes de escribir el JSON y renderizar.
7. Renderizar e importar
(cd ../motion-design && npm ci) # una vez
python3 scripts/render-segments.py "/ruta/clip.mp4" # todo: renders + XML
python3 scripts/render-segments.py "/ruta/clip.mp4" --lower-thirds-only
python3 scripts/render-segments.py "/ruta/clip.mp4" --scenes-only
python3 scripts/render-segments.py "/ruta/clip.mp4" --photos-only
python3 scripts/render-segments.py "/ruta/clip.mp4" --preview # solo XML de previsualización
El script genera un XML de Premiere y archivos de medios en una carpeta _overlays/. Importar el XML en Premiere: File > Import... y seleccionar el archivo XML. Los medios se referencian por ruta relativa; si la carpeta _overlays/ está junto al clip, Premiere los encuentra automáticamente. Verificar que todos los clips se hayan importado correctamente (ningún medio faltante en el panel de proyecto de Premiere).