--- title: Media Pipeline — Roadmap aliases: - "Медиа пайплайн роадмап" - "Media Pipeline roadmap" - "tracker parsers roadmap" created: '2026-05-17' updated: '2026-05-24' status: active tags: - media-pipeline - rutracker - rutor - nnm-club related: - "[[personal/projects/media-pipeline-file-routing]]" - "[[family/projects/media-toolbox-kraken]]" --- # Media Pipeline — Roadmap ## ✅ Сделано ### Шаг A — Transmission flow + Rutracker парсинг - `TrackerPageParserRegistry` — dispatch по `url.host` - `RutrackerPageParser` — CP1251/iconv, SwiftSoup, жанры из заголовка топика, KP/IMDb IDs - `TrackerMediaKind` — enum: movie/series/cartoon/documentary/music/audiobook/game/book/unknown - `extractMediaKind` — детект по `BB.catId` / `FORUM_ID` на странице Rutracker (100+ forum IDs) - `TorrentPageMetadataFact.mediaKind` + `TorrentLocator.torrentId` — пробрасывается через весь стек - `QueryBuilderStrategy` — `torrentTitle` как предпочитаемый source для поиска - Fallback NFO при unresolved + `torrentPageMetadata.hasRichData` ### Шаг B — Структура парсеров других трекеров - `RutorPageParser`, `NNMClubPageParser`, `PirateBayPageParser`, `NyaaPageParser` — структура реализована - Требуют верификации на живых HTML (нет тестовых fixture) ### Шаг C — DirectoryPolicy + transmissionMove - `DirectoryPolicy` / `MatchPolicy` / `MoveStrategy` в конфиге — backward-compatible - `FileAction.transmissionMove(torrentId:, from:, to:)` — Codable - `ApplyPhase` — async execute, RPC → fallback filesystem - `buildMovieActions` — читает policy, `needsSymlink`, `needsNFO`, `effectiveOutputDir` - `kindKey` из реального `TrackerMediaKind` → `destination_by_kind` - `transmissionMove` когда есть `torrentId`, иначе `move` - `config.json` на Кракене — `directory_policies` для всех директорий - Canonical маркер `.media-pipeline-canonical` для music/books/games пишется в `output` директорию рядом с файлом ## ⚠️ Частично работает ### Фильмы из downloads - `.mkv/.mp4` → scanner видит → регион → `transmissionMove` → `/media/movies/` → canonical marker → повторный run скипает ✅ - `TrackerMediaKind` детектится по forum_id → правильная `destination_by_kind` ✅ ### Музыка/книги/игры из downloads - Scanner не видит `.mp3/.flac/.epub/.zip` — нет `videoExtensions` для аудио ❌ - Регион не создаётся → `buildMovieActions` не вызывается → файл остаётся в downloads ## 📋 Следующие шаги ### Шаг D — Audio/non-video scanner (приоритет) Чтобы музыка/книги/игры из downloads реально перекладывались: 1. **`AudioClassifier`** — `isAudioFile()` для `.mp3/.flac/.m4a/.ogg/.aac/.wav/.opus` 2. **`NonVideoRegionBuilder`** — создаёт регионы для директорий без video но с audio/doc файлами 3. **`FileScanner`** — при наличии `TrackerMediaKind != .movie/.series` (из Transmission метаданных) создавать регион даже без video 4. **`buildAudioActions`** в `MediaFinalizer` — move + NFO рядом, без symlink, canonical marker рядом 5. **Альтернатива (проще)**: unsorted fallback — отдельный pass по `unsorted_dir` через Transmission API, для торрентов без региона вызывать `torrent-set-location` на основе `mediaKind` из страницы трекера ### Шаг E — Верификация парсеров - HTML fixture тесты для rutor, nnm-club на живых страницах - Проверить `extractMediaKind` на реальных forum_id ### Шаг F — Series из downloads - `buildSeriesActions` сейчас не читает policy → нет move для сериалов из downloads - Нужно прокинуть `torrentURLFacts`/`torrentPageFacts` аналогично movie ## Ключевые питфоллы - CP1251: `/usr/bin/iconv` через temp files — единственный способ на Linux - `extractGenres` — из заголовка топика `[YEAR, жанр, ...]`, не из тела поста - `transmissionMove` генерируется только если scanner создал регион (нужен video файл) - Canonical marker для music/books: `output == source dir` → повторный run скипает через `agentResolvedInLibrary` - `allOutputDirs` в PipelineRunner не включает `directoryPolicies[].output` → нужно обновить ## Файлы - `Sources/MediaSources/APIs/TrackerParsers.swift` — парсеры + registry + `extractMediaKind` - `Sources/MediaDomain/Facts/TorrentPageMetadataFact.swift` — `TrackerMediaKind`, поля - `Sources/MediaDomain/Facts/TorrentInfoURLFact.swift` — `torrentId: Int?` - `Sources/MediaSources/Protocol/TorrentInfoProvider.swift` — `TorrentLocator.torrentId` - `Sources/MediaPipeline/Config/Config.swift` — `DirectoryPolicy`, `MatchPolicy`, `MoveStrategy` - `Sources/MediaPipeline/Apply/FileAction.swift` — `transmissionMove` case - `Sources/MediaPipeline/Apply/ApplyPhase.swift` — async execute, RPC fallback - `Sources/MediaPipeline/Apply/MediaFinalizer.swift` — policy-aware routing, torrent extractors ## Связанные заметки - [[personal/projects/media-pipeline-file-routing]] - [[family/projects/media-toolbox-kraken]]