Youtube Always Play in Highest Quality

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