// ==UserScript== // @name Youtube Player Speed Slider // @namespace youtube_player_speed_slider // @version 0.2.0 // @description Add Speed Slider to Youtube Player Settings // @author Łukasz // @match https://*.youtube.com/*watch* // @match https://*.youtube.com/embed* // @grant none // @downloadURL none // ==/UserScript== var yts = {}; yts.option = { timeoutBuild: 500, timeoutRemove: 1000, lastClick: 0 }; yts.elements = { menu : null, speedMenu: null, slider: null, sliderLabel: null, annot: null }; yts.event = { speedRemove: false, playerObserve: false, addEnd: true }; yts.modules = { 'before':[ 'click', 'setting', 'i18n', 'ad' ], 'after':[ 'slider', 'menu', 'annot', 'player', 'appmenu' ], 'spfdone':[ 'appmenu', 'player', 'annot' ], 'afterAd':[ '' ] }; /************************************* * TRANSLATION * ************************************/ yts.i18n = {}; yts.i18n.dic = { pl: { 'Speed': 'Szybkość', 'Settings': 'Ustawienia', 'Language': 'Język', 'Switch off advertisement': 'Wyłączaj reklamy', 'Hide annotation': 'Ukryj adnotacje', 'Remember speed': 'Pamiętaj szybkość' }, de: { 'Speed' : 'Geschwindigkeit', 'Settings': 'Einstellungen', 'Language': 'Sprache', 'Switch off advertisement': 'Abschalten Werbung', 'Hide annotation': 'Ausblenden Anmerkungen', 'Remember speed': 'Erinnern Geschwindigkeit' }, fr:{ 'Speed':'Vitesse', 'Settings':'Paramètres', 'Language': 'La langue', 'Switch off advertisement': 'Désactiver la publicité', 'Hide annotation': 'Cacher les annotations', 'Remember speed': 'Rappelez-vous la vitesse' }, en:{} }; yts.i18n.opt = { lang: 'en', default: 'en', map:{ pl: 'pl', pl_pl: 'pl', en: 'en', en_gb: 'en', de: 'de', fr: 'fr' } }; yts.i18n.init = function () { yts.i18n.opt.lang = yts.i18n.getLang(); yts.setting.change('lang', function (val) { yts.i18n.opt.lang = yts.i18n.getLang(); }); }; yts.i18n.getLang = function () { var lang = yts.i18n.filter(yts.setting.get('lang')); if(lang !== '' && yts.i18n.isAllow(lang)){ return yts.i18n.map(lang); } lang = yts.i18n.filter(yts.$.get('html').getAttribute('lang')); if(lang !== '' && yts.i18n.isAllow(lang)){ return yts.i18n.map(lang); } return yts.i18n.opt.default; }; yts.i18n.isAllow = function (lang) { return yts.i18n.opt.map.hasOwnProperty(lang); }; yts.i18n.t = function (pattern) { if(yts.i18n.dic.hasOwnProperty(yts.i18n.opt.lang) && yts.i18n.dic[yts.i18n.opt.lang].hasOwnProperty(pattern)){ return yts.i18n.dic[yts.i18n.opt.lang][pattern]; } else { return pattern; } }; yts.i18n.filter = function (lang) { return lang ? lang.replace('-', '_').toLowerCase() : ''; }; yts.i18n.map = function (lang) { return yts.i18n.opt.map[lang] }; yts.i18n.all = function () { var ret=[]; for(var p in yts.i18n.dic) if(yts.i18n.dic.hasOwnProperty(p)) ret.push(p); return ret; }; /************************************* * INIT * ************************************/ yts.init = function () { yts.modules.before.forEach(function (module) { yts[module].init(); }); yts.$.event(document, "spfdone", function () { yts.modules.spfdone.forEach(function (module) { yts[module].init(); }); }); yts.option.lastClick = (new Date()).getTime(); yts.menu.reopen(); yts.buildApp(); }; yts.buildApp = function () { yts.elements.menu = yts.$.get('.ytp-panel-menu'); if (yts.elements.menu !== null) { setTimeout(yts.menu.removeDefaultSpeed, yts.option.timeoutRemove); yts.modules.after.forEach(function (module) { yts[module].init(); }); } else { setTimeout(yts.buildApp, yts.option.timeoutBuild); } }; /************************************* * MENU * ************************************/ yts.menu = {}; yts.menu.init = function () { var speedMenu = yts.$.new('div', {'className': 'ytp-menuitem', id:'yts-menu'}); var right = yts.$.new('div', {'className': 'ytp-menuitem-content'}); speedMenu.appendChild(yts.elements.sliderLabel); speedMenu.appendChild(right.appendChild(yts.elements.slider)); yts.elements.menu.appendChild(speedMenu); yts.menu.event(); }; yts.menu.event = function () { yts.elements.menu.addEventListener('click', yts.menu.click); }; yts.menu.removeDefaultSpeed = function(){ var switchers = yts.$.getOpt(".ytp-menuitem", {role:'menuitemcheckbox'}); var toremove = null; if(!yts.player.hasClass('ad-interrupting') && switchers && switchers.length && !yts.event.speedRemove ){ toremove = switchers[switchers.length-1].nextElementSibling; if(toremove && toremove.id !== 'yts-menu'){ yts.$.style(toremove, 'display', 'none'); yts.event.speedRemove = true; } } }; yts.menu.reopen = function () { var settings_button = yts.$.get(".ytp-settings-button"); settings_button && settings_button.click(); settings_button && settings_button.click(); }; yts.menu.click = function () { yts.option.lastClick = (new Date()).getTime(); }; /************************************* * SLIDER * ************************************/ yts.slider = {}; yts.slider.init = function () { var speed = yts.setting.get('speed') || 1; speed = yts.setting.get('rem') ? speed : 1; yts.elements.sliderLabel = yts.$.new('div', {'className': 'ytp-menuitem-label'}); yts.elements.slider = yts.$.new('input', { 'className': '', 'type': 'range', 'min': 0.5, 'max': 4, 'step': 0.1, 'value': speed, style:{ 'width': '90%', 'margin':'0 5%' } }); yts.$.event(yts.elements.slider, 'change', yts.slider.change); yts.$.event(yts.elements.slider, 'input', yts.slider.move); yts.slider.updateLabel(speed); }; yts.slider.move = function (event) { yts.slider.updateLabel(event.target.value); }; yts.slider.change = function (event) { yts.player.duration(event.target.value); }; yts.slider.updateLabel = function (val) { yts.elements.sliderLabel.innerHTML = yts.i18n.t('Speed') + ': ' + parseFloat(val).toFixed(1); }; /************************************* * PLAYER * ************************************/ yts.player = {}; yts.player.init = function(){ yts.elements.player = yts.$.get('.html5-main-video'); yts.player.observe(); if(yts.setting.get('speed') && yts.setting.get('rem')){ yts.player.duration(yts.setting.get('speed')); yts.slider.updateLabel(yts.setting.get('speed')); } }; yts.player.hasClass = function (cls) { yts.player.init(); return yts.elements.player && yts.elements.player.classList.contains(cls) }; yts.player.duration = function(value){ yts.setting.set('speed', value); yts.elements.player.playbackRate = value; }; yts.player.observe = function () { if(!yts.event.playerObserve){ yts.observer.init(yts.elements.player.parentNode.parentNode, function (mutation) { if(/html5-video-player/.test(mutation.target.className) && !yts.event.speedRemove) { yts.menu.removeDefaultSpeed(); } }); yts.event.playerObserve = true; } }; /************************************* * ANNOTATION * ************************************/ yts.annot = {}; yts.annot.init = function(){ yts.elements.annot = yts.$.get('.video-annotations'); if(yts.setting.get('annot')){ yts.annot.switch("off"); } yts.setting.change('annot', function (val) { if(val){ yts.annot.switch("off"); } else{ yts.annot.switch("on"); } }); }; yts.annot.change = function (mutation) { if(mutation.type == "attributes" && mutation.target.getAttribute('role')=="menuitemcheckbox"){ yts.annot.switch("off"); } }; yts.annot.switch = function(type){ if(yts.elements.annot && type == 'off'){ yts.$.style(yts.elements.annot, 'display', 'none', 'important'); } else if(yts.elements.annot && type == 'on'){ yts.$.style(yts.elements.annot, 'display', 'block'); } }; /************************************* * COOKIE * ************************************/ yts.cookie ={}; yts.cookie.set = function (name, value, option) { var d = new Date(); option = option || {}; option.expires = option.expires || 366; d.setTime(d.getTime() + (option.expires*24*60*60*1000)); option.expires = d.toUTCString(); option.path = option.path || '/'; var cookie = name + "=" + value + "; "; for(var prop in option){ cookie += prop + "=" + option[prop] +"; "; } document.cookie = cookie; }; yts.cookie.get = function (name) { name += "="; var ca = document.cookie.split(';'); for(var i = 0; i Yts', 'data': { 'tooltip-text':yts.i18n.t('Settings'), 'button-toggle':'true', 'trigger-for':'action-panel-yts' } }); return button; }; yts.appmenu.panel = function(){ var panel = yts.$.new('div', { 'id':'action-panel-yts', 'className':'action-panel-content', data: {'panel-loaded':'true'} }); var list = yts.$.new('ul', {'className':'yt-uix-kbd-nav yt-uix-kbd-nav-list'}); yts.appmenu.addSettingItems(list); panel.appendChild(list); var panels = yts.$.get('#watch-action-panels'); if(panels){ panels.appendChild(panel); } }; yts.appmenu.addSettingItems = function (list) { for(var item in yts.setting.item){ var li = yts.$.new('li', { 'className':'ytp-menuitem', 'role':"menuitemcheckbox", attr: { 'aria-checked': yts.setting.get(item) ? "true" : "false", } }); var type = yts.setting.item[item].type; if(type === 'switcher'){ li = yts.appmenu.switcherItem(li, item); } if(type === 'radio'){ li = yts.appmenu.radioItem(li, item); } list.appendChild(li); } }; yts.appmenu.switcherItem = function(li, item){ var switcher = yts.$.new('div', { 'className':'ytp-menuitem-toggle-checkbox', style: { 'background-color':'#d2d2d2', 'margin-left': '10px' }, data: { 'name': item }, 'onclick' : function (event) { var el = event.target; var parent = el.parentNode; var stat = parent.getAttribute('aria-checked'); if(stat == 'false'){ yts.$.attr(parent, 'aria-checked' , 'true'); yts.setting.set(yts.$.data(el, 'name'), true); } else{ yts.$.attr(parent, 'aria-checked' , 'false'); yts.setting.set(yts.$.data(el, 'name'), false); } } }); var span = yts.$.new('span', {'innerHTML':yts.i18n.t(yts.setting.item[item].label)}); li.appendChild(switcher); li.appendChild(span); return li; }; yts.appmenu.radioItem = function(li, item){ var list = yts.setting.item[item].items; var checked = yts.setting.get(item); li.appendChild(yts.$.new('div',{'innerHTML':yts.i18n.t(yts.setting.item[item].label), style:{'margin-bottom':'5px'}})); for(var i =0; i< list.length; i++){ var label = yts.$.new('label',{style:{'margin-right':'5px'}}); var spanInput = yts.$.new('span', { 'className': 'yt-uix-form-input-radio-container' }); var input = yts.$.new('input', { 'type': 'radio', 'name': 'yts-' + item, 'checked': list[i] == checked, data: { 'name': item, 'val': list[i] }, 'onchange': function (event) { var el = event.target; yts.setting.set(yts.$.data(el, 'name'), yts.$.data(el, 'val')); } }); var spanCheck = yts.$.new('span', {'className': 'yt-uix-form-input-radio-element'}); var spanLabel = yts.$.new('span', {'innerHTML': list[i]}); spanInput.appendChild(input); spanInput.appendChild(spanCheck); label.appendChild(spanInput); label.appendChild(spanLabel); li.appendChild(label); } return li; }; yts.init();