Agregar la introducción y el cierre
Las cortinillas son MP4 cortos con su propio jingle, un par por orientación, listados en las preferencias. Este skill normaliza todo a un formato, une la introducción, el video y el cierre con fundidos cruzados cortos (video y audio), y renderiza el resultado.
Preferencias
remote-video-editing/preferences.json (orden de búsqueda como en clean-voice-sidon):
| Clave | Significado |
|---|---|
stings | {"intro_16x9": url, "outro_16x9": url, "intro_9x16": url, "outro_9x16": url}; URLs o rutas locales |
sting_fade_s | Duración del fundido cruzado, por defecto 0.5 |
render_notes | Dónde ejecutar codificaciones largas (por ejemplo, un servidor en lugar de una laptop). Síguelas. |
Si stings no está configurado, pide los archivos. El plugin no incluye ningún activo de marca.
Orientación
Lee la orientación mostrada, no el tamaño codificado: los teléfonos almacenan video vertical como 1920x1080 con una etiqueta de rotación. Verifica con
ffprobe -v error -select_streams v:0 -show_entries stream=width,height:stream_side_data=rotation -of compact IN.
Una rotación de 90 o -90 intercambia ancho y alto. ffmpeg aplica la rotación automáticamente al decodificar.
- Video vertical (9:16): usa
intro_9x16/outro_9x16, lienzo 1080x1920. - Horizontal: usa el par 16:9, lienzo 1920x1080.
- Corte 16:9 de un video vertical (para el feed principal de YouTube): lienzo 1920x1080, el video escalado a 1080 de alto en el centro, sobre una copia desenfocada, oscurecida y ampliada de sí mismo; usa las cortinillas 16:9. La cadena
[v1]a continuación se convierte en:
Pruébalo en un corte de 8 segundos ([1:v]fps=30,split[a][b]; [a]scale=1920:1080:force_original_aspect_ratio=increase,crop=1920:1080,scale=480:270,boxblur=20:2,scale=1920:1080,eq=brightness=-0.06:saturation=0.9[bg]; [b]scale=-2:1080[fg];[bg][fg]overlay=(W-w)/2:0,setsar=1,format=yuv420p[v1]-ss 60 -t 8) y muestra un fotograma antes de renderizar todo.
Renderizar
W:H es el lienzo, F el fundido (0.5), DI la duración de la introducción, DV la duración del video (ffprobe format=duration). Desplazamientos: primer fundido en DI-F; segundo en DI+DV-2*F.
ffmpeg -y -i intro.mp4 -i main.mp4 -i outro.mp4 -filter_complex "
[0:v]fps=30,scale=W:H,setsar=1,format=yuv420p[v0];
[1:v]fps=30,scale=W:H:force_original_aspect_ratio=decrease,pad=W:H:(ow-iw)/2:(oh-ih)/2,setsar=1,format=yuv420p[v1];
[2:v]fps=30,scale=W:H,setsar=1,format=yuv420p[v2];
[0:a]aresample=48000,aformat=channel_layouts=stereo[a0];
[1:a]aresample=48000,aformat=channel_layouts=stereo[a1];
[2:a]aresample=48000,aformat=channel_layouts=stereo[a2];
[v0][v1]xfade=transition=fade:duration=F:offset=DI-F[v01];
[v01][v2]xfade=transition=fade:duration=F:offset=DI+DV-2*F[v];
[a0][a1]acrossfade=d=F[a01];[a01][a2]acrossfade=d=F[a]" \
-map "[v]" -map "[a]" -c:v libx264 -preset medium -crf 20 -maxrate 10M -bufsize 20M -c:a aac -b:a 192k -movflags +faststart out.mp4
- Varios clips fuente: únelos primero (el mismo patrón de filtro, o el demuxer concat si comparten códec), luego trata el resultado como
main.mp4. - Esto es un re-encode completo: minutos de todos los núcleos para un video de 10 minutos en 1080p. Ejecútalo en Modal, no localmente ni en un servidor doméstico: coloca los argumentos después de las entradas (desde
-filter_complexhasta+faststart) uno por línea enrender.args, luego$MODAL run "${CLAUDE_PLUGIN_ROOT}/scripts/ffmpeg_modal.py" --inputs intro.mp4,main.mp4,outro.mp4 --args-file render.args --out out.mp4(modal_commandyrun_notesde las preferencias indican dónde se ejecuta Modal). Une varios clips en el mismo grafo (filtroconcat) en lugar de hacer una pasada separada. Elimina la carpeta del trabajo en el volumenvideo-editing-workdespués. - xfade necesita que ambas entradas estén en la misma base de tiempo: pon
fps=30,settb=1/30en cada rama de video (incluyendo después de unconcat), o ffmpeg falla con "First input link main timebase (1/30) do not match". - Tasa de bits: mantén el límite (
-crf 20 -maxrate 10M -bufsize 20M). CRF 18 simple en material de teléfono ruidoso resultó en 24 Mbps, unos 2 GB para 11 minutos (2026-11-13). YouTube vuelve a codificar de todos modos y sugiere 8-12 Mbps para subidas en 1080p. - Verifica: la duración de salida es aproximadamente
DI + DV + DO - 2*F, y revisa los fotogramas enDI-F/2(mitad del fundido), la mitad y el final.
Material de mano: estabilizar en el mismo render
Los videos de teléfono tomados a mano se benefician de una estabilización ligera (el usuario, 2026-11-13, después de la primera actualización del fin de semana de Hebrish salió sin ella). Usa vid.stab de ffmpeg (libvidstab; Debian's ffmpeg, que la imagen de Modal instala, lo tiene; verifica con ffmpeg -filters | grep vidstab). Dos pasadas, ambas en Modal:
- Detectar (rápido, solo análisis, escribe un archivo de transformaciones), por clip fuente:
ffmpeg -i main.mp4 -vf vidstabdetect=shakiness=5:accuracy=15:result=main.trf -f null -Ejecútalo como su propio trabajo deffmpeg_modal.py(args-vf/vidstabdetect=...:result=/work/<job>/main.trf/-f/null, salida-), o agrega un paso de detección al script. El.trfdebe estar junto a la entrada en el volumen. - Transformar dentro del render principal, lo primero en la rama de video de ese clip, antes de la gradación y el escalado:
vidstabtransform=input=main.trf:smoothing=20:zoom=2:optzoom=0,unsharp=5:5:0.4smoothingalrededor de 15-30 (fotogramas) mantiene el movimiento natural; unzoom=2fijo (porcentaje) oculta los bordes móviles sin el bombeo que causaoptzoom=1en un rostro parlante. La ligera nitidez compensa la suavidad del remuestreo.
Pruébalo en un corte de 8 segundos primero y compara una imagen fija y un clip corto con el usuario antes del render completo. Si falta libvidstab, deshake es un fallback de una pasada (más débil). No se necesitan estabilizadores de IA para esto.
Crear cortinillas
Las cortinillas en uso se renderizaron a partir de los SVG de la marca con un pequeño script de Pillow más un jingle generado. Encuéntralo con find-script (etiquetas logo, sting, intro), y no regeneres el logo con un modelo de video: los modelos de video distorsionan las formas de las letras.