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
+36
-2
@@ -52,8 +52,15 @@ export async function ndAuth() {
|
||||
// ─── ROMAJI INDEX ───
|
||||
// Fetched once per session from the backend, cached in memory.
|
||||
let _romajiIndex = null;
|
||||
let _romajiMap = null; // Map<songId, { titleRomaji, artistRomaji, albumRomaji }>
|
||||
let _romajiIndexFetch = null; // in-flight promise dedup
|
||||
|
||||
function _buildRomajiMap(idx) {
|
||||
const m = new Map();
|
||||
if (Array.isArray(idx)) idx.forEach(e => m.set(String(e.id), e));
|
||||
return m;
|
||||
}
|
||||
|
||||
export async function fetchRomajiIndex() {
|
||||
if (!S.cfg.backendUrl || !S.cfg.url || !S.cfg.user || !S.cfg.pass) return null;
|
||||
if (_romajiIndex) return _romajiIndex;
|
||||
@@ -61,12 +68,39 @@ export async function fetchRomajiIndex() {
|
||||
_romajiIndexFetch = fetch(
|
||||
`${S.cfg.backendUrl}/api/romaji-index?ndUrl=${encodeURIComponent(S.cfg.url)}&user=${encodeURIComponent(S.cfg.user)}&pass=${encodeURIComponent(S.cfg.pass)}`
|
||||
).then(r => r.ok ? r.json() : null)
|
||||
.then(idx => { _romajiIndex = idx; _romajiIndexFetch = null; return idx; })
|
||||
.then(idx => { _romajiIndex = idx; _romajiMap = _buildRomajiMap(idx); _romajiIndexFetch = null; return idx; })
|
||||
.catch(() => { _romajiIndexFetch = null; return null; });
|
||||
return _romajiIndexFetch;
|
||||
}
|
||||
|
||||
export function invalidateRomajiIndex() { _romajiIndex = null; }
|
||||
export function invalidateRomajiIndex() { _romajiIndex = null; _romajiMap = null; }
|
||||
|
||||
// Synchronous lookup — returns index entry for a song ID, or null if not in index.
|
||||
export function romajiLookup(id) { return _romajiMap?.get(String(id)) ?? null; }
|
||||
|
||||
// Display helpers — return romaji when showRomaji is on and the song is in the index,
|
||||
// otherwise fall back to the original Navidrome values.
|
||||
export function dispTitle(s) {
|
||||
if (!S.cfg.showRomaji) return s.title || '';
|
||||
return romajiLookup(s.id)?.titleRomaji || s.title || '';
|
||||
}
|
||||
export function dispArtist(s) {
|
||||
if (!S.cfg.showRomaji) return s.artist || '';
|
||||
return romajiLookup(s.id)?.artistRomaji || s.artist || '';
|
||||
}
|
||||
export function dispAlbum(s) {
|
||||
if (!S.cfg.showRomaji) return s.album || '';
|
||||
return romajiLookup(s.id)?.albumRomaji || 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.
|
||||
export function dispArtistParts(s, parseArtistsFn) {
|
||||
const parts = parseArtistsFn(s);
|
||||
if (!S.cfg.showRomaji) return parts;
|
||||
const info = romajiLookup(s.id);
|
||||
if (!info?.artistRomaji) return parts;
|
||||
return [{ name: info.artistRomaji, id: parts[0]?.id || '' }];
|
||||
}
|
||||
|
||||
export async function romajiConvert(texts) {
|
||||
if (!S.cfg.backendUrl || !texts.length) return texts;
|
||||
|
||||
Reference in new issue
Block a user