Локальная ТВ-программа через CCcam/OScam: настройка EPG
Каналы открываются, картинка есть, но local tv guide пустой — знакомая ситуация после первичной настройки шаринга. Ресивер получает дескремблированный поток, но программа передач либо отсутствует, либо на немецком, либо показывает данные трёхнедельной давности. Это не баг CCcam, это архитектурная особенность DVB-стандарта, которую нужно обойти через внешний XMLTV-источник.
В этом гайде — полная цепочка: от понимания почему EPG пустой до автоматизации обновлений по cron с примерами реальных конфигов.
Что такое EPG и почему он не работает из коробки при использовании CCcam/OScam
EPG в DVB-потоке vs внешний XMLTV
В DVB-транспортном потоке программа передач передаётся через таблицы EIT (Event Information Table) — это часть стандарта SI (Service Information). Ресивер автоматически собирает EIT при зеппинге по каналам и сохраняет в кэш epg.dat. Всё хорошо, когда EIT есть и он незашифрован.
XMLTV — это внешний XML-формат с программой передач, который ресивер загружает отдельно. Его описывает DTD-спецификация xmltv.dtd. Источниками XMLTV могут быть локальные файлы, URL на веб-сервере или вывод grabber-скрипта. Плагин EPGImport (Enigma2) или CrossEPG парсит этот файл и добавляет данные в epg.dat поверх того, что набрал из DVB-потока.
Почему провайдерский EPG часто отсутствует или урезан
Вещатели передают EIT по-разному. Крупные европейские пакеты (Astra 19.2°E, Hotbird 13°E) честно передают present/following и 7-дневный schedule EIT. Региональные и нишевые каналы ограничиваются present/following — это только текущая и следующая передача, без расписания на неделю.
Ещё хуже — часть платформ шифрует EIT так же, как шифрует видео. CAM-эмулятор не расшифровывает таблицы SI, он работает только с ECM/EMM пакетами. Поэтому даже при полностью рабочем CCcam/OScam EIT-данные могут быть недоступны или приходить только на языке оригинального вещания.
Роль CAM-эмулятора: расшифровка ≠ метаданные канала
CCcam и OScam — это системы условного доступа (CAS). Их задача: получить Control Word от сервера шаринга, расшифровать поток с помощью этого CW и отдать декодеру чистый транспортный поток. Всё. Никаких метаданных, никакого EPG, никаких SI-таблиц они не трогают.
Oscam.server, oscam.services, CCcam.cfg — эти конфиги управляют только тем, как эмулятор находит ключи. Путь /etc/CCcam.cfg содержит адреса серверов, порты, логины. Путь /etc/oscam/ содержит файлы oscam.conf, oscam.server, oscam.services. Ни один из них не имеет отношения к EPG. Если хочешь local tv guide с нормальным расписанием — нужно настраивать отдельно.
Источники локального EPG: где брать XMLTV-фиды
Открытые grabber-скрипты (WebGrab+Plus, tv_grab_*)
WebGrab+Plus — это open-source Java-инструмент, который ходит на сайты телепрограмм, парсит расписания и генерирует XMLTV-файл. Конфигурация через siteini-файлы — каждый .ini описывает как парсить конкретный сайт. На GitHub можно найти репозиторий с сотнями готовых siteini для разных стран.
Набор tv_grab_* — это Perl-скрипты из проекта XMLTV (не путать с форматом). tv_grab_ru, tv_grab_de_tvtoday и подобные работают через команду tv_grab_XX --output /tmp/epg.xml. Устанавливаются через пакетный менеджер на Linux или вручную на ресивере.
На Enigma2-ресиверах с busybox можно запустить WebGrab+Plus через Java (если есть) или использовать готовые Python-скрипты. На роутере с OpenWrt или на NAS — проще, там нормальная среда.
Прямой парсинг с сайтов телепрограмм
Если готового grabber-скрипта для нужного сайта нет — пишешь сам. Структура простая: тянешь HTML через requests или wget, парсишь через BeautifulSoup или sed/awk, собираешь XMLTV-файл вручную. Вот минимальный валидный XMLTV:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tv SYSTEM "xmltv.dtd">
<tv generator-info-name="custom-grabber">
<channel id="perviy.channel">
<display-name lang="ru">Первый канал</display-name>
</channel>
<programme start="20260527180000 +0300" stop="20260527190000 +0300" channel="perviy.channel">
<title lang="ru">Новости</title>
<desc lang="ru">Вечерний выпуск новостей</desc>
</programme>
</tv>
Обязательные поля: channel (атрибут id + display-name), programme (атрибуты start, stop, channel + дочерний тег title). Время — строго YYYYMMDDHHMMSS с часовым поясом через пробел. Без часового пояса ресивер будет интерпретировать как UTC и сдвинет всё на 3 часа для московского времени.
Самостоятельный сбор через cron и xmltv.dtd
Если источник обновляется ежедневно — достаточно cron-задачи, которая запускает grabber и кладёт результат в /tmp/epg.xml или сразу в /etc/epgimport/. Валидацию делаем через xmllint:
xmllint --noout --dtdvalid /usr/share/xmltv/xmltv.dtd /tmp/epg.xml
Если xmllint молчит — файл валидный. Если выдаёт ошибки — смотри на encoding (должен быть UTF-8, не Windows-1251) и на корректность времени. Кодировка Windows-1251 из некоторых российских источников превращает кириллицу в кракозябры — конвертируй через iconv: iconv -f cp1251 -t utf-8 epg_raw.xml > epg.xml.
Настройка EPG на популярных образах ресиверов
Enigma2 (OpenATV, OpenPLi): EPGImport плагин
EPGImport — стандартный плагин для всех Enigma2-образов. Устанавливается через Softcam Manager или вручную через opkg: opkg install enigma2-plugin-extensions-epgimport. После установки появляется в Menu → Plugins.
Плагин читает два типа файлов: sources.xml (откуда брать XMLTV) и channels.xml (как маппить channel id на каналы ресивера). Оба лежат в /etc/epgimport/ или ~/.epgimport/ на более новых образах.
Пути конфигов: /etc/epgimport/ и sources.xml
Пример sources.xml с локальным источником:
<sources>
<source type="gen_xmltv" nocheck="1">
<description>Локальный EPG</description>
<url>http://192.168.1.100/epg/ru_epg.xml.gz</url>
<channels>/etc/epgimport/ru_channels.xml</channels>
</source>
</sources>
Атрибут nocheck="1" отключает проверку целостности файла — полезно когда источник обновляется часто и нет контрольных сумм. URL может быть как http, так и file:///tmp/epg.xml для локального файла на ресивере.
После изменения sources.xml — перезагрузка плагина через Blue Button → EPGImport → Update now, или через web-интерфейс: wget -q 'http://STB_IP/web/epgrefresh' -O /dev/null.
DreamOS: CrossEPG и его cache в /media/hdd/epg.dat
На DreamOS (Dreambox DM900, DM920) стандартный инструмент — CrossEPG. Конфиг в /etc/crossepg/, кэш по умолчанию в /media/hdd/epg.dat. Если HDD не подключён — кэш пишется в /etc/enigma2/epg.dat и может переполнить flash-память.
CrossEPG имеет собственный формат провайдеров (.list файлы), но умеет импортировать XMLTV. Настройка через меню CrossEPG Setup → XMLTV provider. Важно: CrossEPG и EPGImport не должны работать одновременно — они пишут в один epg.dat и перезаписывают данные друг друга. Выбирай один инструмент.
VU+ / Octagon: настройка через CrossEPG_setup
На VU+ Solo 4K, Octagon SF8008 под OpenATV работают оба плагина. CrossEPG_setup вызывается из меню ресивера, там же настраивается расписание обновлений. На VU+ кэш EPG лучше перенести на подключённый HDD через символическую ссылку:
rm /etc/enigma2/epg.dat
ln -s /media/hdd/epg.dat /etc/enigma2/epg.dat
На ресиверах с маленьким flash типа Zgemma H1 (256MB) это не опция, а необходимость — EPG для 500+ каналов на 14 дней занимает 60-100MB и просто не влезает в flash.
Привязка каналов: маппинг XMLTV channel id к транспондерам
Формат service reference (sref) в Enigma2
Service reference — это строка из 10 полей, разделённых двоеточием. Пример: 1:0:1:283D:3FB:1:C00000:0:0:0. Структура:
- Поле 1: тип (1 = TV)
- Поле 2: флаги (0 = обычный канал)
- Поле 3: тип сервиса (1 = SD TV, 19 = HD TV, 25 = H.265)
- Поле 4: SID (Service ID) в hex
- Поле 5: TSID (Transport Stream ID) в hex
- Поле 6: ONID (Original Network ID) в hex
- Поле 7: NAMESPACE в hex
- Поля 8-10: всегда 0 для реальных каналов
Откуда брать значения: открываешь /etc/enigma2/lamedb в текстовом редакторе (SSH на ресивер, cat /etc/enigma2/lamedb | head -100), находишь нужный канал по имени, берёшь его sref. Или через Channel List Editor (dreamboxedit, E2ChannelEditor) — там всё в GUI.
Channels.xml: структура и пример
channels.xml маппит строковый id из XMLTV на sref Enigma2:
<?xml version="1.0" encoding="UTF-8"?>
<channels>
<channel id="perviy.channel">1:0:1:283D:3FB:1:C00000:0:0:0</channel>
<channel id="rossiya1">1:0:19:2BC1:3FB:1:C00000:0:0:0</channel>
<channel id="ntv.ru">1:0:1:1F4:7D0:1:C00000:0:0:0</channel>
</channels>
Атрибут id должен точно совпадать со значением атрибута id тега channel в XMLTV-файле. Регистр важен: "Perviy.Channel" и "perviy.channel" — разные значения. Ошибка здесь = EPG для канала не появится, и никакого предупреждения не будет.
Автоматический маппинг по имени канала vs ручной
EPGImport умеет пытаться маппить по имени канала из XMLTV на имя в списке каналов ресивера. Работает плохо. Проблемы: регистр ("НТВ" vs "NTV"), суффиксы (HD, +2, +4, regional), языковые варианты названий. Одна платформа может иметь "Первый HD" и "Первый SD" с разными sref — автомаппинг угадает только один.
Ручной маппинг через channels.xml — единственный надёжный способ. Да, это занимает время один раз. Зато работает стабильно. Для regional/HD версий каждому варианту нужна отдельная строка в channels.xml с уникальным id в XMLTV и уникальным sref.
Если канал сменил частоту или транспондер после feed update — его sref изменится. lamedb обновится автоматически после сканирования, но channels.xml останется со старым значением. Диагностика: берёшь старый sref из channels.xml, ищешь его в новом lamedb — если не находишь, канал переехал, нужно обновить channels.xml вручную.
Решение типичных проблем с локальным EPG
EPG обновился, но в гайде пусто
Первое что смотришь — лог: tail -f /tmp/epgimport.log. Там обычно видно что пошло не так: файл не скачался, XML не распарсился, channel id не найден в channels.xml.
Второй вариант — права доступа. Если /etc/epgimport/ принадлежит root:root с правами 700, а плагин запускается под другим пользователем — файлы не читаются. Исправление: chmod 755 /etc/epgimport/ && chmod 644 /etc/epgimport/*.xml.
Третий вариант — epg.dat повреждён или слишком большой. Удали его (rm /etc/enigma2/epg.dat), перезапусти GUI (init 4 && init 3 через SSH), потом запусти импорт заново.
Время передач смещено на 1-3 часа
Классика: часовой пояс ресивера не совпадает с часовым поясом в XMLTV-файле. Проверяешь часовой пояс ресивера командой date в SSH — смотришь на UTC offset. Потом смотришь в XMLTV-файл на атрибуты start первых программ: там должно быть, например, start="20260527180000 +0300".
Если в XMLTV время без часового пояса — ресивер считает его UTC. Для московского времени это сдвиг +3 часа. Решение: либо фиксишь grabber чтобы добавлял часовой пояс, либо конвертируешь через sed: sed 's/\([0-9]\{14\}\)"/\1 +0300"/g' epg_raw.xml > epg.xml.
EPG показывает старые данные после reboot
После перезагрузки ресивер загружает epg.dat из flash или HDD. Если файл устарел, а автообновление не настроено — видишь вчерашнее расписание. Настраиваешь EPGRefresh плагин: добавляешь временное окно для обновления (03:00-05:00), включаешь опцию "Wake up from standby".
Но если ресивер уходит в deep standby (полное отключение питания, не мягкий standby) — он не просыпается по таймеру. В настройках питания ресивера нужно выбрать "Pseudo standby" или "Network standby" вместо deep standby. Иначе обновление по расписанию не сработает.
Часть каналов с EPG, часть — нет
Смотришь в лог: EPGImport покажет для каких channel id нашёл маппинг, для каких нет. Причины: канала нет в channels.xml, или id в channels.xml не совпадает с id в XMLTV, или канал есть в списке ресивера но его sref изменился.
Двухтюнерные ресиверы иногда имеют разные списки каналов для тюнера A и тюнера B — особенно если один настроен на Astra, второй на Hotbird. В lamedb будут два entry для "похожих" каналов с разными sref. Channels.xml должен содержать оба.
Memory leak при больших XMLTV (>50MB)
EPGImport написан на Python, парсит XML через minidom или ElementTree. Файлы больше 50MB могут вызвать нехватку RAM на ресиверах с 512MB памяти — парсер загружает весь XML в память. Симптом: плагин зависает или падает без сообщения об ошибке.
Решение — разбивать XMLTV на бакеты по группам каналов: отдельный файл для новостных, отдельный для спортивных, отдельный для региональных. В sources.xml добавляешь несколько source-записей. Каждый файл обрабатывается последовательно, пиковая нагрузка на RAM снижается. Также используй .gz сжатие — EPGImport умеет распаковывать на лету, файл 10MB вместо 50MB при той же нагрузке на диск.
Автоматизация: cron-задачи и обновление EPG по расписанию
Cron на ресивере: добавление через /etc/cron/crontabs/root
На Enigma2-ресиверах crontabs root находится в /etc/cron/crontabs/root. Редактируешь через SSH:
# Обновление EPG каждый день в 4:00
0 4 * * * /usr/bin/python /usr/lib/enigma2/python/Plugins/Extensions/EPGImport/EPGImport.py
# Обновление grabber-скрипта каждый день в 3:30
30 3 * * * /usr/bin/python /home/root/grabber/update_epg.py
После редактирования — перезапуск cron: /etc/init.d/cron restart или kill -HUP $(pidof crond). Проверка что задача добавлена: crontab -l.
Trade-off который стоит понимать: частое обновление (каждые 4 часа) даёт свежие данные, но каждый цикл записи изнашивает flash-память ресивера. На ресиверах с eMMC это менее критично, на старых ресиверах с NAND flash — лучше ограничиться одним обновлением в сутки. Если EPG хранится на USB/HDD — ограничений нет.
EPGRefresh плагин: тонкая настройка интервалов
EPGRefresh работает иначе чем EPGImport — он переключается по каналам и собирает EIT из DVB-потока, а не импортирует XMLTV. Используется в связке: EPGRefresh собирает EIT там где он есть, EPGImport добавляет XMLTV для каналов где EIT отсутствует или неполный.
Настройка EPGRefresh: Menu → Plugins → EPGRefresh → Setup. Параметры: Begin time (03:00), End time (05:00), Refresh interval (24 часов). Список каналов для сканирования — добавляешь только те которые реально нужны, иначе зеппинг займёт часы.
Standby-обновление чтобы не мешало просмотру
Запускать EPGRefresh или EPGImport во время просмотра — плохая идея на однотюнерных ресиверах: зеппинг прерывает текущий канал. Поэтому настраиваешь обновление в ночное окно 03:00-05:00 с пробуждением из standby.
Опция "forceNightMode" в EPGRefresh заставляет плагин ждать пока ресивер уйдёт в standby перед запуском. Если ресивер смотрят допоздна — обновление откладывается, но не пропускается. Как уже написал выше — deep standby убивает эту логику, нужен pseudo standby.
Один XMLTV-файл для нескольких ресиверов в домашней сети: поднимаешь lighttpd или nginx на роутере (OpenWrt) или NAS, кладёшь туда epg.xml.gz, в sources.xml каждого ресивера указываешь http://192.168.1.1/epg/epg.xml.gz. Grabber запускается на роутере один раз в сутки, все ресиверы тянут с него — нагрузка на внешний источник снижается, скорость обновления растёт (локальная сеть vs интернет).
Такая архитектура особенно удобна если дома несколько ресиверов и нужен единый local tv guide на всех устройствах с одинаковыми данными.
Почему после настройки CCcam/OScam каналы работают, а программа передач пустая?
CCcam и OScam отвечают только за расшифровку видеопотока через ECM/EMM обмен с сервером шаринга. Программа передач передаётся отдельной таблицей EIT в транспортном потоке — эмулятор её не трогает. Если вещатель не передаёт EIT или передаёт зашифрованный, ресивер не получает данные программы. Решение — подключить внешний XMLTV-источник через плагин EPGImport и настроить channels.xml с маппингом каналов.
Можно ли получить EPG на русском языке для иностранных пакетов?
Да. Нужен XMLTV-файл где теги title и desc содержат атрибут lang="ru" и русский текст. Получить его можно через grabber, собирающий данные с русскоязычных сайтов телепрограмм — WebGrab+Plus с соответствующим siteini или самописный скрипт. Маппинг через channels.xml: channel id в XMLTV привязывается к sref иностранного канала в lamedb. Главное — правильно указать часовой пояс в XMLTV чтобы время передач совпадало с фактическим вещанием.
Какой формат XMLTV правильный и где спецификация?
Стандарт описывает xmltv.dtd — найти его можно в пакете xmltv или в репозитории проекта. Обязательные элементы: тег channel с атрибутом id и дочерним display-name, тег programme с атрибутами start, stop, channel и дочерним title. Время строго в формате YYYYMMDDHHMMSS с указанием часового пояса через пробел (например, 20260527180000 +0300). Валидация: xmllint --noout --dtdvalid xmltv.dtd your_epg.xml. Кодировка — только UTF-8.
EPG обновился, но в TV-гайде по-прежнему пусто — что проверять?
Последовательность: 1) tail /tmp/epgimport.log — смотришь есть ли ошибки парсинга или маппинга. 2) Открываешь channels.xml и сравниваешь sref с /etc/enigma2/lamedb — значения должны совпадать побайтово. 3) Проверяешь размер и наличие /etc/enigma2/epg.dat или /media/hdd/epg.dat. 4) Перезапускаешь GUI: init 4 && init 3 через SSH. 5) Проверяешь свободное место на flash через df -h — если заполнен, epg.dat не пишется.
Сколько места на ресивере занимает EPG-кэш и как его ограничить?
Зависит от количества каналов и глубины кэша. Для 300 каналов на 7 дней — около 20-40MB, для 500 каналов на 14 дней — 60-100MB. Файл epg.dat находится в /etc/enigma2/ или /media/hdd/. Ограничение глубины: Configuration → EPG → Maximum days to cache (ставишь 7 вместо 14). Перенос на USB/HDD через симлинк: rm /etc/enigma2/epg.dat && ln -s /media/hdd/epg.dat /etc/enigma2/epg.dat. На ресиверах Zgemma H1 с 256MB flash это обязательный шаг.
Можно ли использовать один XMLTV-файл сразу для нескольких ресиверов в сети?
Да, это правильная архитектура. Grabber запускается один раз на роутере (OpenWrt) или NAS через cron, результат кладётся в /www/epg/epg.xml.gz. Lighttpd или nginx отдают файл по http://192.168.1.1/epg/epg.xml.gz. В sources.xml каждого ресивера указываешь этот локальный URL. Все ресиверы получают одинаковые данные local tv guide, внешний источник дёргается один раз в сутки вместо нескольких, обновление происходит быстрее за счёт гигабитной домашней сети.
Как настроить EPG для региональных версий канала (HD/SD, +2/+4)?
Каждая региональная версия — отдельный сервис с уникальным SID/TSID в транспортном потоке. Значит отдельный sref в lamedb. В XMLTV нужны отдельные channel id для каждой версии: "ntv.hd", "ntv.sd", "ntv.plus2". В channels.xml каждый id маппится на свой sref. Автомаппинг по имени не работает — все версии называются похоже, плагин возьмёт первое совпадение. Только ручная привязка через Channel List Editor с проверкой SID и TSID из lamedb или с помощью команды grep нужного имени в lamedb.