Enrich downloads page cards with artwork, artist, album, and source badges

The downloads page previously showed only title and source per download.
Now shows album artwork thumbnail, artist name, album name, source badge,
and quality badge (after post-processing). All metadata comes from the
existing matched_downloads_context — no extra API calls needed.

Falls back gracefully to title-only display when context metadata is
not available (e.g. orphaned Soulseek transfers with no task mapping).
This commit is contained in:
Broque Thomas 2026-04-20 22:18:20 -07:00
parent ed97fecc31
commit 9c15d00128
3 changed files with 107 additions and 3 deletions

View file

@ -10119,6 +10119,24 @@ def get_download_status():
print(f"Could not fetch YouTube/Tidal downloads for status: {streaming_error}")
traceback.print_exc()
# Enrich transfers with metadata from download context (artist, album, artwork)
with matched_context_lock:
for transfer in all_transfers:
ctx_key = _make_context_key(transfer.get('username', ''), transfer.get('filename', ''))
ctx = matched_downloads_context.get(ctx_key)
if ctx:
_sp_artist = ctx.get('spotify_artist') or {}
_sp_album = ctx.get('spotify_album') or {}
_sp_track = ctx.get('track_info') or {}
_sp_images = _sp_album.get('images') or []
transfer['_meta'] = {
'artist': _sp_artist.get('name', ''),
'album': _sp_album.get('name', ''),
'artwork_url': _sp_images[0].get('url', '') if _sp_images and isinstance(_sp_images[0], dict) else '',
'track_number': _sp_track.get('track_number'),
'quality': ctx.get('_audio_quality', ''),
}
return jsonify({"transfers": all_transfers})
except Exception as e:

View file

@ -17261,11 +17261,31 @@ function renderQueue(containerId, downloads, isActiveQueue) {
`;
}
// Enrich with metadata from backend context (artist, album, artwork)
const meta = item._meta || {};
const sourceLabels = { youtube: 'YouTube', tidal: 'Tidal', qobuz: 'Qobuz', hifi: 'HiFi', deezer_dl: 'Deezer', lidarr: 'Lidarr' };
const sourceBadge = sourceLabels[item.username] || item.username;
html += `
<div class="download-item" data-id="${item.id}">
<div class="download-item__header">
<div class="download-item__art">
${meta.artwork_url
? `<img src="${meta.artwork_url}" alt="" loading="lazy" onerror="this.parentElement.innerHTML='<div class=\\'download-item__art-placeholder\\'>&#9835;</div>'">`
: '<div class="download-item__art-placeholder">&#9835;</div>'}
</div>
<div class="download-item__info">
<div class="download-item__title" title="${title}">${title}</div>
<div class="download-item__uploader" title="from ${item.username}">from ${item.username}</div>
${meta.artist || meta.album ? `
<div class="download-item__meta">
${meta.artist ? `<span>${escapeHtml(meta.artist)}</span>` : ''}
${meta.artist && meta.album ? '<span class="download-item__sep">&middot;</span>' : ''}
${meta.album ? `<span class="download-item__album">${escapeHtml(meta.album)}</span>` : ''}
</div>
` : ''}
<div class="download-item__badges">
<span class="download-item__source">${sourceBadge}</span>
${meta.quality ? `<span class="download-item__quality">${escapeHtml(meta.quality)}</span>` : ''}
</div>
</div>
<div class="download-item__content">
${actionButtonHTML}

View file

@ -6874,6 +6874,9 @@ body.helper-mode-active #dashboard-activity-feed:hover {
margin: 6px 0;
padding: 12px;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 12px;
}
.download-item:hover {
@ -6883,6 +6886,35 @@ body.helper-mode-active #dashboard-activity-feed:hover {
border-color: rgba(var(--accent-rgb), 0.6);
}
.download-item__art {
width: 44px;
height: 44px;
border-radius: 6px;
overflow: hidden;
flex-shrink: 0;
}
.download-item__art img {
width: 100%;
height: 100%;
object-fit: cover;
}
.download-item__art-placeholder {
width: 100%;
height: 100%;
background: rgba(255,255,255,0.05);
display: flex;
align-items: center;
justify-content: center;
color: rgba(255,255,255,0.25);
font-size: 18px;
border-radius: 6px;
}
.download-item__info {
flex: 1;
min-width: 0;
}
.download-item__header {
display: flex;
justify-content: space-between;
@ -6894,12 +6926,45 @@ body.helper-mode-active #dashboard-activity-feed:hover {
font-size: 13px;
font-weight: 600;
color: #ffffff;
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.download-item__meta {
display: flex;
align-items: center;
gap: 0;
font-size: 0.82em;
color: rgba(255,255,255,0.5);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-top: 1px;
}
.download-item__sep {
margin: 0 5px;
opacity: 0.4;
}
.download-item__album {
opacity: 0.7;
}
.download-item__badges {
display: flex;
gap: 6px;
margin-top: 3px;
}
.download-item__source,
.download-item__quality {
font-size: 0.7em;
padding: 1px 7px;
border-radius: 4px;
background: rgba(255,255,255,0.07);
color: rgba(255,255,255,0.45);
font-weight: 500;
}
.download-item__uploader {
font-size: 10px;
padding-left: 15px;
@ -6912,6 +6977,7 @@ body.helper-mode-active #dashboard-activity-feed:hover {
justify-content: center;
flex-direction: row;
gap: 15px;
flex-shrink: 0;
}
/* Progress Bar Styles */