[2026-05-13] Media toolbox Kraken: plan

This commit is contained in:
Alexey Martemyanov
2026-05-13 01:17:09 +06:00
parent 68a8291500
commit 320f0ac3f3
+679
View File
@@ -0,0 +1,679 @@
---
created: '2026-05-13'
updated: '2026-05-13'
status: planning
tags:
- kraken
- media
- jellyfin
- transmission
- swift
- project
---
# Media Toolbox на Кракене
> Создано: 2026-05-13. Цель: полноценный медиасервер на Кракене (RPi 5) — перенос библиотеки с HTPC, автосортировка через kodi-media-sync, Jellyfin, торренты.
---
## Архитектура (целевая)
```
HTPC (Bazzite 192.168.1.86)
└── /run/media/bazzite/Data/{Мультфильмы,Сериалы,movies,...}
└─── rsync ──→ Kraken HDD /srv/.../media/
Kraken (RPi 5, 192.168.1.14)
├── /srv/dev-disk-by-uuid-.../media/
│ ├── movies/ ← фильмы
│ ├── cartoons/ ← мультфильмы
│ ├── series/ ← сериалы
│ ├── documentaries/ ← документальные
│ └── downloads/ ← папка Transmission (входящие)
├── Docker: Transmission (скачка торрентов)
├── Docker: Jellyfin (стриминг на TV/телефоны)
└── Docker: kodi-media-sync (сортировщик + метаданные)
kodi-media-sync (Swift CLI, cross-compile Mac → Linux ARM64)
├── Читает downloads/ + необработанные папки
├── Идентифицирует через Transmission RPC (если торрент) или поиск по имени
├── Создаёт NFO-файлы (Kodi/Jellyfin-совместимые)
├── Раскладывает по категориям (movies/cartoons/series/docs)
├── Обновляет пути скачки в Transmission через RPC API
└── Запускается как download-complete скрипт Transmission
Hermes cron (Кракен, 1-2x/day)
└── kodi-media-sync --dry-run → лог → manual confirm / auto-apply
TrueNAS (Taiga)
└── Transmission → миграция на Крaken (или оставить для seed, скопировать БД торрентов)
```
---
## Статус по шагам
| Шаг | Статус | Заметки |
|-----|--------|---------|
| 1 — Исследование (Kodi lib на HTPC) | 🔲 | |
| 2 — Wake HTPC + prevent sleep | 🔲 | |
| 3 — Копирование медиа HTPC → Kraken | 🔲 | |
| 4 — Transmission на Кракене | 🔲 | уже есть контейнер в /home/kraken/media/ |
| 5 — Перенос torrent-базы с HTPC | 🔲 | Windows раздел или Bazzite Transmission |
| 6 — kodi-media-sync: изучить + расширить | 🔲 | Swift, scaffold готов |
| 7 — Cross-compile Mac → Linux ARM64 | 🔲 | Swift toolchain для aarch64 |
| 8 — Docker-образ kodi-media-sync на Кракене | 🔲 | |
| 9 — Config: пути, Transmission, категории | 🔲 | |
| 10 — Dry run: проверка сортировки | 🔲 | |
| 11 — Jellyfin: настройка библиотек | 🔲 | уже есть контейнер |
| 12 — Transmission download-complete hook | 🔲 | |
| 13 — Hermes cron на Кракене | 🔲 | |
| 14 — Руtracker-скилл (поиск/закачка) | 🔲 | |
| 15 — Музыка: скачка + sync с TrueNAS | 🔲 | |
---
## Шаг 1 — Исследование: Kodi lib на HTPC
**Цель:** найти реальную структуру медиабиблиотеки, где симлинки, куда ведут.
```bash
ssh -i ~/.ssh/id_rsa bazzite@192.168.1.86
# Kodi userdata (может быть в двух местах)
ls ~/.kodi/userdata/
ls ~/.kodi/userdata/sources.xml # список медиаисточников
# Если Kodi с Windows раздела мигрировал
ls /run/media/bazzite/Data/AppData/Roaming/Kodi/ 2>/dev/null
# Найти симлинки в home
find ~ -type l 2>/dev/null | head -30
# Медиа на Data разделе
ls /run/media/bazzite/Data/ | grep -iE "film|movie|кино|мульт|serial|сери|doc"
```
**Ожидаемые пути на Data:**
- `/run/media/bazzite/Data/Мультфильмы/`
- `/run/media/bazzite/Data/Сериалы/`
- `/run/media/bazzite/Data/movies/`
- `/run/media/bazzite/Data/документальные/` (или аналог)
---
## Шаг 2 — Wake HTPC + предотвращение сна
### Wake on LAN
```bash
# С Mac или Кракена:
wakeonlan B0:6E:BF:60:6D:64
# или
python3 ~/Automation/wake-on-lan.py
```
### Предотвратить сон во время SSH / копирования
**Вариант A — systemd-inhibit (рекомендуется):**
```bash
# Запустить на HTPC в отдельной SSH-сессии, оставить висеть пока идёт копирование:
systemd-inhibit --what=sleep:idle --who="media-copy" \
--why="Copying media to Kraken" --mode=block \
sleep infinity
```
Закрыть эту сессию = сон разрешён снова.
**Вариант B — через systemd сервис-lockfile (долгосрочно):**
Если SSH-сессия падает, нужен более надёжный способ. Создать временный `inhibit.service` который поднимается на время rsync.
**Вариант C — xdg-screensaver (только экран):**
```bash
# Только блокирует screensaver, не suspend
while true; do xdg-screensaver reset; sleep 60; done &
```
**Рекомендация:** вариант A (systemd-inhibit). Запускать в tmux, чтобы сессия жила независимо от SSH:
```bash
ssh bazzite@192.168.1.86 "tmux new-session -d -s inhibit 'systemd-inhibit --what=sleep:idle --who=media-copy --why=Copying --mode=block sleep 86400'"
```
---
## Шаг 3 — Копирование медиа HTPC → Kraken
### Целевая структура на Кракене
```
/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/media/
├── movies/
├── cartoons/
├── series/
├── documentaries/
└── downloads/ ← Transmission download dir
└── complete/ ← сюда Transmission кладёт готовые
```
### Создать директории
```bash
ssh kraken@192.168.1.14 "mkdir -p /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/media/{movies,cartoons,series,documentaries,downloads/complete,downloads/incomplete}"
```
### rsync HTPC → Kraken (прямо с Кракена)
```bash
# Запустить на Кракене (pull с HTPC):
HDD=/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade
# Мультфильмы
rsync -avh --progress --no-links \
bazzite@192.168.1.86:"/run/media/bazzite/Data/Мультфильмы/" \
"$HDD/media/cartoons/"
# Сериалы
rsync -avh --progress --no-links \
bazzite@192.168.1.86:"/run/media/bazzite/Data/Сериалы/" \
"$HDD/media/series/"
# Фильмы (уточнить точное имя папки)
rsync -avh --progress --no-links \
bazzite@192.168.1.86:"/run/media/bazzite/Data/movies/" \
"$HDD/media/movies/"
# Документальные (если есть)
rsync -avh --progress --no-links \
bazzite@192.168.1.86:"/run/media/bazzite/Data/документальные/" \
"$HDD/media/documentaries/" 2>/dev/null || true
```
> ⚠️ `--no-links` — не копировать симлинки как симлинки, следовать им.
> ⚠️ Для SSH без пароля с Кракена нужен ключ: `ssh-copy-id -i ~/.ssh/id_rsa bazzite@192.168.1.86`
### Альтернатива — пушить с HTPC через SSH:
```bash
# На HTPC:
rsync -avh --progress /run/media/bazzite/Data/Мультфильмы/ \
kraken@192.168.1.14:/srv/.../media/cartoons/
```
---
## Шаг 4 — Transmission на Кракене
Transmission уже задеплоен вместе с Jellyfin в `/home/kraken/media/docker-compose.yml`.
**Проверить конфиг:**
```bash
cat /home/kraken/media/docker-compose.yml
```
**Download dir** должен быть смонтирован как HDD/media/downloads.
**Порты:** 9091 (RPC), 51413 (peers)
**Если Transmission ещё не настроен** — проверить и обновить docker-compose, добавить volume:
```yaml
volumes:
- /srv/dev-disk-by-uuid-.../media/downloads:/downloads
- /srv/dev-disk-by-uuid-.../docker/transmission:/config
```
---
## Шаг 5 — Перенос torrent-базы с HTPC
### Найти торрент-клиент на HTPC
```bash
ssh bazzite@192.168.1.86
# Что стоит?
which transmission-daemon qbittorrent-nox rtorrent 2>/dev/null
systemctl list-units | grep -iE "torrent|transmission|qbit"
# Transmission (если есть):
ls ~/.config/transmission/torrents/ | head -10
ls ~/.config/transmission/resume/ | head -10
# qBittorrent:
ls ~/.local/share/data/qBittorrent/BT_backup/ | head -10
```
### Если Transmission на Bazzite:
```bash
# Скопировать на Кракен:
rsync -avh bazzite@192.168.1.86:~/.config/transmission/ \
kraken@192.168.1.14:/tmp/htpc-transmission/
```
### Если торрент-клиент был на Windows (TrueNAS Transmission):
Торрентная БД TrueNAS уже работает. Нужно решить:
- **Вариант A:** Перенести TrueNAS Transmission на Крaken (один клиент)
- **Вариант B:** Оставить TrueNAS для seed, Крaken для новых загрузок
**Миграция `.torrent` + `.resume` файлов:**
```bash
# Скопировать с TrueNAS (через duckdns):
rsync -avh -e "ssh -i ~/.ssh/id_rsa" \
truenas_admin@mallexxx.duckdns.org:/mnt/RED_2TB/docker/transmission/ \
/tmp/taiga-transmission/
# Поправить пути в .resume файлах:
# Старый путь TrueNAS: /mnt/storage/Downloads/...
# Новый путь Кракена: /downloads/complete/...
for f in /tmp/taiga-transmission/resume/*.resume; do
# .resume это bencoded файл — нужен python с bencode или transmission-edit
echo "$f"
done
```
> ⚠️ Файлы `.resume` — бинарный bencode-формат. Для правки путей использовать:
> `pip install bencoder.pyx` или `transmission-remote --torrent N --find /new/path`
> Проще: после импорта торрентов запустить media-sync с --fix-paths флагом через RPC API.
### Исправление путей через Transmission RPC:
```bash
# Transmission RPC: изменить путь для конкретного торрента
curl -s -u user:pass http://kraken:9091/transmission/rpc \
-H "X-Transmission-Session-Id: TOKEN" \
-d '{"method":"torrent-set-location","arguments":{"ids":[N],"location":"/downloads/complete/FolderName","move":false}}'
```
**kodi-media-sync должен уметь это автоматически** (см. шаг 6).
---
## Шаг 6 — kodi-media-sync: функциональность
### Текущее состояние проекта
Проект `~/Developer/kodi-media-sync` (Swift):
- `Config.swift` — читает config.json: `transmission` URL, `directories[]`, `output.{movies,series}`
- `MediaSync.swift` — scaffold: сканирует директории, вызывает `updateMetadata` (пустая)
- `Path.swift` — утилиты для файловой системы
**Нужно реализовать:**
### 6.1 Transmission RPC клиент
```swift
// Sources/TransmissionClient.swift
struct TransmissionClient {
let url: URL
// Получить все торренты
func getTorrents() async throws -> [Torrent]
// Найти торрент по имени файла / папке
func findTorrent(forPath path: Path) async throws -> Torrent?
// Обновить путь торрента
func setLocation(torrentId: Int, location: Path) async throws
}
```
### 6.2 Идентификация медиа
```swift
// Sources/MediaIdentifier.swift
enum MediaType {
case movie(title: String, year: Int?)
case series(title: String, season: Int?, episode: Int?)
case cartoon(title: String, year: Int?)
case documentary(title: String, year: Int?)
case unknown
}
struct MediaIdentifier {
// Из имени папки/файла тип медиа + нормализованное название
func identify(path: Path, torrent: Torrent?) -> MediaType
// Поиск метаданных (TMDB/OMDB/локальный поиск)
func fetchMetadata(for media: MediaType) async throws -> MediaMetadata
}
```
**Алгоритм идентификации:**
1. Если есть торрент в Transmission — взять имя торрента, категорию
2. Парсить имя файла/папки: регексы для `S01E01`, год `(2024)`, качество `1080p`
3. Для документалок типа "знакомимся с кабанами" — поиск по названию (TMDB API)
4. Маппинг исходной папки → тип: `Мультфильмы/` → cartoon, `Сериалы/` → series
### 6.3 NFO-файлы (Kodi/Jellyfin)
```swift
// Sources/NFOWriter.swift
// Формат Kodi NFO: https://kodi.wiki/view/NFO_files/Movies
struct NFOWriter {
func writeMovieNFO(_ metadata: MediaMetadata, to path: Path) throws
func writeSeriesNFO(_ metadata: MediaMetadata, to path: Path) throws
func writeTVShowNFO(_ metadata: MediaMetadata, to path: Path) throws
}
```
### 6.4 Config расширение
```json
{
"transmission": "http://user:pass@192.168.1.14:9091/transmission/rpc",
"tmdb_api_key": "...",
"directories": [
"/media/downloads/complete",
"/media/movies",
"/media/cartoons"
],
"output": {
"movies": "/media/movies",
"series": "/media/series",
"cartoons": "/media/cartoons",
"documentaries": "/media/documentaries"
},
"dry_run": false,
"category_hints": {
"/media/cartoons": "cartoon",
"/media/series": "series",
"/media/documentaries": "documentary"
}
}
```
### 6.5 CLI флаги
```
kodi-media-sync [--config path] [--dry-run] [--path <specific_path>] [--fix-paths]
--dry-run Показать что будет сделано, не двигать файлы
--path PATH Обработать только конкретную папку/файл
--fix-paths Обновить пути в Transmission для уже существующих файлов
```
---
## Шаг 7 — Cross-compilation Mac → Linux ARM64
**Swift на Mac имеет встроенный cross-compiler для Linux.**
### Установить Swift toolchain для Linux (если не стоит):
```bash
# На Mac:
swiftly install latest
# или скачать Linux ARM toolchain:
# https://www.swift.org/install/linux/
```
### Build для aarch64-linux:
```bash
cd ~/Developer/kodi-media-sync
# Вариант A — через Docker buildx (рекомендуется):
docker buildx build --platform linux/arm64 \
-f Dockerfile.linux \
-t kodi-media-sync:arm64 \
--output type=local,dest=./build-arm64 .
# Вариант B — swift cross-compile (нужен SDK):
swift build --triple aarch64-unknown-linux-gnu \
--sdk /path/to/linux-arm64-sdk
```
### Dockerfile.linux для Крaken:
```dockerfile
FROM swift:5.9-jammy AS builder
WORKDIR /build
COPY . .
RUN swift build -c release
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y libstdc++6 libgcc-s1 && rm -rf /var/lib/apt/lists/*
COPY --from=builder /build/.build/release/kodi-media-sync /usr/local/bin/
COPY config.json /etc/kodi-media-sync/config.json
ENTRYPOINT ["kodi-media-sync"]
CMD ["--config", "/etc/kodi-media-sync/config.json"]
```
> ⚠️ Собирать образ на Mac через buildx с эмуляцией QEMU — не на RPi5 (undervoltage под нагрузкой)!
### Deploy на Кракен:
```bash
# Сохранить образ и скопировать:
docker save kodi-media-sync:arm64 | gzip | ssh kraken@192.168.1.14 "docker load"
```
---
## Шаг 8 — Docker-образ на Кракене
### `/home/kraken/media-sync/docker-compose.yml`:
```yaml
version: '3.8'
services:
kodi-media-sync:
image: kodi-media-sync:arm64
container_name: kodi-media-sync
restart: "no" # запускается по требованию, не автостарт
volumes:
- /srv/dev-disk-by-uuid-.../media:/media
- /srv/dev-disk-by-uuid-.../docker/media-sync:/config
environment:
- CONFIG=/config/config.json
network_mode: host # доступ к Transmission на localhost
```
### Запуск (разовый):
```bash
cd /home/kraken/media-sync
sudo docker compose run --rm kodi-media-sync --dry-run
sudo docker compose run --rm kodi-media-sync
```
---
## Шаг 9 — Config для Кракена
```json
{
"transmission": "http://admin:pass@192.168.1.14:9091/transmission/rpc",
"tmdb_api_key": "КЛЮЧ_ИЗ_TMDB",
"directories": [
"/media/downloads/complete"
],
"category_hints": {
"/media/cartoons": "cartoon",
"/media/series": "series",
"/media/documentaries": "documentary",
"/media/movies": "movie"
},
"output": {
"movies": "/media/movies",
"series": "/media/series",
"cartoons": "/media/cartoons",
"documentaries": "/media/documentaries"
},
"dry_run": false
}
```
> TMDB API key: получить на https://www.themoviedb.org/settings/api (бесплатно)
---
## Шаг 10 — Dry run и ручная проверка
```bash
# Dry run — показывает что будет сделано
sudo docker run --rm \
-v /srv/.../media:/media \
-v /srv/.../docker/media-sync:/config \
kodi-media-sync:arm64 --dry-run
# Проверить проблемные документалки
sudo docker run --rm ... kodi-media-sync:arm64 --dry-run \
--path "/media/downloads/complete/знакомимся с кабанами"
# Проверить NFO файлы
cat /srv/.../media/movies/SomeMovie/SomeMovie.nfo
```
**Что проверить:**
- [ ] Фильмы попали в movies/, мультфильмы в cartoons/ и т.д.
- [ ] NFO-файлы созданы и содержат правильные title/year/plot
- [ ] Документалки с нестандартными названиями найдены через TMDB
- [ ] Торренты в Transmission показывают правильные пути
---
## Шаг 11 — Jellyfin: настройка библиотек
Jellyfin уже работает на Кракене: http://192.168.1.14:8096
### Категории библиотек:
| Библиотека | Тип | Путь | Язык |
|------------|-----|------|------|
| Детские мультфильмы | Animated Movies | /media/cartoons | ru |
| Детские сериалы | Series | /media/cartoons-series | ru |
| Документальные | Movies | /media/documentaries | ru |
| Сериалы | TV Shows | /media/series | ru |
| Фильмы | Movies | /media/movies | ru |
### Настройки библиотеки:
- **Metadata Language:** Russian (или auto-detect)
- **NFO reading:** включить (Kodi-compatible NFO)
- **Sort by:** Date Added (поле из NFO), Rating, Title
### Сортировка и фильтрация в UI (Jellyfin):
- Детские: отдельная коллекция "Детям" → мультфильмы + детские сериалы
- Можно использовать **Collections** для кастомных подборок
- **Recently Added** — показывает по дате скачки (metadata: dateadded в NFO)
- Рейтинг: IMDB/TMDB — парсится из NFO `<rating>` тег
---
## Шаг 12 — Transmission download-complete hook
Transmission вызывает скрипт при завершении скачки:
```bash
# settings.json Transmission (в /config/):
# "script-torrent-done-enabled": true,
# "script-torrent-done-filename": "/usr/local/bin/on-download-complete.sh"
```
### `/usr/local/bin/on-download-complete.sh` (внутри контейнера):
```bash
#!/bin/bash
# Transmission передаёт переменные:
# TR_TORRENT_NAME, TR_TORRENT_DIR, TR_TORRENT_ID, TR_APP_VERSION
logger "Transmission: download complete: $TR_TORRENT_NAME"
# Запустить media-sync для этой конкретной папки
docker run --rm \
-v /srv/.../media:/media \
-v /srv/.../docker/media-sync:/config \
kodi-media-sync:arm64 \
--path "$TR_TORRENT_DIR/$TR_TORRENT_NAME"
```
> ⚠️ Скрипт должен быть доступен изнутри контейнера Transmission, или монтируется туда.
> Альтернатива: Transmission вызывает скрипт на хосте → хост запускает docker run.
---
## Шаг 13 — Hermes cron на Кракене
**Добавить через Hermes на Кракене:**
```
Крон: каждые 12 часов
Задача: запустить kodi-media-sync --dry-run на папке downloads, прислать отчёт в Telegram
```
### Cron job (добавить в Hermes Кракена):
```
Schedule: 0 */12 * * *
Prompt: Запусти kodi-media-sync на Кракене:
sudo docker run --rm \
-v /srv/.../media:/media \
-v /srv/.../docker/media-sync:/config \
kodi-media-sync:arm64 --dry-run
Пришли отчёт: что было распознано, что нет.
```
---
## Шаг 14 — Rutracker / торрент-поиск скилл
**Добавить скилл Кракену:**
- Поиск по Rutracker (может требовать авторизацию)
- Альтернативы: Jackett (агрегатор) + Prowlarr
- Добавление торрента в Transmission через RPC API
**Jackett** (рекомендуется как агрегатор):
```yaml
# docker-compose для Jackett:
services:
jackett:
image: linuxserver/jackett
ports:
- "9117:9117"
volumes:
- /srv/.../docker/jackett:/config
```
Jackett поддерживает Rutracker + сотни других tracker-ов, отдаёт unified API.
---
## Шаг 15 — Музыка: скачка + sync с TrueNAS
### Структура:
```
Kraken: /media/music/
├── [Artist]/
│ └── [Album]/
│ └── *.mp3
TrueNAS: /mnt/RED_2TB/storage/Music/ (master copy)
```
### kodi-media-sync — расширение для музыки:
- Идентификация: MusicBrainz / Beets
- Тегирование через `beets` (python tool)
- Sync Kraken → TrueNAS через rsync
### Jellyfin музыкальная библиотека:
- Тип: Music
- Путь: /media/music
- Плеер: Jellyfin + Finamp на Android
---
## Технические детали
### HTPC
- IP: 192.168.1.86
- SSH: `ssh -i ~/.ssh/id_rsa bazzite@192.168.1.86`
- Data mount: `/run/media/bazzite/Data/`
- WoL MAC: `B0:6E:BF:60:6D:64`
### Крaken
- IP: 192.168.1.14
- SSH: `ssh -i ~/.ssh/id_rsa 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
### kodi-media-sync
- Исходники: `~/Developer/kodi-media-sync/` (Mac)
- Язык: Swift 5.9+
- Зависимости: swift-argument-parser
- Target: Linux ARM64 (aarch64)
### TrueNAS (Taiga)
- SSH: `truenas_admin@mallexxx.duckdns.org`
- Transmission config: `/mnt/RED_2TB/docker/transmission/`
- Downloads: `/mnt/RED_2TB/storage/Downloads/`
---
## Журнал выполнения
### 2026-05-13 — Планирование
- Изучена структура: HTPC (Bazzite), Кракен (OMV/Docker), TrueNAS (Taiga)
- Найден проект kodi-media-sync: scaffold (Config + Path + пустой MediaSync)
- Jellyfin + Transmission уже есть на Кракене в `/home/kraken/media/`
- Transmission на TrueNAS: конфиг в `/mnt/RED_2TB/docker/transmission/`, скачки в `storage/Downloads/`
- Plan создан в Obsidian
### TODO (следующий сеанс)
- [ ] Разбудить HTPC, исследовать структуру медиа (шаг 1)
- [ ] Решить вопрос сна HTPC (шаг 2)
- [ ] Запустить rsync HTPC → Крaken (шаг 3)