Commit Graph

46 Commits

Author SHA1 Message Date
4d93218132 feat: complete Task 4 - verify and document live/remix filtering implementation 2026-08-05 17:23:17 +03:00
0dcc86015d feat: complete Task 3 - update web UI and handlers for Live/Remix filtering 2026-08-05 17:19:31 +03:00
a3b8aa8a74 feat: complete Task 2 - update filtering core logic for live/remix filtering 2026-08-05 17:08:31 +03:00
75326cda3a feat: complete Task 1 - update database schema and model for live/remix filtering 2026-08-05 17:03:40 +03:00
f49ad40002 move completed plan: 2026-07-27-verify-scanner-wiring.md 2026-07-30 20:19:55 +03:00
c2f3258186 feat: document and enhance scanner wiring verification - added filter flow documentation and code comments 2026-07-30 19:36:11 +03:00
827b5405e5 feat: verify wiring of ignore_singles/ignore_compilations toggles - completed Task 1 2026-07-30 18:33:10 +03:00
35b1f466ec feat: write unit tests for filtering logic 2026-07-27 23:21:50 +03:00
348d0648ba move completed plan: 2026-07-26-fix-scanner-wiring.md 2026-07-27 10:33:35 +03:00
e95f581c80 move completed plan: 2026-07-21-fix-review-findings.md 2026-07-26 13:06:04 +03:00
6e49f436c3 feat: update documentation to note filter centralization pattern completion 2026-07-26 13:05:14 +03:00
aec9d89435 feat: complete filter centralization pattern and document in CLAUDE.md 2026-07-26 13:03:37 +03:00
f697ebf2f5 feat: fix NotifyOnce map key to use composite ArtistID+RGID 2026-07-26 12:37:57 +03:00
395a7f9b07 feat: handle ErrArtistNotFound in ScanArtist gracefully by using empty TypeFilter 2026-07-26 11:36:32 +03:00
4b4e852fd1 feat: complete task 6 - fix stale notification pruning to avoid SQLite parameter limit 2026-07-26 11:24:40 +03:00
355a12c6a6 feat: complete task 4 - fix scanner diff.go TypeFilter.suppressed to use centralized filter 2026-07-25 23:12:09 +03:00
41e5e079d4 feat: complete task 3 - fix sync.go cache-hit path to use centralized filter 2026-07-25 23:06:56 +03:00
9e4d2385ff feat: complete task 2 - update FilterReleaseGroups to use centralized filter helper 2026-07-25 23:01:54 +03:00
389d177d85 fix: address code review findings
- Fix duplicate Telegram notifications: SyncArtistDiscography no longer wipes
  notifications_sent for the whole artist on every cache-miss re-sync; only
  markers for releases that disappear are pruned (FK-safe via INSERT OR REPLACE
  + rgid NOT IN (...)).
- Cache empty MusicBrainz discographies via a new artist_settings.last_synced
  column (migration 009) so zero-release artists honor the TTL instead of being
  re-fetched every cycle.
- Wire the Web UI server and Telegram notifier scheduler into main.run/NewApp.
- Guard startPeriodicSync against overlapping syncs with a done-channel slot.
- Add server.public_url config; NewServerWithConfig derives reachable links
  and no longer advertises the 0.0.0.0 bind address.
- Web handlers: use scanner.ScanArtist per artist, drop always-false
  releaseIgnored lookup and dead endsWith, thread configured threshold.
- Limit :memory: DB pool to one connection so migrations and queries share the
  same in-memory store.
2026-07-19 23:38:33 +03:00
e493a4d228 feat: document wired sync pipeline, notifier, and web UI in README 2026-07-19 22:50:39 +03:00
1a644dede7 feat: verify acceptance criteria and document sync config
Add a scan->notify->web end-to-end smoke test against a seeded :memory: DB,
fix .gitignore so the cmd/naviwatcher source dir is not excluded by the
root binary rule, and document the new sync.interval field in config.yaml.example.
2026-07-19 22:48:31 +03:00
44f3b0a2a7 feat: add Web UI artist detail, archive, and ignore actions
Implements Task 8: artist detail page (local albums + found-missing
with ignore buttons), ignored-releases archive with restore, and POST
handlers toggling ignore flags and ignore_singles. Adds ErrArtistNotFound
sentinel so callers can distinguish missing artists, and wires routes via
the enhanced ServeMux path wildcard.
2026-07-19 22:43:47 +03:00
cea20957e7 feat: add Web UI server with basic-auth and artist dashboard 2026-07-19 22:35:28 +03:00
11f838ace9 feat: add notifier scheduler and NotifyOnce sent-tracking 2026-07-19 22:32:57 +03:00
3af33bd728 feat: add notifier Sender interface and digest formatter 2026-07-19 22:28:41 +03:00
0635ca8a87 feat: wire main loop with periodic sync+scan and sync_interval config
Replace compute-only run() with an immediate sync+scan followed by a
ticker-driven periodic loop, add the sync.interval config field (default
6h) with defaults and validation, and add tests for the scheduling logic.
2026-07-19 22:27:06 +03:00
dc4bdcdab0 feat: add SyncAll periodic sync pipeline and wire Navidrome client into App 2026-07-19 22:22:46 +03:00
40c4240693 feat: add MusicBrainz artist-ID resolver
Add ResolveArtistMBID to query the MB artist search endpoint and return the
first matching artist ID, with httptest-backed tests for match, no-match,
HTTP error, and invalid-JSON paths.
2026-07-19 22:16:43 +03:00
85c42ec858 feat: add mbid column to artist_settings
Additive migration 008 adds an mbid column to artist_settings, extend the
ArtistSettings struct and persistence functions (SaveArtistSettings,
GetArtistSettings, GetAllArtistSettings, UpdateArtistSettings) to carry the
MusicBrainz ID, and add round-trip tests covering empty and set MBID values.
2026-07-19 22:13:57 +03:00
35d6a5234f move completed plan: 2026-07-19-scanner-engine-fuzzy-diff.md 2026-07-19 21:59:59 +03:00
06e09220ae feat: document Scanner Engine implementation status and normalize convention 2026-07-19 18:23:13 +03:00
9fc89fd5a4 feat: verify scanner engine acceptance criteria
Confirm normalization + 0.85 threshold, IsIgnored skip, per-artist scoping,
and zero-threshold→default contract. Added TestScanArtist_ZeroThresholdUsesDefault,
fixed gofmt on two test files. Coverage: scanner 90.3%, normalize 100.0%.
2026-07-19 18:21:51 +03:00
c2da8c2095 feat: add DB-backed scanner entrypoint and compute-only main hook
Implements ScanArtist/ScanAll in internal/scanner loading local/external
releases via the database layer with ctx-cancellation checks, plus a
compute-only run() hook that logs missing-release counts. Add table-driven
tests using in-memory SQLite fixtures.
2026-07-19 18:19:57 +03:00
a4f0460664 feat: implement scanner diff engine (missing-release detection)
Add FindMissingReleases and MissingRelease to internal/scanner: skips
IsIgnored external releases, scopes matches per ArtistID, and reports
external releases with no local album above the fuzzy threshold.
2026-07-19 18:16:58 +03:00
92aafaab05 feat: implement scanner similarity scoring 2026-07-19 18:14:49 +03:00
edf9c1d1f8 feat: extract normalization into internal/normalize package 2026-07-19 18:11:22 +03:00
c95c740cd5 feat: add fuzzysearch dependency for scanner engine 2026-07-19 18:09:41 +03:00
34ea84fc77 feat: verify MusicBrainz provider acceptance criteria
- All 6 tasks complete: client/models, rate limiting/caching, API
  endpoints/filtering, sync orchestration, app wiring, verification
- Full test suite passes (all packages green)
- go vet clean, no issues
- Coverage: musicbrainz 92.5%, config 100%, navidrome 79.5%, database 53.1%
- Edge cases verified: HTTP errors, rate limit blocking, context
  cancellation, NULL handling, XML parse errors
2026-05-26 13:17:49 +03:00
da49d12bb6 feat: wire up MusicBrainz provider in application entry point
- Add App struct with Config, DB, and MusicBrainz client dependencies
- NewApp() initializes database and MusicBrainz client from config
- App.Close() cleans up resources (MB client + DB) on shutdown
- App.run() blocks until context cancelled (goroutine-ready for future tasks)
- Config validation already requires musicbrainz.user_agent
- Add 6 tests: graceful shutdown, config integration, NewApp creation,
  shutdown cleanup, app.run shutdown, UserAgent validation
- All tests pass (6/6 in cmd, full suite green)
2026-05-26 13:14:47 +03:00
15b05b57fb feat: implement MusicBrainz sync orchestration with caching and upsert
Creates internal/musicbrainz/sync.go with SyncArtistDiscography function
that orchestrates the full flow: cache check via GetCachedReleases, fetch
from MusicBrainz API on cache miss, filter via FilterReleaseGroups, and
upsert into external_releases via database.SaveExternalRelease. Includes
SyncArtistDiscographyWithFilter variant for per-artist type filtering. All
functions support context cancellation. 14 new tests cover cache hit/miss,
status/type filtering, context cancellation, idempotency, API errors, and
full XML pipeline integration.
2026-05-26 13:04:20 +03:00
e624bb0eaf feat: implement MusicBrainz API endpoints, filtering, and normalization
- Add GetArtistReleaseGroups method with pagination support
- Implement status filtering (exclude Bootleg/Promotion/Pseudo-Release)
- Implement type filtering (include Album/Single/EP/Compilation)
- Add per-artist type filtering hooks (ArtistTypeFilter) for Web UI
- Add NormalizeString and NormalizeArtistName for fuzzy matching prep
- Add ReleaseGroup.ToExternalRelease conversion method
- Write comprehensive table-driven tests for filtering logic
- Write tests for normalization functions (18 cases)
- Write tests for GetArtistReleaseGroups (success, pagination, empty, errors)
- All tests pass (47 total across project), go vet clean
2026-05-26 12:31:07 +03:00
b0f69d3a4f feat: implement rate limiting and caching layer for MusicBrainz provider
- Add golang.org/x/time/rate dependency for token-bucket rate limiting
- Replace custom channel-based rate limiter with rate.NewLimiter(1, 1)
- Add context.Context support to doGet for cancellation
- Add cached_at column to external_releases via migration 005
- Implement cache hit/miss queries with TTL-based filtering
- Add CacheStats type for tracking cached RGIDs
- Update ExternalRelease struct with CachedAt field
- Add rate limiting tests (1 req/sec enforcement, burst behavior)
- Add cache tests (hit, miss, expired, mixed, empty artist)
- Update migration count test for new migration
2026-05-26 12:15:28 +03:00
674daed93b feat: create MusicBrainz client and data models
Add internal/musicbrainz/ package with:
- client.go: MusicBrainzClient struct wrapping net/http.Client with
  channel-based rate limiter (1 req/sec), doGet method with proper
  User-Agent header, and Close for cleanup
- model.go: ReleaseGroup, Artist, ExternalRelease, and Parsed* structs
- XML parsing functions for release-group list and artist responses
- Comprehensive tests: XML parsing (success, empty, malformed),
  client constructor, doGet (success, non-200, unreachable server),
  rate limiter behavior
2026-05-26 11:52:17 +03:00
60ecc3f904 Add MusicBrainz provider plan and ignore coverage output 2026-05-26 11:33:30 +03:00
0065057514 feat: add Navidrome client with Subsonic API sync
Add the Navidrome client module that connects to a Navidrome server via
the Subsonic API, fetches artist and album data, and syncs it into the
local SQLite database.

- Add go-subsonic dependency for Subsonic API communication
- Create internal/navidrome/client.go with NavidromeClient wrapper
  - NewClient constructor with token-based auth
  - Ping health check with HTTP status validation
  - GetArtists fetches all artists via getArtists endpoint
  - GetArtistAlbums fetches albums per artist via getArtist endpoint
- Create internal/navidrome/sync.go with sync orchestration
  - SyncArtists upserts artists into artist_settings table
  - SyncAlbums fetches and stores albums for monitored artists
- Add local_albums table (migration 003) with FK to artist_settings
- Add LocalAlbum CRUD operations in internal/database/local_albums.go
- Full test coverage: 19 tests across client and sync packages
- All tests pass, go vet and go fmt clean
2026-05-21 09:45:27 +03:00
735ff0828e feat: add foundation layer (Go module, config, database, Docker)
Squashed commits from foundation-layer branch:

- Initialize Go module and project skeleton (cmd/naviwatcher/main.go)
- Add configuration management with YAML parsing and validation
- Add database layer with schema migrations (artist_settings, external_releases, notifications_sent)
- Add CRUD operations for artist_settings, external_releases, notifications_sent
- Add Docker setup with multi-stage build and docker-compose
- Verify acceptance criteria (tests, vet, fmt)
- Update README.md with build/run/test instructions
- Fix: filter ignored releases in GetUnnotifiedReleases (spec compliance)
- Fix: add FK constraint on notifications_sent.rgid
- Fix: add config.yaml to .gitignore (security)
- Fix: run Docker container as non-root user
- Fix: pin alpine:3.21 instead of alpine:latest
- Fix: wrap migrations in transactions for atomicity

All 49 tests pass, go vet clean, Docker image builds successfully.
2026-05-20 16:11:11 +03:00