Lazy Embedded Video

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

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

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

QingJ © 2025

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