25 KiB
created, updated, status, tags
| created | updated | status | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|
| 2026-05-13 | 2026-05-14 | in-progress |
|
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.
Фикс (задеплоен):
Config.OutputConfig+MediaFinalizer.OutputConfigрасширены: добавленыcartoonsDirectories+documentariesDirectories.movieOutputDir(forGenres:)— роутинг по resolved жанрам (Animation→cartoons, Documentary→documentaries). Source path не используется.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гипотезы), а неhasLiveUnresolvedProviderSearchStrategy: добавлен флаг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:
docker run --rm \
-v /srv/.../media:/media \
-v /srv/.../docker/media-pipeline:/config \
media-pipeline:kraken \
sync --config /config/config.json
Force-resolve одного item:
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
\bepisode 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 (правильная команда)
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 2012bbc. секреты собак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— KP446021|2018находитO_4em_govoryat_muzh4iny_2010—О чем говорят мужчины(year filter: 2010 vs 2011)Serebryanye_konki_2020Generation.P.BDRip-AVCStiratel.1996— KP знает какСтёртый, транслит неверныйThe.Frozen.Kingdom.of.the.Snow.Leopard.(2020)
Группа D — год mismatch:
рики тики тави 1965— в KP год 1976 (советский м/ф)
Группа E — 2024-2025 новинки (возможно не проиндексированы):
Odin.den.v.Stambule.2024Prorok.Istoriya.Aleksandra.Pushkina.2025
Группа F — неопределённое:
25е - первый день
Следующие шаги
- Группа A: удалить дубли-standalone-файлы (папки с тем же контентом есть)
- Группа C: разобраться с wrong-hypothesis багом в арбитраже (KP result привязывается не к той hypothesis)
- Группа B: rename папок на правильные названия (BBC docs не индексированы)
- Группа D/E: relaxed year matching или override через resolve-manual
Журнал
- 2026-05-15 сессия 10: rsync с HTPC завершён (2.2TB). Фиксы: Code=516 idempotent symlink, TitleParser
\bepisode 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).