fix: address code review findings
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.
This commit is contained in:
@@ -3,6 +3,7 @@ package musicbrainz
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
@@ -12,41 +13,17 @@ import (
|
||||
|
||||
// excludedStatuses contains release-group statuses that should be filtered out.
|
||||
var excludedStatuses = map[string]bool{
|
||||
"Bootleg": true,
|
||||
"Promotion": true,
|
||||
"Pseudo-Release": true,
|
||||
"Bootleg": true,
|
||||
"Promotion": true,
|
||||
"Pseudo-Release": true,
|
||||
}
|
||||
|
||||
// includedTypes contains release-group types that should be included.
|
||||
var includedTypes = map[string]bool{
|
||||
"Album": true,
|
||||
"Single": true,
|
||||
"EP": true,
|
||||
"Compilation": true,
|
||||
}
|
||||
|
||||
// ArtistTypeFilter holds per-artist type filtering preferences.
|
||||
// These are placeholders for Web UI integration where users can
|
||||
// toggle which release types to monitor per artist.
|
||||
type ArtistTypeFilter struct {
|
||||
// ArtistID is the MusicBrainz artist ID.
|
||||
ArtistID string
|
||||
// IncludeSingles whether to include Single-type release groups.
|
||||
IncludeSingles bool
|
||||
// IncludeCompilations whether to include Compilation-type release groups.
|
||||
IncludeCompilations bool
|
||||
// IncludeEP whether to include EP-type release groups.
|
||||
IncludeEP bool
|
||||
}
|
||||
|
||||
// DefaultArtistTypeFilter returns an ArtistTypeFilter with all types enabled.
|
||||
func DefaultArtistTypeFilter(artistID string) *ArtistTypeFilter {
|
||||
return &ArtistTypeFilter{
|
||||
ArtistID: artistID,
|
||||
IncludeSingles: true,
|
||||
IncludeCompilations: true,
|
||||
IncludeEP: true,
|
||||
}
|
||||
"Album": true,
|
||||
"Single": true,
|
||||
"EP": true,
|
||||
"Compilation": true,
|
||||
}
|
||||
|
||||
// GetArtistReleaseGroups fetches all release groups for a given artist from MusicBrainz.
|
||||
@@ -61,7 +38,12 @@ func (c *MusicBrainzClient) GetArtistReleaseGroups(ctx context.Context, artistMB
|
||||
limit := 100 // MusicBrainz max limit per request
|
||||
|
||||
for {
|
||||
path := fmt.Sprintf("/release-group?artist=%s&limit=%d&offset=%d", artistMBID, limit, offset)
|
||||
params := url.Values{}
|
||||
params.Set("artist", artistMBID)
|
||||
params.Set("limit", fmt.Sprintf("%d", limit))
|
||||
params.Set("offset", fmt.Sprintf("%d", offset))
|
||||
path := buildPath("/release-group", params)
|
||||
|
||||
body, err := c.doGet(ctx, path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetch release groups for artist %s: %w", artistMBID, err)
|
||||
@@ -101,22 +83,6 @@ func FilterReleaseGroups(groups []ReleaseGroup) []ReleaseGroup {
|
||||
return filtered
|
||||
}
|
||||
|
||||
// FilterReleaseGroupsWithArtistFilter applies status filtering, base type filtering,
|
||||
// and per-artist type filtering preferences.
|
||||
func FilterReleaseGroupsWithArtistFilter(groups []ReleaseGroup, artistFilter *ArtistTypeFilter) []ReleaseGroup {
|
||||
var filtered []ReleaseGroup
|
||||
for _, rg := range groups {
|
||||
if IsStatusExcluded(rg.Status) {
|
||||
continue
|
||||
}
|
||||
if !IsTypeIncludedForArtist(rg.Type, artistFilter) {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, rg)
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// IsStatusExcluded returns true if the given status should be excluded.
|
||||
func IsStatusExcluded(status string) bool {
|
||||
return excludedStatuses[status]
|
||||
@@ -127,34 +93,13 @@ func IsTypeIncluded(releaseType string) bool {
|
||||
return includedTypes[releaseType]
|
||||
}
|
||||
|
||||
// IsTypeIncludedForArtist checks whether a release type should be included
|
||||
// based on per-artist type filtering preferences.
|
||||
func IsTypeIncludedForArtist(releaseType string, filter *ArtistTypeFilter) bool {
|
||||
if filter == nil {
|
||||
return IsTypeIncluded(releaseType)
|
||||
}
|
||||
|
||||
switch releaseType {
|
||||
case "Album":
|
||||
return true // Albums are always included
|
||||
case "Single":
|
||||
return filter.IncludeSingles
|
||||
case "EP":
|
||||
return filter.IncludeEP
|
||||
case "Compilation":
|
||||
return filter.IncludeCompilations
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// NormalizeString normalizes a string for fuzzy matching by:
|
||||
// - Converting to lowercase
|
||||
// - Removing special characters (keeping only letters, digits, and spaces)
|
||||
// - Removing years (4-digit numbers that look like years)
|
||||
// - Removing bracketed keywords (e.g., [Deluxe], [Remastered])
|
||||
// - Collapsing multiple spaces into one
|
||||
// - Trimming leading/trailing whitespace
|
||||
// - Converting to lowercase
|
||||
// - Removing special characters (keeping only letters, digits, and spaces)
|
||||
// - Removing years (4-digit numbers that look like years)
|
||||
// - Removing bracketed keywords (e.g., [Deluxe], [Remastered])
|
||||
// - Collapsing multiple spaces into one
|
||||
// - Trimming leading/trailing whitespace
|
||||
func NormalizeString(s string) string {
|
||||
// Convert to lowercase
|
||||
s = strings.ToLower(s)
|
||||
@@ -212,7 +157,7 @@ func NormalizeArtistName(name string) string {
|
||||
}
|
||||
|
||||
// ToExternalRelease converts a ReleaseGroup to an ExternalRelease
|
||||
// with the current timestamp as CachedAt.
|
||||
// for database persistence.
|
||||
func (rg *ReleaseGroup) ToExternalRelease() *database.ExternalRelease {
|
||||
return &database.ExternalRelease{
|
||||
RGID: rg.ID,
|
||||
|
||||
Reference in New Issue
Block a user