musicbrainz-provider #2

Merged
Mrixs merged 72 commits from musicbrainz-provider into master 2026-08-05 20:19:16 +00:00
2 changed files with 39 additions and 16 deletions
Showing only changes of commit e493a4d228 - Show all commits

View File

@@ -13,7 +13,12 @@ NaviWatcher is an autonomous service daemon that monitors your Navidrome music c
1. **Scans** your Navidrome library via Subsonic API to get the list of artists and albums.
2. **Fetches** full artist discographies from MusicBrainz (using Release Groups to avoid duplicate editions).
3. **Compares** local collection with external data using fuzzy matching (configurable threshold, default 0.85).
4. **Notifies** you about missing albums/singles/EPs through daily Telegram digests and a web dashboard *(not yet implemented — see Implementation Status)*.
4. **Syncs automatically** on a periodic loop (`sync.interval`, default 6h): Navidrome artist/album pull → lazy MusicBrainz ID resolution → discography cache → re-scan, so the dashboard and digests always reflect current state.
5. **Notifies** you about missing albums/singles/EPs through daily Telegram digests and a web dashboard.
### How It Works Now
On startup NaviWatcher opens (or auto-migrates) a local SQLite database at `naviwatcher.db` in the working directory, performs one immediate sync+scan, then runs a ticker-driven periodic sync+scan goroutine until SIGTERM. The Web UI serves a basic-auth-protected dashboard; the Notifier runs a cron scheduler emitting a daily digest of newly-found missing releases (tracked via `notifications_sent`). The MusicBrainz ID for each artist is resolved lazily on first sync and cached on the `artist_settings` row.
### Features
@@ -21,8 +26,8 @@ NaviWatcher is an autonomous service daemon that monitors your Navidrome music c
- **Fuzzy matching** — smart string normalization (ignores remastered/deluxe/anniversary editions, year suffixes, special characters).
- **Per-artist filters** — opt out of Singles and Compilations per artist (via `artist_settings`); type filtering includes only Album/Single/EP primary types (plus release groups whose secondary types include Single/EP/Compilation).
- **MusicBrainz caching** — 24-hour TTL cache to minimize API calls and respect rate limits (1 req/sec).
- **Telegram notifications** — *(not yet implemented)* daily summary messages with links to the web UI.
- **Web dashboard** — *(not yet implemented)* browse missing albums, ignore releases, manage artist-specific settings.
- **Telegram notifications** — daily summary messages with links to the web UI, sent on the configured `telegram.cron_schedule`.
- **Web dashboard** — browse missing albums, ignore releases, manage artist-specific settings, with an archive of ignored releases.
- **Single binary deployment** — all HTML templates embedded via `//go:embed`.
- **Docker support** — ready for `docker compose` deployment.
@@ -63,10 +68,11 @@ NaviWatcher is an autonomous service daemon that monitors your Navidrome music c
5. **Access the web UI** at `http://localhost:8080`
> **Note (current status):** On startup the service opens a local SQLite database at
> `naviwatcher.db` in the working directory (existing databases are auto-migrated) and
> performs a compute-only scan of all monitored artists, logging the count of missing
> releases. `musicbrainz.user_agent` is required and validated at startup. The notifier
> and Web UI are not yet wired into the running service — scan results are logged only.
> `naviwatcher.db` in the working directory (existing databases are auto-migrated), performs
> one immediate sync+scan, then runs a periodic sync+scan loop (`sync.interval`, default 6h)
> until shutdown. `musicbrainz.user_agent` is required and validated at startup. The Web UI
> (basic-auth protected) and Telegram notifier (cron-scheduled) are wired in; set
> `telegram.enabled` to activate digests.
### Configuration
@@ -94,6 +100,10 @@ telegram:
scanner:
fuzzy_threshold: 0.85
# Periodic sync+scan loop frequency (Go duration, e.g. "6h", "30m"); defaults to 6h.
sync:
interval: 6h
```
See [docs/Specification.md](docs/Specification.md) for the full configuration reference and architecture details.
@@ -111,8 +121,10 @@ See [docs/Specification.md](docs/Specification.md) for the full configuration re
### Implementation Status
- **Scanner Engine** — implemented (compute-only). The missing-release detection core is complete: string normalization lives in `internal/normalize`, similarity scoring and the diff engine (`FindMissingReleases`, `ScanArtist`, `ScanAll`) in `internal/scanner`. It uses the configurable `scanner.fuzzy_threshold` (default 0.85), normalizes titles (ignoring `(Remastered)`/year/special-char variants), and skips releases marked ignored.
- **Notifier and Web UI** — not yet implemented (out of scope for the scanner plan). `main.run()` currently performs a compute-only scan and logs missing-release counts; it does not persist results or send notifications.
- **Scanner Engine** — implemented. The missing-release detection core is complete: string normalization lives in `internal/normalize`, similarity scoring and the diff engine (`FindMissingReleases`, `ScanArtist`, `ScanAll`) in `internal/scanner`. It uses the configurable `scanner.fuzzy_threshold` (default 0.85), normalizes titles (ignoring `(Remastered)`/year/special-char variants), and skips releases marked ignored.
- **Sync pipeline** — implemented. `SyncAll` pulls artists/albums from Navidrome into the DB, resolves each artist's MusicBrainz ID lazily (cached on `artist_settings.mbid`), syncs the MusicBrainz discography, then re-runs the scanner. Wired into `main.run()` as an immediate + periodic (`sync.interval`) loop.
- **Notifier** — implemented. A `Sender` interface with a Telegram implementation, `FormatDigest` for daily summaries, and a cron scheduler honoring `telegram.cron_schedule` (no-op when `enabled=false`); sent-tracking via `notifications_sent`.
- **Web UI** — implemented. Basic-auth-protected `net/http` server with `//go:embed` templates: dashboard with missing-release counts, artist detail with ignore actions, and an archive of ignored releases with restore.
### License
@@ -129,7 +141,12 @@ NaviWatcher — это автономный сервис-демон для мо
1. **Сканирует** библиотеку Navidrome через Subsonic API — получает список артистов и альбомов.
2. **Загружает** полные дискографии артистов из MusicBrainz (использует Release Groups, чтобы избежать дубликатов изданий).
3. **Сравнивает** локальную коллекцию с внешними данными через нечёткое сравнение строк (настраиваемый порог, по умолчанию 0.85).
4. **Уведомляет** об отсутствующих альбомах/синглах/EP через ежедневные дайджесты в Telegram и веб-панель *(пока не реализовано — см. раздел «Статус реализации»)*.
4. **Синхронизируется автоматически** по периодическому циклу (`sync.interval`, по умолчанию 6h): выгрузка артистов/альбомов из Navidrome → ленивое разрешение MusicBrainz ID → кэш дискографии → повторное сканирование, чтобы панель и дайджесты всегда отражали текущее состояние.
5. **Уведомляет** об отсутствующих альбомах/синглах/EP через ежедневные дайджесты в Telegram и веб-панель.
### Как это работает сейчас
При запуске NaviWatcher открывает (или автомигрирует) локальную SQLite-БД `naviwatcher.db` в рабочей директории, выполняет одну немедленную синхронизацию+сканирование, затем запускает управляемый тикером периодический цикл до получения SIGTERM. Веб-интерфейс — это панель под basic-auth; нотификатор запускает cron-планировщик, отправляющий ежедневный дайджест новых отсутствующих релизов (отслеживается через `notifications_sent`). MusicBrainz ID каждого артиста разрешается лениво при первой синхронизации и кэшируется в строке `artist_settings`.
### Возможности
@@ -137,8 +154,8 @@ NaviWatcher — это автономный сервис-демон для мо
- **Нечёткое сравнение** — умная нормализация строк (игнорирует ремастеры, deluxe/anniversary-издания, год в скобках, спецсимволы).
- **Фильтры по артистам** — отключение синглов и компиляций для конкретного артиста (через `artist_settings`); фильтрация по типам включает только основные типы Album/Single/EP (а также группы релизов, чьи вторичные типы содержат Single/EP/Compilation).
- **Кэширование MusicBrainz** — TTL 24 часа для минимизации запросов и соблюдения лимитов (1 запрос/сек).
- **Уведомления в Telegram** — *(пока не реализовано)* ежедневные сводки со ссылками на веб-интерфейс.
- **Веб-панель** — *(пока не реализовано)* просмотр отсутствующих альбомов, игнорирование релизов, управление настройками артистов.
- **Уведомления в Telegram** — ежедневные сводки со ссылками на веб-интерфейс, отправляемые по расписанию `telegram.cron_schedule`.
- **Веб-панель** — просмотр отсутствующих альбомов, игнорирование релизов, управление настройками артистов, архив проигнорированных релизов.
- **Один бинарный файл** — все HTML-шаблоны встроены через `//go:embed`.
- **Поддержка Docker** — готов к развёртыванию через `docker compose`.
@@ -204,6 +221,10 @@ telegram:
scanner:
fuzzy_threshold: 0.85
# Частота периодического цикла синхронизации+сканирования (длительность Go, напр. "6h", "30m"); по умолчанию 6h.
sync:
interval: 6h
```
Полную справку по конфигурации и архитектуру см. в [docs/Specification.md](docs/Specification.md).
@@ -221,8 +242,10 @@ scanner:
### Статус реализации
- **Scanner Engine** — реализован (только вычисления). Ядро поиска отсутствующих релизов готово: нормализация строк в `internal/normalize`, оценка схожести и движок сравнения (`FindMissingReleases`, `ScanArtist`, `ScanAll`) в `internal/scanner`. Используется настраиваемый `scanner.fuzzy_threshold` (по умолчанию 0.85), игнорируются варианты `(Remastered)`/год/спецсимволы, пропускаются отмеченные как игнорируемые.
- **Notifier и Web UI** — пока не реализованы (вне рамок плана сканера). `main.run()` выполняет только вычислительное сканирование и логирует количество отсутствующих релизов; результаты не сохраняются и уведомления не отправляются.
- **Scanner Engine** — реализован. Ядро поиска отсутствующих релизов готово: нормализация строк в `internal/normalize`, оценка схожести и движок сравнения (`FindMissingReleases`, `ScanArtist`, `ScanAll`) в `internal/scanner`. Используется настраиваемый `scanner.fuzzy_threshold` (по умолчанию 0.85), игнорируются варианты `(Remastered)`/год/спецсимволы, пропускаются отмеченные как игнорируемые.
- **Sync pipeline** — реализован. `SyncAll` выгружает артистов/альбомы из Navidrome в БД, лениво разрешает MusicBrainz ID каждого артиста (кэшируется в `artist_settings.mbid`), синхронизирует дискографию MusicBrainz, затем повторно запускает сканер. Подключён в `main.run()` как немедленный + периодический (`sync.interval`) цикл.
- **Notifier** — реализован. Интерфейс `Sender` с Telegram-реализацией, `FormatDigest` для ежедневных сводок и cron-планировщик по `telegram.cron_schedule` (no-op при `enabled=false`); отслеживание отправок через `notifications_sent`.
- **Web UI** — реализован. Защищённый basic-auth `net/http` сервер с `//go:embed` шаблонами: панель со счётчиками отсутствующих релизов, страница артиста с действиями игнорирования и архив проигнорированных релизов с восстановлением.
### Лицензия

View File

@@ -138,8 +138,8 @@ name collisions.)
- [x] verify config.yaml.example documents new `sync_interval` field
### Task 10: Update documentation
- [ ] add a short "How it works now" note to README/CLAUDE.md if present
- [ ] note the new `sync_interval` config key in `config.yaml.example`
- [x] add a short "How it works now" note to README/CLAUDE.md if present
- [x] note the new `sync_interval` config key in `config.yaml.example`
## Technical Details
- New migration `006` is additive; existing rows get `mbid = NULL` and are