9.8 KiB
ЦЕЛЬ ПРОЕКТА
1. Общее описание проекта
NaviWatcher — это автономный сервисный инструмент (демон), предназначенный для автоматического мониторинга музыкальных коллекций, хранящихся в Navidrome (или любом сервере с поддержкой Subsonic API).
Основная задача: сопоставлять текущую медиатеку пользователя с полной дискографией артистов во внешних базах данных (MusicBrainz), находить отсутствующие релизы (альбомы, синглы, сборники) и уведомлять об этом пользователя через Telegram и встроенный веб-интерфейс.
2. Технологический стек
- Язык программирования: Go 1.21+
- База данных: SQLite 3 (для хранения кэша, настроек и состояний).
- HTTP-сервер: Стандартная библиотека Go (
net/http) +html/template. - Внешние зависимости (библиотеки):
github.com/mattn/go-sqlite3— драйвер базы данных.github.com/lithammer/fuzzysearch— библиотека для нечеткого сравнения строк.gopkg.in/yaml.v3— парсинг конфигурационных файлов.
2.1. Аутентификация в Navidrome (Subsonic API)
NaviWatcher взаимодействует с Navidrome через Subsonic API v1.16.1 (эндпоинты /rest/*).
Используется токенная аутентификация — стандартный метод Subsonic API:
- Формула токена:
token = md5(password + salt) - Salt генерируется клиентом случайно при каждом запросе.
- Пароль пользователя не передаётся в открытом виде.
- Это обеспечивает совместимость с любыми Subsonic-совместимыми серверами (Navidrome, Airsonic, Ampache).
Пример запроса:
/rest/getArtists?u=watcher_service&t=<md5_hash>&s=<random_salt>&v=1.16.1&c=NaviWatcher
Создание сервисного пользователя в Navidrome:
- В веб-интерфейсе Navidrome: Settings → Users → Add User.
- Задать username и надёжный пароль.
- Убедиться, что роль имеет доступ к нужным медиатекам.
- Указать эти данные в
config.yaml(поляuserиpassword).
3. Архитектура системы
Проект разделен на логические модули:
- Navidrome Client: Взаимодействие с Subsonic API v1.16.1 (чтение артистов и локальных альбомов). Аутентификация — токенная (md5), пароль не передаётся в открытом виде. Используются эндпоинты:
getArtists,getArtist,getAlbum,ping. - MusicBrainz Provider: Запрос дискографий с механизмом кэширования и соблюдением Rate Limit (1 запрос в секунду).
- Scanner Engine: Логика нормализации строк и сравнения списков (Diff).
- Database Layer: Персистентное хранение данных и пользовательских фильтров.
- Notifier: Планировщик задач и отправка уведомлений в Telegram.
- Web UI: Интерфейс для просмотра находок и управления исключениями.
4. Алгоритмы и бизнес-логика
4.1. Работа с внешними данными (MusicBrainz)
Для минимизации дубликатов (разные издания одного альбома) сервис должен работать с сущностью Release Group (RG), а не с конкретными релизами.
- Кэширование: Данные о дискографии артиста сохраняются в SQLite на 24 часа. Повторные запросы в этот период идут только в БД.
4.2. Фильтрация контента
При получении списка Release Groups из MusicBrainz применяется многоуровневый фильтр:
- По статусу: Игнорировать
Bootleg,Promotion,Pseudo-Release. - По типу (глобально): Разрешены
Album,Single,EP,Compilation. - По типу (персонально для артиста): Возможность отключить
SingleилиCompilationдля конкретного исполнителя через Web UI. - По вторичным признакам: Опциональное игнорирование
Live,Remix,Soundtrack.
4.3. Сравнение (Fuzzy Matching)
Чтобы избежать ложных срабатываний (например, «The Wall» vs «The Wall (Remastered)»), используется алгоритм:
- Нормализация: Приведение к нижнему регистру, удаление спецсимволов, удаление года (20xx), удаление ключевых слов в скобках (Deluxe, Anniversary, Expanded).
- Сравнение: Если коэффициент сходства строк выше 0.85 (настраиваемо), альбом считается «уже имеющимся».
4.4. Жизненный цикл уведомлений
- Найден новый релиз -> Проверка, нет ли его в таблице
ignored_releasesилиnotifications_sent. - Если нет -> Добавление в очередь на отправку.
- Раз в сутки -> Отправка сводного сообщения в Telegram -> Пометка в
notifications_sent.
5. Модель данных (SQLite)
Таблица artist_settings
Хранит параметры мониторинга для каждого артиста из Navidrome.
id: string (MBID или имя)name: stringignore_singles: boolean (default: false)ignore_compilations: boolean (default: false)monitored: boolean (default: true)
Таблица external_releases
Кэш релизов, найденных во внешнем мире.
rgid: string (MusicBrainz Release Group ID) — Primary Key.artist_id: string (FK)title: stringtype: string (album/single/ep)release_date: stringis_ignored: boolean (флаг скрытия из списка новинок)
Таблица notifications_sent
rgid: string (FK)sent_at: datetime
6. Требования к интерфейсу
6.1. Web UI (Dashboard)
- Главный экран: Список артистов, у которых есть «Missing Albums».
- Страница артиста:
- Список альбомов в Navidrome (Source: Subsonic).
- Список найденных новинок (Source: MB Cache).
- Действия: «Игнорировать этот релиз», «Игнорировать все синглы артиста».
- Страница «Архив»: Просмотр ранее проигнорированных релизов с возможностью их восстановления.
6.2. Telegram
- Сообщение должно содержать список имен артистов и количество найденных новинок.
- Ссылка на Web UI для детального просмотра.
7. Конфигурация (config.yaml)
server:
host: "0.0.0.0"
port: 8080
# Basic Auth для доступа к веб-интерфейсу
username: "admin"
password: "password123"
navidrome:
url: "http://localhost:4533"
user: "watcher_service"
password: "user_password"
# Токен и salt генерируются автоматически при каждом запросе (md5(password + salt))
musicbrainz:
user_agent: "NaviWatcher/1.0 ( mail@example.com )"
cache_ttl: 24h
telegram:
enabled: true
token: "bot_token"
chat_id: "your_chat_id"
cron_schedule: "0 10 * * *" # Каждый день в 10:00
scanner:
fuzzy_threshold: 0.85
ignore_bootlegs: true
include_compilations: true
8. Программные требования и ограничения
- Rate Limiting: MusicBrainz API позволяет делать не более 1 запроса в секунду. Демон должен строго соблюдать этот интервал.
- Concurrency: Запросы к Navidrome и MusicBrainz должны выполняться в отдельных горутинах, чтобы веб-интерфейс оставался отзывчивым.
- Graceful Shutdown: При получении сигнала завершения (SIGTERM) демон должен корректно закрыть соединение с SQLite.
- Static Files: Все HTML-шаблоны должны быть встроены в бинарный файл (использование
//go:embed). - Docker: Проект должен быть запущен через
docker compose.