diff --git a/personal/plans/watchlist-tmdb-missing-marker.md b/personal/plans/watchlist-tmdb-missing-marker.md index 856d603e..2a399093 100644 --- a/personal/plans/watchlist-tmdb-missing-marker.md +++ b/personal/plans/watchlist-tmdb-missing-marker.md @@ -2,8 +2,8 @@ # Watchlist — автопоиск TMDB + маркер «нет в базе» (без ручных правок) **Дата:** 2026-09-22 -**Статус:** Шаги 1, 1.3, 2, 3 ВЫПОЛНЕНЫ И ПРОВЕРЕНЫ, оба репо запушены. -Осталось только: деплой на Кра́кен + имя в карточке из TMDB/КП. +**Статус:** ✅ Шаги 1, 1.3, 2, 3 ВЫПОЛНЕНЫ, ЗАДЕПЛОЕНЫ НА КРА́КЕН И ПРИНЯТЫ В UI. +Осталось: коммит правок в `lampac-scripts`, имя в карточке из TMDB/КП, порядок категорий в ленте. **Связано:** [[personal/plans/lampac-watchlist-plugin]], [[family/projects/watchlist-automation]], [[family/projects/watchlist-sync]], [[personal/tech/lampac-deployment]] --- @@ -257,6 +257,218 @@ Alex: *«В developer. Пуш на gitea.»* Репо на gitea (private, HTTP --- +## ✅ ПОСТ-ДЕПЛОЙНАЯ ИТЕРАЦИЯ (2026-09-22, поздний вечер) + +### 🔴 Генератор НЕ ЧИСТИЛ кэш промахов — исправлено + +Alex спросил прямо: *«че блядь значит чистка кэша обязательна?! скрипт блядь её делает?!»* — +и был прав: **не делал**. + +```python +key = f"{title.lower().strip()}|{year or ''}" +if key in cache: + return tuple(v) if v else None # промах вернулся, в сеть не пошли +``` + +Инвалидации не было. Промах (`None`) писался **навсегда** → правка названия, не +меняющая ключ кэша (регистр, пробел, порядок слов), `❓` не снимала. + +**Фикс:** `clear_failed_cache()` — вызывается в `main()` сразу после `load_cache()`. +Удаляет **все `None`**, успешные резолвы (~350) не трогает. Промахи (~50) +перезапрашиваются на каждом прогоне. + +Залито на Кра́кен, сумма `c556c52b8cf4714686add4083c4afc09` (совпала с локальной). +Патч: `~/lampac-tmp/patch_autoclear_cache.py`, бэкап `.bak-cacheclear`. + +⚠️ **Ручная чистка `clear_cache_kraken.py` больше не нужна** — это часть прогона. +Подробности: [[personal/tech/lampac-deployment]], раздел «Кэш промахов». + +### Правки Alex в `.md`: он делает их САМ, скрипт подхватывает + +Alex уточнил три названия — **добавил год, снял `❓` и подсказки-подбуллеты**: + +| Было | Стало | +|---|---| +| `❓ Everything Everywhere All at Once 2021` + 2 подбуллета | `Everything Everywhere All at Once 2022` | +| `❓ Духless` + 2 подбуллета | `Духless 2012` | +| `❓ Бумбараш` + 2 подбуллета | `Бумбараш 1972` | + +🔴 **Синк vault — почасовой.** Alex правит в Obsidian на Mac, на Кра́кен уезжает +через `sync-vault.sh` (крон `0 * * * *`). Дифф `.md` до синка **пустой** — прогонять +«после правок» нужно ПОСЛЕ синка, иначе генератор увидит старую версию. +Правки вручную гоняются: `ssh kraken 'bash ~/scripts/sync-vault.sh'`. + +### Ответы на вопросы Alex (проверено `crontab -l` и кодом) + +1. **Порядок в ленте — БАГ, не задумано.** `watchlist.yaml`: `displayindex: 1` — + самый низкий из всех (`rezka 10`, `kinobase 15`, `filmix 20`, `vokino 30`, + `eneyida 35`, `kinoukr 40`, `getstv 45`, `aniliberty 60`). «Мой список» должен + быть ПЕРВЫМ, но оказался ниже «Сейчас Смотрят» и «Рекомендуем посмотреть». + Эти два блока — **встроенные каталоги Lampa, не из `sites/`**, поэтому + `displayindex` их не сдвигает. `watchlist.yaml` в этой сессии НЕ правился. + Требует отдельного разбора. +2. **Маркеры — да, так.** `❓` = резолв не нашёл, не в каталоге. `🗓️` = не вышел, + не в каталоге. Без маркера = вышел и найден → в каталоге. +3. **Прогон после правок сработает** — при условии, что синк прошёл и кэш промахов + очищен (теперь автоматически). +4. **Radarr и лимит — это РАЗНЫЕ лимиты, не один.** + - `batch=2` (снижен с 5 после инцидента 2026-09-21, диск 100%) — сколько позиций + **уходит в Radarr на закачку** за прогон. Плюс `_free_gb()` — стоп при нехватке места. + - **Выдача в Lampa лимита НЕ имеет** — отдаётся весь `watchlist.json`. + - Каталог строится из `.md` → `watchlist.json`; **Radarr в отображении не участвует**. + - «Один скрипт в кроне» — неверно, их **пять** (см. таблицу ниже). + +### Осталось + +1. **Коммит в `lampac-scripts`** — на gitea лежит `1765c05`; **не закоммичены**: + фикс `.env`-пути, `clear_failed_cache()`, переделанный `mark_missing.py`. + Это единственный реально висящий технический долг. +2. **Имя в карточке из TMDB/КП** — «отображать как в tmdb/kp, ничего не укорачивать». + Сейчас генератор пишет `it["name"]` = название из `.md`. +3. **Порядок категорий в ленте** — разобраться со встроенными блоками Lampa. +4. **Доки** — обновлены (этот файл + [[personal/tech/lampac-deployment]]). + +--- + +## ✅ ДЕПЛОЙ НА КРА́КЕН ВЫПОЛНЕН (2026-09-22, вечер) + +### 🔴🔴 Главная находка деплоя: генератор на Кра́кене НИКОГДА не резолвил + +`load_env()` искал `.env` **только по Mac-пути**: + +```python +for p in (Path("/Users/admin/Developer/watchlist-sync/.env"),): +``` + +На Кра́кене такого пути нет → `TMDB_KEY` не найден → `[ERR] нет TMDB_KEY в .env` → +**каталог собирался на одних Кинопоиск-ссылках, без TMDB-ID и постеров.** + +Это объясняет, почему до сессии в JSON не было TMDB-ID у половины позиций: дело было +не в слабом резолве, а в том, что резолв **вообще не запускался**. + +**Фикс:** `load_env()` перебирает 5 мест (последний найденный по ключу побеждает), +печатает, какие файлы прочитаны: + +```python +candidates = [ + os.environ.get("WATCHLIST_ENV", ""), + "/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/watchlist-sync/.env", + "/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/watchlist-sync/.env", + str(Path.home() / "Developer" / "watchlist-sync" / ".env"), + str(Path.home() / ".config" / "watchlist-sync" / ".env"), +] +``` + +Скрипт патча: `~/lampac-tmp/patch_envpath.py`. Бэкап: `.bak-envpath`. + +### Боевой прогон на реальном `.md` (не тест) + +```bash +ssh kraken "cd $H && python3 $H/scripts/gen_watchlist.py $W $H/wwwroot/watchlist.json" +``` + +| Метрика | Значение | +|---|---| +| позиций в каталоге | **373** | +| без TMDB-ID | **0** | +| films / new / cartoons / anime | 331 / 32 / 3 / 7 | +| постеров | 327 / 32 / 3 / 7 | +| `missing.json` | **30** | +| источники | Radarr 9, TMDB 343, автопоиск **2b: 17**, **2c: 4**, miss 30 | + +Размеры: `watchlist.json` 102 456 Б, `missing.json` 1 239 Б. + +### Что задеплоено на Кра́кен + +| Что | Путь | Права | Сумма | +|---|---|---|---| +| генератор | `$H/scripts/gen_watchlist.py` | `kraken:kraken 600` | `3c7d4df3…` | +| маркер | `$H/scripts/mark_missing.py` | `kraken:kraken 600` | `43b3bb15…` | +| крон-скрипт | `~/scripts/watchlist-catalog.sh` | `root:root 755` | `17672d62…` | +| бэкап генератора | `$H/scripts/gen_watchlist.py.bak-20260922-175754` | | | + +`$H` = `/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/lampac` + +Суммы сверены `md5` локально и на Кра́кене — **совпали**. `py_compile` + `bash -n` — OK. + +🔴 **Права критичны:** установка только через +`sudo install -o kraken -g kraken -m 600` — каталог `$H/scripts/` принадлежит +`kraken:kraken`, но `~/scripts/` — `root:root 755`. `cp` без sudo падает на +`Permission denied` (грабля: бэкап крона-скрипта из-за этого не сделался с первого раза). + +### `watchlist-catalog.sh` — обновлён + +Добавлен блок маркера между генерацией и перезапуском lampac: + +```bash +python3 "$GEN" "$WATCHLIST" "$OUT" 2>&1 | tail -12 || { log "ERR: генерация упала"; exit 1; } + +if [ -f "$MISSING" ] && [ -f "$MARK" ]; then + log "Простановка ❓ по missing.json" + python3 "$MARK" --missing "$MISSING" "$WATCHLIST" --apply 2>&1 | tail -8 \ + || log "WARN: маркер не проставился (не фатально)" +fi + +docker restart lampac # сброс кэша Catalog +``` + +`MISSING=$LAMPAC/wwwroot/missing.json`. Нефатально для маркера — генерация важнее. + +### Крон на Кра́кене — полная картина (проверено `crontab -l`) + +``` +0 1 * * * watchlist-nightly.sh process-thumbs → resolve → sync-down → sync-up +0 9 * * 0 watchlist-discover.sh новинки из KP (воскресенье) +0 2 * * * watchlist-cleanup.sh удаление просмотренного при диске >90% +30 2 * * * watchlist-catalog.sh JSON-каталог для Lampa ← ОБНОВЛЁН +0 * * * * sync-vault.sh синк vault +0 * * * * pgs-to-srt.sh (новая загрузка) — не watchlist +0 3 * * * pgs-to-srt.sh (вся библиотека) — не watchlist +``` + +⚠️ **«Один скрипт в кроне» — неверно, их пять по watchlist.** Разделение ролей: +`discover` **только пишет в `.md`**, в закачку не отправляет. Закачку отправляет +`sync-down` через **Radarr** (`radarr_add()` + `radarr_search()`, `sync_down.py:155,158`). +**Лимит есть:** batch снижен `5 → 2` после инцидента 2026-09-21 (диск 100%), +плюс проверка свободного места `_free_gb()`. + +### Осталось открытым + +1. **Коммит в `lampac-scripts`** — `mark_missing.py` (переделка) и фикс `.env`-пути + **ещё не закоммичены**. В gitea лежит `1765c05`, там `mark_missing.py` старой версии. +2. **Имя в карточке из TMDB/КП** — требование Alex «отображать как в tmdb/kp, + ничего не укорачивать». Сейчас генератор пишет `it["name"]` = название из `.md`. +3. **Порядок в ленте Lampa** — «Мой список» (`displayindex: 1`, самый низкий из всех) + оказался **ниже** встроенных блоков «Сейчас Смотрят» и «Рекомендуем посмотреть». + Встроенные каталоги Lampa не из `sites/`, поэтому `displayindex` их не сдвигает. + Требует отдельного разбора — правился ли `watchlist.yaml` в этой сессии: НЕТ. + +### Приёмка Alex (частично) + +Alex подтвердил: *«вижу фильмы в ленте, работают»* — то есть каталог в UI **виден +и играет**. Это закрывает приёмку пунктов 1–3 из плана. +Осталось: **имя в карточке** и **порядок категорий**. + +### Порядок резолва — финальная реализация в коде + +```python +# 1) Radarr по названию +# 2) TMDB по названию как есть +# 2c) kp_id_from_url(it["kp_href"]) -> kp_search_by_id() -> _docs_to_tmdb() +# 2b) tmdb_via_kp() с KP_SIM_THRESHOLD +# 3) miss -> missing.json +``` + +Функции: `kp_id_from_url()`, `kp_search_by_id()`, `_docs_to_tmdb()`, `tmdb_via_kp_id()`, +`tmdb_via_kp()`, `kp_search()`, `title_sim()`. Константы: `KP_BASE`, `UA`, +`KP_SIM_THRESHOLD = 0.75`, `KP_ID_IN_URL`. + +`_docs_to_tmdb()` — общая логика для 2b и 2c. Параметр `_by_id` на документе +отключает проверку порога: **если фильм пришёл по kp_id, близость названия не важна — +id и есть доказательство.** + +--- + ## Требование Alex (дословно) > «Нет блядь, нахуя мне в плеере заглушки?! Нормальный автопоиск! Никаких БЛЯДЬ ручных @@ -534,7 +746,11 @@ def tmdb_via_kp(title, year, tmdb_key, kp_key, cache): | `~/Developer/lampac-scripts/scripts/mark_missing.py` | маркер ❓ по `missing.json` (переделан, `--missing`) | | `~/Developer/lampac-scripts/tests/accept_step1*.py` | приёмки автопоиска (3/3) | | `~/lampac-tmp/` | рабочая свалка + все патч-скрипты сессии (НЕ git) | -| Кра́кен: `lampac/scripts/gen_watchlist.py` | **ЗАДЕПЛОЕННАЯ старая версия** — ждёт обновления | +| Кра́кен: `lampac/scripts/gen_watchlist.py` | **ЗАДЕПЛОЕНА НОВАЯ ВЕРСИЯ** (`3c7d4df3…`, `kraken:kraken 600`), бэкап старой `.bak-20260922-175754` | +| Кра́кен: `lampac/scripts/mark_missing.py` | задеплоен (`43b3bb15…`), ставит `❓` по `missing.json` | +| Кра́кен: `~/scripts/watchlist-catalog.sh` | обновлён (`17672d62…`) — генерация → маркер → restart lampac | +| Кра́кен: `lampac/wwwroot/missing.json` | снимок нерезолвленных (30 позиций) | +| `~/lampac-tmp/patch_envpath.py` | фикс `load_env()` — путь `.env` для Кра́кена (КРИТИЧНО) | | `~/Developer/watchlist-sync/watchlist_sync/resolver.py` | ЭТАЛОН cross-reference | | `~/Developer/watchlist-sync/watchlist_sync/discover.py` | TMDB-фильтр (Шаг 3, `_resolvable()`) | | `~/Developer/watchlist-sync/watchlist_sync/cli.py` | `discover` передаёт `TMDB_KEY` | diff --git a/personal/tech/lampac-deployment.md b/personal/tech/lampac-deployment.md index b404aaa9..c28f66dc 100644 --- a/personal/tech/lampac-deployment.md +++ b/personal/tech/lampac-deployment.md @@ -14,6 +14,7 @@ tags: related: - '[[personal/plans/lampac-kraken]]' - '[[personal/plans/lampac-watchlist-plugin]]' + - '[[personal/plans/watchlist-tmdb-missing-marker]]' - '[[personal/tech/multistream-single-flow-limit]]' - '[[family/how-to/kraken-network]]' - '[[family/how-to/tv-luna-send]]' @@ -82,6 +83,88 @@ related: подстановка `$( … | sed … )` вырезается фильтром безопасности — питфолл №2 из [[family/how-to/gitea-config.md]]). +## 🚀 Деплой генератора на Кра́кен (проверено 2026-09-22) + +### 🔴🔴 Питфолл: `load_env()` искал `.env` только по Mac-пути + +```python +# БЫЛО — генератор на Кра́кене не находил ключей вообще +for p in (Path("/Users/admin/Developer/watchlist-sync/.env"),): +``` + +Симптом на Кра́кене: `[ERR] нет TMDB_KEY в .env` → **каталог собирался на одних +Кинопоиск-ссылках, без TMDB-ID и постеров.** Никакой резолв не выполнялся, хотя +код для него был. Именно это объясняет, почему в JSON не было TMDB-ID у половины +позиций — не «слабый резолв», а его полное отсутствие. + +**СТАЛО** — перебор 5 мест, печать прочитанных файлов в stderr: + +```python +candidates = [ + os.environ.get("WATCHLIST_ENV", ""), + "/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/watchlist-sync/.env", + "/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/watchlist-sync/.env", + str(Path.home() / "Developer" / "watchlist-sync" / ".env"), + str(Path.home() / ".config" / "watchlist-sync" / ".env"), +] +``` + +Патч: `~/lampac-tmp/patch_envpath.py`. **Урок:** при деплое скрипта на другую +машину — первым делом проверять абсолютные пути к конфигам, а не логику. + +### Установка: только `sudo install`, не `cp` + +```bash +scp -q ~/Developer/lampac-scripts/scripts/gen_watchlist.py kraken:/tmp/gen_watchlist.py +ssh kraken "sudo install -o kraken -g kraken -m 600 /tmp/gen_watchlist.py \ + $H/scripts/gen_watchlist.py" +``` + +`$H` = `/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/lampac` + +🔴 **Права — источник немого провала.** `$H/scripts/` = `kraken:kraken`, но +`~/scripts/` = `root:root 755`. Простой `cp` в `~/scripts/` падает на +`Permission denied` (бэкап крона-скрипта так и не сделался с первого раза). +Скрипты генератора — `600 kraken:kraken`; крон-обёртки в `~/scripts/` — `755 root:root`. + +**Проверка после заливки — три уровня, все обязательны:** + +```bash +md5 -q <локальный файл> # сверка с Кра́кеном +ssh kraken "md5sum $H/scripts/gen_watchlist.py" # суммы должны совпасть +ssh kraken "python3 -m py_compile $H/scripts/gen_watchlist.py && echo OK" +ssh kraken "bash -n ~/scripts/watchlist-catalog.sh && echo OK" +``` + +### Что задеплоено (2026-09-22) + +| Файл | Путь | Права | md5 | +|---|---|---|---| +| генератор | `$H/scripts/gen_watchlist.py` | `kraken:kraken 600` | `3c7d4df34ee20f84b92c1b4be46d8382` | +| маркер | `$H/scripts/mark_missing.py` | `kraken:kraken 600` | `43b3bb15e2aaad1ccb352d705990406c` | +| крон-скрипт | `~/scripts/watchlist-catalog.sh` | `root:root 755` | `17672d62285de5f33e0a89776ba34416` | + +Бэкап прежнего генератора: `$H/scripts/gen_watchlist.py.bak-20260922-175754`. + +### Боевой прогон (эталон для сравнения) + +```bash +ssh kraken "cd $H && python3 $H/scripts/gen_watchlist.py \ + /home/kraken/obsidian/family/documents/movies-watchlist.md \ + $H/wwwroot/watchlist.json" +``` + +| Метрика | Значение | +|---|---| +| позиций | **373** | +| без TMDB-ID | **0** | +| films / new / cartoons / anime | 331 / 32 / 3 / 7 | +| постеров | 327 / 32 / 3 / 7 | +| `missing.json` | 30 | +| источники | Radarr 9 · TMDB 343 · автопоиск 2b 17 · 2c 4 · miss 30 | + +`watchlist.json` 102 456 Б · `missing.json` 1 239 Б · карточек в `cards/` — 373. + ## 🔎 Резолв позиций в каталоге — три уровня (2026-09-22) Генератор ищет фильм по цепочке, от точного к эвристике: @@ -109,6 +192,35 @@ related: ⚠️ Порог и `kp_id` — **числовые/машинные критерии**, не ручной словарь соответствий. Инвариант «никаких ручных правок» не нарушен. +### 🔴 Кэш промахов — генератор чистит его САМ + +`~/.lampac_tmdb_cache.json` (на Кра́кене) кэширует и успехи, и **промахи (`None`)**. + +Проблема: промах писался **навсегда**. Если Alex правит название так, что ключ кэша +не меняется (регистр, пробел, порядок слов) — запрос в сеть **не уходит**, +возвращается старый `None`, и `❓` не снимается никогда. Плюс копятся `None` +по давно не используемым названиям. + +**Решение — `clear_failed_cache(cache)` в генераторе**, вызывается в `main()` сразу +после `load_cache()`: + +```python +cleared = clear_failed_cache(cache) # удаляет все None +if cleared: + print(f"[i] кэш: снято промахов {cleared} (будут перепроверены)", file=sys.stderr) +``` + +- Успешные резолвы (числовой TMDB-ID) **не трогаются** — они верны, а повторная + проверка ~350 записей на каждом прогоне бессмысленна. +- Промахи (~50) перезапрашиваются всегда — это ровно те позиции, ради которых + правки и делаются. Стоимость: секунды. + +⚠️ **Ручная чистка больше не нужна.** До этого фикса приходилось гонять +`clear_cache_kraken.py` руками — теперь это часть прогона. + +Патч: `~/lampac-tmp/patch_autoclear_cache.py`. Бэкап: `.bak-cacheclear`. +Сумма после заливки: `c556c52b8cf4714686add4083c4afc09`. + ### `missing.json` и маркер ❓ Генератор пишет снимок нерезолвленных рядом с каталогом: @@ -130,14 +242,35 @@ python3 scripts/mark_missing.py --missing /missing.json <путь.md> [ ## 🔄 Ночной крон на Кра́кене — что делает автоматика +**По watchlist заданий ПЯТЬ, а не одно** (проверено `crontab -l`, 2026-09-22): + +``` +0 1 * * * watchlist-nightly.sh process-thumbs → resolve → sync-down → sync-up +0 9 * * 0 watchlist-discover.sh новинки из KP (воскресенье) +0 2 * * * watchlist-cleanup.sh сносит просмотренное при диске >90% +30 2 * * * watchlist-catalog.sh JSON-каталог для Lampa +0 * * * * sync-vault.sh синк vault (не watchlist, но связан) +``` + `~/scripts/watchlist-nightly.sh`, cron **01:00**: ``` sync-vault (pull) → process-thumbs --apply → resolve --recheck --apply → sync-down --apply --batch 2 → sync-up --apply → sync-vault (push) ``` -`~/scripts/watchlist-catalog.sh`, cron **30 2 * * *** — генерация JSON + рестарт lampac. -`~/scripts/watchlist-cleanup.sh`, cron **0 2 * * *** — сносит просмотренное при заполнении диска. +### 🔴 Разделение ролей: кто пишет в `.md`, кто отправляет в закачку + +| Скрипт | Что делает | Закачка | +|---|---|---| +| `discover` (вс 09:00) | **только пишет в `.md`** — добавляет новинки | ❌ ничего не отправляет | +| `sync-down` (в nightly) | шлёт в **Radarr**: `radarr_add()` + `radarr_search()` (`sync_down.py:155,158`) | ✅ отправляет, **но через Radarr**, не напрямую в torrent | +| `resolve --recheck` | резолвит позиции, **снимает маркер сам**, когда фильм найден | ❌ | +| `cleanup` (02:00) | удаляет файлы просмотренного при заполнении диска | ❌ | +| `catalog` (02:30) | генерация JSON + `mark_missing` + `docker restart lampac` | ❌ | + +🔴 **Лимит закачки ЕСТЬ.** `--batch 2` — снижен с `5` после инцидента 2026-09-21 +(диск 100%). Плюс проверка свободного места `_free_gb()` в `sync_down.py`. +«Проходит весь список без лимитов» — **неверно**, идёт по 2 позиции за прогон. 🔴 **`resolve --recheck --apply` снимает маркер сам**, когда фильм находится. Это работа резолвера `watchlist_sync/resolver.py` — НЕ генератора каталога и НЕ ручная. @@ -229,8 +362,35 @@ services: WAF на префиксе `/catalog/` — **10 запросов/сек**. -### 🔴 Питфолл №1: первая категория игнорирует `format` +### ⚠️ Открытый вопрос: `displayindex` не поднимает «Мой список» выше встроенных блоков +Замечено Alex 2026-09-22: каталог watchlist в ленте идёт **после** блоков +«Сейчас Смотрят» и «Рекомендуем посмотреть». Это **не задумано**. + +`watchlist.yaml`: `displayindex: 1` — **самый низкий** из всех каталогов: + +| `displayindex` | Каталог | +|---|---| +| **1** | **watchlist («Мой список»)** | +| 10 | Rezka | +| 15 | Kinobase | +| 20 | Filmix, FilmixApp | +| 25 | KinoPub | +| 30 | VoKino | +| 35 | Eneyida | +| 40 | KinoUkr | +| 45 | GetsTV | +| 60 | AniLiberty | + +«Сейчас Смотрят» и «Рекомендуем посмотреть» — **встроенные каталоги Lampa**, +их нет в `catalog/sites/`, поэтому `displayindex` их не сдвигает: Lampa держит +свои блоки отдельным списком. + +**Что проверять при разборе:** доступен ли приоритет встроенных блоков в +`init.conf`/настройках Lampa, или встроенные всегда идут первыми. +Понижать `displayindex` ниже 1 бессмысленно — он уже минимальный. + +### 🔴 Питфолл №1: первая категория игнорирует `format` `ListController.cs:50` строит базовый URL **до** обработки `format`: ```csharp