您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
try to take over the world!
当前为
- // ==UserScript==
- // @name video speed
- // @namespace http://tampermonkey.net/
- // @version 20240111.7
- // @description try to take over the world!
- // @author You
- // @match *://*/*
- // @grant none
- // ==/UserScript==
- class KeepVideoTime {
- constructor(props) {
- this.key = "_video_cache_";
- this.obj = this.get() || {};
- this.id = null;
- }
- set(key, time) {
- this.obj[key] = time;
- this.store();
- }
- store() {
- localStorage.setItem(this.key, JSON.stringify(this.obj));
- }
- get() {
- let data = localStorage.getItem(this.key);
- if (!data) {
- return null;
- }
- try {
- data = JSON.parse(data)
- } catch (e) {
- return null;
- } finally {
- return data;
- }
- }
- keep(getUrlId, video) {
- if (!this.id) {
- this.id = setInterval(() => {
- let vid = getUrlId();
- if (vid) {
- this.set(vid, video.currentTime);
- }
- }, 500);
- }
- }
- stop() {
- if (this.id) {
- clearInterval(this.id);
- this.id = null;
- }
- }
- }
- let KeepVideoTimeInstance = null;
- let onceMap = {};
- let clickDomOnce = function (selector, key) {
- if (key in onceMap) {
- return;
- }
- onceMap[key] = true;
- let id = setInterval(() => {
- let d = selector();
- if (d.length) {
- d[0].click();
- clearInterval(id);
- delete onceMap[key];
- }
- }, 500);
- }
- let DefaultAction = {
- "bilibili.com"(video) {
- return [
- {
- group: "屏幕操作",
- name: "宽屏",
- run: false,
- action() {
- clickDomOnce(() => document.getElementsByClassName('bpx-player-ctrl-web-enter'), "bpx-player-ctrl-web-enter");
- }
- },
- {
- group: "屏幕操作",
- name: "全屏",
- run: false,
- action() {
- clickDomOnce(() => document.getElementsByClassName('bpx-player-ctrl-full'), "bpx-player-ctrl-full");
- }
- },
- {
- group: "屏幕操作",
- name: "undo",
- run: true,
- action() {
- console.log("undo");
- }
- }
- ]
- },
- 'youtube.com'(videoInstance) {
- return [
- {
- group: "屏幕操作",
- name: "宽屏",
- run: false,
- action() {
- let video = videoInstance.getVideo();
- if (video.width < window.innerWidth * .8) {
- clickDomOnce(() => document.getElementsByClassName('ytp-size-button'), "ytp-size-button");
- }
- }
- },
- {
- group: "屏幕操作",
- name: "全屏",
- run: false,
- action() {
- clickDomOnce(() => document.getElementsByClassName('ytp-fullscreen-button'), "ytp-fullscreen-button");
- }
- },
- {
- group: "屏幕操作",
- name: "undo",
- run: true,
- action() {
- console.log("undo");
- }
- },
- {
- name: "记住最后播放时间",
- action(run, cache) {
- let video = videoInstance.getVideo();
- let getVid = () => {
- let vid = location.search.split('?')[1].split('&').map(_=>_.split('=')).filter(_=>_[0]==='v');
- if (vid.length === 1) {
- vid = vid[0][1];
- }
- return vid;
- }
- if (!KeepVideoTimeInstance) {
- KeepVideoTimeInstance = new KeepVideoTime();
- let vid = getVid();
- if (vid && vid in KeepVideoTimeInstance.obj) {
- if (video.currentTime < KeepVideoTimeInstance.obj[vid]) {
- video.currentTime = KeepVideoTimeInstance.obj[vid];
- }
- }
- }
- if (run) {
- KeepVideoTimeInstance.keep(getVid, video);
- } else {
- KeepVideoTimeInstance.stop();
- }
- },
- run: true,
- replay: true,
- }
- ]
- }
- };
- function checkWindow() {
- return window === parent.window;
- };
- class DraggableDom {
- static Style = {
- // width: 100px;
- // height: 100px;
- backgroundColor: "lightblue",
- border: "2px solid #4CAF50",
- margin: "10px",
- padding: "10px",
- cursor: "move",
- position: "absolute",
- zIndex: 100000,
- borderRadius: "5px",
- }
- constructor(cache = new Cache()) {
- this.cache = cache;
- this.toggleKey = this.cache.obj.toggleKey;
- this.show = false;
- this.id = `DD_${new Date().getTime()}`;
- this.dom = null;
- this.mark = null;
- if (this.cache.obj.top > window.innerHeight) {
- this.cache.set("height", 0);
- }
- if (this.cache.obj.left > window.innerWidth) {
- this.cache.set("width", 0);
- }
- this.style = {
- ...DraggableDom.Style,
- width: `${cache.obj.width}px`,
- height: `${cache.obj.height}px`,
- top: `${cache.obj.top}px`,
- left: `${cache.obj.left}px`,
- opacity: cache.obj.opacity ? cache.obj.opacity : 1,
- };
- this.createDom();
- this.dom.id = this.id;
- this.bindEvent();
- }
- getDom() {
- if (this.dom) {
- let dom = this.dom.getElementsByClassName('dom');
- if (dom.length) {
- return dom[0];
- }
- }
- return null;
- }
- createDom() {
- // <button accessKey="T" onClick="clickSubmit()">提交按钮</button>
- let btn = document.createElement('button');
- btn.style.display = 'none';
- btn.setAttribute("accessKey", this.toggleKey);
- btn.onclick = () => {
- if (this.dom) {
- this.dom.style.display = this.show ? 'unset' : 'none';
- this.show = !this.show;
- }
- };
- this.mark = document.createElement('div');
- this.mark.style.width = "100vw";
- this.mark.style.height = "100vh";
- this.mark.style.position = "fixed";
- this.mark.style.left = "0";
- this.mark.style.top = "0";
- this.mark.style.display = "none";
- this.mark.style.zIndex = this.style.zIndex - 1;
- this.dom = document.createElement('div');
- this.dom .setAttribute('draggable', "true");
- this.dom.innerHTML = `<div>按下 <ALT+${this.toggleKey}> 可以[隐藏/显示]当前选项框</div><div class="dom"></div>`
- for (let i in this.style) {
- this.dom.style[i] = this.style[i];
- }
- document.body.append(this.dom);
- document.body.append(this.mark);
- document.body.append(btn);
- }
- bindEvent() {
- // JavaScript 代码,实现拖拽功能
- let draggableElement = this.dom;
- let $this = this;
- draggableElement.ondragstart = function(event) {
- event.dataTransfer.setData('text/plain', event.target.id);
- $this.mark.style.display = 'block';
- }
- draggableElement.ondragend = function(event) {
- $this.mark.style.display = 'none';
- }
- document.body.ondragover = function(event) {
- event.preventDefault();
- };
- document.body.ondrop = function(event) {
- event.preventDefault();
- var data = event.dataTransfer.getData('text/plain');
- if (!data) {
- return;
- }
- var draggedElement = document.getElementById(data);
- // 获取鼠标位置
- var mouseX = event.clientX;
- var mouseY = event.clientY;
- // 设置元素的新位置
- draggedElement.style.left = mouseX - draggedElement.offsetWidth / 2 + 'px';
- draggedElement.style.top = mouseY - draggedElement.offsetHeight / 2 + 'px';
- $this.cache.set("top", parseFloat(draggedElement.style.top));
- $this.cache.set("left", parseFloat(draggedElement.style.left));
- };
- }
- };
- class Cache {
- static Reg = {
- Float: /^[0-9]+.[0-9]+$/,
- Int: /^[0-9]+$/,
- }
- static Default = {
- toggleKey: "X",
- top: 0,
- left: 0,
- width: 'unset',
- height: 'unset',
- speed: 1,
- Action: {},
- opacity: 1,
- };
- constructor(action) {
- let $this = this;
- this.key = "_video_speed_cache_";
- this.objType = {
- toggleKey(r) {
- r = r.toString();
- if (r.length > 1) {
- return r[0];
- } else if (r.length < 1) {
- return "X";
- } else {
- return r;
- }
- },
- opacity(r) {
- r = $this.toNumberFloat(r);
- return r <= 0 ? 0.1 : (r > 1 ? 1 : r);
- },
- top: $this.toNumberFloat,
- left: $this.toNumberFloat,
- // width: 'unset',
- // height: 'unset',
- speed(r) {
- return $this.toNumberFloat(r, 1)
- },
- };
- this.obj = this.get() || {};
- for (let i in Cache.Default) {
- if (!(i in this.obj)) {
- this.obj[i] = Cache.Default[i];
- }
- }
- }
- toNumberFloat(r, dv) {
- r = parseFloat(r.toString());
- return Cache.Reg.Float.test(r.toFixed(1)) ? r : (dv||0);
- }
- toNumberInt(r, dv) {
- r = parseInt(r.toString());
- return Cache.Reg.Int.test(r) ? r : (dv||0);
- }
- set(key, value) {
- if (key in this.objType) {
- this.obj[key] = this.objType[key](value);
- } else {
- this.obj[key] = value;
- }
- this.store();
- return this;
- }
- store() {
- localStorage.setItem(this.key, JSON.stringify(this.obj));
- }
- checkData(data) {
- for (let i in this.objType) {
- data[i] = this.objType[i](data[i]);
- }
- return data;
- }
- get() {
- let data = localStorage.getItem(this.key);
- if (!data) {
- return null;
- }
- try {
- data = JSON.parse(data)
- } catch (e) {
- return null;
- } finally {
- return this.checkData(data);
- }
- }
- clear() {
- setTimeout(() => {
- localStorage.removeItem(this.key);
- }, 1000);
- }
- }
- class PlayDom {
- constructor(video, videoInstance, actions = []) {
- this.video = video;
- this.cache = new Cache();
- this.draggableDom = new DraggableDom(this.cache);
- this.action = {};
- let ret = this.buildAction(actions);
- this.createDom(ret.actionListForDom);
- ret.promise().then(() => {
- let fn = () => {
- let v = videoInstance.getVideo();
- if (v) {
- v.playbackRate = this.cache.obj.speed;
- }
- }
- fn();
- setInterval(() => {
- fn();
- },500);
- });
- }
- buildAction(actions) {
- let cacheAction = this.cache.obj.Action;
- let actionList = [];
- let actionListForDom = {};
- let actionListForDom_ = [];
- actions.forEach(act => {
- this.action[act.name] = {
- action:()=>{},
- replay: false,
- }
- // act = { name: "", action() {}, group: "", run: false }
- if (!act.group) {
- act.group = act.name;
- }
- if (!(act.group in actionListForDom)) {
- actionListForDom[act.group] = [];
- }
- if (!(act.group in cacheAction)) {
- cacheAction[act.group] = "";
- } else if (cacheAction[act.group] === "") {
- cacheAction[act.group] = "none_" + new Date().getTime();
- }
- if (act.name === cacheAction[act.group]) {
- // 该主当前希望运行改方法
- actionList.push(act.action);
- cacheAction[act.group] = act.name;
- act.run = true;
- if (act.replay) {
- this.action[act.name] = {
- action: act.action,
- replay: act.replay,
- };
- }
- } else {
- if (act.run) {
- if (cacheAction[act.group] === "") {
- cacheAction[act.group] = act.name;
- actionList.push(act.action);
- if (act.replay) {
- this.action[act.name] = {
- action: act.action,
- replay: act.replay,
- };
- }
- } else {
- act.run = false;
- this.action[act.name] = {
- action: act.action,
- replay: false,
- };
- }
- } else {
- this.action[act.name] = {
- action: act.action,
- replay: false,
- };
- }
- }
- if (!act.replay) {
- this.action[act.name].replay = true;
- }
- actionListForDom[act.group].push({
- name: act.name,
- run: act.run
- });
- });
- for (let i in actionListForDom) {
- actionListForDom_.push({
- group: i,
- actions: actionListForDom[i]
- });
- }
- this.cache.obj.Action = Object.assign(this.cache.obj.Action, cacheAction);
- this.cache.store();
- return {
- promise: (function (actionLis, cache) {
- return new Promise(s => s()).then(() => {
- actionList.forEach(act => act(true, cache));
- return true;
- });
- }).bind(null,actionList,this.cache.obj),
- actionListForDom: actionListForDom_
- };
- }
- createDom(actionListForDom) {
- let styleDom = document.createElement('style');
- styleDom.innerHTML = `.play_dom_box {
- display: flex;
- padding: 5px;
- }
- .play_dom_btn {
- background: gainsboro;
- padding: 2px;
- margin-bottom: 2px;
- border-radius: 4px;
- width: 40px;
- text-align: center;
- cursor: pointer;
- }
- .play_dom_text {
- flex: 1;
- line-height: 80px;
- text-align: center;
- }`;
- let boxDom = document.createElement('div');
- boxDom.innerHTML = `
- <div class="play_dom_box">
- <div>
- <div class="play_dom_btn play_dom_btn_speed" tag="speed">-0.1</div>
- <div class="play_dom_btn play_dom_btn_speed" tag="speed">-0.2</div>
- <div class="play_dom_btn play_dom_btn_speed" tag="speed">-0.5</div>
- </div>
- <div class="play_dom_text play_dom_text_speed">${this.cache.obj.speed}</div>
- <div>
- <div class="play_dom_btn play_dom_btn_speed" tag="speed">+0.1</div>
- <div class="play_dom_btn play_dom_btn_speed" tag="speed">+0.2</div>
- <div class="play_dom_btn play_dom_btn_speed" tag="speed">+0.5</div>
- </div>
- </div>
- <div style="display: flex;">透明 【<div class="play_dom_text_opacity">${this.cache.obj.opacity}</div>】 <div class="play_dom_btn play_dom_btn_opacity" tag="speed">-0.1</div>
- <div class="play_dom_btn play_dom_btn_opacity" tag="speed">+0.1</div></div>
- <div>
- ${actionListForDom.map(act => {
- // {
- // group: "",
- // actions: [{name: '', run: ''}]
- // }
- if (act.actions.length > 1) {
- let dom = act.actions.map(a => {
- return `<input type="radio" tag="${a.name}" name="${act.group}" ${a.run ? 'checked' : ''} class="play_dom_check"/> ${a.name}`;
- }).join('');
- return `<div>${act.group} | ${dom}</div>`
- } else {
- let acti = act.actions[0];
- return `<div>${acti.name} <input tag="${acti.name}" type="checkbox" name="${act.group}" ${acti.run ? 'checked' : ''} class="play_dom_check"/></div>`
- }
- }).join(' ')}
- </div>`;
- let dom = this.draggableDom.getDom();
- dom.append(styleDom);
- dom.append(boxDom);
- let $this = this;
- let speedTextDom = boxDom.getElementsByClassName('play_dom_text_speed')[0];
- let opacityTextDom = boxDom.getElementsByClassName('play_dom_text_opacity')[0];
- new Array(...boxDom.getElementsByClassName('play_dom_btn_opacity')).forEach(btn => {
- btn.onclick = function () {
- let sp = +this.innerText;
- $this.cache.set('opacity', (+$this.cache.obj.opacity + sp).toFixed(1));
- opacityTextDom.innerText = $this.cache.obj.opacity;
- $this.draggableDom.dom.style.opacity = $this.cache.obj.opacity;
- };
- });
- new Array(...boxDom.getElementsByClassName('play_dom_btn_speed')).forEach(btn => {
- btn.onclick = function () {
- let sp = +this.innerText;
- $this.cache.set('speed', (+$this.cache.obj.speed + sp).toFixed(2));
- speedTextDom.innerText = $this.cache.obj.speed;
- // $this.video.playbackRate = $this.cache.obj.speed;
- };
- });
- new Array(...boxDom.getElementsByClassName('play_dom_check')).forEach(btn => {
- btn.onchange = function () {
- let group = this.getAttribute('name');
- let actionName = this.getAttribute('tag');
- let currentAction = $this.action[actionName];
- if (this.checked) {
- $this.cache.obj.Action[group] = actionName;
- if (currentAction.replay) {
- currentAction.action(true, $this.cache.obj);
- } else {
- currentAction.action(true, $this.cache.obj);
- $this.action[actionName].action = () => {};
- }
- } else {
- $this.cache.obj.Action[group] = "";
- if (currentAction.replay) {
- currentAction.action(false, $this.cache.obj);
- }
- }
- $this.cache.store();
- };
- });
- }
- }
- class Video {
- static getVideoMethod = {
- default() {
- let videos = document.getElementsByTagName('video');
- let video = false;
- if (videos.length) {
- if (videos.length === 1) {
- video = videos[0];
- } else {
- let ww = parseInt(window.innerWidth * 0.4);
- let v = videos.filter(v => v.width > ww);
- if (v.length) {
- video = v[0];
- }
- }
- }
- return video;
- },
- 'youtube.com'() {
- let videos = document.getElementsByTagName('video');
- return videos.length ? videos[0] : false;
- },
- 'bilibili.com'() {
- let bwpVideos = document.getElementsByTagName('bwp-video');
- let trVideos = document.getElementsByTagName('video');
- return bwpVideos.length ? bwpVideos[0] : (trVideos.length ? trVideos[0] : false);
- },
- }
- constructor() {
- this.baseDom = null;
- this.host =null;
- this.getVideoMethod = () => false;
- }
- getVideo() {
- return this.getVideoMethod();
- }
- init() {
- return new Promise(s => {
- setTimeout(() => {
- this.host = location.hostname.split('.').reverse().slice(0, 2).reverse().join('.');
- if (this.host in Video.getVideoMethod) {
- this.getVideoMethod = Video.getVideoMethod[this.host];
- }
- // 尝试推荐方法2次后使用默认方法不断尝试,超过一分钟后停止所有尝试
- let times = 2;
- let nexter = () => {
- this.getVideoMethod = Video.getVideoMethod["default"];
- times = 60;
- let id = setInterval(() => {
- let video = this.getVideoMethod();
- if (video) {
- clearInterval(id);
- s();
- } else {
- times--;
- if (!times) {
- clearInterval(id);
- console.log("放弃,没有希望了");
- }
- }
- }, 1000);
- };
- let id = setInterval(() => {
- let video = this.getVideoMethod();
- if (video) {
- clearInterval(id);
- s();
- } else {
- times--;
- if (!times) {
- clearInterval(id);
- nexter();
- }
- }
- }, 500);
- s();
- }, 1000);
- })
- }
- }
- (function() {
- 'use strict';
- if (checkWindow()) {
- let video = new Video();
- video.init().then(() => {
- let v = video.getVideo();
- if (v) {
- console.log("play dom");
- window.playDom = new PlayDom(v, video, [
- ...(DefaultAction[video.host](video) || []),
- {
- group: "重置",
- name: "重置",
- run: false,
- action() {
- new Cache().clear();
- }
- },
- ]);
- }
- });
- }
- // Your code here...
- })();