feat: keep Japanese in main tags, romaji in sort tags + frontend toggle
- Downloads now store original Japanese in TITLE/ARTIST/ALBUM tags so Apple Music, iTunes, YTM etc. all see the authentic original text - Sort tags (TITLESORT/ARTISTSORT/ALBUMSORT) written with romaji for apps that use them for sorting/display - iTunes-resolved English artist name used for ARTISTSORT and folder names when available (more accurate than kuromoji for known artists) - rawArtist parameter added to downloadTrack so YTM search query uses the original Japanese channel name (fixes wrong song downloads) - spotify.js: artistName preserved as original; itunesArtistName stored separately so the Spotify importer shows Japanese titles - romaji index now includes titleRomaji/artistRomaji/albumRomaji fields - Frontend: showRomaji setting (Settings > Bibliothek) — when on, player bar, now-playing, queue, and song lists all show romaji via index lookup - refreshDisplayMode() updates all live UI when the toggle is saved Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
f1eabf7099
commit
1aff19e319
10 files changed
+140
-55
No files matched your search
+16
-8
@@ -1,6 +1,6 @@
|
||||
import { S } from './state.js';
|
||||
import { esc, formatDur, toast } from './utils.js';
|
||||
import { api, streamUrl, coverUrl, downloadUrl } 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.js';
|
||||
import { updateLyricsHighlight, onNpSongChange } from './nowplaying.js';
|
||||
@@ -451,13 +451,13 @@ export function parseArtists(s) {
|
||||
}
|
||||
|
||||
export function updatePlayerUI(s, _skipMediaSession = false) {
|
||||
document.getElementById('player-title').textContent = s.title;
|
||||
const artistParts = parseArtists(s);
|
||||
document.getElementById('player-title').textContent = dispTitle(s);
|
||||
const artistParts = dispArtistParts(s, parseArtists);
|
||||
document.getElementById('player-artist-name').innerHTML = artistParts.length
|
||||
? artistParts.map(a => `<span style="cursor:pointer" onclick="s_goArtist(event,'${esc(a.name)}','${a.id||''}')">${esc(a.name)}</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 cv = coverUrl(s.coverArt, 256);
|
||||
const existingImg = art.querySelector('img');
|
||||
@@ -518,16 +518,16 @@ export function _syncNpDirect(s) {
|
||||
else cover.innerHTML = cv ? `<img src="${cv}" style="width:100%;height:100%;object-fit:cover">` : '<div class="no-art">♪</div>';
|
||||
}
|
||||
_crossfadeNpBg(cv);
|
||||
const t = document.getElementById('np-title'); if(t) t.textContent = s.title || '—';
|
||||
const t = document.getElementById('np-title'); if(t) t.textContent = dispTitle(s) || '—';
|
||||
const an = document.getElementById('np-artist-name');
|
||||
if(an) {
|
||||
const parts = parseArtists(s);
|
||||
const parts = dispArtistParts(s, parseArtists);
|
||||
an.innerHTML = parts.length
|
||||
? parts.map(a => `<span style="cursor:pointer" onclick="closeNowPlaying();s_goArtist(event,'${esc(a.name)}','${a.id||''}')">${esc(a.name)}</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();
|
||||
}
|
||||
|
||||
@@ -994,7 +994,7 @@ export function renderQueue() {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'queue-item' + (i === S.qIdx ? ' active' : '');
|
||||
const cv = coverUrl(s.coverArt, 128);
|
||||
item.innerHTML = `<div class="qi-idx">${i+1}</div><div class="qi-art">${cv ? `<img src="${cv}">` : '♪'}</div><div class="qi-info"><div class="qi-title">${esc(s.title)}</div><div class="qi-artist">${esc(s.artist||'')}</div></div>`;
|
||||
item.innerHTML = `<div class="qi-idx">${i+1}</div><div class="qi-art">${cv ? `<img src="${cv}">` : '♪'}</div><div class="qi-info"><div class="qi-title">${esc(dispTitle(s))}</div><div class="qi-artist">${esc(dispArtist(s))}</div></div>`;
|
||||
item.onclick = () => { S.qIdx = i; playCurrent(); };
|
||||
el.appendChild(item);
|
||||
});
|
||||
@@ -1008,6 +1008,14 @@ export function toggleQueue() {
|
||||
_syncOverlayState();
|
||||
}
|
||||
|
||||
// Called after showRomaji is toggled — re-renders all live player UI with new display mode.
|
||||
export function refreshDisplayMode() {
|
||||
_renderedQueueKey = ''; // force queue re-render
|
||||
if (S.queueOpen) renderQueue();
|
||||
const s = S.queue[S.qIdx];
|
||||
if (s) { updatePlayerUI(s, true); _syncNpDirect(s); }
|
||||
}
|
||||
|
||||
// ─── DOWNLOAD ─────────────────────────────────────────────────────────────
|
||||
|
||||
export function dlSongLocal(s) {
|
||||
|
||||
Reference in new issue
Block a user