// Show or hide the offline status banner. function setOfflineBannerVisible(visible, message) { if (window.__pulseThumbnailPreview) { return; } var normalizedVisible = Boolean(visible); var bannerMessage = String(message || 'Offline mode: using cached playlist.').trim(); if (normalizedVisible) { if (!offlineBanner) { offlineBanner = document.createElement('div'); offlineBanner.className = 'player-offline-banner'; offlineBanner.style.position = 'fixed'; offlineBanner.style.right = '0'; offlineBanner.style.bottom = '0'; offlineBanner.style.left = 'auto'; offlineBanner.style.top = 'auto'; offlineBanner.style.width = '1.25rem'; offlineBanner.style.height = '1.25rem'; offlineBanner.style.zIndex = '9999'; offlineBanner.style.background = 'linear-gradient(135deg, #ff4d4f 0%, #b00020 100%)'; offlineBanner.style.clipPath = 'circle(100% at 100% 100%)'; offlineBanner.style.webkitClipPath = 'circle(100% at 100% 100%)'; offlineBanner.style.boxShadow = '0 0 0 1px rgba(0, 0, 0, 0.16), 0 4px 12px rgba(0, 0, 0, 0.18)'; offlineBanner.style.pointerEvents = 'none'; document.body.appendChild(offlineBanner); } offlineBanner.textContent = ''; offlineBanner.setAttribute('aria-label', bannerMessage); offlineBanner.setAttribute('role', 'img'); offlineBanner.title = bannerMessage; offlineBannerVisible = true; return; } offlineBannerVisible = false; if (offlineBanner && offlineBanner.parentNode) { offlineBanner.parentNode.removeChild(offlineBanner); } offlineBanner = null; } // Update the offline banner based on connectivity or playlist availability. function syncOfflineBanner() { if (window.__pulseThumbnailPreview) { return; } if (!window.navigator.onLine) { setOfflineBannerVisible(true, 'Offline mode: using cached playlist.'); return; } if (offlineBannerVisible) { setOfflineBannerVisible(false); } } // Clear any pending playlist refresh retry. function clearRefreshRetry() { if (refreshRetryTimer) { window.clearTimeout(refreshRetryTimer); refreshRetryTimer = null; } } // Retry playlist refresh with a short backoff while the player is offline. function scheduleRefreshRetry() { if (window.__pulseThumbnailPreview) { return; } if (refreshRetryTimer) { return; } if (window.navigator.onLine === false) { refreshRetryDelayMs = refreshRetryDelayMs ? Math.min(refreshRetryDelayMs * 2, 15000) : 3000; } else { refreshRetryDelayMs = refreshRetryDelayMs ? Math.min(refreshRetryDelayMs * 2, 8000) : 3000; } refreshRetryTimer = window.setTimeout(function () { refreshRetryTimer = null; refresh(); }, refreshRetryDelayMs); } function clearScreenWakeLockRetry() { if (screenWakeLockRetryTimer) { window.clearTimeout(screenWakeLockRetryTimer); screenWakeLockRetryTimer = null; } } function supportsScreenWakeLock() { return Boolean(window.navigator && window.navigator.wakeLock && typeof window.navigator.wakeLock.request === 'function'); } function scheduleScreenWakeLockRetry() { if (screenWakeLockRetryTimer) { return; } if (!supportsScreenWakeLock() || document.visibilityState !== 'visible') { return; } screenWakeLockRetryTimer = window.setTimeout(function () { screenWakeLockRetryTimer = null; acquireScreenWakeLock(); }, 2000); } function releaseScreenWakeLock() { if (screenWakeLock && typeof screenWakeLock.release === 'function') { try { screenWakeLock.release(); } catch (_error) { // ignore wake lock release errors } } screenWakeLock = null; screenWakeLockRequestPromise = null; clearScreenWakeLockRetry(); } function acquireScreenWakeLock() { if (!supportsScreenWakeLock() || document.visibilityState !== 'visible') { return Promise.resolve(null); } if (screenWakeLockRequestPromise) { return screenWakeLockRequestPromise; } if (screenWakeLock && screenWakeLock.released === false) { return Promise.resolve(screenWakeLock); } screenWakeLockRequestPromise = window.navigator.wakeLock.request('screen').then(function (sentinel) { screenWakeLock = sentinel; screenWakeLock.addEventListener('release', function () { screenWakeLock = null; if (document.visibilityState === 'visible') { scheduleScreenWakeLockRetry(); } }); clearScreenWakeLockRetry(); return screenWakeLock; }).catch(function (error) { screenWakeLock = null; if (error && error.name !== 'NotAllowedError') { scheduleScreenWakeLockRetry(); } return null; }).finally(function () { screenWakeLockRequestPromise = null; }); return screenWakeLockRequestPromise; } function syncScreenWakeLock() { if (!supportsScreenWakeLock()) { return; } if (document.visibilityState === 'visible') { acquireScreenWakeLock(); return; } releaseScreenWakeLock(); } // Clear the retry cadence after a successful refresh. function markRefreshHealthy() { refreshRetryDelayMs = 0; clearRefreshRetry(); }