fix: auto-lookup album metadata from iTunes when missing on track download
Songs from new releases (Apple RSS feed) have no collectionName, causing the album tag to be omitted from embedded metadata -> Navidrome shows [Unknown Album]. Backend now calls iTunes search to fill in album, year, trackNum, totalTracks, discNum, genre before downloading. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
f5e0af6e90
commit
5987e1f303
1 file changed
+37
-1
@@ -130,6 +130,35 @@ async function fetchLyrics(artist, title, album) {
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
// iTunes track lookup — fills in album, year, trackNum, totalTracks, genre when missing
|
||||
async function lookupItunesTrackMeta(artist, title) {
|
||||
try {
|
||||
const url = `https://itunes.apple.com/search?term=${encodeURIComponent(artist + ' ' + title)}&media=music&entity=musicTrack&limit=5&country=us&explicit=Yes`;
|
||||
const results = await new Promise((resolve, reject) => {
|
||||
https.get(url, res => {
|
||||
let d = ''; res.on('data', c => d += c);
|
||||
res.on('end', () => { try { resolve(JSON.parse(d).results || []); } catch { reject(new Error('parse')); } });
|
||||
}).on('error', reject);
|
||||
});
|
||||
const norm = s => s?.toLowerCase().trim() || '';
|
||||
const match = results.find(t =>
|
||||
norm(t.trackName) === norm(title) &&
|
||||
norm(t.artistName).split(' ')[0] === norm(artist).split(' ')[0]
|
||||
) || results[0];
|
||||
if (!match) return {};
|
||||
return {
|
||||
album: match.collectionName || '',
|
||||
year: match.releaseDate ? new Date(match.releaseDate).getFullYear() : undefined,
|
||||
trackNum: match.trackNumber || undefined,
|
||||
totalTracks: match.trackCount || undefined,
|
||||
discNum: match.discNumber || undefined,
|
||||
discTotal: match.discCount || undefined,
|
||||
genre: match.primaryGenreName || undefined,
|
||||
artworkUrl: (match.artworkUrl100 || '').replace('100x100bb', '1000x1000bb'),
|
||||
};
|
||||
} catch { return {}; }
|
||||
}
|
||||
|
||||
// Download a single track: yt-dlp audio + ffmpeg clean metadata + iTunes cover embedded
|
||||
async function downloadTrack({ jobId, artist, albumArtist, title, album, year, trackNum, totalTracks, discNum, discTotal, compilation, genre, artworkUrl, explicit, outputDir }) {
|
||||
const mainArtist = albumArtist || artist;
|
||||
@@ -380,7 +409,14 @@ app.post('/api/download', async (req, res) => {
|
||||
updateJob(jobId, { status: 'running', progress: 5, message: 'Gestartet…' });
|
||||
try {
|
||||
if (type === 'track') {
|
||||
const result = await downloadTrack({ jobId, artist, title, album, artworkUrl, explicit: !!explicit });
|
||||
let meta = { album, year: undefined, trackNum: undefined, totalTracks: undefined, discNum: undefined, discTotal: undefined, genre: undefined };
|
||||
if (!album) {
|
||||
log(jobId, '📋 Suche Album-Metadaten von iTunes…');
|
||||
const extra = await lookupItunesTrackMeta(artist, title);
|
||||
if (extra.album) log(jobId, `📋 Album: "${extra.album}" (${extra.year || '?'})`);
|
||||
meta = { ...extra, artworkUrl: artworkUrl || extra.artworkUrl || '' };
|
||||
}
|
||||
const result = await downloadTrack({ jobId, artist, title, artworkUrl: artworkUrl || meta.artworkUrl || '', explicit: !!explicit, ...meta });
|
||||
if (result?.skipped) {
|
||||
updateJob(jobId, { status: 'done', progress: 100, message: 'Bereits vorhanden – übersprungen', skipped: true });
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user