(() => { const root = document.getElementById("now-playing-root"); const initial = JSON.parse(root?.dataset.state || "{}"); const token = root?.dataset.token || ""; let state = initial; let card = null; let hideTimer = null; let progressTimer = null; let lastTrackKey = ""; let lastConfigSignature = ""; let refreshRequest = null; let fallbackTimer = null; const escapeText = (value) => String(value ?? ""); const formatTime = (milliseconds) => { const seconds = Math.max(0, Math.floor(Number(milliseconds || 0) / 1000)); const minutes = Math.floor(seconds / 60); return `${minutes}:${String(seconds % 60).padStart(2, "0")}`; }; const projectedPosition = () => { const playback = state.playback; if (!playback) return 0; let value = Number(playback.position_ms || 0); if (playback.status === "playing") value += Math.max(0, Date.now() - Number(playback.reported_at || Date.now())) * Number(playback.rate || 1); return Math.min(Number(playback.duration_ms || value), value); }; function backgroundColors(config, track) { const coverBased = String(config.background_mode || "").startsWith("cover-"); return { primary: coverBased ? track.color_primary : config.custom_color_primary, secondary: coverBased ? track.color_secondary : config.custom_color_secondary, solid: String(config.background_mode || "").endsWith("-solid") }; } function buildCard() { const config = state.config || {}; const track = state.track || {}; const colors = backgroundColors(config, track); const element = document.createElement("article"); element.className = `np-card scope-${String(config.transparency_scope || "background").replaceAll("_", "-")}${colors.solid ? " background-solid" : ""}`; element.dataset.animation = config.entry_animation || "fade"; element.style.setProperty("--np-primary", colors.primary || "#1f2937"); element.style.setProperty("--np-secondary", colors.secondary || colors.primary || "#111827"); element.style.setProperty("--np-opacity", String(config.opacity ?? .92)); element.style.setProperty("--np-radius", `${Number(config.corner_radius || 18)}px`); element.style.setProperty("--np-scale", String(config.scale || 1)); element.style.setProperty("--np-duration", `${Number(config.animation_ms || 450)}ms`); if (config.show_cover && track.cover_url) { const cover = document.createElement("img"); cover.className = "np-cover"; cover.src = track.cover_url; cover.alt = ""; element.appendChild(cover); } const content = document.createElement("div"); content.className = "np-content"; if (config.show_provider && track.provider) { const provider = document.createElement("span"); provider.className = "np-provider"; provider.textContent = escapeText(track.provider); content.appendChild(provider); } if (config.show_title) { const title = document.createElement("strong"); title.className = "np-title"; title.textContent = escapeText(track.name || "Unknown song"); content.appendChild(title); } if (config.show_artist && track.artist) { const artist = document.createElement("span"); artist.className = "np-artist"; artist.textContent = escapeText(track.artist); content.appendChild(artist); } if ((config.show_album && track.album) || (config.show_release_year && track.release_year)) { const meta = document.createElement("div"); meta.className = "np-meta"; if (config.show_album && track.album) { const album = document.createElement("span"); album.textContent = escapeText(track.album); meta.appendChild(album); } if (config.show_release_year && track.release_year) { const year = document.createElement("span"); year.textContent = escapeText(track.release_year); meta.appendChild(year); } content.appendChild(meta); } if (config.show_timeline && Number(state.playback?.duration_ms || 0) > 0) { const timeline = document.createElement("div"); timeline.className = "np-timeline"; timeline.innerHTML = '0:00