您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add Speed Slider to Youtube Player Settings
当前为
- // ==UserScript==
- // @name Youtube Player Speed Slider
- // @namespace youtube_player_speed_slider
- // @version 0.2.2
- // @description Add Speed Slider to Youtube Player Settings
- // @author Łukasz
- // @match https://*.youtube.com/*
- // @grant none
- // ==/UserScript==
- var yts = {};
- // yts.env = "dev";
- yts.env = "prod";
- 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'
- ]
- };
- /*************************************
- * 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.log('Init App');
- yts.modules.before.forEach(function (module) {
- yts.run(module);
- });
- yts.$.event(document, "spfdone", function () {
- yts.modules.spfdone.forEach(function (module) {
- yts.run(module);
- });
- });
- yts.option.lastClick = (new Date()).getTime();
- yts.menu.reopen();
- yts.buildApp(0);
- };
- yts.run = function (module) {
- try{
- yts.log('Init ' + module + ' module');
- yts[module].init();
- }
- catch (e){
- yts.log('Error in ' + module + ' module');
- }
- };
- yts.buildApp = function (num) {
- num++;
- yts.elements.menu = yts.$.get('.ytp-panel-menu');
- if (yts.elements.menu !== null) {
- yts.log('Init Build Menu after ' + num + ' attempt(s)');
- setTimeout(yts.menu.removeDefaultSpeed, yts.option.timeoutRemove);
- yts.modules.after.forEach(function (module) {
- yts.run(module);
- });
- }
- else {
- yts.log('Settimeout buildApp attempt: ' + num);
- setTimeout(yts.buildApp, yts.option.timeoutBuild, num);
- }
- };
- /*************************************
- * 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.log('Remove default speed menu item');
- 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%',
- 'padding': '6px 0'
- }
- });
- yts.$.event(yts.elements.slider, 'change', yts.slider.change);
- yts.$.event(yts.elements.slider, 'input', yts.slider.move);
- yts.$.event(yts.elements.slider, 'wheel', yts.slider.wheel);
- yts.slider.updateLabel(speed);
- };
- yts.slider.move = function (event) {
- yts.slider.updateLabel(event.target.value);
- };
- yts.slider.wheel = function (event) {
- var val = parseFloat(event.target.value) + (event.wheelDelta > 0 ? 0.1 : -0.1);
- val = val < 0.5 ? 0.5 : (val > 4 ? 4 : val);
- if(event.target.value != val){
- event.target.value = val;
- yts.player.duration(val);
- yts.slider.updateLabel(val);
- }
- event.preventDefault();
- };
- 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 <ca.length; i++) {
- var c = ca[i];
- while (c.charAt(0)==' ') {
- c = c.substring(1);
- }
- if (c.indexOf(name) == 0) {
- return c.substring(name.length,c.length);
- }
- }
- return null;
- };
- /************************************
- * DOM *
- ************************************/
- yts.$ = {};
- yts.$.event = function (obj, event, callback) {
- obj.addEventListener(event, callback);
- };
- yts.$.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;
- };
- yts.$.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;
- }
- };
- yts.$.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];
- };
- yts.$.style = function (elem, key, val, priority) {
- priority = priority || '';
- if(typeof val !== 'undefined'){
- elem.style.setProperty(key, val, priority);
- }
- return elem.style.getPropertyValue(key);
- };
- yts.$.attr = function (elem, key, val) {
- if(typeof val !== 'undefined'){
- elem.setAttribute(key, val);
- }
- return elem.getAttribute(key);
- };
- yts.$.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;
- };
- yts.$.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 *
- ************************************/
- yts.observer= {};
- yts.observer.obj = null;
- yts.observer.get = function () {
- return window.MutationObserver || window.WebKitMutationObserver;
- };
- yts.observer.init = function (element, callback) {
- var MutationObserver = yts.observer.get();
- if( MutationObserver ){
- var obs = new MutationObserver(function(mutations, observer){
- callback(mutations[0]);
- });
- obs.observe( element, {
- childList: true,
- subtree: true,
- attributes:true,
- characterData:true,
- attributeOldValue:true,
- characterDataOldValue:true
- });
- }
- };
- /*************************************
- * SETTINGS *
- ************************************/
- yts.setting = {};
- yts.setting.item = {
- rem:{
- label: 'Remember speed',
- type: 'switcher'
- },
- annot:{
- label: 'Hide annotation',
- type: 'switcher'
- },
- ad:{
- label: 'Switch off advertisement',
- type: 'switcher'
- },
- lang:{
- label: 'Language',
- type: 'radio',
- items: yts.i18n.all()
- }
- };
- yts.setting.settings = null;
- yts.setting.event = {};
- yts.setting.init = function(){
- var setting = localStorage.getItem('yts');
- yts.setting.settings = JSON.parse(setting === null ? '{}' : setting);
- };
- yts.setting.set = function(key, val){
- yts.setting.settings[key] = val;
- if(yts.setting.event.hasOwnProperty(key)){
- yts.setting.event[key](val);
- }
- localStorage.setItem('yts', JSON.stringify(yts.setting.settings));
- };
- yts.setting.get = function(kay){
- return yts.setting.settings.hasOwnProperty(kay) ?
- yts.setting.settings[kay] : null;
- };
- yts.setting.change = function(key, callback){
- yts.setting.event[key] = callback;
- };
- /*************************************
- * CLICK *
- ************************************/
- yts.click = {};
- yts.click.fun = [];
- yts.click.init = function(){
- yts.$.event(yts.$.get('body'), 'click', function(event){
- for(var i =0; i<yts.click.fun.length; i++){
- yts.click.fun[i](event);
- }
- })
- };
- yts.click.add = function(fun){
- yts.click.fun.push(fun);
- };
- /*************************************
- * LOGGER *
- ************************************/
- yts.log = function (text, type) {
- if(yts.env == 'prod') return 1;
- type = type || 'l';
- var app = "[YT SPEED SLIDER] ";
- if(type == "l") console.log(app, text);
- else if(type == "e") console.error(app, text);
- else if(type == "i") console.info(app, text);
- else if(type == "w") console.warn(app, text);
- };
- /*************************************
- * ADVERTISEMENT *
- ************************************/
- yts.ad = {};
- yts.ad.init = function(){
- if(yts.setting.get('ad')){
- yts.ad.switch('off');
- }
- yts.setting.change('ad', function (val) {
- if(val){
- yts.ad.switch('off');
- }
- else{
- yts.ad.switch('on');
- }
- });
- };
- yts.ad.switch = function (type) {
- var ad = yts.$.get(".video-ads");
- if(type == "off"){
- yts.cookie.set("VISITOR_INFO1_LIVE", "oKckVSqvaGw", {domain: "youtube.com"});
- ad && yts.$.style(ad, 'display', 'none', 'important');
- }
- else {
- yts.cookie.set("VISITOR_INFO1_LIVE", "", {domain: "youtube.com", expires: -1});
- ad && yts.$.style(ad, 'display', 'block');
- }
- };
- /*************************************
- * APP MENU *
- ************************************/
- yts.appmenu = {};
- yts.appmenu.init = function(){
- yts.appmenu.build();
- };
- yts.appmenu.build = function(){
- yts.appmenu.panel();
- var menu = yts.$.get("#watch8-secondary-actions");
- if(menu){
- menu.appendChild(yts.appmenu.button());
- }
- };
- yts.appmenu.button = function(){
- var button = yts.$.new('button', {
- 'className':'yt-uix-button yt-uix-button-opacity action-panel-trigger yt-uix-button-toggled',
- 'title' : yts.i18n.t('Settings'),
- 'type':'button',
- 'innerHTML':'<span class="yt-uix-button-content">Yts</span>',
- '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'},
- style: {'display':'none'}
- });
- 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();