136 lines
6.9 KiB
Markdown
136 lines
6.9 KiB
Markdown
---
|
||
title: Media Pipeline — Pitfalls & Debug Notes
|
||
created: '2026-05-16'
|
||
updated: '2026-05-18'
|
||
type: entity
|
||
tags:
|
||
- media-pipeline
|
||
- swift
|
||
- pitfalls
|
||
- debugging
|
||
confidence: high
|
||
---
|
||
# Media Pipeline — Pitfalls & Debug Notes
|
||
|
||
Операционные заметки по Swift media-pipeline (media_files_db / media-toolbox) на Кракене.
|
||
Связано: [[personal-os-architecture]], [[tech/kraken-network]]
|
||
|
||
## §9.x Score Engine — Критические питфолы
|
||
|
||
**§9.9 Year tiebreak:**
|
||
- `if count > 1` only — НИКОГДА не штрафовать единственного кандидата
|
||
- Если кандидат один — year mismatch не является причиной для снижения score
|
||
|
||
**§9.6 Subset floor:**
|
||
- Floor 0.35 когда queryToks ⊆ candidateToks (запрос является подмножеством кандидата)
|
||
- Это нормально — не считать subset-матч за слабый результат
|
||
|
||
**False resolves:**
|
||
- Проверять поле `title:` — если в title шум из имени файла = `hasBestEffortResult` junk, не настоящий resolve
|
||
- Пример: `2001 Космическая одиссея.mkv` → title: `2001 Космическая одиссея.mkv` = junk
|
||
|
||
## Транслитерация
|
||
|
||
- `transliteratedToLatin` для кириллических названий генерирует мусорные запросы — TMDb не находит `Migratsiya`
|
||
- **Решение:** не эмитить lat-запросы когда источник уже кириллица
|
||
- `transliteratedToCyrillic` из латинских имён — полезен (`Osnovnoi` → `Основной Инстинкт` → TMDb находит)
|
||
|
||
## KP (Кинопоиск) Rate Limit
|
||
|
||
- KP — настоящий fallback, дёргается только если TMDb не дал winner
|
||
- 40 KP-запросов vs 8428 TMDb в типичном прогоне
|
||
- Лимит исчерпывается только после очистки кеша когда весь TMDb идёт заново
|
||
- Проблема не в архитектуре — это нормально
|
||
- **KP API URL смена домена (Баг 8):** `api.kinopoisk.dev` → `api.poiskkino.dev`. Swift URLSession при редиректе теряет `X-API-KEY` → пустые ответы кешируются. Фикс: обновить URL, добавить `shouldCache: ((Data) -> Bool)?` — не кешировать пустой `docs`.
|
||
|
||
## Docker Deploy — Kraken (обязательный порядок)
|
||
|
||
После каждого деплоя **обязательно** чистить dangling images, иначе диск забивается (~1-2GB за прогон):
|
||
|
||
```bash
|
||
# Mac (после docker build)
|
||
docker image prune -f
|
||
|
||
# Kraken (после docker load)
|
||
ssh kraken "docker image prune -f"
|
||
```
|
||
|
||
Полная последовательность деплоя:
|
||
```bash
|
||
# 1. Собрать
|
||
docker build -f Dockerfile.kraken -t media-pipeline:kraken .
|
||
|
||
# 2. Перенести
|
||
docker save media-pipeline:kraken -o /tmp/mp.tar
|
||
scp /tmp/mp.tar kraken:/tmp/
|
||
ssh kraken "docker load -i /tmp/mp.tar && rm /tmp/mp.tar && docker image prune -f"
|
||
rm /tmp/mp.tar
|
||
|
||
# 3. Почистить локально
|
||
docker image prune -f
|
||
```
|
||
|
||
## Docker Run — Kraken
|
||
|
||
Всегда монтировать `unresolved.json` явно:
|
||
```bash
|
||
docker run \
|
||
-v "$(pwd)/config.json:/config/config.json:ro" \
|
||
-v "/media:/media" \
|
||
-v "$(pwd)/cache:/cache" \
|
||
-v "$(pwd)/unresolved.json:/config/unresolved.json" \
|
||
media-pipeline
|
||
```
|
||
Без явного маунта `unresolved.json` пишется внутри контейнера и теряется.
|
||
|
||
## Пофикшенные баги (сессии 2026-05)
|
||
|
||
- `Code=516 EEXIST` в ApplyPhase — исправлен
|
||
- Episode regex `\b` boundary — исправлен
|
||
- BBC prefix strip + trim — исправлен
|
||
- Yoficator Linux path — исправлен
|
||
- Backtick в TitleParser (`/[^'\p{L}\p{N}]+/` → strip apostrophes first) — исправлен
|
||
- §9.6 subset floor 0.35 — добавлен
|
||
- §9.9 year tiebreak `count>1` only — исправлен
|
||
- Year-miss retry в TMDb/KP providers — добавлен
|
||
- KP API URL (`api.poiskkino.dev`) + пустой-docs no-cache — исправлен
|
||
- Leet-speak в транслите (`4` = `ч`): `String.decodeLeetSpeak()` — исправлен
|
||
- Год `2023г` (кириллическое `г`): `(19|20)\d\d)г\b` паттерн — исправлен
|
||
- `TitleParser _year_` underscore boundary: `\b` → `(?<![0-9])..(?![0-9])` — исправлен (Баг 12)
|
||
- `OutputConfig` `cartoons`/`documentaries` не optional в JSON: `decodeIfPresent` — исправлен
|
||
- `xvid`/`divx` не в quality markers list — исправлен
|
||
- TitleParser: год как название фильма (1984, 2001) → safety guard ≥2 букв перед — исправлен
|
||
|
||
## Трекер-парсеры (Steps A-C, 2026-05-17)
|
||
|
||
`TrackerPageParserRegistry` — dispatch по `url.host`:
|
||
- `RutrackerPageParser` — CP1251/iconv, SwiftSoup, жанры из заголовка, KP/IMDb IDs
|
||
- `RutorPageParser`, `NNMClubPageParser` — структура реализована, требует fixture тестов
|
||
- `TrackerMediaKind` enum: movie/series/cartoon/documentary/music/audiobook/game/book/unknown
|
||
- `extractMediaKind` — детект по `BB.catId`/`FORUM_ID` на странице (100+ forum IDs)
|
||
|
||
`DirectoryPolicy` / `MatchPolicy` / `MoveStrategy`:
|
||
- `FileAction.transmissionMove(torrentId:, from:, to:)` — Codable
|
||
- `buildMovieActions` читает policy, `needsSymlink`, `needsNFO`, `effectiveOutputDir`
|
||
- `transmissionMove` когда есть `torrentId`, иначе `move`
|
||
|
||
**⚠️ `transmissionMove` генерируется только если scanner создал регион** (нужен video файл).
|
||
Музыка/книги/игры из downloads не перекладываются — нет `videoExtensions` для аудио (Шаг D, pending).
|
||
|
||
## Agent/Sync Pitfall — Duplicate unresolved
|
||
|
||
**Проблема:** agent пишет canonical dir в `/media/library/`, но sync при следующем запуске снова добавляет файл в unresolved (нет source marker).
|
||
|
||
**Фикс:** `updateUnresolvedStore` проверяет `allOutputDirs` на существующий canonical dir → пропускает. Файл: `PipelineRunner.swift`.
|
||
|
||
## QuerySnapshotGenerator
|
||
|
||
При изменениях в `TitleParser`/`NameTokenization`/`QueryBuilder` снапшоты в тестах устаревают.
|
||
**Регенерация:** `swift run QuerySnapshotGenerator` из корня репо.
|
||
НЕ использовать `SNAPSHOT_TESTING_RECORD=all` — крашит при 364 concurrent writes (SIGSEGV).
|
||
|
||
## Связанные заметки
|
||
|
||
- [[personal-os-architecture]]
|
||
- [[tech/kraken-network]]
|