Improved Speed Slider For YouTube (fix)

Add Speed Slider to Youtube Player Settings

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Improved Speed Slider For YouTube (fix)
// @namespace    improved_youtube_player_speed_slider_fix
// @version      1.0
// @description  Add Speed Slider to Youtube Player Settings
// @author       Łukasz, pabli, goldnick7
// @match        https://*.youtube.com/*
// @grant        none
// ==/UserScript==
var yts_build_timeout = 500;
var yts_remove_timeout = 1000;

var yts_el_menu = null;
var yts_el_slider_label = null;
var yts_el_slider_icon = null;
var yts_el_slider_check = null;
var yts_el_slider = null;
var yts_el_player = null;

var yts_event_em_speed = false;
var yts_event_player = false;

var yts_r = 'yts_r';
var yts_s = 'yts_s';


/*************************************
 *          INIT                     *
 ************************************/
function ytsInit() {
    $yts.event(document, "spfdone", function () {
        ytsInitPlayer();
    });
    ytsReopenMenu();
    ytsBuildApp();
}

function ytsBuildApp() {

    yts_el_menu = $yts.get('.ytp-panel-menu');
    if (yts_el_menu !== null) {
        setTimeout(ytsRemoveDefaultSpeed, yts_remove_timeout);
        ytsInitSlider();
        ytsInitMenu();
        ytsInitPlayer();
    }
    else {
        setTimeout(ytsBuildApp, yts_build_timeout);
    }
}


/*************************************
 *          MENU                    *
 ************************************/

function ytsInitMenu() {

    var speedMenu = $yts.new('div', {'className': 'ytp-menuitem', id: 'yts-menu'});
    var right = $yts.new('div', {'className': 'ytp-menuitem-content'});
	var cssMenu = $yts.new('style', {'type': 'text/css'});
	var css =`
.ytp-menuitem-toggle-checkbox[type="checkbox"]{
	-webkit-appearance: none;
	-moz-appearance: none;
	appearance: none;
	outline: none;
	cursor: pointer;
}
.ytp-menuitem-toggle-checkbox:checked[type="checkbox"]
{
	background: #f00;
}
.ytp-menuitem-toggle-checkbox:checked[type="checkbox"]:after
{
	left: 20px;
	background-color: #fff;
}
.ytp-menuitem-slider {
	-webkit-appearance: none;
	vertical-align: middle;
	outline: none;
	border: none;
	padding: 0;
	background: none;
}
.ytp-menuitem-slider::-webkit-slider-runnable-track {
	background-color: rgba(255, 255, 255, .5);
	height: 6px;
	border-radius: 3px;
	border: 1px solid transparent;
}
.ytp-menuitem-slider[disabled]::-webkit-slider-runnable-track {
	border: 1px solid rgba(255, 255, 255, .5);
	background-color: transparent;
	opacity: 0.4;
}
.ytp-menuitem-slider::-moz-range-track {
	background-color: rgba(255, 255, 255, .5);
	height: 6px;
	border-radius: 3px;
	border: none;
}
.ytp-menuitem-slider::-ms-track {
	color: transparent;
	border: none;
	background: none;
	height: 6px;
}
.ytp-menuitem-slider::-ms-fill-lower {
	background-color: rgba(255, 255, 255, .5);
	border-radius: 3px;
}
.ytp-menuitem-slider::-ms-fill-upper {
	background-color: rgba(255, 255, 255, .5);
	border-radius: 3px;
}
.ytp-menuitem-slider::-ms-tooltip {
	display: none;
}
.ytp-menuitem-slider::-moz-range-thumb {
	border-radius: 20px;
	height: 14px;
	width: 14px;
	border: none;
	background: none;
	background-color: #fff;
	box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.6);
	-moz-transition: all .08s cubic-bezier(0.4, 0.0, 1, 1);
}
.ytp-menuitem-slider:active::-moz-range-thumb {
	outline: none;
}
.ytp-menuitem-slider::-webkit-slider-thumb {
	-webkit-appearance: none !important;
	border-radius: 100%;
	background-color: #fff;
	height: 14px;
	width: 14px;
	margin-top: -5px;
	box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.6);
	-moz-transition: all .08s cubic-bezier(0.4, 0.0, 1, 1);
	-webkit-transition: all .08s cubic-bezier(0.4, 0.0, 1, 1);
}
.ytp-menuitem-slider:active::-webkit-slider-thumb {
	outline: none;
}
.ytp-menuitem-slider::-ms-thumb {
	border-radius: 100%;
	background-color: #fff;
	height: 14px;
	width: 14px;
	border: none;
}
.ytp-menuitem-slider:active::-ms-thumb {
	border: none;
}
`;
	cssMenu.innerHTML = css;
    right.appendChild(yts_el_slider_check);
    right.appendChild(yts_el_slider);
    right.appendChild(cssMenu);
    speedMenu.appendChild(yts_el_slider_icon);
    speedMenu.appendChild(yts_el_slider_label);
    speedMenu.appendChild(right);
    yts_el_menu.appendChild(speedMenu);
}

function ytsRemoveDefaultSpeed() {
    var switchers = $yts.getOpt(".ytp-menuitem", {role: 'menuitemcheckbox'});
    var toRemove = null;

    if (!ytsPlayerHasClass('ad-interrupting') && switchers && switchers.length && !yts_event_em_speed) {
        toRemove = switchers[switchers.length - 1].nextElementSibling;
        if (toRemove && toRemove.id !== 'yts-menu') {
            $yts.style(toRemove, 'display', 'none');
            yts_event_em_speed = true;
        }
    }
}

function ytsReopenMenu() {
    var settings_button = $yts.get(".ytp-settings-button");
    settings_button && settings_button.click();
    settings_button && settings_button.click();
}


/*************************************
 *          SLIDER                   *
 ************************************/

function ytsInitSlider() {
    var rem = ytsParam(yts_r);
    var speed = ytsParam(yts_s) || 1;
    speed = rem ? speed : 1;

	yts_el_slider_icon = $yts.new('div', {'className': 'ytp-menuitem-icon'});//pabli
    yts_el_slider_label = $yts.new('div', {'className': 'ytp-menuitem-label'});
    yts_el_slider_check = $yts.new('input', {
        'type': 'checkbox',
        'title': 'Remember speed',
		'className': 'ytp-menuitem-toggle-checkbox'
    });

    yts_el_slider = $yts.new('input', {
		'className': 'ytp-menuitem-slider',
        'type': 'range',
        'min': 0.5,
        'max': 4.0,
        'step': 0.1,
        'value': speed,
        style: {
            'width': 'calc(100% - 60px)',
            'margin': '0 5px',
            'padding': '0',
			'vertical-align': 'text-bottom'
        }
    });
    if(rem){
        yts_el_slider_check.checked = true;
    }
    $yts.event(yts_el_slider, 'change', ytsChangeSlider);
    $yts.event(yts_el_slider_check, 'change', ytsChangeRemember);
    $yts.event(yts_el_slider, 'input', ytsChangeSlider);
    $yts.event(yts_el_slider, 'wheel', ytsWheelSlider);

    ytsUpdateSliderLabel(speed);
}


function ytsWheelSlider(event) {
    var val = parseFloat(event.target.value) + (event.deltaY > 0 ? -0.1 : 0.1);
    val = val < 0.5 ? 0.5 : (val > 4 ? 4 : val);
    if (event.target.value !== val) {
        event.target.value = val;
        ytsUpdateSliderLabel(val);
    }
    event.preventDefault();
}

function ytsChangeSlider(event) {
    ytsUpdateSliderLabel(event.target.value);
}

function ytsChangeRemember() {
    ytsParam(yts_r, ytsParam(yts_r) ? 0 : 1);
}


function ytsUpdateSliderLabel(val) {
    ytsSetPlayerDuration(val);
    yts_el_slider_label.innerHTML = 'Speed (' + parseFloat(val).toFixed(1) + ')';
}


/*************************************
 *          PLAYER                   *
 ************************************/

function ytsInitPlayer() {
    yts_el_player = $yts.get('.html5-main-video');
    ytsObservePlayer();
    if (ytsParam(yts_s) && ytsParam(yts_r)) {
        ytsSetPlayerDuration(ytsParam(yts_s));
        ytsUpdateSliderLabel(ytsParam(yts_s));
    }

}

function ytsPlayerHasClass (cls) {
    ytsInitPlayer();
    return yts_el_player && yts_el_player.classList.contains(cls);
}

function ytsSetPlayerDuration(value) {
    ytsParam(yts_s, value);
    if (yts_el_player) {
        yts_el_player.playbackRate = value;
    }
}

function ytsObservePlayer() {
    if (!yts_event_player) {
        ytsObserver(yts_el_player.parentNode.parentNode, function (mutation) {
            if (/html5-video-player/.test(mutation.target.className) && !yts_event_em_speed) {
                ytsRemoveDefaultSpeed();
            }
        });
        yts_event_player = true;
    }
}


/************************************
 *                DOM                *
 ************************************/
$yts = {
    'event': function (obj, event, callback) {
        obj.addEventListener(event, callback);
    },
    'new': function (tag, option) {
        var element = document.createElement(tag);
        for (var param in option) {
            if (param === 'data' || param === 'style' || param === 'attr') {
                for (var data in option[param]) {
                    $yts[param](element, data, option[param][data]);
                }
            }
            else {
                element[param] = option[param];
            }
        }
        return element;
    },
    'get': function (tselector, all) {
        all = all || false;
        var type = tselector.substring(0, 1);
        var selector = tselector.substring(1);
        var elements;
        if (type === "#") {
            return document.getElementById(selector);
        }
        else if (type === ".") {
            elements = document.getElementsByClassName(selector);
        }
        else {
            elements = document.querySelectorAll(tselector);
        }

        if (all) {
            return elements;
        }
        else {
            return elements.length ? elements[0] : null;
        }
    },
    'data': function (elem, key, val) {
        key = key.replace(/-(\w)/gi, function (x) {
            return x.charAt(1).toUpperCase();
        });
        if (typeof val !== 'undefined') {
            elem.dataset[key] = val;
        }
        return elem.dataset[key];

    },
    'style': function (elem, key, val, priority) {
        priority = priority || '';
        if (typeof val !== 'undefined') {
            elem.style.setProperty(key, val, priority);
        }
        return elem.style.getPropertyValue(key);

    },
    'attr': function (elem, key, val) {
        if (typeof val !== 'undefined') {
            elem.setAttribute(key, val);
        }
        return elem.getAttribute(key);

    },
    'getOpt': function (selector, option) {
        var el = $yts.get(selector, true);
        var pass = [];
        var correct;
        for (var i = 0; i < el.length; i++) {
            correct = true;
            for (var prop in option) {
                if (!$yts.has(el[i], prop, option[prop])) {
                    correct = false;
                    break;
                }
            }
            if (correct) {
                pass.push(el[i]);
            }
        }
        return pass;
    },
    'has': function (elem, key, val) {
        if (elem.hasAttribute(key)) {
            var attr = elem.getAttribute(key);
            if (val !== null) {
                return attr == val;
            }
            return true;
        }
        return false;
    }
};

/*************************************
 *          OBSERVER                 *
 ************************************/
function ytsObserver(element, callback) {
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    if (MutationObserver) {
        var obs = new MutationObserver(function (mutations) {
            callback(mutations[0]);
        });

        obs.observe(element, {
            childList: true,
            subtree: true,
            attributes: true,
            characterData: true,
            attributeOldValue: true,
            characterDataOldValue: true
        });
    }
}

function ytsParam(key, val) {
    if (typeof val !== 'undefined') {
        localStorage.setItem(key, val);
    }
    return parseFloat(localStorage.getItem(key));
}

ytsInit();