Hacker News new | past | comments | ask | show | jobs | submit login

here 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();
})();



For sonyalpharumors.com, the image preview extends into the side panel for post embeds, do you have a fix?

Edit: On line 21 & 22, I changed the container width to grab `frame.parentNode.width` instead and did the same for height. Seems to work better for that site at least.




Consider applying for YC's W25 batch! Applications are open till Nov 12.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: