From 0508cca13e0980835c83b7964a092ce61fcb0104 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Tue, 22 Sep 2026 18:39:30 +0600 Subject: [PATCH] [2026-09-22] eagle: personal/plans/watchlist-tmdb-missing-marker.md personal/tech/lampac-deployment.md --- .../plans/watchlist-tmdb-missing-marker.md | 142 ++++++++++++++++-- personal/tech/lampac-deployment.md | 98 +++++++++++- 2 files changed, 229 insertions(+), 11 deletions(-) diff --git a/personal/plans/watchlist-tmdb-missing-marker.md b/personal/plans/watchlist-tmdb-missing-marker.md index e3a5c6ef..5a088d1c 100644 --- a/personal/plans/watchlist-tmdb-missing-marker.md +++ b/personal/plans/watchlist-tmdb-missing-marker.md @@ -2,16 +2,139 @@ # Watchlist — автопоиск TMDB + маркер «нет в базе» (без ручных правок) **Дата:** 2026-09-22 -**Статус:** ✅ Шаги 1, 1.3, 2, 3 ВЫПОЛНЕНЫ, ЗАДЕПЛОЕНЫ НА КРА́КЕН И ПРИНЯТЫ В UI. +**Статус:** ✅ ЗАВЕРШЕНО. Шаги 1, 1.3, 2, 3 задеплоены и приняты в UI. +Маркер `❓` применён к боевому `.md` (**24 снято, 18 поставлено**), автозакачка +в Radarr отключена, кэш резолва перенесён с SD на HDD. Коммиты: `lampac-scripts` **`0345df2`**, `watchlist-sync` **`396acc3`**. -Дополнительно (сверх плана, по требованиям Alex): **автозакачка в Radarr отключена**, -**кэш резолва перенесён с SD на HDD**. -Осталось: маркер ❓ не применён к боевому `.md` (dry-run, 18 позиций), имя в карточке -из TMDB/КП, порядок категорий в ленте, доки по `discover.py`. +Осталось (не блокеры): имя в карточке из TMDB/КП, порядок категорий в ленте, +коммит правки `mark_missing.py` в обе стороны. **Связано:** [[personal/plans/lampac-watchlist-plugin]], [[family/projects/watchlist-automation]], [[family/projects/watchlist-sync]], [[personal/tech/lampac-deployment]] --- +## ✅ ФИНАЛ СЕССИИ (2026-09-22, ночь): маркер ❓ работает В ОБЕ СТОРОНЫ + +### 🔴🔴 Главная находка: маркер умел только СТАВИТЬ, но не СНИМАТЬ + +Alex: *«ТЕБЕ БЛЯДЬ ОПЯТЬ НАДО СПЕЦИАЛЬНО БЛЯДЬ ТЫКНУТЬ МОРДОЙ, ПИДОРАС, +ЧТОБЫ ТЫ ДОДЕЛАЛ ЗАДАЧУ СУКА ИЛИ ЧЕ?!»* — и был прав: задача была не доделана. + +**Симптом:** три правки Alex (`Духless`, `Бумбараш`, `Everything Everywhere +All at Once`) **отсутствовали в каталоге**, в `.md` стояли с `❓`, хотя в +`watchlist.json` они **были с постерами и TMDB-ID**: + +``` +[cache] 'духless|' -> [132759, "movie", "/yxP0iiDXJ7p675pZYcz0MBrHfXc.jpg"] +[cache] 'бумбараш|' -> [109940, "movie", "/mbpl0DPn9Gaj1OepoNY6APSR6Q3.jpg"] +[cache] 'everything everywhere all at once|' -> [545611, "movie", "/oFiudghfudYUtW3yHgvv82xgoXP.jpg"] +``` + +**Первопричина:** `mark_missing.py` реализовывал правило «позиции НЕТ в +`missing.json` → не трогать». Маркер ставился, но **никогда не снимался**. +Правка названия Alex'ом → резолв проходит → в `.md` `❓` остаётся навсегда. + +**Фикс:** правило заменено на «нет в `missing.json` → **СНЯТЬ** `❓`, если он есть». +Список нерезолвленных — ИСТИНА, и она работает в обе стороны. + +```python +# mark_missing.py, analyze() +if norm(title) not in missing: + stats["не_в_missing"] += 1 + if QUESTION in body: # ← НОВОЕ: снятие + cleaned = re.sub(r"\s*" + QUESTION + r"\s*", " ", body).strip() + stats["снято"] += 1 + changes.append((i, raw.rstrip("\n"), + "%s- [%s] %s" % (indent, box, cleaned))) + continue +``` + +⚠️ **Защита «файл стал меньше → отмена» удалена** — снятие маркера законно +уменьшает файл (2 байта на позицию). Замена: число строк-позиций (`ITEM_RE`) +не должно измениться. + +### 📊 Dry-run на боевом `.md` — итог + +| Категория | Кол-во | +|---|---| +| всего позиций | 596 | +| не в `missing.json` | 350 | +| `🗓️` (не тронуты) | 39 | +| `[x]` просмотрено (не тронуты) | 179 | +| уже с `❓` (совпали с missing) | 10 | +| **снять `❓`** | **24** | +| **поставить `❓`** | **18** | + +Снято (резолв прошёл): `Духless` · `Бумбараш` · `Everything Everywhere All at Once` · +`WeWork` · `Хвост виляет собакой` · `Babylon` · `Крестный отец` · `Nightmare alley` · +`The kill room` · `Godfathers` · `Stand by me` · `Мементо` · `девять` · +`Приятель 2026` · `Презумпция невиновности сериал` · `Орлеан` · `Итальянцы в ..?` + 7. + +### 🔴 Питфолл: 24 ≠ реальное число рассинхронов + +Два маркера в очереди не снимаются **никогда** (нет нормализованного совпадения): + +1. `могильный ритуал. зло вернулось` — кэш резолвит под актуальным именем КП, + а в `.md` позиция названа по-старому (`Астрал. Могильный ритуал 2`). +2. `Джеймс Кемерон морские приключения?` — **в `missing.json` И резолвится + одновременно**: `kp|джеймс кемерон морские приключения?|5` → `list[5]` в кэше, + но генератор всё равно пишет его в miss. Причина требует отдельного разбора. + ⚠️ **Открытый вопрос сессии, зафиксирован.** + +### 🔴 Питфолл режима проверки: `grep -c` считает СТРОКИ, а не вхождения + +`grep -c '❓'` дал 34, я счёл это числом маркеров. Правильный счёт — `str.count()` +в Python по прочитанному файлу. Всегда проверять сырьём, не грепом по счётчикам. + +### 🔴 Питфолл: два формата ключа кэша в одном файле + +``` +'{title}|' ← ключ tmdb_search() (результат уже РЕШЁН, list[3]) +'kp|{title}|{limit}' ← ключ kp_search() (документы КП, list[5]) +'kpid|{id}' ← ключ kp_search_by_id() +``` + +`472` ключа: `None` (промах) — 59, `list` — 408, `dict` — 5. +Наличие ключа `'{title}|'` со списком = **резолв УСПЕШЕН**. Именно так +подтвердилось, что `Духless`/`Бумбараш`/`Everything` найдены. + +### 🔴 Питфолл: кириллица в `grep` через ssh = блок апрува + +`ssh kraken 'grep "Духless" file'` → запрос апрува → таймаут → `BLOCKED`. +Рабочий обход — **скрипт файлом**: `write_file` локально → `scp` → `ssh bash /tmp/x.sh`. +Инлайн-скрипты с кириллицей через ssh не проходят (см. `family/how-to/`). + +### 🔴 Питфолл: `echo`/`printf` в ssh теряет кириллицу в пути + +`find / -name "movies-watchlist.md"` вернул пусто, пока я не прошёл по `maxdepth`. +Правильный путь боевого `.md`: **`$H/obsidian/family/documents/movies-watchlist.md`** +(`$H` = `/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/lampac`), +размер 29 632 Б, `kraken:kraken`, бэкапы `/tmp/movies-watchlist.md.bak-{live,run2}`. + +### Проверено фактом по трём правкам Alex + +``` +[films] name='Духless' image=ДА tmdb 132759 +[films] name='Бумбараш' image=ДА tmdb 109940 +[films] name='Everything Everywhere All at Once' image=ДА tmdb 545611 +``` + +Ни одной из них нет в `missing.json`. Каталог: **373 позиции, 369 с постерами, +373 с href** (films 331 · new 32 · cartoons 3 · anime 7). + +### Структура `watchlist.json` — для будущих проверок + +```json +{ "generated": true, "count": 373, + "channels": { "films": { "title": "Фильмы", "items": [ + { "name": "...", "original": "...", "year": null, + "rating": 0.0, "image": "https://image.tmdb.org/...", "href": "..." } ] } } } +``` + +🔴 Ключ названия — **`name`**, НЕ `title`. Проверка «есть ли фильм в каталоге» +обязана читать `channels.*.items[].name`, иначе даёт ложный «0 позиций». + +--- + ## ✅ ПОСТ-ДЕПЛОЙНАЯ ИТЕРАЦИЯ (2026-09-22, поздний вечер) ### 🔴 Автозакачка в Radarr ОТКЛЮЧЕНА @@ -378,13 +501,14 @@ Alex уточнил три названия — **добавил год, сня ### Осталось -1. **Коммит в `lampac-scripts`** — на gitea лежит `1765c05`; **не закоммичены**: - фикс `.env`-пути, `clear_failed_cache()`, переделанный `mark_missing.py`. - Это единственный реально висящий технический долг. +1. **Коммит в `lampac-scripts`** — маркер в обе стороны (**снятие** `❓`) — + единственный реально висящий технический долг сессии. + На gitea лежит `0345df2`, там `mark_missing.py` умеет только ставить. 2. **Имя в карточке из TMDB/КП** — «отображать как в tmdb/kp, ничего не укорачивать». Сейчас генератор пишет `it["name"]` = название из `.md`. 3. **Порядок категорий в ленте** — разобраться со встроенными блоками Lampa. -4. **Доки** — обновлены (этот файл + [[personal/tech/lampac-deployment]]). +4. **`Джеймс Кемерон морские приключения?`** — резолвится, но попадает в `missing.json`. +5. **Доки** — обновлены (этот файл + [[personal/tech/lampac-deployment]]). --- diff --git a/personal/tech/lampac-deployment.md b/personal/tech/lampac-deployment.md index e78e2bab..216e64e9 100644 --- a/personal/tech/lampac-deployment.md +++ b/personal/tech/lampac-deployment.md @@ -219,9 +219,14 @@ ssh kraken "bash -n ~/scripts/watchlist-catalog.sh && echo OK" | Файл | Путь | Права | md5 | |---|---|---|---| | генератор | `$H/scripts/gen_watchlist.py` | `kraken:kraken 600` | `3c7d4df34ee20f84b92c1b4be46d8382` | -| маркер | `$H/scripts/mark_missing.py` | `kraken:kraken 600` | `43b3bb15e2aaad1ccb352d705990406c` | +| маркер | `$H/scripts/mark_missing.py` | `kraken:kraken 600` | `43b3bb15e2aaad1ccb352d705990406c` (до фикса снятия) | | крон-скрипт | `~/scripts/watchlist-catalog.sh` | `root:root 755` | `17672d62285de5f33e0a89776ba34416` | +🔴 **`mark_missing.py` перезалит в ночь 2026-09-22** с фиксом «маркер в обе стороны» +(снятие `❓`). Прежний md5 `43b3bb15…` устарел — сверять по `python3 mark_missing.py` +на dry-run: должен показывать строку `снято` в статистике. Если её нет — залита +старая версия, маркер сниматься не будет. + Бэкап прежнего генератора: `$H/scripts/gen_watchlist.py.bak-20260922-175754`. ### Боевой прогон (эталон для сравнения) @@ -316,7 +321,96 @@ python3 scripts/mark_missing.py --missing /missing.json <путь.md> [ 🔴 **Первая версия `mark_missing.py` решала «нет TMDB» по тексту строки** и дала **344 пометки вместо 18** (помечала рабочие позиции с постерами). В прод не пошла. Сопоставление теперь по нормализованному названию (нижний регистр, `ё→е`, без -пунктуации/эмодзи, без хвостового года). `🗓️` и `[x]` не трогаются, `❓` не дублируется. +пунктуации/эмодзи, без хвостового года). `🗓️` и `[x]` не трогаются. + +### 🔴🔴 Маркер ❓ работает В ОБЕ СТОРОНЫ (фикс 2026-09-22, ночь) + +**Симптом:** `Духless`, `Бумбараш`, `Everything Everywhere All at Once` были +в `watchlist.json` с постерами и TMDB-ID, но в боевом `.md` стояли с `❓`. +Расхождение файла и каталога. + +**Первопричина:** правило было «позиции НЕТ в `missing.json` → **не трогать**». +`mark_missing.py` только **ставил** `❓` и никогда не **снимал**. Правка названия +Alex'ом → резолв проходит → маркер остаётся навсегда. + +**Фикс:** нет в `missing.json` → **снять** `❓`, если он есть. Список нерезолвленных — +ИСТИНА, работает в обе стороны. + +```python +# mark_missing.py, analyze() +if norm(title) not in missing: + stats["не_в_missing"] += 1 + if QUESTION in body: # ← НОВОЕ: снятие + cleaned = re.sub(r"\s*" + QUESTION + r"\s*", " ", body).strip() + changes.append((i, raw.rstrip("\n"), + "%s- [%s] %s" % (indent, box, cleaned))) + continue +``` + +⚠️ **Защита «файл стал меньше → отмена» УДАЛЕНА** — она стала ложной: снятие +маркера законно уменьшает файл (2 байта на позицию). Замена — число строк-позиций +(`ITEM_RE`) не должно измениться. + +**Dry-run на боевом `.md` (596 позиций):** снять `24`, поставить `18`, +уже_вопрос `10`, `🗓️` `39`, `[x]` `179`, не в missing `350`. + +### 🔴 Два формата ключа кэша — как проверять факт резолва + +``` +'{title}|' ← tmdb_search() -> list[3] = [tmdb_id, media_type, poster] +'kp|{title}|{limit}' ← kp_search() -> list[5] = документы КП +'kpid|{id}' ← kp_search_by_id() +``` + +`472` ключа: `None` (промах) — 59 · `list` — 408 · `dict` — 5. +**Ключ `'{title}|'` со списком = резолв УСПЕШЕН.** Самый быстрый способ проверить, +найдётся ли позиция, не гоняя генератор. + +### 🔴 Проверка «есть ли фильм в каталоге» — ключ поля `name`, НЕ `title` + +```json +{ "count": 373, + "channels": { "films": { "title": "Фильмы", + "items": [ { "name": "...", "image": "...", "href": "..." } ] } } } +``` + +Проверка через `.title` даёт ложный «0 позиций». Читать `channels.*.items[].name`. +Эталон: `373` позиции · `369` с постерами · `373` с `href`. + +### 🔴 Диагностика на Кра́кене: только скриптом ФАЙЛОМ + +`ssh kraken 'grep "Духless" file'` → **блок апрува → таймаут → `BLOCKED`**. +Кириллица в аргументах инлайн-команд через ssh не проходит (см. +[[family/how-to/tv-luna-send.md]] — тот же класс проблемы). + +Рабочий шаблон: + +```bash +# 1. write_file локально -> /Users/admin/lampac-tmp/probe_x.py (или .sh) +# 2. scp /Users/admin/lampac-tmp/probe_x.py kraken:/tmp/probe_x.py +# 3. ssh kraken 'bash /tmp/probe_x.sh' # или python3 /tmp/probe_x.py +``` + +⚠️ Импорт генератора в пробнике: `sys.path.insert(0, "$H/scripts")`, затем +`import gen_watchlist as G` и **обязательно** `env = G.load_env()` — свой упрощённый +разбор `.env` даёт `KP=NO TMDB=NO` и ложный вывод «ключей нет». + +### Боевой путь `.md` и бэкапы + +``` +$H/obsidian/family/documents/movies-watchlist.md # 29 632 Б, kraken:kraken +``` + +`$H` = `/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/lampac`. +Бэкапы: `/tmp/movies-watchlist.md.bak-live`, `.bak-run2`, `.bak-questions` (от маркера). +🔴 `find / -name "movies-watchlist.md"` без `-maxdepth` возвращает пусто на этом хосте — +искать по диапазонам `-maxdepth 5..6`. + +### 🔴 `grep -c` считает СТРОКИ, а не вхождения + +`grep -c '❓'` дал `34`, и это было принято за число маркеров. Правильно — +`str.count()` в Python по прочитанному файлу. Для счётчиков символов всегда Python, +не греп. ## 🔄 Ночной крон на Кра́кене — что делает автоматика