Editor de Reels Cinematográficos
Convierte una carpeta de clips sin procesar y una pista de música en un reel cinematográfico de 15–45 s, 9:16, sincronizado con el ritmo. Toda la edición se realiza con FFmpeg desde la línea de comandos. El plan reside en una lista de edición editable (edl.json) para que cualquier cambio pueda re-renderizarse sin empezar de nuevo.
0. Reglas estrictas
- NUNCA modifiques ni elimines nada dentro de
raw/. Escribe todas las salidas enwork/yout/. - La música debe ser proporcionada por el usuario (con licencia / libre de derechos). Si
music/está vacío, pide una pista — no descargues música con derechos de autor. - Muestra al usuario el plan de tomas (paso 4) y obtén su aprobación antes del renderizado final.
- Prefiere cortes directos en los beats. Usa transiciones con moderación (máximo 2–3 por reel).
1. Verificar requisitos previos
ffmpeg -version && ffprobe -version
python3 -c "import librosa, numpy" || pip install librosa numpy soundfile
Si FFmpeg falta, dile al usuario que lo instale (Windows: winget install ffmpeg, Mac: brew install ffmpeg, Linux: sudo apt install ffmpeg) y detente.
2. Estructura de carpetas
proyecto/
raw/ # clips de cámara (solo lectura)
music/ # una pista de música
luts/ # archivo .cube LUT opcional para corrección de color
work/ # miniaturas, segmentos, edl.json, beats.json
out/ # renders finales (reel_v1.mp4, reel_v2.mp4 ...)
3. Inventario y visualización del metraje
Para cada clip, registra duración, resolución, fps y orientación:
ffprobe -v error -show_entries format=duration:stream=width,height,r_frame_rate,codec_type -of json raw/CLIP.mp4
Crea una hoja de contacto por clip (un fotograma cada 2 s) y VÉELA con la herramienta de lectura para saber qué hay en cada toma:
ffmpeg -y -i raw/CLIP.mp4 -vf "fps=1/2,scale=320:-1,tile=4x3" -frames:v 1 work/thumbs/CLIP.jpg
Etiqueta cada momento útil: tipo de toma (plano general / medio / primer plano / detalle), sujeto, movimiento, calidad (nítido, estable, bien iluminado) y si es apto para cámara lenta (fuente ≥ 50 fps).
4. Construir la historia (plan de tomas)
Estructura cada reel como:
- Gancho (0–2 s) — la toma más impactante. Sin logo ni texto de introducción.
- Desarrollo — detalles y anticipación (primeros planos, cámara lenta).
- Momento heroico — en el beat más grande / caída de la música.
- Emoción / pago — reacciones, personas, celebración.
- Outro (últimos 2–3 s) — plano general limpio, texto o logo opcional, fundido de la música.
Plantilla de entrega de auto (adapta al metraje): exterior general → primer plano del logo → detalles de rueda/faro → familia caminando (cámara lenta) → revelación de la cubierta (en la caída) → entrega de llaves → reacciones/sonrisas → interior + volante → pastel / guirnalda / pooja → primer viaje (plano general, outro).
Reglas: varía los tamaños de toma (nunca dos planos generales seguidos), mantén cada toma entre 0.5–2.5 s, usa cámara lenta solo para tomas emotivas o de revelación, corta en movimiento cuando sea posible.
5. Detección de beats
# work/beats.py
import librosa, json, sys
y, sr = librosa.load(sys.argv[1])
tempo, frames = librosa.beat.beat_track(y=y, sr=sr)
beats = librosa.frames_to_time(frames, sr=sr).tolist()
onset = librosa.onset.onset_strength(y=y, sr=sr)
json.dump({"tempo": float(tempo), "beats": beats}, open("work/beats.json", "w"), indent=1)
print(f"tempo {float(tempo):.1f} BPM, {len(beats)} beats")
Corta en los beats (cada beat para secciones rápidas, cada 2º o 4º beat para las lentas). Encuentra la caída (mayor salto de energía) y coloca la toma heroica allí.
6. Escribir la lista de edición work/edl.json
{
"output": {"w": 1080, "h": 1920, "fps": 30},
"music": {"file": "music/track.mp3", "start": 12.0, "fade_out": 1.5},
"grade": {"lut": "luts/teal_orange.cube", "vignette": true},
"shots": [
{"id": 1, "src": "raw/C0001.mp4", "in": 4.20, "dur": 1.40, "speed": 1.0, "crop_x": "center", "note": "gancho – plano general coche"},
{"id": 2, "src": "raw/C0007.mp4", "in": 10.0, "dur": 1.00, "speed": 0.5, "crop_x": "center", "note": "rueda cámara lenta"}
],
"transitions": [{"after_shot": 4, "type": "fade", "dur": 0.3}],
"text": [{"text": "Bienvenido a casa", "start": 18.0, "end": 21.0}]
}
dur es la duración en la línea de tiempo. Con speed 0.5, el rango de origen utilizado es dur × speed segundos. Muestra al usuario este plan como una tabla simple (toma, tiempo, descripción) antes de renderizar.
7. Renderizar cada toma como un segmento uniforme
Cada segmento debe tener resolución, fps, formato de píxel idénticos y sin audio, para que se unan limpiamente:
# fuente apaisada → recorte 9:16, velocidad, corrección
ffmpeg -y -ss IN -t SRC_DUR -i raw/CLIP.mp4 -an -vf "\
setpts=PTS/SPEED,\
scale=-2:1920,crop=1080:1920,\
fps=30,\
lut3d=luts/teal_orange.cube,\
eq=contrast=1.06:saturation=1.10,\
vignette=PI/5,\
format=yuv420p" \
-c:v libx264 -crf 16 -preset medium work/seg_01.mp4
SRC_DUR = dur × speed. Para cámara lenta usa una fuente de alta fps; evitaminterpolatea menos que se pida explícitamente (lento y con artefactos).- Fuente vertical: usa
scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920. - Sin LUT disponible: usa
eq=contrast=1.08:saturation=1.15:gamma=0.98máscurves=preset=increase_contrastpara un aspecto cinematográfico. - Las barras cinematográficas opcionales NO se usan en reels 9:16.
8. Unir segmentos
Sin transiciones — concat demuxer:
printf "file 'seg_%02d.mp4'\n" 1 2 3 ... > work/list.txt
ffmpeg -y -f concat -safe 0 -i work/list.txt -c copy work/video.mp4
Con una transición entre dos segmentos usa xfade (desplazamiento = duración del primer clip − duración de la transición):
ffmpeg -y -i work/seg_04.mp4 -i work/seg_05.mp4 -filter_complex \
"[0][1]xfade=transition=fade:duration=0.3:offset=1.2,format=yuv420p" work/seg_04_05.mp4
Cada transición acorta la línea de tiempo por su duración — vuelve a verificar la alineación con los beats.
9. Música, texto, exportación final
ffmpeg -y -i work/video.mp4 -ss MUSIC_START -i music/track.mp3 -filter_complex "\
[1:a]afade=t=in:d=0.3,afade=t=out:st=FADE_START:d=1.5,loudnorm=I=-14:TP=-1:LRA=11[a];\
[0:v]drawtext=fontfile=FONT.ttf:text='Bienvenido a casa':fontsize=72:fontcolor=white:\
x=(w-text_w)/2:y=h*0.72:enable='between(t,18,21)':alpha='if(lt(t,18.4),(t-18)/0.4,1)'[v]" \
-map "[v]" -map "[a]" -shortest \
-c:v libx264 -crf 18 -preset slow -profile:v high -pix_fmt yuv420p -r 30 \
-c:a aac -b:a 192k -movflags +faststart out/reel_v1.mp4
Encuentra un archivo de fuente en la máquina para drawtext (o pídelo). Mantén el texto mínimo, dentro del área segura (evita el 15% superior y el 25% inferior, donde se sitúa la interfaz de Instagram).
10. Control de calidad (siempre)
ffprobe out/reel_vN.mp4→ 1080×1920, 30 fps, H.264, AAC, duración según lo planeado.- Hoja de contacto del reel final (
fps=2,scale=270:-1,tile=6x5) y visualízala: sin fotogramas negros, sin recortes incorrectos (cabezas cortadas), color consistente. - Verifica el volumen y que la toma heroica caiga en el drop.
- Informa al usuario: duración, número de tomas, ruta del archivo y cualquier cosa de la que no estés seguro.
11. Revisiones
Nunca empieces de nuevo. Edita work/edl.json (intercambia una toma, cambia in/dur, velocidad, texto, gradación), re-renderiza solo los segmentos modificados, vuelve a unir y exporta como la siguiente versión (reel_v2.mp4). Mantén las versiones antiguas para que el usuario pueda comparar.