This commit includes:
1. Live/Remix Filtering Feature:
- Added ignore_live and ignore_remix columns to artist_settings table (migration 010)
- Updated ArtistSettings struct with IgnoreLive and IgnoreRemix fields
- Modified SaveArtistSettings and UpdateArtistSettings to handle new fields
- Extended FilterOptions struct with IgnoreLive and IgnoreRemix
- Updated ApplyTypeToggles and ApplyTypeTogglesToReleaseGroups to filter Live/Remix types
- Added toggleIgnoreLive and toggleIgnoreRemix handlers in web layer
- Updated ArtistData view model and artist.html template with new toggle UI
- Comprehensive test coverage for all new functionality
2. CI/CD Pipeline with Gitea Actions:
- Added .gitea/workflows/docker-build.yml for automated Docker builds
- Workflow triggers on pushes to main/master and tags, plus PRs
- Runs Go tests before building
- Builds and pushes multi-architecture Docker images to gitea.mrixs.me
- Includes caching for faster subsequent builds
- Proper tagging strategy (branch, semver, SHA)
- CI-CD-GUIDE.md documentation
3. Cleanup:
- Removed temporary build artifacts and coverage files
- Fix pagination to not trust MusicBrainz count for cutoff (prevents
silent discography truncation / hidden missing releases)
- Add idx_external_releases_artist_id index for hot per-artist queries
- Dedupe cached_at layout constant in external_releases.go
- 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.
- Fix FK constraint violation in SyncArtistDiscography: delete
notifications_sent rows before external_releases to prevent
constraint failure when re-syncing artists with prior notifications.
- Implement per-artist type filtering: FilterReleaseGroups now accepts
FilterOptions with IgnoreSingles/IgnoreCompilations flags, read from
artist_settings table via getArtistFilterOptions.
- Fix inconsistent error wrapping: GetExternalRelease now wraps errors
with fmt.Errorf like all other functions in the package; updated test
to use errors.Is for sql.ErrNoRows check.
- Add tests: FilterReleaseGroups ignore singles/compilations,
SyncArtistDiscography per-artist type filtering, and FK-safe resync.
- Prevent infinite pagination loop when API returns empty release groups page
- Move NormalizeString regexes to package level to avoid recompilation on every call
- Use UTC consistently for cached_at timestamps to avoid DST-related TTL skew
- Add context cancellation check between pagination pages in GetArtistReleaseGroups
for responsive graceful shutdown during large discography fetches.
- Eliminate double DB query on cache hit by having GetCachedReleases return
[]ExternalRelease directly instead of just a count, avoiding a redundant
second query in SyncArtistDiscography.
- Update cache_test.go to match new GetCachedReleases return type.
- Format main.go (pre-existing whitespace issue).
- Add CacheTTL default (24h) in config applyDefaults — without this,
omitting cache_ttl from config silently defeats the entire caching
mechanism (TTL=0 means cached data is never served)
- Fix pagination to use total Count instead of checking if last page
was short — avoids wasting a rate-limit token when total count is
an exact multiple of 100
- Preserve user-set IsIgnored flags across re-syncs — previously,
DELETE+INSERT in the sync transaction reset all ignore flags to
false, losing user preferences on every cache-expiry re-sync
- Check context cancellation on cache-hit code path — previously,
ctx.Err() was not checked between cache check and returning cached
data, violating the cancellation contract
Remove dead code: duplicate ExternalRelease/Artist/ParsedArtist structs in
model.go, ParseArtist/mbArtist/mbArtistData in client.go, ArtistTypeFilter
and related filtering functions in api.go, SyncArtistDiscographyWithFilter
in sync.go, and CacheStats/IsArtistCacheValid in cache.go.
Fix bugs: SaveExternalRelease now stores NULL instead of empty string for
zero CachedAt; sync upserts are now transactional with stale release cleanup;
getCachedReleases returns int instead of *CacheStats; doGet uses url.Values
for proper query encoding of MBID.
Fix tests: removed duplicate TestRun_GracefulShutdown, removed dead code
(_ = dbPath) from TestNewApp, fixed assertions in httptest handler goroutine
to avoid data race, increased rate limiter timing tolerance, removed
Client.Close() calls (no-op removed), fixed sync test cache expiry to use
UPDATE instead of 0 TTL races.
Fix formatting: cancel()}() formatting in main.go, error format string in sync.go.
- 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