Lazy Embedded Video

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

当前为 2016-04-15 提交的版本,查看 最新版本

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

QingJ © 2025

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