feat: romaji search for Japanese songs via wanakana
When search input is pure ASCII (romaji), converts it to hiragana in parallel using wanakana and merges both result sets. Deduplicates by ID so no double entries. Helps find artists/songs tagged with hiragana in sort fields (e.g. 'inabakumori' → 'いなばくもり'). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
27eb209945
commit
a47b2be177
1 file changed
+28
-3
+28
-3
@@ -692,6 +692,24 @@ export async function loadRandom() {
|
|||||||
} catch(e) { el.innerHTML = `<p style="color:var(--danger)">${e.message}</p>`; }
|
} catch(e) { el.innerHTML = `<p style="color:var(--danger)">${e.message}</p>`; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── ROMAJI → HIRAGANA (lazy-loaded) ───
|
||||||
|
let _wanakana = null;
|
||||||
|
async function _romajiToHiragana(q) {
|
||||||
|
// Only attempt conversion if query is pure ASCII (likely romaji)
|
||||||
|
if (!/^[a-z\s'-]+$/i.test(q.trim())) return null;
|
||||||
|
try {
|
||||||
|
if (!_wanakana) _wanakana = await import('https://cdn.jsdelivr.net/npm/wanakana@5/esm/wanakana.js');
|
||||||
|
const h = _wanakana.toHiragana(q.trim(), { passRomaji: false });
|
||||||
|
// If conversion produced actual hiragana (not just the same ASCII), use it
|
||||||
|
return /[-ゟ]/.test(h) ? h : null;
|
||||||
|
} catch { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function _mergeUniq(a, b) {
|
||||||
|
const seen = new Set(a.map(x => x.id));
|
||||||
|
return [...a, ...(b || []).filter(x => !seen.has(x.id))];
|
||||||
|
}
|
||||||
|
|
||||||
// ─── LOCAL SEARCH ───
|
// ─── LOCAL SEARCH ───
|
||||||
export function onLocalSearch(val) {
|
export function onLocalSearch(val) {
|
||||||
clearTimeout(S.searchTimer);
|
clearTimeout(S.searchTimer);
|
||||||
@@ -704,10 +722,17 @@ export async function doLocalSearch(q) {
|
|||||||
const el = document.getElementById('search-results');
|
const el = document.getElementById('search-results');
|
||||||
el.innerHTML = '<div class="spinner"></div>';
|
el.innerHTML = '<div class="spinner"></div>';
|
||||||
try {
|
try {
|
||||||
const r = await api('search3', `&query=${encodeURIComponent(q)}&songCount=30&albumCount=10&artistCount=6`);
|
const hiragana = await _romajiToHiragana(q);
|
||||||
const res = r.searchResult3 || {};
|
const [r, r2] = await Promise.all([
|
||||||
|
api('search3', `&query=${encodeURIComponent(q)}&songCount=30&albumCount=10&artistCount=6`),
|
||||||
|
hiragana ? api('search3', `&query=${encodeURIComponent(hiragana)}&songCount=20&albumCount=6&artistCount=4`) : Promise.resolve(null),
|
||||||
|
]);
|
||||||
|
const res = r.searchResult3 || {};
|
||||||
|
const res2 = r2?.searchResult3 || {};
|
||||||
el.innerHTML = '';
|
el.innerHTML = '';
|
||||||
const artists = res.artist||[], albums = res.album||[], songs = res.song||[];
|
const artists = _mergeUniq(res.artist||[], res2.artist);
|
||||||
|
const albums = _mergeUniq(res.album ||[], res2.album);
|
||||||
|
const songs = _mergeUniq(res.song ||[], res2.song);
|
||||||
if(!artists.length && !albums.length && !songs.length) {
|
if(!artists.length && !albums.length && !songs.length) {
|
||||||
el.innerHTML = '<div class="empty-state"><div class="icon">🔍</div><p>Keine Ergebnisse</p></div>'; return;
|
el.innerHTML = '<div class="empty-state"><div class="icon">🔍</div><p>Keine Ergebnisse</p></div>'; return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user