From 5fa4eefb9d60fdfbc4c54b27197b0575438c0053 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 12 Aug 2026 01:36:40 +0700 Subject: [PATCH] Improve video embed overlay and proxy behavior --- public/js/embedResize.js | 26 ++++++++++++--- public/js/widgets.js | 69 ++++++++++++++++++++++++++------------- src/sass/tweet/embed.scss | 15 +++++++-- src/views/embed.nim | 2 +- src/views/general.nim | 2 +- 5 files changed, 82 insertions(+), 32 deletions(-) diff --git a/public/js/embedResize.js b/public/js/embedResize.js index 69829fe..664abbf 100644 --- a/public/js/embedResize.js +++ b/public/js/embedResize.js @@ -1,25 +1,41 @@ -(function() { - var embedElement = document.querySelector('.embed-wrapper, .tweet-embed, .embed-video'); +(function () { + var embedElement = document.querySelector( + ".embed-wrapper, .tweet-embed, .embed-video", + ); if (!embedElement) return; + // Video play state for overlay (hidden while playing, visible on hover/pause) + var video = embedElement.querySelector("video"); + if (video) { + video.onplay = function () { + embedElement.classList.add("video-playing"); + }; + video.onpause = video.onended = function () { + embedElement.classList.remove("video-playing"); + }; + } + var lastHeight = 0; function sendHeight() { var currentHeight = embedElement.offsetHeight; if (currentHeight !== lastHeight && currentHeight > 0) { lastHeight = currentHeight; - window.parent.postMessage(['resizeIframe', { h: currentHeight, url: location.href }], '*'); + window.parent.postMessage( + ["resizeIframe", { h: currentHeight, url: location.href }], + "*", + ); } } // Respond to height requests from parent via MessageChannel - window.addEventListener('message', function(event) { + window.addEventListener("message", function (event) { if (event.source === window.parent && event.ports && event.ports[0]) { event.ports[0].postMessage(embedElement.offsetHeight); } }); - window.addEventListener('load', sendHeight); + window.addEventListener("load", sendHeight); new ResizeObserver(sendHeight).observe(embedElement); // Expose for embedTweet.js diff --git a/public/js/widgets.js b/public/js/widgets.js index 4b6bf49..7bb283a 100644 --- a/public/js/widgets.js +++ b/public/js/widgets.js @@ -8,10 +8,7 @@ if (window.__nitterWidgets) return; window.__nitterWidgets = true; - var scripts = document.querySelectorAll('script[src*="widgets.js"]'); - var NITTER = scripts.length - ? new URL(scripts[scripts.length - 1].src).origin - : location.origin; + var NITTER = new URL(document.currentScript.src).origin; var TWEET_RE = /(?:twitter\.com|x\.com)\/([^\/]+)\/status\/(\d+)/i; var SELECTOR = "blockquote.twitter-tweet, blockquote.twitter-video"; @@ -21,11 +18,15 @@ var isReady = false; function safeCall(fn, arg) { - try { fn(arg); } catch (e) {} + try { + fn(arg); + } catch (e) { } } function fireEvent(name, data) { - (eventCallbacks[name] || []).forEach(function (cb) { safeCall(cb, data); }); + (eventCallbacks[name] || []).forEach(function (cb) { + safeCall(cb, data); + }); } function parseTweetUrl(url) { @@ -44,8 +45,12 @@ var path = tweet.user ? "/" + tweet.user : "/i"; url = NITTER + path + "/status/" + tweet.id + "/embed"; if (opts.theme) { - var theme = opts.theme === "dark" ? "nitter" : - opts.theme === "light" ? "twitter" : opts.theme; + var theme = + opts.theme === "dark" + ? "nitter" + : opts.theme === "light" + ? "twitter" + : opts.theme; url += "?theme=" + encodeURIComponent(theme); } } @@ -60,11 +65,18 @@ if (opts.videoOnly) iframe.setAttribute("allowfullscreen", "true"); var width = opts.width || 550; - var margin = opts.align === "center" ? "10px auto" : - opts.align === "right" ? "10px 0 10px auto" : "10px 0"; + var margin = + opts.align === "center" + ? "10px auto" + : opts.align === "right" + ? "10px 0 10px auto" + : "10px 0"; iframe.style.cssText = - "width:100%;max-width:" + width + "px;height:250px;" + - "border:none;display:block;margin:" + margin; + "width:100%;max-width:" + + width + + "px;height:300px;" + + "border:none;display:block;margin:" + + margin; iframe.addEventListener("load", function () { fireEvent("rendered", { target: iframe }); @@ -89,7 +101,7 @@ width: d.mediaMaxWidth || d.width, align: d.align, theme: d.theme, - videoOnly: d.mediaMaxWidth !== undefined + videoOnly: d.mediaMaxWidth !== undefined, }); bq.style.display = "none"; @@ -98,7 +110,9 @@ } function processEmbeds(root) { - var bqs = (root || document).querySelectorAll(SELECTOR + ":not([data-nitter-processed])"); + var bqs = (root || document).querySelectorAll( + SELECTOR + ":not([data-nitter-processed])", + ); for (var i = 0; i < bqs.length; i++) processBlockquote(bqs[i]); } @@ -147,7 +161,7 @@ createTweet: embedTweet, createTweetEmbed: embedTweet, createVideo: embedTweet, - loaded: true + loaded: true, }, events: { bind: function (name, cb) { @@ -158,33 +172,44 @@ unbind: function (name, cb) { if (!eventCallbacks[name]) return; eventCallbacks[name] = cb - ? eventCallbacks[name].filter(function (f) { return f !== cb; }) + ? eventCallbacks[name].filter(function (f) { + return f !== cb; + }) : []; - } + }, }, ready: function (cb) { if (typeof cb !== "function") return; if (isReady) cb(window.twttr); else readyCallbacks.push(cb); }, - _e: [] + _e: [], }; // Process callbacks queued before load (twttr._e pattern) if (prevTwttr && prevTwttr._e) { - prevTwttr._e.forEach(function (cb) { safeCall(cb); }); + prevTwttr._e.forEach(function (cb) { + safeCall(cb); + }); } // Remove any Twitter scripts that snuck through - document.querySelectorAll('script[src*="platform.twitter.com"], script[src*="platform.x.com"]') - .forEach(function (s) { s.remove(); }); + document + .querySelectorAll( + 'script[src*="platform.twitter.com"], script[src*="platform.x.com"]', + ) + .forEach(function (s) { + s.remove(); + }); function init() { window.addEventListener("message", handleResize); processEmbeds(); observeDOM(); isReady = true; - readyCallbacks.forEach(function (cb) { safeCall(cb, window.twttr); }); + readyCallbacks.forEach(function (cb) { + safeCall(cb, window.twttr); + }); readyCallbacks = []; } diff --git a/src/sass/tweet/embed.scss b/src/sass/tweet/embed.scss index 9ca1d48..9ff8403 100644 --- a/src/sass/tweet/embed.scss +++ b/src/sass/tweet/embed.scss @@ -88,6 +88,7 @@ html:has(body > .embed-video) { // Video-only embed .embed-video { position: relative; + min-height: 300px; background-color: black; border: 1px solid var(--border_grey); @@ -106,7 +107,7 @@ html:has(body > .embed-video) { width: 100%; } - .gallery-video > .attachment { + .gallery-video>.attachment { max-height: 560px; width: 100%; } @@ -135,7 +136,9 @@ html:has(body > .embed-video) { text-decoration: none; border-radius: 9999px; border: 1px solid transparent; - transition: background 0.15s, opacity 0.15s; + transition: + background 0.15s, + opacity 0.15s; z-index: 10; &:hover { @@ -143,8 +146,14 @@ html:has(body > .embed-video) { } } - &:has(video:playing) .video-overlay-link { + // Hide button while playing, show on hover or when paused + &.video-playing .video-overlay-link { opacity: 0; pointer-events: none; } + + &.video-playing:hover .video-overlay-link { + opacity: 1; + pointer-events: auto; + } } diff --git a/src/views/embed.nim b/src/views/embed.nim index 27fda24..82ef670 100644 --- a/src/views/embed.nim +++ b/src/views/embed.nim @@ -14,7 +14,7 @@ proc renderVideoEmbed*(tweet: Tweet; cfg: Config; req: Request): string = video = tweet.getVideos()[0] thumb = video.thumb vidUrl = getVideoEmbed(cfg, tweet.id) - prefs = Prefs(hlsPlayback: true, mp4Playback: true) + prefs = Prefs(hlsPlayback: true, mp4Playback: true, proxyVideos: true) tweetUrl = getLink(tweet) let node = buildHtml(html(lang="en")): diff --git a/src/views/general.nim b/src/views/general.nim index 331c1f2..143da54 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -50,7 +50,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; let opensearchUrl = getUrlPrefix(cfg) & "/opensearch" buildHtml(head): - link(rel="stylesheet", type="text/css", href="/css/style.css?v=101") + link(rel="stylesheet", type="text/css", href="/css/style.css?v=106") link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=7") if theme.len > 0: