[2026-09-23] plan: Lampa watched → watchlist.md via TimeCode DB (sync_from_lampac)

This commit is contained in:
Alexey Martemyanov committed 2026-09-23 21:03:46 +06:00
1 parent 5bd4b155cb
commit 34dfc8cfc6
1 file changed
+136
@@ -0,0 +1,136 @@
---
title: Отметка «просмотрено» из Lampa → watchlist.md
type: plan
status: awaiting-approval
namespace: personal
created: 2026-09-23
updated: 2026-09-23
tags: [lampac, lampa, watchlist, timecode, torrserver, kraken, sync-up]
related:
- "[[family/projects/watchlist-automation]]"
- "[[personal/plans/lampac-watchlist-plugin]]"
- "[[family/documents/movies-watchlist]]"
---
# Отметка «просмотрено» из Lampa → `movies-watchlist.md`
> **Проблема (Alex, 2026-09-23):** «А у нас из лампы уходят отметки просмотрено в список галочками как было в jellyfin?»
> Ответ: **нет**. Задача — сделать, чтобы уходили.
---
## 1. Диагноз (проверено фактами 2026-09-23)
### 1.1 Канал Jellyfin мёртв — и не он нужен
- `sync_up.py` умеет **только** Jellyfin (`GET /Users/{id}/Items?IsPlayed=true`) → `[x]`.
- Jellyfin на Кра́кене хранит `BaseItems`: **53** записи, `movie` — **0**, `series` — **1**.
`UserData.IsPlayed=true` — **0**. `PlaybackActivity` — **0**.
- Причина: смотреть стали через **Lampa + TorrServer**, а не Jellyfin. Плюс с 2026-09-22
автозакачки отключены → в Jellyfin ничего не прибавляется.
- **Вывод:** Jellyfin как источник — тупик. Фактический источник просмотров — **Lampa**.
### 1.2 Lampa уже пишет просмотр на сервер — канал ЕСТЬ
Модуль Lampac **`TimeCode`** (`sync`):
| Элемент | Значение |
|---|---|
| Модуль | `/lampac/module/Sync/TimeCode/` (`Controller.cs`, `plugin.js`, `SqlContext.cs`) |
| БД | `/lampac/database/TimeCode.sql` (SQLite; на хосте `$H/lampac/database/TimeCode.sql`) |
| Таблица | `timecodes` |
| Колонки | `Id, user, identity, card, item, position, duration, percent, profile, deleted, watched_at, updated_at, extra` |
| Запись ставит | `Lampa` → `POST /timecode/add` (легаси) или `/timecode/set` (нативный), пачками ≤10 req/s |
| Читает | `GET /timecode/all?card_id=...` |
| Ключ личности | `identity` = `{tmdb_id}_movie` — **ровно то, что нужно для матча со списком** |
**Живая запись на 2026-09-23:**
```
identity = 1758655_movie card = movie-1758655
position = 1222.0 duration = 6898.0 percent = 18.0
watched_at = 0 deleted = 0
```
`percent` уже считается Lampa/TorrServer. Отметка «просмотрено» в UI Lampa
кладёт `percent ≈ 100` (либо `watched_at > 0` — обе колонки проверять).
⚠️ **`sqlite3` в контейнере НЕТ** (обычный случай у Alex) — БД читать через
`docker cp lampac:/lampac/database/TimeCode.sql /tmp/` + `python3 -c "import sqlite3 ..."`.
На хосте Кра́кена `python3` есть.
### 1.3 Что уже есть в `watchlist.json` — `tmdb_id` готов к матчу
`$H/lampac/wwwroot/watchlist.json` → `channels.*.items[]`, у **346/346** films есть `tmdb_id`.
Матч делается по TMDB-ID, **без** хрупкого сравнения названий.
---
## 2. Решение
**Новый модуль в `watchlist-sync`: `sync_from_lampac.py`.**
Читает `timecodes` → сопоставляет по `tmdb_id` → ставит `[x]` в `.md` (тем же
`sync_up._mark_watched`, что уже работает для Jellyfin). Вызывается в конце
`watchlist-nightly.sh` — после `sync-up`, перед `sync-vault (push)`.
Почему так, а не «писать в Jellyfin»: Jellyfin не источник просмотров и не имеет
файлов для большинства позиций. Единственный честный источник — Lampa.
---
## 3. Шаги
1. **Бэкап.** `TimeCode.sql` + `movies-watchlist.md` + `watchlist-sync/` в
`~/backup-watchlist-before-lampac-sync-<ts>/`. Без бэкапа не начинать.
2. **Новый файл** `watchlist_sync/sync_from_lampac.py`:
- читает БД по пути из env `LAMPAC_TIMECODE_DB` (default
`/lampac/database/TimeCode.sql`; хост-путь подставить в `.env`);
- отбирает `deleted = 0` и (`percent >= 90` **или** `watched_at > 0`);
- `identity` → `tmdb_id` (срезать `_movie` / `_tv`); `_tv` пропускать на первом этапе;
- строит `{tmdb_id: title}` из `watchlist.json` (`channels.*.items[]`);
- для каждого найденного `[ ]`-строки в `.md` — `[x]` через `_mark_watched`;
- `--dry-run` по умолчанию, `--apply` для записи; печатает каждое действие.
3. **Копия для теста.** Dry-run на **копии** боевого `.md`, сверить: сколько строк
перешло, какие именно. Только после сверки — `--apply` на боевом.
4. **`.env`** на Кра́кене: `LAMPAC_TIMECODE_DB=/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/lampac/database/TimeCode.sql`
(БД лежит на хосте, не в контейнере — путь проверен).
5. **Крон.** В `~/scripts/watchlist-nightly.sh` — новый вызов **после** `sync-up`,
**до** `sync-vault (push)`. Правка через `sudo cp` (`~/scripts/` — `root:root 755`).
Бэкап скрипта до правки. Сумма новой версии зафиксировать в доке.
6. **Порог `percent`.** Начать с `>= 90`. Проверить на живой записи (сейчас 18% —
не должна проходить). При ложных срабатываниях — поднять до 95.
7. **Документировать** в `family/projects/watchlist-automation.md` (раздел про синк)
+ ссылка из `personal/plans/lampac-watchlist-plugin.md`.
---
## 4. Грабли (известные заранее)
| # | Граблю | Обход |
|---|---|---|
| 1 | `sqlite3` в контейнерах НЕТ | `docker cp` + `python3 -c "import sqlite3"` на хосте |
| 2 | `~/scripts/` на Кра́кене `root:root 755` — `scp` даёт `Permission denied` | только `sudo cp` |
| 3 | Кириллица в inline-ssh | **BLOCKED** tirith — писать скрипт файлом (`write_file`), не heredoc |
| 4 | `identity` для сериалов = `{tmdb}_tv` + серия неизвестна | этап 1 — только фильмы; сериалы отдельно |
| 5 | `percent` сбрасывается в `0` при `deleted` | фильтровать `deleted = 0` |
| 6 | Матч по названию ломается на год/рейтингах | матч **только** по `tmdb_id`, строгий |
| 7 | `timecodes` пишу сам `TimeCode`, при рестарте лампака не теряется (SQLite на хосте) | ок |
| 8 | Крон-файлы возвращать только `crontab /tmp/файл`; бэкап `crontab -l > f` обязателен | — |
---
## 5. Проверка результата
- [ ] `sync_from_lampac.py --dry-run` находит запись `1758655_movie` (18% — **не** проходит)
- [ ] После ручной отметки фильма в Lampa до 100% → `--dry-run` показывает его, `--apply` ставит `[x]`
- [ ] `[x]`-строка переехала вверх (поведение `_mark_watched` как у Jellyfin-канала)
- [ ] Лог ночного прогона содержит строку от `sync_from_lampac`
- [ ] Alex видит: фильм, отмеченный в Lampa, пропал из «Мой список» в каталоге после ночного прогона
- [ ] `git log` в vault — коммит с изменённым `.md` от `Hermes`
---
## 6. Что НЕ делается
- **Не** трогаем `arr.py` (`searchForMovie: True`) — отдельная задача на апруве.
- **Не** возвращаем `sync-down` в крон — закачек быть не должно.
- **Не** пишем в Jellyfin — он не источник просмотров.
- **Не** трогаем `cleanup` — отдельная задача.