Vladimir Zagainov 8a5b58a817 fix: address code review findings
- Fix artist-ID namespace mismatch in MusicBrainz provider: SyncArtistDiscography
  now stores the canonical Navidrome artist ID (artist_settings.id) as
  external_releases.artist_id instead of the MusicBrainz MBID. Previously the
  MBID was stored, which violated the FK to artist_settings and broke the
  scanner join (local_albums.artist_id is the Navidrome ID), causing every
  external release to be falsely reported as missing and the sync insert to
  fail at runtime. getArtistFilterOptions now also resolves by the Navidrome ID.
- Resolve threshold in FindMissingReleases so the exported primitive honors the
  same zero-means-default contract as ScanArtist/ScanAll.
- Remove dead maxLen==0 guard in scanner.Similarity.
- Inline trivial buildPath helper; drop unused url import in client.go.
- Replace hand-rolled itoa with strconv.Itoa in tests.
- Rewrite SyncArtistDiscography tests to seed artist_settings with the Navidrome
  ID (tests previously seeded the MBID to mask the FK mismatch).
- Fix TestFuzzySmoke to exercise the real dependency (fuzzy.LevenshteinDistance /
  scanner.Similarity) instead of an unused API.
- Fix TestAppRun_ScanLogsMissingReleases to run the scan against a live context
  and assert the missing release is found.
- Document cached_at column in Specification.md and note startup scan / required
  musicbrainz.user_agent in README.
- Stop tracking .serena/ tooling config; add it to .gitignore.
2026-07-19 18:41:18 +03:00
2026-07-19 18:41:18 +03:00
2026-07-19 18:41:18 +03:00
2026-05-19 17:42:12 +03:00
2026-07-19 18:41:18 +03:00

NaviWatcher

Language: English | Русский


English

NaviWatcher is an autonomous service daemon that monitors your Navidrome music collection, compares it against artist discographies from MusicBrainz, and notifies you about missing releases via Telegram and a built-in web interface.

How It Works

  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.

Features

  • Subsonic API compatible — works with Navidrome, Airsonic, Ampache, and other Subsonic-compatible servers.
  • Fuzzy matching — smart string normalization (ignores remastered/deluxe/anniversary editions, year suffixes, special characters).
  • Configurable filters — ignore bootlegs, singles, compilations, live albums, remixes, soundtracks per artist or globally.
  • MusicBrainz caching — 24-hour TTL cache to minimize API calls and respect rate limits (1 req/sec).
  • Telegram notifications — daily summary messages with links to the web UI.
  • Web dashboard — browse missing albums, ignore releases, manage artist-specific settings.
  • Single binary deployment — all HTML templates embedded via //go:embed.
  • Docker support — ready for docker compose deployment.

Technology Stack

  • Language: Go 1.25+
  • Database: SQLite 3
  • HTTP Server: Go standard library (net/http + html/template)
  • Key dependencies:
    • github.com/mattn/go-sqlite3 — SQLite driver
    • github.com/lithammer/fuzzysearch — fuzzy string matching
    • gopkg.in/yaml.v3 — configuration parsing

Quick Start

  1. Create a service user in Navidrome:

    • Go to Settings → Users → Add User
    • Set a username and password
    • Ensure the user has access to your music libraries
  2. Configure NaviWatcher:

    cp config.yaml.example config.yaml
    # Edit config.yaml with your settings
    
  3. Build and run:

    go build -o naviwatcher
    ./naviwatcher
    
  4. Or use Docker:

    docker compose up -d
    
  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.

Configuration

server:
  host: "0.0.0.0"
  port: 8080
  username: "admin"
  password: "CHANGE_ME"

navidrome:
  url: "http://localhost:4533"
  user: "watcher_service"
  password: "CHANGE_ME"

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 * * *"

scanner:
  fuzzy_threshold: 0.85
  ignore_bootlegs: true
  include_compilations: true

See docs/Specification.md for the full configuration reference and architecture details.

Architecture

Module Purpose
Navidrome Client Subsonic API v1.16.1 communication (token-based auth)
MusicBrainz Provider Discography fetching with rate limiting and caching
Scanner Engine String normalization and fuzzy comparison
Database Layer SQLite persistence: artist_settings, local_albums (Navidrome sync), external_releases (MusicBrainz cache), notifications_sent
Notifier Scheduled Telegram notifications
Web UI Dashboard for browsing and managing missing releases

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.

License

WTFPL — Do What The Fuck You Want To Public License


Русский

NaviWatcher — это автономный сервис-демон для мониторинга музыкальной коллекции в Navidrome. Он сравнивает вашу медиатеку с полными дискографиями артистов из MusicBrainz и уведомляет об отсутствующих релизах через Telegram и веб-интерфейс.

Как это работает

  1. Сканирует библиотеку Navidrome через Subsonic API — получает список артистов и альбомов.
  2. Загружает полные дискографии артистов из MusicBrainz (использует Release Groups, чтобы избежать дубликатов изданий).
  3. Сравнивает локальную коллекцию с внешними данными через нечёткое сравнение строк (настраиваемый порог, по умолчанию 0.85).
  4. Уведомляет об отсутствующих альбомах/синглах/EP через ежедневные дайджесты в Telegram и веб-панель.

Возможности

  • Совместим с Subsonic API — работает с Navidrome, Airsonic, Ampache и другими Subsonic-совместимыми серверами.
  • Нечёткое сравнение — умная нормализация строк (игнорирует ремастеры, deluxe/anniversary-издания, год в скобках, спецсимволы).
  • Гибкие фильтры — игнорирование бутлегов, синглов, компиляций, лайвов, ремиксаундов — глобально или для конкретного артиста.
  • Кэширование MusicBrainz — TTL 24 часа для минимизации запросов и соблюдения лимитов (1 запрос/сек).
  • Уведомления в Telegram — ежедневные сводки со ссылками на веб-интерфейс.
  • Веб-панель — просмотр отсутствующих альбомов, игнорирование релизов, управление настройками артистов.
  • Один бинарный файл — все HTML-шаблоны встроены через //go:embed.
  • Поддержка Docker — готов к развёртыванию через docker compose.

Технологический стек

  • Язык: Go 1.21+
  • База данных: SQLite 3
  • HTTP-сервер: стандартная библиотека Go (net/http + html/template)
  • Ключевые зависимости:
    • github.com/mattn/go-sqlite3 — драйвер SQLite
    • github.com/lithammer/fuzzysearch — нечёткое сравнение строк
    • gopkg.in/yaml.v3 — парсинг конфигурации

Быстрый старт

  1. Создайте сервисного пользователя в Navidrome:

    • Перейдите в Settings → Users → Add User
    • Задайте имя пользователя и пароль
    • Убедитесь, что у пользователя есть доступ к медиатекам
  2. Настройте NaviWatcher:

    cp config.yaml.example config.yaml
    # Отредактируйте config.yaml
    
  3. Соберите и запустите:

    go build -o naviwatcher
    ./naviwatcher
    
  4. Или используйте Docker:

    docker compose up -d
    
  5. Откройте веб-интерфейс по адресу http://localhost:8080

Конфигурация

server:
  host: "0.0.0.0"
  port: 8080
  username: "admin"
  password: "CHANGE_ME"

navidrome:
  url: "http://localhost:4533"
  user: "watcher_service"
  password: "CHANGE_ME"

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 * * *"

scanner:
  fuzzy_threshold: 0.85
  ignore_bootlegs: true
  include_compilations: true

Полную справку по конфигурации и архитектуру см. в docs/Specification.md.

Архитектура

Модуль Назначение
Navidrome Client Взаимодействие с Subsonic API v1.16.1 (токенная аутентификация)
MusicBrainz Provider Загрузка дискографий с кэшированием и rate limiting
Scanner Engine Нормализация строк и нечёткое сравнение
Database Layer SQLite: artist_settings, local_albums (синхронизация из Navidrome), external_releases (кэш MusicBrainz), notifications_sent
Notifier Планировщик уведомлений в Telegram
Web UI Панель управления отсутствющими релизами

Статус реализации

  • Scanner Engine — реализован (только вычисления). Ядро поиска отсутствующих релизов готово: нормализация строк в internal/normalize, оценка схожести и движок сравнения (FindMissingReleases, ScanArtist, ScanAll) в internal/scanner. Используется настраиваемый scanner.fuzzy_threshold (по умолчанию 0.85), игнорируются варианты (Remastered)/год/спецсимволы, пропускаются отмеченные как игнорируемые.
  • Notifier и Web UI — пока не реализованы (вне рамок плана сканера). main.run() выполняет только вычислительное сканирование и логирует количество отсутствующих релизов; результаты не сохраняются и уведомления не отправляются.

Лицензия

WTFPL — Do What The Fuck You Want To Public License

Description
No description provided
Readme 388 KiB
Languages
Go 98%
HTML 1.8%
Dockerfile 0.2%