[2026-09-20] eagle: family/projects/watchlist-automation.md family/projects/watchlist-sync.md
This commit is contained in:
1 parent
eba5b38a10
commit
174e528765
2 files changed
+188
-68
No files matched your search
@@ -48,11 +48,27 @@ Jellyfin (8096) → rescan → виден контент
|
||||
| `🗓️` | upcoming (ещё не вышел) | discover помечает, не скачивается |
|
||||
| `❓` | не найдено / ambiguous | ждёт ручного уточнения |
|
||||
| `🧟♀️` | horror (genre_id=27) | информационный маркер |
|
||||
| `⭐` | **приоритет к загрузке** (U+2B50) | sync-down берёт в первую очередь (ПЛАН, не реализован) |
|
||||
| `⭐` | **приоритет к загрузке** (U+2B50) | sync-down берёт в первую очередь, **фильтр рейтинга не применяется** ✅ реализовано 2026-09-20 |
|
||||
|
||||
⚠️ **`✭` (U+272D) — НЕ приоритет.** Alex 2026-09-20 явно указал: звёздочкой-приоритетом считается только `⭐` (U+2B50). Символ `✭` используется в файле как обычная пометка «выделенное», приоритетом не является.
|
||||
|
||||
Также: `⭐` засчитывается **только в строке фильма** (`- [ ] Название ⭐ …`), НЕ в под-буллетах кандидатов, которые генерирует `mark_ambiguous` (там формат «Название (год) ★N.N»). `[x]` (просмотренные) с `⭐` игнорируются.
|
||||
Также: `⭐` засчитывается **только в строке фильма** (`- [ ] Название ⭐ …`), НЕ в под-буллетах кандидатов, которые генерирует `mark_ambiguous` (там формат «Название (год) ★N.N»). `[x]` (просмотренные) с `⭐` игнорируются. Работает **из всех секций** (`## Фильмы`, `## Новинки`, `## Мультфильмы`, `## Аниме`).
|
||||
|
||||
## Правила отбора в `sync-down` (актуально с 2026-09-20)
|
||||
|
||||
Приоритет определяется **двумя факторами**, а не позицией строки:
|
||||
|
||||
| Условие | Поведение |
|
||||
|---------|-----------|
|
||||
| `⭐` (U+2B50) в строке фильма | **первыми**, фильтр рейтинга **не применяется** |
|
||||
| все остальные `[ ]` | **по порядку файла** (сверху вниз), рейтинг ≥ 6.0 |
|
||||
| рейтинг < 6.0 (включая `0.0`) | отсеивается, с логом `[−] … skipped` |
|
||||
| `[x]` | пропускается |
|
||||
| `❓` / `🗓️` (ambiguous) | пропускается, обрабатывается отдельно |
|
||||
|
||||
⚠️ **Порядок файла ≠ рейтинг.** До 2026-09-20 код сортировал по рейтингу TMDB и игнорировал позицию в списке — из-за этого в батч попадали фильмы «не из начала списка». Исправлено коммитом `ada47da`.
|
||||
|
||||
⚠️ **`⭐` = обход фильтров, а не смена сортировки.** Это принципиальная позиция Alex («Я блядь сказал в приоритете завела»): звёздочка означает явное «хочу», поэтому рейтинг для неё не проверяется вовсе.
|
||||
|
||||
## Структура секций movies-watchlist.md
|
||||
|
||||
@@ -242,37 +258,113 @@ Queued 5/5 items [dry-run]
|
||||
|
||||
⚠️ **Реальный прогон (`bash ~/scripts/watchlist-nightly.sh`) после этой правки был ПРЕРВАН** — не отработал. Vault проверен: рабочее дерево чистое, `movies-watchlist.md` остался на коммите `d7ce0a9` (результат первого прогона), частичных изменений нет.
|
||||
|
||||
### ⭐ Приоритет по звёздочке `⭐` (U+2B50) — ПЛАН, НЕ реализован
|
||||
### ✅ ⭐ Приоритет по звёздочке `⭐` (U+2B50) — РЕАЛИЗОВАНО (2026-09-20)
|
||||
|
||||
**Требование Alex (2026-09-20, после реализации «порядка файла»):** фильмы, отмеченные звёздочкой `⭐`, должны идти **в первую очередь** к загрузке — приоритет поверх порядка файла.
|
||||
**Требование Alex:** фильмы, отмеченные звёздочкой `⭐`, идут **в первую очередь** к загрузке — приоритет поверх порядка файла, **из всех секций** списка.
|
||||
|
||||
**Уточнения Alex:**
|
||||
**Правила (финальные, подтверждены Alex):**
|
||||
- Только символ **`⭐` (U+2B50)**. Символ `✭` (U+272D) приоритетом **НЕ является**.
|
||||
- Только те `⭐`, что стоят **в названии фильма в списке**, а не в под-буллетах кандидатов («Название (год) ★N.N»), которые генерирует скрипт.
|
||||
- `[x]` (просмотренные) — **пропускать**.
|
||||
- Только **строки фильмов** (`- [ ] Название ⭐ …`), НЕ под-буллеты кандидатов от `mark_ambiguous` («Название (год) ★N.N»).
|
||||
- `[x]` (просмотренные) — **пропускаются**.
|
||||
- **Из всех секций** — не только `## Фильмы`/`## Новинки` (уточнение Alex).
|
||||
- ⭐ **обходит фильтр `MIN_RATING`** — «Я блядь сказал в приоритете завела». Звёздочка = явное «хочу», рейтинг не важен.
|
||||
- `⭐` **срезается из поискового запроса** перед отправкой в TMDB (очевидное требование Alex).
|
||||
|
||||
**Аудит файла на 2026-09-20:**
|
||||
| Символ | Где | Значение |
|
||||
|--------|-----|----------|
|
||||
| `⭐` U+2B50 | строка 79: `- [ ] Простите за беспокойство ⭐ 2018` | **единственный** приоритетный кандидат |
|
||||
| `✭` U+272D | `⬇️ Наш флаг означает смерть ✭`, `⬇️ Битва за битвой ✭` | не приоритет (уже в очереди) |
|
||||
| `✭` U+272D | `[x] Бруталист ✭`, `[x] Анора ✭` | не приоритет (просмотрены) |
|
||||
**Коммит:** `7db9385 feat: ⭐ (U+2B50) items are queued first, bypassing the rating filter`
|
||||
|
||||
**Проверено:** `parser._clean()` **НЕ срезает** `⭐` (срезает только `✭★☆✩✪✫✬✮✯✰`) — символ остаётся и в `item.raw`, и в `item.title`. Значит проверку можно делать по `item.raw`. Элемент имеет `status=UNWATCHED, queued=False, ambiguous=False` → уже попадает в `candidates_raw`.
|
||||
**Правка 1 — `parser.py::_clean()`.**
|
||||
⚠️ **Найденный баг:** `⭐` стоит **в середине** названия (`Простите за беспокойство ⭐ 2018`), а старый regex был привязан к концу строки (`\s*[...]+\s*$`) — поэтому класс не матчился и символ утекал в запрос TMDB. Работало случайно (TMDB прощал лишний глиф).
|
||||
|
||||
**Согласованный подход (двухпроходная выборка):**
|
||||
```python
|
||||
starred = [i for i in candidates_raw if "\u2b50" in i.raw]
|
||||
rest = [i for i in candidates_raw if "\u2b50" not in i.raw]
|
||||
for item in starred + rest: # приоритетный проход первым
|
||||
...
|
||||
# ⭐ (U+2B50) is a priority marker and may sit ANYWHERE in the title
|
||||
# ("Простите за беспокойство ⭐ 2018"), so strip it without the end anchor.
|
||||
s = s.replace('⭐', ' ')
|
||||
s = re.sub(r'\s+', ' ', s).strip()
|
||||
s = re.sub(r'\s*[✭★☆✩✪✫✬✮✯✰]+\s*$', '', s).strip() # остальные звёзды — по-прежнему end-anchored
|
||||
return s
|
||||
```
|
||||
|
||||
**Открытые вопросы (заданы Alex, ответа ещё НЕТ):**
|
||||
1. **Фильтр рейтинга vs ⭐** — если `⭐`-фильм имеет рейтинг < 6.0, качать всё равно или отсеять? Рекомендация: **отсеять** (⭐ меняет очерёдность, не отменяет фильтр качества). Сейчас конфликта нет — `Простите за беспокойство` имеет 6.8.
|
||||
2. **Чистить `⭐` из поискового запроса** перед TMDB? Сейчас не чистится и работает (TMDB находит), но это случайность. Рекомендация: добавить `⭐` в класс срезаемых символов `_clean()`.
|
||||
**Критично:** `⭐` вырезается из `title` (чистый запрос), но **остаётся в `raw`** — приоритет проверяется по `item.raw`, поэтому оба свойства сохраняются.
|
||||
|
||||
Ответы нужны до начала правки. Правка будет **отдельным коммитом** поверх `ada47da`.
|
||||
**Правка 2 — `sync_down.py`, двухпроходная выборка:**
|
||||
|
||||
```python
|
||||
# ⭐ (U+2B50) marks a must-have: such items are queued FIRST and bypass
|
||||
# the rating filter entirely. Order within each group stays file order.
|
||||
starred = [i for i in candidates_raw if "\u2b50" in i.raw]
|
||||
rest = [i for i in candidates_raw if "\u2b50" not in i.raw]
|
||||
if starred:
|
||||
print(f" ⭐ {len(starred)} priority item(s) {[i.title for i in starred]}")
|
||||
|
||||
for item in starred + rest:
|
||||
result = resolve(item.title, tmdb_key, kp_key)
|
||||
if result.status == "ok":
|
||||
if "\u2b50" not in item.raw and (result.candidate.rating or 0) < MIN_RATING:
|
||||
skipped_low += 1
|
||||
print(f" [−] {item.title!r} — rating "
|
||||
f"{result.candidate.rating or 0:.1f} < {MIN_RATING}, skipped")
|
||||
continue
|
||||
resolved.append((item, result.candidate))
|
||||
else:
|
||||
deferred.append((item, result))
|
||||
```
|
||||
|
||||
Обрати внимание на условие: `"\u2b50" not in item.raw and rating < MIN_RATING` — фильтр применяется **только** к не-⭐. Это и есть «обходит фильтр».
|
||||
|
||||
**Итоговые правила отбора в `sync-down`:**
|
||||
|
||||
| | ⭐ | обычные |
|
||||
|---|---|---|
|
||||
| Очерёдность | первыми | после, по порядку файла |
|
||||
| Фильтр `MIN_RATING = 6.0` | **не применяется** | применяется |
|
||||
| `[x]` | пропуск | пропуск |
|
||||
|
||||
**Проверка (2026-09-20):**
|
||||
```
|
||||
_clean('Простите за беспокойство ⭐ 2018') -> 'Простите за беспокойство 2018' (⭐ срезан)
|
||||
raw: 'Простите за беспокойство ⭐ 2018' star in raw: True (приоритет работает)
|
||||
title: 'Простите за беспокойство 2018' star in title: False (чистый запрос)
|
||||
candidates: 250 | starred: 1 | rest: 249 — ⭐ первым в итерации
|
||||
```
|
||||
|
||||
**Dry-run после правки:**
|
||||
```
|
||||
⭐ 1 priority item(s) ['Простите за беспокойство 2018']
|
||||
[→] 'Простите за беспокойство 2018' → 'Простите за беспокойство' (movie, 2018, ★6.8) [dry-run]
|
||||
[→] 'Гадкая сестра' (2025) ★7.3 ...
|
||||
Queued 5/5 items [dry-run]
|
||||
```
|
||||
|
||||
**Единственный ⭐ в файле** — строка 79, секция `## Фильмы`: `- [ ] Простите за беспокойство ⭐ 2018`.
|
||||
|
||||
### ✅ Реальный прогон 2026-09-20 19:53 — прошёл чисто
|
||||
|
||||
`bash ~/scripts/watchlist-nightly.sh` от юзера `kraken` → `Done`, ошибок нет. Вывод (`/tmp/wl-run.log`):
|
||||
|
||||
```
|
||||
(18 item(s) below rating 6.0 skipped)
|
||||
[=] 'Простите за беспокойство' already in Radarr, marking queued ← ⭐ обработан ПЕРВЫМ
|
||||
[=] 'Гадкая сестра' already in Radarr, marking queued
|
||||
[✅] 'Список заветных желаний' added to Radarr, search triggered
|
||||
[✅] 'Отпуск на двоих' added to Radarr, search triggered
|
||||
[✅] 'Простая случайность' added to Radarr, search triggered
|
||||
Queued 5/5 items
|
||||
[19:53:32] ▶ Done
|
||||
```
|
||||
|
||||
⭐-фильм попал в очередь первым, но добавлять было нечего (уже в Radarr с прерванного прогона) — значит приоритет сработал. Дубли после прогона: **0**. Файл: 21332 байт / 676 строк.
|
||||
|
||||
### 🩹 Почему первая версия правки не сработала (не повторять)
|
||||
|
||||
Первая попытка добавить `⭐` в существующий класс `[✭★☆✩✪✫✬✮✯✰]` **не дала эффекта** — класс end-anchored (`$`), а `⭐` стоит в середине названия. Проверка на тестах показала `star gone: False`. Вывод: для маркеров, которые могут стоять в любой позиции, нужен `str.replace()`, а не класс с якорем.
|
||||
|
||||
### 📋 Как задача ставилась (для контекста будущих сессий)
|
||||
|
||||
Alex формулировал требования короткими репликами, и **план сначала был неверно истолкован дважды**:
|
||||
1. «⭐ - в приоритете, из всех списков. Естественно в tmdb ее срезать» — уточнение, что секции не ограничены и глиф не должен уходить в запрос.
|
||||
2. «Что блядь значит рейтинг остался?! Я блядь сказал в приоритете завела» — категорическое: ⭐ обходит фильтр рейтинга, а не просто меняет очерёдность.
|
||||
|
||||
**Вывод для будущего:** когда Alex говорит «в приоритете» — это означает **обход фильтров**, а не смену сортировки. Уточнять это ДО предложения плана, а не после.
|
||||
|
||||
### 📦 Результат закачки по итогам прогона 2026-09-20
|
||||
|
||||
@@ -302,26 +394,47 @@ for item in starred + rest: # приоритетный проход пер
|
||||
/srv/.../watchlist-sync/watchlist_sync/parser.py.bak-20260920-192909
|
||||
```
|
||||
|
||||
### Статус на конец сессии (обновлено)
|
||||
- ✅ Фикс `parser.py` применён, проверен 3 прогонами, **ЗАКОММИЧЕН** — `2dab180`.
|
||||
- ✅ Фикс `sync_down.py` (порядок файла + `MIN_RATING = 6.0`) **ЗАКОММИЧЕН** — `ada47da`. Dry-run подтверждён (18 отсеяно, 5 в батче по порядку файла).
|
||||
- ✅ Файл watchlist вычищен (168 дублей убрано) и закоммичен в vault.
|
||||
- ✅ Ручной прогон `watchlist-nightly.sh` прошёл чисто, 4 фильма ушли в закачку (3 качаются, 1 ждёт релиз).
|
||||
- 🔴 **Реальный прогон ПОСЛЕ правки `sync_down` был прерван** — не выполнен. Новый отбор (порядок файла + фильтр) пока проверен только в dry-run.
|
||||
- 🔴 Крон по-прежнему отсутствует — pipeline живёт только вручную.
|
||||
- ⏸ **Ожидает решения Alex:** приоритет `⭐` (U+2B50) и два связанных вопроса (см. секцию «⭐ Приоритет по звёздочке»).
|
||||
- ℹ️ Доки [[watchlist-automation]] и [[watchlist-sync]] были обновлены агентом Eagle в этой же сессии (коммиты `bd743da`, `abee5a8` в vault) — при дальнейших правках свериться, чтобы не затереть.
|
||||
### Статус на конец сессии (2026-09-20, финал)
|
||||
|
||||
**Бэкапы, созданные в этой сессии:**
|
||||
**Три коммита в репо watchlist-sync** (порядок применения):
|
||||
```
|
||||
7db9385 feat: ⭐ (U+2B50) items are queued first, bypassing the rating filter
|
||||
ada47da feat(sync-down): follow file order, filter by MIN_RATING
|
||||
2dab180 fix(parser): mark_ambiguous replaces candidate block instead of prepending
|
||||
20ca511 ← HEAD до сессии
|
||||
```
|
||||
Рабочее дерево чистое. `.bak-*` добавлены в `.gitignore`.
|
||||
|
||||
- ✅ Фикс `parser.py` (`mark_ambiguous`) применён, проверен 3 прогонами на чистой копии, закоммичен.
|
||||
- ✅ Фикс `sync_down.py` — порядок файла + `MIN_RATING = 6.0`, закоммичен. Dry-run подтверждён.
|
||||
- ✅ Приоритет `⭐` (U+2B50) **реализован**, закоммичен. Обходит фильтр рейтинга, работает из всех секций, `[x]` пропускает.
|
||||
- ✅ Файл watchlist вычищен (168 дублей → 0) и закоммичен в vault.
|
||||
- ✅ **Два реальных прогона `watchlist-nightly.sh` прошли чисто.** Второй (19:53) — уже с приоритетом ⭐: `Done`, ошибок нет, 3 фильма ушли в закачку.
|
||||
- ✅ Дубли после всех прогонов: **0**.
|
||||
- 🔴 **Крон по-прежнему отсутствует** — pipeline живёт только вручную. ГЛАВНАЯ незакрытая задача.
|
||||
- 🟡 Zulip-пинг не настроен (`ZULIP_*` нет в `.env`).
|
||||
- ℹ️ Доки [[watchlist-automation]] и [[watchlist-sync]] обновлялись агентом Eagle в этой же сессии (коммиты `bd743da`, `abee5a8` в vault) — при правках сверяться.
|
||||
|
||||
**Хронология прогонов 2026-09-20:**
|
||||
| Время | Что | Результат |
|
||||
|-------|-----|-----------|
|
||||
| ~19:33 | Прогон после фикса дедупа (до правки `sync_down`) | 4 фильма в Radarr: Gladiator, American History X, I Swear, Oficina do Diabo (без релиза) |
|
||||
| ~19:45 | Прогон после правки `sync_down` | **ПРЕРВАН** (прерван пользователем), правок не оставил |
|
||||
| 19:53 | Финальный прогон (файловый порядок + `MIN_RATING` + ⭐) | `Done`. +3 фильма: Список заветных желаний, Отпуск на двоих, Простая случайность |
|
||||
|
||||
**Бэкапы (все созданы в этой сессии):**
|
||||
```
|
||||
~/obsidian/family/documents/movies-watchlist.md.bak-20260920-192909 (838 строк, 26430 б — ДО фикса)
|
||||
~/obsidian/family/documents/movies-watchlist.md.dedupe-backup-20260920-193111 (до дедупа)
|
||||
/srv/.../watchlist-sync/watchlist_sync/parser.py.bak-20260920-192909 (md5 0f597606fa14f357ff3b25547bd18ea6)
|
||||
/srv/.../watchlist-sync/watchlist_sync/parser.py.bak-20260920-192909 (md5 0f597606fa14f357ff3b25547bd18ea6)
|
||||
/srv/.../watchlist-sync/watchlist_sync/parser.py.bak-20260920-195100 (до правки ⭐)
|
||||
/srv/.../watchlist-sync/watchlist_sync/sync_down.py.bak-20260920-194447
|
||||
/srv/.../watchlist-sync/watchlist_sync/sync_down.py.bak-20260920-195100 (до правки ⭐)
|
||||
```
|
||||
⚠️ `.bak-*` добавлены в `.gitignore` репо watchlist-sync, чтобы не попадать в коммиты.
|
||||
|
||||
**Скрипты диагностики/фикса (на Кракене, `/tmp/`):** `wl_dedupe.py`, `wl_verify_final.py`, `wl_postcheck.py`, `wl_order2.py`, `wl_grab_check.py`, `wl_fix_parser3.py`, `wl_fix_syncdown.py`, `wl_star.py`, `wl_star_check.py`. `wl_dedupe.py` пригоден для повторного использования при накоплении дублей; `wl_postcheck.py` — для проверки дублей после прогона.
|
||||
**Скрипты диагностики/фикса (на Кракене, `/tmp/`):** `wl_dedupe.py` (дедуп кандидатов, `--apply`), `wl_postcheck.py` (проверка дублей после прогона), `wl_orphan.py` (проверка структуры блоков), `wl_verify_final.py`, `wl_order2.py`, `wl_grab_check.py` (Radarr queue + Transmission), `wl_fix_parser3.py`, `wl_fix_syncdown.py`, `wl_fix_star.py`, `wl_fix_clean2.py`, `wl_star.py`, `wl_star_check.py`, `wl_final_star.py`.
|
||||
|
||||
**Скрипты повторного использования:** `wl_dedupe.py` — при накоплении дублей; `wl_postcheck.py` — контроль после каждого прогона; `wl_grab_check.py` — что реально ушло в закачку.
|
||||
|
||||
### Пути к конфигам *arr (проверено 2026-09-20, в доке ранее были ошибочны)
|
||||
Конфиги Radarr/Sonarr/Transmission лежат на **втором диске** (`6194539b-03e0-4f33-986b-4da09834e82f`), не на том же, что медиа:
|
||||
@@ -448,20 +561,21 @@ Jellyfin library mapping: Movies → `movies`, Cartoons → `mixed` (movies + ep
|
||||
|
||||
## Open Tasks
|
||||
|
||||
- ✅ **[2026-09-20] Фикс `sync_down.py` РЕАЛИЗОВАН** — коммит `ada47da`. Убран `resolved.sort()`, добавлен `MIN_RATING = 6.0` с видимым `print`. Нулевые рейтинги отсекаются (решение Alex: «отсекать»).
|
||||
- ⏸ **[2026-09-20] Приоритет `⭐` (U+2B50) в `sync_down` НЕ реализован** — ждёт ответа Alex на два вопроса (фильтр рейтинга vs ⭐; чистить ли `⭐` из поискового запроса). Детали в секции «⭐ Приоритет по звёздочке» выше. Только `⭐` (U+2B50), `✭` (U+272D) не считается; `[x]` пропускать; только строки фильмов, не под-буллеты кандидатов.
|
||||
- 🔴 **[2026-09-20] Догнать реальный прогон** — после правки `sync_down` полный `watchlist-nightly.sh` ни разу не отработал (был прерван). Прогнать и убедиться, что батч = `Под огнём`, `Секретный агент`, `Охота на воров 2`, `Гадкая сестра`, `Семья в аренду` (dry-run это предсказал).
|
||||
- 🟡 **[2026-09-20] `sync-down` берёт всего 5 за прогон** — при 254 кандидатах разбор очереди займёт ~50 ночей. Рассмотреть увеличение `--batch` (сейчас `--batch 5` в `watchlist-nightly.sh`).
|
||||
- 🔴 **[2026-09-20] Восстановить cron для watchlist pipeline** — pipeline мёртв с 20 июня. Скрипты готовы и проверены вручную (прогон 2026-09-20 прошёл чисто), нужно вернуть в crontab юзера `kraken`:
|
||||
- ✅ **[2026-09-20] Фикс `sync_down.py` РЕАЛИЗОВАН** — коммит `ada47da`. `resolved.sort()` убран, добавлен `MIN_RATING = 6.0` с видимым `print`. Нулевые рейтинги отсекаются (решение Alex: «отсекать»).
|
||||
- ✅ **[2026-09-20] Приоритет `⭐` (U+2B50) РЕАЛИЗОВАН** — коммит `7db9385`. ⭐ первыми из всех секций, **обходит** фильтр `MIN_RATING`. `⭐` срезается из запроса TMDB (`parser._clean` через `str.replace`, НЕ через end-anchored класс). `✭` (U+272D) приоритетом не считается; `[x]` пропускается; под-буллеты кандидатов не считаются.
|
||||
- ✅ **[2026-09-20] Реальный прогон с новой логикой выполнен** — 19:53, `Done`, +3 фильма. Батч по порядку файла, ⭐ первым.
|
||||
- 🔴 **[2026-09-20] Восстановить cron для watchlist pipeline** — ГЛАВНАЯ незакрытая задача. Pipeline мёртв с 20 июня. Скрипты проверены вручную (2 прогона 2026-09-20 прошли чисто), нужно вернуть в crontab юзера `kraken`:
|
||||
```
|
||||
0 1 * * * bash ~/scripts/watchlist-nightly.sh
|
||||
0 9 * * 0 bash ~/scripts/watchlist-discover.sh
|
||||
```
|
||||
⚠️ В [[arr-stack-kraken]] эти строки в секции «Cron'ы на Kraken» указаны как действующие, хотя фактически их НЕТ — док в той части расходится с реальностью.
|
||||
- 🟡 **[2026-09-20] `sync-down` берёт всего 5 за прогон** — при 250 кандидатах разбор очереди займёт ~50 ночей. Рассмотреть увеличение `--batch` (сейчас `--batch 5` в `watchlist-nightly.sh`). Особенно актуально, раз фильтр `<6.0` уже отсеял 18.
|
||||
- 🟡 **[2026-09-20] Разобрать 5 застрявших закачек в очереди Radarr** со статусом `warning`: `Brokeback Mountain`, `Мортал Комбат 2`, `Sorry to Bother You`, `Город бога`, `День хомячка`. Висят давно, релиз не находится/не качается.
|
||||
- 🟡 **[2026-09-20] `Oficina do Diabo` / `Мастерская Сатаны` (2025)** — в Radarr, но релиз не найден. Ждёт RSS. Проверить через несколько дней.
|
||||
- 🟡 **[2026-09-20] Настроить Zulip-пинг** — `ZULIP_URL` / `ZULIP_API_KEY` / `ZULIP_BOT_EMAIL` отсутствуют в `.env` watchlist-sync, 59 нерезолвленных items ушли в никуда без уведомления.
|
||||
- 🟡 **[2026-09-20] Добавить pre-check дублей в `sonarr_add()`** — сейчас при уже существующей серии прилетает `400 SeriesExistsValidator` и шумит в логе. В `radarr_add()` проверка есть.
|
||||
- 🟡 **[2026-09-20] Синхронизировать документацию с кодом** — [[watchlist-sync]] §1.2 и §1.3 описывают порог `min_rating: 6.0` и «приоритет = порядок в файле», которых в коде не было. Порог теперь реализован (`MIN_RATING`), приоритет по порядку тоже — обновить формулировки в [[watchlist-sync]].
|
||||
- 🟡 **[2026-09-20] Синхронизировать документацию с кодом** — [[watchlist-sync]] §1.2 и §1.3 описывают порог `min_rating: 6.0` и «приоритет = порядок в файле», которых в коде не было. Порог теперь реализован (`MIN_RATING`), приоритет по порядку тоже, плюс появился приоритет ⭐ — обновить формулировки в [[watchlist-sync]].
|
||||
- 🔴 **[2026-09-04] Решить стратегию качества загрузки новых фильмов** — уже РЕШЕНО 2026-09-04 (профиль `WEB-4K (no remux)`, id 7 для Radarr и Sonarr). Осталось только убедиться, что старые `⬇️`-позиции, добавленные до фикса, не тянут remux.
|
||||
- 🟡 Оставшиеся `❓`-позиции (71 маркер) — **намеренно не чистятся вручную** (решение Alex 2026-09-20). Разбираются через `resolve --recheck` при работающем кроне.
|
||||
- 🟡 Передавать Transmission locations через router script при каждом импорте (сейчас вручную)
|
||||
|
||||
@@ -63,9 +63,10 @@ Hermes Kraken cron job
|
||||
5. **Не найдено** → добавить `❓`, пропустить
|
||||
|
||||
**Пороги:**
|
||||
- `min_rating: 6.0` — ниже 6 игнорировать (защита от мусора)
|
||||
- `min_score: 0.75` — сходство названия (Jaccard/fuzzy)
|
||||
- `max_candidates_for_auto: 1` — при 2+ близких кандидатах → ambiguous
|
||||
- ~~`min_rating: 6.0`~~ — ⚠️ **в `resolver.py` этого порога НЕТ.** Фильтр по рейтингу реализован в `sync_down.py` как `MIN_RATING = 6.0` (коммит `ada47da`, 2026-09-20). Резолвер возвращает кандидата с любым рейтингом; отбор происходит на этапе загрузки.
|
||||
- `min_score: 0.75` — сходство названия (`AMBIG_SCORE_THRESHOLD` в `resolver.py`)
|
||||
- `AMBIG_DIFF_THRESHOLD: 0.15` — при близких score двух кандидатов → ambiguous
|
||||
- `max_candidates_for_auto: 1` — при 2+ близких кандидатах → ambiguous (в коде: `candidates[:3]`)
|
||||
|
||||
**Тип контента:**
|
||||
- TMDB `media_type: movie` → Radarr
|
||||
@@ -78,7 +79,7 @@ Hermes Kraken cron job
|
||||
- Максимум **5 новых** в одном прогоне (защита от flood) — задаётся `--batch 5` в `watchlist-nightly.sh`
|
||||
- ✅ **Приоритет — порядок в файле** (исправлено 2026-09-20, коммит `ada47da`). Раньше стоял `resolved.sort(key=lambda x: x[1].rating or 0, reverse=True)`, из-за чего позиция строки игнорировалась и в батч попадал топ-5 по рейтингу. Сортировка убрана, `resolved` заполняется в порядке `parse()` = сверху вниз.
|
||||
- ✅ **Фильтр по рейтингу реализован** (там же). Константа `MIN_RATING = 6.0` в начале `sync_down.py`; всё, что ниже, печатается как `[−] … skipped` и не попадает в очередь. **Нулевые рейтинги (`0.0`) отсекаются вместе с остальными** — решение Alex 2026-09-20. На момент замера отсеивалось 18 из 251 кандидатов.
|
||||
- ⏸ **Приоритет `⭐` (U+2B50)** — согласован, но НЕ реализован: такие фильмы должны идти в первую очередь. Детали и открытые вопросы — в [[family/projects/watchlist-automation]] → «⭐ Приоритет по звёздочке».
|
||||
- ⏸ → ✅ **Приоритет `⭐` (U+2B50) РЕАЛИЗОВАН** (2026-09-20, коммит `7db9385`). Двухпроходная выборка: `starred + rest`. Два важных свойства: (а) ⭐ **обходит фильтр `MIN_RATING`** — условие в коде `if "⭐" not in item.raw and rating < MIN_RATING`; (б) `⭐` **срезается из поискового запроса** в `parser._clean()` через `str.replace()` (НЕ через end-anchored класс — маркер стоит в середине названия). Работает из всех секций, `[x]` и под-буллеты не считаются. Детали — в [[family/projects/watchlist-automation]] → «⭐ Приоритет по звёздочке».
|
||||
- После добавления в Radarr/Sonarr → заменить `- [ ]` на `- [ ] ⬇️` в .md
|
||||
|
||||
**API:**
|
||||
@@ -132,13 +133,15 @@ WATCHLIST_PATH=/vault/family/documents/movies-watchlist.md
|
||||
|
||||
| Механизм | Роль | Где в коде |
|
||||
|----------|------|-----------|
|
||||
| **Порядок в файле** | **приоритет** — что качать раньше | `sync_down.py` (порядок `parse()`, сортировки нет) |
|
||||
| **Рейтинг ≥ 6.0** | **отбор** — что вообще качать | `MIN_RATING` в `sync_down.py` |
|
||||
| **`⭐` (U+2B50)** | **приоритет поверх порядка** | ⏸ не реализовано |
|
||||
| **`⭐` (U+2B50)** | **приоритет поверх всего** — качается первым, **обходит фильтр рейтинга** | `sync_down.py`: `starred + rest`, условие `"⭐" not in item.raw and rating < MIN_RATING` |
|
||||
| **Порядок в файле** | **приоритет** — что качать раньше (внутри групп) | `sync_down.py` (порядок `parse()`, сортировки нет) |
|
||||
| **Рейтинг ≥ 6.0** | **отбор** — что вообще качать (кроме ⭐) | `MIN_RATING` в `sync_down.py` |
|
||||
|
||||
**Ключевое правило:** порядок и `⭐` влияют на **очерёдность**, рейтинг — на **допуск**. Рейтинг не поднимает фильм выше в очереди; `⭐` не отменяет порог рейтинга (по предложению; ждёт подтверждения Alex).
|
||||
**Ключевое правило (уточнено Alex 2026-09-20):** порядок влияет на **очерёдность**, рейтинг — на **допуск**. Но `⭐` — это **обход фильтра**, а не просто «в начало очереди»: звёздочка означает явное «хочу», поэтому рейтинг для таких фильмов не проверяется вовсе. Формулировка Alex: «Я блядь сказал в приоритете завела».
|
||||
|
||||
**`✭` (U+272D) приоритетом НЕ является** — только `⭐` (U+2B50), и только в строке фильма, не в под-буллетах кандидатов; `[x]` пропускается.
|
||||
**Обработка `⭐` в `parser._clean()`:** маркер может стоять **в середине** названия (`Простите за беспокойство ⭐ 2018`), поэтому срезается через `str.replace('⭐', ' ')`, а НЕ через end-anchored класс `[...]+$` (первая попытка так не сработала). `raw` сохраняет `⭐` — приоритет проверяется именно по `item.raw`.
|
||||
|
||||
**`✭` (U+272D) приоритетом НЕ является** — только `⭐` (U+2B50), и только в строке фильма, не в под-буллетах кандидатов; `[x]` пропускается; работает из всех секций.
|
||||
|
||||
Используют `~/scripts/sync-vault.sh` для всех git-операций (stash/pull/pop/commit/push). **Не агентные** — скрипты вызывают CLI напрямую, никакого LLM в pipeline.
|
||||
|
||||
@@ -207,26 +210,29 @@ WATCHLIST_PATH=/vault/family/documents/movies-watchlist.md
|
||||
|
||||
## Структура проекта
|
||||
|
||||
> ⚠️ Ниже — **исходный дизайн**. Фактическая структура (проверено 2026-09-20) отличается: `radarr.py`/`sonarr.py` объединены в `arr.py`, добавлен `discover.py`, `recommend.py` не существует (Stage 2 не начата).
|
||||
|
||||
**Фактически (репо на Кракене: `/srv/dev-disk-by-uuid-49e8f586-.../watchlist-sync`):**
|
||||
```
|
||||
~/Developer/watchlist-sync/
|
||||
├── watchlist_sync/
|
||||
│ ├── __init__.py
|
||||
│ ├── parser.py — парсинг .md
|
||||
│ ├── resolver.py — TMDB + KP API
|
||||
│ ├── radarr.py — Radarr API client
|
||||
│ ├── sonarr.py — Sonarr API client
|
||||
│ ├── jellyfin.py — Jellyfin API client
|
||||
│ ├── sync_down.py — непросмотренные → arr
|
||||
│ ├── sync_up.py — Jellyfin → .md
|
||||
│ └── recommend.py — Stage 2 recommendations
|
||||
├── config.example.json — шаблон конфига (без секретов)
|
||||
├── .env.example
|
||||
├── .gitignore — .env, config.json, *.key
|
||||
├── pyproject.toml
|
||||
└── README.md
|
||||
watchlist_sync/
|
||||
├── __init__.py
|
||||
├── cli.py — CLI entry (click): resolve / sync-down / sync-up / discover / process-thumbs
|
||||
├── parser.py — парсинг .md, mark_queued / mark_ambiguous / mark_horror / process_thumbs / _clean
|
||||
├── resolver.py — TMDB /search/multi + KP fallback, кэш .resolve_cache.json
|
||||
├── arr.py — Radarr + Sonarr API client (объединены)
|
||||
├── sync_down.py — непросмотренные → arr (MIN_RATING=6.0, порядок файла, ⭐ приоритет)
|
||||
├── sync_up.py — Jellyfin → .md
|
||||
├── discover.py — KP топ новинок → секция «Новинки»
|
||||
└── jellyfin.py — Jellyfin API client
|
||||
.env — TMDB_KEY, KP_KEY, RADARR_*, SONARR_* (ZULIP_* ОТСУТСТВУЮТ)
|
||||
.resolve_cache.json — кэш резолвера (450 записей на 2026-09-20)
|
||||
.venv/ — py3.11 + httpx + click
|
||||
pyproject.toml
|
||||
```
|
||||
|
||||
**Конфиг секреты:** только в `.env` (не в репо). `config.example.json` — шаблон.
|
||||
**Дизайн (не реализовано):** `recommend.py` — Stage 2 recommendations.
|
||||
|
||||
**Конфиг секреты:** только в `.env` (не в репо).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user