Greasy Fork

Greasy Fork is available in English.

通用音量增强器 - Universal Volume Booster

在所有网站(包括YouTube和TikTok)上强制增强音量,支持Ctrl+Alt+↑/↓/W/S快捷键,最高可增强10倍

// ==UserScript==
// @name         通用音量增强器 - Universal Volume Booster
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  在所有网站(包括YouTube和TikTok)上强制增强音量,支持Ctrl+Alt+↑/↓/W/S快捷键,最高可增强10倍
// @author       醉春风制作
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @run-at       document-start
// @license      All Rights Reserved
// ==/UserScript==

/*
通用音量增强器 - Universal Volume Booster
版本: 1.5
作者: 醉春风制作

版权所有 (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 = []; // 跟踪页面中的所有媒体元素
    
    // 创建音量增强控制界面
    function createVolumeControls() {
        // 添加样式
        GM_addStyle(`
            #volume-booster-container {
                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.visible {
                opacity: 1;
                pointer-events: auto;
            }
            #volume-booster-container.active {
                border: 2px solid #4CAF50;
            }
            #volume-booster-container.inactive {
                border: 2px solid #F44336;
            }
            #volume-slider-container {
                width: 200px;
                margin: 10px 0;
            }
            #volume-slider {
                width: 100%;
                height: 5px;
                -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;
            }
            #volume-slider::-webkit-slider-thumb {
                -webkit-appearance: none;
                width: 15px;
                height: 15px;
                border-radius: 50%;
                background: white;
                cursor: pointer;
                box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
            }
            #volume-slider::-moz-range-thumb {
                width: 15px;
                height: 15px;
                border-radius: 50%;
                background: white;
                cursor: pointer;
                box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
                border: none;
            }
            #volume-value {
                text-align: center;
                font-size: 14px;
                margin-top: 5px;
            }
            #volume-toggle {
                width: 100%;
                padding: 5px;
                background-color: #4CAF50;
                color: white;
                border: none;
                border-radius: 3px;
                cursor: pointer;
                font-size: 14px;
                margin-top: 5px;
            }
            #volume-toggle.inactive {
                background-color: #F44336;
            }
            .volume-booster-title {
                font-size: 14px;
                text-align: center;
                margin-bottom: 5px;
                font-weight: bold;
            }
            .volume-booster-shortcut {
                font-size: 12px;
                text-align: center;
                margin-top: 5px;
                color: #aaa;
            }
            #volume-status-indicator {
                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: 9999999;
                opacity: 0;
                transition: opacity 0.3s ease;
                pointer-events: none;
                text-align: center;
            }
            #volume-status-indicator.visible {
                opacity: 1;
            }
        `);
        
        // 创建控制界面
        const container = document.createElement('div');
        container.id = 'volume-booster-container';
        container.className = isBoosterActive ? 'active' : 'inactive';
        
        container.innerHTML = `
            <div class="volume-booster-title">音量增强器 - 醉春风制作</div>
            <div id="volume-slider-container">
                <input type="range" id="volume-slider" min="1" max="10" step="0.1" value="${volumeMultiplier}">
                <div id="volume-value">增强倍数: ${volumeMultiplier.toFixed(1)}x</div>
            </div>
            <button id="volume-toggle" class="${isBoosterActive ? '' : 'inactive'}">${isBoosterActive ? '已启用' : '已禁用'}</button>
            <div class="volume-booster-shortcut">快捷键: Ctrl+Alt+↑/↓ Ctrl+Alt+W/S</div>
        `;
        
        document.body.appendChild(container);
        
        // 创建状态指示器
        const statusIndicator = document.createElement('div');
        statusIndicator.id = 'volume-status-indicator';
        document.body.appendChild(statusIndicator);
        
        // 添加事件监听
        const slider = document.getElementById('volume-slider');
        const toggle = document.getElementById('volume-toggle');
        
        slider.addEventListener('input', function() {
            volumeMultiplier = parseFloat(this.value);
            document.getElementById('volume-value').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);
            
            applyVolumeBoost();
        });
        
        toggle.addEventListener('click', function() {
            toggleBooster();
        });
        
        // 鼠标悬停显示控制界面
        container.addEventListener('mouseenter', function() {
            clearTimeout(hideTimeout);
        });
        
        container.addEventListener('mouseleave', function() {
            if (!isDragging) {
                hideTimeout = setTimeout(() => {
                    hideControls();
                }, 2000);
            }
        });
        
        return container;
    }
    
    // 显示状态消息
    function showStatusMessage(message) {
        const statusIndicator = document.getElementById('volume-status-indicator');
        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')) {
            createVolumeControls();
        }
        
        const container = document.getElementById('volume-booster-container');
        container.classList.add('visible');
        controlsVisible = true;
        
        hideTimeout = setTimeout(() => {
            hideControls();
        }, 3000);
    }
    
    // 隐藏控制界面
    function hideControls() {
        if (!isDragging) {
            const container = document.getElementById('volume-booster-container');
            if (container) {
                container.classList.remove('visible');
                controlsVisible = false;
            }
        }
    }
    
    // 应用音量增强
    function applyVolumeBoost() {
        // 获取所有媒体元素
        mediaElements = [...document.querySelectorAll('video, audio')];
        
        // 特殊处理YouTube和TikTok
        const isYouTube = window.location.hostname.includes('youtube.com');
        const isTikTok = window.location.hostname.includes('tiktok.com');
        
        mediaElements.forEach(media => {
            // 保存原始音量
            if (media.dataset.originalVolume === undefined) {
                media.dataset.originalVolume = media.volume;
            }
            
            // 应用或恢复音量
            if (isBoosterActive) {
                // 使用 AudioContext 增强音量
                setupAudioBooster(media, isYouTube, isTikTok);
            } 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);
                    }
                }
            }
        });
    }
    
    // 使用 AudioContext 设置音量增强
    function setupAudioBooster(mediaElement, isYouTube, isTikTok) {
        if (!mediaElement.boosterContext) {
            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;
            } catch (e) {
                console.log('创建音频处理节点失败:', e);
                // 备用方案:直接修改音量属性
                if (isYouTube || isTikTok) {
                    // 对于YouTube和TikTok,使用备用方案
                    try {
                        const originalVolume = parseFloat(mediaElement.dataset.originalVolume) || 0.5;
                        // 确保不超过1.0
                        mediaElement.volume = Math.min(1.0, originalVolume * volumeMultiplier);
                    } catch (err) {
                        console.log('备用音量调整失败:', err);
                    }
                }
                return;
            }
        }
        
        if (mediaElement.boosterGain) {
            mediaElement.boosterGain.gain.value = volumeMultiplier;
        }
    }
    
    // 增加音量
    function increaseVolume() {
        // 如果未启用,则启用
        if (!isBoosterActive) {
            enableBooster();
        }
        
        // 增加音量倍数
        volumeMultiplier = Math.min(10, volumeMultiplier + 0.1);
        GM_setValue('volumeMultiplier', volumeMultiplier);
        
        // 更新滑块
        if (document.getElementById('volume-slider')) {
            document.getElementById('volume-slider').value = volumeMultiplier;
            document.getElementById('volume-value').textContent = `增强倍数: ${volumeMultiplier.toFixed(1)}x`;
            document.getElementById('volume-slider').style.background = `linear-gradient(to right, #4CAF50 0%, #4CAF50 ${(volumeMultiplier-1)*100/9}%, #ddd ${(volumeMultiplier-1)*100/9}%, #ddd 100%)`;
        }
        
        applyVolumeBoost();
        showControls();
        
        // 显示状态提示
        showStatusMessage(`音量增强: ${volumeMultiplier.toFixed(1)}x`);
    }
    
    // 减少音量
    function decreaseVolume() {
        // 减少音量倍数
        volumeMultiplier = Math.max(1, volumeMultiplier - 0.1);
        GM_setValue('volumeMultiplier', volumeMultiplier);
        
        // 更新滑块
        if (document.getElementById('volume-slider')) {
            document.getElementById('volume-slider').value = volumeMultiplier;
            document.getElementById('volume-value').textContent = `增强倍数: ${volumeMultiplier.toFixed(1)}x`;
            document.getElementById('volume-slider').style.background = `linear-gradient(to right, #4CAF50 0%, #4CAF50 ${(volumeMultiplier-1)*100/9}%, #ddd ${(volumeMultiplier-1)*100/9}%, #ddd 100%)`;
        }
        
        applyVolumeBoost();
        showControls();
        
        // 显示状态提示
        showStatusMessage(`音量增强: ${volumeMultiplier.toFixed(1)}x`);
    }
    
    // 启用音量增强器
    function enableBooster() {
        isBoosterActive = true;
        GM_setValue('isBoosterActive', true);
        
        if (document.getElementById('volume-toggle')) {
            document.getElementById('volume-toggle').textContent = '已启用';
            document.getElementById('volume-toggle').className = '';
            document.getElementById('volume-booster-container').className = 'visible active';
        }
        
        applyVolumeBoost();
        showStatusMessage('音量增强已启用');
    }
    
    // 禁用音量增强器
    function disableBooster() {
        isBoosterActive = false;
        GM_setValue('isBoosterActive', false);
        
        if (document.getElementById('volume-toggle')) {
            document.getElementById('volume-toggle').textContent = '已禁用';
            document.getElementById('volume-toggle').className = 'inactive';
            document.getElementById('volume-booster-container').className = 'visible inactive';
        }
        
        applyVolumeBoost(); // 这会恢复原始音量
        showStatusMessage('音量增强已禁用');
    }
    
    // 切换音量增强器状态
    function toggleBooster() {
        if (isBoosterActive) {
            disableBooster();
        } else {
            enableBooster();
        }
    }
    
    // 切换控制面板显示状态
    function toggleControlPanel() {
        if (controlsVisible) {
            hideControls();
        } else {
            showControls();
        }
    }
    
    // 增强的快捷键检测函数
    function isUpArrowWithCtrlAlt(e) {
        // 多重检测方法确保兼容性
        return e.ctrlKey && e.altKey && (
            e.keyCode === 38 || 
            e.which === 38 || 
            e.code === 'ArrowUp' || 
            e.key === 'ArrowUp' || 
            e.key === 'Up' ||
            e.key === '↑'
        );
    }
    
    function isDownArrowWithCtrlAlt(e) {
        return e.ctrlKey && e.altKey && (
            e.keyCode === 40 || 
            e.which === 40 || 
            e.code === 'ArrowDown' || 
            e.key === 'ArrowDown' || 
            e.key === 'Down' ||
            e.key === '↓'
        );
    }
    
    function isWKeyWithCtrlAlt(e) {
        return e.ctrlKey && e.altKey && (
            e.keyCode === 87 ||
            e.which === 87 ||
            e.code === 'KeyW' ||
            e.key === 'w' ||
            e.key === 'W'
        );
    }
    
    function isSKeyWithCtrlAlt(e) {
        return e.ctrlKey && e.altKey && (
            e.keyCode === 83 ||
            e.which === 83 ||
            e.code === 'KeyS' ||
            e.key === 's' ||
            e.key === 'S'
        );
    }
    
    // 设置键盘监听
    function setupKeyboardListeners() {
        document.addEventListener('keydown', function(e) {
            const now = Date.now();
            if (now - lastKeyTime < 200) return;
            
            if (isUpArrowWithCtrlAlt(e)) {
                e.preventDefault();
                e.stopPropagation();
                lastKeyTime = now;
                increaseVolume();
                return false;
            }
            
            if (isDownArrowWithCtrlAlt(e)) {
                e.preventDefault();
                e.stopPropagation();
                lastKeyTime = now;
                decreaseVolume();
                return false;
            }
            
            if (isWKeyWithCtrlAlt(e)) {
                e.preventDefault();
                e.stopPropagation();
                lastKeyTime = now;
                enableBooster();
                return false;
            }
            
            if (isSKeyWithCtrlAlt(e)) {
                e.preventDefault();
                e.stopPropagation();
                lastKeyTime = now;
                disableBooster();
                return false;
            }
        }, true);
    }
    
    // 监听新添加的媒体元素
    function setupMediaObserver() {
        const observer = 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();
            }
        });
        
        // 开始观察DOM变化
        if (document.body) {
            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        }
        
        return observer;
    }
    
    // 初始化音量增强器
    function initializeVolumeBooster() {
        // 创建控制界面但不显示
        createVolumeControls();
        
        // 设置键盘监听
        setupKeyboardListeners();
        
        // 设置媒体观察器
        setupMediaObserver();
        
        // 应用音量增强
        if (isBoosterActive) {
            applyVolumeBoost();
        }
    }
    
    // 如果页面已经加载完成,立即初始化
    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();
})();