// ==UserScript==
// @name YouTube Auto Buffer & Auto HD
// @namespace http://userscripts.org/users/23652
// @description Buffers the video without autoplaying and puts it in HD if the option is on. For Firefox, Opera, & Chrome
// @icon https://raw.github.com/joesimmons/YouTube---Auto-Buffer---Auto-HD/master/media/logo-64x64.png
// @include http://*.youtube.com/*
// @include http://youtube.com/*
// @include https://*.youtube.com/*
// @include https://youtube.com/*
// @copyright JoeSimmons
// @author JoeSimmons
// @version 1.2.87
// @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @require https://github.com/joesimmons/jsl/raw/master/versions/jsl-1.3.1.js
// @require https://github.com/joesimmons/YouTube_-_Button_Container_require/raw/master/versions/YouTube_-_Button_Container_1.0.2.user.js
// @require https://greasyfork.org/scripts/1884-gm-config/code/GM_config.js?version=4836
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// ==/UserScript==
/* CHANGELOG
1.2.87 (1/22/2014)
- fixed odd internal error that made the player not show
- fixed problem with "Red Bar" not being disabled properly
1.2.86 (12/21/2013)
- fixed Large player button (as far as I can tell)
- added Automatic quality option (the YouTube default)
- switched the DASH option default to enabled
this is really what the script was made for... auto-buffering
1.2.85 (12/11/2013)
- added a script icon
- added a "Player Color Scheme" option
- fixed bug with SPF not being de-activated properly in non-Firefox browsers
- fixed volume bug
- fixed adding time in url bug. it will now skip to the right portion of the video
- changed internal name of the "Activation Mode" option. shouldn't affect the user
- the script doesn't add any javascript to the page anymore
it uses onYouTubePlayerReady to detect when the player is ready;
it's much more performant than an interval
1.2.84 (10/31/2013)
- added primitive type checking when copying ytplayer.config.args into the flashvars.
this fixes the issue with Flashgot and possibly other add-ons
- fixed non-activation by moving the _spf_state check to the top of init.
this disables SPF on every YouTube page now, and should make the script activate correctly
- changed all RegExp test methods to match. match seems more consistent.
I've had cases where test doesn't work, but match does
1.2.83 (10/28/2013)
- added auto HD, volume, and more activation modes for html5 (thanks to youtube updating its API)
- changed the default quality to 1080p
- changed the wording of some options
- changed the "Disable Dash Playback" option to false for default
- disabled SPF (aka Red Bar feature) completely until I get playlists working better
- changed the setPref prototype function to a regular function
1.2.82 (9/5/2013)
- added support for older Firefox versions (tested on 3.6)
- added a new option to disable 'dash' playback (videos loading in blocks/pieces)
- re-added ad removal feature (experimental for now)
1.2.81
- fixed HTML5 support. YT changed tag names so the script got confused
- made a few minor performance tweaks
- fixed 'play symbol in title' bug in autobuffer mode (it would show playing, even though it's paused/buffering)
1.2.80
- switched to JSL.setInterval for consistency and drift accommodation
- visual tweaks to:
msg().
the rest of the page now dims while the msg box is visible
changed the spacing of most of the elements
changed the font sizes and the font (Arial)
added a close button instead of requiring a double click
made it auto-open the options screen when the msg is closed
GM_config.
made the background color more mellow and moved the section title near the middle
1.2.79
- adjusted to the new play symbol in the youtube title feature
- Changed margins on the settings button when in footer
- Switched JSL/pushState checking order.
Previously in 1.2.78, if JSL didn't exist or wasn't @required, the script
would still loop every 500ms to re-set the pushState method, even though the
script wasn't going to be running.
I switched that so that JSL has to exist before the script does anything.
1.2.78
- Fixed bug where options button wasn't getting added to the footer with the new Red Bar YT feature
1.2.77
- Adapted to the new YouTube feature that uses HTML5's history.pushState to load videos
- Small fixes here and there
- Excluded (with RegExp) pages without videos on them
- Fixed GM_config.log()
- Declared all variables at the beginning of functions
- Made finding the video player a little more reliable
- Make 'autoplay on playlists' work with HTML5 videos
1.2.76
- Added new quality option ('1080p+' - for anything higher than 1080p)
1.2.75
- Added a new option (to move option button to page footer)
- Added a new option (to autoplay on playlists regardless of auto[play/buffer] setting)
- Added a first time user message box
- Fixed bug with GM_config's [set/get]Value functions. Chrome/Opera were not using localStorage before this update
1.2.74
- Adapted to YouTube's new layout
1.2.73
- Added compatibility for user pages
1.2.72
- Made it fully working again in Opera & Chrome
- Switched from setInterval to setTimeout due to instability
- Added an anonymous function wrapper
1.2.71
- Added compatibility for HTML5
*/
// run the script in an IIFE, to hide its variables from the global scope
(function (undefined) {
'use strict';
var aBlank = ['', '', ''],
URL = location.href,
navID = 'watch7-user-header',
rYoutubeUrl = /^https?:\/\/([^\.]+\.)?youtube\.com\//,
// rYoutubeBlacklistedUrl = /^https?:\/\/([^\.]+\.)?youtube\.com\/(feed\/(?!subscriptions)|account|inbox|my_|tags|view_all|analytics)/i,
rList = /[?&]list=/i,
rPlaySymbol = /^\u25B6\s*/,
script_name = 'YouTube - Auto-Buffer & Auto-HD',
tTime = (URL.match(/[&#?]t=([sm0-9]+)/) || aBlank)[1],
ads = [
'supported_without_ads',
'ad3_module',
'adsense_video_doc_id',
'allowed_ads',
'baseUrl',
'cafe_experiment_id',
'afv_inslate_ad_tag',
'advideo',
'ad_device',
'ad_channel_code_instream',
'ad_channel_code_overlay',
'ad_eurl',
'ad_flags',
'ad_host',
'ad_host_tier',
'ad_logging_flag',
'ad_preroll',
'ad_slots',
'ad_tag',
'ad_video_pub_id',
'aftv',
'afv',
'afv_ad_tag',
'afv_instream_max',
'afv_ad_tag_restricted_to_instream',
'afv_video_min_cpm',
'prefetch_ad_live_stream'
],
hasMainBeenRun, missing_require, nav, uw, wait_intv;
function toNum(a) {
return parseInt(a, 10);
}
// msg by JoeSimmons
function msg(infoObject) {
var box_id_name = 'script_msg',
box = document.getElementById(box_id_name),
rLinebreaks = /[\r\n]/g,
title = typeof infoObject.title === 'string' && infoObject.title.length > 3 ? infoObject.title : 'Message Box by JoeSimmons.';
// add BR tags to line breaks
infoObject.text = infoObject.text.replace(rLinebreaks, '<br />\n');
function msg_close(event) {
event.preventDefault();
document.getElementById(box_id_name).style.display = 'none';
if (typeof infoObject.onclose === 'function') {
infoObject.onclose();
}
}
if (box == null) {
JSL.addStyle('' +
'@keyframes blink { ' +
'50% { color: #B95C00; } ' +
'}\n\n' +
'#' + box_id_name + ' .msg-header { ' +
'animation: blink 1s linear infinite normal; ' +
'}' +
'');
document.body.appendChild(
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);'}, [
// main box
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;'}, [
// header
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}),
// text (message)
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;'}),
// close button
JSL.create('div', {style : 'position: absolute; bottom: 20px; left: 0; width: 100%; text-align: center;'}, [
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;'})
])
])
])
);
} else {
box.innerHTML += infoObject.text;
}
}
// will return true if the value is a primitive value
function isPrimitiveType(value) {
switch (typeof value) {
case 'string': case 'number': case 'boolean': case 'undefined': {
return true;
}
case 'object': {
return !value;
}
}
return false;
}
function setPref(str, values) {
var i, value, rQuery;
for (i = 0; value = values[i]; i += 1) {
// (several lines for readability)
rQuery = new RegExp('[?&]?' + value[0] + '=[^&]*');
str = str.replace(rQuery, '') + '&' + value[0] + '=' + value[1];
str = str.replace(/^&+|&+$/g, '');
}
return str;
}
// unwraps the element so we can use its methods freely
function unwrap(elem) {
if (elem) {
if ( typeof XPCNativeWrapper === 'function' && typeof XPCNativeWrapper.unwrap === 'function' ) {
return XPCNativeWrapper.unwrap(elem);
} else if (elem.wrappedJSObject) {
return elem.wrappedJSObject;
}
}
return elem;
}
function fixPlaySymbol() {
document.title = document.title.replace(rPlaySymbol, '');
}
// grabs the un-wrapped player
function getPlayer() {
var doc = uw.document;
return doc.getElementById('c4-player') || doc.getElementById('movie_player');
}
// adds the Options button below the video
function addButton() {
var footer = GM_config.get('footer') === true,
footerHolder = document.getElementById('footer-main');
addButtonToContainer('Auto-Buffer Options', function () { GM_config.open(); }, 'autobuffer-options');
if (footer && footerHolder) {
footerHolder.appendChild( document.getElementById('autobuffer-options') );
}
}
// this function sets up the script
function init() {
hasMainBeenRun = false;
// get the raw window object of the YouTube page
uw = typeof unsafeWindow !== 'undefined' ? unsafeWindow : unwrap(window);
// disable Red Bar aka SPF
if (uw._spf_state && uw._spf_state.config) {
uw._spf_state.config['navigate-limit'] = 0;
uw._spf_state.config['navigate-part-received-callback'] = function (targetUrl) {
location.href = targetUrl;
};
}
uw.onYouTubePlayerReady = function onYouTubePlayerReady(player) {
if (typeof player === 'object' && hasMainBeenRun === false) {
window.postMessage('YTAB__ready', '*');
}
};
JSL.waitFor({
selector : '#c4-player, #movie_player',
verifier : function (elem) {
elem = unwrap( elem[0] );
return typeof elem.stopVideo === 'function';
},
done : function () {
if (hasMainBeenRun === false) {
main();
}
}
});
}
// this is the main function. it does all the autobuffering, quality/volume changing, annotation hiding, etc
function main() {
var player = getPlayer(),
parent = player.parentNode,
alreadyBuffered = false,
time = 0,
args, arg, buffer_intv, fv, isHTML5, playerClone,
playIfPlaylist, val, userOpts;
// don't let main() run again unless a new video is loaded
hasMainBeenRun = true;
// remove the player out of the document temporarily while other things are being done,
// to reduce the time the player may be playing the video
parent.removeChild(player);
// set up the user options object
userOpts = {
activationMode : GM_config.get('activationMode'),
disableDash : GM_config.get('disableDash') === true,
hideAnnotations : GM_config.get('hideAnnotations') === true,
hideAds : GM_config.get('hideAds') === true,
quality : GM_config.get('autoHD'),
theme : GM_config.get('theme'),
volume : GM_config.get('volume')
};
// set up other variables
playerClone = player.cloneNode(true);
fv = player.getAttribute('flashvars');
isHTML5 = !!document.querySelector('video.html5-main-video');
playIfPlaylist = !!URL.match(rList) && GM_config.get('autoplayplaylists') === true;
if (uw.ytplayer && uw.ytplayer.config && uw.ytplayer.config.args) {
args = uw.ytplayer.config.args;
}
// set the volume to the user's preference
if (userOpts.volume != 1000) {
player.setVolume(userOpts.volume);
}
if (isHTML5) {
if (player.getPlaybackQuality() !== userOpts.quality) {
player.setPlaybackQuality(userOpts.quality);
}
if (!playIfPlaylist) {
if (userOpts.activationMode === 'buffer') {
player.pauseVideo();
} else if (userOpts.activationMode === 'none') {
player.stopVideo();
}
}
} else {
// copy 'ytplayer.config.args' into the flash vars
if (args) {
for (arg in args) {
val = args[arg];
if ( args.hasOwnProperty(arg) && isPrimitiveType(val) ) {
fv = setPref(fv, [ [ arg, encodeURIComponent(val) ] ]);
}
}
}
// ad removal
if (userOpts.hideAds) {
fv = fv.replace(new RegExp('(&|[&?])?(' + ads.join('|') + ')=[^&]*', 'g'), '');
/*
fv = setPref(fv,
ads.map(function (ad) {
return [ad, ''];
})
);
*/
}
// disable DASH playback
if (userOpts.disableDash) {
fv = setPref(fv, [
['dashmpd', ''],
['dash', '0']
]);
}
// edit the flashvars
fv = setPref(fv, [
['vq', userOpts.quality], // set the quality
['autoplay', (userOpts.activationMode !== 'none' || playIfPlaylist) ? '1' : '0' ], // enable/disable autoplay
['iv_load_policy', userOpts.hideAnnotations ? '3' : '1' ], // enable/disable annotations
['theme', userOpts.theme], // use light/dark theme
// some "just-in-case" settings
['enablejsapi', '1'], // enable JS API
['jsapicallback', 'onYouTubePlayerReady'], // enable JS ready callback
['fs', '1'], // enable fullscreen button, just in-case
['modestbranding', '1'], // hide YouTube logo in player
['disablekb', '0'] // enable keyboard controls in player
]);
// handle video starting time
if ( tTime.match(/\d+m/) ) {
time += toNum( tTime.match(/(\d+)m/)[1] ) * 60;
}
if ( tTime.match(/\d+s/) ) {
time += toNum( tTime.match(/(\d+)s/)[1] );
}
if ( tTime.match(/^\d+$/) ) {
time += toNum(tTime);
}
if (time <= 3) {
// if no time is in the url, check the player's time
try {
// sometimes causes a weird error.
// it will say getCurrentTime isn't a function,
// even though the typeof is "function",
// and alerting its value says [native code]
time = player.getCurrentTime();
} catch (e) {}
if (time <= 3) {
time = 0;
}
}
fv = setPref( fv, [ ['start', time] ] );
// set the new player's flashvars
playerClone.setAttribute('flashvars', fv);
// replace the original player with the modified clone
parent.appendChild(playerClone);
if (userOpts.activationMode === 'buffer' && playIfPlaylist === false) {
// handle auto-buffering
buffer_intv = JSL.setInterval(function () {
var player = getPlayer();
if (player && typeof player.getPlayerState === 'function') {
JSL.clearInterval(buffer_intv);
// pause the video so it can buffer
player.pauseVideo();
// seek back to beginning if time elapsed is not much
if (player.getCurrentTime() <= 3) {
player.seekTo(0);
}
// adjust to the 'play symbol in title' feature
window.setTimeout(fixPlaySymbol, 1000);
}
}, 100);
} else if (userOpts.activationMode === 'none') {
// adjust to the 'play symbol in title' feature
window.setTimeout(fixPlaySymbol, 1500);
}
}
// show the first time user message, then set it to never show again
if (GM_config.getValue('yt-autobuffer-autohd-first', 'yes') === 'yes') {
msg({
text : 'Welcome to "' + script_name + '".\n\n\n\n' +
'There is an options button below the video.\n\n\n\n' +
'The options screen will automatically open when you close this message.',
title : '"' + script_name + '" Message',
onclose : function () { GM_config.open(); }
});
GM_config.setValue('yt-autobuffer-autohd-first', 'no');
}
}
// make sure the page is not in a frame
// & is on a YouTube page (the @include works most of the time, but this is 100%)
// & isn't on a blacklisted YouTube page
if ( window !== window.top || !URL.match(rYoutubeUrl) /*|| URL.match(rYoutubeBlacklistedUrl)*/ ) { return; }
// quit if one of the @requires is non-existent
if (typeof JSL === 'undefined' || typeof GM_config === 'undefined' || typeof addButtonToContainer === 'undefined') {
missing_require = typeof JSL === 'undefined' ? 'JSL' : typeof GM_config === 'undefined' ? 'GM_config' : typeof addButtonToContainer ? 'Button Container' : 'unknown';
return alert('' +
'A @require is missing (' + missing_require + ').\n\n' +
'Either you\'re not using the correct plug-in, or @require isn\'t working.\n\n' +
'Please review the script\'s main page to see which browser & add-on to use.' +
'');
}
// add a user-script command
if (typeof GM_registerMenuCommand === 'function') {
GM_registerMenuCommand('"' + script_name + '" Options', GM_config.open);
}
// init GM_config
GM_config.init('"' + script_name + '" Options', {
activationMode : {
section : ['Main Options'],
label : 'Activation Mode',
type : 'select',
options : {
'buffer' : 'Auto Buffer (aka Auto Pause)',
'play' : 'Auto Play',
'none' : 'Stop Loading Immediately'
},
'default' : 'buffer'
},
autoHD : {
label : 'Auto HD',
type : 'select',
options : {
'default' : 'Automatic (default)',
'tiny' : '144p',
'small' : '240p',
'medium' : '360p',
'large' : '480p',
'hd720' : '720p (HD)',
'hd1080' : '1080p (HD)',
'hd1440' : '1440p (HD)',
'highres' : 'Original (highest)'
},
'default' : 'hd1080'
},
disableDash : {
label : 'Disable DASH Playback',
type : 'checkbox',
'default' : true,
title : '"DASH" loads the video in blocks/pieces; disrupts autobuffering -- Note: Qualities are limited when disabled'
},
hideAds : {
label : 'Disable Ads',
type : 'checkbox',
'default' : true,
title : 'Should disable advertisements. AdBlock is better, though. Get that instead'
},
hideAnnotations : {
label : 'Disable Annotations',
type : 'checkbox',
'default' : false
},
theme : {
section : ['Other Options'],
label : 'Player Color Scheme',
type : 'select',
options : {
'dark' : 'Dark Theme',
'light' : 'Light Theme'
},
'default' : 'dark'
},
volume : {
label : 'Set volume to: ',
type : 'select',
options : {
'1000' : 'Don\'t Change',
'0' : 'Off',
'5' : '5%',
'10' : '10%',
'20' : '20%',
'25' : '25% (quarter)',
'30' : '30%',
'40' : '40%',
'50' : '50% (half)',
'60' : '60%',
'70' : '70%',
'75' : '75% (three quarters',
'80' : '80%',
'90' : '90%',
'100' : '100% (full)',
},
title : 'What to set the volume to',
'default' : '1000'
},
autoplayplaylists : {
label : 'Autoplay on Playlists (override)',
type : 'checkbox',
'default' : false,
title : 'This will enable autoplay on playlists, regardless of the "Activation Mode" option'
},
footer : {
label : 'Options Button In Footer',
type : 'checkbox',
'default' : false,
title : 'This will make the options button show at the bottom of the page in the footer'
}
}, '' +
'body { ' +
'background-color: #DDDDDD !important; ' +
'color: #434343 !important; ' +
'font-family: Arial, Verdana, sans-serif !important; ' +
'}' +
'#config_header { ' +
'font-size: 16pt !important; ' +
'}' +
'.config_var { ' +
'margin-left: 20% !important; ' +
'margin-top: 20px !important; ' +
'}' +
'#header { ' +
'margin-bottom: 40px !important; ' +
'margin-top: 20px !important; ' +
'}' +
'.indent40 { ' +
'margin-left: 20% !important; ' +
'}' +
'.config_var * { ' +
'font-size: 10pt !important; ' +
'}' +
'.section_header_holder { ' +
'border-bottom: 1px solid #BBBBBB !important; ' +
'margin-top: 14px !important; ' +
'}' +
'.section_header { ' +
'background-color: #BEDBFF !important; ' +
'color: #434343 !important; ' +
'margin-left: 20% !important; ' +
'margin-top: 8px !important; ' +
'padding: 2px 200px !important; ' +
'text-decoration: none !important; ' +
'}' +
'.section_kids { ' +
'margin-bottom: 14px !important; ' +
'}' +
'.saveclose_buttons { ' +
'font-size: 14pt !important; ' +
'}' +
'#buttons_holder { ' +
'padding-right: 50px; ' +
'}' +
'', {
close : function () {
JSL('#c4-player, #movie_player').css('visibility', 'visible');
JSL('#lights_out').hide();
},
open : function () {
JSL('#c4-player, #movie_player').css('visibility', 'hidden');
JSL('#lights_out').show('block');
JSL('#GM_config').css('height', '80%').css('width', '80%').css('zIndex', '999999999999');
}
});
// this is for the "lights out" feature of GM_config
JSL.runAt('interactive', function () {
JSL(document.body).append('div', {
id : 'lights_out',
style : 'display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 999999999998; background: rgba(0, 0, 0, 0.72);'
});
// call the function that sets up everything
init();
});
// add a message listener for when the unsafeWindow function fires a message
window.addEventListener('message', function (msg) {
if (msg.data === 'YTAB__ready') {
main();
}
}, false);
// wait for an element that can hold the options button to load,
// then run our add button function
JSL.waitFor({
selector : '#watch7-headline, #gh-overviewtab div.c4-spotlight-module-component, #footer-main',
done : addButton
});
}());
Donate for the site OpenUserJS
Are you sure you want to go to an external site to donate a monetary value?
WARNING: Some countries laws may supersede the payment processors policy such as the GDPR and PayPal. While it is highly appreciated to donate, please check with your countries privacy and identity laws regarding privacy of information first. Use at your utmost discretion.