YouTube Auto Buffer & Auto HD

Buffers the video without autoplaying and puts it in HD if the option is on. For Firefox, Opera, & Chrome

当前为 2014-06-04 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube Auto Buffer & Auto HD
  3. // @namespace http://userscripts.org/users/23652
  4. // @description Buffers the video without autoplaying and puts it in HD if the option is on. For Firefox, Opera, & Chrome
  5. // @icon https://raw.github.com/joesimmons/YouTube---Auto-Buffer---Auto-HD/master/media/logo-64x64.png
  6. // @include http://*.youtube.com/*
  7. // @include http://youtube.com/*
  8. // @include https://*.youtube.com/*
  9. // @include https://youtube.com/*
  10. // @copyright JoeSimmons
  11. // @author JoeSimmons
  12. // @version 1.2.87
  13. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  14. // @require https://greasyfork.org/scripts/1885-joesimmons-library/code/JoeSimmons'%20Library.js?version=4838
  15. // @require https://greasyfork.org/scripts/2104-youtube-button-container-require/code/YouTube%20-%20Button%20Container%20(@require).js?version=5493
  16. // @require https://greasyfork.org/scripts/1884-gm-config/code/GM_config.js?version=4836
  17. // @grant GM_info
  18. // @grant GM_getValue
  19. // @grant GM_log
  20. // @grant GM_openInTab
  21. // @grant GM_registerMenuCommand
  22. // @grant GM_setValue
  23. // @grant GM_xmlhttpRequest
  24. // ==/UserScript==
  25.  
  26. /* CHANGELOG
  27.  
  28. 1.2.87 (1/22/2014)
  29. - fixed odd internal error that made the player not show
  30. - fixed problem with "Red Bar" not being disabled properly
  31.  
  32. 1.2.86 (12/21/2013)
  33. - fixed Large player button (as far as I can tell)
  34. - added Automatic quality option (the YouTube default)
  35. - switched the DASH option default to enabled
  36. this is really what the script was made for... auto-buffering
  37.  
  38. 1.2.85 (12/11/2013)
  39. - added a script icon
  40. - added a "Player Color Scheme" option
  41. - fixed bug with SPF not being de-activated properly in non-Firefox browsers
  42. - fixed volume bug
  43. - fixed adding time in url bug. it will now skip to the right portion of the video
  44. - changed internal name of the "Activation Mode" option. shouldn't affect the user
  45. - the script doesn't add any javascript to the page anymore
  46. it uses onYouTubePlayerReady to detect when the player is ready;
  47. it's much more performant than an interval
  48.  
  49. 1.2.84 (10/31/2013)
  50. - added primitive type checking when copying ytplayer.config.args into the flashvars.
  51. this fixes the issue with Flashgot and possibly other add-ons
  52. - fixed non-activation by moving the _spf_state check to the top of init.
  53. this disables SPF on every YouTube page now, and should make the script activate correctly
  54. - changed all RegExp test methods to match. match seems more consistent.
  55. I've had cases where test doesn't work, but match does
  56.  
  57. 1.2.83 (10/28/2013)
  58. - added auto HD, volume, and more activation modes for html5 (thanks to youtube updating its API)
  59. - changed the default quality to 1080p
  60. - changed the wording of some options
  61. - changed the "Disable Dash Playback" option to false for default
  62. - disabled SPF (aka Red Bar feature) completely until I get playlists working better
  63. - changed the setPref prototype function to a regular function
  64.  
  65. 1.2.82 (9/5/2013)
  66. - added support for older Firefox versions (tested on 3.6)
  67. - added a new option to disable 'dash' playback (videos loading in blocks/pieces)
  68. - re-added ad removal feature (experimental for now)
  69.  
  70. 1.2.81
  71. - fixed HTML5 support. YT changed tag names so the script got confused
  72. - made a few minor performance tweaks
  73. - fixed 'play symbol in title' bug in autobuffer mode (it would show playing, even though it's paused/buffering)
  74.  
  75. 1.2.80
  76. - switched to JSL.setInterval for consistency and drift accommodation
  77. - visual tweaks to:
  78. msg().
  79. the rest of the page now dims while the msg box is visible
  80. changed the spacing of most of the elements
  81. changed the font sizes and the font (Arial)
  82. added a close button instead of requiring a double click
  83. made it auto-open the options screen when the msg is closed
  84. GM_config.
  85. made the background color more mellow and moved the section title near the middle
  86.  
  87. 1.2.79
  88. - adjusted to the new play symbol in the youtube title feature
  89. - Changed margins on the settings button when in footer
  90. - Switched JSL/pushState checking order.
  91. Previously in 1.2.78, if JSL didn't exist or wasn't @required, the script
  92. would still loop every 500ms to re-set the pushState method, even though the
  93. script wasn't going to be running.
  94. I switched that so that JSL has to exist before the script does anything.
  95.  
  96. 1.2.78
  97. - Fixed bug where options button wasn't getting added to the footer with the new Red Bar YT feature
  98.  
  99. 1.2.77
  100. - Adapted to the new YouTube feature that uses HTML5's history.pushState to load videos
  101. - Small fixes here and there
  102. - Excluded (with RegExp) pages without videos on them
  103. - Fixed GM_config.log()
  104. - Declared all variables at the beginning of functions
  105. - Made finding the video player a little more reliable
  106. - Make 'autoplay on playlists' work with HTML5 videos
  107.  
  108. 1.2.76
  109. - Added new quality option ('1080p+' - for anything higher than 1080p)
  110.  
  111. 1.2.75
  112. - Added a new option (to move option button to page footer)
  113. - Added a new option (to autoplay on playlists regardless of auto[play/buffer] setting)
  114. - Added a first time user message box
  115. - Fixed bug with GM_config's [set/get]Value functions. Chrome/Opera were not using localStorage before this update
  116.  
  117. 1.2.74
  118. - Adapted to YouTube's new layout
  119.  
  120. 1.2.73
  121. - Added compatibility for user pages
  122.  
  123. 1.2.72
  124. - Made it fully working again in Opera & Chrome
  125. - Switched from setInterval to setTimeout due to instability
  126. - Added an anonymous function wrapper
  127.  
  128. 1.2.71
  129. - Added compatibility for HTML5
  130.  
  131. */
  132.  
  133.  
  134.  
  135.  
  136. // run the script in an IIFE, to hide its variables from the global scope
  137. (function (undefined) {
  138.  
  139. 'use strict';
  140.  
  141. var aBlank = ['', '', ''],
  142. URL = location.href,
  143. navID = 'watch7-user-header',
  144. rYoutubeUrl = /^https?:\/\/([^\.]+\.)?youtube\.com\//,
  145. // rYoutubeBlacklistedUrl = /^https?:\/\/([^\.]+\.)?youtube\.com\/(feed\/(?!subscriptions)|account|inbox|my_|tags|view_all|analytics)/i,
  146. rList = /[?&]list=/i,
  147. rPlaySymbol = /^\u25B6\s*/,
  148. script_name = 'YouTube - Auto-Buffer & Auto-HD',
  149. tTime = (URL.match(/[&#?]t=([sm0-9]+)/) || aBlank)[1],
  150. ads = [
  151. 'supported_without_ads',
  152. 'ad3_module',
  153. 'adsense_video_doc_id',
  154. 'allowed_ads',
  155. 'baseUrl',
  156. 'cafe_experiment_id',
  157. 'afv_inslate_ad_tag',
  158. 'advideo',
  159. 'ad_device',
  160. 'ad_channel_code_instream',
  161. 'ad_channel_code_overlay',
  162. 'ad_eurl',
  163. 'ad_flags',
  164. 'ad_host',
  165. 'ad_host_tier',
  166. 'ad_logging_flag',
  167. 'ad_preroll',
  168. 'ad_slots',
  169. 'ad_tag',
  170. 'ad_video_pub_id',
  171. 'aftv',
  172. 'afv',
  173. 'afv_ad_tag',
  174. 'afv_instream_max',
  175. 'afv_ad_tag_restricted_to_instream',
  176. 'afv_video_min_cpm',
  177. 'prefetch_ad_live_stream'
  178. ],
  179. hasMainBeenRun, missing_require, nav, uw, wait_intv;
  180.  
  181. function toNum(a) {
  182. return parseInt(a, 10);
  183. }
  184.  
  185. // msg by JoeSimmons
  186. function msg(infoObject) {
  187.  
  188. var box_id_name = 'script_msg',
  189. box = document.getElementById(box_id_name),
  190. rLinebreaks = /[\r\n]/g,
  191. title = typeof infoObject.title === 'string' && infoObject.title.length > 3 ? infoObject.title : 'Message Box by JoeSimmons.';
  192.  
  193. // add BR tags to line breaks
  194. infoObject.text = infoObject.text.replace(rLinebreaks, '<br />\n');
  195.  
  196. function msg_close(event) {
  197. event.preventDefault();
  198.  
  199. document.getElementById(box_id_name).style.display = 'none';
  200.  
  201. if (typeof infoObject.onclose === 'function') {
  202. infoObject.onclose();
  203. }
  204. }
  205.  
  206. if (box == null) {
  207. JSL.addStyle('' +
  208. '@keyframes blink { ' +
  209. '50% { color: #B95C00; } ' +
  210. '}\n\n' +
  211. '#' + box_id_name + ' .msg-header { ' +
  212. 'animation: blink 1s linear infinite normal; ' +
  213. '}' +
  214. '');
  215. document.body.appendChild(
  216. JSL.create('div', {id : box_id_name, style : 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 999999; background-color: rgba(0, 0, 0, 0.6);'}, [
  217. // main box
  218. JSL.create('div', {id : box_id_name + '_box', style : 'position: absolute; top: 25%; left: 25%; width: 50%; height: 50%; padding-top: 50px; background-color: #E9E9E9; border: 3px double #006195;'}, [
  219. // header
  220. JSL.create('div', {style : 'margin: 0 auto; padding-bottom: 40px; color: #F07800; font-size: 21pt; font-family: Arial, Verdana, "Myriad Pro"; font-weight: normal; text-shadow: 2px 2px 4px #C7C7C7; text-align: center;', 'class' : 'msg-header', textContent : title}),
  221.  
  222. // text (message)
  223. JSL.create('div', {innerHTML : infoObject.text, style : 'text-align: center; margin: 0 auto; padding-top: 39px; border-top: 1px solid #B0B0B0; color: #000000; font-size: 11pt; font-family: Arial, Verdana, "Myriad Pro"; font-weight: normal; text-shadow: 0 0 8px #AEAEAE;'}),
  224.  
  225. // close button
  226. JSL.create('div', {style : 'position: absolute; bottom: 20px; left: 0; width: 100%; text-align: center;'}, [
  227. JSL.create('input', {id : box_id_name + '_close', type : 'button', value : 'Close Message', onclick : msg_close, style : 'margin: 0 auto; padding: 2px 20px; font-size: 11pt; font-family: Arial, Verdana, "Myriad Pro"; font-weight: normal;'})
  228. ])
  229. ])
  230. ])
  231. );
  232. } else {
  233. box.innerHTML += infoObject.text;
  234. }
  235. }
  236.  
  237. // will return true if the value is a primitive value
  238. function isPrimitiveType(value) {
  239. switch (typeof value) {
  240. case 'string': case 'number': case 'boolean': case 'undefined': {
  241. return true;
  242. }
  243. case 'object': {
  244. return !value;
  245. }
  246. }
  247.  
  248. return false;
  249. }
  250.  
  251. function setPref(str, values) {
  252. var i, value, rQuery;
  253.  
  254. for (i = 0; value = values[i]; i += 1) {
  255. // (several lines for readability)
  256. rQuery = new RegExp('[?&]?' + value[0] + '=[^&]*');
  257. str = str.replace(rQuery, '') + '&' + value[0] + '=' + value[1];
  258. str = str.replace(/^&+|&+$/g, '');
  259. }
  260.  
  261. return str;
  262. }
  263.  
  264. // unwraps the element so we can use its methods freely
  265. function unwrap(elem) {
  266. if (elem) {
  267. if ( typeof XPCNativeWrapper === 'function' && typeof XPCNativeWrapper.unwrap === 'function' ) {
  268. return XPCNativeWrapper.unwrap(elem);
  269. } else if (elem.wrappedJSObject) {
  270. return elem.wrappedJSObject;
  271. }
  272. }
  273.  
  274. return elem;
  275. }
  276.  
  277. function fixPlaySymbol() {
  278. document.title = document.title.replace(rPlaySymbol, '');
  279. }
  280.  
  281. // grabs the un-wrapped player
  282. function getPlayer() {
  283. var doc = uw.document;
  284. return doc.getElementById('c4-player') || doc.getElementById('movie_player');
  285. }
  286.  
  287. // adds the Options button below the video
  288. function addButton() {
  289. var footer = GM_config.get('footer') === true,
  290. footerHolder = document.getElementById('footer-main');
  291.  
  292. addButtonToContainer('Auto-Buffer Options', function () { GM_config.open(); }, 'autobuffer-options');
  293.  
  294. if (footer && footerHolder) {
  295. footerHolder.appendChild( document.getElementById('autobuffer-options') );
  296. }
  297. }
  298.  
  299. // this function sets up the script
  300. function init() {
  301. hasMainBeenRun = false;
  302.  
  303. // get the raw window object of the YouTube page
  304. uw = typeof unsafeWindow !== 'undefined' ? unsafeWindow : unwrap(window);
  305.  
  306. // disable Red Bar aka SPF
  307. if (uw._spf_state && uw._spf_state.config) {
  308. uw._spf_state.config['navigate-limit'] = 0;
  309. uw._spf_state.config['navigate-part-received-callback'] = function (targetUrl) {
  310. location.href = targetUrl;
  311. };
  312. }
  313.  
  314. uw.onYouTubePlayerReady = function onYouTubePlayerReady(player) {
  315. if (typeof player === 'object' && hasMainBeenRun === false) {
  316. window.postMessage('YTAB__ready', '*');
  317. }
  318. };
  319.  
  320. JSL.waitFor({
  321. selector : '#c4-player, #movie_player',
  322. verifier : function (elem) {
  323. elem = unwrap( elem[0] );
  324. return typeof elem.stopVideo === 'function';
  325. },
  326. done : function () {
  327. if (hasMainBeenRun === false) {
  328. main();
  329. }
  330. }
  331. });
  332. }
  333.  
  334. // this is the main function. it does all the autobuffering, quality/volume changing, annotation hiding, etc
  335. function main() {
  336. var player = getPlayer(),
  337. parent = player.parentNode,
  338. alreadyBuffered = false,
  339. time = 0,
  340. args, arg, buffer_intv, fv, isHTML5, playerClone,
  341. playIfPlaylist, val, userOpts;
  342.  
  343. // don't let main() run again unless a new video is loaded
  344. hasMainBeenRun = true;
  345.  
  346. // remove the player out of the document temporarily while other things are being done,
  347. // to reduce the time the player may be playing the video
  348. parent.removeChild(player);
  349.  
  350. // set up the user options object
  351. userOpts = {
  352. activationMode : GM_config.get('activationMode'),
  353. disableDash : GM_config.get('disableDash') === true,
  354. hideAnnotations : GM_config.get('hideAnnotations') === true,
  355. hideAds : GM_config.get('hideAds') === true,
  356. quality : GM_config.get('autoHD'),
  357. theme : GM_config.get('theme'),
  358. volume : GM_config.get('volume')
  359. };
  360.  
  361. // set up other variables
  362. playerClone = player.cloneNode(true);
  363. fv = player.getAttribute('flashvars');
  364. isHTML5 = !!document.querySelector('video.html5-main-video');
  365. playIfPlaylist = !!URL.match(rList) && GM_config.get('autoplayplaylists') === true;
  366.  
  367. if (uw.ytplayer && uw.ytplayer.config && uw.ytplayer.config.args) {
  368. args = uw.ytplayer.config.args;
  369. }
  370.  
  371. // set the volume to the user's preference
  372. if (userOpts.volume != 1000) {
  373. player.setVolume(userOpts.volume);
  374. }
  375.  
  376. if (isHTML5) {
  377. if (player.getPlaybackQuality() !== userOpts.quality) {
  378. player.setPlaybackQuality(userOpts.quality);
  379. }
  380.  
  381. if (!playIfPlaylist) {
  382. if (userOpts.activationMode === 'buffer') {
  383. player.pauseVideo();
  384. } else if (userOpts.activationMode === 'none') {
  385. player.stopVideo();
  386. }
  387. }
  388. } else {
  389. // copy 'ytplayer.config.args' into the flash vars
  390. if (args) {
  391. for (arg in args) {
  392. val = args[arg];
  393. if ( args.hasOwnProperty(arg) && isPrimitiveType(val) ) {
  394. fv = setPref(fv, [ [ arg, encodeURIComponent(val) ] ]);
  395. }
  396. }
  397. }
  398.  
  399. // ad removal
  400. if (userOpts.hideAds) {
  401. fv = fv.replace(new RegExp('(&amp;|[&?])?(' + ads.join('|') + ')=[^&]*', 'g'), '');
  402. /*
  403. fv = setPref(fv,
  404. ads.map(function (ad) {
  405. return [ad, ''];
  406. })
  407. );
  408. */
  409. }
  410.  
  411. // disable DASH playback
  412. if (userOpts.disableDash) {
  413. fv = setPref(fv, [
  414. ['dashmpd', ''],
  415. ['dash', '0']
  416. ]);
  417. }
  418.  
  419. // edit the flashvars
  420. fv = setPref(fv, [
  421. ['vq', userOpts.quality], // set the quality
  422. ['autoplay', (userOpts.activationMode !== 'none' || playIfPlaylist) ? '1' : '0' ], // enable/disable autoplay
  423. ['iv_load_policy', userOpts.hideAnnotations ? '3' : '1' ], // enable/disable annotations
  424. ['theme', userOpts.theme], // use light/dark theme
  425.  
  426. // some "just-in-case" settings
  427. ['enablejsapi', '1'], // enable JS API
  428. ['jsapicallback', 'onYouTubePlayerReady'], // enable JS ready callback
  429. ['fs', '1'], // enable fullscreen button, just in-case
  430. ['modestbranding', '1'], // hide YouTube logo in player
  431. ['disablekb', '0'] // enable keyboard controls in player
  432. ]);
  433.  
  434. // handle video starting time
  435. if ( tTime.match(/\d+m/) ) {
  436. time += toNum( tTime.match(/(\d+)m/)[1] ) * 60;
  437. }
  438. if ( tTime.match(/\d+s/) ) {
  439. time += toNum( tTime.match(/(\d+)s/)[1] );
  440. }
  441. if ( tTime.match(/^\d+$/) ) {
  442. time += toNum(tTime);
  443. }
  444. if (time <= 3) {
  445. // if no time is in the url, check the player's time
  446. try {
  447. // sometimes causes a weird error.
  448. // it will say getCurrentTime isn't a function,
  449. // even though the typeof is "function",
  450. // and alerting its value says [native code]
  451. time = player.getCurrentTime();
  452. } catch (e) {}
  453. if (time <= 3) {
  454. time = 0;
  455. }
  456. }
  457. fv = setPref( fv, [ ['start', time] ] );
  458.  
  459. // set the new player's flashvars
  460. playerClone.setAttribute('flashvars', fv);
  461.  
  462. // replace the original player with the modified clone
  463. parent.appendChild(playerClone);
  464.  
  465. if (userOpts.activationMode === 'buffer' && playIfPlaylist === false) {
  466. // handle auto-buffering
  467. buffer_intv = JSL.setInterval(function () {
  468. var player = getPlayer();
  469.  
  470. if (player && typeof player.getPlayerState === 'function') {
  471. JSL.clearInterval(buffer_intv);
  472.  
  473. // pause the video so it can buffer
  474. player.pauseVideo();
  475.  
  476. // seek back to beginning if time elapsed is not much
  477. if (player.getCurrentTime() <= 3) {
  478. player.seekTo(0);
  479. }
  480.  
  481. // adjust to the 'play symbol in title' feature
  482. window.setTimeout(fixPlaySymbol, 1000);
  483. }
  484. }, 100);
  485. } else if (userOpts.activationMode === 'none') {
  486. // adjust to the 'play symbol in title' feature
  487. window.setTimeout(fixPlaySymbol, 1500);
  488. }
  489. }
  490.  
  491. // show the first time user message, then set it to never show again
  492. if (GM_config.getValue('yt-autobuffer-autohd-first', 'yes') === 'yes') {
  493. msg({
  494. text : 'Welcome to "' + script_name + '".\n\n\n\n' +
  495. 'There is an options button below the video.\n\n\n\n' +
  496. 'The options screen will automatically open when you close this message.',
  497. title : '"' + script_name + '" Message',
  498. onclose : function () { GM_config.open(); }
  499. });
  500. GM_config.setValue('yt-autobuffer-autohd-first', 'no');
  501. }
  502. }
  503.  
  504. // make sure the page is not in a frame
  505. // & is on a YouTube page (the @include works most of the time, but this is 100%)
  506. // & isn't on a blacklisted YouTube page
  507. if ( window !== window.top || !URL.match(rYoutubeUrl) /*|| URL.match(rYoutubeBlacklistedUrl)*/ ) { return; }
  508.  
  509. // quit if one of the @requires is non-existent
  510. if (typeof JSL === 'undefined' || typeof GM_config === 'undefined' || typeof addButtonToContainer === 'undefined') {
  511. missing_require = typeof JSL === 'undefined' ? 'JSL' : typeof GM_config === 'undefined' ? 'GM_config' : typeof addButtonToContainer ? 'Button Container' : 'unknown';
  512.  
  513. return alert('' +
  514. 'A @require is missing (' + missing_require + ').\n\n' +
  515. 'Either you\'re not using the correct plug-in, or @require isn\'t working.\n\n' +
  516. 'Please review the script\'s main page to see which browser & add-on to use.' +
  517. '');
  518. }
  519.  
  520. // add a user-script command
  521. if (typeof GM_registerMenuCommand === 'function') {
  522. GM_registerMenuCommand('"' + script_name + '" Options', GM_config.open);
  523. }
  524.  
  525. // init GM_config
  526. GM_config.init('"' + script_name + '" Options', {
  527. activationMode : {
  528. section : ['Main Options'],
  529. label : 'Activation Mode',
  530. type : 'select',
  531. options : {
  532. 'buffer' : 'Auto Buffer (aka Auto Pause)',
  533. 'play' : 'Auto Play',
  534. 'none' : 'Stop Loading Immediately'
  535. },
  536. 'default' : 'buffer'
  537. },
  538. autoHD : {
  539. label : 'Auto HD',
  540. type : 'select',
  541. options : {
  542. 'default' : 'Automatic (default)',
  543. 'tiny' : '144p',
  544. 'small' : '240p',
  545. 'medium' : '360p',
  546. 'large' : '480p',
  547. 'hd720' : '720p (HD)',
  548. 'hd1080' : '1080p (HD)',
  549. 'hd1440' : '1440p (HD)',
  550. 'highres' : 'Original (highest)'
  551. },
  552. 'default' : 'hd1080'
  553. },
  554. disableDash : {
  555. label : 'Disable DASH Playback',
  556. type : 'checkbox',
  557. 'default' : true,
  558. title : '"DASH" loads the video in blocks/pieces; disrupts autobuffering -- Note: Qualities are limited when disabled'
  559. },
  560. hideAds : {
  561. label : 'Disable Ads',
  562. type : 'checkbox',
  563. 'default' : true,
  564. title : 'Should disable advertisements. AdBlock is better, though. Get that instead'
  565. },
  566. hideAnnotations : {
  567. label : 'Disable Annotations',
  568. type : 'checkbox',
  569. 'default' : false
  570. },
  571. theme : {
  572. section : ['Other Options'],
  573. label : 'Player Color Scheme',
  574. type : 'select',
  575. options : {
  576. 'dark' : 'Dark Theme',
  577. 'light' : 'Light Theme'
  578. },
  579. 'default' : 'dark'
  580. },
  581. volume : {
  582. label : 'Set volume to: ',
  583. type : 'select',
  584. options : {
  585. '1000' : 'Don\'t Change',
  586. '0' : 'Off',
  587. '5' : '5%',
  588. '10' : '10%',
  589. '20' : '20%',
  590. '25' : '25% (quarter)',
  591. '30' : '30%',
  592. '40' : '40%',
  593. '50' : '50% (half)',
  594. '60' : '60%',
  595. '70' : '70%',
  596. '75' : '75% (three quarters',
  597. '80' : '80%',
  598. '90' : '90%',
  599. '100' : '100% (full)',
  600. },
  601. title : 'What to set the volume to',
  602. 'default' : '1000'
  603. },
  604. autoplayplaylists : {
  605. label : 'Autoplay on Playlists (override)',
  606. type : 'checkbox',
  607. 'default' : false,
  608. title : 'This will enable autoplay on playlists, regardless of the "Activation Mode" option'
  609. },
  610. footer : {
  611. label : 'Options Button In Footer',
  612. type : 'checkbox',
  613. 'default' : false,
  614. title : 'This will make the options button show at the bottom of the page in the footer'
  615. }
  616. }, '' +
  617. 'body { ' +
  618. 'background-color: #DDDDDD !important; ' +
  619. 'color: #434343 !important; ' +
  620. 'font-family: Arial, Verdana, sans-serif !important; ' +
  621. '}' +
  622. '#config_header { ' +
  623. 'font-size: 16pt !important; ' +
  624. '}' +
  625. '.config_var { ' +
  626. 'margin-left: 20% !important; ' +
  627. 'margin-top: 20px !important; ' +
  628. '}' +
  629. '#header { ' +
  630. 'margin-bottom: 40px !important; ' +
  631. 'margin-top: 20px !important; ' +
  632. '}' +
  633. '.indent40 { ' +
  634. 'margin-left: 20% !important; ' +
  635. '}' +
  636. '.config_var * { ' +
  637. 'font-size: 10pt !important; ' +
  638. '}' +
  639. '.section_header_holder { ' +
  640. 'border-bottom: 1px solid #BBBBBB !important; ' +
  641. 'margin-top: 14px !important; ' +
  642. '}' +
  643. '.section_header { ' +
  644. 'background-color: #BEDBFF !important; ' +
  645. 'color: #434343 !important; ' +
  646. 'margin-left: 20% !important; ' +
  647. 'margin-top: 8px !important; ' +
  648. 'padding: 2px 200px !important; ' +
  649. 'text-decoration: none !important; ' +
  650. '}' +
  651. '.section_kids { ' +
  652. 'margin-bottom: 14px !important; ' +
  653. '}' +
  654. '.saveclose_buttons { ' +
  655. 'font-size: 14pt !important; ' +
  656. '}' +
  657. '#buttons_holder { ' +
  658. 'padding-right: 50px; ' +
  659. '}' +
  660. '', {
  661. close : function () {
  662. JSL('#c4-player, #movie_player').css('visibility', 'visible');
  663. JSL('#lights_out').hide();
  664. },
  665. open : function () {
  666. JSL('#c4-player, #movie_player').css('visibility', 'hidden');
  667. JSL('#lights_out').show('block');
  668. JSL('#GM_config').css('height', '80%').css('width', '80%').css('zIndex', '999999999999');
  669. }
  670. });
  671.  
  672. // this is for the "lights out" feature of GM_config
  673. JSL.runAt('interactive', function () {
  674. JSL(document.body).append('div', {
  675. id : 'lights_out',
  676. style : 'display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 999999999998; background: rgba(0, 0, 0, 0.72);'
  677. });
  678.  
  679. // call the function that sets up everything
  680. init();
  681. });
  682.  
  683. // add a message listener for when the unsafeWindow function fires a message
  684. window.addEventListener('message', function (msg) {
  685. if (msg.data === 'YTAB__ready') {
  686. main();
  687. }
  688. }, false);
  689.  
  690. // wait for an element that can hold the options button to load,
  691. // then run our add button function
  692. JSL.waitFor({
  693. selector : '#watch7-headline, #gh-overviewtab div.c4-spotlight-module-component, #footer-main',
  694. done : addButton
  695. });
  696.  
  697. }());