Youtube Always Play in Highest Quality Everywhere

Always play youtube videos in highest available quality, embedded ones too, everywhere.

当前为 2014-09-13 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Youtube Always Play in Highest Quality Everywhere
  3. // @description Always play youtube videos in highest available quality, embedded ones too, everywhere.
  4. // @namespace YAPiHQ
  5. // @include *
  6. // @run-at document-start
  7. // @grant none
  8. // @version 6.0.1
  9. // @license GNU GPL v3
  10. // ==/UserScript==
  11.  
  12. //Tested with:
  13. // Firefox 24.0 with Greasemonkey 1.12
  14. // Chrome 30.0 with Tampermonkey 3.5
  15.  
  16. //SETTINGS
  17. //Change default quality level by setting the variable defaultQuality to one of the following values:
  18. // small, medium, large, hd720, hd1080, highres
  19. // It's set to highres by default.
  20. // More information about the quality levels: https://developers.google.com/youtube/js_api_reference#Playback_quality
  21. var defaultQuality = "highres";
  22. //END OF SETTINGS
  23.  
  24. defaultQuality = verifyDefaultQuality(defaultQuality);
  25.  
  26. //console.log(window.location.href); //DEBUG
  27.  
  28. //On /v/ or /embed/ pages (loaded directly or via an iframe)
  29. if ((/^(https?\:\/\/(www\.)?youtube(-nocookie)?.com\/(v|embed|e)\/[a-zA-Z0-9+\-\_]{11})/).test(window.location.href)){
  30. //console.log("v/embed"); //DEBUG
  31. var nhref=window.location.href;
  32. var nhrefFragId = "";
  33.  
  34. //Split url by the the # fragment identifier into two parts for easier handling
  35. var rxFid = window.location.href.match(/^(.*?)#/);
  36. if (rxFid != null){
  37. nhref=rxFid[1];
  38. nhrefFragId=window.location.href.replace(nhref,"");
  39. }
  40. var rxVq = nhref.match(new RegExp("(\\?|\\&)vq=([^&]*)(\\&)?"));
  41. //If there is a vq parameter in the url and it's set to the value of defaultQuality, don't do anything
  42. if (!(rxVq != null && rxVq[2] === defaultQuality)){
  43. if (rxVq != null && rxVq[2] !== defaultQuality)
  44. {
  45. nhref = nhref.replace(new RegExp("(\\?|\\&)vq=[^&]*(\\&)?"),"$1vq=" +defaultQuality+ "$2");
  46. } else if (rxVq == null){
  47. nhref = nhref.replace(/^(https?\:\/\/(?:www\.)?youtube(?:-nocookie)?\.com\/(?:v|embed|e)\/[a-zA-Z0-9+\-\_]{11})\??/,"$1?vq=" +defaultQuality + "&");
  48. }
  49. }
  50. var rxAPI = nhref.match(new RegExp("(\\?|\\&)enablejsapi=([^&]*)(\\&)?"));
  51. //If there is an enablejsapi parameter in the url and it's set to 1, don't do anything
  52. if (!(rxAPI != null && rxAPI[2] === "1")){
  53. if (rxAPI != null && rxAPI[2] !== "1")
  54. {
  55. nhref = nhref.replace(new RegExp("(\\?|\\&)enablejsapi=[^&]*(\\&)?"),"$1enablejsapi=" +"1"+ "$2");
  56. } else if (rxAPI == null){
  57. nhref = nhref.replace(/^(https?\:\/\/(?:www\.)?youtube(?:-nocookie)?\.com\/(?:v|embed|e)\/[a-zA-Z0-9+\-\_]{11})\??/,"$1?enablejsapi=" + "1" + "&");
  58. }
  59. }
  60. if (nhref+nhrefFragId !== window.location.href)
  61. window.location = nhref + nhrefFragId;
  62. else {
  63. document.addEventListener('DOMContentLoaded', function(){
  64. //console.log("embed"); //DEBUG
  65. var interval = setInterval(function(){
  66. var player = document.getElementById("player").childNodes[0];
  67. if (player != undefined){
  68. if(player.nodeName.toLowerCase() != "embed"){
  69. //console.log("html5 embed"); //DEBUG
  70. ensureSetQuality(player);
  71. }
  72. clearInterval(interval);
  73. }
  74. },10);
  75. }, false);
  76. }
  77. //All the other pages
  78. } else {
  79. // Continue after the page is fully loaded
  80. document.addEventListener('DOMContentLoaded', execOnPageLoad, false);
  81. }
  82.  
  83. function execOnPageLoad(){
  84. //console.log("execOnPageLoad"); // DEBUG
  85. if ((/^(https?\:\/\/(www\.)?youtube.com\/(watch|user\/|channel\/))/).test(window.location.href)){
  86. //console.log("watch"); //DEBUG
  87. if (document.getElementById("gm-YAPiHQ") == null){
  88. var scr = document.createElement("script");
  89. scr.id = "gm-YAPiHQ";
  90. scr.innerHTML =
  91. ["var tag = document.createElement('script');",
  92. "tag.src = '//www.youtube.com/iframe_api';",
  93. "var firstScriptTag = document.getElementsByTagName('script')[0];",
  94. "firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);",
  95. "var ytplayer0;",
  96. "function onYouTubePlayerReady(playerId){",
  97. //" console.log('onReady1');", //DEBUG
  98. " ytplayer0 = window.top.document.getElementById('movie_player') || window.top.document.getElementById('movie_player-html5') || window.top.document.getElementById('movie_player-flash') || window.top.document.getElementById('video-player-flash') || window.top.document.getElementById('video-player');",
  99. " ytplayer0.setPlaybackQuality('" + defaultQuality + "');",
  100. " waitSetHQ();",
  101. "};",
  102. "function ytPlayerOnYouTubePlayerReady(playerId){",
  103. //" console.log('onReady2');", //DEBUG
  104. " ytplayer0 = window.top.document.getElementById('movie_player') || window.top.document.getElementById('movie_player-html5') || window.top.document.getElementById('movie_player-flash') || window.top.document.getElementById('video-player-flash') || window.top.document.getElementById('video-player');",
  105. " ytplayer0.setPlaybackQuality('" + defaultQuality + "');",
  106. " waitSetHQ();",
  107. "};",
  108. "var c;",
  109. "function waitSetHQ(wStart){",
  110. //" console.log('WSHQ');", //DEBUG
  111. " if (wStart==undefined)",
  112. " wStart = new Date();",
  113. " ytplayer0 = window.top.document.getElementById('movie_player') || window.top.document.getElementById('movie_player-html5') || window.top.document.getElementById('movie_player-flash') || window.top.document.getElementById('video-player-flash') || window.top.document.getElementById('video-player');",
  114. " ytplayer0.setPlaybackQuality('" + defaultQuality + "');",
  115. " if (c==undefined){",
  116. //" console.log(c);", //DEBUG
  117. " c='" + defaultQuality + "';",
  118. " var q = ['small','medium','large','hd720','hd1080','highres'];",
  119. " var avq = ytplayer0.getAvailableQualityLevels();",
  120. " for (var i=q.lastIndexOf('" + defaultQuality + "');i>=0;--i){",
  121. " if (avq.lastIndexOf(q[i]) >= 0){",
  122. " c=q[i];",
  123. " break;",
  124. " }",
  125. " }",
  126. " }",
  127. " if (c==ytplayer0.getPlaybackQuality()){",
  128. //" console.log(c+' '+ytplayer0.getPlaybackQuality());", //DEBUG
  129. " c=undefined;",
  130. " return;",
  131. " } else if ((new Date())-wStart<10000)",
  132. " setTimeout(function(){waitSetHQ(wStart)},10);",
  133. "}",
  134. ].join('\n');
  135. //console.log(scr.innerHTML); // DEBUG
  136. document.getElementsByTagName("body")[0].appendChild(scr);
  137. }
  138. var target = document.getElementById("player");
  139. var observer;
  140. try {
  141. observer = new MutationObserver(waitSetHQ); //Firefox (Gecko)
  142. } catch (e){
  143. observer = new WebKitMutationObserver(waitSetHQ); //Chrome (WebKit)
  144. }
  145. var config = { attributes: true, childList: false, characterData: true, subtree: false };
  146. observer.observe(target, config); // register observer
  147. } else if ((/^(https?\:\/\/(www\.)?youtube.com\/channels)/).test(window.location.href)){
  148. var target = document.body;
  149. var observer;
  150. try {
  151. observer = new MutationObserver(onChannelsMutation); //Firefox (Gecko)
  152. } catch (e){
  153. observer = new WebKitMutationObserver(onChannelsMutation); //Chrome (WebKit)
  154. }
  155. var config = { attributes: true, childList: true, characterData: true, subtree: true };
  156. observer.observe(target, config);
  157. //Everywhere else
  158. } else {
  159. searchAndReplace(); // Look for embedded videos
  160. // Keep watching for changes in page content, pass changed/inserted nodes to searchAndReplace()
  161. // https://developer.mozilla.org/en-US/docs/DOM/MutationObserver
  162. var target = document.body;
  163. var observer;
  164. try {
  165. observer = new MutationObserver(onMutation); //Firefox (Gecko)
  166. } catch (e){
  167. observer = new WebKitMutationObserver(onMutation); //Chrome (WebKit)
  168. }
  169. var config = { attributes: true, childList: true, characterData: true, subtree: true };
  170. observer.observe(target, config); // register observer
  171. }
  172. }
  173.  
  174. //Caller must make sure that videoObj.setPlaybackQuality will be defined at some point,
  175. // if not, the function will keep calling itself every 100ms on videoObj until the 10s timeout.
  176. function ensureSetQuality(videoObj,highestAQ,firstCalledAt){
  177. //console.log("ensureSetQuality()" + firstCalledAt); //DEBUG
  178. if (firstCalledAt==undefined)
  179. firstCalledAt = new Date();
  180. else if ((new Date()-firstCalledAt) > 10000)
  181. return;
  182. if (typeof(videoObj.setPlaybackQuality) == "function"){
  183. videoObj.setPlaybackQuality(defaultQuality);
  184. if (videoObj.getPlayerState() == 1 || videoObj.getPlayerState() == 3)
  185. videoObj.playVideo(); // to avoid reload glitch after pausing/starting again
  186. //Store the defaultQuality or the next highest available quality in highestAQ
  187. if (highestAQ==undefined){
  188. var q = ["small","medium","large","hd720","hd1080","highres"];
  189. for (var i=q.lastIndexOf(defaultQuality);i>=0;--i){
  190. if ((videoObj.getAvailableQualityLevels()).lastIndexOf(q[i]) >= 0){
  191. highestAQ=q[i];
  192. break;
  193. }
  194. }
  195. }
  196. if (videoObj.getPlaybackQuality() != highestAQ){
  197. setTimeout(function(){ensureSetQuality(videoObj,highestAQ,firstCalledAt)},100)
  198. }
  199. } else
  200. setTimeout(function(){ensureSetQuality(videoObj,highestAQ,firstCalledAt)},100);
  201. }
  202.  
  203. function onChannelsMutation(mutations){
  204. mutations.forEach(function(mutation) {
  205. if (mutation.type === "attributes"){
  206. if (mutation.target.nodeName.toLowerCase() == "embed"){
  207. if (typeof(mutation.target.setPlaybackQuality == "function")){
  208. //console.log("attr hit"); //DEBUG
  209. ensureSetQuality(ensureSetQuality);
  210. }
  211. }
  212. } else if (mutation.type === "childList"){
  213. for (var i=0;i<mutation.addedNodes.length;++i)
  214. if (mutation.addedNodes[i].nodeName.toLowerCase() == "embed"){
  215. if (typeof(mutation.addedNodes[i].setPlaybackQuality == "function")){
  216. //console.log("added hit"); //DEBUG
  217. ensureSetQuality(mutation.addedNodes[i]);
  218. }
  219. }
  220. }
  221. });
  222. }
  223.  
  224. function onMutation(mutations){
  225. var significantNodes = [];
  226. mutations.forEach(function(mutation) {
  227. //console.log(mutation.type); // DEBUG
  228. if (mutation.type === "attributes"){
  229. significantNodes[significantNodes.length] = mutation.target;
  230. } else if (mutation.type === "childList"){
  231. // console.log("onMutation: childList"); //DEBUG
  232. for (var i=0;i<mutation.addedNodes.length;++i)
  233. significantNodes[significantNodes.length] = mutation.addedNodes[i];
  234. }
  235. });
  236. //console.log("significantNodes length: " + significantNodes.length); // DEBUG
  237. if (significantNodes.length>0)
  238. searchAndReplace(significantNodes);
  239. }
  240.  
  241. function recAddToArr(obj,array){
  242. array.push(obj);
  243. }
  244.  
  245. function onMutation(mutations){
  246. var significantNodes = [];
  247. mutations.forEach(function(mutation) {
  248. //console.log(mutation.type); // DEBUG
  249. if (mutation.type === "attributes"){
  250. significantNodes[significantNodes.length] = mutation.target;
  251. } else if (mutation.type === "childList"){
  252. // console.log("onMutation: childList"); //DEBUG
  253. for (var i=0;i<mutation.addedNodes.length;++i)
  254. significantNodes[significantNodes.length] = mutation.addedNodes[i];
  255. }
  256. });
  257. //console.log("significantNodes length: " + significantNodes.length); // DEBUG
  258. if (significantNodes.length>0)
  259. searchAndReplace(significantNodes);
  260. }
  261.  
  262. function searchAndReplace(pNodes){
  263. // console.log("searchAndReplace()"); //DEBUG
  264. if (typeof pNodes == "object") //if searchAndReplace() was called because the page content changed and the function received the changed nodes
  265. {
  266. // console.log("searchAndReplace(pNodes) : " + pNodes.length); // DEBUG
  267. for (var i=0;i<pNodes.length;++i){
  268. // console.log(pNodes[i].nodeName.toLowerCase());
  269. // console.log(pNodes[i].getElementsByTagName("object").length); //DEBUG
  270. if ((/^embed$/i).test(pNodes[i].nodeName)){
  271. // console.log("embed mutation"); //DEBUG
  272. var embedSrc = pNodes[i].getAttribute("src");
  273. if (!embedSrc)
  274. continue;
  275. // console.log("embedSrc: " + embedSrc); //DEBUG
  276. var replaceUrl = replaceSrc(embedSrc);
  277. if (embedSrc != replaceUrl)
  278. pNodes[i].setAttribute("src",replaceUrl);
  279. } else if ((/^object$/i).test(pNodes[i].nodeName)){
  280. var objectData = pNodes[i].getAttribute("data");
  281. if (!objectData)
  282. continue;
  283. // console.log("objectData: " + objectData); //DEBUG
  284. var replaceUrl = replaceSrc(objectData);
  285. if (objectData != replaceUrl)
  286. pNodes[i].setAttribute("data",replaceUrl);
  287. var objectChildren = pNodes[i].getElementsByTagName("param");
  288. for (var j=0;j<objectChildren.length;++j){
  289. if (objectChildren[j].getAttribute("name") != "movie")
  290. continue;
  291. var paramMovie = objectChildren[j].getAttribute("value");
  292. if (paramMovie){
  293. // console.log("paramMovie: " + paramMovie); //DEBUG
  294. var replaceUrl = replaceSrc(paramMovie);
  295. if (paramMovie != replaceUrl)
  296. objectChildren[j].setAttribute("movie",replaceUrl);
  297. } // if
  298. } // for j
  299. } // else if
  300. } // for i
  301. } else {
  302. //console.log("searchAndReplace()"); // DEBUG
  303.  
  304. var lElems = new Array();
  305. lElems["object"] = "data";
  306. lElems["embed"] = "src";
  307. lElems["param"] = "value";
  308.  
  309. for (var cTag in lElems){
  310. var cParam = lElems[cTag];
  311. var tagArr = document.getElementsByTagName(cTag);
  312. for (var i=0;i<tagArr.length;++i){
  313. var tagParam = tagArr[i].getAttribute(cParam);
  314. if (!tagParam
  315. || (cTag == "param" && tagArr[i].getAttribute("name") != "movie"))
  316. continue;
  317. var replaceUrl = replaceSrc(tagParam);
  318. if (tagParam != replaceUrl)
  319. tagArr[i].setAttribute(cParam,replaceUrl);
  320. } // for i
  321. } //for key
  322. } // else
  323. }
  324.  
  325. function replaceSrc(src){
  326. if ((/^(https?\:\/\/(www\.)?youtube(-nocookie)?\.com\/(v|e)\/[a-zA-Z0-9+\-\_]{11})/).test(src)){
  327. if ((new RegExp("(\\?|\\&)vq=[^#^&]*(\\#|\\&)?")).test(src))
  328. src = src.replace(new RegExp("(\\?|\\&)vq=[^#^&]*(\\#|\\&)?"),"$1vq=" +defaultQuality+ "$2");
  329. else
  330. src = src.replace(/^(https?\:\/\/(www\.)?youtube(-nocookie)?\.com\/(v|e)\/[a-zA-Z0-9+\-\_]{11})\??/,"$1?vq=" +defaultQuality + "&");
  331. }
  332. return src;
  333. }
  334.  
  335. function verifyDefaultQuality(quality){
  336. if (["small","medium","large","hd720","hd1080","highres"].indexOf(quality) < 0)
  337. return "highres";
  338. return quality;
  339. }