From e3424ad77b46045b9f354c1e7c4aaa83806814f9 Mon Sep 17 00:00:00 2001 From: Sebastiaan de Schaetzen Date: Tue, 17 Jun 2025 10:05:39 +0200 Subject: [PATCH] Refactor video overlay implementation to improve control handling and visibility --- static/index.html | 305 ++++++++++++++++++++++++---------------------- 1 file changed, 160 insertions(+), 145 deletions(-) diff --git a/static/index.html b/static/index.html index f1cd3d6..03be685 100644 --- a/static/index.html +++ b/static/index.html @@ -86,7 +86,7 @@ height: 100%; background-color: black; z-index: 10000; /* Above other page content */ - display: flex; + display: none; /* Initially hidden, JS will change to flex */ align-items: center; justify-content: center; } @@ -159,175 +159,180 @@ } let currentVideo = null; - let videoElement = null; - let overlayContainer = null; - let inactivityTimer = null; - let backButton = null; - let customControls = null; - let playPauseButton = null; - let seekBar = null; + let videoElement = null; + let overlayContainer = null; + let inactivityTimer = null; + let backButton = null; + let customControls = null; + let playPauseButton = null; + let seekBar = null; - function exitVideoPlayback() { - if (videoElement && videoElement.pause) { - videoElement.pause(); - } - clearTimeout(inactivityTimer); - if (overlayContainer) { - overlayContainer.css('cursor', 'default'); - overlayContainer.off('mousemove.inactivityControls'); - // Detach document-level fullscreen listener specific to this playback - $(document).off('fullscreenchange.videoPlayback webkitfullscreenchange.videoPlayback mozfullscreenchange.videoPlayback MSFullscreenChange.videoPlayback', handleFullscreenChange); - overlayContainer.remove(); - } - // Attempt to exit fullscreen if the document is still in fullscreen mode - // and our container was likely the one in fullscreen. - if (document.fullscreenElement || document.webkitIsFullScreen || document.mozFullScreen || document.msFullscreenElement) { - if (document.exitFullscreen) { - document.exitFullscreen().catch(() => {}); - } else if (document.mozCancelFullScreen) { - document.mozCancelFullScreen(); - } else if (document.webkitExitFullscreen) { - document.webkitExitFullscreen(); - } else if (document.msExitFullscreen) { - document.msExitFullscreen(); - } - } - } + function exitVideoPlayback() { + if (videoElement && videoElement.pause) { + videoElement.pause(); + } + clearTimeout(inactivityTimer); + if (overlayContainer) { + overlayContainer.css('cursor', 'default'); + overlayContainer.off('mousemove.inactivityControls'); // Remove session-specific listener + overlayContainer.hide(); // Hide instead of remove + } - function showControlsAndResetTimer() { - backButton.removeClass('button-hidden'); - customControls.removeClass('controls-hidden'); - overlayContainer.css('cursor', 'default'); // Show cursor - clearTimeout(inactivityTimer); - inactivityTimer = setTimeout(function() { - backButton.addClass('button-hidden'); - customControls.addClass('controls-hidden'); - overlayContainer.css('cursor', 'none'); // Hide cursor - }, 3000); // 3 seconds - } + $(document).off('fullscreenchange.videoPlayback webkitfullscreenchange.videoPlayback mozfullscreenchange.videoPlayback MSFullscreenChange.videoPlayback', handleFullscreenChange); - // --- Moved Helper Function Definitions --- - function handlePlayPauseClick() { - if (videoElement.paused) { - videoElement.play(); - } else { - videoElement.pause(); - } - } + if (videoElement) { + $(videoElement).off('.currentVideo'); // Remove all namespaced video events + videoElement.src = ""; // Clear the source + videoElement.load(); // Important to ensure the video unloads + } + if (seekBar) { + seekBar.val(0); + seekBar.css('background', ''); // Reset seek bar style + } + updatePlayPauseButtonState(); // Update button to show play icon usually - function updatePlayPauseButtonState() { - if (!playPauseButton) return; // Ensure button exists - if (videoElement.paused) { - playPauseButton.text('►'); // Play icon - } else { - playPauseButton.text('❚❚'); // Pause icon - } - } + // Attempt to exit fullscreen + if (document.fullscreenElement || document.webkitIsFullScreen || document.mozFullScreen || document.msFullscreenElement) { + if (document.exitFullscreen) { + document.exitFullscreen().catch(() => {}); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } + } + } - function handleVideoLoadedMetadata() { - if (!seekBar) return; // Ensure seekbar exists - seekBar.attr('max', videoElement.duration); - } + function showControlsAndResetTimer() { + if (backButton) backButton.removeClass('button-hidden'); + if (customControls) customControls.removeClass('controls-hidden'); + if (overlayContainer) overlayContainer.css('cursor', 'default'); - function handleVideoTimeUpdate() { - if (!seekBar) return; // Ensure seekbar exists - seekBar.val(videoElement.currentTime); - const percentage = (videoElement.currentTime / videoElement.duration) * 100; - seekBar.css('background', `linear-gradient(to right, #3498db ${percentage}%, #555 ${percentage}%)`); - } + clearTimeout(inactivityTimer); + inactivityTimer = setTimeout(function() { + if (backButton) backButton.addClass('button-hidden'); + if (customControls) customControls.addClass('controls-hidden'); + if (overlayContainer) overlayContainer.css('cursor', 'none'); + }, 3000); + } - function handleSeekBarInput() { - if (!seekBar) return; // Ensure seekbar exists - videoElement.currentTime = seekBar.val(); - } + function handlePlayPauseClick() { + if (videoElement) { + if (videoElement.paused) { + videoElement.play(); + } else { + videoElement.pause(); + } + } + } - function handleFullscreenChange() { - const isActuallyFullscreen = document.fullscreenElement || document.webkitIsFullScreen || document.mozFullScreen || document.msFullscreenElement; - if (!isActuallyFullscreen && overlayContainer && overlayContainer.length && !overlayContainer.is(document.fullscreenElement)) { - if (overlayContainer.is(':visible')) { - exitVideoPlayback(); - } - } - } + function updatePlayPauseButtonState() { + if (!playPauseButton || !videoElement) return; + if (videoElement.paused) { + playPauseButton.text('►'); + } else { + playPauseButton.text('❚❚'); + } + } + + function handleVideoLoadedMetadata() { + if (seekBar && videoElement) { + seekBar.attr('max', videoElement.duration); + } + } + + function handleVideoTimeUpdate() { + if (seekBar && videoElement && videoElement.duration) { // Check videoElement.duration to prevent NaN + seekBar.val(videoElement.currentTime); + const percentage = (videoElement.currentTime / videoElement.duration) * 100; + seekBar.css('background', `linear-gradient(to right, #3498db ${percentage}%, #555 ${percentage}%)`); + } else if (seekBar) { + seekBar.val(0); // Reset if duration is not available + } + } + + function handleSeekBarInput() { + if (videoElement && seekBar) { + videoElement.currentTime = seekBar.val(); + } + } + + function handleFullscreenChange() { + const isActuallyFullscreen = document.fullscreenElement || document.webkitIsFullScreen || document.mozFullScreen || document.msFullscreenElement; + if (!isActuallyFullscreen && overlayContainer && overlayContainer.is(':visible')) { + // If fullscreen exited and overlay was visible, treat as exiting video playback + // This helps if user presses Esc. + exitVideoPlayback(); + } + } $(document).ready(function() { + // Initialize global selectors for static elements + overlayContainer = $("#video-overlay-container"); + videoElement = overlayContainer.find(".video-player")[0]; + backButton = overlayContainer.find("#back-button"); + customControls = overlayContainer.find("#custom-video-controls"); + playPauseButton = customControls.find("#custom-play-pause-button"); + seekBar = customControls.find("#custom-seek-bar"); + // Pre-set all thumbnails to a placeholder state $(".video-thumbnail").attr("src", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 16 9'%3E%3C/svg%3E"); $.getJSON("/api/homepage", function(data) { - currentVideo = data.currentVideo; - // previousVideo = data.previousVideo; - // nextVideo = data.nextVideo; - // - // // Set thumbnails - $("#current-thumb").attr("src", currentVideo.thumbnail); - // $("#prev-thumb").attr("src", previousVideo.thumbnail); - // $("#next-thumb").attr("src", nextVideo.thumbnail); - - // Hide loader and show videos - hideLoader(); + currentVideo = data.currentVideo; + $("#current-thumb").attr("src", currentVideo.thumbnail); + hideLoader(); }) - $("#play-button").click(function() { - if (currentVideo && currentVideo.url) { - // Create the overlay container - overlayContainer = $("
").appendTo("body"); + // Attach event listeners that can be set up once for static controls + if (playPauseButton) playPauseButton.click(handlePlayPauseClick); + if (seekBar) seekBar.on('input', handleSeekBarInput); + if (backButton) backButton.click(exitVideoPlayback); - // Create video element and append to container - videoElement = $("") // Removed 'controls' attribute - .attr("src", currentVideo.url) - .appendTo(overlayContainer)[0]; - // Create back button and append to container - backButton = $("") - .appendTo(overlayContainer); + $("#play-button").click(function() { + if (currentVideo && currentVideo.url && videoElement && overlayContainer) { + // Set video source and load + videoElement.src = currentVideo.url; + videoElement.load(); - // Create custom video controls container - customControls = $("
").appendTo(overlayContainer); - playPauseButton = $("").appendTo(customControls); // Assign to global var - seekBar = $("").appendTo(customControls); // Assign to global var + // --- Setup Inactivity Controls (session-specific) --- + showControlsAndResetTimer(); // Initial call - // Remove local inactivityTimer declaration, it's global now - // let inactivityTimer; + updatePlayPauseButtonState(); // Initial state for the button - // --- Setup Inactivity Controls --- - overlayContainer.on('mousemove.inactivityControls', showControlsAndResetTimer); - showControlsAndResetTimer(); // Initial call - // --- Setup Custom Video Controls --- - playPauseButton.click(handlePlayPauseClick); - $(videoElement).on('play', updatePlayPauseButtonState); - $(videoElement).on('pause', updatePlayPauseButtonState); - $(videoElement).on('loadedmetadata', handleVideoLoadedMetadata); - $(videoElement).on('timeupdate', handleVideoTimeUpdate); - seekBar.on('input', handleSeekBarInput); - // Initial call to set play/pause button state, especially if autoplay is effective - updatePlayPauseButtonState(); + // Show overlay and request fullscreen + overlayContainer.css('display', 'flex'); + const containerEl = overlayContainer[0]; + if (containerEl.requestFullscreen) { + containerEl.requestFullscreen().catch(err => console.error("Error attempting to enable full-screen mode:", err)); + } else if (containerEl.mozRequestFullScreen) { + containerEl.mozRequestFullScreen(); + } else if (containerEl.webkitRequestFullscreen) { + containerEl.webkitRequestFullscreen(); + } else if (containerEl.msRequestFullscreen) { + containerEl.msRequestFullscreen(); + } - // --- Setup Video End and Back Button --- - $(videoElement).on('ended', exitVideoPlayback); - backButton.click(exitVideoPlayback); + videoElement.play().catch(err => console.error("Error attempting to play video:", err)); + } + }); - // --- Setup Fullscreen Change Listener --- - // Detach any previous listeners first (using the specific named handler for accuracy if needed, but general works too) - $(document).off('fullscreenchange.videoPlayback webkitfullscreenchange.videoPlayback mozfullscreenchange.videoPlayback MSFullscreenChange.videoPlayback'); - $(document).on('fullscreenchange.videoPlayback webkitfullscreenchange.videoPlayback mozfullscreenchange.videoPlayback MSFullscreenChange.videoPlayback', handleFullscreenChange); + // --- Setup Custom Video Controls Events (session-specific for video element) --- + $(videoElement).on('play.currentVideo', updatePlayPauseButtonState); + $(videoElement).on('pause.currentVideo', updatePlayPauseButtonState); + $(videoElement).on('loadedmetadata.currentVideo', handleVideoLoadedMetadata); + $(videoElement).on('timeupdate.currentVideo', handleVideoTimeUpdate); + $(videoElement).on('ended.currentVideo', exitVideoPlayback); - // --- Request Fullscreen --- - const containerEl = overlayContainer[0]; - if (containerEl.requestFullscreen) { - containerEl.requestFullscreen(); - } else if (containerEl.mozRequestFullScreen) { /* Firefox */ - containerEl.mozRequestFullScreen(); - } else if (containerEl.webkitRequestFullscreen) { /* Chrome, Safari & Opera */ - containerEl.webkitRequestFullscreen(); - } else if (containerEl.msRequestFullscreen) { /* IE/Edge */ - containerEl.msRequestFullscreen(); - } - // NOTE: exitVideoPlayback was defined above. - // The original snippet showed its definition here, it's now grouped with other helpers. - } - }); + // --- Setup Fullscreen Change Listener (session-specific) --- + $(document).off('fullscreenchange.videoPlayback webkitfullscreenchange.videoPlayback mozfullscreenchange.videoPlayback MSFullscreenChange.videoPlayback', handleFullscreenChange); // Ensure clean state + $(document).on('fullscreenchange.videoPlayback webkitfullscreenchange.videoPlayback mozfullscreenchange.videoPlayback MSFullscreenChange.videoPlayback', handleFullscreenChange); + + overlayContainer.on('mousemove.inactivityControls', showControlsAndResetTimer); }) @@ -339,5 +344,15 @@ Next Video
+ + +
+ + +
+ + +
+