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.

当前为 2018-05-07 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube Embedder
  3. // @namespace https://greasyfork.org/en/users/85671-jcunews
  4. // @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.
  5. // @author jcunews
  6. // @version 1.0.1
  7. // @license GNU AGPLv3
  8. // @match *://*/*
  9. // @exclude *://www.youtube.com/embed/*
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function(xe, fe, ce, rx, trx, trxg, hrx, lo) {
  14.  
  15. //===== CONFIGURATION BEGIN =====
  16.  
  17. //The minimum width & height of the container element where the link or URL text is found, in order to apply the conversion.
  18. var minimumWidth = 256;
  19. var minimumHeight = 144;
  20.  
  21. //Embedded YouTube frame width in pixels (should be multiplication of 16).
  22. //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).
  23. //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.
  24. var frameWidth = 0;
  25.  
  26. //Enable URL text to YouTube frame converter (aside from links).
  27. var convertUrlText = true;
  28.  
  29. //Convert to link only, when on these domains.
  30. var convertToLinkOnlyDomains = /www\.youtube\.com|somesite\.info|subnet\d+\.other\.net/i;
  31.  
  32. //===== CONFIGURATION END =====
  33.  
  34. function linkToFrame(node, m, pn, w, h, a) {
  35. if (node.attributes["ytebd_nocovert"] || !node.offsetWidth || !node.offsetHeight) return;
  36. if (!lo && (m = node.href.match(rx))) {
  37. m = "https://www.youtube.com/embed/" + m[1];
  38. pn = node.parentNode;
  39. while (pn) {
  40. if (pn.scrollHeight > pn.offsetHeight) break;
  41. pn = pn.parentNode;
  42. }
  43. h = ((pn || node.parentNode).offsetHeight * 0.9) >> 0;
  44. w = frameWidth > 0 ? frameWidth : node.parentNode.offsetWidth;
  45. if ((pn = (w / 16 * 9) >> 0) > h) {
  46. w = (h / 9 * 16) >> 0;
  47. } else h = pn;
  48. if ((w >= minimumWidth) && (h >= minimumHeight)) {
  49. c = document.createElement("IFRAME");
  50. c.src = m;
  51. c.allowFullscreen = true;
  52. c.referrerPolicy = "no-referrer";
  53. c.style.border = "none";
  54. c.width = w;
  55. c.height = h;
  56. c.setAttribute("ytebd_nocovert", "1");
  57. node.replaceWith(c);
  58. } else if (w && h) node.setAttribute("ytebd_nocovert", "1");
  59. } else node.setAttribute("ytebd_nocovert", "1");
  60. }
  61.  
  62. function processNode(node, c, m, w, h, pn) {
  63. switch (node.nodeType) {
  64. case Node.ELEMENT_NODE:
  65. case Node.DOCUMENT_NODE:
  66. if ((node.attributes && node.attributes["ytebd_nocovert"])) break;
  67. if (node.nodeName === "A") {
  68. linkToFrame(node);
  69. node = null;
  70. }
  71. if (node && (xe.indexOf(node.nodeName) < 0)) {
  72. if (fe.indexOf(node.nodeName) >= 0) {
  73. m = (m = node.src.match(/:\/\/(.*?)\//)) && (m[1] === location.hostname);
  74. } else m = true;
  75. if (m && ((node.nodeName !== "A") || convertUrlText)) {
  76. node.childNodes.forEach(processNode);
  77. }
  78. }
  79. break;
  80. case Node.TEXT_NODE:
  81. if (convertUrlText && (m = node.nodeValue.match(trx))) {
  82. w = node.nodeValue;
  83. h = -1;
  84. a = [];
  85. while (c = trxg.exec(w)) {
  86. if (c.index > 0) a.push(w.substring(h, c.index));
  87. a.push("ytebd_" + c[1]);
  88. h = c.index + c[0].length;
  89. }
  90. if ((h > 0) && (h < w.length)) a.push(w.substr(h));
  91. for (c = a.length - 1; c >= 0; c--) {
  92. if (!a[c].replace(/^\s+|\s+$/g, "")) a.splice(c, 1);
  93. }
  94. if (node.parentNode.nodeName === "A") {
  95. if (a.length === 1) {
  96. node.href = "https://www.youtube.com/embed/" + a[0].substr(6);
  97. node.rel = "nofollow noopener noreferrer";
  98. node.target = "_blank";
  99. }
  100. a = null;
  101. }
  102. if (a) {
  103. pn = node.parentNode;
  104. c = node.nextSibling;
  105. a.forEach(function(v, i, n) {
  106. if (v.substr(0, 6) !== "ytebd_") {
  107. n = document.createTextNode(v);
  108. } else {
  109. n = document.createElement("A");
  110. n.textContent = "[Link]";
  111. n.title = v.substr(6);
  112. n.href = "https://www.youtube.com/embed/" + v.substr(6);
  113. n.rel = "nofollow noopener noreferrer";
  114. n.target = "_blank";
  115. }
  116. if (i > 0) {
  117. pn.insertBefore(n, c);
  118. } else {
  119. node.replaceWith(n);
  120. }
  121. });
  122. }
  123. }
  124. }
  125. }
  126.  
  127. xe = ["BUTTON", "INPUT", "SCRIPT", "SELECT", "STYLE"];
  128. fe = ["FRAME", "IFRAME"];
  129. rx = /^(?:https?:\/\/)?(?:(?:(?:www\.)?youtube\.com\/)(?:embed\/|watch\?.*v=)|youtu\.be\/)([0-9a-z_\-]{11})/i;
  130. trx = /(?:https?:\/\/)?(?:(?:(?:www\.)?youtube\.com\/)(?:embed\/|watch\?.*v=)|youtu\.be\/)([0-9a-z_\-]{11})[^\s,'")\]}>]*/i;
  131. trxg = /(?:https?:\/\/)?(?:(?:(?:www\.)?youtube\.com\/)(?:embed\/|watch\?.*v=)|youtu\.be\/)([0-9a-z_\-]{11})[^\s,'")\]}>]*/gi;
  132. hrx = /^(?:(?:www\.)?youtube\.com|youtu\.be)$/i;
  133. lo = convertToLinkOnlyDomains.test(location.hostname);
  134.  
  135. (new MutationObserver(function(records) {
  136. records.forEach(function(record) {
  137. if (record.type === "childList") {
  138. record.addedNodes.forEach(processNode);
  139. } else {
  140. processNode(record.target);
  141. }
  142. });
  143. })).observe(document.body, {attributes: true, attributeFilter: ["class", "style"], childList: true, subtree: true});
  144.  
  145. if (document.body) processNode(document.body);
  146.  
  147. })();