From dd47fbed93d02eba0de09e247362d4f7f3e69b78 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Wed, 24 Sep 2025 13:59:09 -0400 Subject: [PATCH] Ensure thumbnails fall back to placeholder --- js/app.js | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/js/app.js b/js/app.js index b0ac1e38..fdc797e8 100644 --- a/js/app.js +++ b/js/app.js @@ -23,6 +23,8 @@ function fakeDecrypt(str) { const UNSUPPORTED_BTITH_MESSAGE = "This magnet link is missing a compatible BitTorrent v1 info hash."; +const FALLBACK_THUMBNAIL_SRC = "assets/jpg/video-thumbnail-fallback.jpg"; + /** * Basic validation for BitTorrent magnet URIs. * @@ -107,19 +109,56 @@ class MediaLoader { el.getAttribute("data-fallback-src") || ""; - const handleError = () => { + const applyFallback = () => { if (!fallbackSrc) { - el.removeEventListener("error", handleError); return; } if (el.src !== fallbackSrc) { el.src = fallbackSrc; } + el.dataset.thumbnailFailed = "true"; + }; + + const cleanupListeners = () => { el.removeEventListener("error", handleError); + el.removeEventListener("load", handleLoad); + }; + + const handleError = () => { + cleanupListeners(); + applyFallback(); + }; + + const handleLoad = () => { + cleanupListeners(); + if ( + (el.naturalWidth === 0 && el.naturalHeight === 0) || + !el.currentSrc + ) { + applyFallback(); + } else { + delete el.dataset.thumbnailFailed; + } }; el.addEventListener("error", handleError); - el.src = lazySrc; + el.addEventListener("load", handleLoad); + + if (fallbackSrc && lazySrc === fallbackSrc) { + cleanupListeners(); + delete el.dataset.thumbnailFailed; + } else { + el.src = lazySrc; + + if (el.complete) { + // Handle cached results synchronously + if (el.naturalWidth === 0 && el.naturalHeight === 0) { + handleError(); + } else { + handleLoad(); + } + } + } } delete el.dataset.lazy; @@ -142,6 +181,20 @@ class MediaLoader { el.getAttribute("data-fallback-src") || el.getAttribute("src") || ""; if (fallbackAttr) { el.dataset.fallbackSrc = fallbackAttr; + } else if (el.tagName === "IMG") { + el.dataset.fallbackSrc = FALLBACK_THUMBNAIL_SRC; + el.setAttribute("data-fallback-src", FALLBACK_THUMBNAIL_SRC); + } + } + + if ( + el.tagName === "IMG" && + typeof HTMLImageElement !== "undefined" && + "loading" in HTMLImageElement.prototype + ) { + el.loading = el.loading || "lazy"; + if ("decoding" in HTMLImageElement.prototype) { + el.decoding = el.decoding || "async"; } } @@ -1521,7 +1574,8 @@ class bitvidApp { >
${this.escapeHTML(video.title)} @@ -2510,7 +2564,11 @@ class bitvidApp { } escapeHTML(unsafe) { - return unsafe + if (unsafe === null || typeof unsafe === "undefined") { + return ""; + } + + return String(unsafe) .replace(/&/g, "&") .replace(//g, ">")