fix: artist page resolved "Flay!" to the far more popular "K.FLAY"
The Deezer artist match required only that every query word appear in the candidate name, so
"Flay!" (query word: flay) matched "K.FLAY" (words: k, flay). Sorted by fan count, K.FLAY
(54,822 fans) buried the real "Flay!" (39 fans) — the page showed K.FLAY's fans + top tracks.
Now prefer an EXACT normalized-name match ("flay" == "flay", not "kflay"), falling back to the
loose match only when there's no exact one. Web (detail.js) + Android (ArtistViewModel).
Verified against Deezer: exact match selects the real Flay! (id 12242284, whose top tracks
are actually Flay!). SW ns-v60.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
b3cf01dd3b
commit
e4f030c98d
3 files changed
+17
-8
No files matched your search
@@ -112,12 +112,15 @@ class ArtistViewModel(app: Application, savedState: SavedStateHandle) : AndroidV
|
||||
}.getOrNull()
|
||||
|
||||
val artists = searchResp?.results?.filter { it.wrapperType == "artist" } ?: emptyList()
|
||||
// artists are sorted by fan count (ItunesClient), so the first name match is the most popular
|
||||
// one — disambiguates same-name artists (5x "Joji").
|
||||
// artists are fan-sorted (ItunesClient), so firstOrNull gives the most popular match.
|
||||
// Prefer an EXACT normalized-name match: "Flay!" must not resolve to the far more popular
|
||||
// "K.FLAY" (its name merely contains "flay"). Only fall back to a loose contains-match.
|
||||
fun norm(s: String?) = (s ?: "").lowercase().replace(Regex("[^a-z0-9]"), "")
|
||||
val q = norm(artistName)
|
||||
val iArtist = (
|
||||
artists.find { it.artistName?.equals(artistName, ignoreCase = true) == true }
|
||||
?: artists.find {
|
||||
val n = it.artistName ?: return@find false
|
||||
artists.firstOrNull { norm(it.artistName) == q }
|
||||
?: artists.firstOrNull {
|
||||
val n = it.artistName ?: return@firstOrNull false
|
||||
n.contains(artistName, ignoreCase = true) || artistName.contains(n, ignoreCase = true)
|
||||
}
|
||||
?: artists.firstOrNull()
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'ns-v59';
|
||||
const CACHE = 'ns-v60';
|
||||
const PRECACHE = ['/', 'index.html', 'manifest.json', 'icon.svg'];
|
||||
const SKIP_PATHS = ['/rest/stream', '/rest/download', '/config.js'];
|
||||
// Auth token params stripped from cache keys so artwork survives across sessions/token refreshes.
|
||||
|
||||
+8
-2
@@ -142,14 +142,20 @@ export async function loadArtistDetail(artist) {
|
||||
const [infoRes, topRes, iArtistRes, eventsRes] = await Promise.allSettled([infoP, topP, iArtistP, eventsP]);
|
||||
|
||||
const _normWords = s => new Set((s || '').toLowerCase().replace(/[^a-z0-9]/g, ' ').split(/\s+/).filter(Boolean));
|
||||
const _normName = s => (s || '').toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||
const _queryWords = _normWords(artist.name);
|
||||
const _qNorm = _normName(artist.name);
|
||||
const iArtistResults = iArtistRes.status === 'fulfilled' ? (iArtistRes.value || []) : [];
|
||||
// Name-matching candidates, already ordered by fanCount (itunesSearch sorts musicArtist by nb_fan).
|
||||
const nameMatches = iArtistResults.filter(r => {
|
||||
// Prefer EXACT normalized-name matches. "Flay!" must not resolve to the far more popular "K.FLAY"
|
||||
// (whose name merely contains the word "flay") — exact "flay" == "flay" wins over "kflay". Only fall
|
||||
// back to the looser word-subset match when there's no exact match. Both lists are fan-sorted already.
|
||||
const exactMatches = iArtistResults.filter(r => _normName(r.artistName) === _qNorm);
|
||||
const looseMatches = iArtistResults.filter(r => {
|
||||
if (_queryWords.size === 0) return true;
|
||||
const rw = _normWords(r.artistName);
|
||||
return [..._queryWords].every(w => rw.has(w));
|
||||
});
|
||||
const nameMatches = exactMatches.length ? exactMatches : looseMatches;
|
||||
// Default pick = most popular name match (fixes "5× Joji": Deezer's order buries the real one).
|
||||
let iArtistMatch = nameMatches[0] || null;
|
||||
|
||||
|
||||
Reference in new issue
Block a user