🏠 Home 

音量增强器

增强页面音视频音量

// ==UserScript==
// @name         音量增强器
// @name:en_US   Volume Booster
// @name:zh-CN   音量增强器
// @namespace    System233
// @version      0.3
// @description  增强页面音视频音量
// @description:zh-CN  增强页面音视频音量
// @description:en_US  Increase page audio and video volume
// @author       System233
// @match        *://*/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=bilibili.com
// @grant        none
// @license      GPL-3.0-only
// ==/UserScript==
// Copyright (c) 2022 System233
//
// This software is released under the GPL-3.0 License.
// https://opensource.org/licenses/GPL-3.0
(function () {
'use strict';
const setup = () => {
// 热键配置:当前为CTRL+Shift
const hotKeys = {
Control: true,
Shift: true,
Alt: false,
};
/** @type {HTMLElement} */
let currentNode = null;
/** @type {(node:HTMLElement)=>void} */
const setupNode = node => {
if(!node){
return;
}
if ('volumeBoost' in node) {
return;
}
const audioCtx = new AudioContext();
const gainNode = audioCtx.createGain();
const mediaSource = audioCtx.createMediaElementSource(node);
gainNode.connect(audioCtx.destination);
mediaSource.connect(gainNode);
let volume = 1;
Object.defineProperty(node, 'volumeBoost', {
get() {
return volume;
},
set(value) {
volume = Math.max(value, 0);
gainNode.gain.setValueAtTime(volume, audioCtx.currentTime);
}
})
}
const META_CLEANUP = 'VolumeBoosterCleanup';
const current = {
Control: false,
Shift: false,
Alt: false,
}
const keys = Object.keys(current);
const cleanup = (node) => {
if (node && Reflect.has(node, META_CLEANUP)) {
Reflect.get(node, META_CLEANUP).call();
}
}
const check = () => keys.findIndex(x => current[x] != hotKeys[x]) == -1;
let lastNode=null;
const selectNode = (node) => {
setupNode(node);
cleanup(lastNode);
if (!node) {
return;
}
lastNode = node;
const { border, boxSizing } = node.style;
node.style.border = '.5em solid red';
node.style.boxSizing = 'border-box';
const rect = node.getBoundingClientRect();
if (rect.top + rect.height > window.innerHeight
|| rect.top < 0
|| rect.left < 0
|| rect.left + rect.width > window.innerWidth) {
node.scrollIntoView();
}
const META_KEY = 'VolumeBooster';
/** @type {HTMLDivElement} */
const volumeBooster = Reflect.get(node, META_KEY) || document.createElement('div');
volumeBooster.style.color = 'red';
volumeBooster.style.position = 'absolute';
volumeBooster.style.right = 0;
volumeBooster.style.top = 0;
volumeBooster.style.background = '#ccc';
volumeBooster.style.padding = '0.2em';
volumeBooster.style.zIndex = 10000;
volumeBooster.innerHTML = `${Math.round(node.volumeBoost * 100)}%`;
if (!volumeBooster.parentNode) {
node.parentNode.append(volumeBooster);
Reflect.set(node, META_KEY, volumeBooster);
}
Reflect.set(node, META_CLEANUP, () => {
node.style.border = border;
node.style.boxSizing = boxSizing;
volumeBooster.remove();
});
}
const boost = (step) => {
if (currentNode) {
currentNode.volumeBoost += step;
selectNode(currentNode);
}
}
const moveNextNode = (next) => {
const nodes = Array.from(document.querySelectorAll('video,audio'));
if (!nodes.length) {
return;
}
const index = Math.max(currentNode ? nodes.findIndex(node => node == currentNode) : 0, 0);
const nextIndex = (index + next + nodes.length) % nodes.length;
currentNode=nodes[nextIndex];
}
/** @type {(HTMLVideoElement|HTMLAudioElement)[]} */
let active = false;
const press = (key, press) => {
if (key in current) {
current[key] = press
}
active = check();
}
document.addEventListener('keyup', e => press(e.key, false));
document.addEventListener('keydown', e => press(e.key, true));
document.addEventListener('keyup', e => !active&&selectNode(null));
document.addEventListener('keydown', e => {
if (active) {
if(!currentNode){
moveNextNode(0);
}
switch (e.key) {
case 'ArrowUp': boost(.1); break;
case 'ArrowDown': boost(-.1); break;
case 'ArrowLeft': moveNextNode(-1); break;
case 'ArrowRight': moveNextNode(1); break;
}
selectNode(currentNode);
}
})
}
window.addEventListener('load', setup);
})();