mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-02 17:18:36 +02:00
136 lines
3.4 KiB
Go
136 lines
3.4 KiB
Go
package gobackend
|
|
|
|
import (
|
|
"math"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var simplifyTrackNamePatterns = func() []*regexp.Regexp {
|
|
patterns := []string{
|
|
`\s*\(feat\.?.*?\)`,
|
|
`\s*\(ft\.?.*?\)`,
|
|
`\s*\(featuring.*?\)`,
|
|
`\s*\(with.*?\)`,
|
|
`\s*-\s*Remaster(ed)?.*$`,
|
|
`\s*-\s*\d{4}\s*Remaster.*$`,
|
|
`\s*\(Remaster(ed)?.*?\)`,
|
|
`\s*\(Deluxe.*?\)`,
|
|
`\s*\(Bonus.*?\)`,
|
|
`\s*\(Live.*?\)`,
|
|
`\s*\(Acoustic.*?\)`,
|
|
`\s*\(Radio Edit\)`,
|
|
`\s*\(Single Version\)`,
|
|
}
|
|
compiled := make([]*regexp.Regexp, len(patterns))
|
|
for i, pattern := range patterns {
|
|
compiled[i] = regexp.MustCompile("(?i)" + pattern)
|
|
}
|
|
return compiled
|
|
}()
|
|
|
|
func simplifyTrackName(name string) string {
|
|
result := name
|
|
for _, re := range simplifyTrackNamePatterns {
|
|
result = re.ReplaceAllString(result, "")
|
|
}
|
|
result = strings.TrimSpace(result)
|
|
if result == "" {
|
|
return result
|
|
}
|
|
|
|
if loose := normalizeLooseTitle(result); loose != "" {
|
|
return loose
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func normalizedLyricsSearchTitle(name string) string {
|
|
return strings.ToLower(strings.TrimSpace(simplifyTrackName(name)))
|
|
}
|
|
|
|
func containsWordSequence(value, sequence string) bool {
|
|
valueWords := strings.Fields(value)
|
|
sequenceWords := strings.Fields(sequence)
|
|
if len(valueWords) == 0 || len(sequenceWords) == 0 || len(sequenceWords) > len(valueWords) {
|
|
return false
|
|
}
|
|
|
|
for start := 0; start <= len(valueWords)-len(sequenceWords); start++ {
|
|
matches := true
|
|
for offset := range sequenceWords {
|
|
if valueWords[start+offset] != sequenceWords[offset] {
|
|
matches = false
|
|
break
|
|
}
|
|
}
|
|
if matches {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func lyricsSearchTitlesMatch(candidateTrack, trackName string, allowDecoratedCandidate bool) bool {
|
|
expected := normalizedLyricsSearchTitle(trackName)
|
|
candidate := normalizedLyricsSearchTitle(candidateTrack)
|
|
if expected == "" || candidate == "" {
|
|
return false
|
|
}
|
|
if candidate == expected {
|
|
return true
|
|
}
|
|
return allowDecoratedCandidate && containsWordSequence(candidate, expected)
|
|
}
|
|
|
|
func lyricsSearchArtistsMatch(candidateArtist, artistName string) bool {
|
|
expected := normalizeLooseArtistName(normalizeArtistName(artistName))
|
|
if expected == "" {
|
|
return true
|
|
}
|
|
candidate := normalizeLooseArtistName(normalizeArtistName(candidateArtist))
|
|
if candidate == "" {
|
|
return false
|
|
}
|
|
return candidate == expected || sameWordsUnordered(candidate, expected)
|
|
}
|
|
|
|
func lyricsSearchDurationMatches(candidateDuration, durationSec float64) bool {
|
|
if candidateDuration <= 0 || durationSec <= 0 {
|
|
return true
|
|
}
|
|
return math.Abs(candidateDuration-durationSec) <= durationToleranceSec
|
|
}
|
|
|
|
func lyricsSearchArtistAppearsInTitle(candidateTrack, artistName string) bool {
|
|
expectedArtist := normalizeLooseArtistName(normalizeArtistName(artistName))
|
|
candidateTitle := normalizeLooseArtistName(candidateTrack)
|
|
return expectedArtist != "" &&
|
|
candidateTitle != "" &&
|
|
containsWordSequence(candidateTitle, expectedArtist)
|
|
}
|
|
|
|
func normalizeArtistName(name string) string {
|
|
separators := []string{", ", "; ", " & ", " feat. ", " ft. ", " featuring ", " with "}
|
|
|
|
result := name
|
|
for _, sep := range separators {
|
|
if idx := strings.Index(strings.ToLower(result), strings.ToLower(sep)); idx > 0 {
|
|
result = result[:idx]
|
|
break
|
|
}
|
|
}
|
|
|
|
return strings.TrimSpace(result)
|
|
}
|
|
|
|
func isLikelyInstrumentalTrack(name string) bool {
|
|
trimmed := strings.TrimSpace(name)
|
|
if trimmed == "" {
|
|
return false
|
|
}
|
|
|
|
return instrumentalTrackPattern.MatchString(trimmed)
|
|
}
|