---
title: EPG-сервер (XMLTV)
url: https://doc.pstreamer.tv/ru/manual/epg_server.html
lang: ru
product: Perfect Streamer
version: 1.13.3.445
---

# EPG-сервер (XMLTV)

Отдельный встроенный HTTP-сервер Perfect Streamer выдаёт полный XMLTV для заданного набора каналов. Эндпоинт предназначен для middleware и плееров, которые хранят программу передач локально и обновляют её редко, файлом целиком — обычно один-два раза в сутки.

Сервер и его клиенты настраиваются в разделе **Configuration/EPG/EPG Server**.

## URL и аутентификация

Сервис обслуживается **отдельным** HTTP-сервером `epg-server` (не тем, что `/data/*`). По умолчанию он слушает порты `10444` (HTTP) и `10445` (HTTPS); порты и SSL настраиваются в разделе `/config/epg-server`.

Маршруты:

| URL | Content-Type | Поведение |
| --- | --- | --- |
| `GET /xmltv` | `text/xml` | При `Accept-Encoding: gzip` ответ сжимается на лету (`Content-Encoding: gzip`), иначе отдаётся как обычный XML. |
| `GET /xmltv.gz` | `application/octet-stream` | Всегда возвращает gzip-поток с `Content-Disposition: inline; filename="xmltv.xml.gz"` — удобно сохранять как файл. |

Поддерживаются три способа аутентификации:

- **HTTP Digest** (предпочтительно) — учётная запись из `/config/epg-server/login`.
- **Параметры в URL** — `?l=<login>&p=<password>` (синонимы: `login=…`, `password=…`).
- **Loopback** — запрос с `127.0.0.1` обрабатывается анонимно. Удобно для скриптов, развёрнутых на той же машине.

> **Предупреждение**
>
> Логин и пароль в URL попадают в access-логи reverse-proxy и в историю браузера. Для публичной раздачи XMLTV используйте HTTP Digest либо принимайте запросы только с приватных адресов.

## Доступ по channel-set

Каждая учётная запись `epg-server/login` привязана к **одному** `channel-set` из `/config/epg-channel-set`. EPG в ответе содержит только те каналы, которые входят в указанный набор. Это позволяет одной инсталляции PSS выдавать разный XMLTV разным операторам и middleware.

Базовая настройка в UI:

1. В **Configuration/EPG/EPG Channel Sets** создайте группу каналов и назначьте в неё нужные каналы у источников EPG.
2. В **Configuration/EPG/EPG Server Clients** заведите учётную запись и привяжите к ней созданную группу. Без привязки `channel-set` клиенту отдаётся пустой XMLTV.

Дополнительные ограничения для login:

- `ip-addr` — если задан и не wildcard, запрос с другого IP получит `403 Forbidden`.
- `limit-day` — Unix epoch-сек, после которого учётная запись перестаёт обслуживаться (`403 Forbidden`). Удобно для абонементной модели.
- `pause` — временно отключить login без удаления.

## Формат ответа

Тело ответа — XMLTV-документ с корнем `<tv>`. Структура соответствует общепринятой схеме [XMLTV DTD](http://wiki.xmltv.org/index.php/XMLTVFormat):

```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>
```

Заметки по полям:

- Атрибуты `source-info-name` и `source-info-url` корня `<tv>` заполняются из полей **EPG Source Name** и **EPG Source URL** в **Configuration/EPG/EPG Server**.
- Атрибуты `start` и `stop` оформлены в формате `YYYYMMDDhhmmss ±zone` (часовой пояс берётся из поля `time_zone` канала).
- В `<programme>` могут быть несколько `<title>`/`<desc>` для разных языков. Атрибут `lang` пустой, если в исходных данных EPG идентификатор языка не сопоставился со словарём (запись всё равно попадёт в выдачу).
- Каналы с конфликтующим `channel_id` (если один и тот же id пришёл из разных источников) показываются один раз, остальные источники пропускаются с предупреждением в логе сервера.
- В выдачу попадают только события с `stop_time >= now`.

## HTTP-заголовки

Сервер всегда отправляет:

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

Для `/xmltv` дополнительно — `Content-Encoding: gzip` при `Accept-Encoding: gzip` в запросе. Для `/xmltv.gz` — `Content-Disposition: inline; filename="xmltv.xml.gz"`.

Запрет клиентского кеша сделан намеренно: XMLTV меняется при каждом импорте EIT либо обновлении внешних XMLTV-источников, и плеер не должен держать устаревшие данные у себя бессрочно. Edge-кеш (nginx) при этом вполне допустим — см. раздел про производительность ниже.

## Серверный кеш и его сброс

Готовый XMLTV кешируется в памяти процесса PSS:

- По одной записи на каждый `channel-set`; внутри хранятся обе версии тела (raw и gzip) — повторные запросы с `Accept-Encoding: gzip` или `/xmltv.gz` не пересжимают данные.
- Запись помечается счётчиком `update-time`. Любое обновление EPG (импорт EIT, обновление XMLTV-источника) увеличивает счётчик, и при следующем запросе кеш перестраивается.

Принудительный сброс кеша:

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

Маршрут обслуживается на **admin-сервере** (порт `43971`/`43981`), не на epg-server. Тело — пустое; ответ — `200 OK` с JSON-конвертом.

## HTTP-коды ответа

| Код | Условие |
| --- | --- |
| `200 OK` | Запрос обработан. Тело — XMLTV-документ (возможно, пустой `<tv></tv>` при временном сбое БД). |
| `401 Unauthorized` | Ни Digest, ни параметры `l/p` не прошли проверку (для не loopback-запросов). |
| `403 Forbidden` | Login существует, но запрос не с разрешённого IP, либо истёк `limit-day`. |
| `404 Not Found` | Любой URL, кроме `/xmltv` и `/xmltv.gz`. |
| `405 Method Not Allowed` | Метод не GET. |

Тело ошибки — JSON-конверт фиксированного формата:

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

## Производительность и масштабирование

### Серверный кеш

Серверный кеш обслуживает повторные обращения к одному `channel-set` без обращения к SQLite — копированием готового тела.

Построение XMLTV «с нуля» (cache miss) дороже: для каждого канала делается отдельный SELECT по `channel_name`, для каждого события — по `event_text` и `event_rating`. Время построения ориентировочно:

| Размер выдачи | Cache hit | Cache miss (build) |
| --- | --- | --- |
| 100 каналов / сутки | десятки мс | ~0.5–1 с |
| 500 каналов / сутки | ~50 мс | 2–5 с |
| 1000+ каналов / неделя | ~100–300 мс | 5–15 с |

Для большинства middleware приемлемо запрашивать XMLTV раз в несколько часов или раз в сутки.

### Когда нужен внешний reverse-proxy (nginx)

В отличие от `/data/epg/channel` (короткие JSON-ответы), XMLTV — это один большой документ на `channel-set`, идеально подходящий для edge-кеша:

- **Десятки и сотни клиентов на один channel-set** — внутреннего кеша PSS обычно достаточно, если они опрашивают XMLTV раз в час–сутки.
- **Тысячи клиентов одновременно** — рекомендуется reverse-proxy с кешем. Выдача XMLTV-файла на сотни/тысячи запросов — это в первую очередь сетевая нагрузка (сотни КБ — единицы МБ на ответ), которую стоит снимать с PSS.
- **Географически распределённая раздача** — без CDN/edge-кеша не обойтись независимо от числа клиентов.

PSS отдаёт `Cache-Control: no-cache`, поэтому в nginx нужно явно указать nginx игнорировать заголовок upstream-сервера и держать собственный TTL.

### Пример конфигурации 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;
    }
}
```

### Пояснения и рекомендации

- **TTL ``proxy_cache_valid 200 30m``** — XMLTV редко меняется чаще получаса. Если синхронизация с источниками идёт раз в час и больше, можно поднимать до 1 часа и более; если важна свежесть после `POST /xmltv/reset-cache` — снижайте.
- **``proxy_cache_lock_timeout 60s``** — увеличен по сравнению с `/data/epg/channel` (где обычно 5 секунд), потому что построение XMLTV для большого `channel-set` занимает дольше.
- **Отдельный ``keys_zone``** — даже на крупной инсталляции уникальных ключей XMLTV единицы (по числу учётных записей × `channel-set`), 8 МБ хватит с запасом. `max_size` выбирается по объёму XMLTV, а не по числу ключей.
- **gzip на стороне nginx не нужен**: для `/xmltv.gz` ответ уже сжат, для `/xmltv` PSS сам ответит gzip-encoded при наличии `Accept-Encoding: gzip`.
- **HTTPS-терминация** на nginx даёт лучшую производительность при многих одновременных TLS-рукопожатиях.

## Связанные эндпоинты

- `POST /xmltv/reset-cache` — принудительный сброс серверного кеша XMLTV (на admin-сервере `43971`/`43981`).
- `POST /data/epg/update?s=<src_id>` — принудительное обновление внешнего XMLTV-источника; после успешного обновления серверный кеш XMLTV сбрасывается автоматически.
- `GET /data/epg/channel?…` — выдача EPG за сутки по одному каналу в JSON; см. отдельный раздел.

Полный перечень и подробное описание HTTP API приведены в `manual/http_data_api.txt`.
