// ==UserScript== // @name Video Userscript // @version 1657354152 // @description HTML5 视频增强脚本 // @author So // @namespace site.sooo.userscript.video // @match http://*/* // @match https://*/* // @grant GM_addStyle // @downloadURL none // ==/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: 1.5em; color: whitesmoke; background-color: rgba(0, 0, 0, 0.555); z-index: 9000; } .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 { top: 0; left: 0; width: 100%; height: 100%; position: fixed !important; background: rgba(0, 0, 0, 0.9); z-index: 1000000; } .sooo--video-mirror video { transform: rotateX(0deg) rotateY(180deg); } `); var style = ""; const value = [ { match: `^https?://www.bilibili.com/video/`, player: "#bilibili-player .bpx-player-container .bpx-player-video-area" } ]; class Video { rule() { for (const rule of value) { const rg = new RegExp(rule.match); if (location.href.search(rg) > -1) return rule; } return null; } defaultMedia() { var _a; const items = document.querySelectorAll("video"); let media = (_a = items[0]) != null ? _a : null; for (const item of items) { if (!item.paused) break; media = item; } return media; } defaultPlayer(media = null) { let player = media != null ? media : this.defaultMedia(); if (!player) return null; return actionByAncestor(player, (parent) => { return parent.clientHeight == (player == null ? void 0 : player.clientHeight) && parent.clientWidth == (player == null ? void 0 : player.clientWidth); }); } media() { const rule = this.rule(); if (rule) { if (rule.media) return document.querySelector(rule.media); return document.querySelector(`${rule.player} video`); } return this.defaultMedia(); } player(media = null) { const rule = this.rule(); if (rule) return document.querySelector(rule.player); return this.defaultPlayer(media); } } function actionByAncestor(element, action) { for (let _i = 0; _i < 500; _i++) { const parent = element.parentElement; if (!parent || parent.tagName == "BODY") break; if (!action(parent)) break; element = parent; } return element; } function reanimation(func) { window.requestAnimationFrame(() => window.requestAnimationFrame(() => { func(); })); } function toast(player, text) { 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 toast2 = player.querySelector(`.${className}`); toast2.classList.remove(animationClassName); toast2.innerHTML = ""; toast2.append(text); reanimation(() => { toast2.classList.add(animationClassName); }); } function isActiveElementEditable() { const activeElement = document.activeElement; if (!activeElement) return false; if (activeElement.isContentEditable) return true; if ("value" in activeElement) return true; return false; } function isExistMedia() { return !!new Video().media(); } function between(value2, min = 0, max = 1) { if (value2 < min) return min; if (value2 > max) return max; return value2; } class Action { constructor() { __publicField(this, "_name", ""); __publicField(this, "video", new Video()); __publicField(this, "_media", null); __publicField(this, "_player", null); } get name() { return this._name; } get media() { if (!this._media) this._media = this.video.media(); return this._media; } get player() { if (!this._player) this._player = this.video.player(this.media); return this._player; } safeAction(action, that = this) { if (!this.media) return; action.apply(that); } toast(text) { toast(this.player, text); } } class SwitchAction extends Action { get isEnable() { return false; } enableAction() { } enable() { this.safeAction(this.enableAction); this.toast(`${this.name}: \u5F00`); } disableAction() { } disable() { this.safeAction(this.disableAction); this.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 !!document.fullscreenElement; } enableAction() { var _a; (_a = this.player) == null ? void 0 : _a.requestFullscreen(); } disableAction() { 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() { return !!document.pictureInPictureElement; } enableAction() { var _a; (_a = this.media) == null ? void 0 : _a.requestPictureInPicture(); } disableAction() { if (!this.isEnable) return; document.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.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.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.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() { var _a; (_a = this.player) == null ? void 0 : _a.classList.add(this.className); document.body.append((() => { const modal = document.createElement("DIV"); modal.className = this.modalClassName; return modal; })()); actionByAncestor(this.player, (element) => { element.classList.add(this.parentClassName); return true; }); } disableAction() { var _a, _b; (_a = this.player) == null ? void 0 : _a.classList.remove(this.className); (_b = document.querySelector(`.${this.modalClassName}`)) == null ? void 0 : _b.remove(); 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; } } document.addEventListener("keydown", (e) => { if (isActiveElementEditable() || !isExistMedia()) return; 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; default: hasAction = false; } if (!hasAction) return; e.stopPropagation(); e.stopImmediatePropagation(); e.preventDefault(); }); })();