885 lines
61 KiB
Markdown
885 lines
61 KiB
Markdown
---
|
||
created: '2026-05-13'
|
||
updated: '2026-05-17'
|
||
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.", год, технические теги из запроса перед поиском).
|
||
|
||
#### 7. 🟡 ROM-инфраструктура: qBittorrent + Rutracker + Эмуляторы
|
||
|
||
**Цель:** Eagle/Кракен могут самостоятельно искать и качать игры/ROM-ы.
|
||
|
||
**qBittorrent на HTPC:**
|
||
- [ ] Установить qBittorrent (Flatpak: `com.qbittorrent.qBittorrent`)
|
||
- [ ] Настроить Web UI (порт 8080, доступ с Mac/Кракена)
|
||
- [ ] Добавить Rutracker в список трекеров
|
||
|
||
**Switch эмуляция:**
|
||
- [ ] Установить EmuDeck на HTPC
|
||
- [ ] Достать `prod.keys` + `title.keys` (Switch firmware keys) — путь: `~/.config/Ryujinx/system/`
|
||
- Источник: rutracker (поиск "nintendo switch keys prod.keys") или community
|
||
- [ ] Скачать DKC: Tropical Freeze (.nsp или .xci) через qBittorrent
|
||
|
||
**Rutracker доступ для агентов:**
|
||
- [ ] Настроить парсинг/API-доступ к Rutracker (учётка + cookie или jackett)
|
||
- [ ] Научить Eagle/Кракена инициировать скачку через qBittorrent Web UI API
|
||
- [ ] Документировать workflow в этом файле
|
||
|
||
**Wii альтернатива (проще чем Switch):**
|
||
- [ ] Dolphin ставится через EmuDeck без BIOS
|
||
- [ ] DKC Returns (Wii) скачать как .wbfs
|
||
- [ ] Маппинг геймпада (нет Wiimote)
|
||
|
||
#### 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`.
|
||
|
||
### Баг 12: TitleParser `_year_` — `\b` не срабатывает на underscore-границах
|
||
|
||
**Проблема:** `bezumnyj_maks_3_pod_kupolom_groma_1985_[torrents.ru]` — год `1985` окружён `_`, а `_` является `\w` в Swift regex. Поэтому `\b1985\b` не срабатывает → `year=nil`, `1985` остаётся в title → TMDb запрос содержит `1985` в строке вместо `year=` параметра → 0 результатов.
|
||
|
||
**Следствие:** заголовок `безумный макс 3 под куполом грома 1985` → TokenParser видит `1985` как токен → вариант обрезается по digit-граничному паттерну → появляется мусорный вариант `безумный мак` (truncated).
|
||
|
||
**Фикс:** в `TitleParser.cleanupTitleAndYear` заменён `\b(\d{4})\b` на `(?<![0-9])((?:19|20)\d\d)(?![0-9])` — игнорирует `_` как boundary, проверяет только цифровые границы.
|
||
**Тест:** `NameTokenizationTests.yearInUnderscores` + `titleParserYearInUnderscores` — оба GREEN.
|
||
**Коммит:** `61be066` (заодно с NFO sidecar fix, фактически в коде с TitleParser.swift line 39).
|
||
|
||
### Баг 13: `OutputConfig` — `cartoons`/`documentaries` не optional в JSON
|
||
**Проблема:** Старые config-файлы и тест-JSON без новых полей бросали `keyNotFound` при декодировании.
|
||
**Фикс:** Добавлен `init(from: Decoder)` с `decodeIfPresent` + дефолт `[]`. Backward compatible. Коммит `2db0f81`.
|
||
|
||
### Баг 14: `xvid`/`divx` не в списке quality markers
|
||
**Проблема:** `Nehochuha.XVid.DVDRip` → `xvid` не стрипался → попадал в title tokens → мусорный запрос.
|
||
**Фикс:** Добавлены `xvid|divx` в регекс quality markers в `TitleParser.cleanupTitleAndYear`. Коммит `2db0f81`.
|
||
|
||
### Баг 15: TitleParser — год как название фильма (1984, 2001)
|
||
**Проблема:** `TitleParser` жадно вырезал первый `(19|20)\d\d` — для фильмов `1984`, `2001 Odyssey` оставлял пустой title и ломал TMDb запрос.
|
||
**Фикс:** Safety guard: стрип года только если перед ним ≥2 буквенных символа. Применён в обоих year-extraction блоках.
|
||
**Тесты:** `TitleParserExhaustiveTests.nineteenEightyFour`, `nineteenEightyFourBDRip`, `spaceOdyssey` — GREEN. Коммит `2db0f81`.
|
||
|
||
---
|
||
|
||
### Инструмент: QuerySnapshotGenerator
|
||
При изменениях в `TitleParser`/`NameTokenization`/`QueryBuilder` снапшоты в `QueryBuilderFixtureParameterizedTests.swift` устаревают.
|
||
**Регенерация:** `swift run QuerySnapshotGenerator` из корня репо.
|
||
НЕ использовать `SNAPSHOT_TESTING_RECORD=all` — InlineSnapshotTesting 1.18.7 крашит при 364 concurrent writes (SIGSEGV).
|
||
|
||
---
|
||
|
||
### Баг 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 kraken` → LAN (`192.168.1.14`) или VPN (`10.99.1.2`) через DNS-резолв |
|
||
| docker deploy | `docker save img -o /tmp/img.tar && scp /tmp/img.tar kraken:/tmp/ && ssh kraken "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`). Файла нет нигде — **никогда не скачивался**.
|
||
|
||
## Router script: Sonarr/Radarr → правильная библиотека Jellyfin
|
||
|
||
**Задача:** после импорта Sonarr/Radarr создать symlink в нужную папку Jellyfin на основе жанра/рейтинга. Sonarr/Radarr кладут файлы в `movies-radarr/` и `series-sonarr/` — Jellyfin видит только эти папки. Роутер создаёт symlinks в тематические библиотеки чтобы Jellyfin показывал правильную структуру.
|
||
|
||
### Логика маршрутизации
|
||
|
||
```
|
||
Входные папки:
|
||
/media/movies-radarr/ → фильмы
|
||
/media/series-sonarr/ → сериалы
|
||
|
||
Для каждого тайтла читаем NFO (или TMDb API):
|
||
- genres[]
|
||
- content_rating (R / 18+ / TV-MA / PG / etc.)
|
||
- type (movie / series)
|
||
|
||
Алгоритм (приоритет сверху вниз):
|
||
|
||
IF documentary IN genres:
|
||
movie → /media/documentaries/
|
||
series → /media/documentaries-series/
|
||
|
||
ELIF animation IN genres AND rating NOT IN [R, 18+, TV-MA]:
|
||
movie → /media/cartoons/
|
||
series → /media/cartoons-series/
|
||
|
||
ELSE (default):
|
||
movie → /media/movies/
|
||
series → /media/series/
|
||
```
|
||
|
||
Movies и Series — финальный fallback, если не попало в другие категории.
|
||
|
||
### Что делает скрипт
|
||
|
||
1. Сканирует `movies-radarr/` и `series-sonarr/`
|
||
2. Для каждого тайтла читает `movie.nfo` / `tvshow.nfo` → жанры + рейтинг
|
||
3. Если NFO нет → запрос TMDb API по имени папки
|
||
4. Определяет целевую папку по алгоритму выше
|
||
5. Создаёт symlink: `ln -s /media/movies-radarr/X /media/movies/X`
|
||
6. Копирует NFO + постер рядом с symlink (Jellyfin не видит метадату через symlink)
|
||
7. Тригерит Jellyfin rescan нужной библиотеки
|
||
|
||
### Запуск
|
||
|
||
- **При добавлении**: вызывается из Sonarr/Radarr → Settings → Connect → Custom Script
|
||
- **Полный пересчёт**: `router.py --rescan-all`
|
||
|
||
### Что НЕ делает
|
||
|
||
- Не трогает файлы в `movies-radarr/` и `series-sonarr/`
|
||
- Не удаляет старые symlinks без `--cleanup`
|
||
- Не переименовывает торренты
|
||
|
||
### Jellyfin библиотеки (целевые)
|
||
|
||
| Библиотека | Тип | Пути |
|
||
|---|---|---|
|
||
| Movies | movies | `/media/movies` + `/media/movies-radarr` |
|
||
| Series | tvshows | `/media/series` + `/media/series-sonarr` |
|
||
| Cartoons | movies | `/media/cartoons` |
|
||
| Cartoon Series | tvshows | `/media/cartoons-series` |
|
||
| Documentaries | movies | `/media/documentaries` |
|
||
| Documentary Series | tvshows | `/media/documentaries-series` |
|
||
|
||
Порядок в Jellyfin UI: Movies → Series → Cartoons → Cartoon Series → Documentaries → Documentary Series → Music
|
||
|
||
---
|
||
|
||
## *arr стек (Prowlarr / Radarr / Sonarr) — настройка 2026-05-17
|
||
|
||
### Архитектура
|
||
|
||
```
|
||
Prowlarr (9696) → Radarr (7878) + Sonarr (8989) → Transmission (9091)
|
||
↓ файлы на диск
|
||
Jellyfin (8096)
|
||
```
|
||
|
||
### Статус контейнеров
|
||
|
||
| Контейнер | Порт | Compose | Сети |
|
||
|-----------|------|---------|------|
|
||
| transmission | 9091 | docker/transmission/ | media_default + media_net |
|
||
| radarr | 7878 | docker/radarr/ | docker_default + media_net |
|
||
| sonarr | 8989 | docker/sonarr/ | docker_default + media_net |
|
||
| prowlarr | 9696 | docker/prowlarr/ | docker_default + media_net |
|
||
| jellyfin | 8096 | docker/jellyfin/ | media_default (не в media_net — OK, читает диск) |
|
||
|
||
Все compose-файлы обновлены: добавлена `media_net` (external).
|
||
|
||
### API ключи
|
||
|
||
| Сервис | Ключ |
|
||
|--------|------|
|
||
| Radarr | `cbcb8ec320104359920c0f611d503c7b` |
|
||
| Sonarr | `15fbec32f1cb4b05b1c2b229ce27359b` |
|
||
| Prowlarr | `134ac38a02b94d5fb3e3a6e67228b18e` |
|
||
| Jellyfin | `87af49b6ff62ea68da6abdab0d7a4fbc` (eagle-script) |
|
||
|
||
### Конфигурация
|
||
|
||
- **Radarr root folder:** `/media/movies-radarr`
|
||
- **Sonarr root folder:** `/media/series-sonarr`
|
||
- **Radarr ← Transmission:** настроен (host=transmission, port=9091, no auth)
|
||
- **Sonarr ← Transmission:** настроен
|
||
- **Prowlarr → Radarr + Sonarr:** fullSync
|
||
- **Prowlarr индексер:** RuTracker (user: mallexxx), baseUrl `https://rutracker.org/`
|
||
- **Radarr/Sonarr NFO:** включен (Kodi/Emby metadata writer)
|
||
|
||
### Jellyfin библиотеки
|
||
|
||
| Библиотека | Пути |
|
||
|------------|------|
|
||
| Movies | `/media/library/movies` + `/media/movies-radarr` |
|
||
| Cartoons | `/media/library/cartoons` |
|
||
| Series | `/media/library/series` + `/media/series-sonarr` |
|
||
|
||
`/media/library/` — существующая библиотека с NFO/постерами от media-pipeline.
|
||
`/media/movies-radarr`, `/media/series-sonarr` — новые скачки через *arr.
|
||
|
||
### Полная документация
|
||
|
||
Скилл на Кракене: `~/.hermes/skills/kraken-media-stack/SKILL.md`
|
||
Покрывает: все операции, API примеры, pitfalls.
|
||
|
||
---
|
||
|
||
## Текущий статус (2026-05-18, сессия 16)
|
||
|
||
### Jellyfin библиотеки (актуально после 2026-05-18)
|
||
|
||
| Библиотека | Тип | Пути |
|
||
|---|---|---|
|
||
| Movies | movies | `/media/movies` |
|
||
| Series | tvshows | `/media/series` |
|
||
| Cartoons | movies | `/media/cartoons` |
|
||
| Cartoon Series | tvshows | `/media/cartoons-series` |
|
||
| Documentaries | movies | `/media/documentaries` |
|
||
| Documentary Series | tvshows | `/media/documentaries-series` |
|
||
| Music | music | *(пусто)* |
|
||
|
||
`movies-radarr` и `series-sonarr` убраны из библиотек Jellyfin. Router script создаёт symlinks в тематические папки при каждом новом импорте.
|
||
|
||
**Router script:** `/srv/.../docker/media-pipeline/router.py`
|
||
```bash
|
||
# Dry-run:
|
||
python3 /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker/media-pipeline/router.py
|
||
# Apply:
|
||
python3 /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker/media-pipeline/router.py --apply
|
||
```
|
||
|
||
### Сделано сегодня
|
||
|
||
- ✅ Love, Death & Robots → `/media/series` (был в cartoons)
|
||
- ✅ Пингвины. Шпион в толпе → `/media/documentaries-series` (был в cartoons-series)
|
||
- ✅ Удалены 79 дублей standalone-файлов в cartoons (папка+файл → оставлена папка)
|
||
- ✅ Удалены standalone-дубли в documentaries: Шпион в снегах, Невероятные хищники, Таинственная жизнь собак 2013
|
||
- ✅ Удалён LDR s01e18 lowercase-дубль
|
||
- ✅ Исправлен NFO Чебурашки → советский 1971 (tmdb 34859)
|
||
- ✅ Созданы NFO+постеры: В синем море в белой пене, Котёнок по имени Гав, Смешарики Новый сезон, Трям! Здравствуйте!, Тигрёнок в чайнике
|
||
- ✅ Исправлен NFO Знакомимся с кабанами → русский заголовок + постер
|
||
- ✅ Стрим (Flow 2024) → `/media/movies/Flow (2024)/`
|
||
- ✅ Обновлены Transmission locations: ~15 торрентов (cartoons-series, documentaries-series, series, правильные подпапки)
|
||
- ✅ Исправлена Тайна третьей планеты — удалена lowercase NFD-дублирующая папка
|
||
- ✅ Удалена пустая папка-призрак Бременских музыкантов
|
||
- ✅ Перемещены 5 арменфильм мультиков в отдельные папки
|
||
- ✅ rsync "в синем море в белой пене.avi" с HTPC
|
||
- ✅ Вокзал для двоих — `movie.nfo` рядом с part1/part2 (part1/part2 формат поддерживается Jellyfin нативно)
|
||
- ✅ Написан план router script: Sonarr/Radarr → тематические библиотеки по жанру/рейтингу
|
||
|
||
### Открытые задачи
|
||
|
||
- ✅ **Router script** — `router.py` реализован и запущен. Symlinks созданы, `movies-radarr`/`series-sonarr` убраны из Jellyfin библиотек.
|
||
- 🟡 **Передать Transmission locations** через router script при каждом новом импорте (вместо ручного обновления)
|
||
- 🔴 **rsync невыполненных с HTPC** — список в `Фильмы не перенесенные с htpc на кракен.md` (пункты `[ ]`)
|
||
- 🟡 **Watchlist** — добавить перенесённые фильмы в нужные разделы (кроме Знакомимся с кабанами и Нехочухи)
|
||
|
||
---
|
||
|
||
## Текущий статус (2026-05-17, сессия 15)
|
||
|
||
### rsync финальный прогон (28 файлов)
|
||
|
||
Запущен rsync 28 медиафайлов отсутствующих на Кракене (скрипт `/tmp/rsync_missing28.sh`, tmux сессия rsync3):
|
||
|
||
**Фильмы (22):** Брат 2 (4K UpScale), Вивариум, Ворон, Голодные игры Баллада, Голый пистолет, Злая, Инвалид, Как взломать экзамен, Компаньон, Кошачьи миры Луиса Уэйна, Леон, Начало, Операция С Новым Годом, Особенности охоты (1995), Особенности охоты зимний период, Особенности рыбалки, Первый день моей жизни, Пираты, Планета обезьян, Самолетом поездом машиной, Тетрис, Знакомимся с кабанами
|
||
|
||
**Мультфильмы (5):** Despicable Me 4, Encanto, Inside Out, Le Grand Mechant Renard, Nehochuha
|
||
|
||
**Сериалы (1):** Fleabag
|
||
|
||
После завершения — прогнать `fix_torrent_case.py` ещё раз для привязки новых файлов к торрентам.
|
||
|
||
## Текущий статус (2026-05-16, сессия 14)
|
||
|
||
### Регрессия 278→194 — диагноз и фикс
|
||
|
||
**Причина:** два бага в `RegionPartitioner.shadowedFilePaths`:
|
||
|
||
1. **Case-sensitive сравнение имён** — `The.Iron.Giant.1999.mkv` не матчился с sibling-dir `the.iron.giant.1999.../` (Linux case-sensitive FS).
|
||
2. **Canonical-dir не видны партиционеру** — `FileScanner.discover()` пропускал canonical-папки (`.media-pipeline-canonical` маркер) но не сообщал о них. Поэтому `Cloud.Atlas.BDRip-AVC.mkv` не знал что рядом уже есть `Cloud Atlas (2012)/`.
|
||
|
||
**Фиксы:**
|
||
- `ScanModels.ScanResult`: новое поле `canonicalDirectories: [URL]`
|
||
- `FileScanner.discover()`: заполняет `canonicalDirectories` при скипе
|
||
- `RegionPartitioner`: (1) case-insensitive baseName match, (2) shadow standalone если в том же каталоге есть любая canonical-dir
|
||
- **3 новых теста** в `ScanPhaseRegionSelectionTests`
|
||
- **Коммит:** `855b806`
|
||
|
||
**402/402 тестов GREEN. Деплой на Кракен запущен.**
|
||
|
||
## Текущий статус (2026-05-16, сессия 13)
|
||
|
||
### Итог прогона (новый образ, после всех фиксов сессий 10–13)
|
||
| Метрика | Значение |
|
||
|---------|----------|
|
||
| applied | ~269 |
|
||
| skipped (unresolved) | **13** |
|
||
| errors | **0** ✅ |
|
||
|
||
### ✅ Resolved в этой сессии
|
||
- **Generation П (2011)** — standalone `.mkv`, applied
|
||
- **Mad Max Beyond Thunderdome (1985)** — standalone `.mkv` слинкован в существующую canonical папку
|
||
- **Nehochuha** — resolved (папка)
|
||
|
||
### 13 остатков — финальный список
|
||
|
||
| # | Путь | Группа | Причина |
|
||
|---|------|---------|---------|
|
||
| 1 | `/media/cartoons/budil'nik 1984` | Мультик | Нет в TMDb/KP под этим названием |
|
||
| 2 | `/media/cartoons/novogodn'aya_pesenka_deda_moroza` | Мультик | Нет в TMDb/KP |
|
||
| 3 | `/media/documentaries/bbc. the real jungle book bear 2012` | BBC (genuinely absent) | Нет в TMDb/KP |
|
||
| 4 | `/media/documentaries/bbc. секреты собак` | BBC (genuinely absent) | Нет в TMDb/KP |
|
||
| 5 | `/media/documentaries/penguin meet the family (2020)` | BBC (genuinely absent) | Нет в TMDb/KP |
|
||
| 6 | `/media/documentaries/Таинственная жизнь собак (HDTVRip 720p, 2013).mkv` | Дубль | Дубль строки 7 (разный регистр) |
|
||
| 7 | `/media/documentaries/таинственная жизнь собак (hdtvrip 720p, 2013)` | Дока | Нет в TMDb/KP |
|
||
| 8 | `/media/documentaries/бегемоты - жизнь в воде` | BBC (genuinely absent) | Нет в TMDb/KP |
|
||
| 9 | `/media/documentaries/супер белки (2018)` | Дока | Нет в TMDb/KP |
|
||
| 10 | `/media/documentaries/шпион в снегах 2018` | Дока | Нет в TMDb/KP |
|
||
| 11 | `/media/movies/Dolina.Ekho.2025.ATVP.WEB-DLRip.AVC.mkv` | Дубль | Дубль строки 12 |
|
||
| 12 | `/media/movies/dolina.ekho.2025.atvp.web-dlrip.avc` | Папка | Уже resolved как папка — дубль |
|
||
| 13 | `/media/movies/Stiratel.1996.x264.BDRip.(AVC).0ptimus.mkv` | Фильм | Транслит `Stiratel` ≠ `Стёртый` (KP не находит) |
|
||
|
||
**По группам:**
|
||
- **Дубли** (3): строки 6/7 — один и тот же контент; строки 11/12 — папка уже resolved
|
||
- **Genuinely absent в TMDb/KP** (8): BBC документалки + советские мультфильмы
|
||
- **Транслит mismatch** (1): `Stiratel` → нужен русский title `Стёртый` для KP поиска
|
||
|
||
|
||
|
||
### 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.providers в config.json** — если `tmdb`/`kinopoisk` отсутствуют в блоке → disabled → 213 skipped вместо 278+ applied!
|
||
```json
|
||
"pipeline": {
|
||
"providers": {
|
||
"tmdb": { "enabled": true, "language": "ru-RU" },
|
||
"kinopoisk": { "enabled": true },
|
||
"torrent": { "enabled": true },
|
||
"webSearch": { "enabled": true },
|
||
"aiAgent": { "enabled": false }
|
||
}
|
||
}
|
||
```
|
||
|
||
### Запуск 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
|
||
|
||
## Unresolved: Investigation List
|
||
|
||
> Эти 15 позиций прошли все автоматические тиры (TMDb→KP→IMDb→WebSearch) и остались нераспознанными.
|
||
> Статус на 2026-05-16. Для каждого — план ручного расследования.
|
||
|
||
### Группа: Дубли (удалить с диска или дедуплицировать scanner)
|
||
|
||
| Путь | Действие |
|
||
|------|----------|
|
||
| `/media/cartoons/Nehochuha.XVid.DVDRip.avi` | Папка `nehochuha.xvid.dvdrip` уже resolved. Удалить standalone файл. |
|
||
| `/media/movies/Bezumnyj_Maks_3_Pod_Kupolom_Groma_1985_[torrents.ru].mkv` | Resolved в прогоне 2026-05-16 (слинкован). Удалить из unresolved.json вручную. |
|
||
| `/media/movies/Dolina.Ekho.2025.ATVP.WEB-DLRip.AVC.mkv` | Папка `dolina.ekho.2025...` уже resolved. Это standalone дубль. |
|
||
| `/media/movies/dolina.ekho.2025.atvp.web-dlrip.avc` | Та же папка что выше — в scanner попадает дважды. |
|
||
| `/media/documentaries/Таинственная жизнь собак (HDTVRip 720p, 2013).mkv` | Дубль строки ниже (разный регистр). |
|
||
| `/media/documentaries/таинственная жизнь собак (hdtvrip 720p, 2013)` | Основная версия — нужно искать. |
|
||
|
||
### Группа: Советские мультфильмы (нет под транслитом в TMDb/KP)
|
||
|
||
| Путь | Query для ручного поиска | Примечание |
|
||
|------|--------------------------|------------|
|
||
| `/media/cartoons/budil'nik 1984` | КП: «Будильник», год 1978 или 1984 | Советский м/ф Союзмультфильм |
|
||
| `/media/cartoons/novogodn'aya_pesenka_deda_moroza` | КП: «Новогодняя песенка Деда Мороза», год 1983 | |
|
||
|
||
**Расследование:** `curl "https://api.poiskkino.dev/v1.4/movie/search?query=Будильник&year=1984&type=cartoon"`
|
||
|
||
### Группа: Документальные (BBC/Discovery, нет в TMDb)
|
||
|
||
| Путь | Варианты поиска | Примечание |
|
||
|------|-----------------|------------|
|
||
| `/media/documentaries/bbc. the real jungle book bear 2012` | IMDb: «The Real Jungle Book» (2012, BBC) | Возможно серия BBC Natural World |
|
||
| `/media/documentaries/bbc. секреты собак` | IMDb/КП: «Secrets of Your Dog», «BBC Секреты собак» | |
|
||
| `/media/documentaries/penguin meet the family (2020)` | IMDb: «Penguin: Meet the Family» (2020) | BBC Studios |
|
||
| `/media/documentaries/бегемоты - жизнь в воде` | КП/IMDb: «Hippos: Life in the Water», «BBC Бегемоты» | |
|
||
| `/media/documentaries/супер белки (2018)` | КП: «Супер белки», IMDb: «Super Squirrels» (2018) | |
|
||
| `/media/documentaries/шпион в снегах 2018` | КП: «Шпион в снегах», IMDb: «Snow Spy», «Spy in the Snow» | |
|
||
| `/media/documentaries/таинственная жизнь собак (hdtvrip 720p, 2013)` | КП: «Тайная жизнь собак», IMDb: «Secret Life of Dogs» (2013, BBC) | |
|
||
|
||
**Расследование:**
|
||
1. Поиск на IMDb: `https://www.imdb.com/find?q=<title>&s=tt`
|
||
2. Если нашли — взять TMDb ID через: `https://api.themoviedb.org/3/find/<imdb_id>?external_source=imdb_id`
|
||
|
||
### Группа: Транслит-mismatch
|
||
|
||
| Путь | Реальное название | KP ID | Действие |
|
||
|------|-------------------|-------|---------|
|
||
| `/media/movies/Stiratel.1996.x264.BDRip.(AVC).0ptimus.mkv` | «Стёртый» (1996) | KP 9268 | `resolve-manual --tmdb <id>` или добавить alias в транслит-словарь |
|
||
|
||
**Расследование:** KP ID 9268 уже в кеше. Проблема — `Stiratel` → `Стирател` не совпадает с `Стёртый`.
|
||
Нужно проверить есть ли у KP 9268 TMDb external ID: `curl "https://api.poiskkino.dev/v1.4/movie/9268"`.
|
||
|
||
---
|
||
|
||
## Архитектура: Agentic Flow (agent subcommand)
|
||
|
||
> Записано 2026-05-16. Это главная спека `media-pipeline agent` subcommand.
|
||
|
||
### Входные данные агента (для каждого unresolved item)
|
||
|
||
Агент получает **всё что можно извлечь без баз**:
|
||
|
||
1. **Файловые метаданные** — имя папки/файла, набор файлов в папке (для TV show), длительность, формат, битрейт, разрешение (через `ffprobe` или `AVFoundation`)
|
||
2. **Транскрипция речи** — `ffmpeg` вырезает первые 60–120 сек аудио → **Whisper API** → текст (речь дикторов, диалоги, announcer voice)
|
||
3. **OCR титров** — `ffmpeg` захватывает 5–10 кадров из начала → **tesseract** или Vision API → текст с title card, студия, год
|
||
4. **Имена файлов** как уже есть (query names из UnresolvedEntry)
|
||
|
||
### Инструменты агента (function calling / tool use)
|
||
|
||
LLM получает инструменты и итеративно их вызывает до 10 шагов:
|
||
|
||
| Tool | Параметры | Описание |
|
||
|------|-----------|----------|
|
||
| `search_tmdb` | `title, year?, language?` | Поиск в TMDb API |
|
||
| `search_kinopoisk` | `title, year?` | Поиск в KP API |
|
||
| `search_imdb` | `title, year?` | IMDb full-text search |
|
||
| `fetch_page` | `url` | Фетч и возврат текста страницы (IMDb, Wikipedia, BBC, Rutracker, etc.) |
|
||
| `search_web` | `query` | Веб-поиск (DuckDuckGo / SerpAPI) |
|
||
|
||
### Выходные данные агента — одно из трёх
|
||
|
||
**Вариант A — DB-bound resolve** (нашёл в базе):
|
||
```json
|
||
{
|
||
"tmdb_id": 12345,
|
||
"kp_id": 678,
|
||
"imdb_id": "tt1234567"
|
||
}
|
||
```
|
||
→ Pipeline пишет NFO стандартно через TMDb/KP fetch
|
||
|
||
**Вариант B — full metadata без DB binding** (BBC/редкие доки):
|
||
```json
|
||
{
|
||
"title": "The Frozen Kingdom of the Snow Leopard",
|
||
"original_title": "...",
|
||
"year": 2020,
|
||
"description": "...",
|
||
"genres": ["Documentary"],
|
||
"poster_url": "https://...",
|
||
"source_url": "https://bbc.com/programmes/...",
|
||
"duration_min": 59
|
||
}
|
||
```
|
||
→ Pipeline пишет custom NFO с этими данными (без tmdb/kp ID). Jellyfin подхватит.
|
||
|
||
**Вариант C — manual resolve**:
|
||
```json
|
||
{
|
||
"category": "unrecognized",
|
||
"reason": "Не удалось идентифицировать после всех источников"
|
||
}
|
||
```
|
||
→ Остаётся в unresolved.json с agent_hint для ручного разбора
|
||
|
||
### Выход для TV show / папки с несколькими фильмами
|
||
|
||
Агент возвращает **список результатов**, по одному на файл/эпизод. Структура аналогична тому что делает media sync — те же поля что и NFO.
|
||
|
||
### Приоритет обработки
|
||
|
||
1. Сначала `The.Frozen.Kingdom.of.the.Snow.Leopard.(2020)` — самый сложный кейс, нет в TMDb/KP
|
||
2. Остальные BBC/Soviet docs из unresolved.json
|
||
3. Все остальные unresolved
|
||
|
||
### Реализация
|
||
|
||
#### Шаг 1: `MediaFileInspector` (новый модуль)
|
||
- `ffprobe` / AVFoundation → duration, format, resolution, audio tracks
|
||
- `ffmpeg` → extract frames → Vision/tesseract OCR → title card text
|
||
- `ffmpeg` → extract 60s audio → Whisper API → speech transcript
|
||
- Все результаты упакованы в `MediaFileContext` struct
|
||
|
||
#### Шаг 2: `ChatGPTAPI.resolveMedia(context: MediaFileContext)` (новый метод)
|
||
- OpenAI function calling loop (max 10 iterations)
|
||
- Системный промпт включает все данные из `MediaFileContext`
|
||
- LLM итеративно вызывает tools пока не придёт к выводу
|
||
- Возвращает `AgentResolutionResult` (A/B/C)
|
||
|
||
#### Шаг 3: `Agent.swift` subcommand расширен
|
||
- Запускает `MediaFileInspector` для каждого unresolved item
|
||
- Передаёт `MediaFileContext` в `resolveMedia`
|
||
- По результату: пишет NFO (A/B) или обновляет hint (C)
|
||
|
||
#### Шаг 4: Tier 6 в sync pipeline (опционально)
|
||
- После Tier 5 (AIAgentStrategy) — запускает VideoAnalysis для застрявших
|
||
- Добавляет найденные variants как новые QueryNameFact → передаёт через Tier 0–4
|
||
|
||
### Зависимости (Linux/arm64 на Кракене)
|
||
- `ffprobe` / `ffmpeg` — уже установлен на Кракене
|
||
- `whisper-cpp` или OpenAI Whisper API (через ChatGPTAPI)
|
||
- `tesseract` — нужно установить (`apt install tesseract-ocr tesseract-ocr-rus`)
|
||
- Vision API (macOS only) — для Linux использовать tesseract
|
||
|
||
## План: CV/Whisper для Tier 6 (для оценки)
|
||
|
||
> Идея: когда все тиры исчерпаны — использовать сам медиафайл как источник сигнала для идентификации.
|
||
|
||
### Подход A: Whisper — транскрипция аудиодорожки начала фильма
|
||
- `ffmpeg -i film.mkv -ss 0 -t 120 -vn -acodec pcm_s16le -ar 16000 /tmp/opening.wav`
|
||
- `whisper /tmp/opening.wav --language ru --model small` → текст
|
||
- Из текста: название, студия, год, авторы → уточнённый TMDb/KP запрос
|
||
- **Применимо:** советские мультфильмы, документалки с русским закадровым голосом
|
||
|
||
### Подход B: CV — захват кадров с титрами
|
||
- `ffmpeg -i film.mkv -ss 5 -vframes 10 /tmp/frames/frame%03d.jpg` (начало)
|
||
- `ffmpeg -i film.mkv -sseof -120 -vframes 10 /tmp/frames/end%03d.jpg` (конец — credits)
|
||
- OCR через `tesseract` или Vision API → текст с кадров
|
||
- Из текста: оригинальное название, год, студия
|
||
- **Применимо:** советские мультфильмы всегда имеют title card в первые секунды
|
||
|
||
### Подход C: Chromaprint/AcoustID — audio fingerprint
|
||
- `fpcalc film.mkv` → fingerprint → `https://api.acoustid.org/v2/lookup`
|
||
- **Применимо:** редко
|
||
|
||
### Реализация (если решим делать)
|
||
1. Новый `Tier6VideoAnalysisStrategy` в `MediaDomain`
|
||
2. Запускается только для `attempt_count >= 10`
|
||
3. На Linux: `ffmpeg`, `whisper-cpp` или OpenAI Whisper API, `tesseract`
|
||
4. Вывод: дополнительные `NameVariant` для QueryBuilder
|
||
5. **Стоимость Whisper API:** ~$0.006/мин, 120 сек = $0.01 за фильм. 13 items = $0.13
|
||
6. **Вердикт:** Хорошая идея для stuck документалок. Реализовать после agentic flow.
|
||
|
||
---
|
||
|
||
- **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).
|
||
- **2026-05-16 сессия 10–11 (продолжение):** Серия кодовых фиксов. **278 applied, 3 skipped (98.9%).** Созданы ручные NFO для 11 тайтлов (нарушение принципа — удалены в сессии 12).
|
||
- **2026-05-16 сессия 13:** Integration тест `BezumyjMaksIntegrationTests` (8 кейсов, все шаги pipeline). Generation П ✅ resolved. Mad Max Beyond Thunderdome standalone ✅ resolved. Nehochuha ✅. **13 unresolved** — финальный список: 3 дубля, 8 genuinely absent (BBC/советские доки), 1 транслит-mismatch (Stiratel→Стёртый). Удалены 15 ручных NFO. Подтверждён и задокументирован Баг 12 (`_1985_` year extraction). TitleParser регекс исправлен. Exhaustive тесты `TitleParserExhaustiveTests` (24 кейса) — GREEN. Баги 13 (OutputConfig backward compat), 14 (xvid/divx markers), 15 (год-как-название: 1984, 2001). `QuerySnapshotGenerator` executable — регенерация 364 снапшотов без racing. **391/391 тестов GREEN.** Текущий unresolved на Кракене: **6**. Следующий шаг: deploy → clean run → agentic flow для 3 BBC/доков.
|
||
|
||
### Фиксы сессий 10–11 (код)
|
||
- ✅ **double-counting fix** — `HypothesisArbiter`: `penalizedZeroProviders` в `HypothesisFact`, только newly-zero провайдеры пенализируются за проход. Серебряные коньки resolved.
|
||
- ✅ **originalHypo scope bug** — `collectEvidence` использовал `originalHypo` вне scope → compile error. Заменён на `hypothesis`.
|
||
- ✅ **TMDb en-US locale merge** — для Latin-script запросов: dual search (ru-RU + en-US), merge (ru-RU first), лимит 40. Generation П resolved.
|
||
- ✅ **NFO reverse-prefix match** — `HypothesisGenerator`: sidecar `Generation.P.nfo` теперь матчится к `Generation.P.BDRip-AVC.mkv` через `stemPrefix` проверку.
|
||
- ✅ **FileScanner sibling sidecar** — для file-region (не директория) сканируются сиблинги в parent dir → NFO рядом с mkv подхватывается. Stiratel resolved.
|
||
|
||
### 6 оставшихся unresolved (на Кракене, актуально)
|
||
|
||
**Группа A — standalone-дубли (2 шт.):**
|
||
Папки с тем же контентом уже resolved. Standalone-файлы pipeline не резолвит потому что дубль.
|
||
- `/media/movies/Bezumnyj_Maks_3_Pod_Kupolom_Groma_1985_[torrents.ru].mkv`
|
||
- `/media/cartoons/Nehochuha.XVid.DVDRip.avi`
|
||
|
||
**Группа B — genuinely absent в TMDb и KP (3 шт.):**
|
||
- `/media/documentaries/bbc. the real jungle book bear 2012`
|
||
- `/media/documentaries/bbc. секреты собак`
|
||
- `/media/documentaries/бегемоты - жизнь в воде`
|
||
|
||
**Группа C — папка (дубль resolved-папки):**
|
||
- `/media/movies/bezumnyj_maks_3_pod_kupolom_groma_1985_[torrents.ru]`
|
||
|
||
Группы A и C — устранить через дедупликацию scanner'а (пропускать standalone если есть resolved canonical папка с тем же контентом).
|
||
Группа B — Tier 5 (agentic flow) или manual resolve как последний resort.
|
||
|
||
### Следующий шаг: exhaustive tests → deploy → agentic flow
|
||
|
||
**Задача:** написать exhaustive тесты валидирующие каждый шаг парсинга для **всех** проблемных тайтлов — как незарезолвленных, так и тех, где создавались ручные NFO. Никаких ручных NFO не создавать — тул должен всё находить автоматически.
|
||
|
||
**Тайтлы для exhaustive тестов:**
|
||
|
||
| Filename | Ожидаемый title | Ожидаемый year | Проблема/баг |
|
||
|----------|----------------|----------------|-------------|
|
||
| `bezumnyj_maks_3_pod_kupolom_groma_1985_[torrents.ru]` | `bezumnyj maks 3 pod kupolom groma` | 1985 | Баг 12: `_year_` |
|
||
| `Nehochuha.XVid.DVDRip.avi` | `nehochuha` | nil | — |
|
||
| `budil'nik 1984` | `budilnik` | 1984 | апостроф soft-sign |
|
||
| `novogodn'aya_pesenka_deda_moroza_1982` | `novogodnyaya pesenka deda moroza` | 1982 | апостроф + год в underscore |
|
||
| `таинственная жизнь собак (HDTVRip 720p, 2013)` | `таинственная жизнь собак` | 2013 | год в скобках со стороссей |
|
||
| `шпион в снегах 2018` | `шпион в снегах` | 2018 | год в конце |
|
||
| `супер белки (2018)` | `супер белки` | 2018 | год в скобках |
|
||
| `penguin meet the family (2020)` | `penguin meet the family` | 2020 | год в скобках |
|
||
| `dolina.ekho.2025.atvp.web-dlrip.avc` | `dolina ekho` | 2025 | качество-маркеры |
|
||
| `Stiratel.1996` | `stiratel` | 1996 | год через точку |
|
||
| `The.Frozen.Kingdom.of.the.Snow.Leopard.(2020)` | `the frozen kingdom of the snow leopard` | 2020 | точки как разделители |
|
||
| `Generation.P.BDRip-AVC` | `generation p` | nil | буква как часть названия |
|
||
| `Ni4ego_horoshego_v_otele_El_Royal_2018` | `nichego horoshego v otele el royal` | 2018 | leet-speak + год |
|
||
| `O_4em_govoryat_muzh4iny_2010` | `o chem govoryat muzhchiny` | 2010 | leet-speak |
|
||
| `Serebryanye_konki_2020` | `serebryanye konki` | 2020 | год в underscore |
|
||
| `bbc. the real jungle book bear 2012` | `the real jungle book bear` | 2012 | BBC prefix + год |
|
||
| `bbc. секреты собак` | `секреты собак` | nil | BBC prefix кириллица |
|
||
| `Миграция. 2023г.mkv` | `миграция` | 2023 | Баг 10: кирилл. суффикс `г` |
|
||
| `рики тики тави 1965` | `рики тики тави` | 1965 | — |
|
||
| `25е - первый день` | `первый день` | nil | числовой префикс |
|