YouTube Auto Enhanced (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 Enhanced (Auto Buffer & Auto HD)
  3. // @namespace https://gf.qytechs.cn/en/users/10166-moriarty
  4. // @description Buffers the video without autoplaying and puts it in HD if the option is on. For Firefox, Opera, & Chrome
  5. // @include http://*.youtube.com/*
  6. // @include http://youtube.com/*
  7. // @include https://*.youtube.com/*
  8. // @include https://youtube.com/*
  9. // @copyright CodeFelony
  10. // @author Moriarty
  11. // @version 1.0.0
  12. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  13. // @require https://gf.qytechs.cn/scripts/9005-gm-config/code/GM_config.js?version=44609
  14. // @require https://gf.qytechs.cn/scripts/9003-codefelony-js-library/code/CodeFelony%20JS%20Library.js?version=44596
  15. // @require https://gf.qytechs.cn/scripts/9004-youtube-button-container-require/code/YouTube%20-%20Button%20Container%20(@require).js?version=44603
  16. // @grant GM_info
  17. // @grant GM_getValue
  18. // @grant GM_log
  19. // @grant GM_openInTab
  20. // @grant GM_registerMenuCommand
  21. // @grant GM_setValue
  22. // @grant GM_xmlhttpRequest
  23. // ==/UserScript==
  24.  
  25. // run the script in an IIFE, to hide its variables from the global scope
  26. (function (undefined) {
  27.  
  28. 'use strict';
  29.  
  30. var aBlank = ['', '', ''],
  31. URL = location.href,
  32. navID = 'watch7-user-header',
  33. rYoutubeUrl = /^https?:\/\/([^\.]+\.)?youtube\.com\//,
  34. // rYoutubeBlacklistedUrl = /^https?:\/\/([^\.]+\.)?youtube\.com\/(feed\/(?!subscriptions)|account|inbox|my_|tags|view_all|analytics)/i,
  35. rList = /[?&]list=/i,
  36. rPlaySymbol = /^\u25B6\s*/,
  37. script_name = 'YouTube - Auto-Buffer & Auto-HD',
  38. tTime = (URL.match(/[&#?]t=([sm0-9]+)/) || aBlank)[1],
  39. ads = [
  40. 'supported_without_ads',
  41. 'ad3_module',
  42. 'adsense_video_doc_id',
  43. 'allowed_ads',
  44. 'baseUrl',
  45. 'cafe_experiment_id',
  46. 'afv_inslate_ad_tag',
  47. 'advideo',
  48. 'ad_device',
  49. 'ad_channel_code_instream',
  50. 'ad_channel_code_overlay',
  51. 'ad_eurl',
  52. 'ad_flags',
  53. 'ad_host',
  54. 'ad_host_tier',
  55. 'ad_logging_flag',
  56. 'ad_preroll',
  57. 'ad_slots',
  58. 'ad_tag',
  59. 'ad_video_pub_id',
  60. 'aftv',
  61. 'afv',
  62. 'afv_ad_tag',
  63. 'afv_instream_max',
  64. 'afv_ad_tag_restricted_to_instream',
  65. 'afv_video_min_cpm',
  66. 'prefetch_ad_live_stream'
  67. ],
  68. hasMainBeenRun, nav, uw, wait_intv;
  69.  
  70. function toNum(a) {
  71. return parseInt(a, 10);
  72. }
  73.  
  74. function msg(infoObject) {
  75.  
  76. var box_id_name = 'script_msg',
  77. box = document.getElementById(box_id_name),
  78. rLinebreaks = /[\r\n]/g,
  79. title = typeof infoObject.title === 'string' && infoObject.title.length > 3 ? infoObject.title : 'Message Box by Moriarty.';
  80.  
  81. // add BR tags to line breaks
  82. infoObject.text = infoObject.text.replace(rLinebreaks, '<br />\n');
  83.  
  84. function msg_close(event) {
  85. event.preventDefault();
  86.  
  87. document.getElementById(box_id_name).style.display = 'none';
  88.  
  89. if (typeof infoObject.onclose === 'function') {
  90. infoObject.onclose();
  91. }
  92. }
  93.  
  94. if (box == null) {
  95. CFL.addStyle('' +
  96. '@keyframes blink { ' +
  97. '50% { color: #B95C00; } ' +
  98. '}\n\n' +
  99. '#' + box_id_name + ' .msg-header { ' +
  100. 'animation: blink 1s linear infinite normal; ' +
  101. '}' +
  102. '');
  103. document.body.appendChild(
  104. CFL.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);'}, [
  105. // main box
  106. CFL.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;'}, [
  107. // header
  108. CFL.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}),
  109.  
  110. // text (message)
  111. CFL.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;'}),
  112.  
  113. // close button
  114. CFL.create('div', {style : 'position: absolute; bottom: 20px; left: 0; width: 100%; text-align: center;'}, [
  115. CFL.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;'})
  116. ])
  117. ])
  118. ])
  119. );
  120. } else {
  121. box.innerHTML += infoObject.text;
  122. }
  123. }
  124.  
  125. // will return true if the value is a primitive value
  126. function isPrimitiveType(value) {
  127. switch (typeof value) {
  128. case 'string': case 'number': case 'boolean': case 'undefined': {
  129. return true;
  130. }
  131. case 'object': {
  132. return !value;
  133. }
  134. }
  135.  
  136. return false;
  137. }
  138.  
  139. function setPref(str, values) {
  140. var i, value, rQuery;
  141.  
  142. for (i = 0; value = values[i]; i += 1) {
  143. // (several lines for readability)
  144. rQuery = new RegExp('[?&]?' + value[0] + '=[^&]*');
  145. str = str.replace(rQuery, '') + '&' + value[0] + '=' + value[1];
  146. str = str.replace(/^&+|&+$/g, '');
  147. }
  148.  
  149. return str;
  150. }
  151.  
  152. // unwraps the element so we can use its methods freely
  153. function unwrap(elem) {
  154. if (elem) {
  155. if ( typeof XPCNativeWrapper === 'function' && typeof XPCNativeWrapper.unwrap === 'function' ) {
  156. return XPCNativeWrapper.unwrap(elem);
  157. } else if (elem.wrappedJSObject) {
  158. return elem.wrappedJSObject;
  159. }
  160. }
  161.  
  162. return elem;
  163. }
  164.  
  165. function fixPlaySymbol() {
  166. document.title = document.title.replace(rPlaySymbol, '');
  167. }
  168.  
  169. // grabs the un-wrapped player
  170. function getPlayer() {
  171. var doc = uw.document;
  172. return doc.getElementById('c4-player') || doc.getElementById('movie_player');
  173. }
  174.  
  175. // adds the Options button below the video
  176. function addButton() {
  177. var footer = GM_config.get('footer') === true,
  178. footerHolder = document.getElementById('footer-main');
  179.  
  180. addButtonToContainer('Auto-Buffer Options', function () { GM_config.open(); }, 'autobuffer-options');
  181.  
  182. if (footer && footerHolder) {
  183. footerHolder.appendChild( document.getElementById('autobuffer-options') );
  184. }
  185. }
  186.  
  187. // this function sets up the script
  188. function init() {
  189. hasMainBeenRun = false;
  190.  
  191. // get the raw window object of the YouTube page
  192. uw = typeof unsafeWindow !== 'undefined' ? unsafeWindow : unwrap(window);
  193.  
  194. // temporary fix to disable SPF aka the "red bar" feature
  195. if (uw._spf_state && uw._spf_state.config) {
  196. uw._spf_state.config['navigate-limit'] = 0;
  197. uw._spf_state.config['navigate-part-received-callback'] = function (targetUrl) {
  198. location.href = targetUrl;
  199. };
  200. }
  201.  
  202. uw.onYouTubePlayerReady = function onYouTubePlayerReady(player) {
  203. if (typeof player === 'object' && hasMainBeenRun === false) {
  204. window.postMessage('YTAB__ready', '*');
  205. }
  206. };
  207.  
  208. CFL.waitFor({
  209. selector : '#c4-player, #movie_player',
  210. verifier : function (elem) {
  211. elem = unwrap( elem[0] );
  212. return typeof elem.stopVideo === 'function';
  213. },
  214. done : function () {
  215. if (hasMainBeenRun === false) {
  216. main();
  217. }
  218. }
  219. });
  220. }
  221.  
  222. // this is the main function. it does all the autobuffering, quality/volume changing, annotation hiding, etc
  223. function main() {
  224. var player = getPlayer(),
  225. parent = player.parentNode,
  226. alreadyBuffered = false,
  227. time = 0,
  228. args, arg, buffer_intv, fv, isHTML5, playerClone,
  229. playIfPlaylist, val, userOpts;
  230.  
  231. // don't let main() run again unless a new video is loaded
  232. hasMainBeenRun = true;
  233.  
  234. // remove the player out of the document temporarily while other things are being done,
  235. // to reduce the time the player may be playing the video
  236. parent.removeChild(player);
  237.  
  238. // set up the user options object
  239. userOpts = {
  240. activationMode : GM_config.get('activationMode'),
  241. disableDash : GM_config.get('disableDash') === true,
  242. hideAnnotations : GM_config.get('hideAnnotations') === true,
  243. hideAds : GM_config.get('hideAds') === true,
  244. quality : GM_config.get('autoHD'),
  245. theme : GM_config.get('theme'),
  246. volume : GM_config.get('volume')
  247. };
  248.  
  249. // set up other variables
  250. playerClone = player.cloneNode(true);
  251. fv = player.getAttribute('flashvars');
  252. isHTML5 = !!document.querySelector('video.html5-main-video');
  253. playIfPlaylist = !!URL.match(rList) && GM_config.get('autoplayplaylists') === true;
  254.  
  255. if (uw.ytplayer && uw.ytplayer.config && uw.ytplayer.config.args) {
  256. args = uw.ytplayer.config.args;
  257. }
  258.  
  259. // set the volume to the user's preference
  260. if (userOpts.volume != 1000) {
  261. player.setVolume(userOpts.volume);
  262. }
  263.  
  264. if (isHTML5) {
  265. if (player.getPlaybackQuality() !== userOpts.quality) {
  266. player.setPlaybackQuality(userOpts.quality);
  267. }
  268.  
  269. if (!playIfPlaylist) {
  270. if (userOpts.activationMode === 'buffer') {
  271. player.pauseVideo();
  272. } else if (userOpts.activationMode === 'none') {
  273. player.stopVideo();
  274. }
  275. }
  276. } else {
  277. // copy 'ytplayer.config.args' into the flash vars
  278. if (args) {
  279. for (arg in args) {
  280. val = args[arg];
  281. if ( args.hasOwnProperty(arg) && isPrimitiveType(val) ) {
  282. fv = setPref(fv, [ [ arg, encodeURIComponent(val) ] ]);
  283. }
  284. }
  285. }
  286.  
  287. // ad removal
  288. if (userOpts.hideAds) {
  289. fv = fv.replace(new RegExp('(&amp;|[&?])?(' + ads.join('|') + ')=[^&]*', 'g'), '');
  290. /*
  291. fv = setPref(fv,
  292. ads.map(function (ad) {
  293. return [ad, ''];
  294. })
  295. );
  296. */
  297. }
  298.  
  299. // disable DASH playback
  300. if (userOpts.disableDash) {
  301. fv = setPref(fv, [
  302. ['dashmpd', ''],
  303. ['dash', '0']
  304. ]);
  305. }
  306.  
  307. // edit the flashvars
  308. fv = setPref(fv, [
  309. ['vq', userOpts.quality], // set the quality
  310. ['autoplay', (userOpts.activationMode !== 'none' || playIfPlaylist) ? '1' : '0' ], // enable/disable autoplay
  311. ['iv_load_policy', userOpts.hideAnnotations ? '3' : '1' ], // enable/disable annotations
  312. ['theme', userOpts.theme], // use light/dark theme
  313.  
  314. // some "just-in-case" settings
  315. ['enablejsapi', '1'], // enable JS API
  316. ['jsapicallback', 'onYouTubePlayerReady'], // enable JS ready callback
  317. ['fs', '1'], // enable fullscreen button, just in-case
  318. ['modestbranding', '1'], // hide YouTube logo in player
  319. ['disablekb', '0'] // enable keyboard controls in player
  320. ]);
  321.  
  322. // handle video starting time
  323. if ( tTime.match(/\d+m/) ) {
  324. time += toNum( tTime.match(/(\d+)m/)[1] ) * 60;
  325. }
  326. if ( tTime.match(/\d+s/) ) {
  327. time += toNum( tTime.match(/(\d+)s/)[1] );
  328. }
  329. if ( tTime.match(/^\d+$/) ) {
  330. time += toNum(tTime);
  331. }
  332. if (time <= 3) {
  333. // if no time is in the url, check the player's time
  334. try {
  335. // sometimes causes a weird error.
  336. // it will say getCurrentTime isn't a function,
  337. // even though the typeof is "function",
  338. // and alerting its value says [native code]
  339. time = player.getCurrentTime();
  340. } catch (e) {}
  341. if (time <= 3) {
  342. time = 0;
  343. }
  344. }
  345. fv = setPref( fv, [ ['start', time] ] );
  346.  
  347. // set the new player's flashvars
  348. playerClone.setAttribute('flashvars', fv);
  349.  
  350. // replace the original player with the modified clone
  351. parent.appendChild(playerClone);
  352.  
  353. if (userOpts.activationMode === 'buffer' && playIfPlaylist === false) {
  354. // handle auto-buffering
  355. buffer_intv = CFL.setInterval(function () {
  356. var player = getPlayer();
  357.  
  358. if (player && typeof player.getPlayerState === 'function') {
  359. CFL.clearInterval(buffer_intv);
  360.  
  361. // pause the video so it can buffer
  362. player.pauseVideo();
  363.  
  364. // seek back to beginning if time elapsed is not much
  365. if (player.getCurrentTime() <= 3) {
  366. player.seekTo(0);
  367. }
  368.  
  369. // adjust to the 'play symbol in title' feature
  370. window.setTimeout(fixPlaySymbol, 1000);
  371. }
  372. }, 100);
  373. } else if (userOpts.activationMode === 'none') {
  374. // adjust to the 'play symbol in title' feature
  375. window.setTimeout(fixPlaySymbol, 1500);
  376. }
  377. }
  378.  
  379. // show the first time user message, then set it to never show again
  380. if (GM_config.getValue('yt-autobuffer-autohd-first', 'yes') === 'yes') {
  381. msg({
  382. text : 'Welcome to "' + script_name + '".\n\n\n\n' +
  383. 'There is an options button below the video.\n\n\n\n' +
  384. 'The options screen will automatically open when you close this message.',
  385. title : '"' + script_name + '" Message',
  386. onclose : function () { GM_config.open(); }
  387. });
  388. GM_config.setValue('yt-autobuffer-autohd-first', 'no');
  389. }
  390. }
  391.  
  392. // make sure the page is not in a frame
  393. // & is on a YouTube page (the @include works most of the time, but this is 100%)
  394. // & isn't on a blacklisted YouTube page
  395. if ( window !== window.top || !URL.match(rYoutubeUrl) /*|| URL.match(rYoutubeBlacklistedUrl)*/ ) { return; }
  396.  
  397. // quit if CFL/GM_config is non-existant
  398. if (typeof CFL === 'undefined' || typeof GM_config === 'undefined' || typeof addButtonToContainer === 'undefined') {
  399. return alert('' +
  400. 'A @require is missing.\n\n' +
  401. 'Either you\'re not using the correct plug-in, or @require isn\'t working.\n\n' +
  402. 'Please review the script\'s main page to see which browser & add-on to use.' +
  403. '');
  404. }
  405.  
  406. // add a user-script command
  407. if (typeof GM_registerMenuCommand === 'function') {
  408. GM_registerMenuCommand('"' + script_name + '" Options', GM_config.open);
  409. }
  410.  
  411. // init GM_config
  412. GM_config.init('"' + script_name + '" Options', {
  413. activationMode : {
  414. section : ['Main Options'],
  415. label : 'Activation Mode',
  416. type : 'select',
  417. options : {
  418. 'buffer' : 'Auto Buffer (aka Auto Pause)',
  419. 'play' : 'Auto Play',
  420. 'none' : 'Stop Loading Immediately'
  421. },
  422. 'default' : 'buffer'
  423. },
  424. autoHD : {
  425. label : 'Auto HD',
  426. type : 'select',
  427. options : {
  428. 'default' : 'Automatic (default)',
  429. 'tiny' : '144p',
  430. 'small' : '240p',
  431. 'medium' : '360p',
  432. 'large' : '480p',
  433. 'hd720' : '720p (HD)',
  434. 'hd1080' : '1080p (HD)',
  435. 'hd1440' : '1440p (HD)',
  436. 'highres' : 'Original (highest)'
  437. },
  438. 'default' : 'hd1080'
  439. },
  440. disableDash : {
  441. label : 'Disable DASH Playback',
  442. type : 'checkbox',
  443. 'default' : true,
  444. title : '"DASH" loads the video in blocks/pieces; disrupts autobuffering -- Note: Qualities are limited when disabled'
  445. },
  446. hideAds : {
  447. label : 'Disable Ads',
  448. type : 'checkbox',
  449. 'default' : true,
  450. title : 'Should disable advertisements. AdBlock is better, though. Get that instead'
  451. },
  452. hideAnnotations : {
  453. label : 'Disable Annotations',
  454. type : 'checkbox',
  455. 'default' : false
  456. },
  457. theme : {
  458. section : ['Other Options'],
  459. label : 'Player Color Scheme',
  460. type : 'select',
  461. options : {
  462. 'dark' : 'Dark Theme',
  463. 'light' : 'Light Theme'
  464. },
  465. 'default' : 'dark'
  466. },
  467. volume : {
  468. label : 'Set volume to: ',
  469. type : 'select',
  470. options : {
  471. '1000' : 'Don\'t Change',
  472. '0' : 'Off',
  473. '5' : '5%',
  474. '10' : '10%',
  475. '20' : '20%',
  476. '25' : '25% (quarter)',
  477. '30' : '30%',
  478. '40' : '40%',
  479. '50' : '50% (half)',
  480. '60' : '60%',
  481. '70' : '70%',
  482. '75' : '75% (three quarters',
  483. '80' : '80%',
  484. '90' : '90%',
  485. '100' : '100% (full)',
  486. },
  487. title : 'What to set the volume to',
  488. 'default' : '1000'
  489. },
  490. autoplayplaylists : {
  491. label : 'Autoplay on Playlists (override)',
  492. type : 'checkbox',
  493. 'default' : false,
  494. title : 'This will enable autoplay on playlists, regardless of the "Activation Mode" option'
  495. },
  496. footer : {
  497. label : 'Options Button In Footer',
  498. type : 'checkbox',
  499. 'default' : false,
  500. title : 'This will make the options button show at the bottom of the page in the footer'
  501. }
  502. }, '' +
  503. 'body { ' +
  504. 'background-color: #DDDDDD !important; ' +
  505. 'color: #434343 !important; ' +
  506. 'font-family: Arial, Verdana, sans-serif !important; ' +
  507. '}' +
  508. '#config_header { ' +
  509. 'font-size: 16pt !important; ' +
  510. '}' +
  511. '.config_var { ' +
  512. 'margin-left: 20% !important; ' +
  513. 'margin-top: 20px !important; ' +
  514. '}' +
  515. '#header { ' +
  516. 'margin-bottom: 40px !important; ' +
  517. 'margin-top: 20px !important; ' +
  518. '}' +
  519. '.indent40 { ' +
  520. 'margin-left: 20% !important; ' +
  521. '}' +
  522. '.config_var * { ' +
  523. 'font-size: 10pt !important; ' +
  524. '}' +
  525. '.section_header_holder { ' +
  526. 'border-bottom: 1px solid #BBBBBB !important; ' +
  527. 'margin-top: 14px !important; ' +
  528. '}' +
  529. '.section_header { ' +
  530. 'background-color: #BEDBFF !important; ' +
  531. 'color: #434343 !important; ' +
  532. 'margin-left: 20% !important; ' +
  533. 'margin-top: 8px !important; ' +
  534. 'padding: 2px 200px !important; ' +
  535. 'text-decoration: none !important; ' +
  536. '}' +
  537. '.section_kids { ' +
  538. 'margin-bottom: 14px !important; ' +
  539. '}' +
  540. '.saveclose_buttons { ' +
  541. 'font-size: 14pt !important; ' +
  542. '}' +
  543. '#buttons_holder { ' +
  544. 'padding-right: 50px; ' +
  545. '}' +
  546. '', {
  547. close : function () {
  548. CFL('#c4-player, #movie_player').css('visibility', 'visible');
  549. CFL('#lights_out').hide();
  550. },
  551. open : function () {
  552. CFL('#c4-player, #movie_player').css('visibility', 'hidden');
  553. CFL('#lights_out').show('block');
  554. CFL('#GM_config').css('height', '80%').css('width', '80%');
  555. }
  556. });
  557.  
  558. // this is for the "lights out" feature of GM_config
  559. CFL.runAt('interactive', function () {
  560. CFL(document.body).append('div', {
  561. id : 'lights_out',
  562. style : 'display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 999998; background: rgba(0, 0, 0, 0.72);'
  563. });
  564.  
  565. // call the function that sets up everything
  566. init();
  567. });
  568.  
  569. // add a message listener for when the unsafeWindow function fires a message
  570. window.addEventListener('message', function (msg) {
  571. if (msg.data === 'YTAB__ready') {
  572. main();
  573. }
  574. }, false);
  575.  
  576. // wait for an element that can hold the options button to load,
  577. // then run our add button function
  578. CFL.waitFor({
  579. selector : '#watch7-headline, #gh-overviewtab div.c4-spotlight-module-component, #footer-main',
  580. done : addButton
  581. });
  582.  
  583. }());

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址