diff --git a/public/audioCache.js b/public/audioCache.js index ebccca7..7a30a87 100644 --- a/public/audioCache.js +++ b/public/audioCache.js @@ -4,6 +4,55 @@ (function() { const M = window.MusicRoom; + // Load stream-only preference from localStorage + M.streamOnly = localStorage.getItem("musicroom_streamOnly") === "true"; + + // Toggle stream-only mode + M.setStreamOnly = function(enabled) { + M.streamOnly = enabled; + localStorage.setItem("musicroom_streamOnly", enabled ? "true" : "false"); + M.showToast(enabled ? "Stream-only mode enabled" : "Caching enabled"); + }; + + // Check cache size and prune if over limit + M.checkCacheSize = async function() { + const stats = await TrackStorage.getStats(); + if (stats.totalSize > M.cacheLimit) { + console.log(`[Cache] Size ${(stats.totalSize / 1024 / 1024).toFixed(1)}MB exceeds limit ${(M.cacheLimit / 1024 / 1024).toFixed(0)}MB, pruning...`); + await M.pruneCache(stats.totalSize - M.cacheLimit + (10 * 1024 * 1024)); // Free extra 10MB + } + }; + + // Remove oldest cached tracks to free up space + M.pruneCache = async function(bytesToFree) { + // Get all cached tracks with metadata + const allKeys = await TrackStorage.list(); + if (allKeys.length === 0) return; + + // Get track sizes (we need to fetch each to know size) + const trackSizes = []; + for (const key of allKeys) { + const cached = await TrackStorage.get(key); + if (cached && cached.blob) { + trackSizes.push({ id: key, size: cached.blob.size }); + } + } + + // Sort by... we don't have timestamps, so just remove from start + let freed = 0; + for (const { id, size } of trackSizes) { + if (freed >= bytesToFree) break; + await TrackStorage.remove(id); + M.cachedTracks.delete(id); + freed += size; + console.log(`[Cache] Pruned: ${id.slice(0, 16)}... (${(size / 1024 / 1024).toFixed(1)}MB)`); + } + + M.showToast(`Freed ${(freed / 1024 / 1024).toFixed(0)}MB cache space`); + M.renderQueue(); + M.renderLibrary(); + }; + // Get or create cache for a track M.getTrackCache = function(trackId) { if (!trackId) return new Set(); @@ -78,12 +127,22 @@ const blobUrl = URL.createObjectURL(blob); M.trackBlobs.set(trackId, blobUrl); - // Persist to storage - await TrackStorage.set(trackId, blob, contentType); + // Persist to storage (handle quota errors gracefully) + try { + await TrackStorage.set(trackId, blob, contentType); + M.cachedTracks.add(trackId); + // Check if we need to prune old tracks + M.checkCacheSize(); + } catch (e) { + if (e.name === 'QuotaExceededError') { + M.showToast("Cache full - track will stream only", "warning"); + } else { + console.warn("[Cache] Storage error:", e); + } + } // Update cache status and re-render lists console.log("[Cache] Track cached:", trackId.slice(0, 16) + "...", "| size:", (data.byteLength / 1024 / 1024).toFixed(2) + "MB"); - M.cachedTracks.add(trackId); M.renderQueue(); M.renderLibrary(); diff --git a/public/controls.js b/public/controls.js index 26d3523..a73e85b 100644 --- a/public/controls.js +++ b/public/controls.js @@ -96,6 +96,18 @@ M.$("#btn-prev").onclick = () => jumpToTrack(M.currentIndex - 1); M.$("#btn-next").onclick = () => jumpToTrack(M.currentIndex + 1); + // Stream-only toggle + const streamBtn = M.$("#btn-stream-only"); + M.updateStreamOnlyButton = function() { + streamBtn.classList.toggle("active", M.streamOnly); + streamBtn.title = M.streamOnly ? "Stream-only mode (click to enable caching)" : "Click to enable stream-only mode (no caching)"; + }; + streamBtn.onclick = () => { + M.setStreamOnly(!M.streamOnly); + M.updateStreamOnlyButton(); + }; + M.updateStreamOnlyButton(); + // Playback mode button const modeLabels = { "once": "once", diff --git a/public/core.js b/public/core.js index 18b8992..bcef3ad 100644 --- a/public/core.js +++ b/public/core.js @@ -46,6 +46,8 @@ window.MusicRoom = { trackBlobs: new Map(), // Map of filename -> Blob URL for fully cached tracks bulkDownloadStarted: new Map(), cachedTracks: new Set(), // Set of track IDs that are fully cached locally + streamOnly: false, // When true, skip bulk downloads (still cache played tracks) + cacheLimit: 100 * 1024 * 1024, // 100MB default cache limit // Download metrics audioBytesPerSecond: 20000, // Audio bitrate estimate for range requests diff --git a/public/index.html b/public/index.html index b551641..aaea4a9 100644 --- a/public/index.html +++ b/public/index.html @@ -4,7 +4,7 @@