// ==UserScript== // @name 倍速播放 // @namespace https://greasyfork.org/zh-CN/users/104201-%E9%BB%84%E7%9B%90 // @version 0.2 // @description HTML5播放器,倍速功能。可以实现最高10倍数播放。不同网站可以设置不同速率和位置 // @author 黄盐 // @include *:* // @noframes // @run-at document-end // @grant GM_getResourceText // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @require https://cdn.bootcss.com/vue/2.6.6/vue.js // @require https://cdn.bootcss.com/zepto/1.2.0/zepto.min.js // require file:///D:\soft\github\greasyforks\speedyPlay/speedy.jqMobile.js // @downloadURL none // ==/UserScript== /* jshint esversion: 6 */ /*!Tdrag 0.0.1 这里经过我修改,要恢复为jQuery插件,可以全部替换[Zepto]为[jQuery]即可*/;(function($,window,document,undefined){Zepto(function(){$.fn.Tdrag=function(opt){var call={scope:null,grid:null,axis:"all",pos:false,handle:null,moveClass:"tezml",dragChange:false,changeMode:"point",cbStart:function(){},cbMove:function(){},cbEnd:function(){},random:false,randomInput:null,animation_options:{duration:800,easing:"ease-out"},disable:false,disableInput:null};var dragfn=new Dragfn(this,opt);if(opt&&$.isEmptyObject(opt)==false){dragfn.options=$.extend(call,opt);}else{dragfn.options=call;} dragfn.firstRandom=true;var ele=dragfn.$element;dragfn.pack(ele,false);if(dragfn.options.randomInput!=null){$(dragfn.options.randomInput).bind("click",function(){dragfn.pack(ele,true);})} dragfn.loadJqueryfn()};var Dragfn=function(ele,opt){this.$element=ele;this.options=opt;};Dragfn.prototype={init:function(obj){var self=this;self.ele=self.$element;self.handle=$(obj);self.options=self.options;self.disable=self.options.disable;self._start=false;self._move=false;self._end=false;self.disX=0;self.disY=0;self.zIndex=1000;self.moving=false;self.moves="";self.box=$.type(self.options.scope)==="string"?self.options.scope:null;if(self.options.handle!=null){self.handle=$(obj).find(self.options.handle);} self.handle.on("mousedown",function(ev){self.start(ev,obj);obj.setCapture&&obj.setCapture();return false;});if(self.options.dragChange){$(obj).on("mousemove",function(ev){self.move(ev,obj);});$(obj).on("mouseup",function(ev){self.end(ev,obj);});}else{$(document).on("mousemove",function(ev){self.move(ev,obj);});$(document).on("mouseup",function(ev){self.end(ev,obj);});}},loadJqueryfn:function(){var self=this;$.extend({sortBox:function(obj){var arr=[];for(var s=0;s<$(obj).length;s++){arr.push($(obj).eq(s));} for(var i=0;iNumber(arr[j].attr("index"))){var temp=arr[i];arr[i]=arr[j];arr[j]=temp;}}} return arr},randomfn:function(obj){self.pack($(obj),true);},disable_open:function(){self.disable=false;},disable_cloose:function(){self.disable=true;}});},toDisable:function(){var self=this;if(self.options.disableInput!=null){$(self.options.disableInput).bind("click",function(){if(self.disable==true){self.disable=false}else{self.disable=true}})}},start:function(ev,obj){var self=this;self.moved=obj;if(self.disable==true){return false} self._start=true;var oEvent=ev||event;self.disX=oEvent.clientX-obj.offsetLeft;self.disY=oEvent.clientY-obj.offsetTop;$(obj).css("zIndex",self.zIndex++);self.options.cbStart();},move:function(ev,obj){var self=this;if(self._start!=true){return false} if(obj!=self.moved){return false} self._move=true;var oEvent=ev||event;var l=oEvent.clientX-self.disX;var t=oEvent.clientY-self.disY;if(self.box!=null){var rule=self.collTestBox(obj,self.box);if(l>rule.lmax){l=rule.lmax;}else if(lrule.tmax){t=rule.tmax;}else if(tb1||b2Number($(arr_li[j]).attr("index"))){var temp=arr_li[i];arr_li[i]=arr_li[j];arr_li[j]=temp;}}} return arr_li;},pointDrag:function(obj){var self=this;var oNear=self.findNearest(obj);if(oNear){self.animation(obj,self.aPos[$(oNear).attr("index")]);self.animation(oNear,self.aPos[$(obj).attr("index")]);var tmp;tmp=$(obj).attr("index");$(obj).attr("index",$(oNear).attr("index"));$(oNear).attr("index",tmp);$(oNear).removeClass(self.options.moveClass);}else if(self.options.changeWhen=="end"){self.animation(obj,self.aPos[$(obj).attr("index")]);}},sortDrag:function(obj){var self=this;var arr_li=self.sort();var oNear=self.findNearest(obj);if(oNear){if(Number($(oNear).attr("index"))>Number($(obj).attr("index"))){var obj_tmp=Number($(obj).attr("index"));$(obj).attr("index",Number($(oNear).attr("index"))+1);for(var i=obj_tmp;iNumber($(oNear).attr("index"))){var obj_tmp=Number($(obj).attr("index"));$(obj).attr("index",$(oNear).attr("index"));for(var i=Number($(oNear).attr("index"));i
{{speed}}
`; // 获取当前站点取得名称 O.getSiteName = function(){ return location.host.replace(/\./g, ''); }; O.getSiteSpeed = function(){ let speedCollete = GM_getValue('speedCollete', {}); let siteName = O.getSiteName(); return speedCollete.hasOwnProperty(siteName) ? speedCollete[siteName] : 1.0; }; O.getSitePosition = function(){ let positionCollect = GM_getValue('positionCollect', {}); let siteName = O.getSiteName(); return positionCollect.hasOwnProperty(siteName) ? positionCollect[siteName] : {left: 50, top: 100}; }; // 根据不同的网站,设置不同的速度。存储键名用 location.host 字母来拼接 // 还有设置不同的位置 // 按照不同频道读取各自的播放速度,未完成 O.saveSpeed = function(speed){ if(typeof speed != 'number'){ O.log(`速度值设置错误,应该提供数值类型,${speed} 是 ${typeof speed} 类型!`, 'error'); return false; } let speedCollete = GM_getValue('speedCollete', {}); let siteName = O.getSiteName(); speedCollete[siteName] = speed; GM_setValue('speedCollete', speedCollete); return true; }; O.savePosition = function(left, top){ if(typeof left != 'number' || typeof top != 'number'){ O.log(`位置值设置错误,应该提供数值类型,现在 left:${typeof left},top: ${typeof top}`, 'error'); return false; } let positionCollect = GM_getValue('positionCollect', {}); let siteName = O.getSiteName(); positionCollect[siteName] = { left: left, top: top }; GM_setValue('positionCollect', positionCollect); O.log(left+'--'+top); return true; }; O.log = function(message,msgType="normal"){ let style = { hint: `background:#ff0; border-left: 5px solid #333; padding:1px 3px; font-weight:bold;`, normal: `background:lightgreen;padding:1px 5px 1px 2px; border-right: 3px solid darkblue;`, warning: `background:#FFFBE5;padding:1px 5px 1px 2px; border-right: 3px solid darkblue;`, error: `background:red;padding:1px 5px 1px 2px; border-right: 3px solid darkblue;` }; console.log("%c SpeedyPlay Info: %c"+message, style.hint, style[msgType]); }; // 如果网页环境满足调速情况,才构造调速模块 // window.sessionStorage.setItem("playbackRate", Math.min(videoRate, 4.0)); O.showUp = function(){ GM_addStyle(O.CSS); $('body').first().before(O.speedDivOuterHTML); setTimeout(()=>{ $('#speedDiv').Tdrag({ handle:".speedText", // 拖拽结束后调用 cbEnd: ()=>{ O.savePosition(parseInt($('#speedDiv').css('left')), parseInt($('#speedDiv').css('top'))); } }); }, 600); let speedDiv = $('#speedDiv'); let speedDivPosition = O.getSitePosition(); speedDiv.css({ 'left': speedDivPosition.left+'px', 'top': speedDivPosition.top+'px' }); O.speedVue = new Vue({ el: "#speedDiv", data: { speed: O.getSiteSpeed(), }, methods: { slowDown: function(){ let speed = Number((this.speed - 0.1).toFixed(1)); this.speed = speed >= 0.1 ? speed : 0.1; this.changeSpeed(); }, speedUp: function(){ let speed = Number((this.speed - (-0.1)).toFixed(1)); this.speed = speed <= 10 ? speed :10; this.changeSpeed(); }, resetSpeed: function(){ this.speed = 1; this.changeSpeed(); }, changeSpeed: function(){ // GM_setValue("speed", this.speed); O.saveSpeed(this.speed); // 这里默认只对第一个 video 元素进行调速。多 video 的页面暂时不考虑 try { let videos = document.querySelectorAll("video"); if(location.host == "v.qq.com"){ window.sessionStorage.setItem("playbackRate", Math.min(this.speed, 4.0)); }else{ videos.forEach(vdo=>{vdo.playbackRate = this.speed;}); } if(videos[0] && videos[0].playbackRate){ O.log(this.speed+"倍速", "normal"); } } catch(e) { O.log("没有找到HTML5播放器!!!", "warning"); } }, changeRange: function(){ // GM_setValue("speed", this.speed); O.saveSpeed(this.speed); this.changeSpeed(); }, backgroundSize: function(){ return `background-size:${this.speed*10}% 100%`; }, } }); }; O.setPlaybackRate = function(){ // 腾讯视频,最高4.0 倍速度,需要通过设置 sessionStorage 值来改变。目前没有找到直接操作的方法 let videos = document.querySelectorAll("video"); // 按照不同频道读取各自的播放速度,未完成 let videoRate = O.getSiteSpeed(); if(location.host == "v.qq.com"){ window.sessionStorage.setItem("playbackRate", Math.min(videoRate, 4.0)); }else{ videos.forEach((vdo)=>{vdo.playbackRate = videoRate;}); } O.log(videos[0].playbackRate); }; // 检测页面是否有 video 元素,如果有,并且可以设置 video.playbackRate 属性,那么就出现速度框,否则就不显示。 // 循环检测 30s ,如果都没有找到 video 元素,那就不再检测了 O.isReady = function(){ let checkVideoCounter = 0; window.checkVideoTimer = setInterval(()=>{ let videos = document.querySelectorAll("video"); if(videos.length){ clearInterval(checkVideoTimer); O.showUp(); O.setPlaybackRate(); }else if(checkVideoCounter>30){ clearInterval(checkVideoTimer); }else{ O.log('waiting...'); checkVideoCounter ++; } },1000); }; // === END === return O; })(); SpeedyPlay.isReady(); })();