From cc00e585618a8dd29574847c97a4ccdac6c7318f Mon Sep 17 00:00:00 2001
From: Sebastiaan de Schaetzen <sebastiaan.de.schaetzen@gmail.com>
Date: Tue, 17 Jun 2025 11:47:50 +0200
Subject: [PATCH] Refactor loader functionality to improve user experience
 during video loading

---
 static/index.html | 65 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 58 insertions(+), 7 deletions(-)

diff --git a/static/index.html b/static/index.html
index 27e489a..345e69b 100644
--- a/static/index.html
+++ b/static/index.html
@@ -16,6 +16,11 @@
 		}
 
 		.loader {
+			position: fixed; /* Changed for global overlay */
+			left: 50%;
+			top: 50%;
+			transform: translate(-50%, -50%); /* Center on screen */
+			z-index: 10001; /* Above video overlay */
 			border: 1.5vw solid #333;
 			border-top: 1.5vw solid #3498db;
 			border-bottom: 1.5vw solid #3498db;
@@ -164,11 +169,18 @@
 		}
 	</style>
 	<script>
-		function hideLoader() {
+		function hideSpinner() { // Was hideLoader, now only hides spinner
             $(".loader").hide();
-            $(".video-container").css("display", "flex");
 		}
 
+        function showSpinner() { // Was showLoader
+			$(".loader").show();
+		}
+
+        function showVideoContainer() {
+            $(".video-container").css("display", "flex");
+        }
+
         let currentVideo = null;
 		let videoElement = null;
 		let overlayContainer = null;
@@ -293,18 +305,30 @@
 			playPauseButton = customControls.find("#custom-play-pause-button");
 			seekBar = customControls.find("#custom-seek-bar");
 
+			// Spinner is visible by default (CSS). Video container is hidden (CSS).
 			// 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");
+			$(".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;
 				$("#current-thumb").attr("src", currentVideo.thumbnail);
-				hideLoader();
+				// Consider populating prev-thumb and next-thumb here if data provides them
+
+				hideSpinner();
+                showVideoContainer();
+
 				// FOCUS: Set initial focus on the main screen
 				if ($(".video-container").is(":visible") && $('#play-button').length) {
 					$('#play-button').focus();
 				}
-			})
+			}).fail(function(jqXHR, textStatus, errorThrown) {
+                console.error("Failed to load homepage data:", textStatus, errorThrown);
+                hideSpinner();
+                // Display a user-friendly error message
+                if (!$('#init-error-msg').length) { // Prevent multiple error messages
+                    $('body').append('<div id="init-error-msg" style="color:white; text-align:center; padding:20px; position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); background-color:rgba(0,0,0,0.8); border-radius:8px; z-index: 10002;">Failed to load content. Please try again later.</div>');
+                }
+            });
 
 			// Attach event listeners that can be set up once for static controls
 			if (playPauseButton) playPauseButton.click(handlePlayPauseClick);
@@ -314,7 +338,30 @@
 
 			$("#play-button").click(function() {
 				if (currentVideo && currentVideo.url && videoElement && overlayContainer) {
-					// Set video source and load
+					showSpinner();
+
+                    // Clear previous handlers and set new ones for this specific load attempt
+                    videoElement.oncanplaythrough = null;
+                    videoElement.onerror = null;
+
+                    videoElement.oncanplaythrough = function() {
+                        hideSpinner();
+                        videoElement.oncanplaythrough = null; // Clean up handler
+                        videoElement.onerror = null;       // Clean up handler
+                        videoElement.play().catch(err => {
+                            console.error("Error attempting to play video:", err);
+                            exitVideoPlayback(); // Exit overlay on play error
+                        });
+                    };
+
+                    videoElement.onerror = function() {
+                        console.error("Error loading video.");
+                        hideSpinner();
+                        videoElement.oncanplaythrough = null; // Clean up handler
+                        videoElement.onerror = null;       // Clean up handler
+                        exitVideoPlayback(); // Exit overlay on load error
+                    };
+
 					videoElement.src = currentVideo.url;
 					videoElement.load();
 
@@ -341,7 +388,11 @@
 					 playPauseButton.focus();
 					}
 
-					videoElement.play().catch(err => console.error("Error attempting to play video:", err));
+                    // The oncanplaythrough is now set before load()
+                    // videoElement.oncanplaythrough = function() {
+                    //    hideSpinner(); // Changed from hideLoader()
+                    //    videoElement.play().catch(err => console.error("Error attempting to play video:", err));
+                    // }
 				}
 			});