Todos los sistemas

Activo · en producción desde

beBetterStudio

Pipeline de contenido que genera video corto, lo publica programado en cuatro redes y mide qué decisiones funcionaron de verdad.

Diseño, construcción y operación · 2026 · Node.js, TypeScript, React, FFmpeg, Gemini, n8n, PostgreSQL, AWS EC2, Docker

El problema que creía tener

Publicar contenido corto a mano no escala. Elegir imagen, elegir frase, maquetar el texto, renderizar, subir a cuatro redes, escribir cuatro copies, programar. Veinte minutos por pieza, todos los días, para siempre.

Así que construí la máquina. Funcionó. Y aun así la cuenta se empezó a caer, que resultó ser el problema realmente interesante.

El sistema

Una app web más una capa de automatización. La app tiene un banco de imágenes y un banco de frases; cada uno se analiza y se vectoriza, de modo que emparejar imagen con frase es una consulta por similitud y no una decisión que yo tenga que tomar a las once de la noche.

De ahí en adelante: la composición se renderiza con FFmpeg a un video 9:16, se elige pista de audio cruzando etiquetas de mood y energía, y la pieza entra en una cola con su propia cadencia. n8n drena esa cola y publica en YouTube Shorts, Instagram Reels, Facebook Reels y TikTok, con el baile particular que exige cada uno, incluida la subida en tres fases de Facebook y los tokens que caducan y tienen que rotarse solos.

También hay modo carrusel: se redacta un guion, las slides se generan en serie con la portada primero como referencia de estilo para las demás, y el conjunto se publica de forma nativa.

El paso 07 es el que no existió durante los tres primeros meses, y es la razón por la que este caso tiene trama.
  1. 01 Bancos

    Las imágenes y las frases se analizan y se vectorizan al entrar.

  2. 02 Emparejamiento

    Cruzar una imagen con una frase es una consulta por similitud, no una decisión que tome yo a las once de la noche.

  3. 03 Render

    FFmpeg compone el video 9:16 con el texto quemado en el fotograma.

  4. 04 Audio

    Se elige pista cruzando etiquetas de mood y energía contra la frase.

  5. 05 Cola

    La pieza espera su turno bajo una cadencia que controlo, no una ráfaga de publicaciones.

  6. 06 Publicación

    n8n drena la cola y resuelve el baile particular de cada plataforma, incluida la subida en tres fases de Facebook y los tokens que se rotan solos.

    YouTube Shorts, Instagram Reels, Facebook Reels, TikTok

  7. 07 Medición El paso que faltaba

    Cada día se recogen métricas y se cruzan con la receta que produjo cada publicación.

Cuando los números empezaron a caer

El pipeline producía. La cuenta no.

Alcance mediano por pieza
AbrilMayoJunioJulio
3.8402.3101.138385

La explicación fácil es el algoritmo, y es la explicación que yo quería. Otros dos números decían lo contrario:

Ninguno de los dos depende de a cuánta gente te muestra la plataforma. Dependen de lo que pasa cuando alguien ya está mirando. El alcance caía porque el contenido había empeorado, y yo no podía verlo porque no tenía forma de saber qué decisiones producían qué resultados.

El eslabón perdido

Cada publicación tiene una receta: qué frase, qué imagen, qué mood, qué formato, a qué hora. Esa receta vivía en mi base de datos. El rendimiento vivía en la API de la plataforma. Nada los unía.

Construir el vínculo para las publicaciones nuevas fue fácil. El problema eran las 40 anteriores a que todo esto existiera: justo las que cubren toda la caída. El plan las daba por perdidas.

No lo estaban. Los videos llevan la frase quemada en el fotograma, lo que significa que la miniatura la contiene literal. El caption no: el caption es una reescritura, que es exactamente por lo que cruzar por solapamiento léxico se quedaba en 25-50% incluso para publicaciones que sí correspondían.

Así que leí las miniaturas con visión y comparé frase contra frase. De 40 huérfanas, se recuperaron 39. Queda una. Para comparar: el pase por embeddings que probé primero recuperó exactamente una.

La regla que hizo confiable la reconciliación fue negarse a adivinar: cuatro pasadas, de la señal más fuerte a la más débil, y cuando no había confianza la publicación se quedaba sin vincular. Un cruce equivocado envenena todas las conclusiones que vengan después y no deja rastro. Una huérfana se ve y se arregla.

Lo que dijeron los datos

Con el rendimiento cruzado con la receta, una variable se movía casi al mismo ritmo que la caída. Las frases atribuidas a un autor con nombre:

Abril 17% → Mayo 32% → Junio 91% → Julio 83%

Vi los diez extremos: las cinco mejores y las cinco peores. Las que viajan señalan a alguien reconocible y rematan con un giro. Las planas son citas de filósofos entre comillas.

Eso era una hipótesis con evidencia detrás, no una conclusión. Así que se convirtió en un test controlado: diez piezas nuevas escritas con la voz propia de la cuenta, etiquetadas como grupo aparte, intercaladas con citas, y medidas por skip rate y watch time, no por alcance, porque el alcance es el número que miente.

El resultado, después de arreglar el instrumento

Antes de leer el resultado tuve que reparar la medición. Una de las vías de inserción no pasaba el tipo de medio, así que las publicaciones del test se consultaban con el juego de métricas de las imágenes, el que no trae ni skip rate ni watch time. Ningún error en ninguna parte: los números simplemente no estaban. Se pudo recuperar solo porque esas dos métricas son acumuladas del lado de la plataforma: recogerlas tarde sigue dando la cifra real.

Prueba controlada: control frente a voz propia
Skip rate (mediana)Watch time (mediana)
Control 51,5%4,88s
Voz propia 47,1%6,64s
p p = 0,019, menos es mejorp = 0,003, más es mejor
Control: 16 publicaciones, 1-27 de julio. Voz propia: 10 publicaciones. Medianas, con un test de permutación.

Un test de permutación da p = 0,019 para el skip rate y p = 0,003 para el watch time. El watch time volvió al nivel de abril, cuando la cuenta funcionaba, y la mediana de reproducciones se multiplicó por algo más de tres la semana siguiente.

La salvedad honesta: las diez frases del test compartían además una estructura en dos tiempos (un planteamiento y un giro), así que el test no puede separar quién habla de cómo está construida la frase. Separarlas habría exigido publicar diez piezas peores a propósito durante la única semana en que la cuenta volvía a crecer. Decidí no hacerlo, dejé los dos tiempos como norma de marca y dejé escrita la pregunta abierta como abierta.

De mi máquina a su propio servidor

En agosto la app salió de mi portátil. Ahora corre en su propia instancia de AWS con Docker detrás de Caddy y TLS, sobre PostgreSQL en lugar de SQLite, con login de Google restringido a una lista de correos y sin puerto SSH abierto: se entra por AWS Systems Manager.

El servidor además genera lotes enteros por su cuenta. Para hacerlo sin navegador mide el texto con el mismo archivo de fuente con el que pinta FFmpeg, de modo que los cortes de línea coinciden con la vista previa del editor en 705 de 708 casos (118 frases × 6 presets), y en todos los del preset de marca. Las reglas de marca (dos tiempos, tercera persona) son columnas que consulta el planificador de lotes, así que el bot no puede proponer una frase que no se publicaría.

Estado actual

Funcionando, a tres piezas diarias, y cada pieza sigue pasando por aprobación humana antes de publicarse. Al 12 de septiembre de 2026: 124 piezas con métricas, 755.151 reproducciones en total y 127.404 en la mejor pieza. El alcance mediano por pieza es 1.687. Cito la mediana y no el promedio (4.313) porque dos piezas tiran del promedio hacia arriba.

Lo que vale la pena llevarse no es el renderizador. Es que un pipeline de contenido sin medición es una máquina para producir más de aquello que ya estabas haciendo mal, solo que más rápido.

¿Necesitas algo así funcionando?

Cuéntame qué necesitas que funcione. Te respondo por escrito, con preguntas, un plan aproximado y un número.