// MusicRoom - UI module // Progress bar, buffer display, and UI state updates (function() { const M = window.MusicRoom; // Create buffer segments on load for (let i = 0; i < M.SEGMENTS; i++) { const seg = document.createElement("div"); seg.className = "segment"; M.$("#buffer-bar").appendChild(seg); } // Update general UI state M.updateUI = function() { const isPlaying = M.synced ? !M.serverPaused : !M.audio.paused; M.$("#btn-sync").classList.toggle("synced", M.wantSync); M.$("#btn-sync").classList.toggle("connected", M.synced); M.$("#btn-sync").title = M.wantSync ? "Unsync" : "Sync"; M.$("#status").textContent = M.synced ? "Synced" : (M.wantSync ? "Connecting..." : "Local"); M.$("#sync-indicator").classList.toggle("visible", M.synced); M.$("#progress-bar").classList.toggle("synced", M.synced); M.$("#progress-bar").classList.toggle("local", !M.synced); M.$("#progress-bar").classList.toggle("muted", M.audio.volume === 0); M.$("#btn-mute").textContent = M.audio.volume === 0 ? "🔇" : "🔊"; M.$("#status-icon").textContent = isPlaying ? "⏸" : "▶"; // Show/hide controls based on permissions const hasControl = M.canControl(); M.$("#status-icon").style.cursor = hasControl || !M.synced ? "pointer" : "default"; }; // Update auth-related UI M.updateAuthUI = function() { if (M.currentUser) { M.$("#login-panel").classList.add("hidden"); M.$("#player-content").classList.add("visible"); if (M.currentUser.isGuest) { M.$("#current-username").textContent = "Guest"; M.$("#btn-logout").textContent = "Sign In"; } else { M.$("#current-username").textContent = M.currentUser.username; M.$("#btn-logout").textContent = "Logout"; } M.$("#admin-badge").style.display = M.currentUser.isAdmin ? "inline" : "none"; } else { M.$("#login-panel").classList.remove("hidden"); M.$("#player-content").classList.remove("visible"); // Pause and unsync when login panel is shown if (!M.audio.paused) { M.localTimestamp = M.audio.currentTime; M.audio.pause(); } if (M.synced && M.ws) { M.synced = false; M.ws.close(); M.ws = null; } // Show guest button if server allows guests if (M.serverStatus?.allowGuests) { M.$("#guest-section").classList.remove("hidden"); } else { M.$("#guest-section").classList.add("hidden"); } } M.updateUI(); }; // Progress bar and buffer update loop (250ms interval) setInterval(() => { if (M.serverTrackDuration <= 0) return; let t, dur; if (M.synced) { t = M.audio.paused ? M.getServerTime() : M.audio.currentTime; dur = M.audio.duration || M.serverTrackDuration; } else { t = M.audio.paused ? M.localTimestamp : M.audio.currentTime; dur = M.audio.duration || M.serverTrackDuration; } const pct = Math.min((t / dur) * 100, 100); if (Math.abs(pct - M.lastProgressPct) > 0.1) { M.$("#progress-bar").style.width = pct + "%"; M.lastProgressPct = pct; } const timeCurrent = M.fmt(t); const timeTotal = M.fmt(dur); if (timeCurrent !== M.lastTimeCurrent) { M.$("#time-current").textContent = timeCurrent; M.lastTimeCurrent = timeCurrent; } if (timeTotal !== M.lastTimeTotal) { M.$("#time-total").textContent = timeTotal; M.lastTimeTotal = timeTotal; } // Update buffer segments const segments = M.$("#buffer-bar").children; const segmentDur = dur / M.SEGMENTS; let availableCount = 0; for (let i = 0; i < M.SEGMENTS; i++) { const segStart = i * segmentDur; const segEnd = (i + 1) * segmentDur; const trackCache = M.getTrackCache(M.currentFilename); let available = trackCache.has(i); // Check our cache first if (!available) { for (let j = 0; j < M.audio.buffered.length; j++) { const bufStart = M.audio.buffered.start(j); const bufEnd = M.audio.buffered.end(j); if (bufStart <= segStart && bufEnd >= segEnd) { available = true; break; } } } if (available) availableCount++; const isAvailable = segments[i].classList.contains("available"); const isLoading = segments[i].classList.contains("loading"); const shouldBeLoading = !available && M.loadingSegments.has(i); if (available !== isAvailable) segments[i].classList.toggle("available", available); if (shouldBeLoading !== isLoading) segments[i].classList.toggle("loading", shouldBeLoading); } // Update download speed display const kbps = M.downloadSpeed > 0 ? M.downloadSpeed * 8 / 1000 : 0; const bufferPct = Math.round(availableCount / M.SEGMENTS * 100); let speedText = ""; if (kbps > 0) { speedText = kbps >= 1024 ? ` | ${(kbps / 1024).toFixed(1)} Mb/s` : ` | ${Math.round(kbps)} kb/s`; } if (bufferPct !== M.lastBufferPct || speedText !== M.lastSpeedText) { M.$("#download-speed").textContent = `${bufferPct}% buffered${speedText}`; M.lastBufferPct = bufferPct; M.lastSpeedText = speedText; } }, 250); // Prefetch loop (1s interval) setInterval(() => { if (M.currentFilename && M.audio.src) { M.prefetchSegments(); } }, 1000); })();