344 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			344 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<!DOCTYPE html>
 | 
						|
<html lang="en">
 | 
						|
<head>
 | 
						|
	<meta charset="UTF-8">
 | 
						|
	<title>Viva++</title>
 | 
						|
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
 | 
						|
	<style>
 | 
						|
		body {
 | 
						|
			background-color: #000;
 | 
						|
			margin: 0;
 | 
						|
			padding: 0;
 | 
						|
			display: flex;
 | 
						|
			justify-content: center;
 | 
						|
			align-items: center;
 | 
						|
			height: 100vh;
 | 
						|
		}
 | 
						|
 | 
						|
		.loader {
 | 
						|
			border: 1.5vw solid #333;
 | 
						|
			border-top: 1.5vw solid #3498db;
 | 
						|
			border-bottom: 1.5vw solid #3498db;
 | 
						|
			border-radius: 50%;
 | 
						|
			width: 10vw;
 | 
						|
			height: 10vw;
 | 
						|
			animation: spin 2s linear infinite;
 | 
						|
		}
 | 
						|
 | 
						|
		@keyframes spin {
 | 
						|
			0% { transform: rotate(0deg); }
 | 
						|
			100% { transform: rotate(360deg); }
 | 
						|
		}
 | 
						|
 | 
						|
		.video-container {
 | 
						|
			display: none;
 | 
						|
			width: 100%;
 | 
						|
			justify-content: center;
 | 
						|
			align-items: center;
 | 
						|
			gap: 2vw;
 | 
						|
		}
 | 
						|
 | 
						|
		.video-thumbnail {
 | 
						|
			border-radius: 8px;
 | 
						|
			transition: transform 0.3s ease;
 | 
						|
			box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
 | 
						|
			background-color: #444;
 | 
						|
			aspect-ratio: 16/9;
 | 
						|
			object-fit: cover;
 | 
						|
		}
 | 
						|
 | 
						|
		.current-video {
 | 
						|
			width: 50vw;
 | 
						|
			z-index: 2;
 | 
						|
			box-shadow: 0 0 200px -10px #444;
 | 
						|
		}
 | 
						|
 | 
						|
		.side-video {
 | 
						|
			width: 20vw;
 | 
						|
			opacity: 0.7;
 | 
						|
			z-index: 1;
 | 
						|
		}
 | 
						|
 | 
						|
		.play-button {
 | 
						|
			position: absolute;
 | 
						|
			top: 50%;
 | 
						|
			left: 50%;
 | 
						|
			transform: translate(-50%, -50%);
 | 
						|
			width: 80px; /* Adjust size as needed */
 | 
						|
			height: 80px; /* Adjust size as needed */
 | 
						|
			background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M8 5v14l11-7z'/%3E%3C/svg%3E");
 | 
						|
			background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent background */
 | 
						|
			background-repeat: no-repeat;
 | 
						|
			background-position: center;
 | 
						|
			background-size: 50%; /* Adjust icon size within button */
 | 
						|
			border-radius: 50%; /* Circular button */
 | 
						|
			cursor: pointer;
 | 
						|
			z-index: 3; /* Ensure it's above the video thumbnails */
 | 
						|
			border: 2px solid white;
 | 
						|
			box-shadow: 0 0 15px rgba(0,0,0,0.7);
 | 
						|
		}
 | 
						|
 | 
						|
		#video-overlay-container {
 | 
						|
			position: fixed;
 | 
						|
			top: 0;
 | 
						|
			left: 0;
 | 
						|
			width: 100%;
 | 
						|
			height: 100%;
 | 
						|
			background-color: black;
 | 
						|
			z-index: 10000; /* Above other page content */
 | 
						|
			display: flex;
 | 
						|
			align-items: center;
 | 
						|
			justify-content: center;
 | 
						|
		}
 | 
						|
 | 
						|
		#video-overlay-container .video-player {
 | 
						|
			width: 100%;
 | 
						|
			height: 100%;
 | 
						|
			display: block;
 | 
						|
		}
 | 
						|
 | 
						|
		#video-overlay-container #back-button { /* Back button is now inside the container */
 | 
						|
			position: absolute;
 | 
						|
			top: 20px;
 | 
						|
			left: 20px;
 | 
						|
			z-index: 1; /* z-index relative to siblings in container */
 | 
						|
			padding: 10px 20px;
 | 
						|
			font-size: 16px;
 | 
						|
			background-color: rgba(0, 0, 0, 0.5);
 | 
						|
			color: white;
 | 
						|
			border: 1px solid white;
 | 
						|
			border-radius: 5px;
 | 
						|
			cursor: pointer;
 | 
						|
			opacity: 1; /* Visible by default */
 | 
						|
			transition: opacity 0.3s ease-in-out; /* Smooth transition */
 | 
						|
		}
 | 
						|
 | 
						|
		#video-overlay-container #back-button.button-hidden {
 | 
						|
			opacity: 0; /* Class to hide the button */
 | 
						|
		}
 | 
						|
 | 
						|
		#custom-video-controls {
 | 
						|
			position: absolute;
 | 
						|
			bottom: 0;
 | 
						|
			left: 0;
 | 
						|
			width: 100%;
 | 
						|
			padding: 10px;
 | 
						|
			background-color: rgba(0, 0, 0, 0.5);
 | 
						|
			display: flex;
 | 
						|
			align-items: center;
 | 
						|
			z-index: 1; /* Above video, but below back button if it were in the same direct stacking context */
 | 
						|
			opacity: 1;
 | 
						|
			transition: opacity 0.3s ease-in-out;
 | 
						|
			box-sizing: border-box;
 | 
						|
		}
 | 
						|
 | 
						|
		#custom-video-controls.controls-hidden {
 | 
						|
			opacity: 0;
 | 
						|
		}
 | 
						|
 | 
						|
		#custom-play-pause-button {
 | 
						|
			background: none;
 | 
						|
			border: none;
 | 
						|
			color: white;
 | 
						|
			font-size: 1.5em; /* Adjust size as needed */
 | 
						|
			cursor: pointer;
 | 
						|
			padding: 5px 10px;
 | 
						|
			margin-right: 10px;
 | 
						|
		}
 | 
						|
 | 
						|
		#custom-seek-bar {
 | 
						|
			flex-grow: 1;
 | 
						|
			cursor: pointer;
 | 
						|
			accent-color: #3498db; /* Optional: color for the seek bar thumb and progress */
 | 
						|
		}
 | 
						|
	</style>
 | 
						|
	<script>
 | 
						|
		function hideLoader() {
 | 
						|
            $(".loader").hide();
 | 
						|
            $(".video-container").css("display", "flex");
 | 
						|
		}
 | 
						|
 | 
						|
        let currentVideo = 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 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
 | 
						|
        }
 | 
						|
 | 
						|
        // --- Moved Helper Function Definitions ---
 | 
						|
        function handlePlayPauseClick() {
 | 
						|
            if (videoElement.paused) {
 | 
						|
                videoElement.play();
 | 
						|
            } else {
 | 
						|
                videoElement.pause();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        function updatePlayPauseButtonState() {
 | 
						|
            if (!playPauseButton) return; // Ensure button exists
 | 
						|
            if (videoElement.paused) {
 | 
						|
                playPauseButton.text('►'); // Play icon
 | 
						|
            } else {
 | 
						|
                playPauseButton.text('❚❚'); // Pause icon
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        function handleVideoLoadedMetadata() {
 | 
						|
            if (!seekBar) return; // Ensure seekbar exists
 | 
						|
            seekBar.attr('max', videoElement.duration);
 | 
						|
        }
 | 
						|
 | 
						|
        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}%)`);
 | 
						|
        }
 | 
						|
 | 
						|
        function handleSeekBarInput() {
 | 
						|
            if (!seekBar) return; // Ensure seekbar exists
 | 
						|
            videoElement.currentTime = seekBar.val();
 | 
						|
        }
 | 
						|
 | 
						|
        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();
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
		$(document).ready(function() {
 | 
						|
			// 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();
 | 
						|
			})
 | 
						|
 | 
						|
            $("#play-button").click(function() {
 | 
						|
                if (currentVideo && currentVideo.url) {
 | 
						|
                    // Create the overlay container
 | 
						|
                    overlayContainer = $("<div id='video-overlay-container'></div>").appendTo("body");
 | 
						|
 | 
						|
                    // Create video element and append to container
 | 
						|
                    videoElement = $("<video autoplay class='video-player'></video>") // Removed 'controls' attribute
 | 
						|
                        .attr("src", currentVideo.url)
 | 
						|
                        .appendTo(overlayContainer)[0];
 | 
						|
 | 
						|
                    // Create back button and append to container
 | 
						|
                    backButton = $("<button id='back-button'>Back</button>")
 | 
						|
                        .appendTo(overlayContainer);
 | 
						|
 | 
						|
                    // Create custom video controls container
 | 
						|
                    customControls = $("<div id='custom-video-controls'></div>").appendTo(overlayContainer);
 | 
						|
                    playPauseButton = $("<button id='custom-play-pause-button'>❚❚</button>").appendTo(customControls); // Assign to global var
 | 
						|
                    seekBar = $("<input type='range' id='custom-seek-bar' value='0' />").appendTo(customControls); // Assign to global var
 | 
						|
 | 
						|
                    // Remove local inactivityTimer declaration, it's global now
 | 
						|
                    // let inactivityTimer;
 | 
						|
 | 
						|
                    // --- 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();
 | 
						|
 | 
						|
                    // --- Setup Video End and Back Button ---
 | 
						|
                    $(videoElement).on('ended', exitVideoPlayback);
 | 
						|
                    backButton.click(exitVideoPlayback);
 | 
						|
 | 
						|
                    // --- 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);
 | 
						|
 | 
						|
                    // --- 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.
 | 
						|
                }
 | 
						|
            });
 | 
						|
		})
 | 
						|
	</script>
 | 
						|
</head>
 | 
						|
<body>
 | 
						|
	<div class="loader" id="loader"></div>
 | 
						|
	<div class="video-container">
 | 
						|
		<img id="prev-thumb" class="video-thumbnail side-video" alt="Previous Video">
 | 
						|
		<img id="current-thumb" class="video-thumbnail current-video" alt="Current Video">
 | 
						|
		<img id="next-thumb" class="video-thumbnail side-video" alt="Next Video">
 | 
						|
		<div class="play-button" id="play-button"></div>
 | 
						|
	</div>
 | 
						|
</body>
 | 
						|
</html>
 |