Files
NaviWatcher/CLAUDE.md

185 lines
6.1 KiB
Markdown

# 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
- Applies per-artist ignore_singles/ignore_compilations filters at scan time for immediate responsiveness to setting changes
Shared normalization lives in `internal/normalize` (`NormalizeString`, `NormalizeArtistName`) — this is the single source of truth for string normalization, reused by both `internal/musicbrainz` and `internal/scanner`. Do NOT add local copies of normalization logic elsewhere.
4. **Database Layer** (`internal/database/` or similar)
- SQLite 3 integration via github.com/mattn/go-sqlite3
- Subsonic API client via github.com/delucks/go-subsonic
- Manages schema migrations
- Handles tables:
- `artist_settings`: Artist monitoring configuration
- `local_albums`: Navidrome albums synced via Subsonic API (id, artist_id, title)
- `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
- Centralize shared logic: Place reusable filtering, validation, or utility functions in dedicated files (e.g., internal/musicbrainz/filter.go) and import them across packages to ensure consistent behavior across cache-hit, cache-miss, and real-time paths
### Execution Flow
The application follows a sequential data pipeline:
1. Sync artists from Navidrome (populate artist_settings)
2. Sync discographies from MusicBrainz (populate external_releases with filtering)
3. Sync albums from Navidrome (populate local_albums)
4. Scan for missing releases using fuzzy matching (produces MissingRelease results)
This flow is implemented in the `syncAndScan()` function in `cmd/naviwatcher/main.go`, which is called by the periodic sync loop and on startup.
## 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)