Cómo Diseñar un Automatizador Radial 100% en Python para emisoras online
En nuestro día a día surgió la necesidad de crear Cómo Diseñar un Automatizador Radial 100% en Python sin depender de software propietario.
La madrugada del viernes 12 nos quedamos sin playlist en La MIX Radio y la señal cayó.
Tuvimos que improvisar una solución que corriera en nuestra VM y que pudiera gestionar envíos a Icecast, actualizar la web y avisar en Telegram.
En este artículo vemos paso a paso el proceso que seguimos.
El problema concreto
Sin un automatizador, la cadena falla cuando el proceso de playout se detiene.
Los oyentes escuchan silencio, la página muestra “Stream offline” y los indicadores de Icecast se quedan en rojo.
El problema no es solo la pérdida de audiencia; también afecta al ranking de SEO porque el motor de búsqueda marca la URL como inactiva.
Además, siempre que reiniciamos el script manualmente perdemos la información del histórico de reproducciones, lo que complica la generación de reportes para los artistas.
Qué necesitamos antes de empezar
- Una VM Debian 12 (VM 111) con acceso root.
- Python 3.11 instalado y pip actualizado.
- Credenciales de Icecast (admin:password) y de WordPress (Application Password).
- Bot de Telegram y token de BotFather.
- Repositorio Git para versionar el script.
- n8n instalado en contenedor si queremos orquestar flujos.
Cómo lo resolvimos, paso a paso
Primero preparamos el entorno. Instalamos los paquetes necesarios con un solo comando.
apt update && apt install -y python3-pip git curl
Después clonamos el repositorio donde guardaremos el código.
git clone https://github.com/fmartinez/automatizador-radial.git /home/lamix/automatizador
Creemos un entorno virtual para aislar dependencias.
python3 -m venv /home/lamix/automatizador/venv
Activamos el entorno y instalamos librerías.
source /home/lamix/automatizador/venv/bin/activate
pip install requests python-telegram-bot schedule mutagen
A continuación configuramos el archivo config.yaml con nuestras credenciales.
icecast:
host: localhost
port: 8000
mount: /lamix
user: admin
password: password123
wordpress:
url: https://lamixradio.com/wp-json/wp/v2/posts
user: fabio
app_password: abcdefghijklmnop
telegram:
token: 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
chat_id: -1001234567890
playlist_path: /home/lamix/playlists/lamix.m3u
El script principal, automate.py, sigue una lógica simple: leer la playlist, reproducir la pista con VLC, enviar metadatos a Icecast, publicar entrada en WordPress y notificar en Telegram.
Cada paso está encapsulado en una función.
import os
import time
import yaml
import requests
import schedule
from telegram import Bot
from mutagen.easyid3 import EasyID3
from subprocess import Popen
with open('/home/lamix/automatizador/config.yaml') as f:
cfg = yaml.safe_load(f)
bot = Bot(token=cfg['telegram']['token'])
def get_next_track():
with open(cfg['playlist_path']) as f:
lines = [l.strip() for l in f if l.strip() and not l.startswith('#')]
if not lines:
return None
return lines.pop(0)
def send_to_icecast(track_path):
metadata = EasyID3(track_path)
title = metadata.get('title', ['Unknown'])[0]
artist = metadata.get('artist', ['Unknown'])[0]
url = f"http://{cfg['icecast']['user']}:{cfg['icecast']['password']}@{cfg['icecast']['host']}:{cfg['icecast']['port']}{cfg['icecast']['mount']}"
headers = {'Content-Type': 'audio/mpeg'}
with open(track_path, 'rb') as f:
requests.put(url, data=f, headers=headers)
print(f"Enviada a Icecast: {title} - {artist}")
def post_to_wordpress(title, artist, url):
data = {'title': f"{title} - {artist}", 'content': f"Escucha ahora: {title}", 'status': 'publish'}
auth = (cfg['wordpress']['user'], cfg['wordpress']['app_password'])
resp = requests.post(cfg['wordpress']['url'], json=data, auth=auth)
if resp.status_code == 201:
print("Publicación en WordPress creada")
else:
print(f"Error WordPress: {resp.status_code}")
def notify_telegram(message):
bot.send_message(chat_id=cfg['telegram']['chat_id'], text=message)
print("Notificación enviada a Telegram")
def play_track():
track = get_next_track()
if not track:
print("Playlist vacía, esperando nuevas pistas")
return
print(f"Reproduciendo: {track}")
proc = Popen(['cvlc', '--intf', 'dummy', '--play-and-exit', track])
proc.wait()
send_to_icecast(track)
meta = EasyID3(track)
title = meta.get('title', ['Unknown'])[0]
artist = meta.get('artist', ['Unknown'])[0]
stream_url = f"http://{cfg['icecast']['host']}:{cfg['icecast']['port']}{cfg['icecast']['mount']}"
post_to_wordpress(title, artist, stream_url)
notify_telegram(f"Ahora suena: {title} de {artist}")
schedule.every(5).seconds.do(play_track)
while True:
schedule.run_pending()
time.sleep(1)
Para lanzar el script como servicio usamos systemd. Creamos el archivo /etc/systemd/system/lamix-automator.service.
[Unit]
Description=Automatizador Radial para La MIX Radio
After=network.target
[Service]
User=lamix
WorkingDirectory=/home/lamix/automatizador
ExecStart=/home/lamix/automatizador/venv/bin/python automate.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
Activamos y verificamos el servicio.
systemctl daemon-reload
systemctl enable lamix-automator
systemctl start lamix-automator
systemctl status lamix-automator
Con esto el proceso corre 24/7, lee la playlist y publica cada pista automáticamente.
Errores que nos costaron tiempo
- Síntoma: Icecast devolvía 401 al subir la pista.
Causa: El usuario configurado carecía del permisosource.
Solución: Editamosicecast.xmly añadimossource-passwordcorrecto. - Síntoma: WordPress creaba borradores en vez de publicar.
Causa: Usábamos una Application Password con rol de colaborador.
Solución: Cambiamos el rol a autor y volvimos a generar la contraseña. - Síntoma: El bot de Telegram se quedaba sin responder después de la primera notificación.
Causa: No cerrábamos la sesión del bot y alcanzaba el límite de conexiones.
Solución: InstanciamosBotfuera del bucle y reutilizamos la misma conexión.
Cómo saber que funciona
Primero verificamos que VLC reproduce la pista sin errores.
Luego consultamos el panel de Icecast; la barra de listeners debe subir y el mensaje “source client connected” debe aparecer.
En WordPress buscamos la entrada recién creada y comprobamos que el enlace al stream sea clicable. Finalmente, revisamos el chat de Telegram; debe mostrarse el mensaje “Ahora suena: …”.
Si todos los puntos están activos, el automatizador está funcionando.
Qué sigue
Ahora que el automatizador está estable, planeamos añadir soporte para podcasts con n8n.
También queremos crear un dashboard en Grafana que muestre métricas de reproducciones en tiempo real.
La base está lista; solo queda integrar esas capas.
Este artículo muestra cómo Cómo Diseñar un Automatizador Radial 100% en Python usando herramientas que ya operamos en nuestro homelab.

