Similar Artists worker: dashboard enrichment bubble
Adds the dashboard status bubble (the small icon row) for the Similar Artists worker, alongside the modal entry. Mirrors the per-source bubbles: MusicMap logo, purple accent, spinner + active/complete/paused states, hover tooltip, and a 2s status poll against /api/enrichment/similar_artists/status. Click toggles pause/resume. Tooltip shows matched/pending (the worker has no artist/album/track phases). 74 JS integrity tests pass.
This commit is contained in:
parent
89e3486e84
commit
d70d410f6f
3 changed files with 165 additions and 0 deletions
|
|
@ -565,6 +565,29 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Similar Artists (MusicMap) Enrichment Status Icon -->
|
||||||
|
<div class="similar-artists-enrich-button-container">
|
||||||
|
<button class="similar-artists-enrich-button" id="similar-artists-enrich-button" title="Similar Artists (MusicMap) Enrichment"
|
||||||
|
onclick="toggleSimilarArtistsEnrichment()">
|
||||||
|
<img src="https://www.music-map.com/elements/objects/og_logo.png"
|
||||||
|
alt="Similar Artists" class="similar-artists-enrich-logo">
|
||||||
|
<div class="similar-artists-enrich-spinner"></div>
|
||||||
|
</button>
|
||||||
|
<div class="similar-artists-enrich-tooltip" id="similar-artists-enrich-tooltip">
|
||||||
|
<div class="similar-artists-enrich-tooltip-content">
|
||||||
|
<div class="similar-artists-enrich-tooltip-header">Similar Artists Enrichment</div>
|
||||||
|
<div class="similar-artists-enrich-tooltip-body" id="similar-artists-enrich-tooltip-body">
|
||||||
|
<div class="tooltip-status">Status: <span
|
||||||
|
id="similar-artists-enrich-tooltip-status">Idle</span>
|
||||||
|
</div>
|
||||||
|
<div class="tooltip-current" id="similar-artists-enrich-tooltip-current">No active matches
|
||||||
|
</div>
|
||||||
|
<div class="tooltip-progress" id="similar-artists-enrich-tooltip-progress">Progress: 0 / 0
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- Hydrabase P2P Mirror Status Icon -->
|
<!-- Hydrabase P2P Mirror Status Icon -->
|
||||||
<div class="hydrabase-button-container" id="hydrabase-button-container" style="display: none;">
|
<div class="hydrabase-button-container" id="hydrabase-button-container" style="display: none;">
|
||||||
<button class="hydrabase-button" id="hydrabase-button" title="Hydrabase P2P Mirror">
|
<button class="hydrabase-button" id="hydrabase-button" title="Hydrabase P2P Mirror">
|
||||||
|
|
|
||||||
|
|
@ -1311,6 +1311,80 @@ if (document.readyState === 'loading') {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// SIMILAR ARTISTS (MUSICMAP) WORKER — dashboard bubble
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
async function updateSimilarArtistsEnrichmentStatus() {
|
||||||
|
if (socketConnected) return;
|
||||||
|
if (document.hidden) return;
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/enrichment/similar_artists/status');
|
||||||
|
if (!response.ok) return;
|
||||||
|
updateSimilarArtistsEnrichmentStatusFromData(await response.json());
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating Similar Artists enrichment status:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSimilarArtistsEnrichmentStatusFromData(data) {
|
||||||
|
const button = document.getElementById('similar-artists-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 tStatus = document.getElementById('similar-artists-enrich-tooltip-status');
|
||||||
|
const tCurrent = document.getElementById('similar-artists-enrich-tooltip-current');
|
||||||
|
const tProgress = document.getElementById('similar-artists-enrich-tooltip-progress');
|
||||||
|
|
||||||
|
if (tStatus) {
|
||||||
|
if (data.paused) tStatus.textContent = 'Paused';
|
||||||
|
else if (data.idle) tStatus.textContent = 'Complete';
|
||||||
|
else if (data.running) tStatus.textContent = 'Running';
|
||||||
|
else tStatus.textContent = 'Idle';
|
||||||
|
}
|
||||||
|
if (tCurrent) {
|
||||||
|
if (data.idle) tCurrent.textContent = 'All library artists processed';
|
||||||
|
else if (data.current_item) tCurrent.textContent = `Now: ${data.current_item}`;
|
||||||
|
else tCurrent.textContent = 'No active matches';
|
||||||
|
}
|
||||||
|
// This worker has no artist/album/track phases — show its matched/pending tally.
|
||||||
|
if (tProgress && data.stats) {
|
||||||
|
const s = data.stats;
|
||||||
|
tProgress.textContent = `${s.matched || 0} matched · ${s.pending || 0} pending`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toggleSimilarArtistsEnrichment() {
|
||||||
|
try {
|
||||||
|
const button = document.getElementById('similar-artists-enrich-button');
|
||||||
|
if (!button) return;
|
||||||
|
const isRunning = button.classList.contains('active');
|
||||||
|
const endpoint = isRunning ? '/api/enrichment/similar_artists/pause' : '/api/enrichment/similar_artists/resume';
|
||||||
|
const response = await fetch(endpoint, { method: 'POST' });
|
||||||
|
if (!response.ok) throw new Error(`Failed to ${isRunning ? 'pause' : 'resume'} Similar Artists enrichment`);
|
||||||
|
await updateSimilarArtistsEnrichmentStatus();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error toggling Similar Artists enrichment:', error);
|
||||||
|
showToast(`Error: ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(function _wireSimilarArtistsBubble() {
|
||||||
|
const wire = () => {
|
||||||
|
const button = document.getElementById('similar-artists-enrich-button');
|
||||||
|
if (button) {
|
||||||
|
updateSimilarArtistsEnrichmentStatus();
|
||||||
|
setInterval(updateSimilarArtistsEnrichmentStatus, 2000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', wire);
|
||||||
|
else wire();
|
||||||
|
})();
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
// HYDRABASE P2P MIRROR WORKER
|
// HYDRABASE P2P MIRROR WORKER
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
|
||||||
|
|
@ -39710,6 +39710,74 @@ div.artist-hero-badge {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Similar Artists (MusicMap relationship worker) dashboard bubble — mirrors the
|
||||||
|
per-source bubbles with a purple accent. The MusicMap logo is a wide branded
|
||||||
|
image, so it's cover-cropped + rounded rather than icon-filtered. */
|
||||||
|
.similar-artists-enrich-button-container { position: relative; display: inline-block; margin-right: 12px; }
|
||||||
|
.similar-artists-enrich-button {
|
||||||
|
position: relative; width: 44px; height: 44px;
|
||||||
|
background: linear-gradient(135deg, rgba(168,85,247,0.10) 0%, rgba(124,58,237,0.16) 100%);
|
||||||
|
backdrop-filter: blur(20px) saturate(1.4); -webkit-backdrop-filter: blur(20px) saturate(1.4);
|
||||||
|
border: 1.5px solid rgba(168,85,247,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(168,85,247,0.12), 0 2px 8px rgba(0,0,0,0.15), inset 0 1px 0 rgba(255,255,255,0.08);
|
||||||
|
}
|
||||||
|
.similar-artists-enrich-logo {
|
||||||
|
width: 26px; height: 26px; opacity: 0.7; object-fit: cover; border-radius: 7px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.similar-artists-enrich-button:hover {
|
||||||
|
transform: scale(1.1); border-color: rgba(168,85,247,0.45);
|
||||||
|
box-shadow: 0 6px 24px rgba(168,85,247,0.28), 0 3px 12px rgba(0,0,0,0.2), inset 0 1px 0 rgba(255,255,255,0.12);
|
||||||
|
}
|
||||||
|
.similar-artists-enrich-spinner {
|
||||||
|
position: absolute; width: 44px; height: 44px; border: 3px solid transparent;
|
||||||
|
border-top-color: rgba(168,85,247,0.85); border-right-color: rgba(168,85,247,0.5);
|
||||||
|
border-radius: 50%; opacity: 0; transition: opacity 0.3s ease; pointer-events: none;
|
||||||
|
}
|
||||||
|
@keyframes similar-artists-enrich-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
||||||
|
.similar-artists-enrich-button.active .similar-artists-enrich-spinner { opacity: 1; animation: similar-artists-enrich-spin 1.2s linear infinite; }
|
||||||
|
.similar-artists-enrich-button.active {
|
||||||
|
background: linear-gradient(135deg, rgba(168,85,247,0.20) 0%, rgba(124,58,237,0.26) 100%);
|
||||||
|
border-color: rgba(168,85,247,0.55);
|
||||||
|
box-shadow: 0 6px 24px rgba(168,85,247,0.35), 0 3px 12px rgba(0,0,0,0.2), inset 0 1px 0 rgba(255,255,255,0.15);
|
||||||
|
}
|
||||||
|
.similar-artists-enrich-button.active .similar-artists-enrich-logo { opacity: 1; }
|
||||||
|
.similar-artists-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);
|
||||||
|
}
|
||||||
|
.similar-artists-enrich-button.complete .similar-artists-enrich-spinner { opacity: 0; }
|
||||||
|
.similar-artists-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);
|
||||||
|
}
|
||||||
|
.similar-artists-enrich-button.paused .similar-artists-enrich-logo { opacity: 0.5; }
|
||||||
|
.similar-artists-enrich-button.paused .similar-artists-enrich-spinner { opacity: 0; }
|
||||||
|
.similar-artists-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;
|
||||||
|
}
|
||||||
|
.similar-artists-enrich-button:hover + .similar-artists-enrich-tooltip { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); }
|
||||||
|
.similar-artists-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);
|
||||||
|
}
|
||||||
|
.similar-artists-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);
|
||||||
|
}
|
||||||
|
.similar-artists-enrich-tooltip-body { display: flex; flex-direction: column; gap: 8px; }
|
||||||
|
#similar-artists-enrich-tooltip-status { color: #c084fc; font-weight: 600; }
|
||||||
|
|
||||||
.deezer-button-container {
|
.deezer-button-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue