fix: owned albums wrongly shown as "Unvollständig 1/0" on artist page

Deezer's /artist/{id}/albums list omits nb_tracks, so the album total read as 0 and every
owned album (e.g. N!GHT's "Moonlight"/"Yume") was classified as incomplete "1 / 0 Tracks".

- Web (detail.js): resolve the real Deezer track count via the album endpoint for MATCHED
  (owned) albums only — a couple of calls, not the whole discography — and treat an unknown
  total as complete. Genuine partials still work when the true count is known.
- Android (categorizeAlbums): mark partial only when a known total exceeds what you own;
  unknown total counts as complete (never a false "1 / 0").
- CHANGELOG: documented the fix; corrected the Android lyrics known bug (auto-scroll follows
  playback but isn't smooth — it jumps rather than glides). SW ns-v55.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Opus 4.8 committed 2026-07-31 17:05:14 +02:00
1 parent 5528947a93
commit 640e475c51
4 files changed
+41 -20

No files matched your search

+30 -15
View File
@@ -282,36 +282,51 @@ export async function loadArtistDetail(artist) {
actionBar.appendChild(evBtn);
}
if(albEl) { albEl.innerHTML = ''; _renderArtistAlbums(albEl, ndAlbums, iAlbums, artist); }
if(albEl) _renderArtistAlbums(albEl, ndAlbums, iAlbums, artist);
}
function _normAlbum(s) {
return (s || '').toLowerCase().replace(/[^a-z0-9\s]/g, ' ').replace(/\s+/g, ' ').trim();
}
function _renderArtistAlbums(el, ndAlbums, iAlbums, artist) {
const downloaded = [], partial = [], notDl = [];
const usedNdIds = new Set();
async function _renderArtistAlbums(el, ndAlbums, iAlbums, artist) {
el.innerHTML = '<div class="spinner"></div>';
for(const ia of iAlbums) {
const iName = _normAlbum(ia.collectionName);
const iCount = ia.trackCount || 0;
const match = ndAlbums.find(a => {
// Match each Deezer album to a Navidrome album by normalized name.
const entries = iAlbums.map(ia => {
const iName = _normAlbum(ia.collectionName);
const match = ndAlbums.find(a => {
const n = _normAlbum(a.name);
return n === iName || (n.length > 3 && iName.startsWith(n)) || (iName.length > 3 && n.startsWith(iName));
});
if(!match) {
notDl.push(ia);
} else if(iCount > 0 && match.songCount >= iCount) {
downloaded.push({ nd: match, itunes: ia }); usedNdIds.add(match.id);
} else {
partial.push({ nd: match, itunes: ia, have: match.songCount || 0, total: iCount }); usedNdIds.add(match.id);
}
return { ia, match };
});
// Deezer's /artist/{id}/albums list omits nb_tracks, so the total is unknown here — which made every
// owned album look "incomplete" (e.g. "Moonlight 1 / 0"). Resolve the real track count from the
// album endpoint, but ONLY for matched (owned) albums, so it's a couple of calls, not the whole
// discography. An unknown total is then treated as complete, never a false "Unvollständig".
await Promise.all(entries
.filter(e => e.match && !(e.ia.trackCount > 0) && e.ia.collectionId)
.map(async e => {
const tracks = await itunesLookupAlbum(e.ia.collectionId).catch(() => []);
if (tracks.length) e.ia = { ...e.ia, trackCount: tracks.length };
}));
const downloaded = [], partial = [], notDl = [];
const usedNdIds = new Set();
for(const { ia, match } of entries) {
if(!match) { notDl.push(ia); continue; }
const have = match.songCount || 0;
const total = ia.trackCount || 0;
if(total > have) { partial.push({ nd: match, itunes: ia, have, total }); usedNdIds.add(match.id); }
else { downloaded.push({ nd: match, itunes: ia }); usedNdIds.add(match.id); }
}
for(const na of ndAlbums) {
if(!usedNdIds.has(na.id)) downloaded.push({ nd: na, itunes: null });
}
el.innerHTML = '';
downloaded.sort((a, b) => (b.nd.year||0) - (a.nd.year||0));
partial.sort((a, b) => (b.nd.year||0) - (a.nd.year||0));
notDl.sort((a, b) => new Date(b.releaseDate||0) - new Date(a.releaseDate||0));