您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
滚轮、013速度、28音量、46+-5sec、5(空白键)播放暂停、enter全萤幕切换、小键盘+-增减10%进度。完整支援:YouTube、B站、Steam。B站直播(局部)
当前为
// ==UserScript== // @name 滾動音量Dx版 Scroll Volume Dx Edition // @name:zh-CN 滚动音量Dx版 // @name:en Scroll Volume Dx Edition // @namespace http://tampermonkey.net/ // @version 9.1 // @description 滾輪、013速度、28音量、46+-5sec、5(空白鍵)播放暫停、enter全螢幕切換、小鍵盤+-增減10%進度。完整支援:YouTube、B站、Steam。B站直播(局部) // @description:zh-CN 滚轮、013速度、28音量、46+-5sec、5(空白键)播放暂停、enter全萤幕切换、小键盘+-增减10%进度。完整支援:YouTube、B站、Steam。B站直播(局部) // @description:en wheel scroll for volume. NumpadKey:013 for speed, 28 for volume, 46 for 5sec、5(space) for play/pause, enter for fullscreen, numpad+- for 5sec. Fully supports: YouTube, Bilibili, Steam. Bilibili live (partial) // @match *://www.youtube.com/* // @match *://www.bilibili.com/* // @match *://live.bilibili.com/* // @match *://www.twitch.tv/* // @match *://store.steampowered.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @license MIT // ==/UserScript== (function() { 'use strict'; const initStorage = () => { if (GM_getValue('storageInitialized') !== true) { GM_setValue('stepTime', 5); GM_setValue('stepTimeLong', 30); GM_setValue('stepVolume', 10); GM_setValue('storageInitialized', true); } }; initStorage(); const CONFIG = { stepTime: GM_getValue('stepTime', 5), stepTimeLong: GM_getValue('stepTimeLong', 30), stepVolume: GM_getValue('stepVolume', 10) }; GM_registerMenuCommand("⚙️ 設定步進", () => { const newVal = prompt("設定快進/快退", CONFIG.stepTime); if (newVal && !isNaN(newVal)) { GM_setValue('stepTime', parseFloat(newVal)); CONFIG.stepTime = parseFloat(newVal); } }); GM_registerMenuCommand("⏱️ 設定長步進", () => { const newVal = prompt("設定長跳轉", CONFIG.stepTimeLong); if (newVal && !isNaN(newVal)) { GM_setValue('stepTimeLong', parseFloat(newVal)); CONFIG.stepTimeLong = parseFloat(newVal); } }); GM_registerMenuCommand("🔊 設定音量步進", () => { const newVal = prompt("設定音量幅度 (%)", CONFIG.stepVolume); if (newVal && !isNaN(newVal)) { GM_setValue('stepVolume', parseFloat(newVal)); CONFIG.stepVolume = parseFloat(newVal); } }); let cachedVideo = null; let lastVideoCheck = 0; const PLATFORM = (() => { const host = location.hostname; if (/youtube\.com|youtu\.be/.test(host)) return "YOUTUBE"; if (/www.bilibili\.com/.test(host)) return "BILIBILI"; //live.bilibili對應GENERIC if (/twitch\.tv/.test(host)) return "TWITCH"; if (/steam(community|powered)\.com/.test(host)) return "STEAM"; return "GENERIC"; })(); const videoStateMap = new WeakMap(); function getVideoState(video) { if (!videoStateMap.has(video)) { videoStateMap.set(video, { lastCustomRate: 1.0, isDefaultRate: true }); } return videoStateMap.get(video); } function getVideoElement() { if (cachedVideo && document.contains(cachedVideo)) { return cachedVideo; } const handler = PLATFORM_HANDLERS[PLATFORM] || PLATFORM_HANDLERS.GENERIC; cachedVideo = handler.getVideo(); lastVideoCheck = Date.now(); return cachedVideo; } function commonAdjustVolume(video, delta) { const newVolume = Math.max(0, Math.min(100, (video.volume * 100) + (delta > 0 ? -CONFIG.stepVolume : CONFIG.stepVolume) )); video.volume = newVolume / 100; showVolume(newVolume); return newVolume; } const PLATFORM_HANDLERS = { YOUTUBE: { getVideo: () => document.querySelector('video, ytd-player video') || findVideoInIframes(), adjustVolume: commonAdjustVolume, toggleFullscreen: () => document.querySelector('.ytp-fullscreen-button')?.click(), specialKeys: { 'Space': () => {}, //代表使用YT默認 'Numpad7': () => document.querySelector('.ytp-prev-button')?.click(), 'Numpad9': () => document.querySelector('.ytp-next-button')?.click() } }, BILIBILI: { getVideo: () => document.querySelector('.bpx-player-video-wrap video') || findVideoInIframes(), adjustVolume: commonAdjustVolume, toggleFullscreen: () => document.querySelector('.bpx-player-ctrl-full')?.click(), specialKeys: { 'Space': () => {}, 'Numpad2': () => {}, 'Numpad8': () => {}, 'Numpad4': () => {}, 'Numpad6': () => {}, //空值代表使用bilibili默認 'Numpad7': () => document.querySelector('.bpx-player-ctrl-prev')?.click(), 'Numpad9': () => document.querySelector('.bpx-player-ctrl-next')?.click() } }, TWITCH: { getVideo: () => document.querySelector('.video-ref video') || findVideoInIframes(), adjustVolume: commonAdjustVolume, toggleFullscreen: () => document.querySelector('[data-a-target="player-fullscreen-button"]')?.click(), specialKeys: { 'Numpad7': () => simulateKeyPress('ArrowLeft'), 'Numpad9': () => simulateKeyPress('ArrowRight') } }, STEAM: { getVideo: () => Array.from(document.querySelectorAll('video')).find(v => v.offsetParent !== null) || findVideoInIframes(), adjustVolume: commonAdjustVolume, toggleFullscreen: (video) => { if (!video) return; const container = video.closest('.game_hover_activated') || video.parentElement; if (container && !document.fullscreenElement) { container.requestFullscreen?.().catch(() => { video.requestFullscreen?.(); }); } else { document.exitFullscreen?.(); } } }, GENERIC: { getVideo: () => { const iframeVideo = findVideoInIframes(); if (iframeVideo) return iframeVideo; return document.querySelector('video, .video-player video, .video-js video, .player-container video'); }, adjustVolume: commonAdjustVolume, toggleFullscreen: (video) => toggleNativeFullscreen(video), } }; function findVideoInIframes() { const iframes = document.querySelectorAll('iframe'); for (const iframe of iframes) { try { const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document; return iframeDoc?.querySelector('video'); } catch {} } return null; } function toggleNativeFullscreen(video) { if (!video) return; try { if (document.fullscreenElement) { document.exitFullscreen(); } else { let elementToFullscreen = video; for (let i = 0; i < 2; i++) { if (elementToFullscreen.parentElement) { elementToFullscreen = elementToFullscreen.parentElement; } else { break; } } if (elementToFullscreen.requestFullscreen) { elementToFullscreen.requestFullscreen(); } else if (elementToFullscreen.webkitRequestFullscreen) { elementToFullscreen.webkitRequestFullscreen(); } else if (elementToFullscreen.msRequestFullscreen) { elementToFullscreen.msRequestFullscreen(); } else { if (video.requestFullscreen) { video.requestFullscreen(); } else if (video.webkitRequestFullscreen) { video.webkitRequestFullscreen(); } else if (video.msRequestFullscreen) { video.msRequestFullscreen(); } } } } catch (e) { console.error('Fullscreen error:', e); } } function simulateKeyPress(key) { document.dispatchEvent(new KeyboardEvent('keydown', {key, bubbles: true})); } function isInputElement(target) { return /^(INPUT|TEXTAREA|SELECT)$/.test(target.tagName) || target.isContentEditable; } function adjustRate(video, changeValue) { const state = getVideoState(video); const newRate = Math.max(0.1, Math.min(16, video.playbackRate + changeValue)); video.playbackRate = parseFloat(newRate.toFixed(1)); state.lastCustomRate = video.playbackRate; state.isDefaultRate = (video.playbackRate === 1.0); showVolume(video.playbackRate * 100); } function togglePlaybackRate(video) { const state = getVideoState(video); if (state.isDefaultRate) { video.playbackRate = state.lastCustomRate; state.isDefaultRate = false; } else { state.lastCustomRate = video.playbackRate; video.playbackRate = 1.0; state.isDefaultRate = true; } showVolume(video.playbackRate * 100); } function showVolume(vol) { const display = document.getElementById('dynamic-volume-display') || createVolumeDisplay(); display.textContent = `${Math.round(vol)}%`; display.style.opacity = '1'; setTimeout(() => display.style.opacity = '0', 1000); } function createVolumeDisplay() { const display = document.createElement('div'); display.id = 'dynamic-volume-display'; Object.assign(display.style, { position: 'fixed', zIndex: 2147483647, 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 handleVideoWheel(e) { e.preventDefault(); const video = e.target; PLATFORM_HANDLERS[PLATFORM].adjustVolume(video, e.deltaY); } function handleKeyEvent(e) { if (isInputElement(e.target)) return; const video = getVideoElement(); if (!video) return; const handler = PLATFORM_HANDLERS[PLATFORM]; if (handler.specialKeys?.[e.code]) { handler.specialKeys[e.code](); e.preventDefault(); return; } const actions = { 'Space': () => video[video.paused ? 'play' : 'pause'](), 'Numpad5': () => video[video.paused ? 'play' : 'pause'](), 'NumpadEnter': () => handler.toggleFullscreen(video), 'NumpadAdd': () => video.currentTime += video.duration * 0.1, 'NumpadSubtract': () => video.currentTime -= video.duration * 0.1, 'Numpad0': () => togglePlaybackRate(video), 'Numpad1': () => adjustRate(video, -0.1), 'Numpad3': () => adjustRate(video, 0.1), 'Numpad8': () => handler.adjustVolume(video, -CONFIG.stepVolume), 'Numpad2': () => handler.adjustVolume(video, CONFIG.stepVolume), 'Numpad4': () => video.currentTime -= CONFIG.stepTime, 'Numpad6': () => video.currentTime += CONFIG.stepTime, 'Numpad7': () => video.currentTime -= CONFIG.stepTimeLong, 'Numpad9': () => video.currentTime += CONFIG.stepTimeLong }; if (actions[e.code]) { actions[e.code](); e.preventDefault(); } } function bindVideoEvents() { const videos = document.querySelectorAll('video'); videos.forEach(video => { if (!video.dataset.volumeBound) { video.addEventListener('wheel', handleVideoWheel, { passive: false }); video.dataset.volumeBound = 'true'; } }); } function init() { bindVideoEvents(); document.addEventListener('keydown', handleKeyEvent, true); const observer = new MutationObserver(bindVideoEvents); observer.observe(document.body, { childList: true, subtree: true }); } if (document.readyState !== 'loading') init(); else document.addEventListener('DOMContentLoaded', init); })();