---
title: Servidor EPG (XMLTV)
url: https://doc.pstreamer.tv/es/manual/epg_server.html
lang: es
product: Perfect Streamer
version: 1.13.3.445
---

# Servidor EPG (XMLTV)

Un servidor HTTP integrado e independiente de Perfect Streamer devuelve el XMLTV completo para un conjunto dado de canales. El endpoint está pensado para middleware y reproductores que guardan la guía localmente y la actualizan con poca frecuencia, como archivo completo — normalmente una o dos veces al día.

El servidor y sus clientes se configuran en **Configuration/EPG/EPG Server**.

## URL y autenticación

El servicio lo ofrece un servidor HTTP **independiente** `epg-server` (no el que atiende `/data/*`). Por defecto escucha en los puertos `10444` (HTTP) y `10445` (HTTPS); los puertos y SSL se configuran en `/config/epg-server`.

Rutas:

| URL | Content-Type | Comportamiento |
| --- | --- | --- |
| `GET /xmltv` | `text/xml` | Con `Accept-Encoding: gzip` la respuesta se comprime al vuelo (`Content-Encoding: gzip`); en caso contrario, se entrega como XML sin comprimir. |
| `GET /xmltv.gz` | `application/octet-stream` | Siempre devuelve un flujo gzip con `Content-Disposition: inline; filename="xmltv.xml.gz"` — práctico para guardarlo como archivo. |

Se admiten tres métodos de autenticación:

- **HTTP Digest** (preferido) — cuenta de `/config/epg-server/login`.
- **Parámetros en la URL** — `?l=<login>&p=<password>` (sinónimos: `login=…`, `password=…`).
- **Loopback** — una solicitud desde `127.0.0.1` se trata como anónima. Útil para scripts implantados en la misma máquina.

> **Advertencia**
>
> Un login y una contraseña en la URL acaban en los access-logs del reverse-proxy y en el historial del navegador. Para distribución pública de XMLTV utilice HTTP Digest o acepte solicitudes únicamente desde direcciones privadas.

## Acceso por channel-set

Cada cuenta `epg-server/login` está vinculada a **un único** `channel-set` de `/config/epg-channel-set`. El EPG devuelto contiene solo los canales del grupo indicado. Esto permite que una misma instalación de PSS sirva XMLTV distintos a distintos operadores/middleware.

Configuración básica en la UI:

1. En **Configuration/EPG/EPG Channel Sets** cree un grupo de canales y asigne los canales deseados en las fuentes EPG.
2. En **Configuration/EPG/EPG Server Clients** cree una cuenta y vincúlela al grupo creado. Sin vincular un `channel-set`, el cliente recibe un XMLTV vacío.

Restricciones adicionales para un login:

- `ip-addr` — si está definido y no es un comodín, una solicitud desde otra IP recibe `403 Forbidden`.
- `limit-day` — Unix epoch en s, después del cual la cuenta deja de ser atendida (`403 Forbidden`). Útil para un modelo de suscripción.
- `pause` — desactivar temporalmente un login sin eliminarlo.

## Formato de la respuesta

El cuerpo de la respuesta es un documento XMLTV con raíz `<tv>`. La estructura sigue el esquema [XMLTV DTD](http://wiki.xmltv.org/index.php/XMLTVFormat) habitual:

```xml
<?xml version="1.0" encoding="utf-8"?>
<tv source-info-name="..." source-info-url="...">
  <channel id="channel.one">
    <display-name lang="en">Channel One</display-name>
    <icon src="https://.../channel-one.png"/>
  </channel>
  ...
  <programme start="20260504060000 +0300"
             stop ="20260504070000 +0300"
             channel="channel.one">
    <title lang="en">Morning News</title>
    <desc  lang="en">Overview of the day's events</desc>
    <rating system="MPAA"><value>PG</value></rating>
  </programme>
  ...
</tv>
```

Notas sobre los campos:

- Los atributos `source-info-name` y `source-info-url` de la raíz `<tv>` se completan con los campos **EPG Source Name** y **EPG Source URL** de **Configuration/EPG/EPG Server**.
- Los atributos `start` y `stop` siguen el formato `YYYYMMDDhhmmss ±zone` (la zona horaria se toma del campo `time_zone` del canal).
- Un `<programme>` puede contener varios `<title>`/`<desc>` en distintos idiomas. El atributo `lang` queda vacío cuando el identificador de idioma del EPG original no se pudo asignar al diccionario (la entrada igualmente aparece en la salida).
- Los canales con un `channel_id` en conflicto (si el mismo id procede de varias fuentes) se muestran una sola vez; las fuentes restantes se omiten con una advertencia en el log del servidor.
- Solo se incluyen en la salida los eventos con `stop_time >= now`.

## Encabezados HTTP

El servidor siempre envía:

```
Cache-Control: no-cache, no-store, must-revalidate
Pragma:        no-cache
Expires:       0
Connection:    close
```

Para `/xmltv` adicionalmente — `Content-Encoding: gzip` cuando `Accept-Encoding: gzip` está presente en la solicitud. Para `/xmltv.gz` — `Content-Disposition: inline; filename="xmltv.xml.gz"`.

El veto al caché del cliente es intencional: el XMLTV cambia con cada importación EIT o refresco de fuentes XMLTV externas, y el reproductor no debe conservar datos obsoletos de forma indefinida. Una caché edge (nginx), sin embargo, es perfectamente aceptable — véase la sección sobre rendimiento más abajo.

## Caché del servidor y su vaciado

El XMLTV listo se almacena en la memoria del proceso PSS:

- Una entrada por cada `channel-set`; se guardan ambas variantes del cuerpo (cruda y gzip) — las solicitudes repetidas con `Accept-Encoding: gzip` o `/xmltv.gz` no recomprimen los datos.
- Cada entrada lleva un contador `update-time`. Cualquier actualización de EPG (importación EIT, refresco de fuente XMLTV) incrementa el contador y la caché se reconstruye en la siguiente solicitud.

Vaciado forzado de la caché:

```
POST /xmltv/reset-cache
```

La ruta la atiende el **servidor admin** (puerto `43971`/`43981`), no el epg-server. Cuerpo vacío; la respuesta es `200 OK` con un envoltorio JSON.

## Códigos de respuesta HTTP

| Código | Condición |
| --- | --- |
| `200 OK` | Solicitud procesada. El cuerpo es un documento XMLTV (posiblemente un `<tv></tv>` vacío ante un fallo transitorio de la BD). |
| `401 Unauthorized` | Ni Digest ni los parámetros `l/p` superaron la validación (para solicitudes no loopback). |
| `403 Forbidden` | El login existe pero la solicitud no proviene de una IP permitida, o ha expirado `limit-day`. |
| `404 Not Found` | Cualquier URL distinta de `/xmltv` y `/xmltv.gz`. |
| `405 Method Not Allowed` | Método distinto de GET. |

El cuerpo del error es un envoltorio JSON de formato fijo:

```json
{"status": 401, "message": "Unauthorized"}
```

## Rendimiento y escalado

### Caché del servidor

La caché del servidor atiende las solicitudes repetidas sobre un mismo `channel-set` sin acceder a SQLite — copiando el cuerpo ya preparado.

Construir XMLTV «desde cero» (cache miss) es más costoso: por cada canal se hace un SELECT aparte por `channel_name`, y por cada evento por `event_text` y `event_rating`. Tiempos de construcción aproximados:

| Tamaño de salida | Cache hit | Cache miss (build) |
| --- | --- | --- |
| 100 canales / día | decenas de ms | ~0,5–1 s |
| 500 canales / día | ~50 ms | 2–5 s |
| 1000+ canales / semana | ~100–300 ms | 5–15 s |

Para la mayoría de los middleware es aceptable solicitar XMLTV cada pocas horas o una vez al día.

### Cuándo se necesita un reverse-proxy externo (nginx)

A diferencia de `/data/epg/channel` (respuestas JSON cortas), XMLTV es un único documento grande por `channel-set`, ideal para una caché edge:

- **Decenas o cientos de clientes por channel-set** — la caché interna de PSS suele bastar si consultan el XMLTV cada hora o cada día.
- **Miles de clientes simultáneos** — se recomienda un reverse-proxy con caché. Servir un archivo XMLTV a cientos/miles de solicitudes es, ante todo, una carga de red (cientos de KB a unos pocos MB por respuesta) que conviene quitar de PSS.
- **Distribución geográficamente repartida** — un CDN/caché edge es ineludible sin importar el número de clientes.

PSS devuelve `Cache-Control: no-cache`, por lo que hay que indicarle explícitamente a nginx que ignore la cabecera del upstream y mantenga su propio TTL.

### Ejemplo de configuración nginx

```nginx
# /etc/nginx/conf.d/pss-xmltv.conf

proxy_cache_path /var/cache/nginx/pss-xmltv
                 levels=1:2
                 keys_zone=pss_xmltv:8m
                 max_size=4g
                 inactive=2h
                 use_temp_path=off;

upstream pss_epg {
    server 127.0.0.1:10444;
    keepalive 16;
}

server {
    listen 80;
    # listen 443 ssl http2;     # SSL termination makes sense here
    server_name epg-files.example.com;

    # Do not enable gzip on: /xmltv.gz is already compressed, and /xmltv
    # arrives gzip-encoded from PSS — re-compressing is pointless.

    location ~ ^/xmltv(\.gz)?$ {
        proxy_pass http://pss_epg;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # PSS returns no-cache; force-cache it at the edge.
        proxy_ignore_headers Cache-Control Expires Set-Cookie;
        proxy_hide_header   Cache-Control;
        proxy_hide_header   Pragma;
        proxy_hide_header   Expires;

        # Cache key = full URL including query (login/password in /xmltv?l=...&p=...)
        # produce distinct keys for different accounts with different channel-sets.
        proxy_cache_key     "$scheme$host$request_uri";

        proxy_cache         pss_xmltv;
        proxy_cache_valid   200      30m;       # XMLTV changes infrequently
        proxy_cache_valid   401 403  1m;
        proxy_cache_lock           on;          # coalesce cache misses
        proxy_cache_lock_timeout   60s;         # XMLTV build may take seconds
        proxy_cache_use_stale      error timeout updating
                                   http_500 http_502 http_503;

        # Large buffer: XMLTV for a big channel-set can reach
        # several megabytes.
        proxy_buffering      on;
        proxy_buffers        16 256k;
        proxy_buffer_size    256k;
        proxy_busy_buffers_size 1m;

        # Let the client cache for a short period.
        add_header Cache-Control "public, max-age=600";
        add_header X-Cache-Status $upstream_cache_status always;
    }
}
```

### Explicaciones y recomendaciones

- **TTL ``proxy_cache_valid 200 30m``** — el XMLTV rara vez cambia con mayor frecuencia que cada media hora. Si la sincronización con las fuentes es horaria o menos frecuente, puede subirse a 1 hora o más; si importa la frescura tras `POST /xmltv/reset-cache`, redúzcalo.
- **``proxy_cache_lock_timeout 60s``** — incrementado respecto a `/data/epg/channel` (donde lo habitual son 5 segundos), porque construir el XMLTV de un channel-set grande lleva más tiempo.
- **Una ``keys_zone`` dedicada** — incluso en una instalación grande, las claves XMLTV únicas son pocas (número de cuentas × `channel-set`); 8 MB sobran. `max_size` se dimensiona por el volumen de XMLTV, no por el número de claves.
- **gzip en el lado de nginx no hace falta**: para `/xmltv.gz` la respuesta ya está comprimida y para `/xmltv` PSS responde directamente con gzip-encoded si está presente `Accept-Encoding: gzip`.
- **Terminación HTTPS** en nginx ofrece mejor rendimiento ante muchos handshakes TLS simultáneos.

## Endpoints relacionados

- `POST /xmltv/reset-cache` — vaciado forzado de la caché XMLTV del servidor (en el servidor admin `43971`/`43981`).
- `POST /data/epg/update?s=<src_id>` — actualización forzada de una fuente XMLTV externa; tras un refresco exitoso, la caché XMLTV del servidor se vacía automáticamente.
- `GET /data/epg/channel?…` — salida EPG en JSON para un canal durante un día; véase la sección aparte.

La lista completa y la descripción detallada de la API HTTP se encuentran en `manual/http_data_api.txt`.
