diff --git a/android/app/src/main/kotlin/com/novasonic/app/viewmodel/ExploreViewModel.kt b/android/app/src/main/kotlin/com/novasonic/app/viewmodel/ExploreViewModel.kt index e966eb2..efd051e 100644 --- a/android/app/src/main/kotlin/com/novasonic/app/viewmodel/ExploreViewModel.kt +++ b/android/app/src/main/kotlin/com/novasonic/app/viewmodel/ExploreViewModel.kt @@ -102,10 +102,13 @@ class ExploreViewModel(app: Application) : AndroidViewModel(app) { } private fun search(q: String) { + // Strip leading aesthetic punctuation (".diedlonely", "†…") so iTunes actually surfaces the + // artist instead of nothing. Falls back to the raw query if it was only symbols. + val term = q.trimStart { !it.isLetterOrDigit() }.trim().ifBlank { q } viewModelScope.launch { _state.value = _state.value.copy(isSearching = true, error = null) try { - val resp = ItunesClient.search(q) + val resp = ItunesClient.search(term) val results = resp.results _state.value = _state.value.copy( artists = results.filter { it.wrapperType == "artist" }, diff --git a/backend/lib/metadata.js b/backend/lib/metadata.js index 54cf30e..15cf927 100644 --- a/backend/lib/metadata.js +++ b/backend/lib/metadata.js @@ -148,7 +148,10 @@ async function lookupItunesTrackMeta(artist, title) { // e.g. "don't rush - slowed" → search "don't rush" so iTunes finds the single const baseTitle = title.replace(/\s*[-–]\s*\S[^-]*$/, '').trim() || title; const searchTitle = baseTitle !== title ? baseTitle : title; - const searchArtist = artist.split(/[;,&]/)[0].trim(); + // Strip leading aesthetic punctuation many artists use (".diedlonely", "†", "✝", "_x") + // so the iTunes search actually surfaces them instead of a same-titled track by someone else. + const stripLeadSym = s => (s || '').replace(/^[^\p{L}\p{N}]+/u, '').trim(); + const searchArtist = stripLeadSym(artist.split(/[;,&]/)[0].trim()); const fetchItunes = (country) => new Promise((resolve, reject) => { const url = `https://itunes.apple.com/search?term=${encodeURIComponent(searchArtist + ' ' + searchTitle)}&media=music&entity=musicTrack&limit=10&country=${country}&explicit=Yes`; https.get(url, res => { @@ -159,7 +162,7 @@ async function lookupItunesTrackMeta(artist, title) { const normT = normalizeTitle(title); const normA = (searchArtist || '').toLowerCase().trim(); // Split by space OR comma/semicolon/slash so "VXLLAIN, iGRES" → "vxllain" not "vxllain," - const firstWord = s => (s || '').toLowerCase().split(/[\s,;/]/)[0]; + const firstWord = s => stripLeadSym((s || '').toLowerCase()).split(/[\s,;/]/)[0]; const strictMatch = results => results.find(t => normalizeTitle(t.trackName) === normT && firstWord(t.artistName) === firstWord(normA) );