mirror of
https://github.com/zedeus/nitter
synced 2026-09-05 22:59:31 +00:00
Improve video embed overlay and proxy behavior
This commit is contained in:
parent
b76e3c3913
commit
5fa4eefb9d
5 changed files with 82 additions and 32 deletions
|
|
@ -1,25 +1,41 @@
|
||||||
(function () {
|
(function () {
|
||||||
var embedElement = document.querySelector('.embed-wrapper, .tweet-embed, .embed-video');
|
var embedElement = document.querySelector(
|
||||||
|
".embed-wrapper, .tweet-embed, .embed-video",
|
||||||
|
);
|
||||||
if (!embedElement) return;
|
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;
|
var lastHeight = 0;
|
||||||
|
|
||||||
function sendHeight() {
|
function sendHeight() {
|
||||||
var currentHeight = embedElement.offsetHeight;
|
var currentHeight = embedElement.offsetHeight;
|
||||||
if (currentHeight !== lastHeight && currentHeight > 0) {
|
if (currentHeight !== lastHeight && currentHeight > 0) {
|
||||||
lastHeight = currentHeight;
|
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
|
// 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]) {
|
if (event.source === window.parent && event.ports && event.ports[0]) {
|
||||||
event.ports[0].postMessage(embedElement.offsetHeight);
|
event.ports[0].postMessage(embedElement.offsetHeight);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('load', sendHeight);
|
window.addEventListener("load", sendHeight);
|
||||||
new ResizeObserver(sendHeight).observe(embedElement);
|
new ResizeObserver(sendHeight).observe(embedElement);
|
||||||
|
|
||||||
// Expose for embedTweet.js
|
// Expose for embedTweet.js
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,7 @@
|
||||||
if (window.__nitterWidgets) return;
|
if (window.__nitterWidgets) return;
|
||||||
window.__nitterWidgets = true;
|
window.__nitterWidgets = true;
|
||||||
|
|
||||||
var scripts = document.querySelectorAll('script[src*="widgets.js"]');
|
var NITTER = new URL(document.currentScript.src).origin;
|
||||||
var NITTER = scripts.length
|
|
||||||
? new URL(scripts[scripts.length - 1].src).origin
|
|
||||||
: location.origin;
|
|
||||||
|
|
||||||
var TWEET_RE = /(?:twitter\.com|x\.com)\/([^\/]+)\/status\/(\d+)/i;
|
var TWEET_RE = /(?:twitter\.com|x\.com)\/([^\/]+)\/status\/(\d+)/i;
|
||||||
var SELECTOR = "blockquote.twitter-tweet, blockquote.twitter-video";
|
var SELECTOR = "blockquote.twitter-tweet, blockquote.twitter-video";
|
||||||
|
|
@ -21,11 +18,15 @@
|
||||||
var isReady = false;
|
var isReady = false;
|
||||||
|
|
||||||
function safeCall(fn, arg) {
|
function safeCall(fn, arg) {
|
||||||
try { fn(arg); } catch (e) {}
|
try {
|
||||||
|
fn(arg);
|
||||||
|
} catch (e) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
function fireEvent(name, data) {
|
function fireEvent(name, data) {
|
||||||
(eventCallbacks[name] || []).forEach(function (cb) { safeCall(cb, data); });
|
(eventCallbacks[name] || []).forEach(function (cb) {
|
||||||
|
safeCall(cb, data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTweetUrl(url) {
|
function parseTweetUrl(url) {
|
||||||
|
|
@ -44,8 +45,12 @@
|
||||||
var path = tweet.user ? "/" + tweet.user : "/i";
|
var path = tweet.user ? "/" + tweet.user : "/i";
|
||||||
url = NITTER + path + "/status/" + tweet.id + "/embed";
|
url = NITTER + path + "/status/" + tweet.id + "/embed";
|
||||||
if (opts.theme) {
|
if (opts.theme) {
|
||||||
var theme = opts.theme === "dark" ? "nitter" :
|
var theme =
|
||||||
opts.theme === "light" ? "twitter" : opts.theme;
|
opts.theme === "dark"
|
||||||
|
? "nitter"
|
||||||
|
: opts.theme === "light"
|
||||||
|
? "twitter"
|
||||||
|
: opts.theme;
|
||||||
url += "?theme=" + encodeURIComponent(theme);
|
url += "?theme=" + encodeURIComponent(theme);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60,11 +65,18 @@
|
||||||
if (opts.videoOnly) iframe.setAttribute("allowfullscreen", "true");
|
if (opts.videoOnly) iframe.setAttribute("allowfullscreen", "true");
|
||||||
|
|
||||||
var width = opts.width || 550;
|
var width = opts.width || 550;
|
||||||
var margin = opts.align === "center" ? "10px auto" :
|
var margin =
|
||||||
opts.align === "right" ? "10px 0 10px auto" : "10px 0";
|
opts.align === "center"
|
||||||
|
? "10px auto"
|
||||||
|
: opts.align === "right"
|
||||||
|
? "10px 0 10px auto"
|
||||||
|
: "10px 0";
|
||||||
iframe.style.cssText =
|
iframe.style.cssText =
|
||||||
"width:100%;max-width:" + width + "px;height:250px;" +
|
"width:100%;max-width:" +
|
||||||
"border:none;display:block;margin:" + margin;
|
width +
|
||||||
|
"px;height:300px;" +
|
||||||
|
"border:none;display:block;margin:" +
|
||||||
|
margin;
|
||||||
|
|
||||||
iframe.addEventListener("load", function () {
|
iframe.addEventListener("load", function () {
|
||||||
fireEvent("rendered", { target: iframe });
|
fireEvent("rendered", { target: iframe });
|
||||||
|
|
@ -89,7 +101,7 @@
|
||||||
width: d.mediaMaxWidth || d.width,
|
width: d.mediaMaxWidth || d.width,
|
||||||
align: d.align,
|
align: d.align,
|
||||||
theme: d.theme,
|
theme: d.theme,
|
||||||
videoOnly: d.mediaMaxWidth !== undefined
|
videoOnly: d.mediaMaxWidth !== undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
bq.style.display = "none";
|
bq.style.display = "none";
|
||||||
|
|
@ -98,7 +110,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function processEmbeds(root) {
|
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]);
|
for (var i = 0; i < bqs.length; i++) processBlockquote(bqs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,7 +161,7 @@
|
||||||
createTweet: embedTweet,
|
createTweet: embedTweet,
|
||||||
createTweetEmbed: embedTweet,
|
createTweetEmbed: embedTweet,
|
||||||
createVideo: embedTweet,
|
createVideo: embedTweet,
|
||||||
loaded: true
|
loaded: true,
|
||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
bind: function (name, cb) {
|
bind: function (name, cb) {
|
||||||
|
|
@ -158,33 +172,44 @@
|
||||||
unbind: function (name, cb) {
|
unbind: function (name, cb) {
|
||||||
if (!eventCallbacks[name]) return;
|
if (!eventCallbacks[name]) return;
|
||||||
eventCallbacks[name] = cb
|
eventCallbacks[name] = cb
|
||||||
? eventCallbacks[name].filter(function (f) { return f !== cb; })
|
? eventCallbacks[name].filter(function (f) {
|
||||||
|
return f !== cb;
|
||||||
|
})
|
||||||
: [];
|
: [];
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
ready: function (cb) {
|
ready: function (cb) {
|
||||||
if (typeof cb !== "function") return;
|
if (typeof cb !== "function") return;
|
||||||
if (isReady) cb(window.twttr);
|
if (isReady) cb(window.twttr);
|
||||||
else readyCallbacks.push(cb);
|
else readyCallbacks.push(cb);
|
||||||
},
|
},
|
||||||
_e: []
|
_e: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Process callbacks queued before load (twttr._e pattern)
|
// Process callbacks queued before load (twttr._e pattern)
|
||||||
if (prevTwttr && prevTwttr._e) {
|
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
|
// Remove any Twitter scripts that snuck through
|
||||||
document.querySelectorAll('script[src*="platform.twitter.com"], script[src*="platform.x.com"]')
|
document
|
||||||
.forEach(function (s) { s.remove(); });
|
.querySelectorAll(
|
||||||
|
'script[src*="platform.twitter.com"], script[src*="platform.x.com"]',
|
||||||
|
)
|
||||||
|
.forEach(function (s) {
|
||||||
|
s.remove();
|
||||||
|
});
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
window.addEventListener("message", handleResize);
|
window.addEventListener("message", handleResize);
|
||||||
processEmbeds();
|
processEmbeds();
|
||||||
observeDOM();
|
observeDOM();
|
||||||
isReady = true;
|
isReady = true;
|
||||||
readyCallbacks.forEach(function (cb) { safeCall(cb, window.twttr); });
|
readyCallbacks.forEach(function (cb) {
|
||||||
|
safeCall(cb, window.twttr);
|
||||||
|
});
|
||||||
readyCallbacks = [];
|
readyCallbacks = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ html:has(body > .embed-video) {
|
||||||
// Video-only embed
|
// Video-only embed
|
||||||
.embed-video {
|
.embed-video {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
min-height: 300px;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
border: 1px solid var(--border_grey);
|
border: 1px solid var(--border_grey);
|
||||||
|
|
||||||
|
|
@ -135,7 +136,9 @@ html:has(body > .embed-video) {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
transition: background 0.15s, opacity 0.15s;
|
transition:
|
||||||
|
background 0.15s,
|
||||||
|
opacity 0.15s;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
|
||||||
&:hover {
|
&: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;
|
opacity: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.video-playing:hover .video-overlay-link {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ proc renderVideoEmbed*(tweet: Tweet; cfg: Config; req: Request): string =
|
||||||
video = tweet.getVideos()[0]
|
video = tweet.getVideos()[0]
|
||||||
thumb = video.thumb
|
thumb = video.thumb
|
||||||
vidUrl = getVideoEmbed(cfg, tweet.id)
|
vidUrl = getVideoEmbed(cfg, tweet.id)
|
||||||
prefs = Prefs(hlsPlayback: true, mp4Playback: true)
|
prefs = Prefs(hlsPlayback: true, mp4Playback: true, proxyVideos: true)
|
||||||
tweetUrl = getLink(tweet)
|
tweetUrl = getLink(tweet)
|
||||||
|
|
||||||
let node = buildHtml(html(lang="en")):
|
let node = buildHtml(html(lang="en")):
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
|
||||||
let opensearchUrl = getUrlPrefix(cfg) & "/opensearch"
|
let opensearchUrl = getUrlPrefix(cfg) & "/opensearch"
|
||||||
|
|
||||||
buildHtml(head):
|
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")
|
link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=7")
|
||||||
|
|
||||||
if theme.len > 0:
|
if theme.len > 0:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue