From 3837783225567ca96c95fa1ed61ccc619286d558 Mon Sep 17 00:00:00 2001 From: Honey Date: Wed, 27 May 2026 17:16:57 +0200 Subject: [PATCH] fix: iTunes JP-first with US fallback; romaji filter in playlist/starred search --- backend/lib/metadata.js | 6 ++++-- src/api.js | 10 ++++++---- src/views.js | 10 +++++++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/backend/lib/metadata.js b/backend/lib/metadata.js index 550e5e4..c86e4d9 100644 --- a/backend/lib/metadata.js +++ b/backend/lib/metadata.js @@ -92,13 +92,15 @@ 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 url = `https://itunes.apple.com/search?term=${encodeURIComponent(artist + ' ' + searchTitle)}&media=music&entity=musicTrack&limit=10&country=jp&explicit=Yes`; - const results = await new Promise((resolve, reject) => { + const fetchItunes = (country) => new Promise((resolve, reject) => { + const url = `https://itunes.apple.com/search?term=${encodeURIComponent(artist + ' ' + searchTitle)}&media=music&entity=musicTrack&limit=10&country=${country}&explicit=Yes`; https.get(url, res => { let d = ''; res.on('data', c => d += c); res.on('end', () => { try { resolve(JSON.parse(d).results || []); } catch { reject(new Error('parse')); } }); }).on('error', reject); }); + let results = await fetchItunes('jp'); + if (!results.length) results = await fetchItunes('us'); const normT = normalizeTitle(title); const normA = (artist || '').toLowerCase().trim(); // Split by space OR comma/semicolon/slash so "VXLLAIN, iGRES" → "vxllain" not "vxllain," diff --git a/src/api.js b/src/api.js index 4596203..b6adc86 100644 --- a/src/api.js +++ b/src/api.js @@ -135,10 +135,12 @@ function _itunesBase() { export async function itunesSearch(query, entity='musicTrack', limit=25) { try { - const r = await fetch(`${_itunesBase()}/search?term=${encodeURIComponent(query)}&media=music&entity=${entity}&limit=${limit}&country=jp&explicit=Yes`); - const text = await r.text(); - if (!text) return []; - return JSON.parse(text).results || []; + const base = `${_itunesBase()}/search?term=${encodeURIComponent(query)}&media=music&entity=${entity}&limit=${limit}&explicit=Yes`; + const rJp = await fetch(`${base}&country=jp`); + const jpResults = JSON.parse(await rJp.text()).results || []; + if (jpResults.length) return jpResults; + const rUs = await fetch(`${base}&country=us`); + return JSON.parse(await rUs.text()).results || []; } catch { return []; } } diff --git a/src/views.js b/src/views.js index 31a39f6..faee608 100644 --- a/src/views.js +++ b/src/views.js @@ -6,7 +6,7 @@ import { playQueue, dlSongLocal, playPreview, toggleStar, parseArtists } from '. import { showCtxMenu, openEditPlModal, openDlModal, closeSidebar, switchNavMode } from './ui.js'; import { closeNowPlaying, closeNpMenu } from './nowplaying.js'; import { makeCard, makeAlbumCard, makeSongItem, makeGlobalTrackItem, mkSection } from './components.js'; -import { fetchRomajiIndex, dispArtistById } from './api.js'; +import { fetchRomajiIndex, dispArtistById, romajiLookup } from './api.js'; import { loadSpotifyImporter } from './spotify.js'; import { loadSoundCloudImporter } from './soundcloud.js'; @@ -748,7 +748,9 @@ export function filterPlaylist(val) { document.querySelectorAll('#playlist-songs .song-item').forEach(item => { const title = (item.querySelector('.song-title')?.textContent || '').toLowerCase(); const artist = (item.querySelector('.song-artist')?.textContent || '').toLowerCase(); - item.style.display = (!q || title.includes(q) || artist.includes(q)) ? '' : 'none'; + if (!q || title.includes(q) || artist.includes(q)) { item.style.display = ''; return; } + const rm = romajiLookup(item.dataset.songId); + item.style.display = ((rm?.titleRomaji||'').toLowerCase().includes(q) || (rm?.artistRomaji||'').toLowerCase().includes(q)) ? '' : 'none'; }); } @@ -916,7 +918,9 @@ export function filterStarred(val) { document.querySelectorAll('#starred-songs .song-item').forEach(item => { const title = (item.querySelector('.song-title')?.textContent || '').toLowerCase(); const artist = (item.querySelector('.song-artist')?.textContent || '').toLowerCase(); - item.style.display = (!q || title.includes(q) || artist.includes(q)) ? '' : 'none'; + if (!q || title.includes(q) || artist.includes(q)) { item.style.display = ''; return; } + const rm = romajiLookup(item.dataset.songId); + item.style.display = ((rm?.titleRomaji||'').toLowerCase().includes(q) || (rm?.artistRomaji||'').toLowerCase().includes(q)) ? '' : 'none'; }); }