Communitygithub.com

videolabs: creación de videos (estilo curso en terminal)

Crea un curso en video técnico narrado de ~30 minutos usando Remotion + ElevenLabs, con estilo de terminal Omarchy / Tokyo Night, sincronización precisa de elementos visuales con la narración, subtítulos claros, renderizado remoto rápido y un reproductor web por capítulos.

¿Qué es videolabs: creación de videos (estilo curso en terminal)?

Este skill permite construir un video educativo técnico narrado de aproximadamente 30 minutos, utilizando Remotion para la generación de video y ElevenLabs para la síntesis de voz. El estilo visual es el de una terminal Omarchy / Tokyo Night (Waybar, ventana en mosaico, prompt oh-my-zsh, encabezados Markdown). Cada elemento en pantalla se revela exactamente cuando el narrador lo menciona, con pausas entre oraciones, subtítulos simples, renderizado remoto rápido y un reproductor web con capítulos. Para temas prácticos, primero construye un laboratorio real (máquinas virtuales QEMU/KVM) y utiliza la salida real de los comandos en pantalla y en la narración. La publicación al sitio videolabs (Cloudflare) la realiza el skill complementario 'videolabs_publish'. Úsalo cuando el usuario desee un video explicativo, curso, capacitación o laboratorio sobre un tema técnico, o cuando quiera editar, cambiar la voz, volver a renderizar o republicar uno ya creado con este método.

Compatible con✓Claude Code✓Codex CLI✓Cursor
npx skills add https://github.com/bigzaqui/videolabs/tree/HEAD/skills/videolabs_create-video

Preguntar en tu IA favorita

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

Documentación

videolabs: crear un video (estilo curso en terminal)

Todo reside en un repositorio, /opt/opus/videolabs (github.com/bigzaqui/videolabs, público: nunca subir credenciales; ejecuta scripts/check-secrets.sh antes de hacer commit). El código de este skill está en skills/videolabs_create-video/ de ese repositorio, enlazado a ~/.claude/skills por scripts/setup.sh. Sus valores de configuración (claves de ElevenLabs y Cloudflare, el host de renderizado) están en el archivo .env ignorado por git en la raíz del repositorio; consulta .env.example.

El repositorio contiene: un motor genérico (herramientas Remotion + ElevenLabs, un npm ci) y una carpeta por laboratorio bajo labs/. Se ha utilizado para "kafka - in a nutshell" (26 lecciones / 18 min), "ceph - first steps" (14 lecciones / 11.6 min) y, anteriormente, "ceph in a nutshell" (41 lecciones / 30 min; su guión y elementos visuales se mantienen en reference/ como ejemplo de tarjetas, comparación, medidor, árbol, incidencia, estados y sitios). Ambos laboratorios actuales son prácticos: cada comando se ejecutó primero en un laboratorio real (ver "Laboratorio real").

/opt/opus/videolabs/
  engine/                       herramientas genéricas: engine/cli.mjs <lab> <comando>, src/ (Remotion), scripts/
  labs/<slug>/
    lab.config.json             entrada del catálogo (título, descripción, etiquetas) + apariencia (marca, logotipo, barra de estado, nombres de herramientas, corte de muestra)
    script/script.md            narración, una escena por encabezado ###
    visuals.ts                  qué muestra cada escena + anulaciones de señales
    lab/                        create-lab.sh, destroy-lab.sh, cap.sh, captures/, lab-meta.json
    audio/raw/                  salida de ElevenLabs (pagada; comprometida en git)
    out/ data/ public/voiceover/  generado, ignorado por git
  site/  scripts/publish-all.sh  state/    el sitio y su publicación (skill videolabs_publish)

script.md ──cli escenas──▶ data/scenes.json
   └─ cli tts (temporizaciones de palabras de ElevenLabs) ─▶ audio/raw ──pace──▶ public/voiceover/* + data/{durations,captions}.json
visuals.ts + engine/src/cues.ts (revelar = cuando se pronuncia la palabra) ─▶ Remotion ──cli render──▶ out/<slug>.mp4
cli player ─▶ out/player/{chapters.json,lab.json} ──videolabs_publish──▶ videolabs.pages.dev

Todos los comandos: node engine/cli.mjs <lab> <scenes|tts|pace|cues|stills|typecheck|studio|sample|render|player>, además de list y new <slug> (el README.md del repositorio tiene la tabla). La carpeta del laboratorio es lo único que difiere entre cursos; nunca copies código del motor en un laboratorio.

Flujo de trabajo (acordar el esquema y el guión con el usuario; todo lo demás se ejecuta sin preguntar)

Autorización permanente del usuario: una vez acordado el guión, ejecuta el tts pago de ElevenLabs, la auditoría de señales, el renderizado en el host de renderizado, el paso de reproductor/póster y la eliminación del laboratorio sin pedir permiso en cada paso. Informa los resultados y la nueva duración después. Solo detente para preguntar cuando algo esté fuera de este flujo de trabajo (por ejemplo, una acción destructiva sobre algo que el laboratorio no creó, o la publicación en el sitio cuando el usuario no lo ha solicitado).

  1. ¿Tema práctico? Primero construye y ejecuta un laboratorio real (ver "Laboratorio real" más abajo). Nunca escribas la salida de comandos de memoria: el primer borrador del guión de ceph-first-steps tenía errores en diez lugares una vez que se ejecutó en un clúster real. Luego escribe el guión a partir de lo que realmente sucedió.

  2. Primero el guión, luego el video. Propón un esquema (módulos → lecciones, con el diagrama planeado por lección), obtén la aprobación, luego escribe labs/<slug>/script/script.md. Cambiar texto es mucho más barato que regenerar audio. Marca a velocidad 1.0 con pausas de ≈ 160 palabras por minuto: 1.500 palabras ≈ 9.5 min incluyendo tarjetas de módulo; a 1.08 ≈ 168 ppm. Verifica la duración real con la línea Paced … total X min justo después de tts y ajusta antes de renderizar. Para comandos específicos de una versión, verifica el lanzamiento actual (documentación y las descargas/etiquetas de imagen del proyecto) primero. Formato (analizado por engine/scripts/build-scenes.mjs):

    ## Módulo 1 — Arquitectura Central
    ### S02 · RADOS
    **Visual:** notas para el diseñador
    **Comandos:** `ceph -s` `ceph osd tree`        (opcional, solo en pantalla)
    **Narración:**
    Un párrafo, escrito para ser hablado. Deletrea los comandos como se hablan ("ceph dash s").
    

    Termina la última lección de un módulo con "Conclusión del módulo: …" — la palabra conclusión activa la ventana de conclusión.

  3. node engine/cli.mjs new <slug> crea la estructura de labs/<slug>/ (configuración, un guión de tres escenas, un visuals.ts); completa lab.config.json (título, descripción, etiquetas para el catálogo; brand, user, logo, statusbar, statusCmd, networkLabels, session, tools, sample para la apariencia) y ejecuta node engine/cli.mjs <slug> scenes.

  4. Visuales en labs/<slug>/visuals.ts (tipos en engine/src/types.ts, colors de @engine/theme), una entrada por ID de escena. Tipos de diagrama (engine/src/components/Diagrams.tsx):

    • topology: hosts (nombre, IP, chips de daemon, discos/réplicas) en una o dos líneas de red, una línea de estado opcional (etiqueta statusCmd, predeterminado de la configuración) y flows (bloques que se mueven entre hosts, desde cue hasta until). Cualquier host/daemon/disco/estado puede tener un cue (aparece cuando se habla) y changes: [{cue, state, label}] (disco empty|osd|leader|idle|down|destroyed|new, host ok|spare|down|maint). Los elementos sin señal son visibles desde el principio. Construye el estado del laboratorio con ayudantes (host(n), built(n)) para que el diagrama crezca lección por lección.
    • session: una terminal realista; cada cmd se escribe a sí mismo cuando se habla, luego sus líneas out se imprimen; mantiene las filas más nuevas. host establece el prompt completo de una línea (predeterminado session.prompt de la configuración). topology compacto opcional sobre él. La barra de comandos inferior está oculta para las escenas de sesión. Otros tipos: title (tarjeta fastfetch; user, filas extra), layers, cards, flow, tree, compare, gauge, terminal (salida de comandos con anotaciones), incident (síntoma/verificación/acción), checklist, states, sites; takeaway y commandCues opcionales por escena. Usa salida real de lab/captures/ para cada out y symptom.
  5. Revisa los visuales antes de pagar por el audio: node engine/cli.mjs <slug> stills (un fotograma por escena al 60%; S04,S11 --at=12.5 para un momento) y colócalos en mosaicos de 2×2 con ffmpeg xstack para examinarlos. Verifica desbordamiento, ajuste de línea, diseños vacíos.

  6. Voz: archivo .env en la raíz del repositorio con ELEVENLABS_API_KEY / ELEVENLABS_VOICE_ID, luego node engine/cli.mjs <slug> tts (ejecútalo directamente, sin necesidad de aprobación; omite los existentes; --only=S03,S04 --force para rehacer solo las escenas cuya narración cambió, lo que también ahorra créditos). Ejecuta pace automáticamente. Informa al usuario la nueva duración cuando el guión crezca. Haz commit de labs/<slug>/audio/ (salida pagada).

  7. Auditoría de sincronización: node engine/cli.mjs <slug> cues (después de cualquier cambio en un comando, cambia también su entrada commandCues: una etiqueta para dmesg que aún está señalada en "smartctl" es una discrepancia silenciosa) — lista la palabra que revela cada elemento y cada línea de comando (MISS = propagación entre vecinos). Corrige coincidencias incorrectas con anulaciones de señales (a continuación). Vuelve a ejecutar después de cualquier cambio de narración o voz.

  8. Vista previa: node engine/cli.mjs <slug> sample (≈1 min en el servidor de renderizado), verifica fotogramas con ffmpeg -ss <t> -frames:v 1, volumedetect para niveles (sin recorte: máximo < -1 dB).

  9. Renderiza el curso completo (remoto, a continuación), luego node engine/cli.mjs <slug> player, crea el póster labs/<slug>/out/spot.jpg, destruye las VMs del laboratorio y publica todo el sitio con el skill videolabs_publish.

Reglas de diseño aprobadas por el usuario

  • Apariencia: escritorio Omarchy/Hyprland, paleta Tokyo Night (src/theme.ts; nombres semánticos como rados, mon, cephfs son solo ranuras de paleta). Barra Waybar con módulos como espacios de trabajo 0–7, un estado (HEALTH_WARN en el módulo de solución de problemas), contador de lección 07/41. Una ventana en mosaico, borde cuadrado de 2px, sin radio/brillo/gradiente. Cada lección se abre con un prompt robbyrussell escrito ➜ module-1-core-architecture git:(s03) cat the-daemons.md, luego un # Heading. Las notas se renderizan como // comments, los comandos como líneas ❯ cmd, las conclusiones como una ventana flotante cat sre-takeaway.md, los divisores de módulo como cd module-N-… + título enorme, el abridor como fastfetch. Todo JetBrains Mono; Inter solo para subtítulos. Reemplazó una apariencia "genérica" de degradado oscuro que al usuario no le gustaba.
  • Sincronización de revelación: nunca escalonar por tiempo. Cada elemento aparece cuando el narrador dice su nombre por primera vez (engine/src/cues.ts: el token significativo más largo, con prefijo, buscado hacia adelante desde la señal anterior). Anulaciones en visuals.ts del laboratorio:
    • cue: "=managers" palabra hablada exacta (se ignoran guiones/puntuación: "=write-ahead"), cue: null = sin palabra, interpolar.
    • pointCues (lados de comparación), noteCue (árbol), linkCue (sitios), cues (insignias de título), commandCues (líneas de comando, por escena).
    • El orden de las etiquetas en cueLabels() debe coincidir con el orden de índice que usa el renderizador en engine/src/components/Visuals.tsx (topología/sesión usan recorrido con clave en topologyKeys/sessionKeys).
    • Un cursor hacia adelante recorre todas las etiquetas de una escena, por lo que una etiqueta posterior que reutilice una palabra común ("tres", "recuperación", "añadir") puede aterrizar en una aparición posterior. Prefiere una palabra que aparezca una vez cerca del momento ("=repetir", "=comienza"), y siempre lee la auditoría de cues.
  • Subtítulos: frases simples, divididas al final de oración/cláusula, una línea ≤ 64 caracteres, sin resaltado de karaoke por palabra ("agotador").
  • Respiración: engine/scripts/pace.mjs inserta 250 ms después de . ? ! y 120 ms después de : ; en el audio sin procesar de ElevenLabs y cambia las temporizaciones de palabras. Los puntos de corte provienen de ffmpeg silencedetect (estricto -38 dB/40 ms, luego -30 dB/20 ms), de lo contrario, la ventana más silenciosa de 5 ms, con fundidos de 8 ms — nunca solo de los límites de palabras de ElevenLabs (estos aterrizan dentro de las palabras). Ajusta SENTENCE_PAUSE/CLAUSE_PAUSE, vuelve a ejecutar node engine/cli.mjs <slug> pace (gratuito; el audio sin procesar se conserva en audio/raw/). El ritmo depende de la versión de ffmpeg (7.1.5 y 8.0.1 dieron 17.9 y 17.8 min para el mismo audio de Kafka): ajusta el ritmo donde renderices, o copia data/ y public/voiceover/ al host de renderizado.
  • Transiciones: fundidos cruzados de 15 fotogramas a través de TransitionSeries de @remotion/transitions; se superponen solo en la entrada silenciosa (0.4 s) / cola (0.8 s). engine/src/timeline.ts resta las superposiciones; engine/scripts/player.mjs y stills.mjs reflejan esas constantes — mantenlas sincronizadas.
  • Voz elegida por el usuario: ElevenLabs "Mark" v3p1kjzUvro6S76qmYmH (Biblioteca de Voces; funciona para TTS aunque GET /v1/voices/{id} devuelve 404), eleven_multilingual_v2, velocidad 1.08, punto final with-timestamps, previous_text/next_text para continuidad. Siempre verifica un ID de voz con una llamada TTS de 1 oración antes de una ejecución completa (un ID anterior en .env estaba truncado).

Configuraciones por curso (labs//lab.config.json)

Los espacios de trabajo en la Waybar y los recuentos de módulo/lección de fastfetch siguen las escenas automáticamente. Todo lo demás específico del curso son datos en la configuración, nunca código: brand (nombre de Waybar), user (prompt en la tarjeta de título), logo (cinco líneas de letras en bloque), statusbar (texto ok/warn, texto right, warnModule = el módulo que muestra la advertencia), statusCmd y networkLabels (valores predeterminados de las escenas de topología), session.prompt/session.title (sesiones de terminal), tools (palabras de comando que se omiten al señalar automáticamente una línea de comando), sample (IDs de escena del corte de Muestra), además de title, description, tags para el catálogo. Un nuevo laboratorio con una apariencia diferente no necesita cambios en el motor; un nuevo tipo de diagrama o estado sí (engine/src/types.ts, components, cues.ts) y luego beneficia a todos los laboratorios.

Prácticas de Remotion aplicadas (de los skills remotion-*)

Fuentes a través de @remotion/google-fonts (nunca confíes en las fuentes del sistema); <Audio> de @remotion/media con from; TransitionSeries; calculateMetadata dimensiona la composición a partir de las duraciones de audio; anima con interpolate/Easing.bezier y translate (sin transiciones CSS). No adoptado a propósito: Interactive.create/composiciones conectadas (solo útil para edición manual en Studio), mínimos de texto grande para video social, efectos de sonido de meme.

Renderizado

  • VIDEOLABS_RENDER_HOST (destino SSH del servidor de renderizado y laboratorio, claves ya configuradas) proviene del archivo .env en la raíz del repositorio: ejecuta set -a; . /opt/opus/videolabs/.env; set +a antes de los siguientes comandos, y escribe esa variable, no una dirección literal, en cualquier cosa que hagas commit. El mismo host ejecuta las VMs del laboratorio.

  • TMPDIR=/var/tmp es establecido por el CLI: /tmp es un tmpfs de RAM pequeño en estos hosts y una mezcla de audio larga falla con "No space left on device" al final.

  • Máquina local (4 vCPU) ≈ 45 min por 30 min de video. Servidor de renderizado remoto $VIDEOLABS_RENDER_HOST (Ubuntu 26.04, 24 hilos, 31 GB RAM, RTX 3090 Ti, KVM; Node 22, ffmpeg, rsync y las librerías de Chromium de apt) renderiza el curso de ceph de 11.6 min en 3 min y el curso de Kafka de 18 min en 5 min con concurrencia 12–16. El repositorio reside en /opt/opus/videolabs en ambas máquinas. Flujo:

    rsync -az --delete --exclude node_modules --exclude out --exclude .env --exclude .git --exclude site --exclude state ./ $VIDEOLABS_RENDER_HOST:/opt/opus/videolabs/   # desde /opt/opus/videolabs
    ssh $VIDEOLABS_RENDER_HOST 'cd /opt/opus/videolabs/engine && npm ci && cd .. && node engine/cli.mjs <slug> scenes && node engine/cli.mjs <slug> pace && node engine/cli.mjs <slug> render --concurrency=12 --log=error'
    rsync -a $VIDEOLABS_RENDER_HOST:/opt/opus/videolabs/labs/<slug>/out/<slug>.mp4 labs/<slug>/out/ && node engine/cli.mjs <slug> player
    

    (Para mantener el tiempo idéntico al que revisaste, sincroniza labs/<slug>/data y public/voiceover con rsync en lugar de ejecutar scenes/pace en el servidor). Nunca copies .env allí. Ejecuta renderizados largos en segundo plano (nohup bash script & iniciado con ssh -f -n) y espera con un Monitor en la línea real de time; no busques en el registro "error" (la línea de comando tiene --log=error). Si el código de Waybar/diagrama cambia, verifica ambos laboratorios (stills), ya que lo comparten.

  • Verificar un cambio en el motor: renderiza el corte de Muestra con el código antiguo y el nuevo y compara con ffmpeg -i a.mp4 -i b.mp4 -lavfi ssim -f null -; los archivos scenes/durations/captions.json regenerados y la auditoría de cues deben coincidir con la salida (el único parámetro que cambia la duración de la voz es si el texto del guión cambió, porque tts ya se ejecutó). Cuando actualices la versión de ElevenLabs (voice id o modelo), vuelve a ejecutar tts para cada laboratorio + cues audit + pace + sample antes de renderizar.

Laboratorio real (solo para temas prácticos)

Usa máquinas virtuales KVM en $VIDEOLABS_RENDER_HOST, creadas con los scripts de la carpeta labs/<slug>/lab/. create-lab.sh prepara las VMs (por ejemplo, para ceph: 1 admin + 3 mon + 3 osd, debian bookworm, ssh -F config). Ejecuta los comandos reales en el laboratorio real, captura la salida y las capturas de pantalla a captures/; luego intégralos en la sesión y otros visuales. El laboratorio se ejecuta en el mismo servidor que el renderizado; el CLI puede invocarlo, o puedes acceder por SSH manualmente y pegar las salidas en el guión. Los scripts de laboratorio son parte del repositorio y se comprometen como cualquier otro código. Teardown: labs/<slug>/lab/destroy-lab.sh, que se ejecuta después del renderizado o cuando ya no se necesita (nunca dejes VMs en funcionamiento).

Publicación

Hecho por el skill videolabs_publish para publicar el sitio completo (todos los cursos) en Cloudflare Pages. Se necesita autorización del usuario. El sitio incluye un reproductor web (build desde engine/player/) que divide el video en módulos/lecciones, tiene un modo oscuro/claro, reproduce con subtítulos y permite navegar por escena.

Enlaces y referencias

  • Repositorio: [bigzaqui/videolabs] (https://github.com/bigzaqui/videolabs)
  • Motor: Remotion + ElevenLabs
  • Estilo: Omarchy / Tokyo Night terminal
  • Sitio publicado: videolabs.pages.dev (cuando se publique)

Consejos adicionales

  • Siempre verifica que el guión no tenga errores de hecho antes de ejecutar tts (paga). Una vez generado el audio, cambiar el texto requiere regenerar y pagar de nuevo.
  • Para cursos largos, considera dividir el renderizado en partes o usar el servidor remoto para ahorrar tiempo.
  • La sincronización de señales (cues) es crítica para la calidad del video; revísala siempre después de cualquier cambio.
  • El laboratorio real es obligatorio para temas prácticos; no confíes en la memoria para las salidas de comandos.
  • Si el usuario no especifica un tema, pregúntale antes de proceder.
  • Si el usuario quiere editar un video existente, primero verifica si el guión y el audio ya existen; si es así, solo regenera el video con los cambios necesarios.

Solución de problemas

  • Error de espacio en disco durante la mezcla de audio: asegúrate de que TMPDIR=/var/tmp esté establecido.
  • Fallas en tts: verifica que la clave API de ElevenLabs sea válida y que el ID de voz exista. Prueba con una oración primero.
  • Renderizado lento: usa el servidor remoto. Si el servidor remoto no está disponible, reduce la concurrencia.
  • Problemas de sincronización: ejecuta la auditoría de señales (cues) y corrige las anulaciones en visuals.ts.
  • Errores de compilación de TypeScript: ejecuta node engine/cli.mjs <slug> typecheck para verificar los tipos.

Laboratorios existentes

  • kafka-in-a-nutshell (26 lecciones, 18 min)
  • ceph-first-steps (14 lecciones, 11.6 min)
  • ceph-in-a-nutshell (referencia, 41 lecciones, 30 min)

Cada laboratorio tiene su propia configuración, guión y visuales; el motor es compartido.

Licencia

El código del motor y los laboratorios es de código abierto (repositorio público). El audio generado por ElevenLabs es propiedad del usuario (pagado).

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Skills relacionados