--- created: '2026-09-22' namespace: personal related: - '[[personal/plans/lampac-kraken]]' - '[[personal/tech/lampac-deployment]]' - '[[family/projects/watchlist-sync]]' - '[[family/projects/watchlist-automation]]' - '[[family/documents/movies-watchlist]]' status: rework tags: - lampac - lampa - watchlist - catalog - webos - plugin - kraken - radarr title: Watchlist в UI Lampa — каталог из списка type: plan updated: '2026-09-22' --- # Watchlist в UI Lampa — каталог из списка > Реализовано 2026-09-22. План был переписан после разведки кода модуля Catalog. > Базовое развёртывание — [[personal/plans/lampac-kraken]] (шаги 1–6 закрыты). > > 🔴 **АРХИТЕКТУРА ПРИЗНАНА НЕВЕРНОЙ 2026-09-22 — переписывается.** > Сделано `menu` (подмена родных разделов) вместо `main` (секции СВЕРХУ). > Плюс источник данных — парсинг текстового `.md` вместо **Radarr API**, где уже > есть TMDB ID и постеры. Alex: «Я предполагал что будет отображаться то что и раньше > (список cub) но В НЕГО будет сверху добавлено несколько секций в которых будут > РЕАЛЬНЫЕ ЭЛЕМЕНТЫ - а не сгенерируют пустые заглушки!» > См. «🔴 АРХИТЕКТУРНАЯ ОШИБКА» — читать ПЕРВЫМ. > > **Итог одной строкой:** серверная часть каталога работает (4 категории, 360/33/3/7), > но реализована не по замыслу: подменяет каталог вместо добавления секций, > и не использует Radarr как источник постеров/ID. ## 🔴 АРХИТЕКТУРНАЯ ОШИБКА 2026-09-22 — почему каталог «не работает» **Что хотел Alex (дословно):** > «Я предполагал что будет отображаться то что и раньше (список cub) но В НЕГО будет > сверху добавлено несколько секций в которых будут РЕАЛЬНЫЕ ЭЛЕМЕНТЫ — а не сгенерируют > пустые заглушки! Нахуя это надо то?! Чё блядь значит оно так и должно быть?!» **Что сделано вместо этого — две независимые ошибки:** ### Ошибка 1 — структура: `menu` вместо `main` Модуль Catalog умеет **два независимых блока**, и они делают РАЗНОЕ: | Блок YAML | Что делает | Поведение | |---|---|---| | **`menu`** | **подменяет** родные разделы Lampa (Фильмы/Сериалы/Аниме) | ❌ заполнен → родной каталог исчезает | | **`main`** | **добавляет секции на главную СВЕРХУ** | ✅ родной каталог остаётся | Реализация заполнила **оба** (`menu` и `main`) → при выборе источника «Мой список» исчез весь CUB-контент. **Симптом по словам Alex:** «при его выборе исчезло все что было до этого из списка CUB во всех разделах. Нет превью.» **Фикс:** в `sites/watchlist.yaml` **убрать блок `menu`**, оставить только `main`. Тогда родной каталог (CUB/tmdb) работает как раньше, а твои секции идут рядами сверху. Код: `plugin.js` → `function menu()` собирает из `catalog.menu`; если ключа нет — `menu` не подменяется (вернётся пустой, Lampa использует родные). `main()` строит `parts_data` из `catalog.main` и подмешивает их в `Lampa.Api.partNext`. ### Ошибка 2 — источник данных: парсинг `.md` вместо Radarr API Генератор читал `movies-watchlist.md` — **текстовый список названий**. В нём **нет ни TMDB ID, ни постеров**. Результат: | Метрика | Значение | |---|---| | Всего позиций (films) | 360 | | Из них с постером (`img != null`) | **0** | | Из них с реальным ID (`film/...`) | **18** | Остальные 342 отдавались с `id` = **ссылка на поиск Кинопоиска** (`index.php?kp_query=...`). Отсюда жалоба «при открытии элемента пишет *здесь пусто*»: Lampa идёт по ссылке, получает страницу поиска, а не фильм. **Правильный источник — Radarr API.** Alex: «Они уже резолвятся же блядь!! Оно же добавлялось в arr!» — TMDB ID и постеры уже есть там, потому что фильмы добавлялись в Radarr. ```bash # API-ключ Radarr grep -oE "[^<]+" /srv/dev-disk-by-uuid-6194539b-.../docker/radarr/config.xml # → cbcb8ec320104359920c0f611d503c7b # ⚠️ ВАЖНО: -L обязателен! Без него Radarr отдаёт 307, тело пустое curl -sL "http://localhost:7878/api/v3/movie?apikey=$KEY" | jq length # → 50 ``` **Форма ответа Radarr (то, что нужно было брать с самого начала):** ```json { "tmdbId": 1292695, "title": "They Will Kill You", "year": 2026, "hasFile": true, "images": [{"coverType":"poster", "remoteUrl":"https://image.tmdb.org/t/p/original/6oI4oQKTWMVUlr8Ivqydp28R"}] } ``` Есть **всё**: TMDB ID → рабочий `href`, `remoteUrl` → постер, `title`/`year` → название, `hasFile` → скачан или нет. Плюс постеры локально: `/config/MediaCover/` (46 каталогов). ⚠️ **Покрытие:** в Radarr **50 фильмов**, в `.md` — **360**. Разница (310) — то, что в список записано, но в Radarr не заказано. Что с ними делать — **решение Alex не получено**, открытый вопрос №1 ниже. ### Что НЕ надо было делать (записано, чтобы не повторять) - Парсить текст вместо API, где данные уже резолвлены - Складывать `menu` + `main` одновременно - Считать «список названий» готовым каталогом для UI с постерами ### Alex: корневая претензия к процессу > «А нахуя ты это так реализовал?? Подумать блядь вообще не твое!» Разбор: задача звучала как «покажи список в UI» — и была прочитана буквально («отдать строки файла»). **Надо было сначала выяснить, откуда брать постеры и ID** (а ответ — Radarr, куда фильмы и добавлялись), а не начинать с парсера текста. ## Задача Показывать список фильмов Alex (`family/documents/movies-watchlist.md`) **в UI Lampa на ТВ** — каталогом. Не локальный плейбэк. Формулировка Alex: «отображение фильмов из списка». **Уточнение от 2026-09-22 (важное!):** каталог **добавляется СЕКЦИЯМИ СВЕРХУ** к родному каталогу Lampa. Родной CUB-контент **не подменяется и не исчезает**. Элементы — **реальные** фильмы с постерами и карточками, а не пустые заглушки (см. «🔴 АРХИТЕКТУРНАЯ ОШИБКА»). ⚠️ **Что НЕ спрашивать повторно.** Задача — **отображение**, не воспроизведение. Список — это перечень, а не источник видео. Вопрос «а что играть по клику?» — **выдумка сверх постановки**, Alex на него реагировал раздражением («я чё тебе сказал в постановке задачи»). Штатное воспроизведение Lampa (торренты/онлайн) работает само, отдельно решать нечего. ## ✅ Затык снят: чем это делается В образе Lampac уже есть модуль **`Catalog`** — YAML-каталоги → плагин Lampa `/catalog.js`. Сейчас **выключен**. Писать плагин с нуля **не нужно**. ## Как работает Catalog (разобрано по коду 2026-09-22) **Файлы в контейнере:** `/lampac/module/Catalog/` — `Controllers/`, `plugin.js`, `README.md`, `sites/*.yaml` (10 готовых примеров: `vokino`, `rezka`, `kinopub`, `anime`, …). ### Механика | Что | Как | |---|---| | Данные | `sites/.yaml` — описание сайта: `host`, `menu`, `search`, `list`, `content` | | Подстановки | `override/.yaml` и `override/_.yaml` — переопределения поверх базового | | Роуты | `/catalog.js` (плагин), `/catalog` (JSON каналов), `/catalog/list`, `/catalog/card` | | WAF | `^/catalog/` → **10 запросов/сек** | | Имя файла | только `[a-z0-9-]`; есть fallback-поиск по `displayname` | ### Ключевое: обязателен HTTP ```csharp if (!url.StartsWith("http")) url = $"{init.host}/{url}"; // ListController.cs:87 ``` `host` из YAML сканируется **по HTTP** (`Http.Get` / Playwright / RCH). **Локальные файлы напрямую Catalog читать не умеет** — только HTTP-ответ. ### Два режима парсинга ```csharp bool? jsonPath = contentParse.jsonPath ?? init.jsonPath; if (jsonPath == true) json = JToken.Parse(html); // JSON-режим else doc.LoadHtml(html); // HTML-режим ``` - `jsonPath: true` → ответ парсится как **JSON**, элементы берутся по `nodes` (`$.channels[*].details`) - иначе → HTML через HtmlAgilityPack 🔴 **Это и есть рычаг:** любой **свой HTTP-эндпоинт, отдающий JSON нужной формы**, подойдёт как `host`. Тогда задача сводится к генерации JSON + YAML-обёртки, без правки кода Lampac. ### Форма элемента (`content` в YAML) ```yaml content: nodes: "$.channels[*].details" # jsonPath-выражение (JSON) или CSS/XPath (HTML) name: { attribute: name } original_name:{ attribute: originalname } href: { attribute: ident } image: { attribute: poster } year: { attribute: released } serial_key: { attribute: type, format: | return value is "serial" ? value : null; } args: - { name_arg: vote_average, attribute: rating_kp } ``` Карточка — отдельная секция `card_parse` + `card_args` (описание, рейтинги, жанры, страна). Полный живой пример — `sites/vokino.yaml` (копия в [[personal/tech/lampac-deployment]], раздел Catalog). ## Включение модуля — 2 правки ```jsonc // current.conf (внутри контейнера /lampac/current.conf) "SkipModules": [ "Tracks", "Transcoding", ... ] // ← убрать "Catalog" (строка ~277) "plugins": { ..., "catalog": true } // ← было false (строка ~1653) ``` 🔴 **ПИТФОЛЛ — `init.conf` НЕ смонтирован.** В нашем `docker-compose.yml` нет volume на `/lampac/init.conf`, файл живёт в образе. Правки через `docker exec` **исчезнут при пересоздании контейнера** (`docker compose up`, watchtower-обновление образа). Варианты: - (а) добавить volume `./config/init.conf:/lampac/init.conf:ro` — надёжно, но нужен первичный `docker cp` - (б) править через **AdminPanel** `/adminpanel` (модуль, правит `init.conf` + `current.conf`) - (в) `docker cp` + commit — плохо **Рекомендация: (а)** — конфиг на HDD, переживает пересоздание. ## Архитектура решения ``` family/documents/movies-watchlist.md ← источник (vault, git-синхрон) │ существующий парсер: watchlist-sync/parser.py ▼ генератор (Python, на Кра́кене) │ пишет JSON нужной формы ▼ /lampac/... JSON-эндпоинт, доступный по HTTP с Lampac │ ▼ sites/watchlist.yaml ← Catalog-описание, host=<наш JSON URL> menu: Фильмы / Мультфильмы / Аниме (по секциям .md) │ ▼ Lampa на ТВ: плагин http://192.168.1.14:9118/catalog.js ``` **Откуда отдавать JSON — варианты (выбрать на этапе реализации):** | # | Где | Плюс | Минус | |---|---|---|---| | 1 | **статика в `/lampac/wwwroot/`** | ничего не поднимать, Lampac сам отдаёт | только чтение, обновление = перезапись файла | | 2 | **отдельный лёгкий HTTP-сервер** на Кра́кене | есть API/логика | ещё один контейнер | | 3 | `file://` | — | ❌ не поддерживается (только http) | **Склоняюсь к (1)** — генерировать `wwwroot/watchlist.json`, Lampac отдаёт его сам, без новых сущностей. ## Обновление каталога — реализовано Скрипт `~/scripts/watchlist-catalog.sh` (root:root, ставить только `sudo install -m 755`): 1. `python3 $LAMPAC/scripts/gen_watchlist.py $LAMPAC/wwwroot/watchlist.json` 2. `docker restart lampac` — **обязательно**, сбрасывает кэш Catalog (см. питфолл 3) Cron: `30 2 * * *` → лог `~/logs/watchlist-catalog.log`. Время выбрано после `watchlist-nightly` (01:00) и `watchlist-cleanup` (02:00). Бэкап crontab перед установкой: `~/crontab-backup-20260922-112915.txt`. ⚠️ **`watchlist-catalog.sh` сделан отдельной задачей, а не врезан в `watchlist-nightly.sh`.** Причина: `nightly` — рабочий боевой скрипт, его падение остановило бы весь пайплайн списка. Отдельный cron падает изолированно. ## Что осталось — ПЕРЕДЕЛКА (после признания архитектуры неверной) | # | Шаг | Статус | |---|---|---| | 1 | Генератор берёт данные **из Radarr API** (`localhost:7878/api/v3/movie`), а не парсит текст | ⏳ ждёт апрува | | 2 | Подставлять в `id` → `tmdbId`, в `img` → `images[].remoteUrl` (постер), `year` → `year` | ⏳ | | 3 | Из `sites/watchlist.yaml` **убрать блок `menu`**, оставить `main` → родной CUB остаётся | ⏳ | | 4 | Постеры кэшировать локально (Radarr `MediaCover/` или проксировать через `tmdbproxy`) | ⏳ | | 5 | Фикс `plugin.js` (`enumerable:true`) — **отдельная независимая правка**, см. ниже | ⏳ ждёт апрува | **Порядок:** шаги 1–4 закрывают жалобу «пустые заглушки + исчез CUB». Шаг 5 — про то, что источника вообще не видно в списке выбора. Приёмка (формулировка Alex): **родной список CUB на месте, сверху добавлены ряды с реальными фильмами из списка, у элементов есть постеры и рабочие карточки.** ## Диагностический скрипт для ТВ (`/tmp/lampa_probe.py`) Правки в UI Lampa по SSH напрямую не сделать, но **состояние читается** через DevTools: ```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).join('|') # Object.getOwnPropertyDescriptor(Lampa.Api.sources,'Мой список') # Lampa.Api.sources['Мой список'].getCatalog() ``` Требует `pip3 install websocket-client` (стоит). См. [[family/how-to/tv-lampac-plugin]]. ## Известные ограничения | Ограничение | Детали | |---|---| | **Постеров нет** | `image` пустой. Опционально — тянуть через TMDB-кэш Lampac (`tmdbproxy`) | | **Карточки нет** | Только список названий. `card_parse` не настроен — Lampa может показывать пустую карточку | | **`🗓️` upcoming показываются** | Не прячем. Решение Alex не запрашивалось | | **Перезапуск контейнера** | Каждое обновление каталога = `docker restart lampac` (~30 сек простоя). Кэш не инвалидируется иначе | ## ⚠️ Что проверено, а что нет **Проверено по HTTP (серверная часть):** | Проверка | Результат | |---|---| | `/catalog.js` по IP | 200, ~32 КБ, `api_host:'http://192.168.1.14:9118'` | | Каталог в `/catalog` | **7 ключей**, «Мой список» первым (Rezka, Kinobase, FilmixApp, VoKino, Eneyida, KinoUkr, Мой список) | | JSON-файлы | `watchlist.json` + `watchlist-{films,new,cartoons,anime}.json` — все 200 | | Выдача по категориям | films 360 · new **33** · cartoons 3 · anime 7 (после фикса сирот 2026-09-22) | | Формат ответа `/catalog/list` | `{page, results, total_pages}`, `total_pages: 1` для films | | Сортировка | ⭐ → рейтинг ↓ → ужасы | | Чистота названий | эмодзи-маркеров 0 | | Сирота «Конец света» | ❌ отсутствует во всех 4 категориях | **НЕ проверено:** - ✅ **UI Lampa на ТВ — ПРИЧИНА НАЙДЕНА 2026-09-22** (см. ниже «🔴 ГЛАВНЫЙ ПИТФОЛЛ»). Диагностика через Chrome DevTools ТВ (`http://192.168.1.70:9998/json`): - ТВ **физически достаёт** Кра́кен — `curl` с ТВ отдаёт `/catalog` 200 за 0.26 с. - Lampa **работает и ходит на сервер** — соединение `192.168.1.70 → 192.168.1.14:9118` ESTABLISHED. - `Storage.field('source')` = **`'Мой список'`** — источник уже выбран! - `Lampa.Api.sources['Мой список']` **существует** и отдаёт каталог (`getCatalog()` → `search, search_lazy, movie, cartoons, anime, menu, main`). - ❌ НО: `Object.keys(Lampa.Api.sources)` = `'tmdb|cub'` — **источника нет в перечислении**. - Причина: `defineProperty(..., {get: ..., enumerable:false, configurable:false})`. - **Диагноз: баг в `plugin.js` самого Lampac, а не в нашей настройке.** UI Lampa строит список источников через `Object.keys()` — «Мой список» из него выпадает, поэтому в UI источник не видно и выбрать его нельзя. - ❌ Боевой прогон крона в 02:30 (скрипт отработал вручную успешно). - ❌ Карточка/постеры в UI. ## 🔴 ГЛАВНЫЙ ПИТФОЛЛ: источник не виден в UI Lampa (`enumerable: false`) **Симптом.** Alex: «Ни на главной ни в Фильмах нет! Там только Сейчас смотрят, рекомендуем итд». Плагин добавлен, серверная часть отдаёт всё корректно, а в UI источника нет. **Диагностика (по возрастанию точности):** 1. `ssh kraken 'sudo ss -tn | grep ":9118"'` — сначала показал **0 соединений** (ложный вывод: замер сделан в момент старта Lampa). Через 30 с на ТВ: `netstat -tn | grep 9118` → **1 ESTABLISHED + 6 TIME_WAIT** — Lampa скачала все файлы. 2. Foreground-приложение: `luna-send -n 1 -f luna://com.webos.applicationManager/getForegroundAppInfo {}` → `com.lampac.tv`. Запускать надо `launch` с **экранированным JSON в кавычках**, иначе `errorCode: 2 invalid parameters`. 3. Доступ к отладчику ТВ: **`curl http://192.168.1.70:9998/json`** — webOS отдаёт Chrome DevTools Protocol на порту 9998 (у `WebAppMgr` в аргументах `--remote-debugging-port=9998`). Через `websocket` + `Runtime.evaluate` читаются переменные живой Lampa. 4. Ключевой замер: `JSON.stringify(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; } }); ``` Свойство создаётся не-enumerable → `Object.keys()` его не видит → Lampa не рисует источник в панели выбора. При этом `Api.sources['Мой список']` доступен напрямую и каталог отдаёт. **Фикс** (одна правка, два вхождения): ```js Object.defineProperty(Lampa.Api.sources, key, { get: function(){ return Api; }, enumerable: true, configurable: true }); ``` **Как применять — `plugin.js` живёт в образе, не на HDD!** (проверено: `docker inspect` не показывает монтирования на `module/Catalog`, монтируются только `catalog/sites` и `catalog/override`). Правка через `docker exec` **исчезнет при обновлении образа**. Предложенный путь (на апруве у Alex, 2026-09-22): ```yaml # docker-compose.yml volumes: - ./catalog/plugin.js:/lampac/module/Catalog/plugin.js:ro ``` ```bash # 1. забрать, поправить локально (sed/grep по файлу не применять — только patch) ssh kraken 'docker exec lampac cat /lampac/module/Catalog/plugin.js' > /tmp/catalog-plugin.js # 2. бэкап cp /tmp/catalog-plugin.js /tmp/catalog-plugin.js.bak-$(date +%Y%m%d-%H%M%S) # 3. правка двух вхождений defineProperty → +enumerable:true,configurable:true # 4. проверка синтаксиса ПЕРЕД заливкой node --check /tmp/catalog-plugin.js # 5. на HDD, добавить volume, пересоздать scp /tmp/catalog-plugin.js kraken:/srv/.../lampac/catalog/plugin.js ssh kraken 'cd /srv/.../lampac && docker compose up -d' # 6. проверка на ТВ: Object.keys(Lampa.Api.sources) должен стать 'tmdb|cub|Мой список' ``` **Откат:** убрать строку volume из compose → `docker compose up -d`. Плюс бэкап файла в `/tmp`. **Статус: правка НЕ применена** — ждёт апрува Alex (2026-09-22). ## Готовые команды диагностики ТВ (переиспользовать) ```bash # 1. Что запущено на ТВ (luna-send пишет в tty → нужен script -q -c) ssh root@192.168.1.70 'script -q -c "luna-send -n 1 -f \ luna://com.webos.applicationManager/getForegroundAppInfo {}" /dev/null' # 2. Запустить приложение (JSON экранирован И обёрнут в кавычки) ssh root@192.168.1.70 'script -q -c "luna-send -n 1 -f \ luna://com.webos.applicationManager/launch \"{\\\"id\\\":\\\"com.lampac.tv\\\"}\"" /dev/null' # 3. Достаёт ли ТВ Кра́кен ssh root@192.168.1.70 'curl -s -o /dev/null -w "%{http_code} %{time_total}s\n" \ --max-time 8 http://192.168.1.14:9118/catalog' # 4. Живые соединения с Lampac (на ТВ, не на сервере!) ssh root@192.168.1.70 'netstat -tn 2>/dev/null | grep 9118' # 5. DevTools-цель Lampa на ТВ curl -s --max-time 8 "http://192.168.1.70:9998/json" | /usr/bin/jq -r '.[] | "\(.type): \(.url)"' ``` Скрипты-пробы DevTools: `/tmp/lampa_probe.py` (состояние источника), `/tmp/lampa_probe3.py` (асинхронный вызов `menu()` каталога). ⚠️ `tcpdump` на Кра́кене **нет** — для съёма трафика ставить не стали, использован DevTools-подход (дал больше: видно состояние JS, а не только пакеты). ⚠️ `luna://com.webos.service.capture/takeScreenShot` на этом ТВ **не существует** (`Unknown method`) — скриншот так не снять. ## Файлы реализации (на HDD Кра́кена) Корень: `/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/lampac/` | Путь | Роль | |---|---| | `config-module/init.conf` | включает Catalog → `/lampac/init.conf:ro` | | `catalog/sites/watchlist.yaml` | описание каталога | | `catalog/override/` | переопределения (пусто) | | `wwwroot/watchlist.json` | все секции | | `wwwroot/watchlist-.json` | по файлу на категорию | | `scripts/gen_watchlist.py` | генератор | | `docker-compose.yml` | 7 монтирований; бэкапы `.bak-*` | На хосте: `~/scripts/watchlist-catalog.sh`, лог `~/logs/watchlist-catalog.log`. ## Откат ```bash # выключить каталог ssh kraken 'sed -i "s/\"catalog\": true/\"catalog\": false/" \ /srv/.../lampac/config-module/init.conf && docker restart lampac' # в Lampa убрать плагин catalog.js # cron: crontab -l | grep -v watchlist-catalog | crontab - ``` Ничего в коде Lampac не патчилось — только конфиг и данные. ## Статус: ✅ РЕАЛИЗОВАНО 2026-09-22 Каталог «Мой список» работает, отдаётся в Lampa через `/catalog.js`. ### Что сделано | # | Шаг | Результат | |---|---|---| | 1 | Включён модуль Catalog | `SkipModules` без Catalog + `catalog: true` → `/catalog.js` 200 | | 2 | `init.conf` смонтирован | `./config-module/init.conf:/lampac/init.conf:ro` — правится на HDD, не теряется | | 3 | `sites/` + `override/` смонтированы | `./catalog/sites`, `./catalog/override` | | 4 | `wwwroot` смонтирован | `./wwwroot` — Lampac сам отдаёт JSON по HTTP | | 5 | Генератор написан | `lampac/scripts/gen_watchlist.py` | | 6 | `sites/watchlist.yaml` создан | `lampac/catalog/sites/watchlist.yaml` | | 7 | Автообновление в cron | `30 2 * * *` → `~/scripts/watchlist-catalog.sh` | | 8 | Проверка | 4 категории: films 360, new 34, cartoons 3, anime 7 | ### Архитектура (финальная) ``` family/documents/movies-watchlist.md ← источник │ gen_watchlist.py (свой парсер, ~200 строк) ▼ lampac/wwwroot/watchlist.json ← все секции lampac/wwwroot/watchlist-.json ← по файлу на категорию │ Lampac отдаёт wwwroot по HTTP сам ▼ lampac/catalog/sites/watchlist.yaml ← описание для Catalog │ plugin=watchlist, jsonPath: true, nodes: $.items[*] ▼ Lampa на ТВ: плагин http://192.168.1.14:9118/catalog.js ``` ### 🔴 Питфоллы, найденные по ходу (не повторять) 1. **Catalog ВЫБРАСЫВАЕТ элемент с пустым `href`.** `ListController.cs`: `if (!IsNullOrWhiteSpace(name) && !IsNullOrWhiteSpace(href))`. Симптом: из 404 позиций отдалось 52 — ровно те, у кого есть ссылка Кинопоиска. **Фикс:** генератор подставляет fallback-href `https://www.kinopoisk.ru/index.php?kp_query=<название>`. 2. **Catalog НЕ подставляет `{cat}` в `nodes`.** JSONPath статичен, отфильтровать секцию внутри одного файла нечем. Симптом: все категории отдавали весь список (404 вместо 360/34/3/7). **Фикс:** генератор пишет отдельный плоский файл на категорию (`watchlist-films.json` и т.д.), `nodes: "$.items[*]"` одинаков для всех. 3. **Кэш Catalog без версии данных.** Ключ `catalog:{plugin}:{search}:{sort}:{cat}:{page}` — не зависит от содержимого. Симптом: после перегенерации JSON выдача не менялась. **Фикс:** `docker restart lampac` в скрипте обновления. 4. **`init.conf` не был смонтирован.** Файл живёт в образе; правки через `docker exec` теряются при пересоздании. Правки шли в `current.conf` (сгенерированный) — неверная цель. **Фикс:** volume `:ro` + свой минимальный `init.conf` (перекрывает `base.conf`). 5. **`api_host` подставляется из запроса** (`{localhost}` → хост, по которому пришли). По IP `192.168.1.14` отдаёт правильный адрес — специально ничего настраивать не нужно. 6. **Кириллица в inline-jq внутри `ssh '...'`** ломает команду (tirith + quoting). Выносить в файл или убирать строки с кириллицей. 7. **Осиротевшие строки в `## Новинки` давали дубли в каталоге.** Строки без `👍`/`👎` (например `- [ ] ⬇️ Название`) `process-thumbs` не трогал — они висели в Новинках навсегда. Если фильм уже `[x]` в главном списке — получался дубль: непросмотренный «Конец света» при просмотренном оригинале. **Фикс в `watchlist-sync/parser.py`** (удаление сирот + флаг `_patch_dropped`) и `cli.py`. Разбор: [[family/projects/watchlist-automation]], секция «ОСИРОТЕВШИЕ СТРОКИ». 8. **Двойное экранирование при правке `parser.py` скриптами.** `r'\1'` превращался в байт `\x01`, `r'\b'` — в `\x08`. Синтаксис валиден, регекспы молчат. **Проверять файл на `chr(1)` и `chr(8)` после патча.** ### Управление ```bash ssh kraken cd /srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/lampac # перегенерировать каталог + сбросить кэш bash ~/scripts/watchlist-catalog.sh # логи tail -f ~/logs/watchlist-catalog.log # только генератор (без перезапуска) python3 scripts/gen_watchlist.py \ /home/kraken/obsidian/family/documents/movies-watchlist.md \ wwwroot/watchlist.json ``` ### Откат ```bash # выключить каталог ssh kraken 'sed -i "s/\"catalog\": true/\"catalog\": false/" \ /srv/.../lampac/config-module/init.conf && docker restart lampac' # в Lampa убрать плагин catalog.js ``` ## Правила отбора и сортировки (утверждено Alex 2026-09-22) ### Фильтрация | Правило | Действие | |---|---| | `[ ]` непросмотренные | **показывать** | | `[x]` просмотренные | ❌ скрыть | | `👎` | ❌ скрыть | | `🗓️` upcoming | показывать (решить при реализации — есть в файле) | | `❓` ambiguous | показывать (название как есть) | ### Сортировка внутри списка — трёхключевая 1. **`⭐` (U+2B50) — первыми** 2. **Рейтинг — по убыванию** (IMDb/KP из строки файла; строки `[название](kp) (год) IMDb N.N KP N.N`) 3. **Ужасы (`🧟♀️`, TMDB genre_id=27) — последними** ⚠️ **Только `⭐` (U+2B50)** — как в `sync-down` (см. [[family/projects/watchlist-automation]]). `✭` (U+272D) приоритетом **не является**. ### Порядок секций 1. **Главный список — первым.** Это `## Фильмы` 2. Дальше — **остальные по порядку в файле**: `## Новинки - 👍/👎`, `## Мультфильмы`, `## Аниме`, … ⚠️ `## Новинки👎` — **не показывать** (это скрытая секция отказов). ⚠️ Секция `## Новинки` содержит `👍`/`👎`; `👎`-строки прячем, `👍` показываем. ### Источник рейтинга В строках со ссылками Кинопоиска рейтинг **уже в тексте**: `IMDb 7.6 KP 8.2`. Для строк без рейтинга (например `- [ ] Gods don't give gifts`) — сортировать как `0`, в конец группы. Опционально: добирать через TMDB (кэш `tmdbproxy` в Lampac) — решить при реализации. ## Открытые вопросы — БЛОКИРУЮЩИЕ (2026-09-22, ждут решения Alex) 1. **Что делать с 310 фильмами, которых нет в Radarr?** В `.md` — 360, в Radarr — 50. У 310 нет TMDB ID и постера. Варианты: (а) показывать только 50 из Radarr; (б) добирать остальные через TMDB-search (часть названий найдётся криво: «девять» → не то); (в) показывать заглушкой без постера. **Без решения шаг 1 переделки не начать.** 2. **Нужны 4 секции или одна?** Секции «Фильмы / Новинки / Мультфильмы / Аниме» или одна «Мой список» сверху. Вопрос задан, ответа нет. ## Открытые вопросы (не блокируют) 1. **Постеры** — тянуть через TMDB-кэш Lampac, через Radarr `MediaCover/`, или `remoteUrl` CDN? 2. **Карточка** — `card_parse` не настроен. С реальным TMDB ID карточка должна подтянуться штатным механизмом Lampa — проверить после переделки. 3. **`🗓️` upcoming** — показывать или прятать? Сейчас показываем. 4. **`❓` ambiguous** — сейчас показываем с названием как есть. ## ⚠️ Отменённые/ошибочные указания (не повторять) - ❌ **«Настройки → Источник»** — такого пункта в Lampa **не существует**. Выдумано, Alex не нашёл. Источник переключается в разделе «остальное → основной источник» (Alex нашёл сам 2026-09-22). - ❌ **«влево → панель источников»** — тоже не подтверждено. - ❌ **`plugins_add` в `lampainit.js`** — это серверные плагины Lampac (`tmdbproxy.js`, `cubproxy.js`, `online.js`, `catalog.js`…), а **не** список плагинов Lampa. `catalog.js` там есть при `"catalog": true` в init.conf. ## Связанные - [[personal/plans/lampac-kraken]] — базовое развёртывание (шаги 1–6 сделаны) - [[personal/tech/lampac-deployment]] — эксплуатация Lampac, модуль Catalog - [[family/how-to/tv-lampac-plugin]] — подключение плагина на ТВ - [[family/projects/watchlist-sync]] — парсер списка, CLI - [[family/projects/watchlist-automation]] — форматы маркеров, cron - [[family/documents/movies-watchlist]] — источник данных