diff --git a/src/api.js b/src/api.js index 373e0f7..d19efc3 100644 --- a/src/api.js +++ b/src/api.js @@ -178,11 +178,15 @@ export function dispArtistById(id, name) { if (sort) return sort; return _artistRomajiMap?.get(String(id)) || name || ''; } -export function dispAlbumName(name) { - if (!S.cfg.showRomaji) return name || ''; +// Takes a full album object so it can read the Navidrome sortName tag. +export function dispAlbumName(album) { + const name = album?.name || ''; + if (!S.cfg.showRomaji) return name; const ov = getRomajiOverrides(); if (name && ov[name]) return ov[name]; - return _albumRomajiMap?.get(name) || name || ''; + const sort = album?.sortName; + if (sort && !_isJp(sort)) return sort; + return name; } // Synchronous lookup — returns index entry for a song ID, or null if not in index. @@ -215,7 +219,7 @@ export function dispAlbum(s) { if (s.album && ov[s.album]) return ov[s.album]; const st = s.sortAlbum || s.albumSort; if (st && !_isJp(st)) return st; - return romajiLookup(s.id)?.albumRomaji || s.album || ''; + return s.album || ''; } // Returns [{name, id}] like parseArtists but with romaji display names when showRomaji is on. // Multi-artist romaji is collapsed into one entry since we can't reliably split the converted string. diff --git a/src/player/player.js b/src/player/player.js index 1970fc9..81572b5 100644 --- a/src/player/player.js +++ b/src/player/player.js @@ -1,6 +1,6 @@ import { S } from '../state.js'; import { esc, formatDur, toast } from '../utils.js'; -import { api, streamUrl, coverUrl, downloadUrl, dispTitle, dispArtist, dispArtistParts } from '../api.js'; +import { api, streamUrl, coverUrl, downloadUrl, dispTitle, dispArtist, dispArtistParts, dispAlbum } from '../api.js'; // views.js/ui.js circular imports — safe (only used inside function bodies) import { loadRecommendations } from '../views/views.js'; import { updateLyricsHighlight, onNpSongChange, syncNowPlaying } from './nowplaying.js'; @@ -469,7 +469,7 @@ export function updatePlayerUI(s, _skipMediaSession = false) { ? artistParts.map(a => `${esc(a.name)}`) .join(' • ') : '—'; - document.getElementById('player-album-name').textContent = s.album || ''; + document.getElementById('player-album-name').textContent = dispAlbum(s); const art = document.getElementById('player-art'); const cv = coverUrl(s.coverArt, 256); const existingImg = art.querySelector('img'); @@ -594,7 +594,7 @@ export function _syncNpDirect(s) { .join(' • ') : '—'; } - const aln = document.getElementById('np-album-name'); if(aln) aln.textContent = s.album || ''; + const aln = document.getElementById('np-album-name'); if(aln) aln.textContent = dispAlbum(s); syncNpStar(); } @@ -1255,7 +1255,7 @@ export function _restoreLastPlayed() { ? artistParts.map(a => `${esc(a.name)}`) .join(' • ') : '—'; - document.getElementById('player-album-name').textContent = s.album || ''; + document.getElementById('player-album-name').textContent = dispAlbum(s); const art = document.getElementById('player-art'); const cv = coverUrl(s.coverArt, 256); art.innerHTML = cv ? `` : '
♪
'; diff --git a/src/ui/components.js b/src/ui/components.js index ed5f4bf..9c40a40 100644 --- a/src/ui/components.js +++ b/src/ui/components.js @@ -8,7 +8,7 @@ window._imgRetry = function(img, fallback) { if (n > 3 || !img.src) { img.parentNode.innerHTML = `
${fallback}
`; return; } setTimeout(() => { const s = img.src; img.src = ''; img.src = s; }, 1500 * n); }; -import { coverUrl, dispTitle, dispArtistParts, dispArtistById } from '../api.js'; +import { coverUrl, dispTitle, dispArtistParts, dispArtistById, dispAlbumName } from '../api.js'; // Circular imports — safe (only used inside event handlers after module init) import { playQueue, toggleStar, playPreview, parseArtists } from '../player/player.js'; import { showCtxMenu, openDlModal } from './ui.js'; @@ -28,7 +28,7 @@ export function makeCard(title, sub, imgUrl, fallback='💿') { export function makeAlbumCard(a) { const cv = coverUrl(a.coverArt, 512); - const c = makeCard(a.name || '', dispArtistById(a.artistId, a.artist||''), cv, '💿'); + const c = makeCard(dispAlbumName(a), dispArtistById(a.artistId, a.artist||''), cv, '💿'); c.querySelector('.card-art').onclick = (e) => { e.stopPropagation(); quickPlayAlbum(a); }; c.querySelector('.card-info').onclick = () => navigate('album-detail', a); return c; diff --git a/src/views/detail.js b/src/views/detail.js index 6bac391..9e157b8 100644 --- a/src/views/detail.js +++ b/src/views/detail.js @@ -1,6 +1,6 @@ import { S } from '../state.js'; import { esc, toast } from '../utils.js'; -import { api, coverUrl, itunesSearch, itunesLookupAlbum, itunesLookupArtistSongs, itunesLookupArtistAlbums, fetchRomajiIndex, dispArtistById, cacheArtistSortNames } from '../api.js'; +import { api, coverUrl, itunesSearch, itunesLookupAlbum, itunesLookupArtistSongs, itunesLookupArtistAlbums, fetchRomajiIndex, dispArtistById, dispAlbumName, dispAlbum, cacheArtistSortNames } from '../api.js'; import { makeCard, makeAlbumCard, makeGlobalTrackItem, makeSongItem } from '../ui/components.js'; import { playQueue, dlSongLocal, toggleStar, parseArtists } from '../player/player.js'; import { showCtxMenu, openDlModal } from '../ui/ui.js'; @@ -380,7 +380,7 @@ export async function loadItunesAlbumDetail(album) { export async function loadAlbumDetail(album) { document.getElementById('album-back-btn').onclick = () => album._fromArtist ? navigate('artist-detail', album._fromArtist) : navigate('albums'); - document.getElementById('album-detail-name').textContent = album.name || ''; + document.getElementById('album-detail-name').textContent = dispAlbumName(album); const artistEl = document.getElementById('album-detail-artist'); artistEl.innerHTML = parseArtists(album) .map(a => `${esc(dispArtistById(a.id, a.name))}`) @@ -396,7 +396,7 @@ export async function loadAlbumDetail(album) { const totalMin = Math.round(songs.reduce((s,t) => s+(t.duration||0), 0) / 60); document.getElementById('album-detail-stats').textContent = `${songs.length} Titel · ${r.album.year||''} · ${totalMin} Min.`; if (songs[0]) { - document.getElementById('album-detail-name').textContent = songs[0].album || album.name || ''; + document.getElementById('album-detail-name').textContent = dispAlbum(songs[0]) || dispAlbumName(album); artistEl.innerHTML = parseArtists(songs[0]) .map(a => `${esc(dispArtistById(a.id, a.name))}`) .join(' • ') || ''; diff --git a/src/views/home.js b/src/views/home.js index a63dc7f..926e0d6 100644 --- a/src/views/home.js +++ b/src/views/home.js @@ -1,6 +1,6 @@ import { S } from '../state.js'; import { esc } from '../utils.js'; -import { api, apiCached, apiIsCached, coverUrl, fetchRomajiIndex, dispArtistById, cacheArtistSortNames } from '../api.js'; +import { api, apiCached, apiIsCached, coverUrl, fetchRomajiIndex, dispArtistById, dispAlbumName, cacheArtistSortNames } from '../api.js'; import { makeCard, makeAlbumCard, makeSongItem } from '../ui/components.js'; import { playQueue } from '../player/player.js'; // Circular — safe (called inside event handlers) @@ -60,7 +60,7 @@ async function loadHeroCard() { ${thumbUrl ? `${esc(album.name)}` : ''}
${heroLabel}
-
${esc(album.name || '')}
+
${esc(dispAlbumName(album))}
${esc(dispArtistById(album.artistId, album.artist || ''))}