From 2020b5925950f4d4b43115fec4b07b83f13d2705 Mon Sep 17 00:00:00 2001 From: Peter Stockings Date: Mon, 2 Mar 2026 21:28:24 +1100 Subject: [PATCH] Remove speed control --- public/app.js | 110 +++++++++------------------------------------- public/index.html | 9 ---- public/style.css | 24 ++++++++++ server.ts | 12 +---- 4 files changed, 46 insertions(+), 109 deletions(-) diff --git a/public/app.js b/public/app.js index 2100891..3984556 100644 --- a/public/app.js +++ b/public/app.js @@ -10,8 +10,6 @@ let joinFile = null; // File object for the joiner let ignoreSync = false; // flag to avoid feedback loops let syncTimeout = null; - let driftInterval = null; - let serverState = { playing: false, position: 0, speed: 1 }; // Reconnection state let reconnectAttempts = 0; @@ -73,7 +71,6 @@ const volumeSlider = $("volume-slider"); const currentTimeEl = $("current-time"); const durationEl = $("duration"); - const speedSelect = $("speed-select"); const fullscreenBtn = $("fullscreen-btn"); const userCountEl = $("user-count"); const usersList = $("users-list"); @@ -243,7 +240,6 @@ applySync(msg.state); } addSystemMessage("Reconnected"); - startDriftCorrection(); flushMessageQueue(); break; @@ -298,82 +294,22 @@ // Load local file into video player const file = localFile || joinFile; if (file) { - loadVideoSource(file); + if (currentBlobUrl) URL.revokeObjectURL(currentBlobUrl); + currentBlobUrl = URL.createObjectURL(file); + videoPlayer.src = currentBlobUrl; } - // --- Handle false "ended" events (Chromium MKV blob bug) --- - // Chrome can't properly index MKV containers loaded via blob URLs. - // After ~30s it may jump to the end and fire "ended" even though - // the video isn't actually over. Recovery: reload the blob and seek. - let recoveryAttempts = 0; - const MAX_RECOVERY_ATTEMPTS = 5; - - videoPlayer.addEventListener("ended", () => { - const duration = videoPlayer.duration || 0; - // Only attempt recovery if we have server state showing we're not near the end - if (duration > 0 && lastServerState.position < duration - 5 && recoveryAttempts < MAX_RECOVERY_ATTEMPTS) { - recoveryAttempts++; - console.log(`[MKV-RECOVERY] False ended detected. Server pos=${lastServerState.position.toFixed(1)}, duration=${duration.toFixed(1)}. Reloading source (attempt ${recoveryAttempts}/${MAX_RECOVERY_ATTEMPTS})`); - - const targetPos = lastServerState.position; - const wasPlaying = lastServerState.playing; - const currentFile = localFile || joinFile; - - if (currentFile) { - // Reload the video with a fresh blob URL - loadVideoSource(currentFile, targetPos, wasPlaying); - } - } - }); - - // Reset recovery counter when video plays successfully for a while - let recoveryResetTimer = null; - videoPlayer.addEventListener("timeupdate", () => { - if (recoveryAttempts > 0) { - clearTimeout(recoveryResetTimer); - recoveryResetTimer = setTimeout(() => { - recoveryAttempts = 0; - }, 10000); // Reset after 10s of successful playback - } - }); - // --- Resync on tab focus (handles background tab throttling) --- document.addEventListener("visibilitychange", () => { if (!document.hidden && roomCode && ws && ws.readyState === WebSocket.OPEN) { - console.log("[SYNC] Tab became visible, requesting state resync"); send({ type: "request_state" }); } }); - - // Start drift correction - startDriftCorrection(); } - // --- Video Source Loading --- + // --- State tracking --- let currentBlobUrl = null; - let lastServerState = { playing: false, position: 0, speed: 1 }; - - function loadVideoSource(file, seekTo, shouldPlay) { - // Revoke old blob URL to free memory - if (currentBlobUrl) { - URL.revokeObjectURL(currentBlobUrl); - } - currentBlobUrl = URL.createObjectURL(file); - videoPlayer.src = currentBlobUrl; - - if (seekTo !== undefined) { - videoPlayer.addEventListener("loadedmetadata", function onMeta() { - videoPlayer.removeEventListener("loadedmetadata", onMeta); - videoPlayer.currentTime = seekTo; - if (shouldPlay) { - videoPlayer.play().then(() => { - videoWrapper.classList.add("playing"); - updatePlayPauseIcon(); - }).catch(() => {}); - } - }); - } - } + let lastServerState = { playing: false, position: 0 }; // --- File Check Modal --- let pendingRoomFileInfo = null; @@ -459,12 +395,7 @@ // Track latest server state for recovery if (data.position !== undefined) lastServerState.position = data.position; if (data.playing !== undefined) lastServerState.playing = data.playing; - if (data.speed !== undefined) lastServerState.speed = data.speed; - if (data.speed !== undefined && videoPlayer.playbackRate !== data.speed) { - videoPlayer.playbackRate = data.speed; - speedSelect.value = String(data.speed); - } if (data.position !== undefined) { const diff = Math.abs(videoPlayer.currentTime - data.position); @@ -510,14 +441,7 @@ } } - function startDriftCorrection() { - if (driftInterval) clearInterval(driftInterval); - driftInterval = setInterval(() => { - if (ws && ws.readyState === WebSocket.OPEN) { - send({ type: "request_state" }); - } - }, 5000); - } + // --- Get video duration from a file (used for file info) --- function getVideoDuration(file) { @@ -544,6 +468,17 @@ container.classList.remove("hidden"); } + function isMkvFile(file) { + return file.name.toLowerCase().endsWith(".mkv"); + } + + function showFormatWarning(container) { + const warning = document.createElement("div"); + warning.className = "format-warning"; + warning.innerHTML = `⚠️ MKV files may not play correctly in browsers. Convert to MP4 for best results:
ffmpeg -i file.mkv -c copy file.mp4`; + container.parentNode.insertBefore(warning, container.nextSibling); + } + // ===== EVENT LISTENERS ===== // --- Lobby: Username --- @@ -560,7 +495,11 @@ localFile = file; const duration = await getVideoDuration(file); localFile._duration = duration; + // Remove any previous format warning + const oldWarning = createFileInfo.parentNode.querySelector(".format-warning"); + if (oldWarning) oldWarning.remove(); renderFileInfo(createFileInfo, file, duration); + if (isMkvFile(file)) showFormatWarning(createFileInfo); createRoomBtn.disabled = !usernameInput.value.trim(); }); @@ -679,7 +618,6 @@ joinFile = null; roomFileInfo = null; messageQueue = []; - if (driftInterval) clearInterval(driftInterval); videoPlayer.src = ""; chatMessages.innerHTML = '

Welcome to the room! 👋

'; createFileInfo.classList.add("hidden"); @@ -766,12 +704,6 @@ volOffIcon.classList.toggle("hidden", !videoPlayer.muted); }); - // Speed - speedSelect.addEventListener("change", () => { - const speed = parseFloat(speedSelect.value); - videoPlayer.playbackRate = speed; - send({ type: "sync", action: "speed", speed }); - }); // Fullscreen fullscreenBtn.addEventListener("click", () => { diff --git a/public/index.html b/public/index.html index 2882310..2600289 100644 --- a/public/index.html +++ b/public/index.html @@ -158,15 +158,6 @@
-