feat: extract normalization into internal/normalize package

This commit is contained in:
2026-07-19 18:11:22 +03:00
parent c95c740cd5
commit edf9c1d1f8
4 changed files with 169 additions and 68 deletions

View File

@@ -4,11 +4,9 @@ import (
"context"
"fmt"
"net/url"
"regexp"
"strings"
"unicode"
"naviwatcher/internal/database"
"naviwatcher/internal/normalize"
)
// excludedStatuses contains release-group statuses that should be filtered out.
@@ -26,14 +24,6 @@ var includedTypes = map[string]bool{
"Compilation": true,
}
// Precompiled regexes for NormalizeString — compiled once at package init.
var (
bracketRe = regexp.MustCompile(`\[[^\]]*\]`)
parenRe = regexp.MustCompile(`\([^)]*\)`)
yearRe = regexp.MustCompile(`\b(1[0-9]{3}|2[0-9]{3})\b`)
spaceRe = regexp.MustCompile(`\s+`)
)
// 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.
@@ -122,63 +112,18 @@ func IsTypeIncluded(releaseType string) bool {
return includedTypes[releaseType]
}
// 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
// NormalizeString normalizes a string for fuzzy matching.
// It delegates to the shared normalize package; see normalize.NormalizeString
// for the full normalization contract.
func NormalizeString(s string) string {
// Convert to lowercase
s = strings.ToLower(s)
// Remove bracketed content first (e.g., [Deluxe Edition], [Remastered 2020])
s = bracketRe.ReplaceAllString(s, "")
// Remove parenthesized content (e.g., (Deluxe), (Remastered))
s = parenRe.ReplaceAllString(s, "")
// Remove years (4-digit numbers between 1000-2999)
s = yearRe.ReplaceAllString(s, "")
// Replace common separators with spaces before stripping other special chars
s = strings.ReplaceAll(s, "-", " ")
s = strings.ReplaceAll(s, "_", " ")
// Keep only letters, digits, and spaces
var b strings.Builder
for _, r := range s {
if unicode.IsLetter(r) || unicode.IsDigit(r) || unicode.IsSpace(r) {
b.WriteRune(r)
}
}
s = b.String()
// Collapse multiple spaces
s = spaceRe.ReplaceAllString(s, " ")
// Trim
s = strings.TrimSpace(s)
return s
return normalize.NormalizeString(s)
}
// NormalizeArtistName normalizes an artist name for comparison.
// It applies NormalizeString and additionally handles common prefixes.
// It delegates to the shared normalize package; see
// normalize.NormalizeArtistName for the full normalization contract.
func NormalizeArtistName(name string) string {
name = NormalizeString(name)
// Remove common leading articles for better matching
prefixes := []string{"the ", "a ", "an "}
for _, prefix := range prefixes {
if strings.HasPrefix(name, prefix) {
name = strings.TrimPrefix(name, prefix)
break
}
}
return strings.TrimSpace(name)
return normalize.NormalizeArtistName(name)
}
// ToExternalRelease converts a ReleaseGroup to an ExternalRelease