Greasy Fork is available in English.

YouTube Embedder

Convert links and optionally URL text which points to a YouTube video, into embedded YouTube frame. For URL text, it can be converted to a link only. The main function can be configured for on-demand-only for slow computers. If enabled, the main function can be executed via bookmarklet using this URL: javascript:yte_ujs()

// ==UserScript==
// @name         YouTube Embedder
// @namespace    https://greasyfork.org/en/users/85671-jcunews
// @description  Convert links and optionally URL text which points to a YouTube video, into embedded YouTube frame. For URL text, it can be converted to a link only. The main function can be configured for on-demand-only for slow computers. If enabled, the main function can be executed via bookmarklet using this URL: javascript:yte_ujs()
// @author       jcunews
// @version      1.0.6
// @license      GNU AGPLv3
// @match        *://*/*
// @exclude      *://www.youtube.com/embed/*
// @grant        none
// ==/UserScript==

(function(xe, fe, ce, rx, trx, trxg, hrx, lo, qu) {

  //===== CONFIGURATION BEGIN =====

  //The minimum width & height of the container element where the link or URL text is found, in order to apply the conversion.
  var minimumWidth  = 256;
  var minimumHeight = 144;

  //Embedded YouTube frame width in pixels (should be multiplication of 16).
  //If zero, the width is the container element width of the link or URL text, and the height is calculated using 16:9 ratio (widescreen video dimension).
  //If the final width is smaller than the minimum width setting, and if the source is an URL text, it will be converted to a link instead.
  var frameWidth = 0;

  //Enable URL text to YouTube frame converter (aside from links).
  var convertUrlText = true;

  //Convert to link only, when on these domains.
  var convertToLinkOnlyDomains = /somesite\.info|subnet\d+\.other\.net/i;

  //Delay between processing each node scanning queue. Lower value means faster scanning, but more CPU intensive.
  //Higher value means lesser CPU usage, but also means slower scanning.
  var queueDelay = 50; //in milliseconds. 1000ms = 1 second.

  //Number of nodes to process at a time. Adjust this and queueDelay settings to find the best performance without triggering the web browser's busy dialog.
  //Lower value means lesser CPU usage, but also means slower scanning.
  var nodesPerProcessing = 5;

  //The onDemandOnlyDomains makes the main function available on-demand-only for specific or all sites.
  //If enabled, it's accessible via bookmarklet using this URL: javascript:yte_ujs()
  //This setting is for slow computers, since the main function is CPU intensive.
  var onDemandOnlyDomains = /somesite\.info|subnet\d+\.other\.net/i;

  //===== CONFIGURATION END =====

  function linkToFrame(node, m, pn, w, h, a) {
    if (node.ytebd_nocovert || !node.offsetWidth || !node.offsetHeight) return;
    if (!lo && (m = node.href.match(rx))) {
      m = "https://www.youtube.com/embed/" + m[1];
      pn = node.parentNode;
      while (pn) {
        if (pn.scrollHeight > pn.offsetHeight) break;
        pn = pn.parentNode;
      }
      h = ((pn || node.parentNode).offsetHeight * 0.9) >> 0;
      w = frameWidth > 0 ? frameWidth : node.parentNode.offsetWidth;
      if ((pn = (w / 16 * 9) >> 0) > h) {
        w = (h / 9 * 16) >> 0;
      } else h = pn;
      if ((w >= minimumWidth) && (h >= minimumHeight)) {
        c = document.createElement("IFRAME");
        c.src = m;
        c.allowFullscreen = true;
        c.referrerPolicy = "no-referrer";
        c.style.border = "none";
        c.width = w;
        c.height = h;
        c.ytebd_nocovert = 1;
        node.replaceWith(c);
      } else if (w && h) node.ytebd_nocovert = 1;
    } else node.ytebd_nocovert = 1;
  }

  function processNode(node, c, m, w, h, pn) {
    switch (node.nodeType) {
      case Node.ELEMENT_NODE:
      case Node.DOCUMENT_NODE:
        if (node.ytebd_nocovert) break;
        if (node.nodeName === "A") {
          linkToFrame(node);
          node = null;
        }
        if (node && (xe.indexOf(node.nodeName) < 0)) {
          if (fe.indexOf(node.nodeName) >= 0) {
            m = (m = node.src.match(/:\/\/(.*?)\//)) && (m[1] === location.hostname);
          } else m = true;
          if (m && ((node.nodeName !== "A") || convertUrlText)) {
            node.childNodes.forEach(queue);
          }
        }
        break;
      case Node.TEXT_NODE:
        if (convertUrlText && (m = node.nodeValue.match(trx))) {
          w = node.nodeValue;
          h = -1;
          a = [];
          while (c = trxg.exec(w)) {
            if (c.index > 0) a.push(w.substring(h, c.index));
            a.push(c);
            h = c.index + c[0].length;
          }
          if ((h > 0) && (h < w.length)) a.push(w.substr(h));
          for (c = a.length - 1; c >= 0; c--) {
            if (!Array.isArray(a[c]) && !a[c].replace(/^\s+|\s+$/g, "")) a.splice(c, 1);
          }
          if (node.parentNode.nodeName === "A") {
            if (a.length === 1) {
              node.href = "https://www.youtube.com/embed/" + a[0][1];
              node.rel = "nofollow noopener noreferrer";
              node.target = "_blank";
            }
            a = null;
          }
          if (a) {
            pn = node.parentNode;
            c = node.nextSibling;
            a.forEach(function(v, i, n) {
              if (!Array.isArray(v)) {
                n = document.createTextNode(v);
              } else {
                n = document.createElement("A");
                n.textContent = v[0];
                n.title = v[1];
                n.href = "https://www.youtube.com/embed/" + v[1];
                n.rel = "nofollow noopener noreferrer";
                n.target = "_blank";
              }
              if (i > 0) {
                pn.insertBefore(n, c);
              } else {
                node.replaceWith(n);
              }
              linkToFrame(n);
            });
          }
        }
    }
  }

  function processQueue() {
    setTimeout(function(nodes) {
      nodes.forEach(processNode);
      if (qu.length) setTimeout(processQueue, queueDelay);
    }, queueDelay, qu.splice(0, nodesPerProcessing));
  }

  function queue(node) {
    qu.push(node);
    if (qu.length === 1) setTimeout(processQueue, queueDelay);
  }

  xe = ["BUTTON", "INPUT", "SCRIPT", "SELECT", "STYLE"];
  fe = ["FRAME", "IFRAME"];
  rx = /^(?:https?:\/\/)?(?:(?:(?:www\.)?youtube\.com\/)(?:embed\/|watch\?.*v=)|youtu\.be\/)([0-9a-z_\-]{11})/i;
  trx = /(?:https?:\/\/)?(?:(?:(?:www\.)?youtube\.com\/)(?:embed\/|watch\?.*v=)|youtu\.be\/)([0-9a-z_\-]{11})[^\s,'")\]}>]*/i;
  trxg = /(?:https?:\/\/)?(?:(?:(?:www\.)?youtube\.com\/)(?:embed\/|watch\?.*v=)|youtu\.be\/)([0-9a-z_\-]{11})[^\s,'")\]}>]*/gi;
  hrx = /^(?:(?:www\.)?youtube\.com|youtu\.be)$/i;
  lo = convertToLinkOnlyDomains.test(location.hostname);
  qu = [];

  if (nodesPerProcessing <= 0) nodesPerProcessing = 1;
  if (!document.body) return;

  if (onDemandOnlyDomains.test(location.hostname)) {
    window.yte_ujs = function() {
      queue(document.body);
      return undefined;
    };
  } else {
    if (document.body) queue(document.body);
    (new MutationObserver(function(records) {
      records.forEach(function(record) {
        if (record.type === "childList") {
          record.addedNodes.forEach(queue);
        } else queue(record.target);
      });
    })).observe(document.body, {attributes: true, attributeFilter: ["class", "style"], childList: true, subtree: true});
  }

})();