130 lines
3.4 KiB
JavaScript
130 lines
3.4 KiB
JavaScript
// MusicRoom - Core module
|
|
// Shared state and namespace setup
|
|
|
|
window.MusicRoom = {
|
|
// Audio element
|
|
audio: new Audio(),
|
|
|
|
// WebSocket and channel state
|
|
ws: null,
|
|
currentChannelId: null,
|
|
currentTrackId: null,
|
|
currentTitle: null,
|
|
serverTimestamp: 0,
|
|
serverTrackDuration: 0,
|
|
lastServerUpdate: 0,
|
|
serverPaused: true,
|
|
|
|
// Channels list
|
|
channels: [],
|
|
|
|
// Sync state
|
|
wantSync: true, // User intent - do they want to be synced?
|
|
synced: false, // Actual state - are we currently synced?
|
|
|
|
// Volume
|
|
preMuteVolume: 1,
|
|
STORAGE_KEY: "blastoise_volume",
|
|
CHANNEL_STORAGE_KEY: "blastoise_channel",
|
|
THEME_STORAGE_KEY: "blastoise_theme",
|
|
|
|
// Playback state
|
|
localTimestamp: 0,
|
|
queue: [],
|
|
currentIndex: 0,
|
|
playbackMode: "repeat-all",
|
|
|
|
// User state
|
|
currentUser: null,
|
|
serverStatus: null,
|
|
|
|
// Library (all discovered tracks)
|
|
library: [],
|
|
|
|
// Caching state
|
|
prefetchController: null,
|
|
loadingSegments: new Set(),
|
|
trackCaches: new Map(), // Map of filename -> Set of cached segment indices
|
|
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
|
|
downloadSpeed: 0, // Actual network download speed
|
|
recentDownloads: [], // Track recent downloads for speed calculation
|
|
|
|
// Constants
|
|
SEGMENTS: 20,
|
|
FAST_THRESHOLD: 10 * 1024 * 1024, // 10 MB/s
|
|
|
|
// UI update tracking (to avoid unnecessary DOM updates)
|
|
lastProgressPct: -1,
|
|
lastTimeCurrent: "",
|
|
lastTimeTotal: "",
|
|
lastBufferPct: -1,
|
|
lastSpeedText: ""
|
|
};
|
|
|
|
(function() {
|
|
const M = window.MusicRoom;
|
|
const CHANNEL_COOKIE_MAX_AGE = 60 * 60 * 24 * 400;
|
|
|
|
M.getCookieValue = function(name) {
|
|
const prefix = `${name}=`;
|
|
return document.cookie
|
|
.split(";")
|
|
.map(cookie => cookie.trim())
|
|
.find(cookie => cookie.startsWith(prefix))
|
|
?.slice(prefix.length) || null;
|
|
};
|
|
|
|
M.setCookieValue = function(name, value, maxAge = CHANNEL_COOKIE_MAX_AGE) {
|
|
document.cookie = `${name}=${encodeURIComponent(value)}; Path=/; SameSite=Lax; Max-Age=${maxAge}`;
|
|
};
|
|
|
|
M.clearCookieValue = function(name) {
|
|
document.cookie = `${name}=; Path=/; SameSite=Lax; Max-Age=0`;
|
|
};
|
|
|
|
M.getSavedChannelId = function() {
|
|
const cookieValue = M.getCookieValue(M.CHANNEL_STORAGE_KEY);
|
|
if (cookieValue) {
|
|
try {
|
|
return decodeURIComponent(cookieValue);
|
|
} catch {
|
|
M.clearRememberedChannel();
|
|
}
|
|
}
|
|
|
|
try {
|
|
const storedValue = localStorage.getItem(M.CHANNEL_STORAGE_KEY);
|
|
if (storedValue) {
|
|
M.rememberChannel(storedValue);
|
|
return storedValue;
|
|
}
|
|
} catch {}
|
|
|
|
return null;
|
|
};
|
|
|
|
M.rememberChannel = function(channelId) {
|
|
if (!channelId) return;
|
|
M.setCookieValue(M.CHANNEL_STORAGE_KEY, channelId);
|
|
|
|
try {
|
|
localStorage.setItem(M.CHANNEL_STORAGE_KEY, channelId);
|
|
} catch {}
|
|
};
|
|
|
|
M.clearRememberedChannel = function() {
|
|
M.clearCookieValue(M.CHANNEL_STORAGE_KEY);
|
|
|
|
try {
|
|
localStorage.removeItem(M.CHANNEL_STORAGE_KEY);
|
|
} catch {}
|
|
};
|
|
})();
|