YouTube Auto Buffer & Auto HD

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

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