Dailymotion: "Playback Quality Control" Feature

Add some site-wide video playback quality control settings to Dailymotion.

当前为 2014-07-31 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @id www.dailymotion.com-6843eec7-c1ba-4a14-8700-738e52bcb8e6@http://foo.bar/baz
  3. // @name Dailymotion: "Playback Quality Control" Feature
  4. // @version 0.0.5
  5. // @namespace http://foo.bar/baz
  6. // @author David Toso
  7. // @description Add some site-wide video playback quality control settings to Dailymotion.
  8. // @include http://www.dailymotion.com/*
  9. // @require http://code.jquery.com/jquery-1.9.1.min.js
  10. // @run-at document-start
  11. // ==/UserScript==
  12.  
  13. // Magic Sauce: prevent the standard Dailymotion Player's JavaScript
  14. // from ever loading!
  15. window.addEventListener('beforescriptexecute', function(e) {
  16. if (/window\.DM_Player_Type/.test(e.target.text)) {
  17. // attempt to block the standard player JS from loading
  18. // for video & member pages
  19. console.log("stopped standard player from loading! [DM_Player_Type]");
  20. e.stopPropagation();
  21. e.preventDefault();
  22. } else if (/playerPlaying/.test(e.target.text)) {
  23. // attempt to block the standard player JS from loading
  24. // for playlist pages
  25. console.log("stopped standard player from loading! [playerPlaying]");
  26. e.stopPropagation();
  27. e.preventDefault();
  28. }
  29. }, true);
  30.  
  31. // Get a list of available 'quality' settings for the current video
  32. // (in *old* API format -- convert to numeric as per *new* API).
  33. var getAvailableResolutions = function (vID, cID, cb) {
  34. console.log("inside GAR");
  35. $('#'+cID).html(
  36. '<div id="get_res" style="width: 100%; margin-top: 160px; font-size: '+
  37. '22px; text-align: center; color: white; font-weight: bold">Getting '+
  38. 'available resolutions...</div>');
  39.  
  40. var rest_apis = 'https://api.dailymotion.com';
  41. GM_xmlhttpRequest({
  42. method: "GET",
  43. url: rest_apis+"/video/"+vID+"?fields=available_formats",
  44. onload: function(res) {
  45. console.log("GOT resolutions");
  46. var obj = JSON.parse(res.responseText);
  47. var avail = obj.available_formats;
  48. var fmts = [];
  49. for (var i=0; i<avail.length; i++) {
  50. switch(avail[i]) {
  51. case 'ld': fmts.push(240); break;
  52. case 'sd': fmts.push(380); break;
  53. case 'hq': fmts.push(480); break;
  54. case 'hd720': fmts.push(720); break;
  55. case 'hd1080': fmts.push(1080); break;
  56. }
  57. }
  58. cb(fmts);
  59. },
  60. onerror: function(res) {
  61. console.log("problem getting available resolutions!");
  62. }
  63. });
  64. };
  65.  
  66.  
  67. // Wait for element given by selector to become available
  68. var waitFor = function(doc, selector, cb) {
  69. if ($(selector, doc).get(0)) return cb();
  70. else setTimeout(function(){ waitFor(doc, selector, cb); }, 200);
  71. };
  72.  
  73. // Select the best resolution available (from the given list) or
  74. // the maximum desired resolution (as previously recorded) whichever
  75. // is lowest.
  76. var select_best_resolution = function (resolutions, cID, cb) {
  77.  
  78. // set 1080p as max desired resolution if setting has never been recorded
  79. if (GM_getValue('max_desired_quality',null) == null) {
  80. GM_setValue('max_desired_quality', 1080);
  81. }
  82.  
  83. // set Yes as auto playback setting if setting has never been recorded
  84. if (GM_getValue('auto_playback',null) == null) {
  85. GM_setValue('auto_playback', 'Yes');
  86. }
  87.  
  88. // choose best available resolution
  89. var avail = resolutions.slice(0);
  90. var best = resolutions.pop();
  91.  
  92. // downgrade to desired maximum if required
  93. var max = GM_getValue('max_desired_quality',null);
  94. var auto = GM_getValue('auto_playback');
  95. while (best > max) { best = resolutions.pop(); }
  96.  
  97. // fall back to 380 (seems to always be available)
  98. if (best == null) best = 380;
  99.  
  100. // notify of selected resolution
  101. $('#'+cID).html(
  102. '<div id="sel_res" style="width: 100%; margin-top: 130px; font-size: 55px;'+
  103. ' text-align: center; color: white; font-weight: bold">'+best+'P</div>');
  104. $('#sel_res')
  105. .fadeOut(1500, function(){
  106. $(this).remove();
  107. cb(best, auto);
  108. });
  109.  
  110. // asynchronously: build quality tab
  111. waitFor(document,'.pl_video_tabs ul.mo_tabs', function(){
  112. build_quality_tab(best, max, avail, auto);
  113. });
  114. };
  115.  
  116. // Inject a 'Quality' video tab which shows the available resolutions
  117. // (current highlighted), and allows the user to select the maximum
  118. // resolution they'd like to set on future video views
  119. var build_quality_tab = function(best, max, avail, auto) {
  120.  
  121. avail = avail.map(function(e){ return e+'P'; }).join(", ");
  122. var re = new RegExp('(, )*('+best+'P)');
  123. avail = avail.replace(re, function(_all, _p, _f) { return _p+'<b>'+_f+'</b>'; });
  124.  
  125. // find tabs & corresponding panels
  126. var tabs = $('.pl_video_tabs ul.mo_tabs');
  127. var panels = $('.pl_video_tabs');
  128.  
  129. // Render tab
  130. tabs.append(
  131. '<li id="tab_myquality" class="pull-start mrg-end-lg"><a class="alt-link'+
  132. ' link-border-color-on-hvr" href="">Quality</li>');
  133. panels.append(
  134. '<div id="tab_myquality_content" class="pl_video_tabmyquality tab_content'+
  135. ' clearfix" style="display: none"></div>');
  136.  
  137. // Render panel
  138. var myPanel = $('#tab_myquality_content');
  139. myPanel.append(
  140. '<h3 class="tab_title clearfix" style="clear:both; font-weight: normal; '+
  141. 'font-size: 20px; color: #0079B8; font-family: arial;">Playback Quality '+
  142. 'Settings</h3>');
  143. myPanel.append(
  144. '<p style="font-weight: normal; margin-top: 15px; font-size: 15px; color:'+
  145. ' black; font-family: arial;"><span style="display: inline-block; width: '+
  146. '188px;">Resolutions available: </span>'+avail+'</p>');
  147. myPanel.append(
  148. '<p style="font-weight: normal; margin-top: 3px; font-size: 15px; color:'+
  149. ' black; font-family: arial;"><span style="display: inline-block; width: '+
  150. '188px;">Maximum desired quality: </span><select id="my_sel_qual" style="'+
  151. 'font-size: 12px; width: 230px; background-color: white;"><option value="'+
  152. '240">240P - I don\'t even</option><option value="380">380P - Low Quality'+
  153. '</option><option value="480">480P - Standard Definition</option><option '+
  154. 'value="720">720P - High Quality</option><option value="1080">1080P - '+
  155. 'Highest Quality</option></select></p>');
  156. myPanel.append(
  157. '<p style="font-weight: normal; margin-top: 3px; font-size: 15px; color:'+
  158. ' black; font-family: arial;"><span style="display: inline-block; width: '+
  159. '188px;">Automatic playback: </span><select id="my_auto_play" style="'+
  160. 'font-size: 12px; width: 230px; background-color: white;"><option value="'+
  161. 'Yes">Yes</option><option value="No">No</option></select></p>');
  162.  
  163. // Record changes to max desired playback quality setting
  164. $('#my_sel_qual')
  165. .val(max)
  166. .change(function(){
  167. GM_setValue('max_desired_quality', $('#my_sel_qual').eq(0).val()*1);
  168. });
  169.  
  170. // Record changes to auto playback setting
  171. $('#my_auto_play')
  172. .val(auto)
  173. .change(function(){
  174. GM_setValue('auto_playback', $('#my_auto_play').eq(0).val());
  175. });
  176. };
  177.  
  178. // synchronize readiness of DM Player embedding API and Quality Selection
  179. var when_ready = function(uwin, cb) {
  180. if ((uwin.__DM_JS_READY == 1) && (uwin.__RES_CHOSEN == 1)) return cb();
  181. else setTimeout(function(){ when_ready(uwin, cb); }, 50);
  182. };
  183.  
  184. // load the dailymotion embedded player
  185. var load_embedded_player = function(uwin, quality, auto, cID, vID) {
  186.  
  187. // Configure player with previously chosen video quality.
  188. var params = {
  189. api: 1,
  190. quality: quality,
  191. related: 0,
  192. logo: 0,
  193. info: 0,
  194. autoplay: auto=='Yes'?1:0
  195. };
  196.  
  197. // Actually ask for the video to be loaded!
  198. var player = uwin.DM.player(cID, {
  199. video: vID,
  200. width: 620,
  201. height: 348,
  202. params: params
  203. });
  204.  
  205. // when we can talk to the flash player via JavaScript...
  206. player.addEventListener("apiready", function(e) {
  207. var x = e.target.contentWindow;
  208. x.onload = function() {
  209.  
  210. //$('#player_container').css({ height: 348 });
  211. console.log("FLASHPLAYER JS API is ready!");
  212. };
  213. });
  214. };
  215.  
  216. // main script entry point
  217. var once = 0;
  218. window.addEventListener('DOMContentLoaded', function(e) {
  219. if (once) return; once = 1;
  220. var uwin = unsafeWindow;
  221. var doc = uwin.document;
  222.  
  223. // Determine whether we're dealing with a Video page, a Playlist page, or a
  224. // Member's home page (the User Interface Mode)
  225. var uiMode;
  226. if (uwin.DM_CurrentVideoXID) uiMode = 'video';
  227. if ($('#js-playlist-name').get(0)) uiMode = 'playlist';
  228. if ($('.user-screenname-inner').get(0)) uiMode = 'member';
  229. if (!uiMode) return; // don't bother doing the rest if there's no player
  230. console.log("uiMode="+uiMode);
  231.  
  232. // Wait until both Player & Quality selection are ready
  233. when_ready(uwin, function(){
  234. console.log("BOTH ARE READY!");
  235. //if (uiMode != 'video') return;
  236. load_embedded_player(uwin,
  237. uwin.__RES_SETTINGS.quality,
  238. uwin.__RES_SETTINGS.autoplay,
  239. uwin.__RES_SETTINGS.container,
  240. uwin.__RES_SETTINGS.video);
  241. });
  242.  
  243. // Async-load the DM embedded JS API
  244. (function() {
  245. var e = doc.createElement('script');
  246. e.async = true;
  247. e.src = doc.location.protocol + '//api.dmcdn.net/all.js';
  248. var s = doc.getElementsByTagName('script')[0];
  249. uwin.dmAsyncInit = function() {
  250. console.log("DM Player API loaded!");
  251. uwin.__DM_JS_READY = 1;
  252. };
  253. s.parentNode.insertBefore(e, s);
  254. }());
  255.  
  256. // Based on the uiMode, find the "currently playing" video ID as well as the
  257. // container element that will house the embedded DM video player
  258. var vID, pID, cID;
  259. switch(uiMode) {
  260. case 'video':
  261. vID = uwin.DM_CurrentVideoXID;
  262. cID = 'container_player_main';
  263. break;
  264. case 'playlist':
  265. vID = $('.js-list-list > div.media > div.media-img',doc)
  266. .eq(0).attr('data-id');
  267. cID = 'container_main_player';
  268. break;
  269. case 'member':
  270. $('#js-featured-card-title > a')
  271. .eq(0).attr('href').replace(/\/video\/([^_]+)/, function(_m, _x) {
  272. vID = _x; });
  273. cID = 'container_player_main';
  274. break;
  275. }
  276. console.log("VID="+vID);
  277.  
  278. // Get a list of available resolutions for the "currently playing" video,
  279. // then compare that to the users's max desired quality & autoplay
  280. // preferences before configuring and loading the DM embedded player.
  281. $('#'+cID).css('background-color', '#00669d');
  282. getAvailableResolutions(vID, cID, function(resolutions){
  283. console.log("smeg");
  284. select_best_resolution(resolutions, cID, function(chosen, auto){
  285. console.log("selected="+chosen+",autoplay="+auto);
  286. uwin.__RES_SETTINGS = {
  287. quality: chosen,
  288. autoplay: auto,
  289. container: cID,
  290. video: vID
  291. };
  292. uwin.__RES_CHOSEN = 1;
  293. });
  294. });
  295.  
  296. }, true);