Greasy Fork is available in English.
脚本基于 Violentmonkey 开发,为 HTML5 视频,添加一些通用功能
// ==UserScript== // @name HTML5 视频增强脚本 // @version 1658069002 // @description 脚本基于 Violentmonkey 开发,为 HTML5 视频,添加一些通用功能 // @author So // @namespace https://github.com/Git-So/video-userscript // @homepageURL https://github.com/Git-So/video-userscript // @supportURL https://github.com/Git-So/video-userscript/issues // @match http://*/* // @match https://*/* // @grant GM_addStyle // @grant GM_openInTab // @grant unsafeWindow // ==/UserScript== var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; (function() { "use strict"; GM_addStyle(` @charset "UTF-8"; @keyframes toast-show { from { opacity: 0; } 25% { opacity: 1; } 75% { opacity: 1; } to { opacity: 0; } } .sooo--video { /** * 动作提示 */ /** * 关灯影院模式 */ /** * 视频镜像 */ /** * 视频解析 */ } .sooo--video-action-toast { position: absolute !important; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; padding: 10px 15px; font-size: 18px; color: whitesmoke; background-color: rgba(0, 0, 0, 0.555); z-index: 7777777; } .sooo--video-action-toast-animation { animation: toast-show 1.2s alternate forwards; } .sooo--video-movie-mode { z-index: 99999999 !important; } .sooo--video-movie-mode-parent { z-index: auto !important; } .sooo--video-movie-mode-modal { inset: 0; width: 100%; height: 100%; position: fixed !important; background: rgba(0, 0, 0, 0.9); z-index: 55555; } .sooo--video-mirror video { transform: rotateX(0deg) rotateY(180deg); } .sooo--video-iframe { inset: 0; width: 100%; height: 100%; position: absolute !important; display: block; z-index: 55555; border: 0; } `); var style = ""; const tagName = { div: "DIV", iframe: "IFRAME" }; function reanimation(func) { window.requestAnimationFrame(() => window.requestAnimationFrame(() => { func(); })); } function isActiveElementEditable() { const activeElement = document.activeElement; if (!activeElement) return false; if (activeElement.isContentEditable) return true; if ("value" in activeElement) return true; return false; } function between(value2, min = 0, max = 1) { if (value2 < min) return min; if (value2 > max) return max; return value2; } function topWindow() { return unsafeWindow.top; } function actionOfAllParent(el, action, level = 0) { let parent = el.parentElement; if (!parent) return el; const currWindow = parent.ownerDocument.defaultView; if (parent.tagName == "BODY") { if (currWindow == currWindow.top) return el; const iframeArr = currWindow.parent.document.querySelectorAll("iframe"); for (const iframe of iframeArr) { if (currWindow != iframe.contentWindow) continue; parent = iframe; break; } } if (level < 1 && action.self) action.self(el); if (parent.tagName == tagName.iframe) { if (action.iframe) action.iframe(parent); } else { if (!action.parent(parent)) return el; } return actionOfAllParent(parent, action, level + 1); } function actionOfAllSubWindow(action, isIncludeSelf = true, win = topWindow()) { const iframeArr = win.document.querySelectorAll("iframe"); for (const iframe of iframeArr) { if (!iframe.contentDocument || !iframe.contentWindow) continue; actionOfAllSubWindow(action, true, iframe.contentWindow); } if (isIncludeSelf) action(win); } const value = [ { match: `^https?://www.bilibili.com/video/`, player: "#bilibili-player .bpx-player-container" }, { match: `^https?://haokan.baidu.com/v?`, player: "#mse .art-video-player" } ]; class Config { constructor() { __publicField(this, "initConfig", { video: { enable: true, lastElement: null, isPirate: false } }); } get window() { return topWindow(); } get value() { if (!this.window.UserscriptConfig) this.window.UserscriptConfig = this.initConfig; return new Proxy(this.window.UserscriptConfig.video, {}); } } const _Video = class { constructor() { __publicField(this, "config"); this.config = new Config().value; } static get instance() { if (!_Video._instance) { _Video._instance = new _Video(); } return this._instance; } set lastElement(el) { this.config.lastElement = el; } get lastElement() { return this.config.lastElement; } rule() { for (const rule of value) { const rg = new RegExp(rule.match); if (location.href.search(rg) > -1) return rule; } return null; } static isExistPlayer() { return !!_Video.instance.player(); } static isNotExistPlayer() { return !_Video.isExistPlayer(); } static isEnable() { return _Video.instance.config.enable; } static isDisable() { return !_Video.instance.config.enable; } getAllVideoElement(doc = document) { const videoArr = doc.querySelectorAll("video"); let allVideo = [...videoArr]; const iframeArr = doc.querySelectorAll("iframe"); for (const iframe of iframeArr) { if (!iframe.contentDocument) continue; allVideo = [ ...allVideo, ...this.getAllVideoElement(iframe.contentDocument) ]; } return allVideo; } element() { var _a; const allMedia = this.getAllVideoElement(); for (const media of allMedia) { if (!media.paused) { this.config.lastElement = media; break; } } if (!this.config.lastElement) { this.config.lastElement = (_a = allMedia[0]) != null ? _a : null; } return this.config.lastElement; } player(videoElement = this.element()) { const rule = this.rule(); if (rule) return document.querySelector(rule.player); if (!videoElement) return null; return actionOfAllParent(videoElement, { parent: (el) => el.clientHeight == videoElement.clientHeight && el.clientWidth == videoElement.clientWidth }); } toast(text) { const player = this.player(); if (!player) return; const className = "sooo--video-action-toast"; const animationClassName = "sooo--video-action-toast-animation"; if (!player.querySelector(`.${className}`)) { const element = document.createElement("DIV"); element.classList.add(className); player.append(element); } const toast = player.querySelector(`.${className}`); toast.classList.remove(animationClassName); toast.innerHTML = ""; toast.append(text); reanimation(() => { toast.classList.add(animationClassName); }); } }; let Video = _Video; __publicField(Video, "_instance"); class Action { constructor() { __publicField(this, "_name", ""); } get name() { return this._name; } get video() { return Video.instance; } get media() { return this.video.element(); } get player() { return this.video.player(); } get window() { return topWindow(); } get document() { return this.window.document; } safeAction(action, that = this) { if (!this.media) return; action.apply(that); } } class SwitchAction extends Action { get isEnable() { return false; } enableAction() { } enable() { this.safeAction(this.enableAction); this.video.toast(`${this.name}: \u5F00`); } disableAction() { } disable() { this.safeAction(this.disableAction); this.video.toast(`${this.name}: \u5173`); } toggle() { this.isEnable ? this.disable() : this.enable(); } } class StepAction extends Action { constructor() { super(...arguments); __publicField(this, "step", 1); } setValue(_value, _isStep = true) { } add(step = this.step) { this.setValue(+step); } sub(step = this.step) { this.setValue(-step); } } class Fullscreen extends SwitchAction { constructor() { super(...arguments); __publicField(this, "_name", "\u89C6\u9891\u5168\u5C4F"); } get isEnable() { return !!this.document.fullscreenElement; } enableAction() { var _a; (_a = this.player) == null ? void 0 : _a.requestFullscreen(); } disableAction() { this.document.exitFullscreen(); } } class PlayState extends SwitchAction { constructor() { super(...arguments); __publicField(this, "_name", "\u89C6\u9891\u64AD\u653E"); } get isEnable() { var _a; return !((_a = this.media) == null ? void 0 : _a.paused); } enableAction() { var _a; (_a = this.media) == null ? void 0 : _a.play(); } disableAction() { var _a; (_a = this.media) == null ? void 0 : _a.pause(); } } class PictureInPicture extends SwitchAction { constructor() { super(...arguments); __publicField(this, "_name", "\u753B\u4E2D\u753B"); } get isEnable() { var _a; return !!((_a = this.media) == null ? void 0 : _a.ownerDocument.pictureInPictureElement); } enableAction() { var _a; (_a = this.media) == null ? void 0 : _a.requestPictureInPicture(); } disableAction() { var _a; if (!this.isEnable) return; (_a = this.media) == null ? void 0 : _a.ownerDocument.exitPictureInPicture(); } } class CurrentTime extends StepAction { constructor() { super(...arguments); __publicField(this, "_name", "\u89C6\u9891\u8FDB\u5EA6"); __publicField(this, "step", 10); } setValue(value2, isStep = true) { this.safeAction(() => { const currentTime = isStep ? this.media.currentTime + value2 : value2; this.media.currentTime = currentTime; this.video.toast(`${this.name}: ${value2 < 0 ? "" : "+"}${value2}\u79D2`); }); } } class Volume extends StepAction { constructor() { super(...arguments); __publicField(this, "_name", "\u97F3\u91CF"); __publicField(this, "step", 0.1); } setValue(value2, isStep = true) { this.safeAction(() => { const volume = isStep ? this.media.volume + value2 : value2; this.media.volume = between(volume, 0, 1); this.video.toast(`${this.name}:${this.media.volume * 100 | 0}% `); }); } } class PlaybackRate extends StepAction { constructor() { super(...arguments); __publicField(this, "_name", "\u500D\u6570\u64AD\u653E"); __publicField(this, "step", 1); __publicField(this, "playbackRate", [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 5]); __publicField(this, "defaultIdx", 3); } get currIdx() { if (!this.media) return this.defaultIdx; const idx = this.playbackRate.indexOf(this.media.playbackRate); return idx < 0 ? this.defaultIdx : idx; } setValue(value2, isStep = true) { this.safeAction(() => { value2 = isStep ? this.currIdx + value2 : value2; const idx = between(value2, 0, this.playbackRate.length - 1); const rate = this.playbackRate[idx]; this.media.playbackRate = rate; this.video.toast(`${this.name}: ${rate}x`); }); } restart() { this.setValue(this.defaultIdx, false); } } class MovieMode extends SwitchAction { constructor() { super(...arguments); __publicField(this, "_name", "\u5F71\u9662\u6A21\u5F0F"); __publicField(this, "className", "sooo--video-movie-mode"); __publicField(this, "parentClassName", "sooo--video-movie-mode-parent"); __publicField(this, "modalClassName", "sooo--video-movie-mode-modal"); } get isEnable() { var _a; return !!((_a = this.player) == null ? void 0 : _a.classList.contains(this.className)); } enableAction() { const action = (el) => { el.classList.add(this.className); el.ownerDocument.body.append((() => { const modal = el.ownerDocument.createElement("DIV"); modal.className = this.modalClassName; return modal; })()); }; actionOfAllParent(this.player, { parent: (el) => { el.classList.add(this.parentClassName); return true; }, iframe: action, self: action }); } disableAction() { var _a; (_a = this.player) == null ? void 0 : _a.classList.remove(this.className); actionOfAllSubWindow((win) => { var _a2; (_a2 = win.document.querySelector(`.${this.modalClassName}`)) == null ? void 0 : _a2.remove(); win.document.querySelectorAll(`.${this.parentClassName}`).forEach((el) => { el.classList.remove(this.parentClassName); }); }); } } class Mirror extends SwitchAction { constructor() { super(...arguments); __publicField(this, "_name", "\u89C6\u9891\u955C\u50CF"); __publicField(this, "className", "sooo--video-mirror"); } get isEnable() { var _a; return !!((_a = this.player) == null ? void 0 : _a.classList.contains(this.className)); } enableAction() { var _a; (_a = this.player) == null ? void 0 : _a.classList.add(this.className); } disableAction() { var _a; (_a = this.player) == null ? void 0 : _a.classList.remove(this.className); } } class Loop extends SwitchAction { constructor() { super(...arguments); __publicField(this, "_name", "\u5FAA\u73AF\u64AD\u653E"); } get isEnable() { var _a; return !!((_a = this.media) == null ? void 0 : _a.loop); } enableAction() { this.media.loop = true; } disableAction() { this.media.loop = false; } } class Muted extends SwitchAction { constructor() { super(...arguments); __publicField(this, "_name", "\u89C6\u9891\u9759\u97F3"); } get isEnable() { var _a; return !!((_a = this.media) == null ? void 0 : _a.muted); } enableAction() { this.media.muted = true; } disableAction() { this.media.muted = false; } } class Pirate extends Action { constructor() { super(...arguments); __publicField(this, "_name", "\u89C6\u9891\u89E3\u6790"); __publicField(this, "ruleArr", [ "https://z1.m1907.cn/?jx=", "https://jsap.attakids.com/?url=", "https://jx.bozrc.com:4433/player/?url=", "https://okjx.cc/?url=", "https://jx.blbo.cc:4433/?url=", "https://www.yemu.xyz/?url=", "https://jx.aidouer.net/?url=", "https://jx.xmflv.com/?url=", "https://jx.m3u8.tv/jiexi/?url=" ]); } open(idx) { new PlayState().disable(); GM_openInTab(this.ruleArr[between(idx, 0, this.ruleArr.length - 1)] + location.href); } } class ScriptState extends SwitchAction { constructor() { super(...arguments); __publicField(this, "_name", "\u89C6\u9891\u811A\u672C"); } get isEnable() { return this.video.config.enable; } enableAction() { this.video.config.enable = true; } disableAction() { this.video.config.enable = false; } } document.addEventListener("keydown", (e) => { if (isActiveElementEditable() || Video.isNotExistPlayer()) return; const defer = () => { e.stopPropagation(); e.stopImmediatePropagation(); e.preventDefault(); }; if (e.shiftKey && e.code == "KeyU") { new ScriptState().toggle(); } if (Video.isDisable()) return defer(); let hasAction = true; switch (true) { case e.code == "Enter": new Fullscreen().toggle(); break; case e.code == "Space": new PlayState().toggle(); break; case (e.shiftKey && e.code == "KeyA"): new CurrentTime().sub(); break; case (e.shiftKey && e.code == "KeyD"): new CurrentTime().add(); break; case (e.shiftKey && e.code == "KeyW"): new Volume().add(); break; case (e.shiftKey && e.code == "KeyS"): new Volume().sub(); break; case (e.shiftKey && e.code == "KeyZ"): new PlaybackRate().sub(); break; case (e.shiftKey && e.code == "KeyX"): new PlaybackRate().restart(); break; case (e.shiftKey && e.code == "KeyC"): new PlaybackRate().add(); break; case (e.ctrlKey && e.shiftKey && e.code == "BracketRight"): new PictureInPicture().toggle(); break; case (e.shiftKey && e.code == "KeyO"): new MovieMode().toggle(); break; case (e.shiftKey && e.code == "KeyH"): new Mirror().toggle(); break; case (e.shiftKey && e.code == "KeyL"): new Loop().toggle(); break; case (e.shiftKey && e.code == "KeyM"): new Muted().toggle(); break; case (e.shiftKey && e.code == "Digit1"): new Pirate().open(1); break; case (e.shiftKey && e.code == "Digit2"): new Pirate().open(2); break; case (e.shiftKey && e.code == "Digit3"): new Pirate().open(3); break; case (e.shiftKey && e.code == "Digit4"): new Pirate().open(4); break; case (e.shiftKey && e.code == "Digit5"): new Pirate().open(5); break; case (e.shiftKey && e.code == "Digit6"): new Pirate().open(6); break; case (e.shiftKey && e.code == "Digit7"): new Pirate().open(7); break; case (e.shiftKey && e.code == "Digit8"): new Pirate().open(8); break; case (e.shiftKey && e.code == "Digit9"): new Pirate().open(9); break; default: hasAction = false; } if (!hasAction) return; defer(); }); })();