Communitygithub.com

Developh/resolve-cut

Edita videos de personas hablando (lecciones, cursos, entrevistas) de principio a fin en DaVinci Resolve Studio: transcripciones palabra por palabra, un dossier de cada toma, la última toma completa de cada párrafo, una lista de cortes precisa a nivel de fotograma, ensamblaje de línea de tiempo basado en guion, renderizado con preajustes, control de calidad (sonoridad, fotogramas, palabras repetidas) y entrega. Cárgalo antes de cualquier trabajo de scripting, ensamblaje, renderizado, control de calidad o entrega en Resolve.

¿Qué es resolve-cut?

El skill Developh/resolve-cut está diseñado para automatizar el flujo de trabajo completo de edición de videos tipo 'talking head' (lecciones, cursos, entrevistas) dentro de DaVinci Resolve Studio. Su propósito es eliminar gran parte del trabajo manual y repetitivo en la posproducción, permitiendo a un agente de IA ejecutar tareas como transcripción, selección de tomas, ensamblaje de línea de tiempo, renderizado y control de calidad a través de scripts parametrizados. Es especialmente útil para desarrolladores y editores que buscan integrar flujos de video con herramientas de IA, ya que funciona tanto mediante scripts Python directos como a través de servidores MCP. El agente coordina el proceso: transcribe el metraje, genera un dossier compacto de todo el material, crea una lista de cortes precisa basada en un guion, ensambla la línea de tiempo en Resolve, renderiza con preajustes específicos para cada plataforma (YouTube, Instagram, LinkedIn, etc.) y realiza verificaciones de calidad como sonoridad, fotogramas y detección de palabras repetidas. Todo manteniendo el control creativo humano: el agente ejecuta y mide, la persona decide qué tomar y qué descartar.

Compatible con✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Developh/resolve-cut/tree/HEAD/skills/resolve-cut

Preguntar en tu IA favorita

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

Documentación

¿Qué hace resolve-cut?

Ensamblas, renderizas y verificas videos en DaVinci Resolve Studio mediante scripts guardados con parámetros. No reescribes el ensamblaje en cada sesión: llamas a los scripts, lees lo que devuelven y verificas el resultado con números y fotogramas. El gusto sigue siendo humano: la persona decide qué se queda, tú ejecutas y mides.

$SKILL a continuación es la carpeta de este skill (donde se encuentra este archivo). Los scripts en scripts/resolve/ se ejecutan dentro de Resolve a través de run.py; los scripts en scripts/media/ son Python simple con ffmpeg. Cada script tiene --help.

Antes de empezar

  1. Respuesta de Resolve: python3 "$SKILL/scripts/resolve/run.py" - '{}' <<< 'result = resolve.GetVersionString()'. El código de salida 2 significa que Resolve está cerrado, es la edición gratuita, o que Preferencias > Sistema > General > Uso de scripting externo no está configurado en Local.
  2. ffmpeg -version, python3 -c "import numpy, PIL" y ELEVENLABS_API_KEY (entorno o ./.env).
  3. Si el proyecto mantiene un archivo de notas (VIDEO_NOTES.md o similar), léelo: lo que la persona aprobó y rechazó antes. No preguntes dos veces por la misma corrección.
  4. Carpeta de trabajo: todo lo que escribas (transcripciones, dossier, fotogramas, renders) va a una carpeta de trabajo, nunca a la carpeta de metraje original.

El flujo

1. Transcripción. Transcripciones palabra por palabra, un JSON por clip: python3 "$SKILL/scripts/media/transcribe.py" <carpeta de clips> --out work/transcripts [--language por]. La transcripción es la base de todo: seleccionar tomas, cortar en límites de fotograma y verificar la mezcla final.

2. Dossier. Todo el material en un archivo de texto compacto, léelo una vez: python3 "$SKILL/scripts/media/dossier.py" <carpeta de clips> --transcripts work/transcripts --out work/dossier.md [--sheets]. Enumera cada oración con su tiempo y marca «repite C0076 20.2» cuando una oración comienza como una anterior: otro intento de la misma línea. Con un guion, conserva la última toma completa de cada párrafo; elimina vacilaciones, falsos comienzos e intentos repetidos. Abre un clip solo para decidir algo: python3 "$SKILL/scripts/media/inspect_range.py" <clip> <t0> <t1> --words work/transcripts/<clip>.json (fotogramas, forma de onda y palabras en una imagen).

3. Lista de cortes. Escribe work/plan.json con las tomas elegidas en orden ({"Lección 01 v1": [["01a", "C0081", 1.9, 12.6], ["01b", "C0083", 5.4, 23.8], ...]}: toma, clip, desde, hasta en segundos del clip), luego: python3 "$SKILL/scripts/media/cut_list.py" work/plan.json work/transcripts <carpeta de clips> work/cuts.json. Los cortes aterrizan en el límite de fotograma más silencioso cerca de las palabras, las pausas de más de 1 s se acortan, y los segmentos alternan zoom 1 / 1.2 para que los cortes entre tomas no salten. Para un falso comienzo sin espacio ("co- contar"), fuerza el fotograma con {"in_frame": N} o {"out_frame": N}. Notas para el editor: add_markers.py.

4. Ensamblar en Resolve. Una operación pesada por llamada, y lee cada result antes de la siguiente:

R="$SKILL/scripts/resolve/run.py"
python3 "$R" "$SKILL/scripts/resolve/project.py" '{"name": "Lección 01", "width": 1920, "height": 1080, "fps": "25"}'
python3 "$R" "$SKILL/scripts/resolve/assemble.py" '{"fase": "import", "bin": "Raw", "media": "/abs/clips", "files": ["C0081.MP4"]}'
python3 "$R" "$SKILL/scripts/resolve/assemble.py" '{"fase": "timeline", "bin": "Raw", "cuts": "/abs/work/cuts.json", "timeline": "Lección 01 v1", "zoom": {"center": [0.45, 0.37]}, "voice_isolation": 50}'

zoom.center es dónde está el rostro (fracción del fotograma), para que el acercamiento lo mantenga en su lugar. Verifica que frames == esperado y que failures esté vacío.

5. Mirar antes de renderizar. frames.py exporta fotogramas directamente desde la línea de tiempo en menos de un segundo ({"timeline": "Lección 01 v1", "frames": [100, 500], "folder": "/abs/work/frames", "max": 960}). Mira un fotograma normal y uno con acercamiento.

6. Renderizado. python3 "$R" "$SKILL/scripts/resolve/render.py" '{"fase": "start", "timeline": "Lección 01 v1", "folder": "/abs/out", "name": "leccion-01", "preset": "reel", "width": 1920, "height": 1080, "mark": [0, <fotogramas - 1>]}', luego consulta {"fase": "status", "job": "<id>"} cada 10 s hasta que JobStatus sea Complete. Espera a que el renderizado termine antes de responder: una ejecución sin interfaz que termina su turno en "renderizado iniciado" deja a nadie para verificarlo.

7. Verificar. python3 "$SKILL/scripts/media/qc.py" out/leccion-01.mp4 --expected <fotogramas> --transcribe (añade --ref contra la última versión aprobada, --frames para una hoja, --simulate para Instagram). Lee la transcripción de la mezcla final en cada corte: las palabras repetidas ("decide, decide"), las palabras cortadas y las palabras en el lugar equivocado son los defectos que una lista de cortes produce. Confirma un corte sospechoso en la forma de onda (inspect_range.py) antes de moverlo, arregla la lista de cortes, ensambla una nueva versión (v2) y renderiza de nuevo. Como máximo 3 rondas, cada defecto nombrado con su tiempo exacto. Nunca lo des por terminado porque "se ve bien": cita los números.

8. Entrega. loudness.py cuando la mezcla no está en el objetivo (ganancia lineal y un limitador, nunca loudnorm), luego deliver.py export.mp4 clean.mp4 [--no-editlist] [--copy <folder>]: sin pista de timecode, sin metadatos, moov primero, copia comparada byte a byte.

9. Revisión, después de la aprobación de la persona. Escribe en el archivo de notas lo que aprobaron y rechazaron, en sus palabras, una línea cada uno; cada nueva trampa con su fecha; y cada rutina que hiciste manualmente más de una vez se convierte en un script con parámetros junto a estos.

Ejecutar scripts dentro de Resolve

  • run.py se conecta a Resolve abierto y ejecuta un script con resolve, project, P (el JSON) y __file__ definidos, luego imprime result como JSON. Cuesta muchos menos tokens que escribir el mismo script a través de un MCP.
  • Con un servidor MCP en su lugar (el oficial run_script_unsafe): P = {...}; __file__ = "<ruta abs del script>"; exec(open(__file__, encoding="utf-8").read()). El sandbox lee archivos como ASCII sin encoding, y los nombres persisten entre llamadas: los scripts prefijan sus variables.
  • Guarda antes y después de escribir (GetProjectManager().SaveProject()); no hay deshacer. Haz cada nueva versión en una copia de la línea de tiempo (DuplicateTimeline, que también copia las composiciones de Fusion).
  • Consulta la API en lugar de adivinar: el MCP oficial tiene search_scripting_api, get_scripting_docs y get_whats_new (una vez por sesión), o el README de Blackmagic en la carpeta Developer/Scripting de Resolve (Ayuda > Documentación > Desarrollador).
  • ¿Nueva operación que se repetirá? Escríbela una vez como un script con un diccionario P y un result, junto a estos.

Destinos de entrega

DestinoImagenSonido
Reels, Stories, Shorts, TikTok1440x2560 a la velocidad de fotogramas de grabación, H.264 High a unos 20 Mbps reales, etiquetas Rec.709 (1-1-1), faststart-14 LUFS, pico verdadero por debajo de -1.5 dBTP
Lecciones y cursoscomo se grabó (1920x1080 o 3840x2160)-16 LUFS, pico verdadero hasta -1 dBTP
YouTubeel master 4K (las subidas a 1080p pierden calidad), MP4 sin listas de ediciónla mezcla del master
LinkedIn1920x1080 y 1080x1920 a 16 Mbps, con SRTla mezcla del master

Trampas verificadas

Medido en macOS con Resolve Studio 21.1.1 y 21.1.2, octubre y noviembre de 2026.

Máquina

  • Un ffmpeg a la vez, con nice -n 15 y -threads 2: 96 procesos ffmpeg paralelos congelaron el Mac y forzaron un reinicio.
  • En zsh, $var no se divide en palabras (ffmpeg $args se rompe): construye listas de argumentos en Python.
  • Escribe las salidas en la carpeta de trabajo: un ayudante que guardaba imágenes junto al clip escribió en la unidad de metraje original.

Configuración de Resolve y API

  • Resolve 21.1 movió el scripting de Python a Studio: run.py y ambos servidores MCP necesitan DaVinci Resolve Studio.
  • El scripting externo debe estar configurado en Local (Preferencias > Sistema > General) o scriptapp("Resolve") devuelve None.
  • StartRendering es la última llamada de un script: todo lo demás devuelve None hasta que el renderizado termina.
  • La velocidad de fotogramas del proyecto se bloquea cuando se crea la primera línea de tiempo: configúrala antes (project.py).
  • Resolución: SetSetting una clave a la vez, useCustomSettings primero.
  • El tiempo de espera del MCP (10 s por defecto, 60 s como máximo) devuelve un error pero el script sigue ejecutándose dentro de Resolve: verifica el estado antes de ejecutarlo de nuevo.
  • No hay trim, move o slip: elimina el elemento y vuelve a añadirlo con startFrame, endFrame (exclusivo) y recordFrame.
  • startFrame cuenta desde el primer fotograma del archivo, incluso con timecode de cámara.
  • GetSourceStartFrame a veces lee un fotograma antes (866 lee 865) mientras la colocación es correcta: lee el punto de entrada con GetLeftOffset.
  • AddMarker mantiene un marcador por fotograma.
  • ReplaceClip borra los metadatos del clip.
  • SetTrackEnable devuelve True y no cambia nada en algunas líneas de tiempo: deshabilita los clips con SetClipEnabled y confírmalo en un fotograma exportado.
  • SetClipEnabled en una línea de tiempo que no es la actual devuelve False: SetCurrentTimeline primero, luego confirma con GetClipEnabled.
  • DeleteClips(items, True) ondula todas las pistas: primero elimina, sin ondular, lo que cruce el corte (pistas de audio, subtítulos).
  • Voice Isolation funciona por script: TimelineItem.SetVoiceIsolationState({"isEnabled": True, "amount": 100}) redujo la reverberación de la sala en 3 a 4 dB; Timeline.SetVoiceIsolationState(track, {...}) establece un control para toda una pista.
  • La API no puede configurar parámetros de ResolveFX (Refinamiento facial, Eliminación de imperfecciones): una persona los configura, o tú conduces la interfaz de usuario a través de accesibilidad.
  • Con la pantalla bloqueada o Resolve minimizado, la ventana desaparece de la accesibilidad pero la API sigue funcionando: verifica con fotogramas exportados.
  • Super Scale es una propiedad del pool de medios: SetClipProperty("Super Scale", 2); un archivo de reemplazo necesita que se configure de nuevo.
  • Los LUT para Graph.SetLUT van en la carpeta de LUT del sistema de Resolve, en una subcarpeta, pasados por ruta relativa después de RefreshLUTList().
  • Cambiar de página mientras Resolve reproduce bloquea la aplicación; item.GetFusionCompByIndex(1).Execute(lua) edita una composición sin abrir la página de Fusion.

Importación y medios

  • Un ProRes por llamada de importación: varios ProRes 4444 (y una vez dos nuevos ProRes 422) en una llamada bloquearon Resolve 21.1 (UiMpController, std::out_of_range). Los MP4 de cámara, 22 en una llamada, estaban bien.
  • Después de ese bloqueo, un proceso huérfano ResolvePython hace que el MCP launch_resolve reporte Resolve como ejecutándose: mátalo y abre Resolve de nuevo.
  • ImportMedia con una lista de rutas: el formato con diccionarios devolvió None para MP4 en 21.1.
  • Resolve almacena en caché la decodificación por ruta: un archivo que renderices de nuevo obtiene un nombre nuevo.
  • PCM 24-bit little-endian en MOV se lee como ruido blanco: usa pcm_s24be.
  • HEVC de iPhone bloqueó el decodificador de macOS: edita desde ProRes.
  • HDR de iPhone (HLG, Dolby Vision): nuestro mapeo de tonos se veía deslavado y fue rechazado dos veces. Pide una exportación SDR al principio.
  • Un .mov de 1 fotograma se importa como un fotograma fijo de 150 fotogramas: dale al menos 3 fotogramas y recórtalo en la línea de tiempo.
  • Alfa directo: Resolve lee el alfa sin premultiplicar, por lo que una superposición hecha con ffmpeg overlay sobre un lienzo transparente sale oscura; colócala con pad=W:H:x:y:[email protected].

Renderizado

  • Con SelectAllFrames, StartRendering a veces devuelve False: establece MarkIn/MarkOut (render.py mark), que también corta las colas de sonido que alargan una línea de tiempo.
  • Un trabajo H.264 creado justo después de un trabajo solo de audio salió sin video: carga un preajuste antes de SetRenderSettings, y verifica el flujo de video con ffprobe.
  • VideoQuality en H.264 es un techo, no un promedio: 20000 dio 14.7 Mbps y 27300 dio 20.7 Mbps a 1440x2560.
  • "Forzar escalado a la máxima calidad" no es una clave de SetRenderSettings, pero viaja dentro de un preajuste de renderizado (ForceHighestQualitySizing).
  • ExportRenderPreset(name, path) trata path como una carpeta; ImportRenderPreset nombra el preajuste según el archivo.
  • Los preajustes almacenan una velocidad de fotogramas: establece FrameRate desde la línea de tiempo, o una línea de tiempo a 29.97 se renderiza convertida (render.py lo hace).
  • ExportFrame (21.1.2) dio 4 fotogramas a 1440x2560 en 0.75 s, dentro de 41 a 46 dB del renderizado: verifica fotogramas sin renderizar.

Sonido y transcripciones

  • Un corte entre dos tomas puede unir una palabra repetida ("decide, decide"): transcribe la mezcla final y busca duplicados. Arréglalo sin cambiar la duración: termina la primera pieza antes de la palabra, comienza la segunda más temprano en la pausa de la buena toma.
  • La transcripción no escucha susurros ni respiraciones: los puntos de corte provienen del sonido propio de la cámara (cut_list.py).
  • Scribe una vez colocó una palabra 3 s antes, después de un aplauso y un susurro: confirma en la forma de onda antes de mover un corte.
  • Los efectos de sonido MP3 llegan unos 60 ms tarde (el retardo del codificador no se compensa): usa WAV para cualquier cosa que deba acertar un fotograma.
  • loudnorm en una pasada es dinámico y cambia la mezcla aprobada; en dos pasadas, cae silenciosamente a dinámico: usa loudness.py.

Metraje y plataformas

  • 25 fps en Instagram: servido a 25 (ffprobe de los archivos servidos, noviembre de 2026). El tartamudeo proviene de los iPhones con ProMotion, que no tienen modo de 25 o 50 Hz. Mantén el metraje a 25 fps a 25 (convertir a 30 repite un fotograma de cada cinco); graba material nuevo a 29.97 con obturación 1/50.
  • El parpadeo bajo luces de 50 Hz depende de la obturación, no de la velocidad de fotogramas: 1/50 y 1/100 no parpadean; 1/60 a 30 fps sí.
  • Instagram recodifica en el teléfono y de nuevo en el servidor: la entrega básica es de unos 720x1280 a 1.4 Mbps, con sonido normalizado a unos -14 LUFS, por lo que un master por encima de -14 no gana nada. qc.py --simulate muestra lo que sobrevive.
  • CRF en ffmpeg priva a los fondos oscuros: un reel se quedó a 6.8 Mbps. Verifica la tasa de bits real antes de entregar.
  • Una ejecución claude -p sin interfaz termina su turno cuando el renderizado comienza a menos que el prompt diga que espere.

Scripts

ScriptQué hace
scripts/media/transcribe.pyJSON palabra por palabra por clip (clave a través de stdin, nunca en la lista de procesos)
scripts/media/dossier.pycada clip como texto compacto: oraciones con tiempos, pausas, intentos repetidos, hojas de fotogramas opcionales
scripts/media/inspect_range.pyuna imagen de un rango: fotogramas, forma de onda, palabras con tiempos
scripts/media/cut_list.pytoma…

…[truncado para traducción]

Skills relacionados