4 ms·
Click2load is an improvement, but embeds still suck. All I want is a plain link, so a while back I got fed up and wrote a short userscript to just rewrite the
by schiffern 2y ago
Click2load is an improvement, but embeds still suck.
All I want is a plain link, so a while back I got fed up and wrote a short userscript to just rewrite the page. Works surprisingly well for how simple it is.
// ==UserScript==
// @name Youtube UnEmbed
// @version 0.1
// @description Converts embedded Youtube iframes into links
// @match *://*/*
// @exclude *://*.youtube.com/*
// @exclude *://*.reddit.com/*
// @exclude *://looptube.io/*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
const SITE = "https://www.youtube.com"; //m.youtube Invidious etc
const LINK_TO_TIMESTAMP = true;
const SHOW_PREVIEW_IMAGE = false;
const replaceEmbeds = () => {
document.querySelectorAll('iframe').forEach((frame) => {
const frameURL = frame.src || frame.dataset?.src;
if (!frameURL) return;
const match = frameURL.match(/(^https?:)?\/\/(www\.)?youtube(-nocookie)?\.com\/embed\/([a-zA-Z0-9\-_]{11}).*?(\?.*((t|start)=([\dsmh]*)))?/i);
if (match?.length == 9) {
const newURL = SITE+"/watch?" + ((LINK_TO_TIMESTAMP && match[8]) ? "t="+match[8]+"&" : "") + "v="+match[4];
const elem = document.createElement("a")
elem.href = newURL;
if (SHOW_PREVIEW_IMAGE) {
let img = document.createElement("img");
img.src = "https://i.ytimg.com/vi/"+match[4]+"/mqdefault.jpg";
img.alt="Preview image of Youtube video";
// 320 x 180 preview. For more resolution options see
// https://medium.com/@viniciu_/how-to-get-the-default-thumbnail-url-for-a-youtube-video-b5497b3b6218
elem.appendChild(img);
} else {
elem.innerHTML = newURL;
}
frame.outerHTML = elem.outerHTML;
// common lazyload hiding methods
elem.style.display = "block !important";
elem.style.opacity = "100% !important";
elem.style.background = "transparent !important";
const parent = elem.parentElement;
if (parent) {
parent.style.display = "block !important";
parent.style.opacity = "100% !important";
parent.style.background = "transparent !important";
}
}
});
};
replaceEmbeds();
setInterval(replaceEmbeds, 3000);
})();
- KomoD 2y agoI suggest using MutationObserver instead of just running the replaceEmbeds function every 3s.
- schiffern 2y agoIntentionally left as an exercise for the reader. ;-D If you do, share it back! I'll switch to your version. Remember I threw this together in about half an hour, and maybe that amount of cleanup to post here. "Works for me" is the order of the day. The extra debugging alone would be longer than the whole project! Besides, the function that runs is so light it doesn't really seem worth it. It could even make performance worse, since for reliability you need to observe mutations across the entire DOM, which could occur a lot more often. So for performance you want to add some debouncing too, adding yet more complexity to what's supposed to be a 'quick and dirty' fix.
- eikaramba 2y agohere it is. i am however showing the preview image and replacing it with the iframe on click. basically click2load. by using mutationobserver it seems i don't need ublock to block the iframe, because as soon as the browser tries to include it on the page it gets replaced anyway (function() { 'use strict'; const SITE = "https://www.youtube.com"; const LINK_TO_TIMESTAMP = true; const SHOW_PREVIEW_IMAGE = true; const createPreviewElement = (videoId, newURL, frame) => { const container = document.createElement("div"); container.setAttribute('data-yt-preview', 'true'); container.style.position = "relative"; container.style.width = frame.width + "px"; container.style.height = frame.height + "px"; container.style.cursor = "pointer"; const img = document.createElement("img"); img.src = `https://i.ytimg.com/vi/${videoId}/mqdefault.jpg`; img.alt = "Preview image of Youtube video"; img.style.width = "100%"; img.style.height = "100%"; img.style.objectFit = "cover"; const playButton = document.createElement("div"); playButton.innerHTML = "▶"; playButton.style.position = "absolute"; playButton.style.top = "50%"; playButton.style.left = "50%"; playButton.style.transform = "translate(-50%, -50%)"; playButton.style.fontSize = "48px"; playButton.style.color = "white"; playButton.style.backgroundColor = "rgba(0, 0, 0, 0.7)"; playButton.style.borderRadius = "50%"; playButton.style.width = "80px"; playButton.style.height = "80px"; playButton.style.display = "flex"; playButton.style.justifyContent = "center"; playButton.style.alignItems = "center"; container.appendChild(img); container.appendChild(playButton); container.addEventListener("click", () => { const iframe = document.createElement("iframe"); iframe.src = frame.src + (frame.src.includes('?') ? '&' : '?') + 'autoplay=1'; iframe.width = frame.width; iframe.height = frame.height; iframe.frameBorder = "0"; iframe.allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"; iframe.allowFullscreen = true; iframe.setAttribute('data-yt-processed', 'true'); container.parentNode.replaceChild(iframe, container); }); return container; }; const replaceEmbed = (frame) => { const frameURL = frame.src || frame.dataset?.src; if (!frameURL) return; const match = frameURL.match(/(^https?:)?\/\/(www\.)?youtube(-nocookie)?\.com\/embed\/([a-zA-Z0-9\-_]{11}).*?(\?.*((t|start)=([\dsmh]*)))?/i); if (match?.length == 9) { const videoId = match[4]; const newURL = SITE + "/watch?" + ((LINK_TO_TIMESTAMP && match[8]) ? "t=" + match[8] + "&" : "") + "v=" + videoId; const previewElement = createPreviewElement(videoId, newURL, frame); frame.parentNode.replaceChild(previewElement, frame); // common lazyload hiding methods previewElement.style.display = "block !important"; previewElement.style.opacity = "100% !important"; previewElement.style.background = "transparent !important"; const parent = previewElement.parentElement; if (parent) { parent.style.display = "block !important"; parent.style.opacity = "100% !important"; parent.style.background = "transparent !important"; } } }; const observeDOM = () => { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList') { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) { if (node.tagName === 'IFRAME' && !node.hasAttribute('data-yt-processed')) { replaceEmbed(node); } else { node.querySelectorAll('iframe:not([data-yt-processed])').forEach(replaceEmbed); } } }); } }); }); observer.observe(document.body, { childList: true, subtree: true }); }; // Initial replacement document.querySelectorAll('iframe:not([data-yt-processed])').forEach(replaceEmbed); // Start observing DOM changes observeDOM(); })();
- Featherknight 2y agoGreat script idea! keeping this. Note, this does not prevent the initial loading of the embed itself, just replaces the embed with the link. The total transfer size of a page is still the same. If only there was a way to prevent the loading AND keep the link replacement.
- deleted 2y ago[deleted]
- schiffern 2y agoThanks. Despite being Not A Project I did edit to add optional image previews and reuse the regex object, so grab the final version if you want. I should have mentioned this is paired with uBlock Origin to block Youtube iframes (and indeed all iframes) globally. At the time I was writing it to unbreak embedded videos. https://github.com/gorhill/uBlock/wiki/Dynamic-filtering:-quick-guide https://github.com/gorhill/uBlock/wiki/Dynamic-filtering:-qu... https://github.com/gorhill/uBlock/wiki/Blocking-mode https://github.com/gorhill/uBlock/wiki/Blocking-mode https://github.com/gorhill/uBlock/wiki/Blocking-mode:-medium-mode https://github.com/gorhill/uBlock/wiki/Blocking-mode:-medium... From uBO My Rules tab: * youtube-nocookie.com * block * youtube.com * block * ytimg.com * block youtube.com youtube.com * noop youtube.com ytimg.com * noop To block all iframes (except sites you whitelist, see links above): * * 3p-frame block
- Featherknight 2y agoOh wow, I was just thinking that optional image previews would make this great, good stuff! The preview image doesn't auto-fit nicely in all layouts(using sonyalpharumors.com to test; works great for header banner embeds but potentially awkward sizing for post embeds) but I think that's due to the fixed image size and you not messing with the DOM outside of adding the img to it. Will you post this somewhere like github or greasyfork? I do have the 3rd party iframes blocked globally in uBo Firefox and works great with your script to replace the placeholder elements but Safari lacks an alternative sadly afaik
- 2y ago