fix(backend): don't tag Western artists with the JP store's katakana album-artist
The iTunes lookup is JP-first (for correct Japanese titles/artwork), but the Japanese store returns katakana-localized names for Western artists — so DJ Snake's single got album_artist / folder "DJスネイク • アルーナジョージ" while the title/artist stayed English. - lookupItunesTrackMeta: take the NAMES (album, album-artist, track, artist) from the US store when the search isn't Japanese; keep JP names for actual Japanese songs. - album & artist download paths: if iTunes' collectionArtistName is Japanese but the request artist isn't, keep the request's English name for the tag and folder. Already-downloaded files aren't rewritten — re-download or fix via the editor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
8cd3e9ac23
commit
72b612f5ff
2 files changed
+19
-7
No files matched your search
+10
-5
@@ -179,22 +179,27 @@ async function lookupItunesTrackMeta(artist, title) {
|
|||||||
let usMatch = strictMatch(usResults) || (!isJpSearch && titleOnlyMatch(usResults));
|
let usMatch = strictMatch(usResults) || (!isJpSearch && titleOnlyMatch(usResults));
|
||||||
if (!match) match = usMatch;
|
if (!match) match = usMatch;
|
||||||
if (!match) return {};
|
if (!match) return {};
|
||||||
|
// For non-Japanese songs, take the NAMES from the US store: the JP store returns katakana-
|
||||||
|
// localized artist / album-artist names for Western artists (e.g. "DJスネイク" for DJ Snake),
|
||||||
|
// which would otherwise become the album_artist tag and the on-disk folder. Japanese songs keep
|
||||||
|
// the JP names (correct kanji). Numeric fields (year, track#, duration) are store-agnostic.
|
||||||
|
const naming = (!isJpSearch && usMatch) ? usMatch : match;
|
||||||
// JP genre names are localized (e.g. イージーリスニング); prefer US genre, map JP→EN as fallback
|
// JP genre names are localized (e.g. イージーリスニング); prefer US genre, map JP→EN as fallback
|
||||||
const rawGenre = (usMatch || match).primaryGenreName;
|
const rawGenre = (usMatch || match).primaryGenreName;
|
||||||
const genre = rawGenre ? toEnglishGenre(rawGenre) : undefined;
|
const genre = rawGenre ? toEnglishGenre(rawGenre) : undefined;
|
||||||
return {
|
return {
|
||||||
album: match.collectionName || '',
|
album: naming.collectionName || '',
|
||||||
albumArtist: match.collectionArtistName || undefined,
|
albumArtist: naming.collectionArtistName || undefined,
|
||||||
trackName: match.trackName || undefined,
|
trackName: naming.trackName || undefined,
|
||||||
year: match.releaseDate ? new Date(match.releaseDate).getFullYear() : undefined,
|
year: match.releaseDate ? new Date(match.releaseDate).getFullYear() : undefined,
|
||||||
trackNum: match.trackNumber || undefined,
|
trackNum: match.trackNumber || undefined,
|
||||||
totalTracks: match.trackCount || undefined,
|
totalTracks: match.trackCount || undefined,
|
||||||
discNum: match.discNumber || undefined,
|
discNum: match.discNumber || undefined,
|
||||||
discTotal: match.discCount || undefined,
|
discTotal: match.discCount || undefined,
|
||||||
genre: genre,
|
genre: genre,
|
||||||
artworkUrl: (match.artworkUrl100 || '').replace('100x100bb', '1000x1000bb'),
|
artworkUrl: (naming.artworkUrl100 || match.artworkUrl100 || '').replace('100x100bb', '1000x1000bb'),
|
||||||
durationMs: match.trackTimeMillis || undefined,
|
durationMs: match.trackTimeMillis || undefined,
|
||||||
itunesArtistName: match.artistName || undefined,
|
itunesArtistName: naming.artistName || undefined,
|
||||||
};
|
};
|
||||||
} catch { return {}; }
|
} catch { return {}; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -627,7 +627,11 @@ app.post('/api/download', async (req, res) => {
|
|||||||
// over the request's `artist`, which can be a single track's performer — e.g. the lead single
|
// over the request's `artist`, which can be a single track's performer — e.g. the lead single
|
||||||
// "TAKEDOWN" is by TWICE, so `artist` arrives as "TWICE" and would tag the whole soundtrack's
|
// "TAKEDOWN" is by TWICE, so `artist` arrives as "TWICE" and would tag the whole soundtrack's
|
||||||
// album_artist as TWICE. Keeps album_artist consistent so Navidrome doesn't split the album.
|
// album_artist as TWICE. Keeps album_artist consistent so Navidrome doesn't split the album.
|
||||||
const albumArtistName = tracks.map(t => t.collectionArtistName).find(Boolean) || artist;
|
let albumArtistName = tracks.map(t => t.collectionArtistName).find(Boolean) || artist;
|
||||||
|
// The JP store localizes Western artists to katakana ("DJスネイク"). If iTunes' album artist is
|
||||||
|
// Japanese but the request artist isn't, keep the request's English name (tag + folder).
|
||||||
|
const JP_RE = /[-ヿ㐀-䶿一-鿿]/;
|
||||||
|
if (albumArtistName && artist && JP_RE.test(albumArtistName) && !JP_RE.test(artist)) albumArtistName = artist;
|
||||||
if (albumArtistName !== artist) log(jobId, `👥 Album-Künstler: "${albumArtistName}" (iTunes)`);
|
if (albumArtistName !== artist) log(jobId, `👥 Album-Künstler: "${albumArtistName}" (iTunes)`);
|
||||||
const { done: dlDone, skipped: dlSkipped } = await downloadAlbumTracks({ jobId, artist: albumArtistName, albumTitle: album || title, tracks, artworkUrl });
|
const { done: dlDone, skipped: dlSkipped } = await downloadAlbumTracks({ jobId, artist: albumArtistName, albumTitle: album || title, tracks, artworkUrl });
|
||||||
const allSkipped = dlSkipped === tracks.length;
|
const allSkipped = dlSkipped === tracks.length;
|
||||||
@@ -655,7 +659,10 @@ app.post('/api/download', async (req, res) => {
|
|||||||
if (tracks.length) {
|
if (tracks.length) {
|
||||||
// Same as the album path: album_artist from iTunes' album-level artist, so a collaboration
|
// Same as the album path: album_artist from iTunes' album-level artist, so a collaboration
|
||||||
// or soundtrack in the artist's discography isn't tagged with the searched artist.
|
// or soundtrack in the artist's discography isn't tagged with the searched artist.
|
||||||
const albArtist = tracks.map(t => t.collectionArtistName).find(Boolean) || alb.artistName || artist;
|
let albArtist = tracks.map(t => t.collectionArtistName).find(Boolean) || alb.artistName || artist;
|
||||||
|
// Don't let the JP store's katakana album-artist override a non-Japanese searched artist.
|
||||||
|
const JP_RE = /[-ヿ㐀-䶿一-鿿]/;
|
||||||
|
if (albArtist && artist && JP_RE.test(albArtist) && !JP_RE.test(artist)) albArtist = artist;
|
||||||
await downloadAlbumTracks({ jobId, artist: albArtist, albumTitle: alb.collectionName, tracks, artworkUrl: alb.artworkUrl100 });
|
await downloadAlbumTracks({ jobId, artist: albArtist, albumTitle: alb.collectionName, tracks, artworkUrl: alb.artworkUrl100 });
|
||||||
}
|
}
|
||||||
done++;
|
done++;
|
||||||
|
|||||||
Reference in new issue
Block a user