您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
可辨識的撥放器,滾輪、013速度28音量5播放暫停enter全螢幕切換可能有。整合YouTube、B站(0123456789enter)、B站直播(局部:012358/無滾輪enter)
当前为
// ==UserScript== // @name 滾動音量Dx版 // @namespace http://tampermonkey.net/ // @version 4.3 // @description 可辨識的撥放器,滾輪、013速度28音量5播放暫停enter全螢幕切換可能有。整合YouTube、B站(0123456789enter)、B站直播(局部:012358/無滾輪enter) // @match *://*/* // @exclude *://www.facebook.com/* // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @license MIT // ==/UserScript== (function() { 'use strict'; const PLATFORM = { BILIBILI: /bilibili\.com/.test(location.hostname), YOUTUBE: /youtube\.com/.test(location.hostname), GENERIC: true }; const DEBOUNCE_INTERVAL = 100; const FLYWHEEL_THRESHOLD = 10; const TRADITIONAL_THRESHOLD = 40; const VOLUME_STEP = 10; const VOLUME_DISPLAY = { 'zh-TW': vol => `${vol}%`, 'zh-CN': vol => `${vol}%`, 'en': vol => `${vol}%`, 'default': vol => `${vol}%` }; const KEYCODE_MAP = { SPACE: 32, BRACKET_LEFT: 219, BRACKET_RIGHT: 221, KEY_F: 70, DIGIT_0: 48, DIGIT_1: 49, DIGIT_2: 50, DIGIT_3: 51, DIGIT_4: 52, DIGIT_5: 53, DIGIT_6: 54, DIGIT_7: 55, DIGIT_8: 56, DIGIT_9: 57, NUMPAD_0: 96, NUMPAD_1: 97, NUMPAD_2: 98, NUMPAD_3: 99, NUMPAD_4: 100, NUMPAD_5: 101, NUMPAD_6: 102, NUMPAD_7: 103, NUMPAD_8: 104, NUMPAD_9: 105, NUMPAD_ENTER: 13 }; const state = { volumeAccumulator: 0, lastVolumeChangeTime: 0, volumeTimeoutId: null, isMouseOverVideo: false, lastCustomRate: 1.0, cachedElements: { video: null, playerWrap: null, lastCheckTime: 0 }, isGlobalMatchEnabled: GM_getValue('isGlobalMatchEnabled', true), customMatches: JSON.parse(GM_getValue('customMatches', '[]')), customExcludes: JSON.parse(GM_getValue('customExcludes', '[]')) }; function getVideoElement() { const now = Date.now(); if (!state.cachedElements.video || now - state.cachedElements.lastCheckTime > 30000) { if (PLATFORM.BILIBILI) { state.cachedElements.video = document.querySelector('.bpx-player-video-wrap video'); } if (PLATFORM.YOUTUBE) { state.cachedElements.video = document.querySelector('ytd-player video'); } if (!state.cachedElements.video) { state.cachedElements.video = document.querySelector('video'); } state.cachedElements.lastCheckTime = now; } return state.cachedElements.video; } function getYTPlayer() { return document.querySelector('ytd-player')?.getPlayer(); } function createVolumeDisplay() { const existingDisplay = document.getElementById("dynamic-volume-display"); if (existingDisplay) return existingDisplay; const display = document.createElement("div"); display.id = "dynamic-volume-display"; Object.assign(display.style, { position: 'fixed', zIndex: '99999', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', padding: '10px 20px', borderRadius: '8px', backgroundColor: 'rgba(0, 0, 0, 0.7)', color: '#fff', fontSize: '24px', fontFamily: 'Arial, sans-serif', opacity: '0', transition: 'opacity 1s', pointerEvents: 'none' }); document.body.appendChild(display); return display; } function showVolume(vol) { clearTimeout(state.volumeTimeoutId); const display = createVolumeDisplay(); const langKey = VOLUME_DISPLAY[navigator.language] ? navigator.language : 'default'; display.textContent = VOLUME_DISPLAY[langKey](Math.round(vol)); display.style.opacity = '1'; state.volumeTimeoutId = setTimeout(() => { display.style.opacity = '0'; }, 1000); } function adjustVolume(deltaY, isWheelEvent = true) { const now = Date.now(); if (isWheelEvent && now - state.lastVolumeChangeTime < DEBOUNCE_INTERVAL) return; const absDelta = Math.abs(deltaY); let shouldTrigger = false; if (absDelta < TRADITIONAL_THRESHOLD) { state.volumeAccumulator += absDelta; shouldTrigger = state.volumeAccumulator >= FLYWHEEL_THRESHOLD; } else { shouldTrigger = true; } if (shouldTrigger) { if (PLATFORM.YOUTUBE) { const player = getYTPlayer(); if (!player) return; let volume = player.getVolume(); volume += (deltaY > 0 ? -VOLUME_STEP : VOLUME_STEP); volume = Math.max(0, Math.min(100, volume)); player.setVolume(volume); showVolume(volume); } else { const video = getVideoElement(); if (!video) return; let volume = video.volume * 100; volume += (deltaY > 0 ? -VOLUME_STEP : VOLUME_STEP); volume = Math.max(0, Math.min(100, volume)); video.volume = volume / 100; showVolume(volume); } state.volumeAccumulator = 0; state.lastVolumeChangeTime = now; } } function adjustRate(changeValue) { const video = getVideoElement(); if (!video) return; const newRate = Math.max(0.1, Math.min(video.playbackRate + changeValue, 16)); video.playbackRate = parseFloat(newRate.toFixed(1)); state.lastCustomRate = newRate; showVolume(newRate * 100); } function togglePlaybackRate() { const video = getVideoElement(); if (!video) return; if (video.playbackRate === 1) { video.playbackRate = state.lastCustomRate > 1 ? state.lastCustomRate : 1.3; } else { video.playbackRate = 1; } showVolume(video.playbackRate * 100); } function handleYTNavigation(key) { const player = getYTPlayer(); if (!player) return; const video = getVideoElement(); if (!video) return; const actions = { '4': () => video.currentTime -= 10, '6': () => video.currentTime += 10, '7': () => document.querySelector('.ytp-prev-button')?.click(), '9': () => document.querySelector('.ytp-next-button')?.click() }; if (actions[key]) { actions[key](); return true; } return false; } function handleWheelEvent(e) { if (PLATFORM.YOUTUBE) { const playerWrap = document.querySelector('ytd-player'); if (playerWrap && playerWrap.contains(e.target)) { adjustVolume(e.deltaY); e.preventDefault(); } return; } if (PLATFORM.BILIBILI) { const playerWrap = document.querySelector('.bpx-player-video-wrap'); if (playerWrap && playerWrap.contains(e.target)) { adjustVolume(e.deltaY); e.preventDefault(); } return; } if (state.isMouseOverVideo && checkDomainMatch()) { adjustVolume(e.deltaY); e.preventDefault(); e.stopPropagation(); } } function handleMouseEnter() { if (!checkDomainMatch()) return; state.isMouseOverVideo = true; } function handleMouseLeave() { state.isMouseOverVideo = false; } function handleMainKeyEvent(e) { if (PLATFORM.YOUTUBE && ['4','6','7','9'].includes(e.key) && handleYTNavigation(e.key)) { e.preventDefault(); return; } const video = getVideoElement(); if (!video || isInputElement(e.target)) return; const keyActions = { 'Space': () => video[video.paused ? 'play' : 'pause'](), 'Numpad5': () => video[video.paused ? 'play' : 'pause'](), 'NumpadEnter': () => simulateKeyEvent(KEYCODE_MAP.KEY_F, 'f'), 'Digit0': () => PLATFORM.BILIBILI && (video.currentTime = 0), 'Digit1': () => PLATFORM.BILIBILI && (video.currentTime = video.duration * 0.1), 'Digit2': () => PLATFORM.BILIBILI && (video.currentTime = video.duration * 0.2), 'Digit3': () => PLATFORM.BILIBILI && (video.currentTime = video.duration * 0.3), 'Digit4': () => PLATFORM.BILIBILI && (video.currentTime = video.duration * 0.4), 'Digit5': () => PLATFORM.BILIBILI && (video.currentTime = video.duration * 0.5), 'Digit6': () => PLATFORM.BILIBILI && (video.currentTime = video.duration * 0.6), 'Digit7': () => PLATFORM.BILIBILI && (video.currentTime = video.duration * 0.7), 'Digit8': () => PLATFORM.BILIBILI && (video.currentTime = video.duration * 0.8), 'Digit9': () => PLATFORM.BILIBILI && (video.currentTime = video.duration * 0.9), 'Numpad7': () => PLATFORM.BILIBILI && simulateKeyEvent(KEYCODE_MAP.BRACKET_LEFT, '['), 'Numpad9': () => PLATFORM.BILIBILI && simulateKeyEvent(KEYCODE_MAP.BRACKET_RIGHT, ']'), 'Numpad0': () => togglePlaybackRate(), 'Numpad1': () => adjustRate(-0.1), 'Numpad3': () => adjustRate(0.1), 'Numpad8': () => adjustVolume(-VOLUME_STEP, false), 'Numpad2': () => adjustVolume(VOLUME_STEP, false) }; const action = keyActions[e.code]; if (action) { action(); e.preventDefault(); e.stopPropagation(); } } function simulateKeyEvent(keyCode, key = '') { const event = new KeyboardEvent('keydown', { key, code: key, keyCode, bubbles: true, cancelable: true }); document.dispatchEvent(event); } function isInputElement(target) { return /^(input|textarea|\[contenteditable\])$/i.test(target.tagName) || target.isContentEditable; } function parseDomainPattern(pattern) { try { const url = new URL(pattern.replace(/\*/g, 'wildcard')); return pattern.replace(/\./g, '\\.').replace(/wildcard/g, '.*'); } catch { return pattern.replace(/\./g, '\\.').replace(/\*/g, '.*'); } } function checkDomainMatch() { if (!state.isGlobalMatchEnabled) { return state.customMatches.some(match => new RegExp(parseDomainPattern(match)).test(location.href)); } return !state.customExcludes.some(exclude => new RegExp(parseDomainPattern(exclude)).test(location.href)); } function cleanBilibiliURL() { try { const url = new URL(location.href); if (/^\/video\/BV\w+/.test(url.pathname)) { const cleanPath = url.pathname.split('/').slice(0,3).join('/'); history.replaceState({}, '', `${url.origin}${cleanPath}`); } } catch(e) {} } function initEventListeners() { document.addEventListener('wheel', handleWheelEvent, { passive: false }); document.addEventListener('keydown', handleMainKeyEvent, true); const video = getVideoElement(); if (video) { video.addEventListener('mouseenter', handleMouseEnter); video.addEventListener('mouseleave', handleMouseLeave); } if (PLATFORM.BILIBILI) { window.addEventListener('load', () => setTimeout(cleanBilibiliURL, 4000)); } if (PLATFORM.YOUTUBE) { const observer = new MutationObserver(() => { if (document.querySelector('ytd-player')) { const video = getVideoElement(); if (video) { video.addEventListener('mouseenter', handleMouseEnter); video.addEventListener('mouseleave', handleMouseLeave); } } }); observer.observe(document.body, { childList: true, subtree: true }); } } function registerMenuCommands() { GM_registerMenuCommand(`切換全域匹配 (${state.isGlobalMatchEnabled ? "開啟" : "關閉"})`, () => { state.isGlobalMatchEnabled = !state.isGlobalMatchEnabled; GM_setValue('isGlobalMatchEnabled', state.isGlobalMatchEnabled); location.reload(); }); GM_registerMenuCommand("添加/移除當前網域於自訂匹配", () => { const pattern = `${location.protocol}//${location.hostname}/*`; const index = state.customMatches.indexOf(pattern); if (index === -1) { state.customMatches.push(pattern); GM_setValue('customMatches', JSON.stringify(state.customMatches)); } else { state.customMatches.splice(index, 1); GM_setValue('customMatches', JSON.stringify(state.customMatches)); } location.reload(); }); GM_registerMenuCommand("添加/移除當前網域於自訂排除", () => { const pattern = `${location.protocol}//${location.hostname}/*`; const index = state.customExcludes.indexOf(pattern); if (index === -1) { state.customExcludes.push(pattern); GM_setValue('customExcludes', JSON.stringify(state.customExcludes)); } else { state.customExcludes.splice(index, 1); GM_setValue('customExcludes', JSON.stringify(state.customExcludes)); } location.reload(); }); } if (document.readyState === 'complete' || document.readyState === 'interactive') { initEventListeners(); } else { document.addEventListener('DOMContentLoaded', initEventListeners); } registerMenuCommands(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址