// ==UserScript==
// @name 通用音量增强器 - Universal Volume Booster
// @namespace http://tampermonkey.net/
// @version 1.8
// @description 在所有网站(包括YouTube、TikTok、抖音、快手)上强制增强音量,支持Alt+s快捷键启用/禁用,Alt+5热重载脚本,Alt+↑/↓调节音量
// @author 醉春风制作
// @match *://*/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @run-at document-start
// @license All Rights Reserved
// ==/UserScript==
/*
通用音量增强器 - Universal Volume Booster
版本: 1.8
作者: 醉春风制作
版权所有 (c) 2025 醉春风制作
保留所有权利。
本脚本为作者独立创作,未经作者明确授权,禁止任何形式的:
1. 复制、修改或二次开发本脚本
2. 重新发布、分发或出售本脚本
3. 移除或修改本脚本中的版权信息和作者信息
使用本脚本即表示您同意遵守上述条款。
*/
(function() {
'use strict';
// 保存用户设置的音量增强倍数
let volumeMultiplier = GM_getValue('volumeMultiplier', 1.0);
let isBoosterActive = GM_getValue('isBoosterActive', false);
let isDragging = false;
let controlsVisible = false;
let hideTimeout;
let lastKeyTime = 0; // 用于防止快捷键重复触发
let mediaElements = []; // 跟踪页面中的所有媒体元素
let iconVisible = GM_getValue('iconVisible', true); // 图标是否可见
let iconPosition = GM_getValue('iconPosition', { x: 20, y: 20 }); // 图标位置
let isFullscreen = false; // 跟踪全屏状态
let fullscreenChangeCount = 0; // 用于跟踪全屏变化次数
let lastVolumeChangeTime = 0; // 用于跟踪最后一次音量变化时间
let lastRightClickTime = 0; // 用于跟踪右键双击
let douyinVolumeApplied = false; // 跟踪抖音音量是否已应用
// 存储脚本实例ID,用于热重载时清理旧实例
const scriptInstanceId = Date.now().toString(36) + Math.random().toString(36).substr(2);
window._volumeBoosterInstances = window._volumeBoosterInstances || [];
window._volumeBoosterInstances.push(scriptInstanceId);
// 存储需要清理的事件监听和定时器
let eventListeners = [];
let intervalTimers = [];
let mediaObserver = null;
// 检测当前网站
const isYouTube = window.location.hostname.includes('youtube.com');
const isTikTok = window.location.hostname.includes('tiktok.com');
const isDouyin = window.location.hostname.includes('douyin.com');
const isKuaishou = window.location.hostname.includes('kuaishou.com') || window.location.hostname.includes('kwai.com');
const isBilibili = window.location.hostname.includes('bilibili.com');
const isXigua = window.location.hostname.includes('ixigua.com');
// 创建音量增强控制界面
function createVolumeControls() {
// 添加样式
GM_addStyle(`
#volume-booster-container-${scriptInstanceId} {
position: fixed;
top: 20px;
right: 20px;
background-color: rgba(0, 0, 0, 0.7);
color: white;
padding: 10px;
border-radius: 5px;
z-index: 9999999;
font-family: Arial, sans-serif;
transition: opacity 0.3s ease;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
user-select: none;
opacity: 0;
pointer-events: none;
}
#volume-booster-container-${scriptInstanceId}.visible {
opacity: 1;
pointer-events: auto;
}
#volume-booster-container-${scriptInstanceId}.active {
border: 2px solid #4CAF50;
}
#volume-booster-container-${scriptInstanceId}.inactive {
border: 2px solid #F44336;
}
#volume-booster-container-${scriptInstanceId}.fullscreen {
position: fixed;
top: 50px;
right: 50px;
z-index: 2147483647;
background-color: rgba(0, 0, 0, 0.8);
box-shadow: 0 0 15px rgba(0, 0, 0, 0.8);
}
#volume-slider-container-${scriptInstanceId} {
width: 200px;
margin: 10px 0;
}
#volume-slider-${scriptInstanceId} {
width: 100%;
height: 8px;
-webkit-appearance: none;
background: linear-gradient(to right, #4CAF50 0%, #4CAF50 ${(volumeMultiplier-1)*100/9}%, #ddd ${(volumeMultiplier-1)*100/9}%, #ddd 100%);
outline: none;
border-radius: 5px;
cursor: pointer;
}
#volume-slider-${scriptInstanceId}::-webkit-slider-thumb {
-webkit-appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: white;
cursor: pointer;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
transition: all 0.2s ease;
}
#volume-slider-${scriptInstanceId}::-webkit-slider-thumb:hover {
width: 22px;
height: 22px;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.7);
}
#volume-slider-${scriptInstanceId}::-webkit-slider-thumb:active {
width: 22px;
height: 22px;
background: #f0f0f0;
}
#volume-slider-${scriptInstanceId}::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
background: white;
cursor: pointer;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
border: none;
transition: all 0.2s ease;
}
#volume-slider-${scriptInstanceId}::-moz-range-thumb:hover {
width: 22px;
height: 22px;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.7);
}
#volume-slider-${scriptInstanceId}::-moz-range-thumb:active {
width: 22px;
height: 22px;
background: #f0f0f0;
}
#volume-slider-${scriptInstanceId}::-moz-range-progress {
background-color: #4CAF50;
height: 8px;
border-radius: 5px;
}
#volume-value-${scriptInstanceId} {
text-align: center;
font-size: 14px;
margin-top: 5px;
font-weight: bold;
}
#volume-toggle-${scriptInstanceId} {
width: 100%;
padding: 8px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 14px;
margin-top: 5px;
transition: background-color 0.2s ease, transform 0.1s ease;
}
#volume-toggle-${scriptInstanceId}:hover {
background-color: #45a049;
}
#volume-toggle-${scriptInstanceId}:active {
transform: scale(0.98);
}
#volume-toggle-${scriptInstanceId}.inactive {
background-color: #F44336;
}
#volume-toggle-${scriptInstanceId}.inactive:hover {
background-color: #e53935;
}
.volume-booster-title-${scriptInstanceId} {
font-size: 14px;
text-align: center;
margin-bottom: 5px;
font-weight: bold;
}
.volume-booster-shortcut-${scriptInstanceId} {
font-size: 12px;
text-align: center;
margin-top: 5px;
color: #aaa;
}
#volume-status-indicator-${scriptInstanceId} {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.7);
color: white;
padding: 15px 20px;
border-radius: 5px;
font-size: 16px;
z-index: 2147483647;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
text-align: center;
}
#volume-status-indicator-${scriptInstanceId}.visible {
opacity: 1;
}
#volume-booster-float-button-${scriptInstanceId} {
position: fixed;
width: 40px;
height: 40px;
background-color: rgba(0, 0, 0, 0.6);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 9999998;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
font-size: 18px;
font-weight: bold;
opacity: 0.7;
transition: opacity 0.3s ease, transform 0.2s ease;
}
#volume-booster-float-button-${scriptInstanceId}:hover {
opacity: 1;
transform: scale(1.05);
}
#volume-booster-float-button-${scriptInstanceId}.hidden {
display: none;
}
#volume-booster-float-button-${scriptInstanceId}.fullscreen {
z-index: 2147483646;
}
`);
// 创建控制界面
const container = document.createElement('div');
container.id = `volume-booster-container-${scriptInstanceId}`;
container.className = isBoosterActive ? 'active' : 'inactive';
if (isFullscreen) {
container.classList.add('fullscreen');
}
container.innerHTML = `
<div class="volume-booster-title-${scriptInstanceId}">音量增强器 - 醉春风制作</div>
<div id="volume-slider-container-${scriptInstanceId}">
<input type="range" id="volume-slider-${scriptInstanceId}" min="1" max="10" step="0.1" value="${volumeMultiplier}">
<div id="volume-value-${scriptInstanceId}">增强倍数: ${volumeMultiplier.toFixed(1)}x</div>
</div>
<button id="volume-toggle-${scriptInstanceId}" class="${isBoosterActive ? '' : 'inactive'}">${isBoosterActive ? '已启用' : '已禁用'}</button>
<div class="volume-booster-shortcut-${scriptInstanceId}">快捷键: Alt+↑/↓ Alt+s Alt+5</div>
`;
// 抖音平台特殊处理:将控制界面添加到顶层容器
if (isDouyin) {
// 尝试找到抖音的顶层容器
const douyinContainer = document.querySelector('#root') || document.body;
douyinContainer.appendChild(container);
} else {
document.body.appendChild(container);
}
// 创建状态指示器
const statusIndicator = document.createElement('div');
statusIndicator.id = `volume-status-indicator-${scriptInstanceId}`;
// 抖音平台特殊处理:将状态指示器添加到顶层容器
if (isDouyin) {
const douyinContainer = document.querySelector('#root') || document.body;
douyinContainer.appendChild(statusIndicator);
} else {
document.body.appendChild(statusIndicator);
}
// 添加事件监听
const slider = document.getElementById(`volume-slider-${scriptInstanceId}`);
const toggle = document.getElementById(`volume-toggle-${scriptInstanceId}`);
// 增强滑块拖动体验
slider.addEventListener('input', function() {
volumeMultiplier = parseFloat(this.value);
document.getElementById(`volume-value-${scriptInstanceId}`).textContent = `增强倍数: ${volumeMultiplier.toFixed(1)}x`;
this.style.background = `linear-gradient(to right, #4CAF50 0%, #4CAF50 ${(volumeMultiplier-1)*100/9}%, #ddd ${(volumeMultiplier-1)*100/9}%, #ddd 100%)`;
// 保存设置
GM_setValue('volumeMultiplier', volumeMultiplier);
// 记录最后一次音量变化时间
lastVolumeChangeTime = Date.now();
// 立即应用音量增强
applyVolumeBoost();
// 抖音平台特殊处理:强制更新所有视频元素的音量
if (isDouyin) {
forceUpdateDouyinVolume();
}
});
// 滑块拖动开始
slider.addEventListener('mousedown', function() {
isDragging = true;
clearTimeout(hideTimeout);
});
// 滑块拖动结束
document.addEventListener('mouseup', function() {
if (isDragging) {
isDragging = false;
// 设置隐藏超时
hideTimeout = setTimeout(() => {
hideControls();
}, 3000);
// 抖音平台特殊处理:强制更新所有视频元素的音量
if (isDouyin && isBoosterActive) {
forceUpdateDouyinVolume();
}
}
});
toggle.addEventListener('click', function() {
toggleBooster();
// 记录最后一次音量变化时间
lastVolumeChangeTime = Date.now();
// 抖音平台特殊处理:强制更新所有视频元素的音量
if (isDouyin) {
forceUpdateDouyinVolume();
}
});
// 鼠标悬停显示控制界面
container.addEventListener('mouseenter', function() {
clearTimeout(hideTimeout);
});
container.addEventListener('mouseleave', function() {
if (!isDragging) {
hideTimeout = setTimeout(() => {
hideControls();
}, 3000);
}
});
return container;
}
// 显示状态消息
function showStatusMessage(message) {
const statusIndicator = document.getElementById(`volume-status-indicator-${scriptInstanceId}`);
if (!statusIndicator) return;
statusIndicator.textContent = message;
statusIndicator.classList.add('visible');
setTimeout(() => {
statusIndicator.classList.remove('visible');
}, 1500);
}
// 显示控制界面
function showControls() {
clearTimeout(hideTimeout);
if (!document.getElementById(`volume-booster-container-${scriptInstanceId}`)) {
createVolumeControls();
}
const container = document.getElementById(`volume-booster-container-${scriptInstanceId}`);
if (!container) {
// 如果容器不存在,重新创建
createVolumeControls();
return showControls(); // 递归调用以显示新创建的控制界面
}
container.classList.add('visible');
if (isFullscreen) {
container.classList.add('fullscreen');
} else {
container.classList.remove('fullscreen');
}
controlsVisible = true;
// 记录最后一次音量变化时间
lastVolumeChangeTime = Date.now();
hideTimeout = setTimeout(() => {
hideControls();
}, 3000);
}
// 隐藏控制界面
function hideControls() {
if (!isDragging) {
const container = document.getElementById(`volume-booster-container-${scriptInstanceId}`);
if (container) {
container.classList.remove('visible');
controlsVisible = false;
}
}
}
// 检测全屏状态变化
function setupFullscreenDetection() {
// 多种全屏API的兼容性处理
const fullscreenChangeEvents = [
'fullscreenchange',
'webkitfullscreenchange',
'mozfullscreenchange',
'MSFullscreenChange'
];
fullscreenChangeEvents.forEach(eventName => {
document.addEventListener(eventName, handleFullscreenChange);
eventListeners.push({ type: eventName, handler: handleFullscreenChange, target: document });
});
// 定期检查全屏状态(备用方法,处理某些平台不触发事件的情况)
const fullscreenCheckInterval = setInterval(checkFullscreenState, 500);
intervalTimers.push(fullscreenCheckInterval);
// 抖音平台特殊处理:额外的全屏检测
if (isDouyin) {
const douyinFullscreenCheckInterval = setInterval(checkDouyinFullscreenState, 300);
intervalTimers.push(douyinFullscreenCheckInterval);
}
}
// 抖音平台特殊处理:检查抖音全屏状态
function checkDouyinFullscreenState() {
// 检查抖音特有的全屏元素
const douyinFullscreenElements = document.querySelectorAll('.fullscreen-container, .fullscreen-mode, [class*="fullscreen"]');
const wasFullscreen = isFullscreen;
if (douyinFullscreenElements.length > 0) {
isFullscreen = true;
} else {
// 检查视频尺寸
const videos = document.querySelectorAll('video');
for (const video of videos) {
const videoRect = video.getBoundingClientRect();
if (
videoRect.width > window.innerWidth * 0.9 &&
videoRect.height > window.innerHeight * 0.9
) {
isFullscreen = true;
break;
}
}
}
// 如果全屏状态发生变化
if (wasFullscreen !== isFullscreen) {
handleFullscreenChange();
}
// 在全屏状态下,检查是否需要显示控制界面
if (isFullscreen) {
// 如果最近有音量变化且控制界面不可见,则显示控制界面
const now = Date.now();
if (now - lastVolumeChangeTime < 500 && !controlsVisible) {
showControls();
}
}
}
// 处理全屏状态变化
function handleFullscreenChange() {
const wasFullscreen = isFullscreen;
updateFullscreenState();
// 如果全屏状态发生变化
if (wasFullscreen !== isFullscreen) {
fullscreenChangeCount++;
// 全屏时显示控制界面
if (isFullscreen) {
showControls();
// 更新浮动按钮状态
const floatButton = document.getElementById(`volume-booster-float-button-${scriptInstanceId}`);
if (floatButton) {
floatButton.classList.add('fullscreen');
}
} else {
// 退出全屏时,如果控制界面可见,更新其样式
const container = document.getElementById(`volume-booster-container-${scriptInstanceId}`);
if (container && container.classList.contains('visible')) {
container.classList.remove('fullscreen');
}
// 更新浮动按钮状态
const floatButton = document.getElementById(`volume-booster-float-button-${scriptInstanceId}`);
if (floatButton) {
floatButton.classList.remove('fullscreen');
}
}
}
}
// 更新全屏状态
function updateFullscreenState() {
// 检查多种全屏API
isFullscreen = !!(
document.fullscreenElement ||
document.webkitFullscreenElement ||
document.mozFullScreenElement ||
document.msFullscreenElement
);
// 备用检测方法:检查视频元素是否处于全屏状态
if (!isFullscreen) {
const videos = document.querySelectorAll('video');
for (const video of videos) {
// 检查视频尺寸是否接近屏幕尺寸
const videoRect = video.getBoundingClientRect();
if (
videoRect.width > window.innerWidth * 0.9 &&
videoRect.height > window.innerHeight * 0.9
) {
isFullscreen = true;
break;
}
}
}
// 抖音平台特殊处理:检查抖音特有的全屏元素
if (!isFullscreen && isDouyin) {
const douyinFullscreenElements = document.querySelectorAll('.fullscreen-container, .fullscreen-mode, [class*="fullscreen"]');
if (douyinFullscreenElements.length > 0) {
isFullscreen = true;
}
}
}
// 定期检查全屏状态(备用方法)
function checkFullscreenState() {
const wasFullscreen = isFullscreen;
updateFullscreenState();
// 如果全屏状态发生变化
if (wasFullscreen !== isFullscreen) {
handleFullscreenChange();
}
// 在全屏状态下,检查是否需要显示控制界面
if (isFullscreen) {
// 如果最近有音量变化且控制界面不可见,则显示控制界面
const now = Date.now();
if (now - lastVolumeChangeTime < 500 && !controlsVisible) {
showControls();
}
}
}
// 应用音量增强
function applyVolumeBoost() {
// 获取所有媒体元素
mediaElements = [...document.querySelectorAll('video, audio')];
mediaElements.forEach(media => {
// 保存原始音量
if (media.dataset.originalVolume === undefined) {
media.dataset.originalVolume = media.volume;
}
// 应用或恢复音量
if (isBoosterActive) {
// 使用 AudioContext 增强音量
if (isDouyin) {
// 抖音特殊处理:使用直接音量调整方法
setupDouyinVolumeBoost(media);
} else {
setupAudioBooster(media);
}
} else {
// 恢复原始音量
if (media.dataset.originalVolume !== undefined) {
media.volume = parseFloat(media.dataset.originalVolume);
}
// 断开音频处理节点
if (media.boosterSource) {
try {
media.boosterSource.disconnect();
media.boosterGain.disconnect();
media.boosterSource = null;
media.boosterGain = null;
media.boosterContext = null;
} catch (e) {
console.log('断开音频节点失败:', e);
}
}
}
});
}
// 抖音平台特殊处理:强制更新所有视频元素的音量
function forceUpdateDouyinVolume() {
const videos = document.querySelectorAll('video');
videos.forEach(video => {
if (isBoosterActive) {
// 强制应用音量增强
const originalVolume = parseFloat(video.dataset.originalVolume) || 0.5;
// 使用直接设置方法
try {
// 先设置为不同的值,再设置为目标值,确保触发更新
video.volume = Math.min(1.0, originalVolume * 0.99);
setTimeout(() => {
video.volume = Math.min(1.0, originalVolume * volumeMultiplier);
}, 10);
} catch (e) {
console.log('抖音强制更新音量失败:', e);
}
// 尝试使用播放器API
try {
if (video.setVolume) {
video.setVolume(Math.min(1.0, originalVolume * volumeMultiplier));
}
} catch (e) {
console.log('抖音播放器API调用失败:', e);
}
} else {
// 恢复原始音量
if (video.dataset.originalVolume !== undefined) {
video.volume = parseFloat(video.dataset.originalVolume);
}
}
});
// 标记抖音音量已应用
douyinVolumeApplied = true;
}
// 抖音平台特殊处理
function setupDouyinVolumeBoost(mediaElement) {
// 清除之前可能存在的音频处理节点
if (mediaElement.boosterSource) {
try {
mediaElement.boosterSource.disconnect();
mediaElement.boosterGain.disconnect();
mediaElement.boosterSource = null;
mediaElement.boosterGain = null;
mediaElement.boosterContext = null;
} catch (e) {
console.log('断开抖音音频节点失败:', e);
}
}
// 对抖音使用直接音量调整方法
try {
const originalVolume = parseFloat(mediaElement.dataset.originalVolume) || 0.5;
// 确保不超过1.0
mediaElement.volume = Math.min(1.0, originalVolume * volumeMultiplier);
// 监听音量变化,防止被平台重置
if (!mediaElement._volumeWatcherSet) {
// 保存原始的音量设置方法
if (!mediaElement._originalSetVolume && mediaElement.setVolume) {
mediaElement._originalSetVolume = mediaElement.setVolume;
// 重写setVolume方法
mediaElement.setVolume = function(val) {
// 保存原始音量
this.dataset.originalVolume = val;
// 如果增强器激活,应用增强
if (isBoosterActive) {
this._originalSetVolume.call(this, Math.min(1.0, val * volumeMultiplier));
} else {
this._originalSetVolume.call(this, val);
}
};
}
// 重写volume属性
const volumeDescriptor = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'volume');
if (volumeDescriptor) {
const originalSetter = volumeDescriptor.set;
Object.defineProperty(mediaElement, 'volume', {
get: function() {
return volumeDescriptor.get.call(this);
},
set: function(val) {
// 如果是我们自己设置的值,直接通过
if (this._isSettingVolume) {
originalSetter.call(this, val);
return;
}
// 如果是外部设置的值,记录为原始音量
this.dataset.originalVolume = val;
// 如果增强器激活,应用增强
if (isBoosterActive) {
this._isSettingVolume = true;
originalSetter.call(this, Math.min(1.0, val * volumeMultiplier));
this._isSettingVolume = false;
} else {
originalSetter.call(this, val);
}
},
configurable: true
});
mediaElement._volumeWatcherSet = true;
}
}
// 应用音量
mediaElement._isSettingVolume = true;
mediaElement.volume = Math.min(1.0, originalVolume * volumeMultiplier);
mediaElement._isSettingVolume = false;
// 尝试使用播放器API
if (mediaElement.setVolume) {
mediaElement.setVolume(Math.min(1.0, originalVolume * volumeMultiplier));
}
} catch (err) {
console.log('抖音音量调整失败:', err);
// 备用方法:尝试使用AudioContext
try {
setupAudioBooster(mediaElement);
} catch (e) {
console.log('抖音备用音量调整失败:', e);
}
}
}
// 使用 AudioContext 设置音量增强
function setupAudioBooster(mediaElement) {
// 如果已经有音频上下文,先清理
if (mediaElement.boosterContext) {
try {
mediaElement.boosterSource.disconnect();
mediaElement.boosterGain.disconnect();
mediaElement.boosterSource = null;
mediaElement.boosterGain = null;
mediaElement.boosterContext = null;
} catch (e) {
console.log('重置音频节点失败:', e);
}
}
// 创建新的音频处理节点
try {
const AudioContext = window.AudioContext || window.webkitAudioContext;
const context = new AudioContext();
const source = context.createMediaElementSource(mediaElement);
const gainNode = context.createGain();
source.connect(gainNode);
gainNode.connect(context.destination);
mediaElement.boosterContext = context;
mediaElement.boosterSource = source;
mediaElement.boosterGain = gainNode;
// 设置增益值
gainNode.gain.value = volumeMultiplier;
} catch (e) {
console.log('创建音频处理节点失败:', e);
// 备用方案:直接修改音量属性
try {
const originalVolume = parseFloat(mediaElement.dataset.originalVolume) || 0.5;
// 确保不超过1.0
mediaElement.volume = Math.min(1.0, originalVolume * volumeMultiplier);
} catch (err) {
console.log('备用音量调整失败:', err);
}
}
}
// 增加音量
function increaseVolume() {
// 如果未启用,则启用
if (!isBoosterActive) {
enableBooster();
}
// 增加音量倍数
volumeMultiplier = Math.min(10, volumeMultiplier + 0.1);
GM_setValue('volumeMultiplier', volumeMultiplier);
// 更新滑块
if (document.getElementById(`volume-slider-${scriptInstanceId}`)) {
document.getElementById(`volume-slider-${scriptInstanceId}`).value = volumeMultiplier;
document.getElementById(`volume-value-${scriptInstanceId}`).textContent = `增强倍数: ${volumeMultiplier.toFixed(1)}x`;
document.getElementById(`volume-slider-${scriptInstanceId}`).style.background = `linear-gradient(to right, #4CAF50 0%, #4CAF50 ${(volumeMultiplier-1)*100/9}%, #ddd ${(volumeMultiplier-1)*100/9}%, #ddd 100%)`;
}
// 记录最后一次音量变化时间
lastVolumeChangeTime = Date.now();
applyVolumeBoost();
showControls();
// 抖音平台特殊处理:强制更新所有视频元素的音量
if (isDouyin) {
forceUpdateDouyinVolume();
}
// 显示状态提示
showStatusMessage(`音量增强: ${volumeMultiplier.toFixed(1)}x`);
}
// 减少音量
function decreaseVolume() {
// 减少音量倍数
volumeMultiplier = Math.max(1, volumeMultiplier - 0.1);
GM_setValue('volumeMultiplier', volumeMultiplier);
// 更新滑块
if (document.getElementById(`volume-slider-${scriptInstanceId}`)) {
document.getElementById(`volume-slider-${scriptInstanceId}`).value = volumeMultiplier;
document.getElementById(`volume-value-${scriptInstanceId}`).textContent = `增强倍数: ${volumeMultiplier.toFixed(1)}x`;
document.getElementById(`volume-slider-${scriptInstanceId}`).style.background = `linear-gradient(to right, #4CAF50 0%, #4CAF50 ${(volumeMultiplier-1)*100/9}%, #ddd ${(volumeMultiplier-1)*100/9}%, #ddd 100%)`;
}
// 记录最后一次音量变化时间
lastVolumeChangeTime = Date.now();
applyVolumeBoost();
showControls();
// 抖音平台特殊处理:强制更新所有视频元素的音量
if (isDouyin) {
forceUpdateDouyinVolume();
}
// 显示状态提示
showStatusMessage(`音量增强: ${volumeMultiplier.toFixed(1)}x`);
}
// 启用音量增强器
function enableBooster() {
isBoosterActive = true;
GM_setValue('isBoosterActive', true);
if (document.getElementById(`volume-toggle-${scriptInstanceId}`)) {
document.getElementById(`volume-toggle-${scriptInstanceId}`).textContent = '已启用';
document.getElementById(`volume-toggle-${scriptInstanceId}`).className = '';
document.getElementById(`volume-booster-container-${scriptInstanceId}`).className = isFullscreen ? 'visible active fullscreen' : 'visible active';
}
applyVolumeBoost();
// 抖音平台特殊处理:强制更新所有视频元素的音量
if (isDouyin) {
forceUpdateDouyinVolume();
}
showStatusMessage('音量增强已启用');
}
// 禁用音量增强器
function disableBooster() {
isBoosterActive = false;
GM_setValue('isBoosterActive', false);
if (document.getElementById(`volume-toggle-${scriptInstanceId}`)) {
document.getElementById(`volume-toggle-${scriptInstanceId}`).textContent = '已禁用';
document.getElementById(`volume-toggle-${scriptInstanceId}`).className = 'inactive';
document.getElementById(`volume-booster-container-${scriptInstanceId}`).className = isFullscreen ? 'visible inactive fullscreen' : 'visible inactive';
}
applyVolumeBoost(); // 这会恢复原始音量
// 抖音平台特殊处理:强制更新所有视频元素的音量
if (isDouyin) {
forceUpdateDouyinVolume();
}
showStatusMessage('音量增强已禁用');
}
// 切换音量增强器状态
function toggleBooster() {
if (isBoosterActive) {
disableBooster();
} else {
enableBooster();
}
}
// 切换控制面板显示状态
function toggleControlPanel() {
if (controlsVisible) {
hideControls();
} else {
showControls();
}
}
// 显示悬浮图标
function showFloatingIcon() {
const floatButton = document.getElementById(`volume-booster-float-button-${scriptInstanceId}`);
if (floatButton) {
floatButton.classList.remove('hidden');
iconVisible = true;
GM_setValue('iconVisible', true);
showStatusMessage('图标已显示');
} else {
// 如果图标不存在,创建它
addFloatingButton();
iconVisible = true;
GM_setValue('iconVisible', true);
showStatusMessage('图标已显示');
}
}
// 隐藏悬浮图标
function hideFloatingIcon() {
const floatButton = document.getElementById(`volume-booster-float-button-${scriptInstanceId}`);
if (floatButton) {
floatButton.classList.add('hidden');
iconVisible = false;
GM_setValue('iconVisible', false);
showStatusMessage('图标已隐藏,双击右键可显示');
}
}
// 清理脚本实例
function cleanupScriptInstance() {
// 清理UI元素
const elementsToRemove = [
document.getElementById(`volume-booster-container-${scriptInstanceId}`),
document.getElementById(`volume-status-indicator-${scriptInstanceId}`),
document.getElementById(`volume-booster-float-button-${scriptInstanceId}`)
];
elementsToRemove.forEach(element => {
if (element && element.parentNode) {
element.parentNode.removeChild(element);
}
});
// 清理事件监听
eventListeners.forEach(listener => {
if (listener.target) {
listener.target.removeEventListener(listener.type, listener.handler, listener.options);
} else {
document.removeEventListener(listener.type, listener.handler, listener.options);
}
});
// 清理定时器
intervalTimers.forEach(timer => {
clearInterval(timer);
});
// 断开媒体观察器
if (mediaObserver) {
mediaObserver.disconnect();
}
// 恢复媒体元素原始音量
mediaElements.forEach(media => {
if (media.dataset.originalVolume !== undefined) {
try {
media.volume = parseFloat(media.dataset.originalVolume);
// 断开音频处理节点
if (media.boosterSource) {
media.boosterSource.disconnect();
media.boosterGain.disconnect();
media.boosterSource = null;
media.boosterGain = null;
media.boosterContext = null;
}
} catch (e) {
console.log('恢复音量失败:', e);
}
}
});
}
// 热重载脚本
function hotReloadScript() {
// 显示状态提示
showStatusMessage('正在重新加载脚本...');
// 保存当前状态
const currentState = {
volumeMultiplier: volumeMultiplier,
isBoosterActive: isBoosterActive,
iconVisible: iconVisible,
iconPosition: iconPosition
};
// 将状态存储到临时变量
window._volumeBoosterTempState = currentState;
// 清理旧的脚本实例
const oldInstances = window._volumeBoosterInstances || [];
oldInstances.forEach(instanceId => {
if (instanceId !== scriptInstanceId) {
// 移除旧实例的UI元素
const elementsToRemove = [
document.getElementById(`volume-booster-container-${instanceId}`),
document.getElementById(`volume-status-indicator-${instanceId}`),
document.getElementById(`volume-booster-float-button-${instanceId}`)
];
elementsToRemove.forEach(element => {
if (element && element.parentNode) {
element.parentNode.removeChild(element);
}
});
}
});
// 清理当前实例
cleanupScriptInstance();
// 重置实例列表,只保留当前实例
window._volumeBoosterInstances = [scriptInstanceId];
// 重新初始化脚本
setTimeout(() => {
// 恢复之前保存的状态
if (window._volumeBoosterTempState) {
volumeMultiplier = window._volumeBoosterTempState.volumeMultiplier;
isBoosterActive = window._volumeBoosterTempState.isBoosterActive;
iconVisible = window._volumeBoosterTempState.iconVisible;
iconPosition = window._volumeBoosterTempState.iconPosition;
// 保存到GM存储
GM_setValue('volumeMultiplier', volumeMultiplier);
GM_setValue('isBoosterActive', isBoosterActive);
GM_setValue('iconVisible', iconVisible);
GM_setValue('iconPosition', iconPosition);
// 清理临时状态
delete window._volumeBoosterTempState;
}
initializeVolumeBooster();
showStatusMessage('脚本已重新加载');
}, 100);
}
// 增强的快捷键检测函数
function isUpArrowWithAlt(e) {
// 多重检测方法确保兼容性
return e.altKey && (
e.keyCode === 38 ||
e.which === 38 ||
e.code === 'ArrowUp' ||
e.key === 'ArrowUp' ||
e.key === 'Up' ||
e.key === '↑'
);
}
function isDownArrowWithAlt(e) {
return e.altKey && (
e.keyCode === 40 ||
e.which === 40 ||
e.code === 'ArrowDown' ||
e.key === 'ArrowDown' ||
e.key === 'Down' ||
e.key === '↓'
);
}
function isSKeyWithAlt(e) {
return e.altKey && (
e.keyCode === 83 ||
e.which === 83 ||
e.code === 'KeyS' ||
e.key === 's' ||
e.key === 'S'
);
}
function is5KeyWithAlt(e) {
return e.altKey && (
e.keyCode === 53 ||
e.which === 53 ||
e.code === 'Digit5' ||
e.key === '5'
);
}
// 设置键盘监听
function setupKeyboardListeners() {
// 主要监听方法 - keydown
const keydownHandler = function(e) {
const now = Date.now();
if (now - lastKeyTime < 200) return;
if (isUpArrowWithAlt(e)) {
e.preventDefault();
e.stopPropagation();
lastKeyTime = now;
increaseVolume();
return false;
}
if (isDownArrowWithAlt(e)) {
e.preventDefault();
e.stopPropagation();
lastKeyTime = now;
decreaseVolume();
return false;
}
if (isSKeyWithAlt(e)) {
e.preventDefault();
e.stopPropagation();
lastKeyTime = now;
toggleBooster();
return false;
}
if (is5KeyWithAlt(e)) {
e.preventDefault();
e.stopPropagation();
lastKeyTime = now;
hotReloadScript();
return false;
}
// 检测Alt键 + V键 (显示/隐藏控制面板)
if (e.altKey && (e.keyCode === 86 || e.which === 86 || e.code === 'KeyV' || e.key === 'v' || e.key === 'V')) {
e.preventDefault();
e.stopPropagation();
toggleControlPanel();
return false;
}
};
document.addEventListener('keydown', keydownHandler, true);
eventListeners.push({ type: 'keydown', handler: keydownHandler, options: true });
// 备用监听方法 - keypress (用于某些浏览器)
const keypressHandler = function(e) {
const now = Date.now();
if (now - lastKeyTime < 200) return;
if (isUpArrowWithAlt(e) || isDownArrowWithAlt(e) || isSKeyWithAlt(e) || is5KeyWithAlt(e)) {
e.preventDefault();
e.stopPropagation();
return false;
}
};
document.addEventListener('keypress', keypressHandler, true);
eventListeners.push({ type: 'keypress', handler: keypressHandler, options: true });
// 监听右键双击事件
const contextMenuHandler = function(e) {
const now = Date.now();
if (now - lastRightClickTime < 500) {
// 双击右键,显示悬浮图标
e.preventDefault();
showFloatingIcon();
lastRightClickTime = 0; // 重置,防止三击触发
} else {
lastRightClickTime = now;
}
};
document.addEventListener('contextmenu', contextMenuHandler, true);
eventListeners.push({ type: 'contextmenu', handler: contextMenuHandler, options: true });
}
// 监听新添加的媒体元素
function setupMediaObserver() {
mediaObserver = new MutationObserver(mutations => {
let mediaAdded = false;
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeName === 'VIDEO' || node.nodeName === 'AUDIO') {
mediaAdded = true;
} else if (node.nodeType === 1) {
const mediaElements = node.querySelectorAll('video, audio');
if (mediaElements.length > 0) {
mediaAdded = true;
}
}
});
});
if (mediaAdded && isBoosterActive) {
applyVolumeBoost();
// 抖音平台特殊处理:强制更新所有视频元素的音量
if (isDouyin) {
forceUpdateDouyinVolume();
}
}
});
// 开始观察DOM变化
if (document.body) {
mediaObserver.observe(document.body, {
childList: true,
subtree: true
});
}
return mediaObserver;
}
// 添加可拖动的悬浮音频图标
function addFloatingButton() {
const floatButton = document.createElement('div');
floatButton.id = `volume-booster-float-button-${scriptInstanceId}`;
floatButton.textContent = '🔊';
floatButton.title = '音量增强器 - 醉春风制作 (Alt+↑/↓ Alt+s Alt+5)';
// 设置初始位置
floatButton.style.left = `${iconPosition.x}px`;
floatButton.style.top = `${iconPosition.y}px`;
// 如果图标应该隐藏,添加隐藏类
if (!iconVisible) {
floatButton.classList.add('hidden');
}
// 如果当前是全屏状态,添加全屏类
if (isFullscreen) {
floatButton.classList.add('fullscreen');
}
// 点击显示控制面板
floatButton.addEventListener('click', function(e) {
if (!isDragging) {
showControls();
}
});
// 右键点击隐藏图标
floatButton.addEventListener('contextmenu', function(e) {
e.preventDefault();
hideFloatingIcon();
});
// 实现拖动功能
let startX, startY, startLeft, startTop;
floatButton.addEventListener('mousedown', function(e) {
// 只处理左键点击的拖动
if (e.button !== 0) return;
e.preventDefault();
isDragging = true;
// 记录初始位置
startX = e.clientX;
startY = e.clientY;
startLeft = parseInt(window.getComputedStyle(floatButton).left);
startTop = parseInt(window.getComputedStyle(floatButton).top);
// 添加鼠标移动和松开事件
document.addEventListener('mousemove', dragMove);
document.addEventListener('mouseup', dragEnd);
});
function dragMove(e) {
if (!isDragging) return;
// 计算新位置
const newLeft = startLeft + (e.clientX - startX);
const newTop = startTop + (e.clientY - startY);
// 限制在窗口内
const maxLeft = window.innerWidth - floatButton.offsetWidth;
const maxTop = window.innerHeight - floatButton.offsetHeight;
floatButton.style.left = `${Math.max(0, Math.min(maxLeft, newLeft))}px`;
floatButton.style.top = `${Math.max(0, Math.min(maxTop, newTop))}px`;
}
function dragEnd() {
if (!isDragging) return;
isDragging = false;
// 保存新位置
iconPosition = {
x: parseInt(floatButton.style.left),
y: parseInt(floatButton.style.top)
};
GM_setValue('iconPosition', iconPosition);
// 移除事件监听
document.removeEventListener('mousemove', dragMove);
document.removeEventListener('mouseup', dragEnd);
}
// 抖音平台特殊处理:将浮动按钮添加到顶层容器
if (isDouyin) {
const douyinContainer = document.querySelector('#root') || document.body;
douyinContainer.appendChild(floatButton);
} else {
document.body.appendChild(floatButton);
}
return floatButton;
}
// 特殊网站兼容性处理
function handleSpecialWebsites() {
// YouTube特殊处理
if (isYouTube) {
// 监听YouTube播放器变化
const ytInterval = setInterval(() => {
const ytPlayer = document.querySelector('video.html5-main-video');
if (ytPlayer && isBoosterActive && !ytPlayer.boosterContext) {
setupAudioBooster(ytPlayer);
}
// 检查是否处于全屏状态
updateFullscreenState();
}, 1000);
intervalTimers.push(ytInterval);
}
// 抖音特殊处理
if (isDouyin) {
// 监听抖音视频变化
const douyinInterval = setInterval(() => {
const douyinVideos = document.querySelectorAll('video');
if (douyinVideos.length > 0 && isBoosterActive) {
douyinVideos.forEach(video => {
setupDouyinVolumeBoost(video);
});
// 强制更新所有视频元素的音量
forceUpdateDouyinVolume();
}
// 检查是否处于全屏状态
updateFullscreenState();
// 确保控制界面在全屏状态下可见
if (isFullscreen && Date.now() - lastVolumeChangeTime < 500 && !controlsVisible) {
showControls();
}
}, 500);
intervalTimers.push(douyinInterval);
// 额外的抖音全屏检测
const douyinFullscreenInterval = setInterval(() => {
checkDouyinFullscreenState();
}, 300);
intervalTimers.push(douyinFullscreenInterval);
}
// TikTok特殊处理
if (isTikTok) {
// 监听TikTok视频变化
const ttInterval = setInterval(() => {
const tikTokVideos = document.querySelectorAll('video');
if (tikTokVideos.length > 0 && isBoosterActive) {
tikTokVideos.forEach(video => {
if (!video.boosterContext) {
setupAudioBooster(video);
}
});
}
// 检查是否处于全屏状态
updateFullscreenState();
}, 1000);
intervalTimers.push(ttInterval);
}
// 快手特殊处理
if (isKuaishou) {
// 监听快手视频变化
const ksInterval = setInterval(() => {
const kuaishouVideos = document.querySelectorAll('video');
if (kuaishouVideos.length > 0 && isBoosterActive) {
kuaishouVideos.forEach(video => {
if (!video.boosterContext) {
setupAudioBooster(video);
}
});
}
// 检查是否处于全屏状态
updateFullscreenState();
}, 1000);
intervalTimers.push(ksInterval);
}
// B站特殊处理
if (isBilibili) {
// 监听B站视频变化
const biliInterval = setInterval(() => {
const biliVideos = document.querySelectorAll('video');
if (biliVideos.length > 0 && isBoosterActive) {
biliVideos.forEach(video => {
if (!video.boosterContext) {
setupAudioBooster(video);
}
});
}
// 检查是否处于全屏状态
updateFullscreenState();
}, 1000);
intervalTimers.push(biliInterval);
}
// 西瓜视频特殊处理
if (isXigua) {
// 监听西瓜视频变化
const xiguaInterval = setInterval(() => {
const xiguaVideos = document.querySelectorAll('video');
if (xiguaVideos.length > 0 && isBoosterActive) {
xiguaVideos.forEach(video => {
if (!video.boosterContext) {
setupAudioBooster(video);
}
});
}
// 检查是否处于全屏状态
updateFullscreenState();
}, 1000);
intervalTimers.push(xiguaInterval);
}
// 通用视频处理(适用于所有网站)
const generalInterval = setInterval(() => {
const videos = document.querySelectorAll('video');
if (videos.length > 0 && isBoosterActive) {
videos.forEach(video => {
if (!video.boosterContext && !isDouyin) {
setupAudioBooster(video);
}
});
}
// 检查是否有音量变化,如果在全屏状态下且最近有音量变化,显示控制界面
if (isFullscreen && Date.now() - lastVolumeChangeTime < 500 && !controlsVisible) {
showControls();
}
}, 1000);
intervalTimers.push(generalInterval);
}
// 初始化音量增强器
function initializeVolumeBooster() {
// 创建控制界面但不显示
createVolumeControls();
// 设置键盘监听
setupKeyboardListeners();
// 设置媒体观察器
setupMediaObserver();
// 设置全屏检测
setupFullscreenDetection();
// 添加悬浮按钮
addFloatingButton();
// 处理特殊网站兼容性
handleSpecialWebsites();
// 应用音量增强
if (isBoosterActive) {
applyVolumeBoost();
// 抖音平台特殊处理:强制更新所有视频元素的音量
if (isDouyin) {
forceUpdateDouyinVolume();
}
}
// 定期检查并应用音量增强(处理动态加载的媒体)
const checkInterval = setInterval(() => {
if (isBoosterActive) {
applyVolumeBoost();
// 抖音平台特殊处理:强制更新所有视频元素的音量
if (isDouyin && !douyinVolumeApplied) {
forceUpdateDouyinVolume();
}
}
}, 2000);
intervalTimers.push(checkInterval);
// 初始检查全屏状态
updateFullscreenState();
}
// 如果页面已经加载完成,立即初始化
if (document.readyState === 'complete' || document.readyState === 'interactive') {
// 延迟一点以确保DOM已准备好
setTimeout(function() {
if (document.body) {
initializeVolumeBooster();
}
}, 100);
} else {
// 否则等待DOMContentLoaded事件
window.addEventListener('DOMContentLoaded', function() {
setTimeout(function() {
initializeVolumeBooster();
}, 100);
});
}
// 立即设置键盘监听,不等待页面加载
setupKeyboardListeners();
// 立即初始化,不等待页面加载完成
// 这样可以更早地捕获键盘事件和处理媒体元素
setTimeout(() => {
if (document.body) {
// 创建控制界面但不显示
createVolumeControls();
// 应用音量增强
applyVolumeBoost();
// 设置媒体观察器
setupMediaObserver();
// 设置全屏检测
setupFullscreenDetection();
}
}, 500);
})();