Automatiza la Gestión de Podcasts desde tu Servidor y ahorra horas de trabajo
En este artículo vemos cómo Automatiza la Gestión de Podcasts desde tu Servidor cuando la rutina nos obliga a publicar episodios a cualquier hora.
El problema apareció una madrugada mientras subíamos un nuevo mix a la página y el proceso manual nos tomó dos horas.
Allí mismo decidimos crear una cadena de scripts que hiciera todo sin intervención.
Problema concreto al no automatizar la gestión
Si no automatizamos la gestión, cada nuevo episodio requiere subir el MP3 a un host, generar el RSS, actualizar la página de WordPress, y avisar a la audiencia en Telegram.
El error típico es olvidar actualizar el feed, lo que hace que los oyentes no reciban el episodio.
Además, el proceso manual genera inconsistencias en los metadatos y en la numeración de los episodios.
Cuando la radio funciona 24/7, esos fallos aparecen justo cuando más impacto tiene el programa.
Qué necesitas antes de empezar
- Una VM en Proxmox (por ejemplo, VM 111) con Python 3.11 y acceso a Internet.
- Una cuenta en un storage S3 compatible (Amazon, Wasabi o MinIO).
- Credenciales de API para WordPress (usuario, aplicación y clave).
- Bot de Telegram y token de bot.
- Git instalado para versionar los scripts.
- Editor de texto (vim, nano o VS Code).
Paso a paso para Automatiza la Gestión de Podcasts desde tu Servidor
Primero preparamos el entorno. Instalamos los paquetes que usaremos y creamos un directorio para los episodios.
sudo apt update && sudo apt install -y python3-pip ffmpeg
python3 -m venv ~/podcast-env
source ~/podcast-env/bin/activate
pip install boto3 requests python-telegram-bot python-wordpress-xmlrpc
mkdir -p /home/lamix/podcasts/incoming
mkdir -p /home/lamix/podcasts/processed
El script principal recibe el archivo MP3, lo sube a S3 y genera el RSS actualizado.
import os
import json
import boto3
import datetime
from pathlib import Path
from xml.etree.ElementTree import Element, SubElement, tostring
S3_BUCKET = os.getenv('S3_BUCKET')
S3_CLIENT = boto3.client('s3',
aws_access_key_id=os.getenv('AWS_KEY'),
aws_secret_access_key=os.getenv('AWS_SECRET'))
def upload_to_s3(file_path):
file_name = Path(file_path).name
s3_key = f'podcasts/{file_name}'
S3_CLIENT.upload_file(file_path, S3_BUCKET, s3_key, ExtraArgs={'ContentType': 'audio/mpeg'})
url = f'https://{S3_BUCKET}.s3.amazonaws.com/{s3_key}'
return url
def build_rss(episode_url, title, description, pub_date):
rss = Element('rss', version='2.0')
channel = SubElement(rss, 'channel')
SubElement(channel, 'title').text = 'La MIX Radio Podcast'
SubElement(channel, 'link').text = 'https://lamixradio.com/podcast'
SubElement(channel, 'description').text = 'Mixes y entrevistas de La MIX Radio.'
item = SubElement(channel, 'item')
SubElement(item, 'title').text = title
SubElement(item, 'description').text = description
SubElement(item, 'enclosure', url=episode_url, length='0', type='audio/mpeg')
SubElement(item, 'pubDate').text = pub_date.strftime('%a, %d %b %Y %H:%M:%S %z')
return tostring(rss, encoding='utf-8')
Después de subir, enviamos el enlace al blog usando la API REST de WordPress.
import requests
WP_URL = os.getenv('WP_URL')
WP_USER = os.getenv('WP_USER')
WP_APP_PASS = os.getenv('WP_APP_PASS')
def post_to_wordpress(title, content):
endpoint = f'{WP_URL}/wp-json/wp/v2/posts'
auth = (WP_USER, WP_APP_PASS)
data = {'title': title, 'content': content, 'status': 'publish', 'categories': [5] # id de la categoría Podcast}
r = requests.post(endpoint, auth=auth, json=data)
r.raise_for_status()
return r.json()['link']
Finalmente, notificamos a la audiencia mediante Telegram.
from telegram import Bot
TELEGRAM_TOKEN = os.getenv('TELEGRAM_TOKEN')
CHAT_ID = os.getenv('TELEGRAM_CHAT_ID')
bot = Bot(token=TELEGRAM_TOKEN)
def send_telegram(message):
bot.send_message(chat_id=CHAT_ID, text=message)
El script completo combina los pasos anteriores. Lo guardamos como publish_podcast.py y lo ejecutamos con un cron cada vez que se detecta un archivo nuevo.
#!/usr/bin/env python3
import time
import os
from pathlib import Path
INCOMING = Path('/home/lamix/podcasts/incoming')
PROCESSED = Path('/home/lamix/podcasts/processed')
while True:
for mp3 in INCOMING.glob('*.mp3'):
title = mp3.stem.replace('_', ' ')
description = f'Episodio {title} de La MIX Radio.'
pub_date = datetime.datetime.now(datetime.timezone.utc)
url = upload_to_s3(str(mp3))
rss_content = build_rss(url, title, description, pub_date)
rss_path = PROCESSED / f'{mp3.stem}.xml'
rss_path.write_bytes(rss_content)
wp_link = post_to_wordpress(title, f'Escucha aquí: {url}')
send_telegram(f'Nuevo podcast publicado: {wp_link}')
mp3.rename(PROCESSED / mp3.name)
time.sleep(60) # revisa cada minuto
Con Automatiza la Gestión de Podcasts desde tu Servidor conseguimos que el proceso completo dure menos de dos minutos.
Errores que nos costaron tiempo
1. Sintoma: El script fallaba al subir el archivo y devolvía “403 Forbidden”.
Causa: La política del bucket S3 no permitía escritura desde la IP del host.
Solución: Añadimos la regla de permiso “s3:PutObject” a la política y habilitamos VPC endpoint en Proxmox.
2. Sintoma: El RSS generado tenía fechas sin zona horaria y los lectores mostraban “fecha desconocida”.
Causa: Usamos datetime.now() sin zona.
Solución: Cambiamos a datetime.now(datetime.timezone.utc) y formateamos con “%z”.
3. Sintoma: WordPress devolvía “401 Unauthorized” al crear el post.
Causa: El token de aplicación expiró por cambio de contraseña.
Solución: Regeneramos el “Application Password” y guardamos la nueva cadena en una variable de entorno segura.
Cómo saber que funciona
Una vez ejecutado el script, revisamos el bucket S3 y comprobamos que el MP3 está allí y accesible vía HTTPS. Después, abrimos el enlace RSS en un lector como Feedly; debe aparecer el nuevo ítem con la fecha correcta.
Por último, verificamos que el post aparece en WordPress y que el mensaje de Telegram llegó al canal.
Qué sigue
Ahora que la cadena está operativa, añadimos una capa de monitoreo con n8n para registrar cada publicación en una hoja de cálculo Google.
También planeamos generar miniaturas automáticas con FFmpeg y almacenarlas en S3 para que el feed incluya imágenes.

