Qué vas a tener cuando termines
Una grabación entra, un video limpio con tu estética sale, sin abrir el editor.
- Editar me come el tiempotu última grabación sin silencios ni repeticiones, montada con subtítulos y música según tu estética, verificada y lista para subir. Sin haber abierto el editor.
- Quiero publicar más sin contratar editoruna rutina de un comando y un pedido por video, y de cada grabación hasta cuatro videos publicables (la base y tres variaciones), sin editor.
- Quiero que todos mis videos se vean igualestu `edit-spec.yaml` terminado (tier, color, formato, subtítulos, música, CTA), tres o cuatro capturas de referencia en `frames/`, y el `CLAUDE.md` que obliga a Claude Code a leerlo antes de tocar cualquier video.
Elige la situación que es la tuya. La página te lleva paso a paso solo por esa.
Editar tus videos con IA
Una grabación entra, un video limpio con tu estética sale, sin abrir el editor.
Tipo: paso a paso · Para: dueños de negocio y marcas personales que graban sus propios videos y no quieren vivir dentro del editor · Tiempo: una tarde la primera vez (instalar el kit y escribir tu estética); después, cada video tarda lo que tarda su transcripción.
Cómo usar esta página
Copia esta página entera (el botón «Copiar todo» de arriba) y pégala en un chat nuevo de Claude. Claude va a leer las instrucciones de abajo y te va a guiar paso a paso, una pregunta a la vez, hasta que lo tengas hecho.
INSTRUCCIONES PARA CLAUDE (si eres Claude y te pegaron esta página, síguelas):
- Eres el asistente que Ignacio Giaverini (CreaVinci) preparó para este recurso. Hablas en español neutro, tú.
- Primero pregúntale a la persona en cuál de estas situaciones está: 1) «Editar me come el tiempo», 2) «Quiero publicar más sin contratar editor», 3) «Quiero que todos mis videos se vean iguales». Una sola pregunta.
- Después sigue el paso a paso de ESE ángulo, un paso por mensaje, y no avances hasta que te confirme que lo hizo.
- Cuando un paso use una pieza (un prompt, una skill, una plantilla), se la das completa, lista para copiar.
- Cortar, transcribir y montar video corre en Claude Code, en el computador de la persona. Desde la app de Claude, tú guías y le dices qué pegar en Claude Code; desde Claude Code, ejecuta tú mismo los pasos siguiendo la Pieza B.
- Si algo falla, ve a «Errores conocidos» antes de improvisar.
- No inventes cifras ni casos. No prometas lo que la página no dice.
- Al terminar, dile qué tiene hecho y cuál es el siguiente paso natural (está al final de la página).
Para quién es, y cuándo no
- Es para ti si grabas videos hablando a cámara o a una pantalla, tienes Claude Code o puedes instalarlo, y hoy editar te cuesta horas o hace que no publiques.
- Es para ti también si vendes contenido o edición a otros: las piezas se instalan en la carpeta de cada cliente con su estética, en una sesión que se cobra.
- No es para ti (todavía) si no tienes ninguna grabación → entonces parte por grabar un video de un minuto, tú a cámara, en un solo plano, leyendo un guion corto. Con ese archivo vuelves aquí.
- No es para ti (todavía) si tus videos son conversaciones de dos personas o improvisación larga sin guion → el corta-silencios sí te sirve (Ángulo 1, paso 4), pero el corte por guion no: no sabe qué guardar de una improvisación.
Ángulo 1 · «Editar me come el tiempo»
Qué vas a tener al final: tu última grabación sin silencios ni repeticiones, montada con subtítulos y música según tu estética, verificada y lista para subir. Sin haber abierto el editor.
- Instala el kit una sola vez. Abre Claude Code en una carpeta nueva (por ejemplo
mi-kit-de-edicion) y pégale el prompt de instalación de la Pieza B seguido de los archivos que ese prompt pide: el script de la Pieza C, elCLAUDE.mdde la Pieza B, eledit-spec.yamlde la Pieza A y las skills de las Piezas D y E (con suvariaciones.yaml). Claude Code instala ffmpeg y Whisper, crea las carpetas y guarda cada archivo en su lugar. No avances hasta que te diga «kit listo». - Copia la grabación a
entrada/. Al disco, no leída desde una carpeta compartida, de red ni del teléfono: desde ahí es hasta un 45 por ciento más lento. - Decide el cortador. ¿Grabaste leyendo un guion, repitiendo frases? Va el corte por guion (Pieza D): guarda el guion como
entrada/mi-grabacion.txt(el mismo nombre que el video), en el orden en que lo grabaste. ¿Grabaste de corrido, sin guion escrito? Va el corta-silencios (Pieza C). - Córrelo. Sin guion:
python corta-silencios.py entrada/mi-grabacion.mp4 --salida salida/mi-grabacion.limpia.mp4. Con guion:/corte-por-guion entrada/mi-grabacion.mp4 entrada/mi-grabacion.txt. Los dos dejansalida/mi-grabacion.limpia.mp4y te dicen cuántos segundos quitaron. - Revisa la lista de cortes, no el video entero. En
salida/mi-grabacion.limpia.cortes.jsoncada corte trae la última palabra antes y la primera después: si en tres cortes al azar las dos son palabras enteras, el cruce funcionó. Si suena atropellado o cortó de más, ajusta con la tabla de parámetros de la Pieza C y vuelve a correr. - Tu estética, versión de hoy. Si todavía no tienes
edit-spec.yaml, usa el de la Pieza A tal como viene y cambia solotier(sobria o llamativa) ypaleta_acento(un color, el tuyo). El resto ya está medido. Afinarlo entero es el Ángulo 3. - Pide el montaje. Deja en
frames/tres o cuatro capturas de un video cuya estética quieras (tuyo o de otro; una captura de pantalla desde el celular sirve) y pégale a Claude Code el pedido de edición de la Pieza A. Antes de editar te va a decir en cinco líneas qué decisiones tomó que el spec no cubría. - Verifica antes de subir. Claude Code tiene que reportar el ancho y el alto reales del archivo (1080 por 1920) y que abre sin error. Después míralo tú en el celular, no en el computador: ahí se ve el video.
Cómo sabes que quedó bien: la duración bajó (12 minutos con pausas y repeticiones quedan en unos 4), ninguna palabra quedó cortada, el archivo es 1080 por 1920 también por dentro, abre en el celular, y tienes cinco líneas de decisiones para tu spec.
Ángulo 2 · «Quiero publicar más sin contratar editor»
Qué vas a tener al final: una rutina de un comando y un pedido por video, y de cada grabación hasta cuatro videos publicables (la base y tres variaciones), sin editor.
- Ten el kit instalado y el spec escrito. Si no, haz el Ángulo 1 con un solo video: esa es la instalación. Lo que sigue da por hecho que
CLAUDE.md(Pieza B),corta-silencios.py(Pieza C),edit-spec.yaml(Pieza A) y las skills (Piezas D y E) ya están en la carpeta. - Graba en tandas, leyendo. Escribe los guiones de la semana, graba uno tras otro en la misma sesión (misma luz, mismo encuadre) y guarda cada video con su guion al lado, con el mismo nombre:
entrada/lunes.mp4conentrada/lunes.txt. Repite cada frase las veces que quieras: la skill se queda con la última toma completa. - Procesa la tanda con un solo pedido. Pégale a Claude Code el pedido por lotes de la Pieza B: por cada video de
entrada/, si tiene guion corre la Pieza D, si no corre la Pieza C, y después monta con el spec. Al final te entrega una tabla por video: duración antes y después, frases no encontradas, decisiones no cubiertas. - Lee la tabla, no los videos. Abre solo los que tengan frases no encontradas o una duración rara. Las frases no encontradas se regraban, nunca se inventan con otro corte.
- Multiplica con variaciones. Por cada montaje que quieras probar, escribe
variaciones.yaml(Pieza E) apuntando asalida/lunes.montaje.mp4, con hasta tres variaciones de una sola variable cada una, y pide/variaciones. Salen tres archivos más junto a la base, uno por variación. - Publica con método. La base y una variación con dos días de diferencia, misma hora, mismo caption. A los tres días compara conversaciones, no vistas. La que gana pasa a ser la base; la semana siguiente pruebas otra variable.
- Alimenta el spec. Cada tanda deja decisiones que el spec no cubría: se pegan en
edit-spec.yaml. Cuando una tanda entera sale con cero decisiones nuevas, el spec está terminado.
Cómo sabes que quedó bien: de una tarde de grabación salen los videos de la semana y cada uno te costó un comando y un pedido. Si un video te cuesta más que su transcripción, algo está mal: ve a «Errores conocidos».
Ángulo 3 · «Quiero que todos mis videos se vean iguales»
Qué vas a tener al final: tu edit-spec.yaml terminado (tier, color, formato, subtítulos, música, CTA), tres o cuatro capturas de referencia en frames/, y el CLAUDE.md que obliga a Claude Code a leerlo antes de tocar cualquier video.
- Elige el tier y no lo mezcles. Sobria o llamativa (la tabla está en la Pieza A). No es gusto: la estética decide a quién atraes.
- Elige el formato. En la Pieza F hay seis specs, uno por tipo de video. Si dudas entre dos, pídele a Claude Code un preview de diez segundos de tu video con cada uno y míralos en el celular.
- Arma el archivo. Si no tienes el kit, el paso 1 del Ángulo 1 lo instala. Copia el
edit-spec.yamlbase (Pieza A) a la raíz de tu carpeta y el archivo del formato elegido aspecs/. En la base cambiatier,paleta_acento(un color, el mismo en todos tus videos) yambiente. En el del formato, colores y tipografía. - Deja capturas. Tres o cuatro fotogramas de videos cuya estética quieres, en
frames/, y sus rutas enreferencia_visual. El spec describe; la captura muestra. - No toques las cinco reglas que ya vienen puestas (están en la Pieza A, debajo del archivo).
- Edita un video real y cierra el spec usándolo. Pide el montaje (el pedido de la Pieza A, con el
CLAUDE.mdde la Pieza B ya en la carpeta) y pega en el archivo las decisiones que Claude Code tomó solo. Repite con un segundo video. Cuando el reporte diga cero decisiones no cubiertas, el spec está terminado. - Cuando quieras cambiar algo de la estética, no lo cambies a mano en el próximo video: pruébalo como variación (Pieza E), una sola variable, contra la base. Si gana, entra al spec para todos.
Cómo sabes que quedó bien: dos videos tuyos editados con una semana de diferencia se ven de la misma marca; una persona nueva produce igual desde el primer día con solo la carpeta; y edit-spec.yaml tiene al menos cinco líneas que no estaban en la plantilla, escritas por ti.
Las piezas
Pieza A · edit-spec.yaml y el pedido de edición (plantilla + prompt)
Tu estética en parámetros: el archivo que hace que la IA edite tu video con tu estilo y no con el suyo. Se escribe una vez, se le pasa a Claude Code con cada video, y crece con las decisiones que la IA tomó sola. La usan los tres ángulos.
Primero, el tier. Sobria o llamativa. No hay tercera y no se combinan.
| Tier | A quién atrae | Cómo se ve |
|---|---|---|
| sobria | Dueños de negocio, B2B, capacidad de pago alta | Luz natural, grading neutro, un color de acento |
| llamativa | Principiantes, B2C, capacidad de pago baja | Saturación, texto grande, muchos efectos |
Los valores que vienen puestos no son preferencias: salen de mirar cientos de videos que funcionaron, cuadro por cuadro. Cambia lo que no sea tuyo y deja el resto. El color de acento viene con uno de ejemplo: ponle el tuyo. Guárdalo como edit-spec.yaml (si el bloc de notas te lo guarda como .txt, elige «Todos los archivos» en el tipo antes de guardar).
# edit-spec.yaml : tu estética, en parámetros
# Llénalo una vez. Pásaselo a Claude Code junto con el video crudo y, si puedes,
# una carpeta frames/ con 3 o 4 capturas de referencia.
edit_spec:
version: 1
duracion_objetivo_s: 60
aspect: "9:16"
fps: 30 # 24 si la máquina no da. 60 no
resolucion: [1080, 1920]
estetica:
tier: sobria # sobria (B2B) | llamativa (B2C). NO se mezclan
ambiente: interior_luz_natural
grading: neutro
paleta_acento: "#C9A840" # de ejemplo. UN color, el tuyo, el mismo en TODOS tus videos
referencia_visual: [] # rutas a frames/ : el spec describe, la captura muestra
subtitulos:
corrido:
fuente: sans
peso: bold
color: "#FFFFFF"
contorno: fino # none | fino | negro_grueso
caja: none
posicion: tercio_inferior
palabras_por_frase: [2, 4]
max_lineas: 2
achicar_si_desborda: false # parte en 2 líneas, NUNCA achica
titular:
activo: false
fijo_todo_el_video: false # en listados: true (captura al que entra a mitad)
color_texto: "#FFFFFF"
peso: bold
posicion: tercio_superior
palabra_en_acento: true # UNA sola palabra por titular
cta:
doble_capa: true
solo_una_vez: true # REGLA DURA
mayusculas: true
escala_vs_corrido: 3.0
layout:
modo: talking_head # talking_head | split_horizontal | pip | mosaico | texto_sobre_video | toma_unica
cortes: auto
zooms: 0 # el zoom corporal le gana al digital
pip_ancho: 0.22 # con la cara al 20-25%, tu fondo deja de importar
screen_recordings:
acelerar: false # el ritmo real se lee como real
limpiar_escritorio: false # el desorden compra credibilidad
audio:
musica:
arco: [vacio, construccion, llegada]
llegada_en_s: null # EXACTO sobre la frase de impacto (~70% del video)
nivel: bajo_la_voz
prohibido:
- "upbeat corporate"
- "inspiring background"
silencio_antes_del_cta_s: 0.6 # crescendo invertido
cortar_pausas: true
foley_accion_continua: true
cta:
tipo: pregunta_cuantificable # convierte mucho Y devuelve un dato por persona
texto: null
entrega: inmediata
destino: dm_automatico
Las cinco reglas que ya vienen puestas (no se tocan):
- La doble capa de subtítulo se usa una sola vez por video, sobre el CTA. Si todo está destacado, nada lo está.
- La música llega exacto sobre la frase de impacto. Ni antes ni después: es lo que convierte un momento en el momento.
- Silencio en seco justo antes del CTA. Crescendo invertido: lo que viene después del silencio pega tres veces más fuerte.
- El subtítulo nunca se achica. Si no entra, parte en dos líneas.
- Las grabaciones de pantalla no se aceleran ni se limpian. El desorden real compra credibilidad; un escritorio impecable se lee como demo.
El anti-patrón con nombre: música tipo «upbeat corporate» o «inspiring background» (ukelele, pianito de cuatro notas en loop): suena a sala de espera de dentista y el sistema nervioso la manda al fondo en tres segundos. Por eso está en la lista de prohibidos.
El pedido de edición (se pega en Claude Code con el video limpio ya en salida/; cambia mi-grabacion por el nombre de tu video):
Edita salida/mi-grabacion.limpia.mp4 siguiendo edit-spec.yaml al pie de la letra (y
specs/<formato>.yaml si existe, que hereda de la base). Las capturas de referencia están
en frames/. Deja el resultado como salida/mi-grabacion.montaje.mp4.
Antes de editar, dime en cinco líneas qué decisiones tomaste que el spec NO cubría,
para que yo las agregue al archivo.
Si algo del spec choca con el material, dímelo en vez de resolverlo por tu cuenta.
Al terminar, reporta: duración final, ancho y alto reales del archivo según ffprobe,
y confirma que abre. No me digas "listo" sin esa verificación.
Pieza B · Kit de edición: prompt de instalación, CLAUDE.md y pedido por lotes (paso a paso)
No hay nada que descargar: con este prompt Claude Code instala lo que falta, crea las carpetas y guarda cada pieza donde va. El CLAUDE.md son las reglas para que use el kit sin que se lo expliques cada vez. Todo corre en tu computador; el video no sale de ahí. La usan los tres ángulos.
El prompt de instalación (pégalo en Claude Code, abierto en una carpeta nueva, seguido de los archivos que nombra el paso 5: Piezas C, B, A, D y E):
Estamos armando mi kit de edición de video en esta carpeta. Hazlo en este orden y no avances
hasta que cada paso funcione de verdad:
1. Comprueba si ffmpeg y ffprobe están instalados (ffmpeg -version). Si no, instálalos con el
gestor de paquetes de mi sistema (winget en Windows, brew en Mac, apt en Linux) y vuelve a
comprobar. Si en Windows no quedan en el PATH, agrégalos y abre una terminal nueva.
2. Comprueba Python 3.10 o superior. Si no está, instálalo igual.
3. Instala Whisper: pip install -U openai-whisper. Si falla por torch, instala torch primero
(pip install torch) y repite.
4. Crea las carpetas: entrada/, salida/, frames/, specs/, skills/corte-por-guion/,
skills/variaciones/.
5. Guarda como corta-silencios.py, en la raíz, el script que te pego abajo, sin cambiarle nada.
Guarda CLAUDE.md, edit-spec.yaml, variaciones.yaml y los dos SKILL.md, cada uno en su ruta.
6. Prueba con el video que dejé en entrada/: python corta-silencios.py entrada/<video> --solo-lista
La primera vez descarga el modelo de Whisper; espera, es una sola vez. Dime cuántos
silencios detectó, cuántas palabras transcribió y cuántos segundos quitaría.
Cuando todo eso funcione, escribe "kit listo" y dime el comando exacto que corro para cada
video nuevo, con y sin guion.
CLAUDE.md (va en la raíz de la carpeta; Claude Code lo lee solo):
# Kit de edición · reglas para Claude Code
Esta carpeta edita videos. Antes de tocar cualquier video lee edit-spec.yaml: ahí está la
estética. Lo que el spec no diga, lo preguntas o lo anotas como "decisión no cubierta";
nunca lo decides en silencio. Si un spec de specs/ tiene la línea "hereda:", carga primero
ese archivo base y aplica encima lo del formato (lo del formato manda).
## El flujo para cada video (entrada/<nombre>.mp4)
1. Si el video viene de una carpeta de red, compartida o del teléfono, cópialo a entrada/
antes de procesarlo. Leer desde ahí es hasta un 45 % más lento.
2. Si existe entrada/<nombre>.txt, corre la skill corte-por-guion. Si no, corre
python corta-silencios.py entrada/<nombre>.mp4 --salida salida/<nombre>.limpia.mp4
Nunca los dos sobre el mismo archivo. Los dos dejan salida/<nombre>.limpia.mp4.
3. Montaje según edit-spec.yaml, con ffmpeg, desde salida/<nombre>.limpia.mp4 hacia
salida/<nombre>.montaje.mp4:
- Subtítulos a partir de entrada/<nombre>.palabras.json (la transcripción con tiempos que
deja corta-silencios.py): agrupa según palabras_por_frase, máximo
max_lineas; si una frase desborda se parte en dos líneas, nunca se achica la fuente.
- Música: arco vacío / construcción / llegada, con la llegada exacta sobre la frase de
impacto (si llegada_en_s es null, pregunta cuál es la frase de impacto antes de
renderizar; suele estar cerca del 70 % del video). Nivel bajo la voz. Silencio en
seco de silencio_antes_del_cta_s antes del CTA.
- CTA: doble capa una sola vez, sobre el CTA, en mayúsculas, a escala_vs_corrido.
- Cortes secos en el mismo cuadro. Sin fundidos ni deslizamientos que muestren la toma
de atrás. Sonido de transición solo donde se ve un cambio.
- Deja al lado salida/<nombre>.montaje.sin-subs.mp4 y salida/<nombre>.voz.wav: la skill
variaciones parte de ahí.
4. Verifica la salida con ffprobe antes de decir "listo": ancho y alto reales iguales a
resolucion del spec, duración esperada, y que abre. Un MP4 puede declarar 1080x1920 y
por dentro ser un 540 estirado; y uno a medio escribir pesa bien y no abre.
5. Reporta en una tabla: video, duración antes y después, cortes aplicados, frases no
encontradas (si hubo guion), y las decisiones que tomaste que el spec no cubría.
## Reglas duras
- Nunca cortar a mitad de palabra: los límites salen de los tiempos por palabra.
- No aplicar filtros de ruido ni compuertas: rompen la voz. Si sobra ruido, sobra tiempo:
corta más.
- No acelerar ni limpiar grabaciones de pantalla.
- No renderizar para averiguar: decide con un preview corto (10 s alrededor de la frase de
impacto, a la mitad de la resolución) y renderiza entero una sola vez, al final, siempre
desde el original o desde el corte limpio, nunca desde un archivo recodificado dos veces.
- Música prohibida: "upbeat corporate", "inspiring background". La música se elige por la
emoción del video, no por la que había a mano.
- Si algo del spec choca con el material, decirlo, no resolverlo por tu cuenta.
- Hablar en español neutro (tú). Sin rayas largas.
El pedido por lotes (para procesar toda una tanda de grabaciones de una vez):
Procesa todo lo que hay en entrada/ siguiendo CLAUDE.md:
- Por cada video, si existe un .txt con el mismo nombre, usa corte-por-guion con ese guion;
si no, usa corta-silencios.py.
- Después monta cada uno con edit-spec.yaml (y specs/<formato>.yaml si existe) y déjalo
como salida/<nombre>.montaje.mp4.
- No me preguntes video por video: junta las preguntas y hazlas todas al final.
- Al terminar dame la tabla de CLAUDE.md (video, duración antes y después, cortes, frases no
encontradas, decisiones no cubiertas) y verifica cada archivo con ffprobe antes de incluirlo.
Pieza C · corta-silencios.py (script)
Detecta los silencios con ffmpeg (tramos por debajo de -35 dB que duren más de 0,35 s), transcribe con Whisper con tiempos por palabra y cruza las dos listas: un silencio se corta solo si cae completo entre el fin de una palabra y el inicio de la siguiente (un «eh» bajito adentro lo salva), con 0,12 s de margen a cada lado para que no suene apretado. Corte seco, misma resolución que la entrada. Lo usan los Ángulos 1 y 2.
#!/usr/bin/env python3
# corta-silencios.py : entra un video, sale sin silencios, más la lista de cortes. Solo corta
# un silencio si cae completo entre el fin de una palabra y el inicio de la siguiente.
# Uso:
# python corta-silencios.py entrada.mp4 --salida limpia.mp4 --umbral -35 --minimo 0.35 --margen 0.12
# python corta-silencios.py entrada.mp4 --solo-lista (escribe lista y transcripción, no renderiza)
# Deja: limpia.mp4, limpia.cortes.json (cada corte con la última palabra antes y la primera
# después) y entrada.palabras.json (la transcripción con tiempos, que reutilizan las skills).
import argparse, json, os, re, shutil, subprocess, sys, tempfile
def correr(cmd):
return subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", errors="replace")
def duracion(ruta):
r = correr(["ffprobe", "-v", "error", "-show_entries", "format=duration",
"-of", "default=noprint_wrappers=1:nokey=1", ruta])
return float(r.stdout.strip())
def resolucion(ruta):
r = correr(["ffprobe", "-v", "error", "-select_streams", "v:0",
"-show_entries", "stream=width,height,codec_name", "-of", "csv=p=0", ruta])
return r.stdout.strip()
def detectar_silencios(ruta, umbral, minimo):
r = correr(["ffmpeg", "-hide_banner", "-i", ruta,
"-af", f"silencedetect=noise={umbral}dB:d={minimo}", "-f", "null", "-"])
inicios = [float(x) for x in re.findall(r"silence_start:\s*([0-9.]+)", r.stderr)]
fines = [float(x) for x in re.findall(r"silence_end:\s*([0-9.]+)", r.stderr)]
silencios = list(zip(inicios, fines))
if len(inicios) > len(fines): # silencio abierto hasta el final del archivo
silencios.append((inicios[-1], duracion(ruta)))
return silencios
def transcribir(ruta, modelo, idioma):
import whisper
res = whisper.load_model(modelo).transcribe(ruta, language=idioma, word_timestamps=True, verbose=False)
return [{"texto": w["word"].strip(), "inicio": round(float(w["start"]), 3), "fin": round(float(w["end"]), 3)}
for seg in res["segments"] for w in seg.get("words", [])]
def cruzar(silencios, palabras, margen):
"""Un silencio se corta solo si, ya con margen, no tiene ninguna palabra adentro."""
cortes = []
for ini, fin in silencios:
a, b = ini + margen, fin - margen
if b <= a or any(p["fin"] > a and p["inicio"] < b for p in palabras):
continue # hay una palabra (o un "eh") adentro: no se toca
antes = [p for p in palabras if p["fin"] <= a]
despues = [p for p in palabras if p["inicio"] >= b]
cortes.append({"inicio": round(a, 3), "fin": round(b, 3), "dura": round(b - a, 3),
"ultima_palabra_antes": antes[-1]["texto"] if antes else "",
"primera_palabra_despues": despues[0]["texto"] if despues else ""})
return cortes
def tramos_a_conservar(cortes, total):
tramos, t = [], 0.0
for c in cortes:
if c["inicio"] > t:
tramos.append((t, c["inicio"]))
t = max(t, c["fin"])
if t < total:
tramos.append((t, total))
return tramos
def cortar(entrada, salida, tramos):
# Corte seco: select/aselect con los tramos a conservar, un solo render desde el original.
sel = "+".join(f"between(t,{a:.3f},{b:.3f})" for a, b in tramos)
carpeta = tempfile.mkdtemp()
rv, ra = os.path.join(carpeta, "v.txt"), os.path.join(carpeta, "a.txt")
open(rv, "w", encoding="utf-8").write(f"select='{sel}',setpts=N/FRAME_RATE/TB")
open(ra, "w", encoding="utf-8").write(f"aselect='{sel}',asetpts=N/SR/TB")
r = correr(["ffmpeg", "-hide_banner", "-y", "-i", entrada,
"-filter_script:v", rv, "-filter_script:a", ra,
"-c:v", "libx264", "-preset", "medium", "-crf", "18", "-pix_fmt", "yuv420p",
"-c:a", "aac", "-b:a", "192k", "-movflags", "+faststart", salida])
shutil.rmtree(carpeta, ignore_errors=True)
if r.returncode != 0:
print(r.stderr[-3000:])
sys.exit("ffmpeg falló al cortar; el mensaje de arriba dice por qué")
def main():
ap = argparse.ArgumentParser(description="Corta silencios sin cortar palabras.")
ap.add_argument("entrada")
ap.add_argument("--salida", default=None)
ap.add_argument("--umbral", type=float, default=-35, help="dB; súbelo si hay ruido de fondo")
ap.add_argument("--minimo", type=float, default=0.35, help="s; súbelo si corta pausas expresivas")
ap.add_argument("--margen", type=float, default=0.12, help="s; súbelo si suena atropellado")
ap.add_argument("--modelo", default="small", help="whisper: tiny | base | small | medium")
ap.add_argument("--idioma", default="es")
ap.add_argument("--solo-lista", action="store_true", help="escribe cortes y palabras, no renderiza")
a = ap.parse_args()
for h in ("ffmpeg", "ffprobe"):
if not shutil.which(h):
sys.exit(f"falta {h} en el PATH: instálalo y abre una terminal nueva")
ent = a.entrada
salida = a.salida or os.path.splitext(ent)[0] + ".limpia.mp4"
os.makedirs(os.path.dirname(os.path.abspath(salida)), exist_ok=True)
total = duracion(ent)
print(f"entrada: {ent} · {total:.1f} s · {resolucion(ent)}")
sil = detectar_silencios(ent, a.umbral, a.minimo)
print(f"silencios detectados (>{a.minimo} s bajo {a.umbral} dB): {len(sil)}")
ruta_palabras = os.path.splitext(ent)[0] + ".palabras.json"
if os.path.exists(ruta_palabras):
pal = json.load(open(ruta_palabras, encoding="utf-8"))
else:
pal = transcribir(ent, a.modelo, a.idioma)
json.dump(pal, open(ruta_palabras, "w", encoding="utf-8"), ensure_ascii=False, indent=1)
print(f"palabras con tiempo: {len(pal)}")
cortes = cruzar(sil, pal, a.margen)
quitado = sum(c["dura"] for c in cortes)
lista = os.path.splitext(salida)[0] + ".cortes.json"
json.dump({"entrada": ent, "duracion_original_s": round(total, 3), "quitado_s": round(quitado, 3),
"duracion_final_s": round(total - quitado, 3), "umbral_db": a.umbral,
"minimo_s": a.minimo, "margen_s": a.margen, "cortes": cortes},
open(lista, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
print(f"cortes que se aplican: {len(cortes)} · se quitan {quitado:.1f} s · lista: {lista}")
if a.solo_lista:
return
if not cortes:
print("no hay nada que cortar con estos parámetros; prueba --umbral -30 o --minimo 0.25")
return
cortar(ent, salida, tramos_a_conservar(cortes, total))
print(f"salida: {salida} · {duracion(salida):.1f} s · {resolucion(salida)}")
print("comprueba que ancho y alto de la salida sean los mismos que los de la entrada")
if __name__ == "__main__":
main()
Los parámetros que cambian el resultado:
| Parámetro | Súbelo si… | Bájalo si… |
|---|---|---|
| Umbral (dB, por defecto -35) | Hay ruido de fondo y no detecta silencios | Corta respiraciones que quieres dejar |
| Mínimo (s, por defecto 0,35) | Corta pausas expresivas | Quedan huecos largos |
| Margen (s, por defecto 0,12) | Suena atropellado | Quedan colas de aire |
Lo que NO resuelve: no elige entre tomas repetidas (para eso está la Pieza D) ni agrega subtítulos, música o b-roll (eso es el montaje con el spec, Pieza A).
Pieza D · corte-por-guion (skill)
Corta el video usando el guion como fuente de verdad: busca cada frase adentro de la grabación, se queda con la toma que la dice entera y mejor, y descarta repeticiones y arranques en falso. Se guarda como skills/corte-por-guion/SKILL.md. La usan los Ángulos 1 y 2.
---
name: corte-por-guion
description: Corta una grabación usando el guion como fuente de verdad. Busca cada frase del guion dentro de la transcripción con tiempos por palabra, se queda con la toma que la dice entera y mejor, descarta repeticiones y arranques en falso, y monta las tomas en el orden del guion con corte seco. Usar cuando el usuario diga "corte por guion", "quédate con la toma buena", "grabé leyendo el guion", escriba /corte-por-guion, o pase un video junto con un guion en .txt.
---
# Corte por guion
Entrada: un video (entrada/grabacion.mp4) y el guion en texto plano con el mismo nombre
(entrada/grabacion.txt), en el orden en que SE GRABÓ, no el orden original si se cambió al
grabar.
Salida: salida/grabacion.limpia.mp4, salida/grabacion.limpia.cortes.json y
salida/grabacion.limpia.informe.md.
## Pasos
1. Tiempos por palabra. Si existe entrada/grabacion.palabras.json (lo deja
corta-silencios.py), úsalo. Si no, genéralo:
python corta-silencios.py entrada/grabacion.mp4 --solo-lista
2. Frases del guion. Parte el .txt en frases: una frase termina en punto, signo de
pregunta o de exclamación. Ignora líneas vacías, títulos y acotaciones entre corchetes.
Numera las frases en orden. Si el guion viene como JSON con cada frase y su segundo
previsto, úsalo tal cual: la alineación mejora.
3. Normaliza los dos lados para comparar: minúsculas, sin tildes, sin puntuación, y los
números escritos como palabras si el guion los trae escritos.
4. Busca cada frase en la transcripción: ventana deslizante sobre las palabras, de largo
entre el 70 % y el 130 % del largo de la frase. Una aparición es una ventana con
similitud >= 0,75 (difflib.SequenceMatcher sobre las listas de palabras normalizadas).
Junta las ventanas solapadas en una sola toma y guarda el inicio de su primera palabra y
el fin de su última.
5. Elige la toma, en este orden y sin saltarte ninguno:
a. La dijo entera: similitud >= 0,9 y sin que falten palabras del final de la frase.
b. Sin muletillas ni corrección a mitad: descarta la toma si adentro aparece "eh", "em",
"o sea", "no no", "perdón", "de nuevo", "otra vez", o una palabra repetida dos veces
seguidas que en el guion va una sola vez.
c. La última de las que quedaron: casi siempre es la mejor, porque ya la ensayó.
Si ninguna pasa (a), toma la de mayor similitud y márcala "menos mala" en el informe.
Si ninguna llega a 0,6, la frase queda NO ENCONTRADA: deja un hueco marcado en el
informe y NO inventes el corte con otra frase parecida.
6. Márgenes: 0,12 s antes de la primera palabra y 0,12 s después de la última, sin invadir
la palabra vecina. Nunca cortes a mitad de palabra: los límites salen de los tiempos por
palabra, no de los silencios.
7. Montaje en el orden del guion, con ffmpeg y corte seco en el mismo cuadro: select y
aselect con between(t,a,b) por toma, setpts=N/FRAME_RATE/TB y asetpts=N/SR/TB, misma
resolución que la entrada, libx264 crf 18, aac 192k, faststart. Si el filtro es largo,
escríbelo en un archivo y pásalo con -filter_script. Si entre dos tomas cambia el
encuadre (el usuario lo dice o se ve en una captura), respeta el cambio: sigue siendo
corte seco, sin fundidos ni deslizamientos que muestren la toma de atrás.
8. Verifica con ffprobe: ancho y alto iguales a la entrada, y duración igual a la suma de las
tomas. Si no coincide, no digas listo: revisa el filtro.
## Formato de salida (el informe)
Una tabla: nº | frase del guion | toma elegida (inicio-fin) | de cuántas tomas | criterio
(entera / última completa / menos mala / NO ENCONTRADA).
Debajo: duración final, la lista de frases no encontradas, y si hay alguna esta línea:
"Mejor graba estas frases que inventar el corte".
## Reglas duras
- Nunca a mitad de palabra.
- No agregues b-roll ni música: eso viene después, con edit-spec.yaml.
- No mejores una toma mala: si las tres veces salieron mal, elige la menos mala y dilo.
- Si el usuario improvisó largo fuera del guion, dilo: esta skill no sabe qué guardar de
eso. Ofrécele corta-silencios.py para ese tramo.
- Dos personas hablando: no aplica. Dilo y para.
- Un solo hablante, a cámara, guion respetado en el orden grabado: ahí funciona bien.
Pieza E · variaciones (skill + plantilla)
Renderiza el mismo montaje en varias versiones cambiando una sola cosa por versión (tipografía, música o saturación), para probar qué estética rinde sin volver a grabar ni a editar. La plantilla se guarda como variaciones.yaml en la raíz y la skill como skills/variaciones/SKILL.md. La usan los Ángulos 2 y 3.
# variaciones.yaml : un montaje, N versiones, UNA variable por versión
base:
spec: edit-spec.yaml # tu estética por defecto
entrada: salida/mi-grabacion.montaje.mp4 # el montaje ya editado con el spec
ya_funciono_organicamente: false # si es true, la skill no varía: este video ya es la base
variaciones:
- nombre: tipo-b
cambia: tipografia
valor: "Space Grotesk"
- nombre: musica-b
cambia: musica
valor: "cama-2.mp3" # elegida por la emoción del video, no por la que tenías a mano
- nombre: saturacion-baja
cambia: saturacion
valor: 0.85
Tres variaciones, tres videos junto a la base: salida/mi-grabacion.montaje.tipo-b.mp4 y los otros dos con su nombre.
---
name: variaciones
description: Renderiza el mismo montaje en N versiones cambiando UNA sola cosa por versión (tipografía, música o saturación) a partir de variaciones.yaml. Usar cuando el usuario diga "variaciones", "versiones", "prueba A/B de estética", escriba /variaciones o pase un variaciones.yaml.
---
# Variaciones
Entrada: variaciones.yaml (base + lista). Salida: un mp4 por variación, junto a la base y
con su nombre más el de la variación: salida/<nombre>.montaje.<variacion>.mp4.
## Antes de renderizar
1. Lee variaciones.yaml. Si una variación tiene más de un campo en "cambia", para y dilo:
una variable por versión, sin excepción. No lo arregles tú: que el usuario elija cuál.
2. Si base.ya_funciono_organicamente es true, no varíes nada: ese video ya es la base. Dile
al usuario que las variaciones se prueban en el siguiente video y para.
3. Comprueba que cada cambio se vea en el celular, porque ahí se ve el video:
- tipografía: la familia nueva tiene que cambiar la forma de las letras a simple vista
(una sans por una serif, una grotesk por una redondeada). Dos sans parecidas no son una
variación: dilo y pide otra.
- saturación: valores entre 0,7 y 1,3; menos de 0,1 de diferencia con la base no se nota.
- música: se elige por la emoción del video. Si el usuario pasó una cama "porque la
tenía a mano", pregúntale qué emoción tiene el video y si esa cama calza. Una cama de
otro cliente suena chillona en el suyo.
4. Preview antes de renderizar: 10 s alrededor de la frase de impacto, a la mitad de la
resolución, por cada variación. El usuario los mira en el celular y aprueba. Recién ahí
renderizas entero. No renderices para averiguar.
## Cómo se aplica cada cambio
Siempre sobre el montaje sin subtítulos y la pista de voz limpia (<nombre>.montaje.sin-subs.mp4
y <nombre>.voz.wav, que el montaje con el spec deja en salida/); nunca sobre un mp4 ya
subtitulado.
- tipografia: vuelve a quemar los subtítulos con la familia nueva y todo lo demás igual al
spec (tamaño, posición, contorno, nunca achicar, partir en 2 líneas).
- musica: mezcla la voz con la cama nueva respetando audio.musica del spec: arco vacío /
construcción / llegada, llegada exacta sobre la frase de impacto, nivel bajo la voz, y el
silencio de silencio_antes_del_cta_s antes del CTA.
- saturacion: filtro eq=saturation=<valor> sobre el video. Nada más.
Todo lo que no cambia se copia idéntico. Misma resolución que la base, libx264 crf 18,
aac 192k, faststart. Verifica cada archivo con ffprobe antes de decir listo.
## Salida
Una tabla: versión | qué cambia | valor | archivo | duración · y debajo la línea
"una variable por versión: cumplido".
## Cómo se prueba (díselo al usuario al terminar)
1. Publica la base y UNA variación con dos días de diferencia, misma hora, mismo caption.
2. A los 3 días compara el número que importa: conversaciones, no vistas.
3. La que gana pasa a ser la base. La siguiente semana, otra variable.
Lo que esta skill NO hace: cambiar el guion ni el gancho. Eso es otro video, no una variación.
Pieza F · Biblioteca de specs: seis formatos (plantilla)
Seis archivos de spec, uno por formato de video: talking head (tú a cámara), pantalla dividida (tú y una pantalla, o un antes y después), mosaico (varios clips a la vez), imagen sobre imagen (tú en chico sobre una pantalla grande), texto sobre video (frases sobre b-roll, sin cara) y toma única (un plano sin cortes). Cada archivo hereda la base de la Pieza A y escribe solo lo que cambia; Claude Code fusiona los dos (lo del formato pisa a la base). Se guardan en specs/. La usa el Ángulo 3. Los colores y la tipografía que vienen puestos son de ejemplo: cambia esos dos por los tuyos y deja el ritmo quieto la primera semana. Los seis van en un solo bloque; cada # specs/... es un archivo aparte.
# specs/talking-head.yaml · tú a cámara explicando
hereda: edit-spec.yaml
colores: { fondo: "#0A0B0D", acento: "#C9A840", texto: "#F4EFE4" } # de ejemplo: pon los tuyos
tipografia: { familia: "Space Grotesk", peso: 600 }
layout:
modo: talking_head
cortes: { cada_s: 4 } # medido; no lo cambies la primera semana
zooms: 0 # el zoom corporal le gana al digital; si no, 1 suave en la frase de impacto
subtitulos:
corrido: { tamano_px: 64, estilo: palabra_a_palabra, posicion: tercio_inferior, resaltar: acento }
audio:
musica: { cama: electronica_suave, nivel_db_bajo_la_voz: -18 }
transiciones: { entre_tomas: seca, entre_bloques: flash_blanco_2_cuadros }
# specs/pantalla-dividida.yaml · tú y una pantalla, o un antes y después
hereda: edit-spec.yaml
layout:
modo: split_horizontal
split: { arriba: camara, abajo: pantalla, proporcion: [0.4, 0.6], audio_de: camara }
cortes: { cada_s: 4 }
subtitulos:
corrido: { posicion: franja_central, tamano_px: 56 } # sobre la unión, sin tapar la pantalla
# specs/mosaico.yaml · varios clips cortos a la vez
hereda: edit-spec.yaml
layout:
modo: mosaico
mosaico: { celdas: 4, dura_cada_una_s: 3, orden: [1, 2, 3, 4], audio_de: 1 } # orden: izquierda a derecha, arriba a abajo
subtitulos:
titular: { activo: true, fijo_todo_el_video: true, posicion: tercio_superior }
corrido: { tamano_px: 56 }
transiciones: { entre_tomas: seca, entre_bloques: flash_blanco_2_cuadros }
# specs/imagen-sobre-imagen.yaml · tú en chico sobre una pantalla grande
hereda: edit-spec.yaml
layout:
modo: pip
pip: { ancho: 0.22, esquina: inferior_derecha, borde_px: 0, sombra: suave } # cara al 20-25 %: el fondo deja de importar
subtitulos:
corrido: { posicion: tercio_inferior, tamano_px: 56 } # nunca encima del recuadro
# specs/texto-sobre-video.yaml · frases sobre b-roll, sin cara
hereda: edit-spec.yaml
layout:
modo: texto_sobre_video
texto: { fuente: sans, peso: bold, tamano_px: 96, animacion_entrada: aparece_en_seco,
dura_cada_frase_s: 3, max_lineas: 3, posicion: centro }
broll_desde: frames/broll/ # tus clips; se cortan cada dura_cada_frase_s
subtitulos:
corrido: { activo: false } # el texto grande ES el subtítulo
audio:
musica: { nivel_db_bajo_la_voz: -12 } # sin voz encima, la música puede subir un poco
# specs/toma-unica.yaml · un plano sin cortes
hereda: edit-spec.yaml
layout: { modo: toma_unica, cortes: none, zooms: 0 }
estetica: { grading: neutro } # solo color, subtítulos y música; nada más se toca
subtitulos:
corrido: { tamano_px: 64, posicion: tercio_inferior }
audio:
cortar_pausas: false # en toma única las pausas son parte del plano
transiciones: { entre_tomas: none, entre_bloques: none }
Lo que NO resuelve: no edita, es el archivo que lee el editor.
Errores conocidos
| Lo que ves | Por qué pasa | Qué haces |
|---|---|---|
| La salida pesa lo que debe pero no abre, o abre negro | MP4 a medio escribir: ffmpeg se cortó antes de cerrar el archivo | ffprobe sobre el archivo: si da error, vuelve a renderizar desde el original, una sola vez, y espera a que termine |
| Dice 1080 por 1920 y se ve borroso en el celular | Por dentro es un 540 estirado: se reescaló en algún paso | Claude Code reporta width,height reales con ffprobe y vuelve a codificar desde el original sin ningún scale |
| El corte tarda muchísimo | Lees el video desde una carpeta de red, compartida o del teléfono: hasta un 45 por ciento más lento | Copia el archivo a entrada/ en el disco y vuelve a correr |
| Cortó a mitad de una palabra | No se usó el cruce con los tiempos por palabra | Mira salida/mi-grabacion.limpia.cortes.json: si los cortes no traen última palabra antes y primera después, el script no corrió entero; vuelve a correrlo |
| Suena atropellado, quedan colas de aire, no detecta silencios o corta pausas que querías dejar | Umbral, mínimo o margen fuera de lugar para tu grabación | Tabla de la Pieza C: --margen de 0,12 a 0,20 si atropella; --umbral de -35 a -30 si no detecta; de -35 a -40 o --minimo más alto si corta de más |
| Sobra ruido de fondo después de cortar | Casi siempre sobra tiempo, no falta filtro | Corta más. Nunca filtros de ruido ni compuertas: rompen la voz |
| La primera corrida se queda quieta varios minutos | Whisper descarga el modelo la primera vez | Espera. Es una sola vez y no sube tu video a ningún lado |
| Corte por guion: frase NO ENCONTRADA o «menos mala» | No la dijiste entera ninguna vez, o las tres tomas salieron mal: la skill no mejora tomas ni inventa cortes | Regraba esa frase (diez segundos) y vuelve a correr |
| Corte por guion: tomas raras o el orden quedó mal | Pasaste el guion original y al grabar cambiaste el orden | Pásale el guion en el orden en que SE GRABÓ |
| Corte por guion: se perdió un tramo bueno | Improvisaste largo fuera del guion: la skill no sabe qué guardar de eso | Ese tramo va por corta-silencios, o lo escribes en el guion y regrabas |
| Claude Code editó «a su gusto» y no como tu spec | No leyó edit-spec.yaml (no está en la raíz o quedó .yaml.txt), o el pedido no lo nombró |
Comprueba el nombre exacto y que CLAUDE.md esté en la raíz; usa el pedido de la Pieza A tal cual |
| Muchas «decisiones no cubiertas» | Normal las dos primeras veces: el spec se termina usándolo | Pégalas en edit-spec.yaml. Con una tanda a cero decisiones nuevas, está listo |
| La música suena a sala de espera, o chillona | Cama «upbeat corporate» o «inspiring background», o elegida porque estaba a mano | Elígela por la emoción del video |
| El video se ve «de principiante» aunque vendes caro | Tier llamativa, o mezcla de tiers, en una oferta de ticket alto | tier: sobria, sin mezclar |
| Todo destacado y nada pega, o el subtítulo se achica | Doble capa en varias frases, o se achicó la fuente para que entrara | Doble capa una sola vez, sobre el CTA; achicar_si_desborda: false, se parte en dos líneas |
| Una variación «no se nota», o no sabes cuál ganó | Tipografía parecida a la base, saturación con menos de 0,1 de diferencia, o dos cambios en la misma versión | Hazla visible en el celular, una variable por versión, y rehaz la prueba |
Cuando ya lo tienes hecho
Con los videos saliendo solos, el cuello de botella pasa a ser lo que llega después de publicar: quién responde los mensajes, cómo se separa al que puede pagar del que no, y cómo se agenda sin que tú estés. Eso ya no es un archivo: es el setter de IA que atiende Instagram y WhatsApp, el siguiente paso natural después de este.
Si quieres que lo montemos en tu negocio, escríbeme por WhatsApp y te digo por dónde partir.