通用音量增强器 - Universal Volume Booster

在所有网站(包括YouTube、TikTok、抖音、快手)上强制增强音量,支持Alt+s快捷键启用/禁用,Alt+5热重载脚本,Alt+↑/↓调节音量

当前为 2025-05-24 提交的版本,查看 最新版本

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

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址