Initial commit
This commit is contained in:
170
CLAUDE.md
Normal file
170
CLAUDE.md
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Development Commands
|
||||||
|
|
||||||
|
### Building
|
||||||
|
```bash
|
||||||
|
# Build the application
|
||||||
|
go build -o naviwatcher
|
||||||
|
|
||||||
|
# Build with race detector
|
||||||
|
go build -race -o naviwatcher
|
||||||
|
|
||||||
|
# Cross-platform build examples
|
||||||
|
GOOS=linux GOARCH=amd64 go build -o naviwatcher-linux
|
||||||
|
GOOS=darwin GOARCH=amd64 go build -o naviwatcher-mac
|
||||||
|
GOOS=windows GOARCH=amd64 go build -o naviwatcher.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running
|
||||||
|
```bash
|
||||||
|
# Run the application
|
||||||
|
./naviwatcher
|
||||||
|
|
||||||
|
# Run with specific config file
|
||||||
|
./naviwatcher -config=/path/to/config.yaml
|
||||||
|
|
||||||
|
# Run in development mode (if implemented)
|
||||||
|
go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
```bash
|
||||||
|
# Run all tests
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
# Run tests with coverage
|
||||||
|
go test ./... -cover
|
||||||
|
|
||||||
|
# Run a specific test package
|
||||||
|
go test ./internal/scanner
|
||||||
|
|
||||||
|
# Run tests verbose
|
||||||
|
go test ./... -v
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dependency Management
|
||||||
|
```bash
|
||||||
|
# Add a new dependency
|
||||||
|
go get github.com/example/package@v1.2.3
|
||||||
|
|
||||||
|
# Update dependencies
|
||||||
|
go get -u ./...
|
||||||
|
|
||||||
|
# Tidy up dependencies
|
||||||
|
go mod tidy
|
||||||
|
|
||||||
|
# Vendor dependencies (if needed)
|
||||||
|
go mod vendor
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linting and Formatting
|
||||||
|
```bash
|
||||||
|
# Format code
|
||||||
|
go fmt ./...
|
||||||
|
|
||||||
|
# Vet for potential issues
|
||||||
|
go vet ./...
|
||||||
|
|
||||||
|
# Static analysis (if golangci-lint is installed)
|
||||||
|
golangci-lint run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
```bash
|
||||||
|
# Build Docker image
|
||||||
|
docker build -t naviwatcher .
|
||||||
|
|
||||||
|
# Run with docker compose
|
||||||
|
docker compose up
|
||||||
|
|
||||||
|
# Run in background
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Architecture
|
||||||
|
|
||||||
|
Based on the specification (docs/Specification.md), the application follows a modular architecture:
|
||||||
|
|
||||||
|
### Core Modules
|
||||||
|
1. **Navidrome Client** (`internal/navidrome/` or similar)
|
||||||
|
- Handles Subsonic API communication
|
||||||
|
- Fetches artist and album data from Navidrome instance
|
||||||
|
- Implements authentication and error handling
|
||||||
|
|
||||||
|
2. **MusicBrainz Provider** (`internal/musicbrainz/` or similar)
|
||||||
|
- Interfaces with MusicBrainz API
|
||||||
|
- Implements rate limiting (1 request/second)
|
||||||
|
- Manages caching of artist discographies (24-hour TTL)
|
||||||
|
- Works with Release Group (RG) entities to minimize duplicates
|
||||||
|
|
||||||
|
3. **Scanner Engine** (`internal/scanner/` or similar)
|
||||||
|
- Normalizes string comparisons for fuzzy matching
|
||||||
|
- Implements the comparison algorithm (0.85 similarity threshold)
|
||||||
|
- Handles removal of special characters, years, and bracketed keywords
|
||||||
|
- Compares local albums vs. external discographies
|
||||||
|
|
||||||
|
4. **Database Layer** (`internal/database/` or similar)
|
||||||
|
- SQLite 3 integration via github.com/mattn/go-sqlite3
|
||||||
|
- Manages schema migrations
|
||||||
|
- Handles tables:
|
||||||
|
- `artist_settings`: Artist monitoring configuration
|
||||||
|
- `external_releases`: Cached MusicBrainz data
|
||||||
|
- `notifications_sent`: Sent notification tracking
|
||||||
|
|
||||||
|
5. **Notifier** (`internal/notifier/` or similar)
|
||||||
|
- Telegram bot integration
|
||||||
|
- Scheduled task execution (cron-based)
|
||||||
|
- Batch notification sending
|
||||||
|
- Message formatting with Web UI links
|
||||||
|
|
||||||
|
6. **Web UI** (`internal/web/` or similar)
|
||||||
|
- Go standard library net/http + html/template
|
||||||
|
- Embedded templates using //go:embed
|
||||||
|
- Basic authentication protection
|
||||||
|
- Dashboard for viewing missing albums
|
||||||
|
- Artist detail views with ignore functionality
|
||||||
|
- Archive view for previously ignored releases
|
||||||
|
|
||||||
|
### Key Technical Requirements
|
||||||
|
- **Concurrency**: Separate goroutines for external API calls to keep web UI responsive
|
||||||
|
- **Rate Limiting**: Strict adherence to MusicBrainz 1 request/second limit
|
||||||
|
- **Graceful Shutdown**: Proper SIGTERM handling to close database connections
|
||||||
|
- **Configuration**: YAML-based config (config.yaml) with environment-specific overrides
|
||||||
|
- **Static Assets**: HTML templates embedded via //go:embed for single-binary deployment
|
||||||
|
- **Data Modeling**: Focus on Release Group entities rather than specific releases
|
||||||
|
|
||||||
|
### Common Development Patterns
|
||||||
|
- Use context.Context for cancellation and timeouts
|
||||||
|
- Implement proper error handling with logging
|
||||||
|
- Follow Go idioms and conventions
|
||||||
|
- Write table-driven tests for complex logic
|
||||||
|
- Use dependency injection for testability
|
||||||
|
- Apply the specified fuzzy matching algorithm consistently
|
||||||
|
|
||||||
|
## Configuration Reference
|
||||||
|
See docs/Specification.md Section 7 for full config.yaml structure including:
|
||||||
|
- Server settings (host, port, basic auth)
|
||||||
|
- Navidrome connection details
|
||||||
|
- MusicBrainz API configuration
|
||||||
|
- Telegram notification settings
|
||||||
|
- Scanner parameters (fuzzy threshold, filters)
|
||||||
|
|
||||||
|
## Database Schema
|
||||||
|
See docs/Specification.md Section 5 for complete SQLite schema including:
|
||||||
|
- artist_settings table columns and defaults
|
||||||
|
- external_releases table structure
|
||||||
|
- notifications_sent table for tracking
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
1. Copy config.yaml.example to config.yaml and fill in your values
|
||||||
|
2. Ensure Navidrome instance is running and accessible
|
||||||
|
3. Set up Telegram bot and obtain token/chat ID
|
||||||
|
4. Initialize database: ./naviwatcher (will create tables on first run)
|
||||||
|
5. Start the service: ./naviwatcher
|
||||||
|
6. Access Web UI at http://localhost:8080 (or configured host/port)
|
||||||
13
License.md
Normal file
13
License.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
Version 2, May 2026
|
||||||
|
|
||||||
|
Copyright (C) 2026 Zagainov Vladimir <mrixs@mrixs.me>
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
copies of this license document, and changing it is allowed as long
|
||||||
|
as the name is changed.
|
||||||
|
|
||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
217
README.md
Normal file
217
README.md
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
# NaviWatcher
|
||||||
|
|
||||||
|
> **Language:** [English](#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.21+
|
||||||
|
- **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:**
|
||||||
|
```bash
|
||||||
|
cp config.yaml.example config.yaml
|
||||||
|
# Edit config.yaml with your settings
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Build and run:**
|
||||||
|
```bash
|
||||||
|
go build -o naviwatcher
|
||||||
|
./naviwatcher
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Or use Docker:**
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Access the web UI** at `http://localhost:8080`
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
server:
|
||||||
|
host: "0.0.0.0"
|
||||||
|
port: 8080
|
||||||
|
username: "admin"
|
||||||
|
password: "password123"
|
||||||
|
|
||||||
|
navidrome:
|
||||||
|
url: "http://localhost:4533"
|
||||||
|
user: "watcher_service"
|
||||||
|
password: "user_password"
|
||||||
|
|
||||||
|
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](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 for settings, cache, and state |
|
||||||
|
| **Notifier** | Scheduled Telegram notifications |
|
||||||
|
| **Web UI** | Dashboard for browsing and managing missing releases |
|
||||||
|
|
||||||
|
### License
|
||||||
|
|
||||||
|
[WTFPL](License.md) — 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:**
|
||||||
|
```bash
|
||||||
|
cp config.yaml.example config.yaml
|
||||||
|
# Отредактируйте config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Соберите и запустите:**
|
||||||
|
```bash
|
||||||
|
go build -o naviwatcher
|
||||||
|
./naviwatcher
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Или используйте Docker:**
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Откройте веб-интерфейс** по адресу `http://localhost:8080`
|
||||||
|
|
||||||
|
### Конфигурация
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
server:
|
||||||
|
host: "0.0.0.0"
|
||||||
|
port: 8080
|
||||||
|
username: "admin"
|
||||||
|
password: "password123"
|
||||||
|
|
||||||
|
navidrome:
|
||||||
|
url: "http://localhost:4533"
|
||||||
|
user: "watcher_service"
|
||||||
|
password: "user_password"
|
||||||
|
|
||||||
|
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](docs/Specification.md).
|
||||||
|
|
||||||
|
### Архитектура
|
||||||
|
|
||||||
|
| Модуль | Назначение |
|
||||||
|
|--------|------------|
|
||||||
|
| **Navidrome Client** | Взаимодействие с Subsonic API v1.16.1 (токенная аутентификация) |
|
||||||
|
| **MusicBrainz Provider** | Загрузка дискографий с кэшированием и rate limiting |
|
||||||
|
| **Scanner Engine** | Нормализация строк и нечёткое сравнение |
|
||||||
|
| **Database Layer** | Хранение настроек, кэша и состояния в SQLite |
|
||||||
|
| **Notifier** | Планировщик уведомлений в Telegram |
|
||||||
|
| **Web UI** | Панель управления отсутствющими релизами |
|
||||||
|
|
||||||
|
### Лицензия
|
||||||
|
|
||||||
|
[WTFPL](License.md) — Do What The Fuck You Want To Public License
|
||||||
150
docs/Specification.md
Normal file
150
docs/Specification.md
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
# ЦЕЛЬ ПРОЕКТА
|
||||||
|
|
||||||
|
## 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:**
|
||||||
|
1. В веб-интерфейсе Navidrome: Settings → Users → Add User.
|
||||||
|
2. Задать username и надёжный пароль.
|
||||||
|
3. Убедиться, что роль имеет доступ к нужным медиатекам.
|
||||||
|
4. Указать эти данные в `config.yaml` (поля `user` и `password`).
|
||||||
|
|
||||||
|
|
||||||
|
## 3. Архитектура системы
|
||||||
|
|
||||||
|
Проект разделен на логические модули:
|
||||||
|
1. **Navidrome Client:** Взаимодействие с Subsonic API v1.16.1 (чтение артистов и локальных альбомов). Аутентификация — токенная (md5), пароль не передаётся в открытом виде. Используются эндпоинты: `getArtists`, `getArtist`, `getAlbum`, `ping`.
|
||||||
|
2. **MusicBrainz Provider:** Запрос дискографий с механизмом кэширования и соблюдением Rate Limit (1 запрос в секунду).
|
||||||
|
3. **Scanner Engine:** Логика нормализации строк и сравнения списков (Diff).
|
||||||
|
4. **Database Layer:** Персистентное хранение данных и пользовательских фильтров.
|
||||||
|
5. **Notifier:** Планировщик задач и отправка уведомлений в Telegram.
|
||||||
|
6. **Web UI:** Интерфейс для просмотра находок и управления исключениями.
|
||||||
|
|
||||||
|
|
||||||
|
## 4. Алгоритмы и бизнес-логика
|
||||||
|
|
||||||
|
### 4.1. Работа с внешними данными (MusicBrainz)
|
||||||
|
Для минимизации дубликатов (разные издания одного альбома) сервис должен работать с сущностью **Release Group (RG)**, а не с конкретными релизами.
|
||||||
|
* **Кэширование:** Данные о дискографии артиста сохраняются в SQLite на 24 часа. Повторные запросы в этот период идут только в БД.
|
||||||
|
|
||||||
|
### 4.2. Фильтрация контента
|
||||||
|
При получении списка Release Groups из MusicBrainz применяется многоуровневый фильтр:
|
||||||
|
1. **По статусу:** Игнорировать `Bootleg`, `Promotion`, `Pseudo-Release`.
|
||||||
|
2. **По типу (глобально):** Разрешены `Album`, `Single`, `EP`, `Compilation`.
|
||||||
|
3. **По типу (персонально для артиста):** Возможность отключить `Single` или `Compilation` для конкретного исполнителя через Web UI.
|
||||||
|
4. **По вторичным признакам:** Опциональное игнорирование `Live`, `Remix`, `Soundtrack`.
|
||||||
|
|
||||||
|
### 4.3. Сравнение (Fuzzy Matching)
|
||||||
|
Чтобы избежать ложных срабатываний (например, «The Wall» vs «The Wall (Remastered)»), используется алгоритм:
|
||||||
|
1. **Нормализация:** Приведение к нижнему регистру, удаление спецсимволов, удаление года (20xx), удаление ключевых слов в скобках (Deluxe, Anniversary, Expanded).
|
||||||
|
2. **Сравнение:** Если коэффициент сходства строк выше 0.85 (настраиваемо), альбом считается «уже имеющимся».
|
||||||
|
|
||||||
|
### 4.4. Жизненный цикл уведомлений
|
||||||
|
1. Найден новый релиз -> Проверка, нет ли его в таблице `ignored_releases` или `notifications_sent`.
|
||||||
|
2. Если нет -> Добавление в очередь на отправку.
|
||||||
|
3. Раз в сутки -> Отправка сводного сообщения в Telegram -> Пометка в `notifications_sent`.
|
||||||
|
|
||||||
|
|
||||||
|
## 5. Модель данных (SQLite)
|
||||||
|
|
||||||
|
### Таблица `artist_settings`
|
||||||
|
Хранит параметры мониторинга для каждого артиста из Navidrome.
|
||||||
|
* `id`: string (MBID или имя)
|
||||||
|
* `name`: string
|
||||||
|
* `ignore_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`: string
|
||||||
|
* `type`: string (album/single/ep)
|
||||||
|
* `release_date`: string
|
||||||
|
* `is_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)
|
||||||
|
|
||||||
|
```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. Программные требования и ограничения
|
||||||
|
1. **Rate Limiting:** MusicBrainz API позволяет делать не более 1 запроса в секунду. Демон должен строго соблюдать этот интервал.
|
||||||
|
2. **Concurrency:** Запросы к Navidrome и MusicBrainz должны выполняться в отдельных горутинах, чтобы веб-интерфейс оставался отзывчивым.
|
||||||
|
3. **Graceful Shutdown:** При получении сигнала завершения (SIGTERM) демон должен корректно закрыть соединение с SQLite.
|
||||||
|
4. **Static Files:** Все HTML-шаблоны должны быть встроены в бинарный файл (использование `//go:embed`).
|
||||||
|
5. **Docker:** Проект должен быть запущен через `docker compose`.
|
||||||
Reference in New Issue
Block a user