diff --git a/personal/plans/lampac-watchlist-plugin.md b/personal/plans/lampac-watchlist-plugin.md index 7fa603ef..315614fd 100644 --- a/personal/plans/lampac-watchlist-plugin.md +++ b/personal/plans/lampac-watchlist-plugin.md @@ -7,7 +7,7 @@ related: - '[[family/projects/watchlist-sync]]' - '[[family/projects/watchlist-automation]]' - '[[family/documents/movies-watchlist]]' -status: card-pending +status: card-done-tv-check-pending tags: - lampac - lampa @@ -39,9 +39,10 @@ updated: '2026-09-22' > РЕАЛЬНЫЕ ЭЛЕМЕНТЫ - а не сгенерируют пустые заглушки!» > См. «🔴 АРХИТЕКТУРНАЯ ОШИБКА» — читать ПЕРВЫМ. > -> **Итог одной строкой:** серверная часть каталога работает (4 категории, 360/33/3/7), -> но реализована не по замыслу: подменяет каталог вместо добавления секций, -> и не использует Radarr как источник постеров/ID. +> **Итог одной строкой:** серверная часть каталога работает полностью — «Мой список» +> первым в `/catalog`, 4 категории (403 позиции, 348 постеров, 352 рабочих карточки), +> карточка по клику отдаёт **HTTP 200** (339/339). Родной CUB не тронут. +> Осталась приёмка глазами на ТВ. > > **Обновление 2026-09-22 (вечер):** Alex лично нашёл источник в UI > («остальное → основной источник → Мой список»), увидел результат и подтвердил @@ -117,6 +118,34 @@ updated: '2026-09-22' > `deploy_lampac.py` пока НЕ умеет лить каталог `cards/` — доклеить `scp -r`. > См. **«🔴 КАРТОЧКА: механика card_parse»**. +> **Обновление 2026-09-22 (ночь, четвёртая сессия): ✅ КАРТОЧКА ПОЧИНЕНА. 339/339 HTTP 200. СЕРВЕРНАЯ ЧАСТЬ ЗАКРЫТА.** +> Схема «файл на фильм» залита и **проверена на живом сервере**: +> - `wwwroot/cards/card-movie-NNN.json` (345 файлов), обёртка `{"card": {...}}`, +> владелец `1000:1000`, режим `644`; +> - `card_parse.initUrl` = C# `return $"{host}/cards/card-{uri.Replace("/", "-")}.json";`, +> `node: card` (статичный — потому и нужен файл-на-фильм); +> - **все 339 карточек с TMDB-ID отдают HTTP 200**, имена читаются +> («Я буду жить», «На цепи», «Простите за беспокойство»); +> - `deploy_lampac.py` дополнен шагом **2b** (заливка каталога `cards/` через tar) +> и проверкой карточек в шаге 7. +> +> **Итог каталога:** Новинки 33/30, Фильмы 360/308, Мультфильмы 3/3, Аниме 7/7 — +> **403 позиции, 348 постеров, 352 рабочих карточки**, постеров-подложек нет. +> +> 🔴 **Три причины поломки, все устранены** (детали — «🔴 ПИТФОЛЛЫ, найденные по ходу»): +> 1. **права**: tar распаковывал от root → `root:root 600` → dotnet `Permission denied` → +> статика молча **404** (выглядит как «файла нет»); +> 2. **macOS AppleDouble**: BSD `tar` клал `._*.json` в `wwwroot` → нужен `--no-mac-metadata`; +> 3. 🔴 **ложный диагноз — потерянный час**: карточку проверял по **`/card`**, где всегда 404. +> Правильный путь — **`/catalog/card?plugin=watchlist&uri=movie/NNN`**. +> +> **ОСТАЛОСЬ: приёмка глазами на ТВ** (`192.168.1.70`): секции сверху с постерами, +> клик → карточка с описанием → воспроизведение через TorrServer. +> **Приёмка = результат виден в UI**, не «API отвечает». +> +> **Не тронуто, ждёт решения Alex:** 310 фильмов из списка, которых нет в Radarr +> (51 без постера, идут через фолбэк Кинопоиска). Без апрува не делать. + ## 🔴 КАРТОЧКА: механика card_parse (разобрано по коду 2026-09-22) Клик по элементу каталога ведёт в `/catalog/card?plugin=watchlist&uri=&type=movie`. @@ -1703,3 +1732,79 @@ cp radarr.db.bak-<дата> radarr.db && docker restart radarr - [[family/projects/watchlist-sync]] — парсер списка, CLI - [[family/projects/watchlist-automation]] — форматы маркеров, cron - [[family/documents/movies-watchlist]] — источник данных + +## ✅ РАБОТАЕТ (2026-09-22, конец дня) + +**Карточка фильма починена. Проверено: 339 из 339 карточек → HTTP 200.** + +### Итог по каталогу + +| Секция | Элементов | Постеров | Рабочих TMDB-ID | +|---|---|---|---| +| Новинки | 33 | 30 | 30 | +| Фильмы | 360 | 308 | 312 | +| Мультфильмы | 3 | 3 | 3 | +| Аниме | 7 | 7 | 7 | +| **Всего** | **403** | **348** | **352** | + +Источники метаданных: Radarr 9, TMDB 343, Кинопоиск-фолбэк 51. + +### Что сделано + +1. Генератор `gen_watchlist_v2.py` — пишет **файл на каждый фильм**: + `wwwroot/cards/card-movie-NNN.json`, владелец `1000:1000`, режим 644. +2. `watchlist.yaml` → `card_parse.initUrl` собирает путь из `uri`: + `return $"{host}/cards/card-{uri.Replace("/", "-")}.json";`, `node: card`. +3. `deploy_lampac.py` — добавлен шаг `2b` (заливка `cards/` через tar), плюс + проверка карточки в шаге 7. +4. `arr.py` — `monitored: False`, `searchFor*: False` (коммит `9872016`). +5. `plugin.js` — патч `enumerable:true`, смонтирован volume, переживает обновление образа. + +### 🔴 ПИТФОЛЛЫ, найденные по ходу (все стоили времени) + +1. **Проверять карточку надо по `/catalog/card`, НЕ по `/card`.** + `/card` отдаёт 404 и выглядит как поломка каталога. Потерян час на ложном диагнозе. +2. **`parse.node` — СТАТИЧНЫЙ JSONPath.** `{uri}`/`{cat}` в него НЕ подставляются + (`json.SelectToken(parse.node)`). Слэш в ключе `movie/NNN` трактуется как вложенность + JSON → `SelectToken` возвращает null → `BadRequest("parse.node")`. + Именно поэтому выбран формат «файл на фильм» с фиксированным `$.card`. +3. **`card_parse.initUrl` ИСПОЛНЯЕТСЯ как C#** (`CSharpEval`, объект + `CatalogInitUrlCard` с полями `host/uri/type/args`). Только здесь можно подставить + `uri`. Кавычки внутри YAML-heredoc писать экранированно. +4. **`CardController.Index(plugin, uri, type)` — параметра `cat` НЕТ.** + `{cat}` в карточке всегда пустой. +5. **macOS `tar` кладёт в архив AppleDouble-мусор `._*`** — файлы попадают в + `wwwroot` и мешают. Использовать `tar --no-mac-metadata` + `rm -f ._*`. +6. **Права решают.** Если файлы отдать `root:root 600` — dotnet отвечает + `Permission denied`, статика молча отдаёт **404**. Нужно `1000:1000` + `755`/`644`. +7. **`find -exec` через `ssh -t` рвётся**: `missing argument to -exec`. + Использовать `chmod -R` отдельными вызовами. +8. **Счётчик TMDB-ID врал нулём**: `id` приходит как `movie/424781` (префикс), + а проверялось `"/movie/"` (вхождение со слэшем спереди). Проверять `startswith`. +9. **Первая категория в `menu.categories` игнорирует `format`** и берёт `list.uri` + напрямую. Поэтому «Новинки» стоят первыми, и их `list.uri = watchlist-new.json`. +10. **Ключи карточки.** `CardController` отдаёт `title`/`original_title`, а + `card_parse` ищет `name`/`original`. В JSON пишем **оба** набора ключей. + +### Воспроизведение + +```bash +cd /Users/admin/lampac-tmp +python3 gen_watchlist_v2.py movies-watchlist.md /tmp/wl-out/watchlist.json +python3 deploy_lampac.py /tmp/wl-out --skip-plugin # ~2 мин +``` + +Cron: генератор в **02:30**, после `watchlist-nightly` (01:00). + +### Осталось на ТВ + +Проверить глазами в Lampa на `192.168.1.70`: секции сверху с постерами, +клик → карточка с описанием → воспроизведение через TorrServer. +Приёмка = результат виден в UI. + +### Не сделано (ждёт решения, без апрува не трогать) + +310 фильмов из списка, которых нет в Radarr — пока показываются через фолбэк +Кинопоиска (51 без постера). Решить судьбу отдельно. + +## Связанные diff --git a/personal/tech/lampac-deployment.md b/personal/tech/lampac-deployment.md index fc334688..eb1105aa 100644 --- a/personal/tech/lampac-deployment.md +++ b/personal/tech/lampac-deployment.md @@ -48,7 +48,7 @@ related: ├── 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) + ├── wwwroot/ → /lampac/wwwroot (JSON-каталог watchlist + cards/) └── scripts/gen_watchlist.py (генератор .md → JSON) ``` @@ -56,7 +56,22 @@ related: `gen_watchlist_v2.py`, `patch_plugin.py`, `plugin-patched.js`, `watchlist.yaml`, `docker-compose.yml`, `deploy_lampac.py`. -Владелец `kraken:kraken`, права `755`. +🔴 **Права на файлы в `wwwroot/` — владелец `1000:1000` (= пользователь `lampac` +в контейнере), каталоги `755`, файлы `644`.** Не `kraken:kraken` — контейнер ходит +не под хост-пользователем. Если файл окажется `root:root 600`, dotnet ответит +`Permission denied`, а веб-сервер **молча отдаст 404** — отсутствие прав выглядит +снаружи как «файла нет». + +### Раздача статики из `wwwroot/` + +- Всё, что положено в `/lampac/wwwroot/`, доступно **по корню**: + `wwwroot/watchlist-films.json` → `GET /watchlist-films.json` (**200**). +- `UseStaticFiles` — в `Core.dll` (серверное ядро), настраивать не нужно. +- 🔴 **`/lampac/wwwroot` ≠ `/lampac/current.conf`**: статика ядра, а не модуль. + Проверять наличие файла надо `docker exec lampac ls ...`, а **доступность** — + обязательно через `curl`, потому что это разные вещи (см. права выше). +- ⚠️ Веб-корень отдаёт **только** `wwwroot/`. Пути вида `/cards/...` работают, + но `/card` — это **не** статика (см. ниже). ⚠️ Всё пишущее — на data-диск (1.8T). **НЕ на SD** (`/`, 14G, 8.6G свободно). @@ -1267,3 +1282,92 @@ prowlarr · sonarr · watchtower · rclone · xray-reverse-bridge · hermes-krak 5. **Автоскачка Radarr + Sonarr** — 45 monitored / очередь 6 у Radarr, 13 monitored / очередь 19 у Sonarr. План отключения на апруве ([[personal/plans/lampac-watchlist-plugin]], «ПЕРЕДЕЛКА 2»). + +## Карточка фильма в модуле Catalog — рабочая схема (проверено 2026-09-22) + +Модуль `Catalog` (в образе `/lampac/module/Catalog/`). Каталог watchlist: +`sites/watchlist.yaml` + JSON в `wwwroot/`. + +### Списки + +```yaml +menu: + - categories: + Новинки: new # ПЕРВОЙ — она игнорирует format, берёт list.uri + Фильмы: films + Мультфильмы: cartoons + Аниме: anime + format: + "-": | + return $"http://127.0.0.1:9118/watchlist-{cat}.json"; +list: + uri: watchlist-new.json # ← сюда смотрит первая категория +content: + nodes: "$.items[*]" + href: { attribute: href } # href = TMDB URL → id = "movie/NNN" +``` + +### Карточка + +`CardController.Index(plugin, uri, type)` — **`cat` не принимает**. + +- `url = $"{init.host}/{uri}"` — дефолт, даёт `http://127.0.0.1:9118/movie/424781` → 500. +- `card_parse.initUrl` **исполняется как C#** (`CSharpEval`, объект + `CatalogInitUrlCard(host, args, uri, query, type)`) → можно собрать динамический URL. +- `card_parse.node` — **статичный JSONPath** (`json.SelectToken(node)`), + подстановок нет; `/` = вложенность. + +Рабочая конфигурация: + +```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-movie-424781.json`: + +```json +{ "card": { "name": "...", "title": "...", "original": "...", "original_title": "...", + "year": "...", "rating": "...", "poster": "...", "img": "...", + "overview": "...", "genres": "...", "cast": "...", "runtime": 116 } } +``` + +Ключи дублируются (`name`+`title`, `poster`+`img`): `CardController` отдаёт +`title`/`original_title`, а `card_parse` ищет `name`/`original`. + +### Эндпоинты + +| Запрос | Назначение | +|---|---| +| `GET /catalog` | список каналов; `main` = категории | +| `GET /catalog/list?plugin=watchlist&cat=films&page=1` | элементы секции | +| `GET /catalog/card?plugin=watchlist&uri=movie/424781` | **карточка** | + +🔴 **Не по `/card`** — там 404 и ложный вывод «карточка сломана». + +### Файлы и права + +- Статика раздаётся из `/lampac/wwwroot/` (`core` UseStaticFiles). +- Владделец — `1000:1000` (пользователь `lampac` в контейнере), каталоги `755`, + файлы `644`. Иначе `Permission denied` → **404**. +- macOS `tar` → AppleDouble `._*`, добавлять `--no-mac-metadata`. + +### Деплой + +```bash +python3 gen_watchlist_v2.py movies-watchlist.md /tmp/wl-out/watchlist.json +python3 deploy_lampac.py /tmp/wl-out --skip-plugin +``` + +Скрипты: `/Users/admin/lampac-tmp/`. Логика заливки — tar+scp+sudo, +`ssh -t` для sudo (без tty `sudo` требует пароль), каждая команда отдельным +вызовом (`&&`-цепочка рвёт tty). + +### Связанные