feat: album names use Navidrome sortName tag when showRomaji is on
dispAlbumName now takes the full album object and reads album.sortName directly — no kuromoji fallback, no backend translation. dispAlbum (song context) also drops the kuromoji fallback, keeping only the sortAlbum/albumSort file tag. Override list → Navidrome sort tag → raw Japanese name. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
4c3ec5726c
commit
e3987a6376
5 files changed
+19
-15
No files matched your search
+8
-4
@@ -178,11 +178,15 @@ export function dispArtistById(id, name) {
|
|||||||
if (sort) return sort;
|
if (sort) return sort;
|
||||||
return _artistRomajiMap?.get(String(id)) || name || '';
|
return _artistRomajiMap?.get(String(id)) || name || '';
|
||||||
}
|
}
|
||||||
export function dispAlbumName(name) {
|
// Takes a full album object so it can read the Navidrome sortName tag.
|
||||||
if (!S.cfg.showRomaji) return name || '';
|
export function dispAlbumName(album) {
|
||||||
|
const name = album?.name || '';
|
||||||
|
if (!S.cfg.showRomaji) return name;
|
||||||
const ov = getRomajiOverrides();
|
const ov = getRomajiOverrides();
|
||||||
if (name && ov[name]) return ov[name];
|
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.
|
// 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];
|
if (s.album && ov[s.album]) return ov[s.album];
|
||||||
const st = s.sortAlbum || s.albumSort;
|
const st = s.sortAlbum || s.albumSort;
|
||||||
if (st && !_isJp(st)) return st;
|
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.
|
// 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.
|
// Multi-artist romaji is collapsed into one entry since we can't reliably split the converted string.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { S } from '../state.js';
|
import { S } from '../state.js';
|
||||||
import { esc, formatDur, toast } from '../utils.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)
|
// views.js/ui.js circular imports — safe (only used inside function bodies)
|
||||||
import { loadRecommendations } from '../views/views.js';
|
import { loadRecommendations } from '../views/views.js';
|
||||||
import { updateLyricsHighlight, onNpSongChange, syncNowPlaying } from './nowplaying.js';
|
import { updateLyricsHighlight, onNpSongChange, syncNowPlaying } from './nowplaying.js';
|
||||||
@@ -469,7 +469,7 @@ export function updatePlayerUI(s, _skipMediaSession = false) {
|
|||||||
? artistParts.map(a => `<span style="cursor:pointer" onclick="s_goArtist(event,'${esc(a.originalName||a.name)}','${a.id||''}')">${esc(a.name)}</span>`)
|
? artistParts.map(a => `<span style="cursor:pointer" onclick="s_goArtist(event,'${esc(a.originalName||a.name)}','${a.id||''}')">${esc(a.name)}</span>`)
|
||||||
.join('<span style="color:var(--text3)"> • </span>')
|
.join('<span style="color:var(--text3)"> • </span>')
|
||||||
: '—';
|
: '—';
|
||||||
document.getElementById('player-album-name').textContent = s.album || '';
|
document.getElementById('player-album-name').textContent = dispAlbum(s);
|
||||||
const art = document.getElementById('player-art');
|
const art = document.getElementById('player-art');
|
||||||
const cv = coverUrl(s.coverArt, 256);
|
const cv = coverUrl(s.coverArt, 256);
|
||||||
const existingImg = art.querySelector('img');
|
const existingImg = art.querySelector('img');
|
||||||
@@ -594,7 +594,7 @@ export function _syncNpDirect(s) {
|
|||||||
.join('<span style="opacity:.5"> • </span>')
|
.join('<span style="opacity:.5"> • </span>')
|
||||||
: '—';
|
: '—';
|
||||||
}
|
}
|
||||||
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();
|
syncNpStar();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1255,7 +1255,7 @@ export function _restoreLastPlayed() {
|
|||||||
? artistParts.map(a => `<span style="cursor:pointer" onclick="s_goArtist(event,'${esc(a.originalName||a.name)}','${a.id||''}')">${esc(a.name)}</span>`)
|
? artistParts.map(a => `<span style="cursor:pointer" onclick="s_goArtist(event,'${esc(a.originalName||a.name)}','${a.id||''}')">${esc(a.name)}</span>`)
|
||||||
.join('<span style="color:var(--text3)"> • </span>')
|
.join('<span style="color:var(--text3)"> • </span>')
|
||||||
: '—';
|
: '—';
|
||||||
document.getElementById('player-album-name').textContent = s.album || '';
|
document.getElementById('player-album-name').textContent = dispAlbum(s);
|
||||||
const art = document.getElementById('player-art');
|
const art = document.getElementById('player-art');
|
||||||
const cv = coverUrl(s.coverArt, 256);
|
const cv = coverUrl(s.coverArt, 256);
|
||||||
art.innerHTML = cv ? `<img src="${cv}" style="width:100%;height:100%;object-fit:cover">` : '<div class="no-art">♪</div>';
|
art.innerHTML = cv ? `<img src="${cv}" style="width:100%;height:100%;object-fit:cover">` : '<div class="no-art">♪</div>';
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ window._imgRetry = function(img, fallback) {
|
|||||||
if (n > 3 || !img.src) { img.parentNode.innerHTML = `<div class="no-art">${fallback}</div>`; return; }
|
if (n > 3 || !img.src) { img.parentNode.innerHTML = `<div class="no-art">${fallback}</div>`; return; }
|
||||||
setTimeout(() => { const s = img.src; img.src = ''; img.src = s; }, 1500 * n);
|
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)
|
// Circular imports — safe (only used inside event handlers after module init)
|
||||||
import { playQueue, toggleStar, playPreview, parseArtists } from '../player/player.js';
|
import { playQueue, toggleStar, playPreview, parseArtists } from '../player/player.js';
|
||||||
import { showCtxMenu, openDlModal } from './ui.js';
|
import { showCtxMenu, openDlModal } from './ui.js';
|
||||||
@@ -28,7 +28,7 @@ export function makeCard(title, sub, imgUrl, fallback='💿') {
|
|||||||
|
|
||||||
export function makeAlbumCard(a) {
|
export function makeAlbumCard(a) {
|
||||||
const cv = coverUrl(a.coverArt, 512);
|
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-art').onclick = (e) => { e.stopPropagation(); quickPlayAlbum(a); };
|
||||||
c.querySelector('.card-info').onclick = () => navigate('album-detail', a);
|
c.querySelector('.card-info').onclick = () => navigate('album-detail', a);
|
||||||
return c;
|
return c;
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
import { S } from '../state.js';
|
import { S } from '../state.js';
|
||||||
import { esc, toast } from '../utils.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 { makeCard, makeAlbumCard, makeGlobalTrackItem, makeSongItem } from '../ui/components.js';
|
||||||
import { playQueue, dlSongLocal, toggleStar, parseArtists } from '../player/player.js';
|
import { playQueue, dlSongLocal, toggleStar, parseArtists } from '../player/player.js';
|
||||||
import { showCtxMenu, openDlModal } from '../ui/ui.js';
|
import { showCtxMenu, openDlModal } from '../ui/ui.js';
|
||||||
@@ -380,7 +380,7 @@ export async function loadItunesAlbumDetail(album) {
|
|||||||
export async function loadAlbumDetail(album) {
|
export async function loadAlbumDetail(album) {
|
||||||
document.getElementById('album-back-btn').onclick = () =>
|
document.getElementById('album-back-btn').onclick = () =>
|
||||||
album._fromArtist ? navigate('artist-detail', album._fromArtist) : navigate('albums');
|
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');
|
const artistEl = document.getElementById('album-detail-artist');
|
||||||
artistEl.innerHTML = parseArtists(album)
|
artistEl.innerHTML = parseArtists(album)
|
||||||
.map(a => `<span class="song-link" onclick="event.stopPropagation();s_goArtist(event,'${esc(a.originalName||a.name)}','${esc(a.id||'')}')">${esc(dispArtistById(a.id, a.name))}</span>`)
|
.map(a => `<span class="song-link" onclick="event.stopPropagation();s_goArtist(event,'${esc(a.originalName||a.name)}','${esc(a.id||'')}')">${esc(dispArtistById(a.id, a.name))}</span>`)
|
||||||
@@ -396,7 +396,7 @@ export async function loadAlbumDetail(album) {
|
|||||||
const totalMin = Math.round(songs.reduce((s,t) => s+(t.duration||0), 0) / 60);
|
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.`;
|
document.getElementById('album-detail-stats').textContent = `${songs.length} Titel · ${r.album.year||''} · ${totalMin} Min.`;
|
||||||
if (songs[0]) {
|
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])
|
artistEl.innerHTML = parseArtists(songs[0])
|
||||||
.map(a => `<span class="song-link" onclick="event.stopPropagation();s_goArtist(event,'${esc(a.originalName||a.name)}','${esc(a.id||'')}')">${esc(dispArtistById(a.id, a.name))}</span>`)
|
.map(a => `<span class="song-link" onclick="event.stopPropagation();s_goArtist(event,'${esc(a.originalName||a.name)}','${esc(a.id||'')}')">${esc(dispArtistById(a.id, a.name))}</span>`)
|
||||||
.join('<span style="color:var(--text3)"> • </span>') || '';
|
.join('<span style="color:var(--text3)"> • </span>') || '';
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
import { S } from '../state.js';
|
import { S } from '../state.js';
|
||||||
import { esc } from '../utils.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 { makeCard, makeAlbumCard, makeSongItem } from '../ui/components.js';
|
||||||
import { playQueue } from '../player/player.js';
|
import { playQueue } from '../player/player.js';
|
||||||
// Circular — safe (called inside event handlers)
|
// Circular — safe (called inside event handlers)
|
||||||
@@ -60,7 +60,7 @@ async function loadHeroCard() {
|
|||||||
${thumbUrl ? `<img class="home-hero-cover" src="${thumbUrl}" alt="${esc(album.name)}">` : ''}
|
${thumbUrl ? `<img class="home-hero-cover" src="${thumbUrl}" alt="${esc(album.name)}">` : ''}
|
||||||
<div class="home-hero-info">
|
<div class="home-hero-info">
|
||||||
<div class="home-hero-label">${heroLabel}</div>
|
<div class="home-hero-label">${heroLabel}</div>
|
||||||
<div class="home-hero-title">${esc(album.name || '')}</div>
|
<div class="home-hero-title">${esc(dispAlbumName(album))}</div>
|
||||||
<div class="home-hero-artist">${esc(dispArtistById(album.artistId, album.artist || ''))}</div>
|
<div class="home-hero-artist">${esc(dispArtistById(album.artistId, album.artist || ''))}</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="home-hero-play" title="Abspielen">
|
<button class="home-hero-play" title="Abspielen">
|
||||||
|
|||||||
Reference in new issue
Block a user