Files
obsidian-vault/family/projects/media-toolbox-kraken.md
T

331 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
created: '2026-05-13'
updated: '2026-05-14'
status: in-progress
tags:
- kraken
- media
- jellyfin
- transmission
- swift
- project
---
# Media Toolbox на Кракене
> Цель: полноценный медиасервер на Кракене (RPi 5) — перенос библиотеки с HTPC, автосортировка через media-pipeline (Swift), Jellyfin, торренты.
---
## Архитектура (целевая)
```
HTPC (Bazzite 192.168.1.86)
└── rsync → Kraken HDD /srv/.../media/
Kraken (RPi 5, 192.168.1.14)
├── /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/media/
│ ├── movies/ ← фильмы (127 папок)
│ ├── cartoons/ ← мультфильмы (126 папок)
│ ├── series/ ← сериалы (45 папок)
│ ├── documentaries/ ← документальные (35 папок)
│ └── downloads/complete ← Transmission
├── Docker: Transmission (128 торрентов, порт 9091)
├── Jellyfin native (порт 8096)
└── Docker: media-pipeline:kraken (Swift CLI, arm64)
media-pipeline (~/Developer/media_files_db, ветка kraken-agent)
├── scan → resolve → apply (sync)
├── NFO-файлы для Jellyfin/Kodi
├── Transmission RPC для обновления путей
└── unresolved.json — реестр нераспознанных
```
---
## Текущий статус (2026-05-14, сессия 5)
### Sync результаты (последний запуск — dry_run=false)
| Категория | Resolved | Unresolved | Статус |
|-----------|----------|------------|--------|
| movies | ✅ | 2 (generation.p, Black Mirror dup) | NFO пишутся |
| series | ✅ | 1 (Black Mirror — homonym) | NFO пишутся |
| cartoons | ✅ | 6 | в работе |
| documentaries | ✅ | 6 | Tier 4 запускается |
| **Итого** | **401** | **15** | sync запущен |
> `dry_run` выключен (2026-05-14), запущен `sync` — NFO пишутся на диск.
### Что работает
- ✅ Docker образ `media-pipeline:kraken` задеплоен (ветка `kraken-agent`)
- ✅ Фильмы: 127/127 NFO
- ✅ Сериалы: все resolved (Black Mirror, Ну погоди!, и др. — через resolve-manual)
- ✅ Кеш API-ответов: `/config/cache`, 30-дневный TTL
- ✅ Transmission download-complete hook установлен
-`resolve-manual` работает для movie/series/single-file
### Открытые задачи
#### 1. 🔴 [ОБЯЗАТЕЛЬНО К ИСПОЛНЕНИЮ] Довести media-pipeline до 0 unresolved без resolve-manual
**Принцип:** `resolve-manual` — не решение. Тул должен резолвить всё автоматически.
**Текущее состояние:** 32 unresolved после sync (dry_run=false). Разбивка:
**Группа A — старые source-папки (scan подхватывает оригиналы после apply):**
`apply` создаёт canonical папки с симлинками, но оригинальные папки (`flow.aka.straume...`, `bezumnyj_maks_3...` и т.д.) остаются и при следующем scan снова попадают в unresolved. Это архитектурный баг.
- **Нужно:** scanner должен пропускать папки, чьи файлы уже используются как цели симлинков в canonical структуре (или: хранить список resolved source-paths в unresolved.json и пропускать их при scan).
**Группа B — мультики (tie/ambiguity):**
`козленок`, `тигренок`, `нехочуха.xvid`, `чебурашка.m4v`, `котенок по имени гав.m4v`, `25е - первый день`, `рики тики тави`, `приключения фунтика`, `незнайка`, `миграция`
- Причины: KP возвращает несколько кандидатов с одинаковым весом (tie), или IMDB/web search не находит по транслитерации.
- **Нужно:** улучшить tie-breaking (год из папки/файла, popularity score из KP), расширить кириллические варианты запроса.
**Группа C — документалки:**
`bbc. the real jungle book bear 2012`, `bbc. секреты собак`, `penguin meet the family (2020)`, `бегемоты - жизнь в воде`, `гиганты моря 2011`, `таинственная жизнь собак (hdtvrip 720p, 2013)`, `тайная жизнь собак (2016г)`, `prehistoric.planet.s02.1080p`, `жизнь птиц 1998`, `пингвины. шпион в толпе`, `супер белки (2018)`, `шпион в снегах 2018`
- Tier 4 (IMDb scrape) запускается, но candidates = 0 — title matching слишком строгий для BBC-документалок.
- **Нужно:** для documentaries/ добавить relaxed matching (убирать "bbc.", год, технические теги из запроса перед поиском).
#### 5. 🔴 Торренты: Transmission не видит 271/293 файлов
**Проблема:** Transmission ищет файл по `downloadDir/torrent_name`. После pipeline `apply` файлы переименованы в canonical форму (`Furiosa - A Mad Max Saga (2024)/Furiosa - A Mad Max Saga (2024).mkv`), а торрент ожидает оригинальное имя (`Furiosa.A.Mad.Max.Saga.2024.1080p....mkv`) прямо в `/media/movies/`. Совпадений нет → `haveValid=0` у 271 торрентов.
**22 торрента с файлами** — только те что в `/downloads/complete` (свежескачанные, pipeline ещё не обработал).
**Нужно:** реализовать `torrent-fix` subcommand: для каждого торрента искать файл по имени (case-insensitive fuzzy) в source директориях `/media/movies/`, `/media/cartoons/` и т.д., и вызывать `torrent-set-location` RPC.
#### 6. 🔴 Мультфильмы в library/movies (классификационный баг)
**Проблема:** `library/cartoons = 0 items`, `library/movies = 202 items` из которых **106 — мультфильмы**. `MediaFinalizer.OutputConfig` содержал только `moviesDirectories` + `seriesDirectories``buildMovieActions` всегда писал в movies.
**Фикс (задеплоен):**
1. `Config.OutputConfig` + `MediaFinalizer.OutputConfig` расширены: добавлены `cartoonsDirectories` + `documentariesDirectories`.
2. `movieOutputDir(forGenres:)` — роутинг по resolved жанрам (Animation→cartoons, Documentary→documentaries). Source path не используется.
3. `ProviderSearchStrategy.facts()` теперь эмитит `NFOMetadataFact` с жанрами провайдера (раньше genres терялись, в граф не писались).
**После деплоя:** нужно переместить существующие 106 неправильных entries из `library/movies` в `library/cartoons` (отдельный скрипт). Новые sync'и будут класть правильно.
- Настроить библиотеки в Jellyfin (http://192.168.1.14:8096)
- NFO файлы готовы (716 шт.), Jellyfin должен их подхватить
#### 4. 🟢 Hermes cron на Кракене
- Периодический запуск sync (новые загрузки через Transmission)
---
### Архитектурные принципы (важно)
| Принцип | Описание |
|---------|----------|
| **NFO как evidence** | LocalEvidenceStrategy читает NFO только если они пришли с контентом (торрент). Pipeline-generated NFO не должны читаться как входные данные — это circular dependency. Сейчас защита через `.media-pipeline-canonical` маркер (FileScanner пропускает canonical dirs). Долгосрочно: добавить `<generator>media-pipeline</generator>` тег в generated NFO и фильтровать в LocalEvidenceStrategy. |
| **Классификация по жанрам провайдера** | Роутинг в cartoons/documentaries — **только по жанрам из API** (TMDb/KP), через `MediaSearchCandidate.genres → NFOMetadataFact → movieOutputDir(forGenres:)`. Source path **никогда не используется** для категоризации. |
| **resolve-manual — не решение** | `unresolved.json` — очередь для автоматического резолва. resolve-manual только для экстренных случаев. |
| Решение | Описание |
|---------|----------|
| **Unresolved = last resort** | `unresolved.json` — НЕ мусорка. Это очередь для AI-агента (Tier 5), который по крону подбирает нераспознанные и разрешает их через LLM + web search. Для данной библиотеки цель: Tier 0-4 (TMDb → KP → IMDb scrape → web search) должны закрывать всё — в `unresolved.json` не должно попадать ни одного item'а из текущей библиотеки. |
| **Web search enabled** | `providers.webSearch.enabled=true` в config.json. IMDb scrape как Tier 4 для всего, что не нашли TMDb/KP. Tier 4 должен срабатывать до того, как item уходит в unresolved.json |
| **Документалки приоритет KP** | BBC/Discovery документалки плохо индексированы в TMDb. KP часто знает их под русскими названиями. Стратегия: сначала KP с русским названием из папки, потом IMDb |
| **resolve-manual** | Только для экстренных случаев, не как регулярный процесс |
| **Single-file NFO** | Для `.mkv`/`.m4v` файлов NFO создаётся рядом (`film.nfo`), не внутри |
---
## Исправленные баги
### Баг 1: Двойной вызов binary (КРИТИЧЕСКИЙ)
`docker run ... media-pipeline:kraken media-pipeline sync` → бинарь получал `media-pipeline` как аргумент.
**Фикс:** `ENTRYPOINT ["media-pipeline"]`
### Баг 2: TitleSimilarity — симметричный Jaccard
`encanto_2021_bdrip_by_dalemake` vs `Encanto`: lengthRatio=0.2 → rejected.
**Фикс:** Asymmetric scoring — candidate-coverage Jaccard когда query длиннее. Коммит `2704ce3`
### Баг 3: maxCallsPerRun = 8
8 API-вызовов → 192/200 items никогда не искались.
**Фикс:** 8 → 500. Коммит `2704ce3`
### Баг 4: Series-гипотезы для одиночных файлов
Одиночные `.avi`/`.mkv` без S/E маркеров получали series-гипотезу → TMDb искал по `/search/tv`.
**Фикс:** `videos.count < 2 && !hasAnyEpisodeMarkers → return nil`. Коммит `17c2ddc`
### Баг 5: HTTP кеш не персистился
`cacheDirectory = /usr/local/bin/cache` — удалялся при `docker run --rm`.
**Фикс:** Autodetect `/config` mount → `/config/cache`. Коммит `5a1053c`
### Баг 7: Tier 4 (web search) не запускался для контента не в TMDb/KP
**Проблема:** `MediaPlanner` гейтил Tier 4 на `hasLiveUnresolved()` — но когда TMDb/KP возвращают 0 кандидатов, арбитр фаерит `provider-not-found`, который прунит все гипотезы. После этого `hasLiveUnresolved=false` → Tier 4 никогда не запускался.
**Фикс:**
- `MediaPlanner`: Tier 4/5 теперь гейтятся на `!hasWinner` (нет `.winning` гипотезы), а не `hasLiveUnresolved`
- `ProviderSearchStrategy`: добавлен флаг `includePrunedHypotheses` — когда `true`, поиск идёт и по `.pruned` ветками
- `WebSearchStrategy.webSearchDefaults`: `includePrunedHypotheses=true`
**Коммит:** `115f757`
### Баг 8: KP API URL смена домена (301 redirect)
`api.kinopoisk.dev` переехал на `api.poiskkino.dev`. Swift `URLSession` при редиректе теряет `X-API-KEY` заголовок → KP возвращает `{"docs":[]}` → кешируется как валидный пустой ответ → советские мультики и BBC не резолвились неделями.
**Фикс:** (1) обновлён URL в `KinopoiskAPI.swift`; (2) `HTTPClient.fetch` получил `shouldCache: ((Data) -> Bool)?` — KP не кеширует ответы с пустым `docs`.
### Баг 9: Leet-speak в именах файлов
`ni4ego_horoshego`, `o_4em_govoryat_muzh4iny` — цифра `4` = `ч` в русском warez-транслите. Стандартная транслитерация не покрывает.
**Фикс:** `String.decodeLeetSpeak()` в `StringExtensions.swift` + дополнительный variant в `NameTokenization.variants`.
### Баг 10: Год `2023г` не парсился как год
`Миграция. 2023г.mkv` — кириллическая `г` (год) является word char, поэтому `\b` после `2023` не срабатывает → `qYear=nil` → TMDb ищет без фильтра года → 0 результатов.
**Фикс:** В `TitleParser.cleanupTitleAndYear` добавлен паттерн `(19|20)\d\d)г\b` до стандартного `\b` regex. Коммит `770e51e`.
### Баг 11: Cyr→Lat query variant — мусорные запросы
`NameTokenization` генерировал `transliteratedToLatin` variant для кириллических имён → `Миграция``Migratsiya` → TMDb/KP не знают транслит, 0 результатов, только занимают cache-слоты. Оригинальный Go-sync делал только Latin→Cyrillic.
**Фикс:** Убран `transliteratedToLatin` из query variants в `NameTokenization.swift`. Коммит `10d64da`.
### Баг 6: resolve-manual NFO path для single-файлов
`resolve-manual` писал NFO внутрь файла вместо рядом.
**Фикс:** Detect `isDirectory`, для файлов → `deletingPathExtension().appendingPathExtension("nfo")`. Коммит `c0fdc59`
---
## Конфиг на Кракене
```
/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker/media-pipeline/
├── config.json ← TMDb/KP ключи, Transmission RPC, directories
├── unresolved.json ← реестр нераспознанных (создаётся автоматически)
├── cache/ ← HTTP кеш 30 дней
└── on-download-complete.sh
```
**Запуск sync:**
```bash
docker run --rm \
-v /srv/.../media:/media \
-v /srv/.../docker/media-pipeline:/config \
media-pipeline:kraken \
sync --config /config/config.json
```
**Force-resolve одного item:**
```bash
docker run --rm ... media-pipeline:kraken \
resolve-manual --config /config/config.json \
--media-path '/media/cartoons/bremenskie.muzykanty' \
--tmdb 14327
```
---
## Технические детали
| | |
|--|--|
| HTPC | `bazzite@192.168.1.86`, Data: `/run/media/bazzite/Data/`, WoL: `B0:6E:BF:60:6D:64` |
| Кракен | `kraken@192.168.1.14`, HDD: `/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/` |
| Jellyfin | http://192.168.1.14:8096 |
| Transmission | http://192.168.1.14:9091 |
| Исходники | `~/Developer/media_files_db/`, ветка `kraken-agent` |
| Docker образ | `media-pipeline:kraken` (arm64, Colima 24GB) |
| kraken-ssh | `ssh kraken-ssh` → 10.99.1.2:22 через WireGuard (обновлено 2026-05-14) |
| docker deploy | `docker save img -o /tmp/img.tar && scp /tmp/img.tar kraken-ssh:/tmp/ && ssh kraken-ssh "docker load -i /tmp/img.tar && rm /tmp/img.tar"``docker save \| ssh` зависает через WireGuard tunnel |
| Transmission RPC | Токен: `curl -D /tmp/hdrs ... > /dev/null`, затем `grep X-Transmission-Session-Id /tmp/hdrs \| awk '{print $2}' \| tr -d '\r\n'`. Payload с base64 торрента — писать в файл (`-d @file`), не аргументом (иначе "Argument list too long") |
---
## Источник торрентов (важно)
Торренты на Кракене (128 шт.) импортированы из **Windows Transmission-daemon** на HTPC:
```
/run/media/bazzite/Windows/windows/serviceprofiles/localservice/appdata/local/transmission-daemon/
├── torrents/ ← 210 .torrent файлов
└── resume/ ← 214 resume (пути, статус)
```
Валидация resume (2026-05-14): **185/214 файлов есть на HTPC**. 29 missing:
- ~15 медиафайлов с утраченного диска `X:/Movies/` — потеряны безвозвратно
- ~14 игр/архивов из `Downloads/Unsorted/` — не медиа, не важно
Отдельно: **uTorrent** (`/run/media/bazzite/Windows/program files/utorrent/`) — 131 .torrent файл, resume.dat с 18 записями. Из них только 6 хешей совпадают с Кракеном.
`Karnavalnaya.noch.1956.BDRip.AVC.mkv` — есть в uTorrent и в Transmission на Кракене (`downloaded=0`). Файла нет нигде — **никогда не скачивался**.
## Текущий статус (2026-05-15, сессия 10)
### Sync результат (pipeline-run3, образ `738b4b7426e9`)
| Метрика | Значение |
|---------|----------|
| applied | 260 |
| skipped (unresolved) | 23 |
| errors | **0** ✅ |
### Фиксы этой сессии
-**Code=516 idempotent symlink** — повторный прогон больше не падает на уже существующих симлинках
-**TitleParser `\b` episode regex**`Movie 43` больше не обрезается до `Movi`; `Movie 43` → resolved
-**BBC prefix strip**`bbc. the real jungle book bear``the real jungle book bear` в запросах
-**Yoficator**`котёнок по имени гав` и `тигрёнок в чайнике` resolved через е→ё
-**rsync с HTPC завершён** — 303 фильма, 206 мультиков, 72 серии, 46 документалок (2.2TB)
-**unresolved.json маунтится**`-v $(pwd)/unresolved.json:/config/unresolved.json`
### Запуск pipeline (правильная команда)
```bash
cd /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker/media-pipeline
docker run --rm \
-v "$(pwd)/config.json:/config/config.json:ro" \
-v "/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/media:/media" \
-v "$(pwd)/cache:/config/cache" \
-v "$(pwd)/unresolved.json:/config/unresolved.json" \
media-pipeline:kraken
```
### 23 оставшихся unresolved — анализ
**Группа A — дубли (папка + файл, 4 пары):**
Папка resolv-ится (separateItems hypothesis), standalone файл нет.
- `Nehochuha.XVid.DVDRip.avi` (папка `nehochuha.xvid.dvdrip` — resolved ✅)
- `Bezumnyj_Maks_3_Pod_Kupolom_Groma_1985_[torrents.ru].mkv` (папка resolved)
- `Dolina.Ekho.2025.ATVP.WEB-DLRip.AVC.mkv` (папка resolved)
- `dolina.ekho.2025.atvp.web-dlrip.avc` (дубль папки выше)
**Группа B — не индексированы в KP/TMDb:**
- `bbc. the real jungle book bear 2012`
- `bbc. секреты собак`
- `penguin meet the family (2020)`
- `бегемоты - жизнь в воде`
- `супер белки (2018)`
- `шпион в снегах 2018`
- `Таинственная жизнь собак (HDTVRip 720p, 2013).mkv`
- `таинственная жизнь собак (hdtvrip 720p, 2013)` (дубль выше)
**Группа C — транслитные фильмы (KP находит, но support=0 из-за wrong-hypothesis):**
- `Ni4ego_horoshego_v_otele_El_Royal_2018` — KP `446021|2018` находит
- `O_4em_govoryat_muzh4iny_2010``О чем говорят мужчины` (year filter: 2010 vs 2011)
- `Serebryanye_konki_2020`
- `Generation.P.BDRip-AVC`
- `Stiratel.1996` — KP знает как `Стёртый`, транслит неверный
- `The.Frozen.Kingdom.of.the.Snow.Leopard.(2020)`
**Группа D — год mismatch:**
- `рики тики тави 1965` — в KP год 1976 (советский м/ф)
**Группа E — 2024-2025 новинки (возможно не проиндексированы):**
- `Odin.den.v.Stambule.2024`
- `Prorok.Istoriya.Aleksandra.Pushkina.2025`
**Группа F — неопределённое:**
- `25е - первый день`
### Следующие шаги
1. **Группа A:** удалить дубли-standalone-файлы (папки с тем же контентом есть)
2. **Группа C:** разобраться с wrong-hypothesis багом в арбитраже (KP result привязывается не к той hypothesis)
3. **Группа B:** rename папок на правильные названия (BBC docs не индексированы)
4. **Группа D/E:** relaxed year matching или override через resolve-manual
---
## Журнал
- **2026-05-15 сессия 10:** rsync с HTPC завершён (2.2TB). Фиксы: Code=516 idempotent symlink, TitleParser `\b` episode regex, BBC prefix strip, Yoficator (е→ё). Результат: **260 applied, 0 errors, 23 unresolved**. Movie 43 ✅, Котёнок ✅, Тигрёнок ✅, Нехочуха (папка) ✅.
- **2026-05-13 сессия 1:** HTPC разбужен, rsync запущен (~1.3TB), торрент-база импортирована (128 торрентов из Windows Transmission-daemon)
- **2026-05-13 сессия 2:** Docker build arm64, deploy на Кракен, first sync
- **2026-05-13 сессия 3:** `kraken-agent` ветка: UnresolvedStore, resolve-manual, agent, torrent-fix subcommands
- **2026-05-14 сессия 4:** 5 системных багов исправлены. Sync: 222/317 resolved (70%).
- **2026-05-14 сессия 5:** resolve-manual для 13 items. unresolved: 24 → 8. NFO path bug fix. kraken-ssh → WireGuard.
- **2026-05-14 сессия 6:** dry_run=false, sync запущен → 401 resolved, 32 unresolved. `apply` создаёт canonical папки + симлинки (оригиналы не трогает). 716 NFO написано. Валидация торрентов HTPC.
- **2026-05-15 сессия 7:** Торренты: 467 скопированы с HTPC, 323 добавлены в Transmission (294 итого после удаления 50 игр/нон-медиа). rsync: movies + мультфильмы + BBC документалки. Фиксы в коде: (1) best-effort collapse при исчерпании tierов, (2) дедупликация dir+file регионов в RegionPartitioner, (3) strip .aka./full.screen из TitleParser, (4) scanner пропускает canonical dirs по маркеру `.media-pipeline-canonical`, (5) Roman numerals в транслитерации (Ампир V). Финал: 19 unresolved.
- **2026-05-16 сессия 8:** Обнаружен корневой баг: KP API переехал `api.kinopoisk.dev``api.poiskkino.dev` (301 redirect, Swift теряет X-API-KEY). Исправлен URL. Удалены 359 невалидных KP-кеш записей. Добавлен leet-speak decode (`4``ch`: ni4ego→nichego, muzh4iny→muzhchiny). Починен кеш: KP пустые `docs:[]` больше не кешируются (были причиной повторных промахов после rate-limit/redirect). **Текущий unresolved: 22. Завтра KP лимит сбросится → ожидаем ≤5.**
- **2026-05-16 сессия 9:** Добавлен жанровый роутинг (cartoons/documentaries — только по genres из провайдера, не по source path). Фикс `nfoMeta priority` (provider genres > local empty NFO). Фикс парсинга года `2023г` (русский суффикс). TMDb genres передаются из config. **Убран Cyr→Lat query variant** (не соответствует оригинальному Go-sync, TMDb не ищет по транслиту). Порядок провайдеров подтверждён: TMDb → KP → IMDb (short-circuit после каждого). KP — настоящий fallback (40 запросов vs 8428 TMDb). Текущий unresolved: 30 (ждём сброса KP rate limit).