- // ==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();
- });
- })();