¿Qué hace make-ai-short — cortos generativos, de principio a fin?
Los píxeles provienen de un modelo de video; Remotion solo compone clips + subtítulos. Probado en
el hombre azul #1 "La Puerta". Hermano de /make-short (100% TSX): misma disciplina, más tres
puertas que TSX nunca necesitó: costo indicado antes de generar, personaje bloqueado desde
character.json, bucle fijado mediante condicionamiento del frame final.
Ejecuta todo desde la raíz del repositorio. Necesita FAL_KEY (clips + imágenes) y ELEVENLABS_API_KEY
(voz) en .env.
Las tres reglas de hierro
- NUNCA generes un personaje a partir de texto dos veces.
ai-shorts/<char>/character.json+character.pnges la referencia bloqueada. Cada imagen fija que necesite al personaje se genera congen_image.py --ref character.png; cada clip es imagen-a-video desde un frame que ya lo contiene. Si aún no existe una hoja de personaje, crearla UNA (y que el usuario la revise) es su propio paso ANTES de cualquier otra cosa. - INDICA EL COSTO ANTES DE GASTARLO. Derivado, no cotizado: Seedance 1080p sin audio =
h×w×24×dur/1024tokens × $1.2/1M ≈ $0.0583/s (API de precios de fal validada 2026-07-14 — consulta ai-shorts/IDEAS.md para la tabla completa + las trampas de Wan/Veo-Lite). Siempregenerate_audio:false— la voz es de ElevenLabs, el sonido de nuestro pipeline de SFX. - Bucle por RESTRICCIÓN, no por suerte. El frame 0 real de la Toma 1 (extraído del clip, no de la
imagen de prompt) es el objetivo del bucle. La toma final lo pasa como
end_image_url(Seedance/Kling), y la composición se asienta sobre esa imagen fija exacta durante los últimos ~10 frames (patrónLoopSettleenremotion/src/shots/ai-1/Ai1Door.tsx).
Contrato del artefacto
ai-shorts/<nombre>/
script.md · beats.json — mismo contrato que /make-short (vo[] con tiempos reales de palabras)
character.json — LA REFERENCIA BLOQUEADA (+ bloque video_model = el modelo elegido)
shots/NN-slug.png — frame de INICIO por toma (gen_image.py --ref character.png [--ref scene])
shots/NN-slug.json — sidecar de gen_clip (modelo, payload, id de solicitud)
shots/upload-urls.json — URLs de almacenamiento de fal (las subidas se reutilizan entre regeneraciones)
voice/ · sfx-plan.json · output/ [voice/output gitignored]
remotion/src/shots/ai-N/ — composición + vo.gen.ts
media/projects/<nombre>/ — LAS copias de los clips para staticFile() (se commitean — los píxeles no
son reproducibles; las copias de trabajo shots/*.mp4 están gitignored)
Orden de las operaciones (voz antes que píxeles)
- Guión — script.md + beats.json. Gancho compuesto en el frame 0; la tesis DEBE querer el bucle (hombre azul #1: "la llegada es un mito" → final = caminando hacia la misma puerta). ~80-100 palabras, ventanas de 2.5-2.7 pps. Nunca, jamás, un CTA de despedida — termina con el remate, el bucle es el final.
- VOZ PRIMERO —
python tools/gen_voice.py --beats ... --emit-ts remotion/src/shots/ai-N/vo.gen.ts(voz y modelo desde.env, o un bloque"voice"en beats.json cuando una serie quiera un narrador como GeorgeJBFqnCBsd6RMkjVDRZzbcon etiquetas eleven_v3; actúa sobre las banderas LINE CHECK como en make-short Stage 4 antes de generar cualquier clip). Los tiempos reales de las palabras deciden las ventanas de las tomas; Seedance acepta duraciones enteras de 4-12 s, así que redondea las duraciones de las tomas HACIA ARRIBA desde las ventanas. Diálogo (dos o más personajes): el bloque"cast"de beats.json asigna cada hablante a sucharacter.json(cuyo bloque"voice"se usa), cada línea de voz nombra a su hablante en"who", y--pack 0.3coloca las líneas una tras otra desde sus longitudes reales en lugar de comprimirlas en ventanas estimadas. Una voz por personaje vive en su character.json, en ningún otro sitio. Bocas en el diálogo: el modelo de video no sabe quién habla; hace que los oyentes hablen, congela a los hablantes y estira una risa a lo largo de la siguiente frase. Después de los clips, ejecutapython tools/mouth_sync.py ai-shorts/<nombre>(el"shots"de beats.json da a cada clip su inicio global y duración; cada bloque"mouth"de character.json da lado, radio, zona, estilo ykeep_above). Borra las bocas del modelo y dibuja la boca de cada personaje a partir de la sonoridad de la pista de voz. Haz QA de la misma manera: ambas caras cada medio segundo contra quién está hablando. Un cambio de acción dentro de una línea (reír, luego hablar) son dos clips, el segundo comenzado desde el frame del primero en el corte. Luego escanea cada clip con boca frame a frame: un pico de más de cuatro veces la mediana del clip es un corte, un gesto rápido o un morph de dos frames con rasgos fantasma; lista los frames morph en el"hold"de esa toma y vuelve a ejecutar, para que el salto se reproduzca como un corte limpio. - Frames de inicio — uno por toma,
python tools/gen_image.py --model pro --aspect 9:16 --ref character.png(+ la imagen fija de la escena de la toma 1 como 2da referencia cuando una ubicación/prop debe coincidir entre tomas). Haz QA de cada PNG ANTES de cualquier llamada de video — aquí es donde arreglar la deriva es barato.- Observa manos, detalles de vestimenta, proporciones — nómbralos explícitamente en el prompt en las regeneraciones.
- La geometría debe ser transitable (la lección de la toma 6, costó 2 regeneraciones): con una fijación de frame final, el frame de inicio debe permitir que el movimiento ALCANCE el estado final caminando hacia adelante — si el personaje del frame final está MÁS CERCA de la cámara que el del inicio, el modelo genera un clon en su lugar. Coloca al personaje más grande/cerca que el estado final para que el movimiento hacia adelante lo encoja.
- Clips — indica el costo total derivado, luego
python tools/gen_clip.pypor toma en paralelo (--set image_url=<url de almacenamiento fal> --set 'duration="N"' --set resolution=1080p --set generate_audio=false, toma final--set end_image_url=<url del frame 0>). Sube las referencias una vez mediantebakeoff_clip.upload_ref; guarda las urls en shots/upload-urls.json. El formulario fal anterior es el predeterminado de este checkout (CLIP_PROVIDER=fal,FAL_VIDEO_MODELen .env). En kie.ai en su lugar (--provider kie): los nombres de campo siguen el modelo kie, no fal. Parabytedance/seedance-2:--aspect none --set first_frame_url=@ai-shorts/<nombre>/shots/NN-slug.png --set duration=N --set resolution=1080p --set generate_audio=false, toma final--set last_frame_url=@<frame0.png>.@pathsube el archivo local (kie lo conserva 24h, así que la reutilización de upload-urls.json solo es válida dentro del mismo día). Costo: kie.ai/pricing, no la tabla de fal; la ejecución imprime los créditos usados.bakeoff_clip.pysigue siendo solo para fal. Haz el prompt de cada clip con: frase exacta del personaje + "EXACTAMENTE UN personaje en la escena durante toda la toma" (los clones son el fallo #1) + movimiento de cámara + "sin morphing" + línea de estilo. - QA de clip, frame a frame — tira inicio/medio/fin por clip + una comparación lado a lado del último frame del clip final vs el objetivo del bucle. Regenera los fallos (indica el costo de la regeneración); una regeneración de 5s a 1080p es ~$0.29 — barato comparado con enviar un clon.
- Composición — Secuencias de
OffthreadVideosobre UNA línea de tiempo, ~8 frames de fundido cruzado con solapamiento (los clips son ~1s más largos que sus ventanas — ese es el margen),Captions+ProgressBaren la raíz, disolventeLoopSettleal final. Copia los clips finales amedia/projects/<nombre>/.npm run gen, frames.mjs en los límites y momentos clave, LEE cada PNG, luego render-all --scale=1 y multiplexa voice.wav. - SFX — flujo /suggest-sfx. Calibración de VO con huecos (hombre azul #1): las señales en los huecos de VO no reciben enmascaramiento ni ducking — necesitan ~7-8 dB MENOS que la tabla de narración densa (silbidos de hueco -9..-13, no -3..-5). Verifica con volumedetect RMS contra una referencia de media de voz; la capa principal puede igualar el nivel de voz una vez. La mezcla de audición espera el oído del usuario (puerta dura).
- Guardar — catalogar used_in, tiempos reales de beats.json (gen_voice los escribe), sidecars commiteados, registro de costos en el informe final.
Hecho =
personaje consistente en cada frame de cada clip (QA frame a frame) · bucle verificado
(último frame vs frame 0 lado a lado) · subtítulos con tiempos reales de palabras · costos indicados antes de
cada gasto y totalizados después · audición SFX renderizada, esperando el oído del usuario · sin CTA de despedida.
· tools/check_text.py limpio en el texto hablado y en pantalla · copia para móvil hecha con
tools/export_phone.py · una entrada fechada en LESSONS.md (y la habilidad actualizada, en términos generales,
si la lección cambia su funcionamiento).