¿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
- 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. ffmpeg -version,python3 -c "import numpy, PIL"yELEVENLABS_API_KEY(entorno o./.env).- Si el proyecto mantiene un archivo de notas (
VIDEO_NOTES.mdo similar), léelo: lo que la persona aprobó y rechazó antes. No preguntes dos veces por la misma corrección. - 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.pyse conecta a Resolve abierto y ejecuta un script conresolve,project,P(el JSON) y__file__definidos, luego imprimeresultcomo 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 sinencoding, 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_docsyget_whats_new(una vez por sesión), o el README de Blackmagic en la carpetaDeveloper/Scriptingde Resolve (Ayuda > Documentación > Desarrollador). - ¿Nueva operación que se repetirá? Escríbela una vez como un script con un diccionario
Py unresult, junto a estos.
Destinos de entrega
| Destino | Imagen | Sonido |
|---|---|---|
| Reels, Stories, Shorts, TikTok | 1440x2560 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 cursos | como se grabó (1920x1080 o 3840x2160) | -16 LUFS, pico verdadero hasta -1 dBTP |
| YouTube | el master 4K (las subidas a 1080p pierden calidad), MP4 sin listas de edición | la mezcla del master |
| 1920x1080 y 1080x1920 a 16 Mbps, con SRT | la 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 15y-threads 2: 96 procesos ffmpeg paralelos congelaron el Mac y forzaron un reinicio. - En zsh,
$varno se divide en palabras (ffmpeg $argsse 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.pyy ambos servidores MCP necesitan DaVinci Resolve Studio. - El scripting externo debe estar configurado en Local (Preferencias > Sistema > General) o
scriptapp("Resolve")devuelve None. StartRenderinges 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:
SetSettinguna clave a la vez,useCustomSettingsprimero. - 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) yrecordFrame. startFramecuenta desde el primer fotograma del archivo, incluso con timecode de cámara.GetSourceStartFramea veces lee un fotograma antes (866 lee 865) mientras la colocación es correcta: lee el punto de entrada conGetLeftOffset.AddMarkermantiene un marcador por fotograma.ReplaceClipborra los metadatos del clip.SetTrackEnabledevuelve True y no cambia nada en algunas líneas de tiempo: deshabilita los clips conSetClipEnabledy confírmalo en un fotograma exportado.SetClipEnableden una línea de tiempo que no es la actual devuelve False:SetCurrentTimelineprimero, luego confirma conGetClipEnabled.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.SetLUTvan en la carpeta de LUT del sistema de Resolve, en una subcarpeta, pasados por ruta relativa después deRefreshLUTList(). - 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
ResolvePythonhace que el MCPlaunch_resolvereporte Resolve como ejecutándose: mátalo y abre Resolve de nuevo. ImportMediacon 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
.movde 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
overlaysobre un lienzo transparente sale oscura; colócala conpad=W:H:x:y:[email protected].
Renderizado
- Con
SelectAllFrames,StartRenderinga veces devuelve False: estableceMarkIn/MarkOut(render.pymark), 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. VideoQualityen 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)tratapathcomo una carpeta;ImportRenderPresetnombra el preajuste según el archivo.- Los preajustes almacenan una velocidad de fotogramas: establece
FrameRatedesde 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 --simulatemuestra 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 -psin interfaz termina su turno cuando el renderizado comienza a menos que el prompt diga que espere.
Scripts
| Script | Qué hace |
|---|---|
scripts/media/transcribe.py | JSON palabra por palabra por clip (clave a través de stdin, nunca en la lista de procesos) |
scripts/media/dossier.py | cada clip como texto compacto: oraciones con tiempos, pausas, intentos repetidos, hojas de fotogramas opcionales |
scripts/media/inspect_range.py | una imagen de un rango: fotogramas, forma de onda, palabras con tiempos |
scripts/media/cut_list.py | toma… |
…[truncado para traducción]