21 KiB
ytdlp-navidrome — сервис поиска и загрузки музыки
Назначение
Сервис-компаньон к Navidrome. Интерфейс — Matrix-бот. Позволяет:
- Искать музыку по названию трека или имени исполнителя.
- Отправлять превью (файл аудио) в чат для прослушивания.
- По подтверждению пользователя скачивать трек в коллекцию Navidrome.
Весь трафик наружу (yt-dlp и Matrix) проходит через локальный ByeByeDPI-прокси (обход DPI).
Инфраструктура: k3s на Raspberry Pi. Музыка хранится на SSD:
- Рабочая директория (превью):
/mnt/ssd/k3s/services/ytdlp_navidrome/tmp - Коллекция Navidrome (финальный файл):
/mnt/ssd/k3s/services/navidrome/music/ytdlp
1. Архитектура
┌────────────┐ ┌────────────────────────────────┐
│ Matrix │ Matrix CS API │ ytdlp-navidrome pod │
│ homeserver │◄──────────────────►│ │
│ │ │ ┌────────────┐ ┌───────────┐ │
└────────────┘ │ │ Go-сервис │ │ ByeByeDPI │ │
│ │ (Matrix │ │ (sidecar │ │
│ │ бот + │ │ SOCKS5 │ │
│ │ yt-dlp) │ │ прокси) │ │
│ └─────┬──────┘ └─────▲─────┘ │
│ │ │ :1080 │
│ │ --proxy │ │
│ └──────────────┘ │
└───────────────┬────────────────┘
│
┌──────────────────────────┼──────────────┐
│ /mnt/ssd (hostPath) │ │
│ ▼ │
│ ytdlp_navidrome/tmp/ navidrome/music/ytdlp/
│ (превью, очищается) (финальные файлы)
└─────────────────────────────────────────┘
Стек
| Компонент | Технология | Обоснование |
|---|---|---|
| Язык | Go | Статический бинарник, минимум зависимостей для ARM. |
| Фреймворк | Echo GO | Простой, быстрый |
| Matrix SDK | mautrix-go | Де-факто стандартный Go SDK для Matrix. |
| Поиск | yt-dlp --flat-playlist --print |
Только метаданные, без скачивания. |
| Скачивание | yt-dlp + ffmpeg | Поддержка всех музыкальных источников. |
| Прокси | ByeByeDPI (sidecar) | Обход DPI без внешнего VPN. Локальный SOCKS5. |
| Хранение | hostPath | Общие директории с Navidrome и для превью. |
Контейнеры в поде
| Контейнер | Образ | Порты | Назначение |
|---|---|---|---|
ytdlp-bot |
Собственный (Go + yt-dlp + ffmpeg) | — | Основной сервис |
byedpi |
ghcr.io/hufrea/byedpi:latest (или собственный) |
1080 (SOCKS5) |
Обход DPI, локальный прокси |
2. Функциональные требования
2.1 Диалог с пользователем (Matrix-бот)
Пользователь общается с ботом в личном или групповом чате Matrix.
Пользователь: /search Radiohead Creep
Бот: Найдено 5 результатов:
1. Radiohead - Creep (3:58)
2. Radiohead - Creep [Live] (4:21)
3. Radiohead - Creep [Acoustic] (3:45)
...
Ответьте номером для превью.
Пользователь: 1
Бот: [прикреплённый аудиофайл]
Добавить в коллекцию Navidrome? (да/нет)
Пользователь: да
Бот: ✅ Добавлено: Radiohead - Creep.opus
Команды бота
| Команда | Описание |
|---|---|
/search <запрос> |
Искать музыку. Возвращает список результатов с номерами. |
<номер> |
Отправить аудиопревью трека из результатов поиска. |
да / yes / добавить |
Сохранить трек в коллекцию Navidrome. |
нет / no / отмена |
Отменить. Удалить превью. |
/status |
Показать статус: место на диске, время работы, версия. |
/proxy |
Показать текущие настройки прокси и статус ByeByeDPI. |
/help |
Справка. |
Состояние диалога
Бот хранит состояние per-user (в памяти, не персистентное в v1):
| Состояние | Данные |
|---|---|
idle |
— |
search_results |
Запрос, список результатов, timestamp |
preview_sent |
video_id, путь к превью-файлу, timestamp |
Таймаут состояния: 10 минут. После таймаута превью удаляется из tmp/.
2.2 Поиск (внутренний)
- Бот формирует запрос к yt-dlp:
yt-dlp "ytsearch{limit}:{query}" \ --flat-playlist --print "%(id)s\t%(title)s\t%(duration_string)s\t%(channel)s" \ --proxy socks5://byedpi:1080 - Парсит вывод, возвращает пронумерованный список.
- Таймаут:
SEARCH_TIMEOUT(по умолчанию10s).
2.3 Превью аудио (внутренний)
- Указанный трек скачивается во временную директорию:
Качество
yt-dlp "https://www.youtube.com/watch?v={id}" \ -x --audio-format opus --audio-quality 5 \ --output "{tmp_dir}/{id}.%(ext)s" \ --proxy socks5://byedpi:10805(среднее) — превью, сэкономить трафик и место. - Бот отправляет файл в Matrix-чат как
m.audio. - Файл сохраняется в
tmp_dirдо решения пользователя (добавить / отменить).
2.4 Добавление в коллекцию (внутренний)
- Окончательное скачивание (или конвертация превью) в коллекцию:
Качество
yt-dlp "https://www.youtube.com/watch?v={id}" \ -x --audio-format opus --audio-quality 0 \ --embed-thumbnail --add-metadata \ --output "{music_dir}/{artist} - {title}.%(ext)s" \ --proxy socks5://byedpi:10800(лучшее) — финальный файл для Navidrome. - Файл переименовывается в формат
<artist> - <title>.<ext>. - Превью из tmp/ удаляется.
- Navidrome обнаружит файл при следующем сканировании (раз в 6ч).
2.5 Health check
Эндпоинт: GET /healthz (для Kubernetes liveness/readiness probes)
Ответ: 200 OK {"status": "ok"} — если:
- Go-сервис запущен
- yt-dlp доступен
- ByeByeDPI sidecar отвечает (проверка через socks5://byedpi:1080)
3. Конфигурация
3.1 Переменные окружения
Matrix
| Переменная | Обязательная | По умолчанию | Описание |
|---|---|---|---|
MATRIX_HOMESERVER |
да | — | URL homeserver (например https://matrix.example.com) |
MATRIX_USER_ID |
да | — | @bot:example.com — ID бота |
MATRIX_ACCESS_TOKEN |
да | — | Access token бота |
MATRIX_DEVICE_ID |
нет | YTDLBOT |
Device ID |
MATRIX_ENCRYPTION |
нет | false |
Использовать E2EE (требует libolm/curve25519-dalek) |
Поиск и скачивание
| Переменная | По умолчанию | Описание |
|---|---|---|
SEARCH_LIMIT |
10 |
Максимум результатов поиска |
SEARCH_TIMEOUT |
10s |
Таймаут на yt-dlp search |
DOWNLOAD_TIMEOUT |
120s |
Таймаут на скачивание |
AUDIO_FORMAT |
opus |
Целевой формат аудио |
AUDIO_QUALITY_PREVIEW |
5 |
Качество превью (0=лучшее, 10=худшее) |
AUDIO_QUALITY_FINAL |
0 |
Качество финального файла |
PREVIEW_TTL |
10m |
Время жизни превью-файла до автоудаления |
ALLOWED_USERS |
— | Список Matrix ID через запятую (пустой = все) |
Прокси (пробрасывается в yt-dlp через --proxy)
| Переменная | По умолчанию | Описание |
|---|---|---|
PROXY_ENABLED |
true |
Включить прокси для yt-dlp. false = без прокси. |
PROXY_URL |
socks5://localhost:1080 |
URL прокси (SOCKS5 / HTTP). Если запущен sidecar — socks5://byedpi:1080 |
ByeByeDPI (sidecar-контейнер)
Параметры ByeByeDPI передаются через COMMAND в Deployment (см. §4.2).
| Флаг | По умолчанию (рекомендация) | Описание |
|---|---|---|
--ip |
0.0.0.0 |
Адрес для прослушивания |
--port |
1080 |
Порт SOCKS5 |
--disorder |
1-1 |
Fake packet для браузеров/доменов |
--auto |
(включён) | Автоматический обход DPI |
--ttl |
3 |
TTL для fake-пакетов |
Все флаги настраиваются в Deployment-манифесте. Если нужен другой обходчик DPI (naiveproxy, xray, sing-box),
поменяйте image и PROXY_URL.
Пути
| Переменная | По умолчанию | Описание |
|---|---|---|
MUSIC_DIR |
/music |
mountPath к коллекции Navidrome |
MUSIC_SUBDIR |
ytdlp |
Поддиректория для финальных файлов |
TMP_DIR |
/tmp/ytdlp-previews |
Рабочая директория для превью |
YTDLP_PATH |
yt-dlp |
Путь к бинарнику |
3.2 Kubernetes-манифесты
Каталог: rasp/k3s/services/ytdlp_navidrome/
├── README.md # ← этот документ
├── main.go # исходный код сервиса
├── Dockerfile # мультистейдж: Go build + yt-dlp + ffmpeg
├── service.yaml # Namespace + Deployment + PersistentVolumes
└── go.mod
Deployment — ключевые моменты
spec:
containers:
- name: ytdlp-bot
image: ytdlp-navidrome:latest # Собственный образ
env:
- name: MATRIX_HOMESERVER
valueFrom:
secretKeyRef:
name: ytdlp-matrix-secret
key: homeserver
- name: MATRIX_USER_ID
valueFrom:
secretKeyRef:
name: ytdlp-matrix-secret
key: user-id
- name: MATRIX_ACCESS_TOKEN # секрет
valueFrom:
secretKeyRef:
name: ytdlp-matrix-secret
key: access-token
- name: PROXY_URL
value: "socks5://localhost:1080" # sidecar
- name: MUSIC_DIR
value: "/music"
volumeMounts:
- name: music
mountPath: /music/ytdlp # коллекция (Read-Write)
- name: tmp
mountPath: /tmp/ytdlp-previews
resources:
requests: { memory: "64Mi", cpu: "50m" }
limits: { memory: "128Mi", cpu: "500m" }
- name: byedpi # ◀ sidecar
image: ghcr.io/hufrea/byedpi:latest
command: ["ciadpi"]
args:
- "--ip=0.0.0.0"
- "--port=1080"
- "--auto"
- "--disorder=1-1"
- "--ttl=3"
ports:
- containerPort: 1080
name: sock5
resources:
requests: { memory: "16Mi", cpu: "10m" }
limits: { memory: "32Mi", cpu: "100m" }
# network=host не нужен — sidecar общается с основным
# контейнером через localhost (shared network namespace в поде)
volumes:
- name: music
hostPath:
path: /mnt/ssd/k3s/services/navidrome/music
type: DirectoryOrCreate
- name: tmp
emptyDir: { medium: Memory, sizeLimit: 512Mi }
# Превью хранятся в tmpfs — не нагружают SSD,
# ограничены 512 MiB для безопасности
Service: не нужен для бота (исходящие подключения к Matrix homeserver). Если нужен health endpoint для мониторинга — ClusterIP на порт 8080.
Secret:
apiVersion: v1
kind: Secret
metadata:
name: ytdlp-matrix-secret
namespace: ytdlp-navidrome
type: Opaque
stringData:
homeserver: "https://matrix.example.com"
user-id: "@bot:example.com"
access-token: "syt_..." # ← ваш токен бота
4. Нефункциональные требования
4.1 Формат аудио
| Параметр | Превью | Финальный файл |
|---|---|---|
| Кодек | Opus | Opus |
| Качество | 5 (среднее) |
0 (лучшее) |
| Обложка | нет | --embed-thumbnail |
| Теги | минимальные | --add-metadata (title, artist, album) |
| Пример размера | ~1–2 МБ на трек | ~3–5 МБ на трек |
Альтернатива: MP3 V0, если клиенты не поддерживают Opus.
4.2 Именование файлов
Директория на хосте: /mnt/ssd/k3s/services/navidrome/music/ytdlp/
Формат имени: <artist> - <title>.<ext>
Примеры:
Radiohead - Creep.opusMassive Attack - Teardrop.opusunknown - dQw4w9WgXcQ.opus(метаданные неизвестны)
Запрещённые символы в имени файла (/ \ : * ? " < > |) заменяются на _.
4.3 Производительность
| Метрика | Целевое значение |
|---|---|
| Поиск (10 результатов) | ≤ 5 секунд |
| Превью (1 трек) | ≤ 30 секунд |
| Финальное скачивание (1 трек) | ≤ 60 секунд |
| Потребление RAM (простой) | ≤ 64 MiB |
| Потребление RAM ( ByeByeDPI ) | ≤ 16 MiB |
| Потребление CPU (простой) | ≤ 50m + 10m (bot + proxy) |
4.4 Доступ из Matrix
- Бот запускается как зарегистрированный пользователь homeserver.
- Бот слушает события
m.room.messageи/search, реагирует наm.text. - Бот может быть ограничен
ALLOWED_USERS— список Matrix ID через запятую (если пусто — отвечает всем). - В v1 без E2EE. E2EE поддерживается mautrix-go (опциональное включение через
MATRIX_ENCRYPTION=true).
4.5 Безопасность
| Вопрос | Решение |
|---|---|
| Токен бота в секрете | ytdlp-matrix-secret — не в env-файле кластера |
| Доступ к боту | Опционально ALLOWED_USERS |
| Прокси — только для yt-dlp | ByeByeDPI слушает на localhost, не проброшен наружу |
| Запись в коллекцию | runAsUser совпадает с Navidrome (совместимость прав) |
4.6 Ограничения (scope v1)
| Feature | Статус в v1 | Примечание |
|---|---|---|
| Matrix-бот | ✅ входит | Основной интерфейс |
| E2EE | ⚠️ опционально | По флагу MATRIX_ENCRYPTION |
| REST API | ❌ не входит | Только Matrix. REST — при необходимости позже. |
| Web UI | ❌ не входит | — |
| Групповые чаты | ⚠️ basic | Бот отвечает на mention в группах |
| Очередь загрузок | ❌ не входит | Синхронная загрузка |
| SoundCloud/Bandcamp | ⚠️ best-effort | yt-dlp поддерживает, поиск может не работать |
| Изменение прокси на лету | ❌ не входит | Требуется перезапуск пода |
5. Логирование
Формат: текстовый, в stdout.
2026-07-02T14:30:00Z INFO search user=@alice:example.com query="Radiohead Creep" results=10 dur=2.1s
2026-07-02T14:30:05Z INFO preview user=@alice:example.com video_id=XF2YMYBLnSw size=1.2MB dur=18s
2026-07-02T14:30:20Z INFO download user=@alice:example.com file="Radiohead - Creep.opus" size=3.7MB dur=12s
2026-07-02T14:30:20Z INFO cleanup preview=XF2YMYBLnSw removed=true
2026-07-02T14:31:00Z WARN preview_ttl_expired video_id=dQw4w9WgXcQ
2026-07-02T14:32:00Z INFO byedpi_check status=ok latency=3ms
Уровень логирования: настраивается через LOG_LEVEL.
6. Тестирование
| Уровень | Что проверять |
|---|---|
| Unit | Парсинг вывода yt-dlp, формирование имён файлов, санитизация, FSM состояний |
| Integration | Запуск yt-dlp в subprocess с /dev/null прокси, проверка формата вывода |
| Matrix | Взаимодействие с тестовым homeserver (Dendrite/Conduit) |
| Manual | Полный сценарий: /search → номер → превью → да → файл в navidrome |
7. Стадии реализации
Stage 1 — MVP
- Matrix-бот: подключение через mautrix-go
- Команда
/search— парсинг вывода yt-dlp, отправка результатов - Превью: скачивание трека в tmp/, отправка аудио в чат
- Добавление: финальное скачивание в коллекцию, очистка tmp
- ByeByeDPI sidecar в Deployment
--proxyвезде где yt-dlp обращается наружу- Dockerfile (Go + yt-dlp + ffmpeg)
- service.yaml (Deployment + Secret)
- Smoke-тест: /search → preview → да → файл появился в файловой системе
Stage 2 — Улучшения
- Авторизация через ALLOWED_USERS
- E2EE (опционально)
- Автоочистка tmp/ по расписанию (cronJob или timer в Go)
/statusи/proxyкоманды- Обработка ошибок: не найдено, таймаут превью, нет места
- Батч-загрузка (серия треков по команде)
Stage 3 — Интеграция
- Принудительное сканирование Navidrome после загрузки (Navidrome API)
- Метрики Prometheus
- Адаптация прокси под разные обходчики (naiveproxy, xray, sing-box)
- REST-эндпоинты для альтернативного доступа (опционально)