您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
1.滚轮控制进度;2.Shift+滚轮调音量;3.Ctrl 临时2倍速;4.左键拖动调节进度条
// ==UserScript== // @name 控制视频快进倍速及音量调节脚本(含拖动进度条) // @namespace http://tampermonkey.net/ // @version 1.1.1 // @description 1.滚轮控制进度;2.Shift+滚轮调音量;3.Ctrl 临时2倍速;4.左键拖动调节进度条 // @author lhr3572651322 // @license MIT // @match *://*.bilibili.com/video/* // @match *://*.youtube.com/watch* // @match *://*.youku.com/* // @grant none // @run-at document-end // ==/UserScript== (function() { 'use strict'; /* ---------- 1. 样式常量(与之前相同,略) ---------- */ const styles = { base: ` position: fixed; color: white; z-index: 2147483647; transition: all 0.3s ease; `, tip: ` left: 50%; top: 20px; transform: translateX(-50%); background: rgba(33, 150, 243, 0.9); padding: 10px 20px; border-radius: 4px; font-size: 14px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); opacity: 0; text-align: center; display: flex; align-items: center; gap: 12px; `, neverBtn: ` background: rgba(255,255,255,0.2); border: none; color: white; padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; `, guide: ` position: absolute; left: 20px; top: 50%; transform: translateY(-50%); background: rgba(0, 0, 0, 0.8); padding: 15px 20px; border-radius: 8px; font-size: 14px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); opacity: 0; border-left: 3px solid #2196F3; line-height: 1.8; pointer-events: none; text-align: left; `, volumeIndicator: ` position: absolute; top: 60px; right: 20px; background: rgba(0, 0, 0, 0.7); padding: 5px 10px; border-radius: 4px; font-size: 14px; pointer-events: none; opacity: 0; `, // 新增:拖动进度条提示 dragBar: ` position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.6); padding: 6px 12px; border-radius: 4px; font-size: 14px; pointer-events: none; opacity: 0; z-index: 2147483647; ` }; /* ---------- 2. 首次加载提示(略,同前) ---------- */ if (!localStorage.getItem('skipShiftGuide')) { const tip = document.createElement('div'); tip.style.cssText = styles.base + styles.tip; tip.innerHTML = ` <span>按住 Shift 键可随时查看操作指引</span> <button id="neverShowAgain" style="${styles.neverBtn}">不再提示</button> `; document.body.appendChild(tip); setTimeout(() => tip.style.opacity = 1, 100); tip.querySelector('#neverShowAgain').addEventListener('click', () => { localStorage.setItem('skipShiftGuide', '1'); tip.style.opacity = 0; setTimeout(() => tip.remove(), 300); }); setTimeout(() => { tip.style.opacity = 0; setTimeout(() => tip.remove(), 300); }, 5000); } /* ---------- 3. 轮询找视频(略,同前) ---------- */ const checkVideo = setInterval(() => { const hostname = window.location.hostname; const video = hostname.includes('missav.com') ? document.querySelector('.plyr--video video') : hostname.includes('jable.tv') ? document.querySelector('#player-container video, #player_3 video, .plyr--video video') : document.querySelector('.html5-main-video, video, .youku-player video'); if (video?.readyState >= 2) { clearInterval(checkVideo); initVideoControl(video); } }, 1000); /* ---------- 4. 初始化视频控制 ---------- */ function initVideoControl(video) { if (video.dataset.controlInitialized) return; video.dataset.controlInitialized = 'true'; const hostname = window.location.hostname; const isYouku = hostname.includes('youku.com'); const isMissav = hostname.includes('missav.com'); const isJable = hostname.includes('jable.tv'); const container = isMissav || isJable ? video.closest('.plyr--video') || video.closest('#player-container') || video.parentElement : isYouku ? document.querySelector('.youku-player') : video.closest('#movie_player') || video.parentElement; if (!container) return; let originalRate = video.playbackRate; /* ---------- 5. 滚轮事件(略,同前) ---------- */ const wheelHandler = e => { e.preventDefault(); e.stopPropagation(); if (e.shiftKey) { video.volume = Math.min(1, Math.max(0, video.volume + (e.deltaY < 0 ? 0.05 : -0.05) )); showVolumeIndicator(video, container); } else { const step = e.deltaY < 0 ? -2 : 2; video.currentTime = Math.min(video.duration, Math.max(0, video.currentTime + step)); } return false; }; const elements = (isMissav || isJable) ? [container, video] : [isYouku ? container : video]; elements.forEach(el => { el.removeEventListener('wheel', wheelHandler, { passive: false, capture: true }); el.addEventListener('wheel', wheelHandler, { passive: false, capture: true }); }); /* ---------- 6. Ctrl 临时 2 倍速(略,同前) ---------- */ document.addEventListener('keydown', e => { if (e.key === 'Control' && !e.repeat) { originalRate = video.playbackRate; if (video.paused) video.play(); video.playbackRate = 2; } }); document.addEventListener('keyup', e => { if (e.key === 'Control') video.playbackRate = originalRate; }); /* ---------- 8. 指引框 & 音量提示(略,同前) ---------- */ addGuideBox(video); } /* ---------- 9. 工具函数 ---------- */ function createElement(className, style) { const element = document.createElement('div'); element.className = className; element.style.cssText = styles.base + style; return element; } function showVolumeIndicator(video, container) { let indicator = container.querySelector('.volume-indicator'); if (!indicator) { indicator = createElement('volume-indicator', styles.volumeIndicator); container.appendChild(indicator); } indicator.textContent = `音量: ${Math.round(video.volume * 100)}%`; indicator.style.opacity = '1'; clearTimeout(indicator.fadeTimeout); indicator.fadeTimeout = setTimeout(() => indicator.style.opacity = '0', 2000); } function addGuideBox(video) { const container = video.closest('.plyr--video') || video.parentElement; const guideBox = createElement('video-control-guide', styles.guide); const updateGuide = () => { guideBox.innerHTML = ` <div style="margin-bottom: 5px;">按住 Shift:显示此提示</div> <div style="margin-bottom: 5px;">🖱️滚轮:快进/快退</div> <div style="margin-bottom: 5px;">Shift+滚轮:调节音量(5%)</div> <div style="margin-bottom: 5px;">按住 Ctrl:临时 2 倍速</div> <div style="margin-bottom: 5px;">左键拖动视频:调节进度</div> <div style="color: #2196F3;">音量: ${Math.round(video.volume * 100)}%</div> `; }; container.appendChild(guideBox); video.addEventListener('volumechange', updateGuide); updateGuide(); document.addEventListener('keydown', e => { if (e.key === 'Shift') { e.preventDefault(); guideBox.style.opacity = '1'; guideBox.style.transform = 'translateY(-50%)'; } }); document.addEventListener('keyup', e => { if (e.key === 'Shift') { guideBox.style.opacity = '0'; guideBox.style.transform = 'translateY(-50%) translateX(-20px)'; } }); /* ---------- 7. 左键拖动进度条(比例版) ---------- */ let isDragging = false; let startX = 0; let startTime = 0; let videoWidth = 0; // 缓存一次视频宽度,防止拖动中实时取抖动 const dragBar = createElement('drag-bar', styles.dragBar); container.appendChild(dragBar); // 倍率:1倍 const DRAG_SENSITIVITY = 1; container.addEventListener('mousedown', e => { if (e.button !== 0) return; isDragging = true; startX = e.clientX; startTime = video.currentTime; // 实时拿一下视频宽度(拖动过程中不变) const rect = video.getBoundingClientRect(); videoWidth = rect.width || 1; // 防止 0 dragBar.style.opacity = '1'; e.preventDefault(); }); document.addEventListener('mousemove', e => { if (!isDragging) return; const deltaX = e.clientX - startX; // 拖动比例:[-∞, +∞] const ratio = deltaX / videoWidth; const deltaTime = ratio * video.duration / DRAG_SENSITIVITY; video.currentTime = Math.min(video.duration, Math.max(0, startTime + deltaTime)); // 实时显示百分比 const percent = ((video.currentTime / video.duration) * 100).toFixed(1); dragBar.textContent = `${percent}%`; }); document.addEventListener('mouseup', () => { if (!isDragging) return; isDragging = false; dragBar.style.opacity = '0'; }); } })();