El EPG no es un campo, es una integración
Cuando alguien nuevo en operación de IPTV escucha "EPG" (Electronic Program Guide) piensa en algo simple: una lista de programas con hora de inicio y fin, mostrada debajo de cada canal. En la práctica, el EPG es el punto donde convergen todas las fuentes de captación de una cabecera —satélite, terrestre, feeds de terceros— cada una con su propio formato, su propia frecuencia de actualización y, casi siempre, su propia zona horaria de origen. La señal de video se valida con un número (bitrate, SNR, lock). El EPG se valida solo mirándolo, canal por canal, franja por franja.
En una cabecera con captación satelital (Canal+ Caribe) y terrestre (TDT Francia) con failover entre ambas, ya descrita en un post anterior sobre arquitectura multi-nodo, el EPG no puede tratarse como un adjunto de la señal. Es una pieza de infraestructura separada, con su propio pipeline, sus propios fallos y su propio monitoreo.
XMLTV: el formato que todo el mundo asume que entiende
La práctica estándar en TVHeadend, Stalker y la enorme mayoría de portales IPTV es consumir la guía en formato XMLTV: un XML con dos tipos de nodo, <channel> (definición del canal) y <programme> (un bloque de programación con inicio, fin y metadata). Un fragmento típico:
<channel id="canalplus.caribe">
<display-name>Canal+ Caribe</display-name>
</channel>
<programme start="20260805190000 -0400" stop="20260805200000 -0400" channel="canalplus.caribe">
<title lang="fr">Journal de 19h</title>
<desc lang="fr">Actualités du jour.</desc>
</programme>
El detalle que casi nadie lee la primera vez: el timestamp de start y stop incluye el offset de zona horaria (-0400) explícitamente. Eso es exactamente lo que hace que el formato funcione entre fuentes distintas — y exactamente lo que se rompe cuando un grabber o un script intermedio normaliza mal ese offset antes de reinyectarlo al sistema.
De dónde sale el EPG real: grabbers, no adivinanzas
El EPG no lo genera TVHeadend ni Flussonic por sí solos — alguien tiene que traerlo desde una fuente externa (el sitio del operador, un feed de terceros, o el propio broadcast si el operador incrusta EIT/EPG en el stream). En una operación real, tres patrones conviven:
- EPG embebido en la señal (EIT/DVB-SI). Muchos operadores satelitales y terrestres incluyen tablas EIT (Event Information Table) directamente en el multiplex DVB. TVHeadend puede extraerlas nativamente sin depender de nada externo — es la fuente más confiable cuando existe, porque viaja sincronizada con el mismo mux que ya se está captando.
- Grabbers dedicados. Cuando el operador no embebe EIT completo (o lo embebe con calidad pobre — títulos truncados, sin descripción), un grabber externo descarga la guía desde el sitio del operador o de un agregador y la convierte a XMLTV. Estos scripts corren en cron, típicamente una o dos veces al día, porque la programación no cambia cada minuto pero sí se ajusta con frecuencia (retrasos de partidos en vivo, cambios de última hora).
- Feeds de terceros pagos. Para mercados donde ni el EIT ni un grabber casero cubren bien la programación, existen proveedores de EPG por suscripción que entregan XMLTV consolidado por región. Se usan como respaldo cuando la fuente primaria es floja, nunca como única fuente si el operador sí expone EIT decente — el EIT del propio operador siempre va a estar más sincronizado con cambios de última hora que un feed externo.
EIT y grabber externo casi nunca deben mezclarse para el mismo canal sin una regla de prioridad clara. Si ambos alimentan el mismo channel id en TVHeadend sin coordinación, se producen programas duplicados o solapados en la guía — visualmente esto se traduce en un STB mostrando dos "ahora" distintos para el mismo canal.
El problema real: mapear channel ID entre mundos que no se hablan
Cada fuente de EPG nombra sus canales a su manera. El EIT embebido identifica el canal por su service_id dentro del mux. Un grabber externo suele usar un slug propio (canalplus.caribe, cplus-caribe-fr, lo que el mantenedor del script haya elegido). El feed de un proveedor tercero trae su propio catálogo de IDs, distinto a ambos. Ninguno de los tres, por defecto, coincide con el channel number lógico que usa el portal Stalker para mostrarle el canal al STB.
El trabajo real de integración de EPG es, en el fondo, mantener una tabla de mapeo estable entre estos identificadores y el canal lógico interno. Cuando esa tabla no existe o se deja desactualizada:
- Un canal puede quedarse sin programación visible aunque el XMLTV se esté descargando correctamente — el dato llega, pero no se asocia a nada porque el
channel iddel feed no matchea ningún canal conocido. - Un cambio de proveedor de grabber (porque el anterior dejó de mantenerse, algo que pasa con scripts comunitarios) rompe silenciosamente el mapeo si el nuevo grabber usa una convención de IDs distinta y nadie actualiza la tabla.
La regla operativa que aplicamos: el mapeo vive en un solo lugar versionado (no en la configuración dispersa de cada herramienta), y cualquier cambio de fuente de EPG para un canal pasa primero por actualizar esa tabla, después por la configuración de TVHeadend o del portal.
Zona horaria: el bug que no se ve hasta que alguien se queja
En el post sobre arquitectura multi-nodo mencionamos el ajuste de zona horaria por canal como "el detalle que casi nadie maneja". Aquí es donde ese detalle se vuelve mecánico y hay que ejecutarlo bien:
- La fuente trae su propia hora local. Un feed de TDT Francia entrega horarios en hora de Europa Central; un feed satelital para audiencia caribeña puede entregar en UTC o en la hora local del operador de origen. Ninguno de los dos coincide, por defecto, con la zona horaria del televidente final en Estados Unidos.
- El offset del XMLTV es la fuente de verdad, no una suposición. Como el formato incluye el offset explícito en cada timestamp, la conversión correcta es tomar ese offset literal y convertirlo a la zona horaria de destino — nunca asumir que "el feed ya viene en la hora que necesito" sin verificarlo contra el offset real del XML.
- DST (horario de verano) rompe conversiones fijas. Un offset de conversión hardcodeado (por ejemplo, "restar 5 horas siempre") funciona la mitad del año y falla la otra mitad, porque Europa y Estados Unidos no cambian de horario de verano el mismo fin de semana. El síntoma clásico: durante dos o tres semanas al año, el EPG de un canal aparece corrido una hora exacta, hasta que alguien reporta que "el programa de las 8 empezó a las 7".
La conversión de zona horaria del EPG nunca debe hacerse con un offset fijo calculado a mano. Hay que resolverla con una librería de zonas horarias con reglas DST reales (tipo tz database / IANA), aplicada por canal según la zona horaria de origen del feed y la zona horaria de destino de la audiencia — que pueden no coincidir con la ubicación física del servidor que procesa el EPG.
Frecuencia de actualización y ventanas de sincronización
No toda la guía necesita actualizarse con la misma urgencia. En la práctica conviene separar:
- Actualización de programación futura (mañana, la semana): puede correr una o dos veces al día sin problema, porque los cambios de última hora en programación a varios días vista son raros.
- Actualización del "ahora/después" (now/next): si el portal o el STB dependen de este dato para el overlay de "en pantalla ahora", conviene refrescarlo con más frecuencia (cada 15-30 minutos), especialmente en canales con programación en vivo donde los horarios se corren (deportes, noticias con cobertura extendida).
- Ventana de retención para DVR/catch-up. Si la plataforma ofrece grabación o catch-up, el EPG histórico tiene que quedar alineado con la ventana real de retención de Flussonic o TVHeadend — un catch-up que ofrece "ver desde las 6pm de ayer" pero cuyo EPG solo retiene 12 horas hacia atrás genera una interfaz que promete contenido que ya no existe.
Sincronizar estas tres capas con la misma cadencia (por ejemplo, regenerar todo el XMLTV completo cada 15 minutos) desperdicia ciclos y aumenta la ventana de riesgo de que un grabber falle a mitad de descarga y deje el portal sin "ahora/después" durante ese ciclo. Separar la cadencia por tipo de dato reduce ese riesgo.
EPG en el portal Stalker: lo que el STB realmente necesita
Los STB MAG que consumen un portal Stalker no piden el XMLTV completo — el portal lo procesa y expone un API propio que el STB consulta bajo demanda: programación del canal actual, del canal siguiente en el zapeo, y la ventana de tiempo que el usuario tenga abierta en la guía. Dos consecuencias prácticas:
- Si el portal no logró importar el XMLTV a tiempo (por un fallo de red del grabber, por ejemplo), el STB no muestra "sin datos" de forma elegante en todos los firmwares — algunos modelos MAG muestran una guía vacía sin error visible, lo cual parece un problema del STB cuando en realidad es un fallo de sincronización aguas arriba.
- El mapeo channel ID → canal lógico descrito antes tiene que coincidir exactamente con el número de canal que el portal le asigna al STB. Un desfase aquí no rompe el video (el canal sigue reproduciendo), solo la guía — que es precisamente el tipo de fallo que un cliente reporta como "no funciona" sin que el equipo de soporte entienda de inmediato qué capa está fallando.
Fallos más comunes en producción
1. "Ahora/después" congelado
El grabber se ejecuta, pero silenciosamente falla a mitad de descarga (timeout de red, cambio de estructura HTML en el sitio origen si el grabber hace scraping) y deja el XMLTV anterior sin sobrescribir. El síntoma es una guía que parece funcionar pero muestra la programación de ayer. La mitigación real es comparar el timestamp de generación del XMLTV contra la hora actual y alertar si supera un umbral, no solo revisar si el cron "corrió" — un cron que corre y falla adentro sigue reportando éxito si nadie valida el contenido resultante.
2. Desfase de una hora durante transición DST
Ya descrito arriba: offsets hardcodeados que ignoran las reglas reales de horario de verano. Se detecta comparando, dos semanas antes de cada transición DST relevante (tanto la de origen como la de destino), un programa conocido contra la hora real de transmisión.
3. Programas duplicados o solapados
Cuando EIT embebido y grabber externo alimentan el mismo canal sin regla de prioridad, ambos escriben bloques de programación para la misma franja horaria. El portal o TVHeadend terminan mostrando dos programas simultáneos superpuestos en la guía. La solución no es técnica sino de proceso: una sola fuente autoritativa por canal, documentada, sin fallback silencioso a una segunda fuente que también esté activa.
4. Canal sin programación tras cambio de proveedor de grabber
Un grabber comunitario deja de mantenerse o cambia su convención de channel id, y el mapeo interno no se actualiza. El canal sigue transmitiendo video con normalidad; el EPG para ese canal específico simplemente desaparece. Vale la pena monitorear activamente "canales con cero programas en las próximas N horas" como una alerta separada del monitoreo de bitrate — son fallas completamente independientes.
Checklist antes de dar por lista la capa de EPG
- Existe una tabla de mapeo channel ID ↔ canal lógico, versionada y centralizada, no dispersa entre TVHeadend, grabbers y portal.
- Hay una regla de prioridad explícita quién es la fuente autoritativa de EPG por canal cuando existe más de una fuente disponible (EIT vs. grabber vs. feed de terceros).
- La conversión de zona horaria usa reglas DST reales (IANA/tz database) por canal, no offsets fijos calculados a mano.
- La frecuencia de actualización está separada por tipo de dato: programación futura, now/next, y ventana de catch-up.
- Hay alertas sobre el contenido del XMLTV generado (antigüedad, canales sin programas próximos), no solo sobre si el cron del grabber terminó sin error.
- La ventana de EPG histórico coincide con la ventana real de retención de DVR/catch-up del sistema.
Cierre
El EPG rara vez es la razón por la que se pierde un cliente en el primer mes, pero es una de las razones más comunes por las que un cliente se queja después de que "todo funciona". La señal de video es binaria: está o no está. La guía de programación falla en grados —un canal sin datos, un desfase de una hora, un duplicado— y esos grados son precisamente los que un monitoreo mal diseñado no detecta. Tratarla como una integración propia, con su propio pipeline y su propio monitoreo, es lo que separa una cabecera que "se ve bien en la demo" de una que sigue funcionando bien seis meses después, cuando cambia de horario de verano o el operador reubica un canal sin avisar.
Si estás evaluando montar o auditar la capa de EPG de una plataforma IPTV/OTT, hablemos.