Wire Amazon Music enrichment worker into dashboard UI
Adds full parity with Deezer/Qobuz/Tidal/Discogs in every dashboard UI layer — orb button, live tooltip, WebSocket push, rate speedometer. - webui/index.html: Amazon enrichment orb button after Discogs - webui/static/amazon.svg: local icon (a + smile, same pattern as hydrabase.png — avoids external URL dependency) - webui/static/style.css: Amazon button/spinner/tooltip CSS with FF9900 brand color; added to mobile tooltip suppress list - webui/static/worker-orbs.js: Amazon orb in WORKER_DEFS [255,153,0] - webui/static/api-monitor.js: Amazon in rate gauge services list, label, and color map - webui/static/enrichment.js: updateAmazonEnrichmentStatusFromData, toggleAmazonEnrichment, DOMContentLoaded init + 2s poll - webui/static/core.js: socket.on enrichment:amazon-enrichment listener - web_server.py: amazon-enrichment added to _emit_enrichment_status_loop workers dict so WebSocket pushes fire every 2s
This commit is contained in:
parent
4fce832ae1
commit
5450f4ac5e
8 changed files with 320 additions and 1 deletions
|
|
@ -33898,6 +33898,7 @@ def _emit_enrichment_status_loop():
|
|||
'genius-enrichment': lambda: genius_worker,
|
||||
'tidal-enrichment': lambda: tidal_enrichment_worker,
|
||||
'qobuz-enrichment': lambda: qobuz_enrichment_worker,
|
||||
'amazon-enrichment': lambda: amazon_worker,
|
||||
'hydrabase': lambda: hydrabase_worker,
|
||||
'soulid': lambda: soulid_worker,
|
||||
'listening-stats': lambda: listening_stats_worker,
|
||||
|
|
|
|||
|
|
@ -541,6 +541,29 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Amazon Music Enrichment Status Icon -->
|
||||
<div class="amazon-enrich-button-container">
|
||||
<button class="amazon-enrich-button" id="amazon-enrich-button" title="Amazon Music Library Enrichment"
|
||||
onclick="toggleAmazonEnrichment()">
|
||||
<img src="/static/amazon.svg"
|
||||
alt="Amazon Music" class="amazon-enrich-logo">
|
||||
<div class="amazon-enrich-spinner"></div>
|
||||
</button>
|
||||
<div class="amazon-enrich-tooltip" id="amazon-enrich-tooltip">
|
||||
<div class="amazon-enrich-tooltip-content">
|
||||
<div class="amazon-enrich-tooltip-header">Amazon Music Enrichment</div>
|
||||
<div class="amazon-enrich-tooltip-body" id="amazon-enrich-tooltip-body">
|
||||
<div class="tooltip-status">Status: <span
|
||||
id="amazon-enrich-tooltip-status">Idle</span>
|
||||
</div>
|
||||
<div class="tooltip-current" id="amazon-enrich-tooltip-current">No active matches
|
||||
</div>
|
||||
<div class="tooltip-progress" id="amazon-enrich-tooltip-progress">Progress: 0 / 0
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Hydrabase P2P Mirror Status Icon -->
|
||||
<div class="hydrabase-button-container" id="hydrabase-button-container" style="display: none;">
|
||||
<button class="hydrabase-button" id="hydrabase-button" title="Hydrabase P2P Mirror">
|
||||
|
|
|
|||
5
webui/static/amazon.svg
Normal file
5
webui/static/amazon.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black">
|
||||
<text x="12" y="13" text-anchor="middle" font-family="Arial,sans-serif" font-size="11" font-weight="bold" fill="black">a</text>
|
||||
<path d="M4.5 17 Q12 21 19.5 17" stroke="black" stroke-width="1.5" fill="none" stroke-linecap="round"/>
|
||||
<path d="M17.2 15 L19.5 17 L17.2 19" fill="none" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 451 B |
|
|
@ -4,17 +4,19 @@
|
|||
const _rateMonitorState = {};
|
||||
const _RATE_GAUGE_SERVICES = [
|
||||
'spotify', 'itunes', 'deezer', 'lastfm', 'genius',
|
||||
'musicbrainz', 'audiodb', 'tidal', 'qobuz', 'discogs',
|
||||
'musicbrainz', 'audiodb', 'tidal', 'qobuz', 'discogs', 'amazon',
|
||||
];
|
||||
const _RATE_GAUGE_LABELS = {
|
||||
spotify: 'Spotify', itunes: 'Apple Music', deezer: 'Deezer',
|
||||
lastfm: 'Last.fm', genius: 'Genius', musicbrainz: 'MusicBrainz',
|
||||
audiodb: 'AudioDB', tidal: 'Tidal', qobuz: 'Qobuz', discogs: 'Discogs',
|
||||
amazon: 'Amazon Music',
|
||||
};
|
||||
const _RATE_GAUGE_COLORS = {
|
||||
spotify: '#1DB954', itunes: '#FC3C44', deezer: '#A238FF',
|
||||
lastfm: '#D51007', genius: '#FFFF64', musicbrainz: '#BA478F',
|
||||
audiodb: '#00BCD4', tidal: '#00FFFF', qobuz: '#FF6B35', discogs: '#D4A574',
|
||||
amazon: '#FF9900',
|
||||
};
|
||||
|
||||
// SVG constants — 240° arc, gap at bottom
|
||||
|
|
|
|||
|
|
@ -446,6 +446,7 @@ function initializeWebSocket() {
|
|||
socket.on('enrichment:genius-enrichment', (data) => updateGeniusEnrichmentStatusFromData(data));
|
||||
socket.on('enrichment:tidal-enrichment', (data) => updateTidalEnrichmentStatusFromData(data));
|
||||
socket.on('enrichment:qobuz-enrichment', (data) => updateQobuzEnrichmentStatusFromData(data));
|
||||
socket.on('enrichment:amazon-enrichment', (data) => updateAmazonEnrichmentStatusFromData(data));
|
||||
socket.on('enrichment:hydrabase', (data) => updateHydrabaseStatusFromData(data));
|
||||
socket.on('enrichment:repair', (data) => updateRepairStatusFromData(data));
|
||||
socket.on('enrichment:soulid', (data) => updateSoulIDStatusFromData(data));
|
||||
|
|
|
|||
|
|
@ -1205,6 +1205,112 @@ if (document.readyState === 'loading') {
|
|||
}
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// AMAZON MUSIC ENRICHMENT WORKER
|
||||
// ===================================================================
|
||||
|
||||
async function updateAmazonEnrichmentStatus() {
|
||||
if (socketConnected) return;
|
||||
if (document.hidden) return;
|
||||
try {
|
||||
const response = await fetch('/api/enrichment/amazon/status');
|
||||
if (!response.ok) { console.warn('Amazon enrichment status endpoint unavailable'); return; }
|
||||
const data = await response.json();
|
||||
updateAmazonEnrichmentStatusFromData(data);
|
||||
} catch (error) {
|
||||
console.error('Error updating Amazon enrichment status:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function updateAmazonEnrichmentStatusFromData(data) {
|
||||
const button = document.getElementById('amazon-enrich-button');
|
||||
if (!button) return;
|
||||
|
||||
button.classList.remove('active', 'paused', 'complete');
|
||||
if (data.paused) {
|
||||
button.classList.add('paused');
|
||||
} else if (data.idle) {
|
||||
button.classList.add('complete');
|
||||
} else if (data.running && !data.paused) {
|
||||
button.classList.add('active');
|
||||
}
|
||||
|
||||
const tooltipStatus = document.getElementById('amazon-enrich-tooltip-status');
|
||||
const tooltipCurrent = document.getElementById('amazon-enrich-tooltip-current');
|
||||
const tooltipProgress = document.getElementById('amazon-enrich-tooltip-progress');
|
||||
|
||||
if (tooltipStatus) {
|
||||
if (data.paused) { tooltipStatus.textContent = data.yield_reason === 'downloads' ? 'Yielding for downloads' : 'Paused'; }
|
||||
else if (data.idle) { tooltipStatus.textContent = 'Complete'; }
|
||||
else if (data.running) { tooltipStatus.textContent = 'Running'; }
|
||||
else { tooltipStatus.textContent = 'Idle'; }
|
||||
}
|
||||
|
||||
if (tooltipCurrent) {
|
||||
if (data.idle) {
|
||||
tooltipCurrent.textContent = 'All items processed';
|
||||
} else if (data.current_item && data.current_item.name) {
|
||||
tooltipCurrent.textContent = `Now: ${data.current_item.name}`;
|
||||
} else {
|
||||
tooltipCurrent.textContent = 'No active matches';
|
||||
}
|
||||
}
|
||||
|
||||
if (data.progress && tooltipProgress) {
|
||||
const artists = data.progress.artists || {};
|
||||
const albums = data.progress.albums || {};
|
||||
const tracks = data.progress.tracks || {};
|
||||
const currentType = data.current_item?.type;
|
||||
let progressText = '';
|
||||
const artistsComplete = artists.matched >= artists.total;
|
||||
const albumsComplete = albums.matched >= albums.total;
|
||||
if (currentType === 'artist' || (!artistsComplete && !currentType)) {
|
||||
progressText = `Artists: ${artists.matched || 0} / ${artists.total || 0} (${artists.percent || 0}%)`;
|
||||
} else if (currentType === 'album' || (!albumsComplete && !currentType)) {
|
||||
progressText = `Albums: ${albums.matched || 0} / ${albums.total || 0} (${albums.percent || 0}%)`;
|
||||
} else {
|
||||
progressText = `Tracks: ${tracks.matched || 0} / ${tracks.total || 0} (${tracks.percent || 0}%)`;
|
||||
}
|
||||
tooltipProgress.textContent = progressText;
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleAmazonEnrichment() {
|
||||
try {
|
||||
const button = document.getElementById('amazon-enrich-button');
|
||||
if (!button) return;
|
||||
const isRunning = button.classList.contains('active');
|
||||
const endpoint = isRunning ? '/api/enrichment/amazon/pause' : '/api/enrichment/amazon/resume';
|
||||
const response = await fetch(endpoint, { method: 'POST' });
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to ${isRunning ? 'pause' : 'resume'} Amazon enrichment`);
|
||||
}
|
||||
await updateAmazonEnrichmentStatus();
|
||||
console.log(`Amazon enrichment ${isRunning ? 'paused' : 'resumed'}`);
|
||||
} catch (error) {
|
||||
console.error('Error toggling Amazon enrichment:', error);
|
||||
showToast(`Error: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const button = document.getElementById('amazon-enrich-button');
|
||||
if (button) {
|
||||
button.addEventListener('click', toggleAmazonEnrichment);
|
||||
updateAmazonEnrichmentStatus();
|
||||
setInterval(updateAmazonEnrichmentStatus, 2000);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const button = document.getElementById('amazon-enrich-button');
|
||||
if (button) {
|
||||
button.addEventListener('click', toggleAmazonEnrichment);
|
||||
updateAmazonEnrichmentStatus();
|
||||
setInterval(updateAmazonEnrichmentStatus, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// HYDRABASE P2P MIRROR WORKER
|
||||
// ===================================================================
|
||||
|
|
|
|||
|
|
@ -9648,6 +9648,7 @@ body.helper-mode-active #dashboard-activity-feed:hover {
|
|||
.tidal-enrich-tooltip,
|
||||
.qobuz-enrich-tooltip,
|
||||
.discogs-tooltip,
|
||||
.amazon-enrich-tooltip,
|
||||
.hydrabase-tooltip,
|
||||
.repair-tooltip,
|
||||
.soulid-tooltip {
|
||||
|
|
@ -36888,6 +36889,185 @@ div.artist-hero-badge {
|
|||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
AMAZON MUSIC ENRICHMENT WORKER BUTTON
|
||||
============================================================ */
|
||||
|
||||
.amazon-enrich-button-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.amazon-enrich-button {
|
||||
position: relative;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 153, 0, 0.10) 0%,
|
||||
rgba(255, 120, 0, 0.16) 100%);
|
||||
backdrop-filter: blur(20px) saturate(1.4);
|
||||
-webkit-backdrop-filter: blur(20px) saturate(1.4);
|
||||
border: 1.5px solid rgba(255, 153, 0, 0.22);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow:
|
||||
0 4px 16px rgba(255, 153, 0, 0.12),
|
||||
0 2px 8px rgba(0, 0, 0, 0.15),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.amazon-enrich-logo {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
opacity: 0.6;
|
||||
filter: brightness(0) invert(1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.amazon-enrich-button:hover {
|
||||
transform: scale(1.1);
|
||||
border-color: rgba(255, 153, 0, 0.45);
|
||||
box-shadow:
|
||||
0 6px 24px rgba(255, 153, 0, 0.28),
|
||||
0 3px 12px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.amazon-enrich-spinner {
|
||||
position: absolute;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: rgba(255, 153, 0, 0.85);
|
||||
border-right-color: rgba(255, 153, 0, 0.5);
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes amazon-enrich-spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.amazon-enrich-button.active .amazon-enrich-spinner {
|
||||
opacity: 1;
|
||||
animation: amazon-enrich-spin 1.2s linear infinite;
|
||||
}
|
||||
|
||||
.amazon-enrich-button.active {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 153, 0, 0.20) 0%,
|
||||
rgba(255, 100, 0, 0.26) 100%);
|
||||
border-color: rgba(255, 153, 0, 0.55);
|
||||
box-shadow:
|
||||
0 6px 24px rgba(255, 153, 0, 0.35),
|
||||
0 3px 12px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.amazon-enrich-button.active .amazon-enrich-logo {
|
||||
opacity: 1;
|
||||
filter: brightness(0) invert(1) drop-shadow(0 0 6px rgba(255, 180, 50, 0.5));
|
||||
}
|
||||
|
||||
.amazon-enrich-button.complete {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(76, 175, 80, 0.15) 0%,
|
||||
rgba(56, 142, 60, 0.20) 100%);
|
||||
border-color: rgba(76, 175, 80, 0.35);
|
||||
box-shadow:
|
||||
0 4px 16px rgba(76, 175, 80, 0.2),
|
||||
0 2px 8px rgba(0, 0, 0, 0.15),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.amazon-enrich-button.complete .amazon-enrich-logo {
|
||||
opacity: 0.8;
|
||||
filter: brightness(0) invert(0.6) sepia(1) saturate(3) hue-rotate(80deg);
|
||||
}
|
||||
|
||||
.amazon-enrich-button.complete .amazon-enrich-spinner {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.amazon-enrich-button.paused {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 193, 7, 0.12) 0%,
|
||||
rgba(255, 152, 0, 0.18) 100%);
|
||||
border-color: rgba(255, 193, 7, 0.35);
|
||||
box-shadow:
|
||||
0 4px 16px rgba(255, 193, 7, 0.2),
|
||||
0 2px 8px rgba(0, 0, 0, 0.15),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.amazon-enrich-button.paused .amazon-enrich-logo {
|
||||
opacity: 0.5;
|
||||
filter: brightness(0) invert(0.6) sepia(1) saturate(5) hue-rotate(15deg);
|
||||
}
|
||||
|
||||
.amazon-enrich-button.paused .amazon-enrich-spinner {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.amazon-enrich-tooltip {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: calc(100% + 12px);
|
||||
transform: translateX(-50%) translateY(-5px);
|
||||
z-index: 5000;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.amazon-enrich-button:hover + .amazon-enrich-tooltip {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
|
||||
.amazon-enrich-tooltip-content {
|
||||
min-width: 260px;
|
||||
background: linear-gradient(135deg, rgba(30, 30, 30, 0.98) 0%, rgba(20, 20, 20, 0.99) 100%);
|
||||
backdrop-filter: blur(40px) saturate(1.6);
|
||||
-webkit-backdrop-filter: blur(40px) saturate(1.6);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 16px;
|
||||
padding: 16px 18px;
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5), 0 6px 20px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.amazon-enrich-tooltip-header {
|
||||
font-family: 'SF Pro Display', -apple-system, sans-serif;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
letter-spacing: -0.2px;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.amazon-enrich-tooltip-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
#amazon-enrich-tooltip-status {
|
||||
color: #FF9900;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.deezer-button-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
{ container: '.tidal-enrich-button-container', color: [180, 180, 255] },
|
||||
{ container: '.qobuz-enrich-button-container', color: [1, 112, 239] },
|
||||
{ container: '.discogs-button-container', color: [180, 180, 180] },
|
||||
{ container: '.amazon-enrich-button-container', color: [255, 153, 0] },
|
||||
{ container: '.hydrabase-button-container', color: [200, 200, 200] },
|
||||
{ container: '.soulid-button-container', color: [29, 185, 84], rainbow: true },
|
||||
{ container: '.repair-button-container', color: [180, 130, 255], rainbow: true },
|
||||
|
|
|
|||
Loading…
Reference in a new issue