mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-05-20 07:04:49 +02:00
fix: improve artist matching for multi-artist tracks and add cover logging
This commit is contained in:
@@ -32,6 +32,16 @@
|
||||
- Added `sameWordsUnordered` check to both Tidal and Qobuz artist matching
|
||||
- Handles Japanese name order (family name first) vs Western name order (given name first)
|
||||
|
||||
- **Multi-Artist Matching**: Fixed artist mismatch for collaboration tracks
|
||||
- "RADWIMPS feat. Toko Miura" now matches when Qobuz/Tidal only shows "Toko Miura"
|
||||
- Split artists by separators (`, `, ` feat. `, ` ft. `, ` & `, ` and `, ` x `)
|
||||
- Match if ANY expected artist matches ANY found artist
|
||||
|
||||
- **Cover Download Logging**: Improved cover download logs for debugging
|
||||
- Shows original URL, upgrade steps, and final URL
|
||||
- Displays estimated resolution based on file size
|
||||
- Logs now appear in Settings > Logs via GoLog
|
||||
|
||||
---
|
||||
|
||||
## [3.0.0-beta.1] - 2026-01-13
|
||||
|
||||
+20
-4
@@ -30,12 +30,12 @@ func downloadCoverToMemory(coverURL string, maxQuality bool) ([]byte, error) {
|
||||
return nil, fmt.Errorf("no cover URL provided")
|
||||
}
|
||||
|
||||
fmt.Printf("[Cover] Downloading cover from: %s\n", coverURL)
|
||||
GoLog("[Cover] Original URL: %s", coverURL)
|
||||
|
||||
// First upgrade small (300) to medium (640) - always do this
|
||||
downloadURL := convertSmallToMedium(coverURL)
|
||||
if downloadURL != coverURL {
|
||||
fmt.Printf("[Cover] Upgraded 300x300 to 640x640: %s\n", downloadURL)
|
||||
GoLog("[Cover] Upgraded 300x300 → 640x640")
|
||||
}
|
||||
|
||||
// Then upgrade to max quality if requested
|
||||
@@ -43,10 +43,14 @@ func downloadCoverToMemory(coverURL string, maxQuality bool) ([]byte, error) {
|
||||
maxURL := upgradeToMaxQuality(downloadURL)
|
||||
if maxURL != downloadURL {
|
||||
downloadURL = maxURL
|
||||
fmt.Printf("[Cover] Upgraded to max quality URL: %s\n", downloadURL)
|
||||
GoLog("[Cover] Upgraded to max resolution (~2000x2000)")
|
||||
} else {
|
||||
GoLog("[Cover] Max resolution not available, using 640x640")
|
||||
}
|
||||
}
|
||||
|
||||
GoLog("[Cover] Final URL: %s", downloadURL)
|
||||
|
||||
client := NewHTTPClientWithTimeout(DefaultTimeout)
|
||||
|
||||
// Create request with User-Agent (required by Spotify CDN)
|
||||
@@ -70,7 +74,19 @@ func downloadCoverToMemory(coverURL string, maxQuality bool) ([]byte, error) {
|
||||
return nil, fmt.Errorf("failed to read cover data: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("[Cover] Downloaded %d bytes\n", len(data))
|
||||
// Calculate approximate resolution from file size
|
||||
// JPEG ~2000x2000 is typically 300-600KB, 640x640 is ~50-100KB
|
||||
sizeKB := len(data) / 1024
|
||||
var resolution string
|
||||
if sizeKB > 200 {
|
||||
resolution = "~2000x2000 (hi-res)"
|
||||
} else if sizeKB > 50 {
|
||||
resolution = "~640x640"
|
||||
} else {
|
||||
resolution = "~300x300"
|
||||
}
|
||||
GoLog("[Cover] Downloaded %d KB (%s)", sizeKB, resolution)
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
|
||||
+44
-23
@@ -64,30 +64,27 @@ func qobuzArtistsMatch(expectedArtist, foundArtist string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check first artist (before comma or feat)
|
||||
expectedFirst := strings.Split(normExpected, ",")[0]
|
||||
expectedFirst = strings.Split(expectedFirst, " feat")[0]
|
||||
expectedFirst = strings.Split(expectedFirst, " ft.")[0]
|
||||
expectedFirst = strings.TrimSpace(expectedFirst)
|
||||
// Split expected artists by common separators (comma, feat, ft., &, and)
|
||||
// e.g., "RADWIMPS, Toko Miura" or "RADWIMPS feat. Toko Miura"
|
||||
expectedArtists := qobuzSplitArtists(normExpected)
|
||||
foundArtists := qobuzSplitArtists(normFound)
|
||||
|
||||
foundFirst := strings.Split(normFound, ",")[0]
|
||||
foundFirst = strings.Split(foundFirst, " feat")[0]
|
||||
foundFirst = strings.Split(foundFirst, " ft.")[0]
|
||||
foundFirst = strings.TrimSpace(foundFirst)
|
||||
|
||||
if expectedFirst == foundFirst {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if first artist is contained in the other
|
||||
if strings.Contains(expectedFirst, foundFirst) || strings.Contains(foundFirst, expectedFirst) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if same words in different order (e.g., "Sawano Hiroyuki" vs "Hiroyuki Sawano")
|
||||
if qobuzSameWordsUnordered(expectedFirst, foundFirst) {
|
||||
GoLog("[Qobuz] Artist names have same words in different order, assuming match: '%s' vs '%s'\n", expectedArtist, foundArtist)
|
||||
return true
|
||||
// Check if ANY expected artist matches ANY found artist
|
||||
for _, exp := range expectedArtists {
|
||||
for _, fnd := range foundArtists {
|
||||
if exp == fnd {
|
||||
return true
|
||||
}
|
||||
// Also check contains for partial matches
|
||||
if strings.Contains(exp, fnd) || strings.Contains(fnd, exp) {
|
||||
return true
|
||||
}
|
||||
// Check same words different order
|
||||
if qobuzSameWordsUnordered(exp, fnd) {
|
||||
GoLog("[Qobuz] Artist names have same words in different order: '%s' vs '%s'\n", exp, fnd)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If scripts are TRULY different (Latin vs CJK/Arabic/Cyrillic), assume match (transliteration)
|
||||
@@ -102,6 +99,30 @@ func qobuzArtistsMatch(expectedArtist, foundArtist string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// qobuzSplitArtists splits artist string by common separators
|
||||
func qobuzSplitArtists(artists string) []string {
|
||||
// Replace common separators with a standard one
|
||||
normalized := artists
|
||||
normalized = strings.ReplaceAll(normalized, " feat. ", "|")
|
||||
normalized = strings.ReplaceAll(normalized, " feat ", "|")
|
||||
normalized = strings.ReplaceAll(normalized, " ft. ", "|")
|
||||
normalized = strings.ReplaceAll(normalized, " ft ", "|")
|
||||
normalized = strings.ReplaceAll(normalized, " & ", "|")
|
||||
normalized = strings.ReplaceAll(normalized, " and ", "|")
|
||||
normalized = strings.ReplaceAll(normalized, ", ", "|")
|
||||
normalized = strings.ReplaceAll(normalized, " x ", "|")
|
||||
|
||||
parts := strings.Split(normalized, "|")
|
||||
result := make([]string, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
trimmed := strings.TrimSpace(p)
|
||||
if trimmed != "" {
|
||||
result = append(result, trimmed)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// qobuzSameWordsUnordered checks if two strings have the same words regardless of order
|
||||
// Useful for Japanese names: "Sawano Hiroyuki" vs "Hiroyuki Sawano"
|
||||
func qobuzSameWordsUnordered(a, b string) bool {
|
||||
|
||||
+44
-23
@@ -1253,30 +1253,27 @@ func artistsMatch(spotifyArtist, tidalArtist string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check first artist (before comma or feat)
|
||||
spotifyFirst := strings.Split(normSpotify, ",")[0]
|
||||
spotifyFirst = strings.Split(spotifyFirst, " feat")[0]
|
||||
spotifyFirst = strings.Split(spotifyFirst, " ft.")[0]
|
||||
spotifyFirst = strings.TrimSpace(spotifyFirst)
|
||||
// Split artists by common separators (comma, feat, ft., &, and)
|
||||
// e.g., "RADWIMPS, Toko Miura" or "RADWIMPS feat. Toko Miura"
|
||||
spotifyArtists := splitArtists(normSpotify)
|
||||
tidalArtists := splitArtists(normTidal)
|
||||
|
||||
tidalFirst := strings.Split(normTidal, ",")[0]
|
||||
tidalFirst = strings.Split(tidalFirst, " feat")[0]
|
||||
tidalFirst = strings.Split(tidalFirst, " ft.")[0]
|
||||
tidalFirst = strings.TrimSpace(tidalFirst)
|
||||
|
||||
if spotifyFirst == tidalFirst {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if first artist is contained in the other
|
||||
if strings.Contains(spotifyFirst, tidalFirst) || strings.Contains(tidalFirst, spotifyFirst) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if same words in different order (e.g., "Sawano Hiroyuki" vs "Hiroyuki Sawano")
|
||||
if sameWordsUnordered(spotifyFirst, tidalFirst) {
|
||||
GoLog("[Tidal] Artist names have same words in different order, assuming match: '%s' vs '%s'\n", spotifyArtist, tidalArtist)
|
||||
return true
|
||||
// Check if ANY expected artist matches ANY found artist
|
||||
for _, exp := range spotifyArtists {
|
||||
for _, fnd := range tidalArtists {
|
||||
if exp == fnd {
|
||||
return true
|
||||
}
|
||||
// Also check contains for partial matches
|
||||
if strings.Contains(exp, fnd) || strings.Contains(fnd, exp) {
|
||||
return true
|
||||
}
|
||||
// Check same words different order
|
||||
if sameWordsUnordered(exp, fnd) {
|
||||
GoLog("[Tidal] Artist names have same words in different order: '%s' vs '%s'\n", exp, fnd)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If scripts are TRULY different (Latin vs CJK/Arabic/Cyrillic), assume match (transliteration)
|
||||
@@ -1292,6 +1289,30 @@ func artistsMatch(spotifyArtist, tidalArtist string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// splitArtists splits artist string by common separators
|
||||
func splitArtists(artists string) []string {
|
||||
// Replace common separators with a standard one
|
||||
normalized := artists
|
||||
normalized = strings.ReplaceAll(normalized, " feat. ", "|")
|
||||
normalized = strings.ReplaceAll(normalized, " feat ", "|")
|
||||
normalized = strings.ReplaceAll(normalized, " ft. ", "|")
|
||||
normalized = strings.ReplaceAll(normalized, " ft ", "|")
|
||||
normalized = strings.ReplaceAll(normalized, " & ", "|")
|
||||
normalized = strings.ReplaceAll(normalized, " and ", "|")
|
||||
normalized = strings.ReplaceAll(normalized, ", ", "|")
|
||||
normalized = strings.ReplaceAll(normalized, " x ", "|")
|
||||
|
||||
parts := strings.Split(normalized, "|")
|
||||
result := make([]string, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
trimmed := strings.TrimSpace(p)
|
||||
if trimmed != "" {
|
||||
result = append(result, trimmed)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// sameWordsUnordered checks if two strings have the same words regardless of order
|
||||
// Useful for Japanese names: "Sawano Hiroyuki" vs "Hiroyuki Sawano"
|
||||
func sameWordsUnordered(a, b string) bool {
|
||||
|
||||
Reference in New Issue
Block a user