Lampac NextGen — локальный Docker-развёртывание

Локальное развёртывание Lampac NextGen — самохостируемого бекенда для Lampa — в Docker. Официальная документация: docs.lampac.dev.

Быстрый старт

docker compose up -d          # первый запуск
docker compose logs -f        # логи (строки compilation / configure module / loaded module)
docker compose restart        # после правки config/init.conf или манифестов в mods/

По умолчанию сервер слушает порт 9118; если порт занят — меняй listen.port в config/init.conf до первого запуска.

Структура проекта

Путь Назначение
docker-compose.yaml Основной сервис (образ закреплён на ghcr.io/lampac-nextgen/lampac:1.68.0)
config/init.conf Конфигурация сервера (JSON, монтируется в /lampac/init.conf)
config/passwd Пароль root для /adminpanel и /weblog (не в git)
cache/ Кеш (/lampac/cache)
database/ SQLite-базы Sync, TimeCode, Music, tg-notify (/lampac/database)
dlna/ Локальные медиа для DLNA (/lampac/data/dlna)
plugins/extensions.json Каталог плагинов для веб-клиента (раздел «Плагины»), отдаётся через /extensions
mods/<Имя>/ Пользовательские копии встроенных модулей с enable: true

docker-compose.override.yml (в .gitignore) — серверное override для Proxmox (том DLNA -> /mnt/torrents), живёт только на сервере.

Включённые модули из mods/

Встроенные модули с "enable": false в manifest.json внутри образа нельзя включать правкой файла в контейнере — файлы пропадут при обновлении. Механика по официальной документации (модули Lampac, пользовательские модули):

  1. Скопировать модуль из образа в mods/<Имя>/ на хосте:
    docker cp lampac:/lampac/module/<Имя> "mods/<Имя>"
    
  2. В mods/<Имя>/manifest.json поставить "enable": true.
  3. Добавить том в docker-compose.yaml:
    - ./mods/<Имя>:/lampac/mods/<Имя>
    
  4. При необходимости добавить секцию модуля в config/init.conf (runtime-флаг).
  5. docker compose up -d (при добавлении тома) или restart (при правке манифеста с dynamic: true).

Сейчас подключены

Модуль Что даёт Секция в init.conf
Tracks /tracks.js, /ffprobe — субтитры и дорожки "Tracks": { "enable": true }
AdminPanel /adminpanel — редактор init.conf, current.conf, users.json "AdminPanel": { "enable": true }
Music /music.js, /music/* — музыкальный каталог, плейлисты, стримы секция "Music" (базовые дефолты)
Tg-notify.bot /tg-notify.js, /api/tg/* — уведомления о новых сериях в Telegram секция "TelegramBot"

Клиентские плагины music.js и tg-notify.js зарегистрированы в LampaWeb.customPlugins; {localhost} в URL автоматически подставляется сервером.

Ещё можно включить тем же способом

В образе выключены также: DatabaseEditor, ExternalBind, LogUserRequest-Lite, Transcoding, WatchTogether, TelegramAuth, TelegramAuthBot.

Конфигурация

  • config/init.conf — JSON. После правки: docker compose restart. Следи за валидностью JSON (лишняя запятая ломает парсинг).
  • BaseModule.SkipModules / BaseModule.LoadModules — фильтры загрузки модулей (имя, группа, regex). SkipModules имеет приоритет над LoadModules.
  • LampaWeb.initPlugins — флаги встроенных плагинов веб-клиента; LampaWeb.customPlugins — дополнительные плагины (status: 1).
  • LampaWeb.autoupdate: true — Lampa Web-клиент автообновляется из yumata/lampa (раз в 90 минут).

Установка плагинов по ссылке через веб (AdminPanel)

Список плагинов, который сервер раздаёт всем клиентам через /lampainit.js и /on.js, собирается по LampaWeb.initPlugins + LampaWeb.customPlugins. Watcher перечитывает init.conf без перезапуска (интервал ~1 с, runtime-параметры применяются на лету).

  1. Открой http://localhost:9118/adminpanel, введи root-пароль (содержимое config/passwd) — пароль сохраняется в cookie accspasswd (штатный механизм root-авторизации).
  2. В редакторе init.conf открой секцию LampaWeb → customPlugins и добавь запись:
    { "url": "https://example.com/plugin.js", "status": 1, "name": "ИмяПлагина" }
    
    Поддерживается плейсхолдер {localhost} — сервер подставит свой адрес.
  3. Сохрани — перезапуск не нужен. Проверка: curl -s http://localhost:9118/lampainit.js | grep <имя>.

Нюансы:

  • status: 1 — активная запись.
  • Family-dedup: name.js и name/js/{token} считаются одним плагином; если в registry клиента уже есть один вариант, второй из серверного списка не добавляется.
  • Внешние URL должны быть доступны из браузеров клиентов и отдавать CORS-заголовки.
  • Альтернатива без сервера: вставить ссылку напрямую в веб-клиенте Lampa (настройки → «Плагины») — тогда плагин живёт только в localStorage этого браузера и грузится браузером напрямую.

API (для скриптов): GET/POST /adminpanel/api/init, POST /adminpanel/api/init/section/{key} с cookie accspasswd=<root-пароль>.

Каталог плагинов (раздел «Плагины» в веб-клиенте)

В веб-клиенте Lampa раздел «Плагины» грузит каталог (store) с маршрута /extensions (включается при LampaWeb.initPlugins.pirate_store: true). Содержимое берётся из файла LampaWeb/plugins/extensions.json модуля — у нас он переопределён файловым mount из ./plugins/extensions.json (см. docker-compose.yaml). Плейсхолдер {localhost} в ссылках сервер заменяет своим адресом.

Все плагины каталога размещены локально (mirror): .js-файлы лежат в ./plugins/, каталог целиком смонтирован в /lampac/wwwroot/plugins/ (одна строка в compose) и ссылки в каталоге ведут на {localhost}/plugins/<имя>.js. (Целиком в корень wwwroot монтировать нельзя — перекроется веб-клиент lampa-main.) Сами файлы плагинов всегда локальные; исключение — плагин-лоадеры, подгружающие основной код с внешнего хоста: «Клубничка 18+» (rc.bwa.ad) и Showy (showy.online/online.js — файл локально патчен под абсолютный URL, иначе он бы подгружал /online.js с нашего сервера). Остальные скачаны целиком (для pubtorr / torrentmanager / td скачано тело, а не лоадер).

  • plugins/sources.json — карта «локальный файл → исходный URL». Для обновления плагина: скачать файл по исходному URL, перезаписать локальный, перезапуск не нужен.
  • plugins/modss.js — локальная patched-копия MODS's (заменён вызов Lampa.Utils.isPWA()), раздаётся как /plugins/modss.js, но из каталога исключена (upstream мёртв: загрузчик отвечает stub-ошибкой «Используйте: lampa.mx»). Вернуть карточку можно, добавив запись с link: "{localhost}/plugins/modss.js".

Удалены из каталога (мёртвы upstream, проверено по факту выдачи):

  • «MODS's» (lampa.stream/modss) — загрузчик отвечает stub-ошибкой;
  • «Отзывы и рецензии» (lampaplugins.github.io/store/o.js) — файл заменён автором на пустой stub;
  • «Вторая кнопка Торренты», «Поиск локального TorrServer», «Горячие кнопки», «Золотая Тема», «Тип управления», «Старый стиль», «Мультфильмы» — публичных копий нет;
  • «Телевидение by Skaz» / «ТВ by skaz» / «ТВ by skaz 2.0» — файлы заменены placeholder-заглушками;
  • «Импорт/Экспорт закладок, истории» (skaz.tv/export.js) — 404.

Важный нюанс — диск-кеш ответа: маршруты LampaWeb кешируют ответы в cache/static/<bucket>/<id>.json (том ./cache), кеш переживает рестарт контейнера. После правки extensions.json для мгновенного применения удалить закешированную запись:

rm -rf cache/static/59   # bucket /extensions; либо rm -rf cache/static

Перезапуск контейнера при этом не требуется (маршрут читает файл при каждом запросе, если нет кеш-записи).

Tg-notify.bot: Telegram-бот уведомлений о новых сериях

Фоновый Telegram-бот (long polling): пользователь подписывается на сериал прямо из карточки Lampa, бот отслеживает выход новых эпизодов и появление выбранной озвучки и шлёт в Telegram уведомление с обложкой, номером серии и описанием из TMDB.

Модуль объединяет три подсистемы:

  1. Трекер новых серий — Trakt API (если задан trakt_client_id), иначе TMDB.
  2. Трекер озвучек — параллельный опрос встроенных балансиров Mirage, Collaps и VideoHub через lampac_host (/lite/mirage, /lite/collaps, /lite/cdnvideohub); берётся максимальный эпизод из всех источников.
  3. Клиентский плагин tg-notify.js — вкладка «TG Подписки», перехват кнопки 🔔 в карточке, меню выбора озвучки.

Как это работает (поток)

  1. Привязка аккаунта — во вкладке «TG Подписки» в Lampa выдаётся deep-link /start link_{uid} (для UID вида email — /start link64_{base64url(uid)}). После /start бот сохраняет связку chat_id ↔ lampac_uid в database/tgnotify/users.json; подписки становятся общими для всех устройств этого аккаунта.

  2. Подписка — в карточке сериала кнопка 🔔 перехватывается плагином, показывается меню озвучек (GET /api/tg/voices), выбор уходит на POST /api/tg/subscribe и сохраняется в subscriptions.json.

  3. Фоновый цикл CheckAll — раз в check_interval_minutes (первый прогон — через 2 минуты после старта, вручную — /check): новые серии через Trakt→TMDB; если выбрана конкретная озвучка — параллельно Mirage + Collaps + VideoHub.

  4. Уведомление — сообщение вида:

    🎬 Название
    🎙 Озвучка
    📺 S05E08 — Название эпизода
    
    Описание (до 300 символов)
    

    с обложкой кадра из TMDB (still_path), если есть.

Конфигурация (секция TelegramBot в config/init.conf)

Поле Назначение
bot_token Токен от @BotFather. Обязателен — без него long polling не стартует (в логе предупреждение, сервер работает)
tmdb_api_key Ключ TMDB v3: обложки, описания эпизодов, определение актуального сезона
trakt_client_id Client ID Trakt; основной источник новых серий (если пуст — только TMDB)
lampac_host Базовый URL Lampac для запросов к балансерам; в Docker — http://127.0.0.1:9118
lampac_token Токен accsdb; обязателен, если включена защита (иначе балансеры вернут ошибку входа)
kp_api_key Ключ kinopoiskapiunofficial.tech — резолвинг tmdb_id → kinopoisk_id по названию для VideoHub (опционально; без него покрытие ~40% через мост /externalids)
check_interval_minutes Период фоновой проверки (дефолт 60)
tmdb_lang Язык метаданных TMDB (дефолт ru-RU, фоллбэк en-US)
data_dir Каталог users.json/subscriptions.json (дефолт database/tgnotify — у нас это том ./database/, переживает рестарты)

Быстрый старт

  1. @BotFather → /newbot → получить токен.
  2. config/init.conf → TelegramBot.bot_token = токен.
  3. docker compose restart lampac (по желанию — заполнить tmdb_api_key и trakt_client_id).
  4. В Lampa (http://localhost:9118) вкладка «TG Подписки» → deep-link → /start у бота.
  5. В карточке сериала — 🔔, выбрать озвучку (или «без озвучки» — уведомление о самом факте выхода серии).

Команды бота

Команда Действие
/start Приветствие; /start link_{uid} — привязка аккаунта
/list Список активных подписок
/check Принудительная проверка в фоне
/help Справка
/unlink Отвязать аккаунт

HTTP API

Метод Маршрут Назначение
GET /tg-notify.js Раздача клиентского плагина (AllowAnonymous)
POST /api/tg/subscribe Подписка на сериал/озвучку
POST /api/tg/unsubscribe Отписка
GET /api/tg/status?tmdb_id= Статус подписки текущего пользователя
GET /api/tg/voices?title=&year=&season=&tmdb_id= Доступные озвучки (Mirage + Collaps + VideoHub)
GET /api/tg/link Deep-link для привязки бота
GET /api/tg/subscriptions Все подписки пользователя с метаданными

Все маршруты, кроме /tg-notify.js и /api/tg/voices, требуют requestInfo.user_uid (вход в аккаунт).

Состояние на нашем инстансе

  • Модуль загружен, /tg-notify.js → 200, плагин уже в LampaWeb.customPlugins, вкладка «TG Подписки» видна.
  • bot_token пуст → long polling не работает (лог: [TelegramBot] Установите bot_token…); уведомления придут только после заполнения токена.
  • Трекинг озвучек зависит от балансиров: на текущий момент GET /lite/mirage → тело disable, GET /lite/collaps → 503 — до тех пор, пока они не будут доступны (или не включится lampac_token при защите), бот будет уведомлять только о выходе серий (TMDB/Trakt).

TelegramAuth + TelegramAuthBot: вход в Lampa через Telegram

Пара модулей из группы Community — замена пароля аккаунта подтверждением в Telegram (сценарий: ТВ, где вводить пароль неудобно). Не путать с Tg-notify.bot — тот только уведомляет о сериях.

  • TelegramAuth — серверная часть: хранение пользователей/устройств, HTTP API /tg/auth/*, синхронизация UID в accsdb; Telegram Auth Gate — вместо экрана deny.js (модалка пароля) показывает fullscreen-оверлей с UID, кнопкой «Открыть Telegram» и QR-кодом на крупных экранах.
  • TelegramAuthBot — long-polling бот: пользователь открывает бота (/start link_{uid} или отправляет UID), бот сам вызывает POST /tg/auth/bind/complete; есть админ-команды. HTTP UI нет.

Активация гейта — только при трёх условиях сразу: accsdb.enable: true + LampaWeb.telegramAuthGate.enabled: true + непустой botUsername. Иначе тихий откат на deny.js.

Конфиг (по образцу init.merge.example.json модулей):

"TelegramAuth": {
  "enable": true,
  "mutations_api_secret": "SECRET"
},
"TelegramAuthBot": {
  "enable": true,
  "bot_token": "ТОКЕН_ОТ_BOTFATHER",
  "mutations_api_secret": "SECRET"
},
"LampaWeb": {
  "telegramAuthGate": {
    "enabled": true,
    "botUsername": "имя_бота_без_@",
    "serviceName": "lampa"
  }
}

mutations_api_secret должен совпадать в обоих модулях и быть ненулевым — защищает bind/complete и бот-админку; вместе с bot_token не публиковать. Оба модуля выключены в base.conf (SkipModules) и в манифестах (enable: false).

Состояние у нас: выключены, accsdb в init.conf не включён → гейт неактивен, используем беззащитный доступ + root-пароль AdminPanel. Включать связку имеет смысл вместе с accsdb (защита сервера учётными записями).

Обновление Lampac

docker compose pull
docker compose up -d

Перед обновлением проверить, что в docker-compose.yaml раскомментированы нужные тома. Данные сохраняются в примонтированных томах: config/, cache/, database/, dlna/, mods/.

Образ закреплён точным digest (1.68.0@sha256:752dafd8…): тег 1.68.0 на ghcr — mutable, его переиздавали, и в новой сборке mods/Music (копия из старой сборки) не компилируется (MusicMetadataCacheService не найден) — сервер уходит в crash-loop. Чтобы обновить образ: взять новый digest, пересобрать копии модулей в mods/ из нового образа (иначе Roslyn-компиляция упадёт) и заменить digest в docker-compose.yaml.

Безопасность

По предупреждению официальной документации:

  • Не публикуй в интернет без аутентификации, WAF и ограничения сети: /adminpanel, /weblog, /database-editor, Tracks, Transcoding, Catalog, DLNA, proxy-модули.
  • Нигде не раскрывай config/passwd, cookie accspasswd, заголовок lcrqpasswd.
  • Для публикации за reverse proxy: deployment/reverse-proxy (HTTPS в ответах, реальный IP клиента, WebSocket, SSE, streaming).
  • shm_size: 4096mb — рекомендованный минимум для Playwright/Chromium.
S
Description
No description provided
Readme
39 MiB
Languages
JavaScript 80.4%
CSS 9.5%
C# 6.4%
HTML 3.5%
Python 0.1%