Lazy Embedded Video

Lazy load embedded videos from Youtube/Dailymotion/Vimeo/Rutube/Twitch/Ustream/Coub/Vine/Facebook

当前为 2016-06-23 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Lazy Embedded Video
  3. // @namespace zeusex81@gmail.com
  4. // @description Lazy load embedded videos from Youtube/Dailymotion/Vimeo/Rutube/Twitch/Ustream/Coub/Vine/Facebook
  5. // @version 2.7
  6. // @include *
  7. // @resource playIcon https://i.imgur.com/1aybyWN.png
  8. // @grant GM_getResourceURL
  9. // @run-at document-start
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. try {
  14. if(/^([^.]+\.)?(youtube|dailymotion|vimeo|rutube|twitch|ustream|coub|vine|facebook)\.[^.]+$/.test(top.location.hostname))
  15. return;
  16. } catch(e) {}
  17. var wnd = typeof(unsafeWindow) != "undefined" ? unsafeWindow : window;
  18. var getResourceURL = typeof(GM_getResourceURL) != "undefined" ? GM_getResourceURL :
  19. function(name) {
  20. switch(name) {
  21. case "playIcon" : return "//i.imgur.com/1aybyWN.png";
  22. }
  23. };
  24. var CSP = -1;
  25. var testCSP = function() {
  26. if(CSP == -1) {
  27. var script = document.createElement("SCRIPT");
  28. script.innerHTML = "CSP_AllowInlineScript = true;";
  29. document.head.appendChild(script);
  30. document.head.removeChild(script);
  31. CSP = wnd.CSP_AllowInlineScript ? 1 : 0;
  32. }
  33. return CSP;
  34. };
  35. var html, a;
  36. var createHtml = function(url, iframe, api, background_img) {
  37. /(.*\/\/(?:[^.\/]+\.)?([^.\/]+)\.[^.\/]+)\//i.test(url);
  38. var provider_url = RegExp.$1, provider_name = RegExp.$2, data_convert = "", extra_script = "", button_hsb = [];
  39. if(api.includes("yahooapis"))
  40. data_convert += "data = data.query.results.json;";
  41. switch(provider_name) {
  42. case "youtube" :
  43. button_hsb.push( 0, 100, 100);
  44. data_convert += "delete data.thumbnail_url;";
  45. extra_script += "var img = new Image();"+
  46. "img.onload = function() {"+
  47. "if(this.naturalWidth > 120) {"+
  48. "document.body.style.backgroundImage = 'url('+this.src+')';"+
  49. "if(this.src.includes('hqdefault_live'))"+
  50. "this.src = this.src.replace('hqdefault', 'maxresdefault');"+
  51. "} else if(this.src.includes('maxresdefault')) {"+
  52. "this.src = this.src.replace('maxresdefault', 'sddefault');"+
  53. "} else if(this.src.includes('hqdefault_live')) {"+
  54. "this.src = this.src.replace('hqdefault_live', 'maxresdefault');"+
  55. "}"+
  56. "};"+
  57. "img.src = '"+background_img.match(/\/(.+)\//)[0]+"hqdefault_live.jpg';";
  58. break;
  59. case "dailymotion" :
  60. button_hsb.push( 60, 30, 300);
  61. data_convert += "var img = new Image();"+
  62. "img.onload = function() { document.body.style.backgroundImage = 'url('+this.src+')'; };"+
  63. "img.src = removeProtocol(data.thumbnail_url.replace(/\\/x240[^.]+/i, ''));"+
  64. "delete data.thumbnail_url;";
  65. break;
  66. case "vimeo" :
  67. button_hsb.push(220, 50, 220);
  68. data_convert += "data.thumbnail_url = data.thumbnail_url.replace(/_\\d+/i, '');";
  69. break;
  70. case "rutube" :
  71. button_hsb.push( 0, 0, 100);
  72. data_convert += "data.thumbnail_url = data.thumbnail_url.replace(/\\?.+/i, '');";
  73. break;
  74. case "twitch" :
  75. button_hsb.push(270, 50, 100);
  76. if(background_img) { // channel live
  77. data_convert += "data.title = data.status || 'Untitled Broadcast';"+
  78. "data.author_url = '"+provider_url+"/'+data.name+'/profile';"+
  79. "data.author_name = data.display_name;"+
  80. "data.duration = data.game && 'playing <a target=_blank href=\""+provider_url+
  81. "/directory/game/'+data.game+'\">'+data.game+'</b>';"+
  82. "offline_image = data.video_banner;";
  83. extra_script += "function jsonpCallback2(data) {"+
  84. "if(data.streams.length != 0) return;"+
  85. "document.body.style.backgroundImage = 'url('+removeProtocol(offline_image)+')';"+
  86. "document.getElementById('duration').textContent = 'offline';"+
  87. "}"+
  88. "</script>"+
  89. "<script defer src='https://api.twitch.tv/kraken/streams?channel="+url.match(/[^\/]+$/)[0]+"&callback=jsonpCallback2'>";
  90. } else { // video recorded
  91. data_convert += "data.thumbnail_url = data.preview.replace(/\\d+x\\d+/i, '0x0');"+
  92. "data.author_url = '"+provider_url+"/'+data.channel.name+'/profile';"+
  93. "data.author_name = data.channel.display_name;"+
  94. "data.duration = data.length;";
  95. }
  96. break;
  97. case "ustream" :
  98. button_hsb.push( 40, 50, 230);
  99. if(background_img) // channel live
  100. data_convert += "delete data.thumbnail_url;";
  101. break;
  102. case "coub" :
  103. button_hsb.push(240, 150, 100);
  104. data_convert += "data.author_url = data.channel_url;";
  105. break;
  106. case "vine" :
  107. button_hsb.push(170, 150, 150);
  108. break;
  109. case "facebook" :
  110. button_hsb.push( 0, 0, 300);
  111. data_convert += "if(/<a.*?>(.+)<\\/a><p>/i.test(data.html))"+
  112. "data.title = RegExp.$1;";
  113. break;
  114. }
  115. if(!html) html = [
  116. "<!doctype html>"+
  117. "<html>"+
  118. "<head>"+
  119. "<title>Lazy Embedded Video</title>"+
  120. "<script defer src='", api, "&callback=jsonpCallback'></script>"+
  121. "<script>"+
  122. "function removeProtocol(url) { return url.replace(/^[a-z]+:/i, ''); };"+
  123. "function jsonpCallback(data) {",
  124. data_convert,
  125. "if(data.thumbnail_url) document.body.style.backgroundImage = 'url('+removeProtocol(data.thumbnail_url)+')';"+
  126. "if(data.url) document.getElementById('title').href = removeProtocol(data.url);"+
  127. "if(data.title) document.getElementById('title').textContent = "+
  128. "document.getElementById('title').title = data.title;"+
  129. "if(data.author_url) document.getElementById('author').href = removeProtocol(data.author_url);"+
  130. "if(data.author_name) document.getElementById('author').textContent = data.author_name;"+
  131. "if(data.duration) {"+
  132. "if(Number(data.duration))"+
  133. "document.getElementById('duration').textContent = new Date(data.duration*1000).toISOString().substr(11,8);"+
  134. "else document.getElementById('duration').innerHTML = data.duration;"+
  135. "}"+
  136. "}",
  137. extra_script,
  138. "</script>"+
  139. "<style>"+
  140. "html { height:100%; }"+
  141. "body { margin:0; height:100%; color:white; font:14px sans-serif;"+
  142. "background:black ", background_img, " center/100% no-repeat; }"+
  143. "a { color:inherit; font-weight:bold; text-decoration:none; }"+
  144. "a:hover { text-decoration:underline; }"+
  145. "ul { margin:0; padding:0; list-style:none; }"+
  146. "#infobar { position:absolute; top:0px; width:100%; padding:8px 16px;"+
  147. "box-sizing:border-box; background:rgba(0,0,0,0.5); word-wrap:break-word; }"+
  148. "#infobar_right { float:right; margin-left:16px; text-align:right; text-transform:capitalize; }"+
  149. "#button { height:100%; cursor:pointer; background-position:0px 50%; }"+
  150. "#button:hover { background-position:-70px 50%;"+
  151. (navigator.userAgent.includes("Firefox") ? "" : "-webkit-")+"filter:"+
  152. "hue-rotate(", button_hsb[0], "deg) saturate(", button_hsb[1], "%) brightness(", button_hsb[2], "%);}"+
  153. "#button > div { width:70px; height:100%; margin:auto; background-repeat:no-repeat; background-position:inherit; "+
  154. "background-image:url("+getResourceURL("playIcon")+"); }"+
  155. "#titleBlock { overflow:hidden; max-height:34px; }"+
  156. "</style>"+
  157. "</head>"+
  158. "<body>"+
  159. "<div id=button onclick='location.replace(\"", iframe, "\");'><div></div></div>"+
  160. "<div id=infobar>"+
  161. "<ul id=infobar_right>"+
  162. "<li><a id=author target=_blank></a></li>"+
  163. "<li><a id=provider target=_blank href='", provider_url, "'>", provider_name, "</a></li>"+
  164. "</ul>"+
  165. "<ul>"+
  166. "<li id=titleBlock><a id=title target=_blank href='", url, "'>", url, "</a></li>"+
  167. "<li id=duration></li>"+
  168. "</ul>"+
  169. "</div>"+
  170. "</body>"+
  171. "</html>"
  172. ];
  173. html[ 1] = api;
  174. html[ 3] = data_convert;
  175. html[ 5] = extra_script;
  176. html[ 7] = background_img;
  177. html[ 9] = button_hsb[0];
  178. html[11] = button_hsb[1];
  179. html[13] = button_hsb[2];
  180. html[15] = iframe;
  181. html[17] = provider_url;
  182. html[19] = provider_name;
  183. html[21] = url;
  184. html[23] = url;
  185. };
  186. var createOembed = function(api, url) { return api+encodeURIComponent(url); };
  187. var createNOembed = function(api, url) { return createOembed("//noembed.com/embed?url=", url); };
  188. var createYOembed = function(api, url) { return createOembed("//query.yahooapis.com/v1/public/yql?format=json&q=",
  189. 'SELECT * FROM json WHERE url="'+createOembed(location.protocol+api,url)+'"'); };
  190. var createLazyVideo = function(elem) {
  191. if(elem.tagName == "IFRAME" && elem.srcdoc) return true;
  192. var id, args, url = elem.src || elem.data || elem.dataset.src;
  193. if(!url) return true;
  194. if(!a) a = document.createElement("A");
  195. a.href = url;
  196. /([^.]+)\.[^.]+$/i.test(a.hostname);
  197. switch(RegExp.$1) {
  198. case "youtube" :
  199. if(/\/(?:v|embed)\/([^&]*)/i.test(a.pathname))
  200. id = RegExp.$1 || (/[?&]v=([^&]+)/i.test(a.search) && RegExp.$1);
  201. if(!id || !testCSP()) return !id;
  202. args = "?autoplay=1";
  203. if(/[?&](list=[^&]+)/i.test(a.search)) args += "&"+RegExp.$1;
  204. if(/[?&](start=[^&]+)/i.test(a.search)) args += "&"+RegExp.$1;
  205. createHtml(url =
  206. "//www.youtube.com/watch"+args+"&v="+id,
  207. "//www.youtube.com/embed/"+id+args,
  208. createNOembed("//www.youtube.com/oembed?format=json&url=", location.protocol+url),
  209. "url(//i.ytimg.com/vi/"+id+"/hqdefault.jpg)"
  210. );
  211. break;
  212. case "dailymotion" :
  213. if(/\/(?:swf|embed)\/video\/([^&_]+)/i.test(a.pathname)) id = RegExp.$1;
  214. if(!id || !testCSP()) return !id;
  215. args = "?autoplay=1";
  216. if(/[?&](mute=[^&]+)/i.test(a.search)) args += "&"+RegExp.$1;
  217. if(/[?&](start=[^&]+)/i.test(a.search)) args += "&"+RegExp.$1;
  218. createHtml(url =
  219. "//www.dailymotion.com/video/"+id+args,
  220. "//www.dailymotion.com/embed/video/"+id+args,
  221. createOembed("//www.dailymotion.com/services/oembed?format=json&url=", location.protocol+url),
  222. "url(//www.dailymotion.com/thumbnail/video/"+id+")"
  223. );
  224. break;
  225. case "vimeo" :
  226. if(/\/(?:moogaloop\.swf|video\/)([^&]*)/i.test(a.pathname))
  227. id = RegExp.$1 || (/[?&]clip_id=([^&]+)/i.test(a.search) && RegExp.$1);
  228. if(!id || !testCSP()) return !id;
  229. args = "?autoplay=1";
  230. if(/[?&](loop=[^&]+)/i.test(a.search)) args += "&"+RegExp.$1;
  231. if(/(\#t=[\dhms]+)/i.test(a.hash)) args += RegExp.$1;
  232. createHtml(url =
  233. "//vimeo.com/"+id+args,
  234. "//player.vimeo.com/video/"+id+args,
  235. createOembed("//vimeo.com/api/oembed.json?url=", url)
  236. );
  237. break;
  238. case "rutube" :
  239. if(/\/play\/embed\/([^&.\/]+)/i.test(a.pathname)) id = RegExp.$1;
  240. if(!id || !testCSP()) return !id;
  241. args = "?autoStart=1";
  242. if(/[?&](bmstart=[^&]+)/i.test(a.search)) args += "&"+RegExp.$1;
  243. createHtml(url =
  244. "//rutube.ru/"+(isNaN(id) ? "video/"+id+"/" : "tracks/"+id+".html/")+args,
  245. "//rutube.ru/play/embed/"+id+args,
  246. createOembed("//rutube.ru/api/oembed/?format=jsonp&url=", url)
  247. );
  248. break;
  249. case "twitch" :
  250. if(/[?&](channel|video)=([^&]+)/i.test(a.search)) {args = RegExp.$1; id = RegExp.$2;}
  251. else if(/\/(.+)\/embed/i.test(a.pathname)) {args = "channel"; id = RegExp.$1;}
  252. if(!id || !testCSP()) return !id;
  253. createHtml(url =
  254. "//www.twitch.tv/"+(args=="video" ? id.replace("v","c/v/") : id),
  255. "//player.twitch.tv/?autoplay=true&"+args+"="+id,
  256. "https://api.twitch.tv/kraken/"+args+"s/"+id+"?",
  257. args=="channel" ? "url(//static-cdn.jtvnw.net/previews-ttv/live_user_"+id+"-0x0.jpg)" : null
  258. );
  259. break;
  260. case "ustream" :
  261. if(/(?:\/embed)?\/(channel\/|recorded\/)?([^&]+)/i.test(a.pathname)) {args = RegExp.$1 || "channel/"; id = RegExp.$2;}
  262. if(!id || !testCSP()) return !id;
  263. createHtml(url =
  264. "//www.ustream.tv/"+args+id,
  265. "//www.ustream.tv/embed/"+(args=="channel/" ? "" : args)+id+"?html5ui=1&autoplay=1",
  266. createYOembed("//www.ustream.tv/oembed?format=json&url=", url),
  267. args=="channel/" && !isNaN(id) ? "url(//static-cdn1.ustream.tv/i/channel/live/1_"+id+",640x360,b:0.jpg)" : null
  268. );
  269. break;
  270. case "coub" :
  271. if(/\/embed\/([^&]+)/i.test(a.pathname)) id = RegExp.$1;
  272. if(!id || !testCSP()) return !id;
  273. createHtml(url =
  274. "//coub.com/view/"+id,
  275. "//coub.com/embed/"+id+"?startWithHD=true&autostart=true",
  276. createYOembed("//coub.com/api/oembed.json?url=", url)
  277. );
  278. break;
  279. case "vine" :
  280. if(/\/v\/([^&]+)\/embed\/([^&]+)/i.test(a.pathname)) {args = RegExp.$2; id = RegExp.$1;}
  281. if(!id || !testCSP()) return !id;
  282. createHtml(url =
  283. "//vine.co/v/"+id,
  284. "//vine.co/v/"+id+"/embed/"+args+"?audio=1",
  285. createOembed("//vine.co/oembed.json?url=", url)
  286. );
  287. break;
  288. case "facebook" :
  289. if(a.pathname.endsWith("/plugins/video.php") && /[?&]href=([^&]+)/i.test(a.search)) {
  290. url = decodeURIComponent(RegExp.$1).replace(/^[a-z]+:/i, '');
  291. if(/\/videos.*?\/(\d+)/i.test(url)) id = RegExp.$1;
  292. }
  293. if(!id || !testCSP()) return !id;
  294. createHtml(
  295. url,
  296. "//www.facebook.com/plugins/video.php?autoplay=1&href="+encodeURIComponent(location.protocol+url),
  297. createOembed("//www.facebook.com/plugins/video/oembed.json/?url=", location.protocol+url),
  298. "url(//graph.facebook.com/"+id+"/picture)"
  299. );
  300. break;
  301. default :
  302. return true;
  303. }
  304. if(elem.tagName != "IFRAME") {
  305. var iframe = document.createElement("IFRAME");
  306. iframe.id = elem.id;
  307. iframe.className = elem.className;
  308. iframe.style.cssText = elem.style.cssText;
  309. if(!iframe.style.width && elem.width ) iframe.style.width = elem.width+"px";
  310. if(!iframe.style.height && elem.height) iframe.style.height = elem.height+"px";
  311. if(elem.parentNode.tagName == "OBJECT") {
  312. elem = elem.parentNode;
  313. iframe.style.cssText = elem.style.cssText + iframe.style.cssText;
  314. if(!iframe.style.width && elem.width ) iframe.style.width = elem.width+"px";
  315. if(!iframe.style.height && elem.height) iframe.style.height = elem.height+"px";
  316. }
  317. if(!iframe.style.borderWidth) iframe.style.borderWidth = (elem.border||0)+"px";
  318. switch(elem.align) {
  319. case "left" : case "right" :
  320. if(!iframe.style.float) iframe.style.float = elem.align; break;
  321. case "top" : case "middle" : case "bottom" :
  322. if(!iframe.style.verticalAlign) iframe.style.verticalAlign = elem.align; break;
  323. }
  324. elem.parentNode.replaceChild(iframe, elem);
  325. elem = iframe;
  326. }
  327. elem.allowFullscreen = true;
  328. elem.srcdoc = html.join("");
  329. return true;
  330. };
  331. var update = function() {
  332. // convert NodeList to Array because for some reason sometimes I wasn't able to read src when iterating directly through NodeList
  333. var nodes = ["IFRAME", "EMBED", "OBJECT"].reduce(function(sum, value) {
  334. return sum.concat([].slice.call(document.getElementsByTagName(value)));
  335. }, frameElement ? [frameElement] : []);
  336. for(var i = 0; i < nodes.length; i++)
  337. if(!createLazyVideo(nodes[i])) return;
  338. if(document.readyState == "loading") requestAnimationFrame(update);
  339. };
  340. update();
  341. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址