feat: complete task 2 - update FilterReleaseGroups to use centralized filter helper

This commit is contained in:
2026-07-25 23:01:54 +03:00
parent aee0241bb7
commit 9e4d2385ff
4 changed files with 246 additions and 51 deletions

View File

@@ -68,53 +68,12 @@ func (c *MusicBrainzClient) GetArtistReleaseGroups(ctx context.Context, artistMB
return allGroups, nil
}
// FilterOptions holds per-artist type filtering preferences.
type FilterOptions struct {
IgnoreSingles bool
IgnoreCompilations bool
}
// FilterReleaseGroups applies type filtering to a list of release groups.
// It includes only Album/Single/EP primary types, or release groups whose
// secondary type list contains Single/EP/Compilation (e.g. an "Album" that is
// also a "Compilation"). The IgnoreSingles / IgnoreCompilations toggles drop
// release groups classified as such via either primary or secondary type.
// GetArtistReleaseGroups fetches all release groups for a given artist from MusicBrainz.
// It queries the artist's release groups via the MusicBrainz Web Service API,
// parses the XML response, and applies status and type filtering.
//
// Release groups carry no status in ws/2, so there is no status filtering.
func FilterReleaseGroups(groups []ReleaseGroup, opts FilterOptions) []ReleaseGroup {
var filtered []ReleaseGroup
for _, rg := range groups {
if !IsTypeIncluded(rg.Type) && !hasSliceType(rg.SecondaryTypes, "Single", "EP", "Compilation") {
continue
}
if opts.IgnoreSingles && (rg.Type == "Single" || hasSliceType(rg.SecondaryTypes, "Single")) {
continue
}
if opts.IgnoreCompilations && (rg.Type == "Compilation" || hasSliceType(rg.SecondaryTypes, "Compilation")) {
continue
}
filtered = append(filtered, rg)
}
return filtered
}
// hasSliceType reports whether the slice contains any of the wanted values.
func hasSliceType(types []string, wanted ...string) bool {
for _, s := range types {
for _, w := range wanted {
if s == w {
return true
}
}
}
return false
}
// IsTypeIncluded returns true if the given primary type is in the base
// included set (Album/Single/EP).
func IsTypeIncluded(releaseType string) bool {
return includedTypes[releaseType]
}
// The method handles pagination automatically by following offset parameters
// until all release groups are fetched.
// ToExternalRelease converts a ReleaseGroup to an ExternalRelease for database
// persistence. artistID is the canonical artist key from artist_settings (the
@@ -131,8 +90,35 @@ func (rg *ReleaseGroup) ToExternalRelease(artistID string) *database.ExternalRel
RGID: rg.ID,
ArtistID: artistID,
Title: rg.Title,
Type: rg.Type,
ReleaseDate: rg.ReleaseDate,
Type: rg.Type,
SecondaryTypes: rg.SecondaryTypes,
}
}
// FilterReleaseGroups applies type filtering to a list of release groups.
// It includes only Album/Single/EP primary types, or release groups whose
// secondary type list contains Single/EP/Compilation (e.g. an "Album" that is
// also a "Compilation"). The IgnoreSingles / IgnoreCompilations toggles drop
// release groups classified as such via either primary or secondary type.
//
// Release groups carry no status in ws/2, so there is no status filtering.
func FilterReleaseGroups(groups []ReleaseGroup, opts FilterOptions) []ReleaseGroup {
// First filter by base allowed types (Album/Single/EP) or those with Single/EP/Compilation as secondary type
var preFiltered []ReleaseGroup
for _, rg := range groups {
if !isTypeIncluded(rg.Type) && !hasSliceType(rg.SecondaryTypes, "Single", "EP", "Compilation") {
continue
}
preFiltered = append(preFiltered, rg)
}
// Then apply the IgnoreSingles/IgnoreCompilations toggles using the centralized logic
return ApplyTypeTogglesToReleaseGroups(preFiltered, opts)
}
// isTypeIncluded returns true if the given primary type is in the base
// included set (Album/Single/EP).
func isTypeIncluded(releaseType string) bool {
return includedTypes[releaseType]
}

View File

@@ -68,12 +68,12 @@ func TestFilterReleaseGroups_IgnoreSingles(t *testing.T) {
result := FilterReleaseGroups(groups, FilterOptions{IgnoreSingles: true})
if len(result) != 2 {
t.Fatalf("FilterReleaseGroups() returned %d groups, want 2", len(result))
if len(result) != 1 {
t.Fatalf("FilterReleaseGroups() returned %d groups, want 1", len(result))
}
for _, rg := range result {
if rg.Type == "Single" || contains(rg.SecondaryTypes, "Single") {
t.Errorf("single %q should have been filtered out", rg.ID)
if rg.Type == "Single" || rg.Type == "EP" || contains(rg.SecondaryTypes, "Single") || contains(rg.SecondaryTypes, "EP") {
t.Errorf("single/ep %q should have been filtered out", rg.ID)
}
}
}

View File

@@ -0,0 +1,73 @@
package musicbrainz
import (
"naviwatcher/internal/database"
)
// FilterOptions holds per-artist type filtering preferences.
type FilterOptions struct {
IgnoreSingles bool
IgnoreCompilations bool
}
// hasSliceType reports whether the slice contains any of the wanted values.
func hasSliceType(types []string, wanted ...string) bool {
for _, s := range types {
for _, w := range wanted {
if s == w {
return true
}
}
}
return false
}
// ApplyTypeToggles filters releases based on the IgnoreSingles and IgnoreCompilations flags.
// Implements canonical filtering logic:
// IgnoreSingles filters: Type == "Single" OR Type == "EP" OR SecondaryTypes contains "Single" OR "EP"
// IgnoreCompilations filters: Type == "Compilation" OR SecondaryTypes contains "Compilation"
func ApplyTypeToggles(releases []database.ExternalRelease, opts FilterOptions) []database.ExternalRelease {
var result []database.ExternalRelease
for _, release := range releases {
// Apply IgnoreSingles filtering: filter out if Type is Single/EP OR SecondaryTypes contains Single/EP
if opts.IgnoreSingles {
if release.Type == "Single" || release.Type == "EP" || hasSliceType(release.SecondaryTypes, "Single", "EP") {
continue
}
}
// Apply IgnoreCompilations filtering: filter out if Type is Compilation OR SecondaryTypes contains Compilation
if opts.IgnoreCompilations {
if release.Type == "Compilation" || hasSliceType(release.SecondaryTypes, "Compilation") {
continue
}
}
result = append(result, release)
}
return result
}
// ApplyTypeTogglesToReleaseGroups applies the same IgnoreSingles/IgnoreCompinators filtering logic
// to a slice of ReleaseGroup objects.
func ApplyTypeTogglesToReleaseGroups(groups []ReleaseGroup, opts FilterOptions) []ReleaseGroup {
var result []ReleaseGroup
for _, rg := range groups {
// Apply IgnoreSingles filtering: filter out if Type is Single/EP OR SecondaryTypes contains Single/EP
if opts.IgnoreSingles {
if rg.Type == "Single" || rg.Type == "EP" || hasSliceType(rg.SecondaryTypes, "Single", "EP") {
continue
}
}
// Apply IgnoreCompilations filtering: filter out if Type is Compilation OR SecondaryTypes contains Compilation
if opts.IgnoreCompilations {
if rg.Type == "Compilation" || hasSliceType(rg.SecondaryTypes, "Compilation") {
continue
}
}
result = append(result, rg)
}
return result
}