Cymo diseyar un automatizador radial 100 en python para emisoras online

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 permiso source.

    Solución: Editamos icecast.xml y añadimos source-password correcto.
  • 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: Instanciamos Bot fuera 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.

Compartir

“Post relacionados”

🤖
Asistente de Fabio Martínez
En línea
¡Hola! 👋 Soy el asistente virtual de
Fabio Martínez
Técnico de Radio · Dev Web · Servidores

¿Cómo te llamás?