--- title: Lampac — развёртывание и эксплуатация (Кра́кен) created: '2026-09-22' updated: '2026-09-22' type: tech namespace: personal tags: - lampac - torrserver - docker - kraken - streaming - webos related: - '[[personal/plans/lampac-kraken]]' - '[[personal/plans/lampac-watchlist-plugin]]' - '[[personal/tech/multistream-single-flow-limit]]' - '[[family/how-to/kraken-network]]' - '[[family/how-to/tv-luna-send]]' --- # Lampac — развёртывание и эксплуатация (Кра́кен) > Технический справочник. Развёрнуто 2026-09-22. План и статус шагов — [[personal/plans/lampac-kraken]]. ## Что это `lampac-nextgen/lampac` (MIT, .NET 10) — серверный бэкенд для Lampa. Один контейнер, порт **9118**. **Внутри (всё в одном образе):** - **TorrServer** — subprocess, кэш раздач - **JacRed** — Jackett-совместимый индексатор - **LampaWeb** — веб-морда - **TmdbProxy**, **DLNA**, **Sync** 🔴 **TorrServer встроенный. Отдельный контейнер не нужен.** Проверено 2026-09-22: в логах первого старта видно `compilation TorrServer`. Внешний TS — лишняя сущность. ## Расположение на Кра́кене ``` /srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/lampac/ ├── docker-compose.yml (бэкапы .bak-*, .bak2-*) ├── config/ → /lampac/config ├── cache/ → /lampac/cache (кэш TorrServer — только сюда) ├── data/ → /lampac/data (БД JacRed) ├── config-module/init.conf → /lampac/init.conf:ro (наш конфиг) ├── catalog/sites/ → /lampac/module/Catalog/sites ├── catalog/override/ → /lampac/module/Catalog/override ├── catalog/plugin.js → /lampac/module/Catalog/plugin.js:ro (ПАТЧ, см. ниже) ├── wwwroot/ → /lampac/wwwroot (JSON-каталог watchlist) └── scripts/gen_watchlist.py (генератор .md → JSON) ``` Локальные копии рабочих артефактов (на Mac, `/Users/admin/lampac-tmp/`): `gen_watchlist_v2.py`, `patch_plugin.py`, `plugin-patched.js`, `watchlist.yaml`, `docker-compose.yml`, `deploy_lampac.py`. Владелец `kraken:kraken`, права `755`. ⚠️ Всё пишущее — на data-диск (1.8T). **НЕ на SD** (`/`, 14G, 8.6G свободно). ## docker-compose.yml (актуальный, 2026-09-22) ```yaml services: lampac: image: ghcr.io/lampac-nextgen/lampac:latest container_name: lampac platform: linux/arm64 restart: unless-stopped ports: - "9118:9118" environment: - TZ=Asia/Bishkek - LANG=ru_RU.UTF-8 volumes: - ./config:/lampac/config - ./cache:/lampac/cache - ./data:/lampac/data # Наш конфиг: перекрывает base.conf из образа. Включает модуль Catalog. # Без монтирования правки теряются при пересоздании контейнера. - ./config-module/init.conf:/lampac/init.conf:ro # Каталоги Catalog - ./catalog/sites:/lampac/module/Catalog/sites - ./catalog/override:/lampac/module/Catalog/override # Патч plugin.js. Файл живёт В ОБРАЗЕ — без монтирования патч теряется # при обновлении образа. Нужен для видимости источника в UI (см. ниже). - ./catalog/plugin.js:/lampac/module/Catalog/plugin.js:ro # Статика: JSON-каталог watchlist (Lampac отдаёт wwwroot по HTTP сам) - ./wwwroot:/lampac/wwwroot ``` ⚠️ В контейнере рабочий каталог `/lampac`, часть файлов читается **из корня**, не из `config/`. ### Минимальный `config-module/init.conf` Перекрывает `base.conf`, поэтому список `SkipModules` нужно **повторить целиком**, без `Catalog`: ```json { "BaseModule": { "SkipModules": [ "Tracks", "Transcoding", "WebLog", "CacheMedia", "ForkPlayerXML", "MsxNative", "Potok", "TelegramAuth", "TelegramAuthBot" ] }, "LampaWeb": { "initPlugins": { "catalog": true } } } ``` После любой правки — `docker compose up -d` (пересоздаст контейнер) или `docker restart lampac`. ## Модуль Catalog — внутренняя механика (разобрано по коду 2026-09-22) Исходники внутри образа: `/lampac/module/Catalog/` — `Controllers/{ApiController,ListController,CardController}.cs`, `ModInit.cs`, `plugin.js`, `sites/*.yaml`. ### Маршруты | Роут | Назначение | |---|---| | `/catalog.js` | плагин для Lampa (клиентский JS) | | `/catalog` | JSON: список каналов с их `menu` и `main` | | `/catalog/list?plugin=

&cat=&page=N` | список элементов категории | | `/catalog/card?plugin=

&uri=&type=movie` | карточка фильма | WAF на префиксе `/catalog/` — **10 запросов/сек**. ### 🔴 Питфолл №1: первая категория игнорирует `format` `ListController.cs:50` строит базовый URL **до** обработки `format`: ```csharp string url = $"{init.host}/{(page==1 && init.list?.firstpage != null ? init.list?.firstpage : init.list?.uri)}"; // строка 50 else if (!string.IsNullOrEmpty(cat)) { var menu = init.menu.FirstOrDefault(i => i.categories.Values.Contains(cat)); string eval = (cat != null && sort != null) ? getFormat("sort") : getFormat("-"); if (!string.IsNullOrEmpty(eval)) { url = CSharpEval(...); } // строки 78–85 if (!url.StartsWith("http")) url = $"{init.host}/{url}"; // строка 87 } ``` **Категория, стоящая ПЕРВОЙ в `menu.categories`, берёт базовый `url` из `list.firstpage` и `format` для неё не применяется.** Симптом: первая категория отдаёт данные чужого файла (например все 360 фильмов из общего `watchlist.json`), у элементов `img: null`, `id` = ссылка на поиск Кинопоиска → в UI «здесь пусто». **Правило:** `list.uri`/`firstpage` и первая категория в `menu.categories` должны указывать на **один файл**. Переставляешь категории — правь `list.uri`. ### 🔴 Питфолл №2: `host` + `uri` склеиваются Строка 50 склеивает `init.host` + `/` + `uri`. Значит `uri`/`firstpage` обязаны быть **относительными** (`watchlist-films.json`), иначе двойной префикс: `http://127.0.0.1:9118/http://127.0.0.1:9118/watchlist-films.json` → пусто. ### 🔴 Питфолл №3: `main` в YAML запрещён `CatalogSettings` не имеет поля `main` для входного YAML → YamlDotNet давится неизвестным полем → `ModInit.goInit()` → `null` → `ApiController.cs:70` отбрасывает сайт: ```csharp if (init == null || !init.enable || init.menu == null || init.hide) continue; ``` **В логах — тишина.** Сайт молча исчезает из `/catalog`. Обе формы (список и словарь) ломают. `main` Catalog **генерирует сам** из `menu` — в JSON-ответе `/catalog` присутствуют оба ключа с одинаковым содержимым. Писать `main` в YAML не нужно и нельзя. ⚠️ `menu` **обязателен** — он регистрирует канал, а не подменяет разделы. ### 🔴 Питфолл №4: элемент без `href` выбрасывается ```csharp if (!string.IsNullOrWhiteSpace(name) && !IsNullOrWhiteSpace(href)) // ListController.cs ``` Элемент включается **только если есть и `name`, и `href`**. Симптом: из 404 позиций отдалось 52 — ровно те, у кого есть ссылка. **Фикс:** генератор подставляет fallback `https://www.kinopoisk.ru/index.php?kp_query=<название>`. ### Как `href` превращается в `id` (карточка фильма) `ListController.cs:314–322`: ```csharp else if (href.StartsWith("http")) href = Regex.Replace(href, "https?://[^/]+/", ""); // вырезает домен ... id = href, ``` Поэтому **полезно отдавать TMDB-URL**: `https://www.themoviedb.org/movie/687163` → `id = "movie/687163"` → `CardController` идёт в TMDB и отдаёт полноценную карточку (постер, жанры, описание, рекомендации). Работает для `movie/N` и `tv/N`. `CardController` использует **встроенный TMDB-прокси Lampac**: `http://{localhost}:{port}/tmdb/api/3/{cat}/{id}?api_key={CoreInit.conf.cub.api_key}` — ключ уже внутри образа, отдельный TMDB-ключ для карточек не нужен. ### 🔴 Питфолл №6: карточка без `card_parse` даёт HTTP 500 Без блока `card_parse` Catalog строит URL карточки как `$"{init.host}/{uri}"` (`CardController.cs:33`) → `http://127.0.0.1:9118/movie/424781` — такого пути нет, `html == null` → `BadRequest("html")` → **HTTP 500**, в UI «здесь пусто». **Сигнатура роута (критично!):** ```csharp [Route("catalog/card")] public async Task Index(string plugin, string uri, string type) ``` 🔴 **Параметра `cat` НЕТ.** Карточка знает только `uri` и `type`. Поэтому `initUrl: $"…/cards-{cat}.json"` даёт `cards-.json` → 404 → HTTP 500. **Что можно и что нельзя подставлять:** | Поле | Механика | Подстановка | |---|---|---| | `initUrl` | `CSharpEval.Execute` с `CatalogInitUrlCard(host, args, uri, query, type)` | ✅ `uri`, `type`, `host` | | `node` | `json.SelectToken(parse.node)` — чистый JSONPath | ❌ **статичен** | | `name`/`image`/`description` | `ModInit.nodeValue(...)` → `attribute` | — | **Питфолл JSONPath:** `SelectToken("movie/424781")` вернёт `null` — `/` в JSONPath это **вложенность** (`movie` → `424781`). Симптом: **HTTP 400, тело `parse.node`**. Слэш в ключе JSON недопустим для `SelectToken`. **Рабочая схема (когда карточку отдаёт наш JSON, а не TMDB):** ```yaml card_parse: initUrl: | return $"{host}/cards/card-{uri.Replace("/", "-")}.json"; node: card # фиксированный путь в файле name: { attribute: name } original_name: { attribute: original } year: { attribute: year } image: { attribute: poster } description: { attribute: overview } ``` Файлы `wwwroot/cards/card--.json` (по одному на фильм) с обёрткой `{"card": {...}}`. По одному файлу — потому что `node` статичен и один общий файл по `uri` не отфильтровать. **Диагностика (ошибки карточки в логи Lampac НЕ пишутся!):** ```bash curl -s -o /tmp/c.json -w "HTTP %{http_code}\n" \ "http://192.168.1.14:9118/catalog/card?plugin=watchlist&uri=movie%2F424781&type=movie" # тела-маркеры: "html" (URL не найден) | "parse.node" (node не нашёл узел) | "json" (не JSON) ``` ### 🔴 Питфолл №5: кэш Catalog Ключ: `catalog:{plugin}:{search}:{sort}:{cat}:{page}` — **не зависит от содержимого файлов**. `hybridCache` живёт **в памяти процесса** (не в `cache/fdb/`, не сбрасывается рестартом контейнера — проверено). После перегенерации JSON выдача может не меняться, пока ключ тот же. Приём `?nocache={Guid.NewGuid()}` в `format` **не помогает**. Практический вывод: при отладке менять сам **URL/ключ** (другой `cat` или `sort`), а не надеяться на инвалидацию кэша. ### `plugin.js` — клиентская часть Лежит **в образе** (`/lampac/module/Catalog/plugin.js`), не на HDD. Что делает: - `main()` — итерирует `catalog.main`, собирает секции **СВЕРХУ** родного каталога - `menu()` — итерирует `catalog.menu` из YAML - `full()`/`list()`/`search()` — карточка, категория, поиск - `Lampa.Listener.follow('menu', ...)` + `e.abort()` — **перехват кликов** по родным разделам (`movie`/`tv`/`cartoon`/`anime`/`relise`). Срабатывает **только когда выбран источник «Мой список»**, поэтому родной CUB не трогается при других источниках ### 🔴 Питфолл №6: источник не виден в UI (`enumerable: false`) ```js Object.defineProperty(Lampa.Api.sources, key, { get: function(){ return Api; } }); ``` Свойство создаётся **не-enumerable** → `Object.keys(Lampa.Api.sources)` его не видит → Lampa не рисует источник в панели выбора. При этом `Api.sources['Мой список']` доступен напрямую и каталог отдаёт. Замер через DevTools ТВ (`http://192.168.1.70:9998/json`) → `{"enumerable":false,"configurable":false}`. **Фикс:** добавить `enumerable:true,configurable:true` в **оба** вхождения. Правка применяется скриптом `patch_plugin.py` (не sed/grep), проверка синтаксиса — `node --check` перед заливкой. Монтируется через compose (см. выше). ### Сканирование `sites/` `Path.Combine(AppContext.BaseDirectory, ModInit.modpath, "sites")` — папка сканируется на старте контейнера. Кэш-фикс: `docker restart lampac` (старый подход) **не всегда достаточен** — см. питфолл №5. ## Управление ```bash ssh kraken cd /srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/lampac docker compose up -d docker compose logs -f docker compose restart docker compose down ``` Правка `docker-compose.yml` — без `sudo` (каталог `kraken:kraken`). Создание новых каталогов в корне диска — **через `sudo`** (корень `root:root`). У `kraken` sudo без пароля. ### Развёртывание каталога watchlist (`deploy_lampac.py`) Локальный скрипт `/Users/admin/lampac-tmp/deploy_lampac.py` — 7 шагов с бэкапами и самопроверкой. Запуск: ```bash python3 /Users/admin/lampac-tmp/deploy_lampac.py # полный цикл python3 /Users/admin/lampac-tmp/deploy_lampac.py --skip-plugin # только JSON+YAML ``` Порядок: бэкап JSON в `wwwroot/.bak/` → заливка 5 JSON → `watchlist.yaml` → патченный `plugin.js` → volume в compose (если нет) → `docker compose up -d` → проверка (`/catalog`, 4 категории с подсчётом постеров и TMDB-ID, факт патча). ### Генератор каталога (`gen_watchlist_v2.py`) `/Users/admin/lampac-tmp/gen_watchlist_v2.py ` Читает `.md`, резолвит каждый фильм: **сначала Radarr** (мгновенно, там уже `tmdbId` и постер), **затем TMDB** по названию (2 языка × movie/tv), **фолбэк** — ссылка Кинопоиска. Кэш `~/.lampac_tmdb_cache.json`, ключ `title|year`. Первый прогон: **330 постеров из 403** (Radarr 7, TMDB 327, фолбэк 69). Занимает **8–10 минут** (403 фильма × TMDB, `sleep(0.02)`). 🔴 **Запускать в background** — foreground упрётся в лимит 600 с. Требует `TMDB_KEY` из `/Users/admin/Developer/watchlist-sync/.env`. ⚠️ TMDB-хук `api.themoviedb.org` с Кра́кена доступен (проверено: 401 без ключа = сеть есть). ## Проверка работоспособности ```bash curl -sS --max-time 20 "http://localhost:9118/version?type=hash" # → 46b29e1fe8fa6021cd83e75c41abffb7 (для текущего :latest) ``` По сети: `http://192.168.1.14:9118/version?type=hash` — то же значение. ## ⚠️ Питфоллы ### 1. Первый старт долгий (~4 минуты) Контейнер компилирует и грузит ~40 native-модулей: `compilation LampaWeb`, `DLNA`, `GStreamer`, `Sync`, `TorrServer`, ... затем `loaded module: <имя>`. Пока идёт загрузка, HTTP **рвёт соединение**: ``` curl: (56) Recv failure: Connection reset by peer ``` 🔴 **`health: healthy` не означает готовность HTTP.** Статус встаёт раньше. Признак готовности — лог **`Configure complete`** (идёт после последнего `loaded module:`). **Не перезапускать контейнер, не считать это поломкой.** Просто ждать. ### 2. `docker manifest inspect` с Mac отдаёт `denied` GHCR требует авторизацию при обращении с Mac. Проверять наличие `linux/arm64` **из Кра́кена**: ```bash ssh kraken 'docker manifest inspect ghcr.io/lampac-nextgen/lampac:latest' | jq -r '.manifests[].platform | .os + "/" + .architecture' # linux/amd64 # linux/arm64 ← есть, сборка из исходников не нужна # unknown/unknown # unknown/unknown ``` ### 3. Шаблон логов первого старта ``` compilation Alloha compilation LampaWeb ... loaded module: Vibix loaded module: TorrServer ... Configure complete ← HTTP начинает отвечать ``` ### 4. 🔴 `sudo` через `ssh` без tty падает ```bash ssh kraken "sudo docker exec lampac ls /lampac" # sudo: a terminal is required to read the password; # either use the -S option ... or configure an askpass helper ``` **Фикс: флаг `-t`.** Но ⚠️ при `-t` **нельзя передавать `a && b`** — цепочка команд рвёт tty-сессию (`Connection to kraken closed`), и первая же команда отваливается. **Каждую команду вызывать отдельно:** ```python def sh(cmd): return subprocess.run(f"ssh -t kraken {cmd}", shell=True, ...) sh("sudo cp /tmp/x.json /srv/.../wwwroot/x.json") # ✅ одна команда sh("sudo chown kraken:kraken /srv/.../wwwroot/x.json") sh("sudo chmod 644 /srv/.../wwwroot/x.json") # ❌ sh("sudo cp … && sudo chown … && sudo chmod …") — рвёт tty ``` ### 5. 🔴 `docker compose up -d` не пересоздаёт контейнер при смене volumes Изменение **только списка `volumes`** в `docker-compose.yml` не приводит к пересозданию: `compose up -d` считает конфигурацию контейнера неизменной. Симптом: новый bind-mount не появился, патч `plugin.js` не виден внутри. **Проверка:** ```bash ssh kraken "sudo docker inspect lampac --format '{{json .Mounts}}'" | jq -r '.[].Destination' ``` **Фикс:** ```bash ssh kraken "cd /srv/dev-disk-by-uuid-6194539b-.../lampac && sudo docker compose up -d --force-recreate" ``` ## Сеть | Хост | IP | |---|---| | Кра́кен (eth0) | `192.168.1.14` (второй адрес `192.168.1.15`) | | ТВ LG | `192.168.1.70` | | Mac Alex | `192.168.1.57` | Один сегмент `192.168.1.x` — туннели и проброс портов не нужны. Подробнее: [[family/how-to/kraken-network]]. ## Модуль Catalog — каталоги из YAML Модуль **`Catalog`** (в образе, по умолчанию **выключен**) отдаёт свои каталоги как плагин Lampa. Разобрано по коду и **включено в работу 2026-09-22** — см. [[personal/plans/lampac-watchlist-plugin]]. ### Наш каталог «Мой список» Отдаёт `movies-watchlist.md` как каталог в Lampa. Файлы: | Путь на HDD (`.../lampac/`) | Роль | |---|---| | `config-module/init.conf` | включает Catalog (монтируется в `/lampac/init.conf:ro`) | | `catalog/sites/watchlist.yaml` | описание каталога для Catalog | | `catalog/override/` | переопределения (пусто) | | `wwwroot/watchlist.json` + `watchlist-.json` | JSON-данные (Lampac отдаёт по HTTP) | | `scripts/gen_watchlist.py` | генератор: .md → JSON | | `~/scripts/watchlist-catalog.sh` | обёртка + перезапуск (сброс кэша), cron 02:30 | Плагин на ТВ: `http://192.168.1.14:9118/catalog.js` → источник «Мой список». ⏳ **Переделывается (2026-09-22).** Текущая реализация признана неверной: заполнен блок `menu` (подменяет родной каталог CUB вместо добавления секций сверху), а данные берутся парсингом `.md` вместо **Radarr API** (в `.md` нет TMDB ID и постеров). Разбор ошибки — [[personal/plans/lampac-watchlist-plugin]], секция «🔴 АРХИТЕКТУРНАЯ ОШИБКА». ### 🔴 Рабочая форма `sites/watchlist.yaml` (проверена 2026-09-22, откат после провала) ```yaml enable: true jsonPath: true displayindex: 1 displayname: Мой список host: http://127.0.0.1:9118 menu: # ← ОБЯЗАТЕЛЕН (регистрирует канал), единственный блок категорий - categories: Фильмы: films Новинки: new Мультфильмы: cartoons Аниме: anime format: "-": | return $"http://127.0.0.1:9118/watchlist-{cat}.json"; search: uri: watchlist.json list: uri: watchlist-films.json firstpage: watchlist-films.json total_pages: 1 content: nodes: "$.items[*]" name: { attribute: name } original_name: { attribute: original } year: { attribute: year } href: { attribute: href } image: { attribute: image } args: - { name_arg: rating, attribute: rating } ``` ### 🔴 `main` в YAML ЗАПРЕЩЁН — ломает канал (опровергнуто 2026-09-22) **Раздел ниже (гипотеза «`menu` подменяет / `main` добавляет») — НЕВЕРЕН.** Проверено на живом сервере: попытка добавить блок `main` в YAML → **«Мой список» исчез из `/catalog`**, `catalog/list?plugin=watchlist` отдавал пустоту. **Три раунда правок, три рестарта.** | Гипотеза (неверна) | Факт (проверено) | |---|---| | `menu` подменяет родные разделы | ❌ `menu` **регистрирует канал**. `ApiController.cs:70`: `if (init == null \|\| !init.enable \|\| init.menu == null \|\| init.hide) continue;` — без `menu` сайт **отбрасывается** | | `main` добавляет секции сверху | ❌ блока `main` **не существует** во входном YAML. YamlDotNet давится неизвестным полем → `ModInit.goInit(site)` → **null** → сайт молча пропадает | | Заполнять нужно только `main` | ❌ `main` Catalog **генерирует САМ** из `menu`. В JSON-ответе `/catalog` есть **оба** ключа с одинаковым содержимым | ⚠️ **Обе формы `main` ломают одинаково** — и список, и словарь: ```yaml main: # вариант 1 — список: НЕ работает - categories: Фильмы: films ``` ```yaml main: # вариант 2 — словарь: НЕ работает Фильмы: http://... ``` ⚠️ **Ошибка не логируется.** В логах Lampac — тишина (`docker logs lampac | grep -i yaml` пусто). Сайт просто не появляется в `/catalog`. Диагностика только перебором YAML. **Кто на самом деле подменяет родные разделы — `plugin.js`, а не YAML:** ```js Lampa.Listener.follow('menu', function(e){ if (e.type=='action' && (e.action=='tv'||'cartoon'||'anime'||'movie'||'relise')) { var catalog = Api.getCatalog(); if (!catalog) return; ... Lampa.Router.call('category', {...}); e.abort(); // ← перехват клика по родному разделу } }); ``` **Перехват срабатывает ТОЛЬКО когда выбран источник «Мой список».** Родной CUB/tmdb не трогается, пока источник не переключён на наш. ### 🔴 `host` + `uri` СКЛЕИВАЮТСЯ — `uri` обязан быть относительным `ListController.cs:53`: ```csharp string url = $"{init.host}/{(page==1 && init.list?.firstpage != null ? init.list?.firstpage : init.list?.uri)}"; ``` Полный URL в `uri`/`firstpage` даёт **двойной префикс** → пусто: ``` http://127.0.0.1:9118/http://127.0.0.1:9118/watchlist-films.json ``` Правильно: только `watchlist-films.json`, `host` приклеится сам. Так же устроены родные YAML (`vokino.yaml` → `uri: search?name={search}`). ### ⚠️ Питфолл: явный `format` в `menu` перезагружает страницу С `format`-блоком в `menu` Lampa **перезагружает прогрузку страницы при каждом выборе раздела** (наблюдение Alex). Если перезагрузка не нужна — убрать `format` из `menu`, оставить только `categories`. ### 🎯 Как добиться «CUB + мой список сверху» — варианты (решение Alex, не принято) Родной CUB и «Мой список» — это **два источника Lampa**, переключаются в «остальное → основной источник». Одновременно показать нельзя без вмешательства: | Путь | Реализуемость | |---|---| | Блок `main` в YAML | ❌ **невозможно** — ломает парсинг | | Оставить `menu` как есть | ⚠️ источник работает, но разделы подменяются, пока он выбран | | Патч `plugin.js` | ⚠️ убрать `Listener.follow('menu')` → перехвата нет, но и секции сверху **не появятся** | | Написать свой плагин | ✅ единственный путь для «сверху», объём работы | ### ⚠️ Баг Lampac: источник не виден в списке выбора `plugin.js`, строка 59 (`loadCatalogs()`), **два вхождения**: ```js Object.defineProperty(Lampa.Api.sources, key, { get: function(){ return Api; } }); ``` `defineProperty` по умолчанию `enumerable: false` → `Object.keys(Lampa.Api.sources)` не перечисляет источник → Lampa не рисует его в панели выбора. При этом `Api.sources['Мой список']` работает напрямую и каталог отдаёт. **Фикс:** `{ get: ..., enumerable: true, configurable: true }`. `plugin.js` живёт **в образе**, не на HDD (`docker inspect` — монтируются только `catalog/sites` и `catalog/override`). Правка через `docker exec` теряется при пересоздании. Постоянный путь — volume: ```yaml volumes: - ./catalog/plugin.js:/lampac/module/Catalog/plugin.js:ro ``` Проверка синтаксиса ПЕРЕД заливкой: `node --check /tmp/catalog-plugin.js`. Статус: **не применено**, ждёт апрува Alex. ### Radarr как источник данных (постеры, TMDB ID) — ✅ ПРОВЕРЕНО 2026-09-22 Фильмы уже резолвлены в Radarr — там есть TMDB ID и постеры. Это **правильный источник** для каталога; `.md` их не содержит. ```bash # API-ключ Radarr grep -oE "[^<]+" \ /srv/dev-disk-by-uuid-6194539b-.../docker/radarr/config/config.xml # → cbcb8ec320104359920c0f611d503c7b # ⚠️ -L обязателен, иначе 307 и пустое тело curl -sL "http://localhost:7878/api/v3/movie?apikey=$KEY" | jq length # → 50 # Sonarr, порт 8989, ключ 15fbec32f1cb4b05b1c2b229ce27359b curl -sL "http://localhost:8989/api/v3/series?apikey=$SKEY" | jq length # → 13 ``` **Поля ответа Radarr (для каталога нужны все):** `tmdbId`, `imdbId`, `title`, `originalTitle`, `year`, `titleSlug`, `hasFile`, `genres`, `ratings.tmdb.value`, `images[] | select(.coverType=="poster") | .remoteUrl`. Кэш постеров на диске: Radarr `/config/MediaCover/` (46 каталогов). ```json { "tmdbId": 1292695, "imdbId": "tt31728330", "title": "They Will Kill You", "year": 2026, "hasFile": true, "genres": ["Horror"], "ratings": {"tmdb": {"value": 6.945}}, "images": [{"coverType": "poster", "remoteUrl": "https://image.tmdb.org/t/p/original/6oI4oQKTWMVUlr8Ivqydp28Ruu6.jpg"}] } ``` ⚠️ **Названия в Radarr латиницей** (`They Will Kill You`), в `.md` — по-русски. Перевод делает **TMDB Proxy** (`proxy_tmdb = true` в `lampainit.js`), уже включён. ⚠️ **Связки kp ↔ tmdb в данных нет.** В `.md` — 68 ссылок `kinopoisk.ru/film//`, в Radarr — `tmdbId`. Прямого соответствия нет ни в одном файле. **Мост — модуль `watchlist_sync/resolver.py`** (см. ниже). ### `watchlist_sync/resolver.py` — резолвер постеров (уже написан!) Путь: `/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/watchlist-sync/watchlist_sync/` Уже решает задачу постеров/ID для каталога: - поиск в **TMDB** (`api.themoviedb.org/3`, языки `ru-RU` + `en-US`) - поиск в **Кинопоиске** (`api.poiskkino.dev/v1.4`) - возвращает `Candidate`: `tmdb_id`, `kp_id`, `title`, `year`, `score`, `rating`, `genres` - `is_horror()` — ужасы по TMDB `genre_id=27` (нужно для сортировки 🧟‍♀️) - кэш `.resolve_cache.json`; различает `ok` / `ambiguous` (score < 0.75) / `not_found` ### 🔴 `watchlist_sync/arr.py` — ИСТОЧНИК АВТОСКАЧКИ (в коде!) ```python # radarr_add(): "monitored": True, "addOptions": {"searchForMovie": True}, # ← качает СРАЗУ после добавления # sonarr_add(): "monitored": True, "addOptions": {"searchForMissingEpisodes": True}, # ← то же для сериалов ``` ⚠️ **Даже если снять `monitored` вручную, следующий запуск добавления из списка включит автоскачку снова** — флаг зашит в код. Фикс: `searchForMovie` → `False`, `searchForMissingEpisodes` → `False`. **Статус: не применён, на апруве.** ### Как выключить автоскачку на HDD — ✅ ВЫПОЛНЕНО 2026-09-22 | Сервис | Очередь | `monitored` | `rssSyncInterval` | |---|---|---|---| | **Radarr** | 6 → **0** | 45 → **0** | 30 → **0** | | **Sonarr** | 19 → **0** | 13 → **0** | 15 → **0** | Метаданные **целы**: 50 фильмов, 46 с постерами, 50 с `tmdbId` и рейтингом, 24 файла на диске **не тронуты**. Бэкапы — метка `20260922-123404`. **Проверка пассивности:** Transmission за 45 с — NET `290GB/26.7GB`, BLOCK `214GB/393kB` не сдвинулись. Логи Radarr (`search|download|grab`) за 2 мин — пусто. **Правильные эндпоинты (проверено):** ```bash # отмена загрузки — файлы на диске НЕ трогаются DELETE /api/v3/queue/?removeFromClient=true&blocklist=false # снятие monitored — КЛЮЧ ПОЛЯ РАЗНЫЙ! PUT /api/v3/movie/editor {"movieIds": [...], "monitored": false} # Radarr PUT /api/v3/series/editor {"seriesIds": [...], "monitored": false} # Sonarr, НЕ "ids" # RSS off — отправлять ВЕСЬ объект конфига с id, иначе молча вернёт старое PUT /api/v3/config/indexer {"rssSyncInterval": 0, "id": 1, ...остальное} ``` **Откат:** ```bash curl -sL -X PUT ".../api/v3/movie/editor" -d '{"movieIds":[...],"monitored":true}' curl -sL -X PUT ".../api/v3/series/editor" -d '{"seriesIds":[...],"monitored":true}' curl -sL -X PUT ".../api/v3/config/indexer" -d '{"rssSyncInterval":30,"id":1,...}' cp radarr.db.bak-20260922-123404 radarr.db && docker restart radarr ``` ### 🔴 Питфолл: `urllib` не идёт по 307, `curl -sL` идёт Симптом: **все** DELETE/PUT из Python-скриптов вернули `HTTP Error 307: Temporary Redirect`, **ничего не изменилось**. Штатный `urllib` на 307/308 превращает DELETE в GET и теряет тело. **Фикс — свой redirect-хендлер:** ```python class RedirectKeepMethod(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): if code in (307, 308): new = urllib.request.Request(newurl, data=req.data, method=req.get_method()) for k, v in req.headers.items(): new.add_header(k, v) return new return super().redirect_request(req, fp, code, msg, headers, newurl) _OPENER = urllib.request.build_opener(RedirectKeepMethod) # далее _OPENER.open(req) вместо urllib.request.urlopen(req) ``` Прочие грабли сессии: - **Sonarr группирует эпизоды сезона под одним `id`** — после удаления первой записи остальные дают `404`, это **не сбой**, они ушли с родительской. - **Кириллица внутри inline-`jq` в `ssh '...'`** ломает команду (`unexpected INVALID_CHARACTER`). Ключи объекта — латиницей: `total`, `monitored`, `queue`. ## TorrServer внутри Lampac — на HDD НЕ пишет (проверено 2026-09-22) | Параметр | Значение | Смысл | |---|---|---| | `UseDisk` | **`false`** | 🔴 **на диск не пишет вообще** | | `CacheSize` | 100663296 (96 МБ) | кэш в RAM | | `TorrentsSavePath` | `""` | пути сохранения нет | | `TorrentDisconnectTimeout` | 180 | раздача живёт 3 мин после остановки | ```bash # конфиг TorrServer docker exec lampac cat /lampac/data/ts/settings.json | jq '.BitTorr | {UseDisk, CacheSize, TorrentsSavePath}' # жив ли curl -s http://192.168.1.14:9118/ts/echo # → MatriX.135 curl -s -X POST http://192.168.1.14:9118/ts/torrents \ -H "Content-Type: application/json" -d '{"action":"list"}' # → [] ``` ## 🔴 Поиск торрентов: отдельного Jackett НЕТ, его роль играет Lampac `lampainit.js` прописывает: ```js Lampa.Storage.set('parser_torrent_type', 'jackett'); Lampa.Storage.set('jackett_url', '192.168.1.14:9118'); // ← сам Lampac! Lampa.Storage.set('jackett_key', '1'); ``` Контейнера **Jackett в системе нет**. Lampac отдаёт Jackett-совместимый API сам: ```bash # ⚠️ на КОРНЕ, БЕЗ префикса /jackett (с ним → 404) curl "http://192.168.1.14:9118/api/v2.0/indexers/all/results?apikey=1&Query=matrix" # → {"Results":[{"Tracker":"rutor","Title":"...","MagnetUri":"magnet:?xt=...","Seeders":0}]} ``` ✅ Проверено: вернул реальные результаты с магнит-ссылками. **Отдельный Jackett ставить НЕ нужно** — так задумано производителем. ## Занятые порты на Кра́кене (2026-09-22) `7878` Radarr · `8096` Jellyfin · `8191` FlareSolverr · `8989` Sonarr · `9000` Portainer · `9091` Transmission · `9696` Prowlarr · `51413` Transmission (UDP) · **`9118` Lampac** ## Отладка UI Lampa на ТВ (webOS) Правки по SSH не сделать, но **состояние читается** через Chrome DevTools Protocol: ```bash # webOS отдаёт DevTools на порту 9998 (WebAppMgr --remote-debugging-port=9998) curl -s http://192.168.1.70:9998/json | jq -r '.[] | "\(.type): \(.url)"' # → file:///.../com.lampac.tv/index.html?card=...&source=cub ``` Дальше `websocket` + `Runtime.evaluate` читает живые переменные: | Выражение | Что даёт | |---|---| | `Lampa.Storage.field('source')` | выбранный источник | | `Object.keys(Lampa.Api.sources).join('\|')` | перечисляемые источники | | `Object.getOwnPropertyDescriptor(Lampa.Api.sources,'Мой список')` | `enumerable`/`configurable` | | `Lampa.Api.sources['Мой список'].getCatalog()` | ключи каталога | Скрипты-пробы: `/tmp/lampa_probe.py`, `/tmp/lampa_probe2.py`, `/tmp/lampa_probe3.py`. ⚠️ `tcpdump` на Кра́кене **нет**. `luna://com.webos.service.capture/takeScreenShot` на этом ТВ **не существует**. `luna-send` пишет в tty → нужен `script -q -c`. Подробности: [[family/how-to/tv-lampac-plugin]]. ### Общее про модуль | Что | Как | |---|---| | Данные | `sites/.yaml` — `host`, `menu`, `search`, `list`, `content` | | Переопределения | `override/.yaml`, `override/_.yaml` | | Роуты | `/catalog.js`, `/catalog`, `/catalog/list`, `/catalog/card` | | WAF | `^/catalog/` → 10 req/sec | | Примеры | 10 файлов: `vokino`, `rezka`, `kinopub`, `aniliberty`, `eneyida`, `filmix`, `filmixapp`, `getstv`, `kinobase`, `kinoukr` | **Обязателен HTTP:** `if (!url.StartsWith("http")) url = $"{init.host}/{url}"` — локальные файлы не читаются. **Два режима:** `jsonPath: true` → `JToken.Parse` (JSON), иначе HtmlAgilityPack (HTML). 🔴 **Питфолл: элемент с пустым `href` ВЫБРАСЫВАЕТСЯ** — `if (!IsNullOrWhiteSpace(name) && !IsNullOrWhiteSpace(href))` (ListController.cs). Нужен непустой `href` у каждого элемента. 🔴 **Питфолл: `{cat}` НЕ подставляется в `nodes`** — JSONPath статичен. Разные категории = разные JSON-файлы. 🔴 **Питфолл: кэш без версии данных** — ключ `catalog:{plugin}:{search}:{sort}:{cat}:{page}`. После правки данных нужен `docker restart lampac`. **Выключен в двух местах** `base.conf`/`current.conf`: ```jsonc "BaseModule": { "SkipModules": [ "Catalog", ... ] } "LampaWeb": { "initPlugins": { "catalog": false } } ``` Синапс модулей (всего 33): `AdminPanel, Adult, Catalog, Community, DLNA, DatabaseEditor, ExternalBind, ForkPlayerXML, GStreamer, JacRed, Kit, LampaWeb, LampacApk, LogUserRequest-Lite, MsxNative, Music, NextHUB, Online, OnlineAnime, OnlineENG, OnlineGEO, OnlinePaid, OnlineRUS, OnlineUKR, PidTor, Potok, Proxy, SISI, Sync, Telemetry, Tracks, Transcoding, WebLog`. Полный пример YAML (`vokino.yaml`): `docker exec lampac cat /lampac/module/Catalog/sites/vokino.yaml`. Выбор порта — `docker compose config` + `sudo ss -tlnp` перед деплоем. ## Интеграция с Lampa (ТВ) — ✅ СДЕЛАНО 2026-09-22 - Плагин: `http://192.168.1.14:9118/online.js` - TorrServer в Lampa: Настройки → TorrServer → «Использовать основную ссылку» → `http://192.168.1.14:9118` - Буфер плеера: 50–100 МБ - `/lg.ipk` отдаёт сам Lampac (проверено: HTTP **200**) ### Установка клиента на LG webOS — по SSH, без флешки 🔴 **`Media Station X` и `.ipk` с флешки — НЕ нужны.** ТВ имеет SSH-доступ (root), установка делается с Mac. **ТВ:** `root@192.168.1.70`, ключ `~/.ssh/konka_webos`, webOS **5.4.2** (ядро 4.4.84, image `starfish-dvb-secured`, build 2602, `webos_build_datetime 20230405092501`). Порты открыты: `22` (SSH), `3000`/`3001` (Luna). Из кастомного уже стоит: `org.webosbrew.hbchannel` 0.7.2, `org.webosbrew.inputhook`, `me.wouterdek.magic4pc`. **Порядок установки:** ```bash # 1. Скачать .ipk от Lampac прямо на ТВ ssh root@192.168.1.70 'curl -sS --max-time 60 -o /tmp/lampa.ipk "http://192.168.1.14:9118/lg.ipk"' # → 6 896 874 байт, формат ar (!), НЕ tar/gzip # 2. Установить (обязательна обёртка script — см. питфолл ниже) ssh root@192.168.1.70 'rm -f /tmp/inst.txt; script -q -c \ "luna-send -n 1 -f luna://com.webos.appInstallService/dev/install \ {\"id\":\"org.lampa.tv\",\"ipkUrl\":\"/tmp/lampa.ipk\",\"subscribe\":true}" \ /tmp/inst.txt >/dev/null 2>&1; cat /tmp/inst.txt' # → {"subscribed":true,"returnValue":true} # 3. Проверить, что установилось ssh root@192.168.1.70 'ls /media/developer/apps/usr/palm/applications/' # → com.lampac.tv ← имя приложения Lampa, НЕ org.lampa.tv # 4. Запустить ssh root@192.168.1.70 'rm -f /tmp/l.txt; script -q -c \ "luna-send -n 1 -f luna://com.webos.applicationManager/launch {\"id\":\"com.lampac.tv\"}" /tmp/l.txt' # 5. Убедиться, что открылось ssh root@192.168.1.70 'rm -f /tmp/f.txt; script -q -c \ "luna-send -n 1 -f luna://com.webos.applicationManager/getForegroundAppInfo {}" /tmp/f.txt; grep appId /tmp/f.txt' # → "appId": "com.lampac.tv" ``` **Фактический ID приложения — `com.lampac.tv`** (vendor Lampac, version 1.0.0, type web). ⚠️ В `luna-send ... dev/install` поле `"id"` — **произвольная метка**; реальный appId берётся из `appinfo.json` внутри ipk. Ошибиться здесь не страшно, но искать приложение надо по фактическому ID. ### 🔴 Питфолл: `luna-send` по SSH молчит **Симптом:** любой вызов `luna-send` через `ssh root@TV "luna-send ..."` печатает **хелп-экран**, а не результат. С `-t` (PTY) — тоже. С `-a '{"json"}'` — тоже. **Причина:** `luna-send` детектирует TTY и отказывается писать в pipe. **Решение:** обернуть в `script(1)`, который аллоцирует псевдо-TTY, и прочитать временный файл: ```bash ssh root@TV 'script -q -c "luna-send -n 1 -f luna://service/method {json}" /tmp/o.txt; cat /tmp/o.txt' ``` Готовый враппер: `~/Automation/tv-luna-send.sh` (`TV_HOST` переопределяет таргет). Доки: [[family/how-to/tv-luna-send]], [[family/how-to/htpc-webos-luna]]. ⚠️ **Установка идёт через `com.webos.appInstallService/dev/install`**, а НЕ через `org.webosbrew.hbchannel.service/install` (последний в образе есть, но `luna-send` его не дёрнул чисто). ⚠️ `luna-send -i` (subscribe, бесконечный) через враппер **не работает** — нужен polling. ### Первый просмотр — проверено 2026-09-22 Фильм запущен, **не дёргается**. Alex: «Вроде стабильно, не делается». | Факт | Значение | |---|---| | Соединения ТВ → Lampac | 3 × ESTABLISHED `192.168.1.70 → 192.168.1.14:9118` | | Нагрузка контейнера | `lampac` CPU 57%, NET 168 МБ ↓ / 132 МБ ↑ | | Битрейт на eth0 | **~3–4 Мбит/с** | ⚠️ **Почему всего 3–4 Мбит, а не 15:** выбран источник **«Смотреть онлайн (lampac 1.18…)»** — встроенный плагин `online.js`, HLS-поток. Lampac отдаёт **ссылку**, ТВ тянет видео напрямую из CDN (в `/proc/net/tcp` ТВ видны внешние адреса на `:443`/`:8443`). Тяжёлый remux с торрента не участвует — поэтому и не дёргается. 🔴 **Следствие:** этот просмотр **не проверяет**, держит ли сеть remux 60–80 Мбит. Тест функциональности, не полосы. Полосу проверять на торрент-источнике. **Как убрать выбор источника:** Lampa → Настройки → Плагины → выключить `online.js`. Останутся только торренты. ## 🔴 Выбор источника каталога в Lampa (проверено 2026-09-22) **В Lampa НЕТ пункта «Настройки → Источник».** Я его выдумал — Alex дважды указал на ошибку. Не повторять. Источник (Rezka / Kinobase / «Мой список» / …) переключается **из меню каталогов на главной**: 1. Открыть Lampa на главной 2. Встать на любой ряд каталога → нажать **влево** на пульте 3. Слева выедет **панель со списком источников** 4. Выбрать нужный → OK; каталог перестроится, в заголовке появится имя источника в верхнем регистре («Фильмы — МОЙ СПИСОК») **Плагин `catalog.js` не добавляет пункт меню — он подменяет существующие.** Механика: при старте приложения `Lampa.Utils.account(api_host + '/catalog', …)` получает список каталогов, регистрирует каждый как `Lampa.Params.values.source[key]` и подменяет `Lampa.Api.sources[key]`. Затем хук `Lampa.Listener.follow('menu', …)` перехватывает клики по `movie`/`tv`/`cartoon`/`anime`/`relise` и подставляет URL из `catalogs[key].menu`. ⚠️ **Следствие — каталоги регистрируются ТОЛЬКО при старте приложения.** Добавил плагин и не перезапустил Lampa → источника не будет, `/catalog` не запрашивается, соединений с `:9118` ноль. Нужен **полный перезапуск** (не сворачивание). ### ⚠️ Клиент на ТВ — это не «отдельная Lampa» `com.lampac.tv` (webOS) грузит UI **с сервера**: `loader.js` читает `lampac_url` из localStorage (дефолт `http://192.168.1.14:9118`) и подключает `app.min.js` + `lampainit.js`. Значит логика каталога на ТВ = **серверная**, и править её надо на Lampac, а не на ТВ. `/lampainit.js` содержит `var plugins_add = [...]` со строкой `{"url":".../catalog.js","name":"Альтернативные источники каталога","status":1}` — это **серверный список плагинов, которые Lampac сам подсовывает** в UI. Каталог watchlist работает через **модуль `Catalog`**, не через эту строку. Не искать причину поломки в `plugins_add`. ### 🔴 БАГ Lampac: источник не виден в панели (`enumerable: false`) **Найдено 2026-09-22** диагностикой через Chrome DevTools ТВ. Симптом Alex: «Ни на главной ни в Фильмах нет! Там только Сейчас смотрят, рекомендуем итд». **Факты с живого ТВ:** | Замер | Значение | |---|---| | `Lampa.Storage.field('source')` | `'Мой список'` — источник **уже выбран** | | `Object.keys(Lampa.Api.sources)` | `'tmdb|cub'` — ❌ источника нет в перечислении | | `Lampa.Api.sources['Мой список'].getCatalog()` | `search, movie, cartoons, anime, menu, main` — ✅ работает | | `Object.getOwnPropertyDescriptor(Lampa.Api.sources,'Мой список')` | `{"enumerable":false,"configurable":false}` ← корень | **Корень — в `/lampac/module/Catalog/plugin.js`** (строка 59, `loadCatalogs()`, **два вхождения**): ```js // БЫЛО (баг) Object.defineProperty(Lampa.Api.sources, key, { get: function(){ return Api; } }); // НАДО Object.defineProperty(Lampa.Api.sources, key, { get: function(){ return Api; }, enumerable: true, configurable: true }); ``` Не-enumerable свойство не попадает в `Object.keys()` → Lampa не рисует источник в панели выбора. **Баг Lampac, не нашей настройки.** 🔴 **`plugin.js` живёт в ОБРАЗЕ, не на HDD.** `docker inspect` показывает монтирования только на `catalog/sites` и `catalog/override`. Правка через `docker exec` **исчезнет при обновлении образа** — нужен bind-mount: ```yaml # docker-compose.yml - ./catalog/plugin.js:/lampac/module/Catalog/plugin.js:ro ``` ```bash ssh kraken 'docker exec lampac cat /lampac/module/Catalog/plugin.js' > /tmp/catalog-plugin.js cp /tmp/catalog-plugin.js /tmp/catalog-plugin.js.bak-$(date +%Y%m%d-%H%M%S) # правка двух вхождений defineProperty (только через patch, не sed) node --check /tmp/catalog-plugin.js # проверить синтаксис ДО заливки scp /tmp/catalog-plugin.js kraken:/srv/.../lampac/catalog/plugin.js ssh kraken 'cd /srv/.../lampac && docker compose up -d' ``` **Статус: фикс НЕ применён**, ждёт апрува Alex (2026-09-22). Полный разбор — [[personal/plans/lampac-watchlist-plugin]], раздел «ГЛАВНЫЙ ПИТФОЛЛ». ### 🛠 Диагностика ТВ через Chrome DevTools — порт 9998 **Главный инструмент для отладки UI Lampa.** webOS отдаёт Chrome DevTools Protocol: у процессов `WebAppMgr` есть `--remote-debugging-port=9998`. Даёт **чтение состояния живой Lampa** — ни логи, ни tcpdump этого не дают. ```bash # цель отладки + URL приложения curl -s --max-time 8 "http://192.168.1.70:9998/json" | /usr/bin/jq -r '.[] | "\(.type): \(.url)"' # → page: file:///media/developer/apps/usr/palm/applications/com.lampac.tv/index.html?... ``` Чтение переменных — `websocket-client` + `Runtime.evaluate`: ```python pages = json.load(urllib.request.urlopen("http://192.168.1.70:9998/json", timeout=8)) target = next(p for p in pages if "com.lampac.tv" in p.get("url","")) ws = websocket.create_connection(target["webSocketDebuggerUrl"], timeout=10) # Runtime.evaluate: Lampa.Storage.field('source') # Object.keys(Lampa.Api.sources) # Object.getOwnPropertyDescriptor(Lampa.Api.sources,'Мой список') ``` Готовые скрипты: `/tmp/lampa_probe.py` (состояние источника), `/tmp/lampa_probe3.py` (асинхронный вызов `menu()` каталога). **Ловушки:** ⚠️ **Замер соединений — на ТВ, не на сервере.** `sudo ss -tn | grep :9118` на Кра́кене сразу после старта Lampa даёт **0** (ложный вывод «не ходит»). Через ~30 с: `ssh root@192.168.1.70 'netstat -tn | grep 9118'` → **1 ESTABLISHED + 6 TIME_WAIT**. ⚠️ **`launch` через luna требует экранированного JSON в двойных кавычках**, иначе `errorCode: 2, invalid parameters`. `getForegroundAppInfo {}` работает без кавычек. ⚠️ **`tcpdump` на Кра́кене нет** — не ставили, DevTools дал больше. ⚠️ **`luna://com.webos.service.capture/takeScreenShot` не существует** на этом ТВ (`Unknown method`) — скриншот так не снять. ### Диагностика «каталога нет в UI» | Проверка | Команда | Норма | |---|---|---| | Плагин отдаётся | `curl -sI http://192.168.1.14:9118/catalog.js` | 200, ~32 КБ | | Каталоги зарегистрированы | `curl -s http://192.168.1.14:9118/catalog \| jq 'keys'` | 7 ключей, «Мой список» есть | | Данные отдаются | `curl -s "…/catalog/list?plugin=watchlist&cat=films" \| jq .total_pages` | 1 | | ТВ достаёт сервер | `ssh root@192.168.1.70 'curl -s -o /dev/null -w "%{http_code}" http://192.168.1.14:9118/catalog'` | 200 | | ТВ реально ходит | `ssh root@192.168.1.70 'netstat -tn \| grep 9118'` | ESTABLISHED
ноль = Lampa не стартовала | | Источник в списке | DevTools: `Object.keys(Lampa.Api.sources)` | **все** каталоги, вкл. «Мой список» | Порядок разбора: сначала сеть с ТВ → потом `netstat` на ТВ → потом `/catalog` на сервере → потом DevTools (состояние JS). **Если источник есть в `Api.sources` напрямую, но нет в `Object.keys()` — это баг `enumerable` в `plugin.js`, а не проблема настроек.** ⚠️ `docker logs lampac` **HTTP-запросы не пишет** — по логам факт обращения ТВ не проверить. ## Внутреннее устройство Lampac (разведка 2026-09-22) **33 модуля** в образе (`/lampac/module/`): `AdminPanel`, `Adult`, **`Catalog`**, `Community`, **`DLNA`**, `DatabaseEditor`, `ExternalBind`, `ForkPlayerXML`, `GStreamer`, `JacRed`, `Kit`, `LampaWeb`, `LampacApk`, `LogUserRequest-Lite`, `MsxNative`, `Music`, `NextHUB`, **`Online`**, `OnlineAnime`, `OnlineENG`, `OnlineGEO`, `OnlinePaid`, `OnlineRUS`, `OnlineUKR`, `PidTor`, `Potok`, `Proxy`, `SISI`, `Sync`, `Telemetry` (+ остальные). **Конфиг — `current.conf`** внутри образа (`/lampac/current.conf`, 157 КБ, генерируется из `base.conf` + `init.conf`). Рабочий каталог `/lampac`, файлы читаются **из корня**, не из `config/`. Выключение модуля — двумя механизмами: 1. **`SkipModules`** в `init.conf` / `current.conf` 2. **`manifest.json`** в каталоге модуля (`"enable": true|false`) ⚠️ **`init.conf` НЕ в образе по умолчанию** — его читают из `/lampac/init.conf`, файл перекрывает `base.conf`. Правка через `docker exec` **теряется при пересоздании контейнера**, поэтому смонтирован с HDD (`./config-module/init.conf:/lampac/init.conf:ro`). См. раздел docker-compose выше. ⚠️ `current.conf` в образе (157 КБ) — **сгенерированный** результат (`guid`, `listen`, полный конфиг). Это **не** входной файл, править его бессмысленно. ### Полезные роуты (проверены curl'ом) | Роут | Код | Что | |---|---|---| | `/` | 200 | LampaWeb (UI, ``) | | `/online.js` | 200 | плагин VOD — 70+ онлайн-источников | | `/lg.ipk` | 200 | установщик клиента для LG (6.9 МБ) | | `/sisi` | 200 | JSON-список платформ 18+ | | `/dlna` | 200 | DLNA-модуль | | `/ts/echo` | 200 | TorrServer (отдаёт версию, напр. `MatriX.135`) | | `/catalog.js` | **200** | плагин каталогов (включён 2026-09-22) — см. раздел Catalog | | `/catalog` | **200** | JSON каналов каталогов | | `/watchlist.json`, `/watchlist-.json` | **200** | наш JSON-каталог watchlist | | `/cub`, `/api`, `/lampac.js` | 404/500 | — | ⚠️ `wwwroot` отдаётся статикой: **любой** файл, положенный туда, доступен по HTTP от корня (`http://:9118/<файл>`). Проверено записью пробного файла. ## Radarr/Sonarr как источник метаданных для каталога (2026-09-22) **Зачем:** каталог watchlist нуждается в **TMDB ID и постерах** для элементов. Список `movies-watchlist.md` — просто текст, там этого нет. Но фильмы добавлялись в **Radarr**, который уже держит метаданные (тянет из TMDB при добавлении). ### Что отдаёт Radarr API ```bash KEY=$(ssh kraken 'grep -oE "[^<]+" \ /srv/...6194539b.../docker/radarr/config/config.xml' | sed 's/<[^>]*>//g') curl -sL "http://localhost:7878/api/v3/movie?apikey=$KEY" | jq . ``` ```json { "tmdbId": 1292695, "title": "They Will Kill You", "year": 2026, "hasFile": true, "monitored": true, "images": [{"coverType": "poster", "remoteUrl": "https://image.tmdb.org/t/p/original/6oI4oQKTWMVUlr8Ivqydp28R"}] } ``` Маппинг в формат каталога: `tools.tmdbId` → `id`/`href`, `images[].remoteUrl` → `img`, `title`+`year` → `title`, `hasFile` → признак «уже скачан». ### 🔴 Питфолл: `curl -sL`, без `-L` Radarr отдаёт 307 ```bash curl -s "http://localhost:7878/api/v3/movie?apikey=$KEY" | jq length # ❌ 0 байт curl -sL "http://localhost:7878/api/v3/movie?apikey=$KEY" | jq length # ✅ 50 ``` ### Окружение arr-стека на Кра́кене | Сервис | Порт | Конфиг на HDD | |---|---|---| | radarr | 7878 | `/srv/...6194539b.../docker/radarr/config/` | | sonarr | 8989 | аналогично | | prowlarr | 9696 | аналогично | | transmission | — | отдельный контейнер | Постеры складываются в `/config/MediaCover/` (46 каталогов на 2026-09-22) — можно раздавать их статикой через `wwwroot`, не дёргая CDN TMDB. ⚠️ `sqlite3` в контейнерах arr **нет** — БД (`radarr.db`) читать либо через `strings`, либо вытащить файл на хост (там `/usr/bin/sqlite3` есть). ### Управление автоскачкой (два рычага, оба нужны) | Рычаг | Команда | Эффект | |---|---|---| | `monitored` | `PUT /api/v3/movie/editor` | фильм не ищется/не качается, метаданные остаются | | `rssSyncInterval` | `PUT /api/v3/config/indexer` | `0` = Radarr не ходит в RSS (сейчас 30 мин) | ⚠️ В Radarr **нет** состояния «источник, но не грузить» — это ровно `monitored: false`. ### Контур воспроизведения — не путать (✅ проверено 2026-09-22) | Слой | Что делает | Пишет на HDD | |---|---|---| | Radarr + Sonarr + Transmission | автоскачка релизов в библиотеку | **да** | | Lampac **TorrServer** (встроенный) | стриминг из UI Lampa по требованию | ❌ **нет — доказано** | | Catalog «Мой список» | показ секций с постерами | ❌ ничего не качает | Просмотр через Lampa **не пополняет библиотеку**: TorrServer отдаёт поток и выбрасывает. `transmission` к Lampa отношения не имеет. **Доказательство, что TorrServer не пишет на диск** — `/lampac/data/ts/settings.json`: ```bash ssh kraken 'docker exec lampac cat /lampac/data/ts/settings.json' | jq .BitTorr ``` ```json { "CacheSize": 100663296, // 96 МБ — RAM-кэш "UseDisk": false, // 🔴 НА ДИСК НЕ ПИШЕТ "TorrentsSavePath": "", // путь сохранения пуст "TorrentDisconnectTimeout": 180, // раздача живёт 3 мин после остановки "PreloadCache": 0, "ReaderReadAHead": 83 } ``` `UseDisk: false` — дефолт Lampac, настраивать не надо. Кэш Lampac целиком: `/lampac/cache/` = 31 МБ (SQLite, Playwright, gstreamer-registry — **не фильмы**), `/lampac/data/` = 44 МБ. Список торрентов пуст: `curl -s -X POST .../ts/torrents -d '{"action":"list"}'` → `[]`. Живость TorrServer: `curl -s "http://192.168.1.14:9118/ts/echo"` → `MatriX.135`. ### 🔴 Поиск торрентов — Jackett внутри Lampac (отдельный НЕ нужен) **Ловушка:** в `lampainit.js` прописано `parser_torrent_type = 'jackett'`, но **контейнера Jackett нет** в `docker ps`. Это не поломка — Lampac **сам отдаёт Jackett-совместимый API**. ```bash curl -s "http://192.168.1.14:9118/lampainit.js" | grep -oE "Lampa.Storage.set\('[^']+', ?'[^']*'\)" # jackett_url = '192.168.1.14:9118' ← сам Lampac, не сторонний сервис # jackett_key = '1' # parser_torrent_type= 'jackett' # parser_use = 'true' # source = 'cub' # torrents_use = true (lampa_settings) ``` ⚠️ **API живёт на КОРНЕ, без префикса `/jackett`:** ```bash curl -s "http://192.168.1.14:9118/api/v2.0/indexers/all/results?apikey=1&Query=matrix" # ✅ 200 — реальные результаты: Tracker:rutor, MagnetUri, Size, Seeders, ffprobe curl -s "http://192.168.1.14:9118/jackett/api/v2.0/indexers/all/results" # ❌ 404 ``` **Вывод:** ничего поднимать не надо, поиск торрентов работает штатно. ### ⚠️ Sonarr — второй источник автоскачки (не только Radarr) Alex упомянул его не зря. Замер 2026-09-22: ```bash SKEY=$(ssh kraken 'grep -oE "[^<]+" \ /srv/...6194539b.../docker/sonarr/config/config.xml' | sed 's/<[^>]*>//g') # → 15fbec32f1cb4b05b1c2b229ce27359b curl -sL "http://localhost:8989/api/v3/series?apikey=$SKEY" | jq length # → 13 сериалов curl -sL "http://localhost:8989/api/v3/series?apikey=$SKEY" \ | jq '[.[]|select(.monitored)]|length' # → 13 monitored curl -sL "http://localhost:8989/api/v3/queue?apikey=$SKEY" | jq .totalRecords # → 19 в очереди ``` | Сервис | Объектов | monitored | Очередь | |---|---|---|---| | Radarr | 50 фильмов | **45** | **6** | | Sonarr | 13 сериалов | **13** | **19** | **Отключать надо оба** — иначе Sonarr продолжит качать сериалы. ### Transmission — замер ```bash ssh kraken 'docker exec transmission cat /config/settings.json' > /tmp/trans-settings.json # download-dir /downloads · rpc-port 9091 · speed-limit-down-enabled false # /config/torrents/*.torrent = 257 файлов (накопленный список РАЗДАЧ, не активная качка) # CPU 0.76% — простаивает. NetIO 290G↑/26.7G↓ (суммарно за жизнь контейнера) ``` Полный состав контейнеров Кра́кена: `lampac · radarr · jellyfin · transmission · portainer · flaresolverr · prowlarr · sonarr · watchtower · rclone · xray-reverse-bridge · hermes-kraken · cloudflared` ## Что осталось 1. **Лимит кэша TorrServer** — сейчас дефолт (`CacheSize` 96 МБ, `UseDisk: false`). Диск 396 ГБ свободно, на HDD TorrServer не пишет вовсе — вопрос неактуален. 2. **Шаг 7 плана — watchlist в UI Lampa** — ✅ **серверная часть сделана** 2026-09-22, 🔴 **архитектура признана неверной**, переделывается: см. [[personal/plans/lampac-watchlist-plugin]], разделы «ПЕРЕДЕЛКА 2» и «✅ ПРОВЕРКА "как это реализуемо"». 3. **Проверка полосы на торрент-источнике** (remux 60–80 Мбит) — не сделана. 4. **Постеры/карточка в каталоге** — не сделаны, `image` пустой. Решение — брать из Radarr (`images[].remoteUrl`) или `MediaCover/`, см. раздел выше. 5. **Автоскачка Radarr + Sonarr** — 45 monitored / очередь 6 у Radarr, 13 monitored / очередь 19 у Sonarr. План отключения на апруве ([[personal/plans/lampac-watchlist-plugin]], «ПЕРЕДЕЛКА 2»).