09.11.2026 Cómo funciona Rubo Studio por dentro [proyectos, rubostudio]
Rubo Studio es el programa con el que se emiten radios online y canales de vídeo 24/7 desde un panel web. Lo instalas en tu servidor, subes la música y los vídeos, defines cómo quieres que suene cada hora y a partir de ahí la emisora va sola: día y noche, sin que nadie tenga que tener el panel abierto. Con él funciona, por ejemplo, Radio Fórmula 24, con sus emisoras y su canal de televisión.
Nació como NuvolaCast, un panel para gestionar emisoras que emitían a un servidor de streaming externo. Con los meses fue creciendo hasta hacerlo todo él mismo, y en octubre le cambié el nombre. En este post cuento cómo está hecho: las piezas, cómo se decide qué suena, cómo llega a los oyentes, cómo se emite vídeo sin cortes entre clip y clip, y todo lo que hay alrededor para que una radio no se quede nunca en silencio.
Las capturas son del panel real con una cuenta de demostración («Ona Nuvola»), y hay un vídeo de minuto y medio en rubostudio.es.

Las piezas
┌──────────── Panel web (Flask + Gunicorn) ────────────┐
│ emisoras · biblioteca · AutoDJ · parrilla · estudio │
└───────┬──────────────────────────────┬───────────────┘
│ genera .liq + socket │ comandos FFmpeg
▼ ▼
Daemon de radio ──► Liquidsoap (uno por emisora) Daemon de vídeo ──► FFmpeg ──► HLS / Twitch / YouTube
│
▼
Icecast (local) ──► nginx (HTTPS) ──► oyentes
▲
Liquidsoap de respaldo
- El panel es una aplicación Flask (Python) servida con Gunicorn detrás de nginx, con SQLite como base de datos. Es donde se configura todo, pero no emite nada: si el panel se reinicia o se cae, las emisoras siguen sonando.
- La radio la hace Liquidsoap, un lenguaje pensado para montar emisoras. Cada emisora es un proceso de Liquidsoap con su propio fichero de configuración, que el panel genera y que se controla por un socket Unix.
- El vídeo lo hace FFmpeg, con un montaje un poco especial para que no haya cortes entre vídeos (más abajo).
- Dos daemons vigilan: uno para la radio y otro para el vídeo. Rellenan colas, disparan eventos programados y levantan lo que se caiga.
- Icecast reparte el audio a los oyentes, siempre detrás de nginx, que pone el HTTPS.
Todo corre en un solo servidor: el de producción es un ordenador pequeño con un disco aparte de 1 TB para la música y los vídeos. Se instala con un script (install.sh) que deja preparados nginx, supervisor, fail2ban, Liquidsoap, FFmpeg, el entorno de Python y los accesos SFTP.
La biblioteca

Cada emisora tiene su biblioteca organizada por categorías de cuatro tipos: música, jingles, publicidad y programas. Una categoría es simplemente una carpeta en disco. Se puede subir desde el navegador, pero lo normal es usar SFTP con FileZilla: el panel crea para cada emisora usuarios de sistema sin shell, encerrados (chroot) en la carpeta de esa emisora, que solo pueden escribir en las carpetas de categoría. Quien lleva la radio sube su música sin ver nada más del servidor.
Al entrar un archivo, el panel lo analiza en segundo plano con FFmpeg (con caché por archivo, para no repetir trabajo):
- Fin de pista (EOM). Muchas canciones acaban con varios segundos de silencio. Se busca el último tramo por debajo de −45 dBFS durante al menos 1,5 s en el último minuto y se marca como punto de corte, para encadenar con la siguiente sin huecos.
- Volumen. Se mide la sonoridad según la norma EBU R128 y se calcula una ganancia para llevarla a −16 LUFS. Solo se suben las pistas flojas (como mucho +10 dB); las que ya están bien no se tocan.
También detecta duplicados (la misma canción por artista y título en varias categorías) y tiene una papelera por emisora, porque tarde o temprano alguien borra lo que no tocaba.
El AutoDJ: fórmulas, no listas

En vez de listas de reproducción eternas, el AutoDJ funciona con fórmulas: una secuencia de bloques que se repite sin fin. Por ejemplo: 1 indicativo, 3 éxitos, 1 novedad, 1 cuña, 2 éxitos, 1 indicativo, 1 novedad, y vuelta a empezar. Cada bloque saca archivos al azar de su categoría, respetando una separación mínima entre artistas en minutos (que no suene el mismo artista dos veces en una hora, por ejemplo). Si la biblioteca es pequeña y no hay alternativa, el artista se repite antes que dejar la radio en silencio.
La decisión importante aquí es quién manda en la cola. Liquidsoap sabe hacer listas aleatorias por su cuenta, pero entonces el panel no podría saber qué va a sonar. Así que en Rubo Studio Liquidsoap tiene una cola vacía (request.queue) y solo la app la llena, en el orden exacto de la fórmula: 15 archivos al arrancar y 10 más cuando quedan menos de 8. Cada archivo va anotado con su ganancia, su punto de corte y la duración del cruce. Así lo que el panel enseña como «próximas canciones» es exactamente lo que va a sonar.
Las transiciones son de radio: las canciones se solapan unos segundos sin fundidos raros, y cuando un jingle da paso a una canción, la canción entra en el último medio segundo del jingle.
La parrilla y los eventos

Encima del AutoDJ va la programación semanal: una parrilla de franjas de media hora (48 al día). Cada franja puede llevar la fórmula del AutoDJ o una categoría concreta (el programa de la mañana, la música de la noche), por días de la semana o en fechas concretas, que tienen prioridad. Una franja marcada como «solo una pista» no ocupa la media hora: suena un único archivo al empezar (una cuña de entrada, un separador) y sigue el AutoDJ.
Además están los eventos: a una hora y unos días concretos suena un archivo al azar de una categoría (campanadas, patrocinios, un boletín). Los eventos tienen prioridad pero esperan a que acabe la pista en curso, para no cortar una canción a la mitad.
Al pulsar «Aplicar», el panel regenera la configuración de Liquidsoap de la emisora y la recarga. Un fallo en el generador podría dejar una emisora muda, así que antes de tocar nada la configuración nueva se valida con liquidsoap --check en un fichero aparte. Si no pasa, la emisora sigue sonando con la anterior, el panel lo dice y el daemon manda un aviso. Esto lo añadí después de que un cambio de sintaxis entre versiones de Liquidsoap tumbara una emisora en directo: no volverá a pasar por lo mismo.
Cómo llega la radio a los oyentes

Cada emisora emite en MP3, AAC u OGG, de 64 a 320 kbps, a su punto de montaje en un Icecast local. Por delante está nginx, que es el que da la cara: los oyentes escuchan en una URL HTTPS del tipo stream.rubostudio.es/listen/<emisora>/radio.mp3.
La clave para que reiniciar no corte a nadie es el respaldo. Hay un Liquidsoap aparte que emite una cuña de respaldo por cada emisora, y en Icecast cada punto de montaje tiene configurado ese respaldo como fallback-mount. Si la emisora se reinicia, los oyentes pasan a la cuña sin desconectarse y vuelven solos a la emisora cuando está de nuevo en antena. Lo probé primero con un fichero de audio como respaldo y no sirve: Icecast lo envía a unas tres veces la velocidad real, por eso tiene que ser un emisor de verdad.
Lo mismo para la configuración de Icecast y de nginx: el panel solo recarga, nunca reinicia, porque un reinicio de cualquiera de los dos corta a todos los oyentes. Por eso también están excluidos de las actualizaciones automáticas del sistema, y la herramienta que reinicia servicios tras actualizar paquetes tiene prohibido tocarlos. Se actualizan a mano, en horas valle.
El «sonando ahora» va aparte del audio: Liquidsoap no manda metadatos dentro del flujo, sino que el título de cada canción (no de los jingles) se envía a Icecast por su API de administración, y se reenvía al reconectar. En el panel se ve al momento por SSE, sin recargar la página, con barra de progreso, historial y las próximas.
Alrededor de esto hay una API pública compatible con AzuraCast (para webs y reproductores que ya la sabían leer), una página pública por emisora con su reproductor, listas .pls y .m3u, y las carátulas sacadas de los propios ficheros.
Nunca en silencio
Una radio callada es lo peor que le puede pasar a una radio, así que hay varias capas para evitarlo:
- El respaldo de Icecast, ya explicado: cubre los reinicios y las caídas.
- El daemon de radio pasa cada 20 segundos por todas las emisoras: rellena la cola, comprueba que el proceso de Liquidsoap sigue vivo (si no, lo relanza, como mucho una vez cada dos minutos por emisora) y lee el log buscando el momento en que Liquidsoap se queda sin nada que emitir; si pasa, salta de pista. Si no se arregla solo, avisa por correo a los administradores.
- El fin de pista evita los silencios largos entre canciones. Lo de saltar pistas «silenciosas» automáticamente lo quité a propósito: saltaba canciones que empiezan con una intro en silencio.
Y una nota sobre reinicios: el daemon de radio se puede reiniciar sin que se entere ninguna emisora, porque cada Liquidsoap corre en su propia sesión. Eso permite desplegar cambios del motor con la radio en marcha.
El Estudio: boletines y señales horarias con voz

El Estudio es la parte que más me divirtió hacer. Todo lo que habla en la radio sin locutor sale de aquí, con voz sintética de ElevenLabs (el modelo Eleven v3, que es el único que habla catalán), en castellano, catalán o inglés.
Boletines de noticias. A las horas y días que elijas (por defecto a las 8, 14 y 20, de lunes a viernes) sale un boletín de un minuto. Cinco minutos antes, el daemon prepara uno nuevo:
- Lee las fuentes RSS (Europa Press, por ejemplo) y escoge unas cuatro noticias, sin repetir tema ni lo que ya salió en los dos boletines anteriores.
- Redacta el texto: o bien los titulares tal cual con su primera frase, hasta unos 900 caracteres, o bien un guion de radio escrito por un modelo de lenguaje a partir de una docena de candidatas. Si la IA falla, cae a los titulares y lo deja anotado.
- Locuta solo las noticias. La entradilla («Ona Nuvola FM, Noticias») y la despedida se generan una sola vez y se reutilizan, para no gastar créditos cada vez.
- Monta el boletín por pistas: la música de noticias, que baja bajo la voz (ducking), la entradilla en su segundo, las noticias y la despedida colocada unos segundos después de la última noticia, que cada día dura distinto.
A la hora en punto el boletín entra detrás de la señal horaria. Los boletines emitidos se guardan 14 días y se ofrecen por una API con token: así la web de Fórmula 24 tiene su sección de «Noticias en 60 segundos» sin hacer nada.

Señales horarias. «¡Hola! ¡Son… las tres!». Se escribe la frase tal como se quiere que suene, y la entonación se marca con la puntuación. Luego se monta por capas: acapella inicial, la voz con la hora, acapella intermedia, acapella final y una base musical, cada una en su segundo y con su volumen. Cada capa se lleva a −16 LUFS, se coloca con un retardo y se mezclan todas con un limitador.
Lo importante es que no se monta nada en antena. El diseño se exporta una sola vez a 24 MP3, uno por hora. Si después se cambia algo del montaje, el panel avisa de que hay cambios sin exportar y las emisoras siguen con la versión anterior hasta que se vuelva a exportar. En cada emisora solo se activa y se elige el montaje.
Al dar la hora, la canción en curso baja en cinco segundos, suena la hora y entra la siguiente canción: la que se cortó no se retoma. Eso necesitó una cola específica para las horas y un cruce que descarta lo que quedaba de la canción cortada, y lo probé antes sin emitir, con tonos en lugar de música y escribiendo la salida a un fichero.
Vídeo 24/7 sin cortes

Los canales de vídeo funcionan igual que la radio desde fuera: biblioteca por categorías (principal, bumpers, promos), AutoDJ por fórmulas, parrilla semanal y eventos. Por dentro es otra historia, porque con vídeo lo difícil es que no se note el paso de un vídeo al siguiente, sobre todo en Twitch o YouTube, donde una reconexión se ve como un corte del directo.
La solución es separar el trabajo en tres procesos de FFmpeg:
[render: 1 FFmpeg por vídeo] ──mpegts──▶ FIFO ──▶ [salida: 1 FFmpeg permanente, sin recodificar] ──▶ HLS
└──▶ UDP local ──▶ [repetidor] ──FLV──▶ RTMP (Twitch, YouTube…)
- El render lanza un FFmpeg por cada vídeo. Lo lee a velocidad real, lo lleva a la resolución y los fps del canal (720p o 1080p), aplica la ganancia de volumen, superpone el grafismo y codifica en H.264 y AAC. Si el servidor tiene chip gráfico, codifica por hardware (VAAPI): en producción pasa de casi dos núcleos a menos de uno con la misma imagen. Cada render escribe en una tubería (FIFO), y el siguiente vídeo arranca encadenado al reloj real, con una línea de tiempo continua.
- La salida es un FFmpeg que no se para nunca. Lee la FIFO y reempaqueta sin recodificar: a HLS para la vista previa del panel y las apps de tele, y a un puerto UDP local. Como no se reinicia entre vídeos, la conexión con Twitch o YouTube no se entera de que ha cambiado el vídeo.
- El repetidor pasa del UDP local a RTMP. Si el destino se corta, se relanza solo sin tocar la emisión ni la vista previa.
Hubo dos detalles que costaron. El primero: cada render empieza un flujo nuevo en la misma tubería, y la salida descartaba el primer fotograma clave de cada vídeo por verlo como «corrupto»; en Twitch se notaba un tirón en cada cambio. Se arregló marcando cada render como discontinuidad inicial. El segundo: el volumen. Normalizar en directo con el filtro loudnorm retiene unos tres segundos de audio y provocaba microcortes, así que, igual que en la radio, el volumen de cada vídeo se mide de antemano y se aplica como una ganancia fija.
Para conectar con Twitch o YouTube basta con pegar la clave de transmisión; el panel compone la URL. Y si Twitch deja de mostrar el canal aunque el servidor le siga mandando vídeo (pasa: una conexión colgada por su lado), el daemon lo detecta consultando su API, reconecta el envío y avisa por correo.
Grafismo y Twitch

Cada canal tiene un estudio de grafismo para diseñar el rótulo de cada videoclip (título, artista y carátula; cuatro estilos, posiciones, tipografías, colores, tiempos y efectos de entrada y salida) y la mosca del canal (fija o intermitente, con sombra, oculta mientras sale el rótulo si se quiere). Se ve sobre un fotograma real del vídeo que elijas y hay un botón para renderizar una prueba de 15 segundos con el mismo proceso que la emisión: lo que ves en la prueba es lo que saldrá. Los gráficos se dibujan con Pillow y se superponen con los filtros de FFmpeg.
Conectado a Twitch, el canal además:
- Avisa en pantalla de cada seguidor, suscripción, regalo, bits o raid, recibidos en directo por EventSub. Salen de uno en uno, con su diseño, y esperan si el rótulo ocupa la misma zona. Por dentro, cada render superpone una imagen fija que el daemon va repintando; un fallo en los avisos nunca corta la emisión.
- Escribe en el chat «Ahora suena…» a cada videoclip, con el mensaje y el retraso que elijas.
- Cambia el título y la categoría del directo desde el panel.
Un detalle pequeño que se nota mucho: los títulos limpios. En el rótulo, el chat y el panel se quitan las coletillas del tipo «- Remastered 2011» u «(Official Video)», para que salga el título como se dice. Los archivos no se tocan.
Lo que hay alrededor
Una radio de verdad necesita más cosas que música:
- Informes para SGAE y AGEDI. El historial de lo emitido se copia cada cinco minutos a la base de datos, y cada mes se puede descargar el CSV para las entidades de gestión (resumen por canción y detalle de cada emisión, con la duración realmente emitida). El día 1 llega por correo el resumen del mes anterior.
- Estadísticas y mapa de oyentes, sin guardar la IP de nadie. nginx traduce la IP de cada oyente a país y ciudad con una base de datos libre y solo escribe eso en su registro. El mapa se dibuja en el propio panel, sin servicios de mapas externos.
- Directorios de radios. Alta en radio-browser.info desde el panel y anuncio en directo en dir.xiph.org, con la canción y los oyentes al día.
- Copias de seguridad diarias automáticas, cifradas con age antes de salir del servidor y subidas a OneDrive. La clave para descifrarlas no está en el servidor.
- Multiusuario: cada cliente solo ve sus emisoras y canales.

Y la administración del servidor, desde el mismo panel: CPU, memoria y discos en tiempo real; servicios con su versión y botones de recargar y reiniciar (los que cortan a los oyentes avisan antes); actualizaciones del sistema, con Icecast, nginx y Liquidsoap desmarcados por defecto porque cortan la radio; cortafuegos, puerto SSH y fail2ban con su lista de IPs bloqueadas. Se entra con Touch ID (llaves de acceso WebAuthn), y cada cuenta puede quitar su contraseña y entrar solo así.
El panel está en castellano, catalán e inglés, en modo claro u oscuro.

Lo que aprendí
- Que el panel no emita. Separar la interfaz de la emisión es lo que permite desplegar a media tarde sin que ningún oyente lo note.
- Recargar, no reiniciar. En una radio, un
restartes un corte. Todo lo que puede recargarse en caliente se recarga, y lo que no, se hace en horas valle. - Validar antes de aplicar. Una configuración nueva se comprueba antes de parar nada; si falla, sigue sonando la anterior.
- Medir de antemano. El volumen, el final de cada pista, las señales horarias: todo lo que se puede calcular antes se calcula antes, y en antena solo se reproduce.
- Probar sin emitir. Los cambios en la cadena de audio se prueban con tonos y escribiendo a un fichero; los de vídeo, con FFmpeg de verdad. Nunca en antena.
Si tienes una radio, quieres montar un canal de vídeo en directo o simplemente te interesa cómo está hecho, tienes más información en rubostudio.es y me puedes escribir a [email protected].