您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
通过快捷键控制网页视频播放速度,按下时加速,松开时恢复原速
// ==UserScript== // @name 网页视频倍速控制器 // @namespace http://tampermonkey.net/ // @version 1.4 // @description 通过快捷键控制网页视频播放速度,按下时加速,松开时恢复原速 // @author 模仿煎蛋的太阳 // @match *://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @run-at document-start // ==/UserScript== (function() { 'use strict'; // 默认设置 const DEFAULT_KEYS = { 'Control': 3.0, 'Alt': 2.0 }; // 自定义速度输入框的默认设置 const DEFAULT_CUSTOM_SPEED = { key: 'Shift+V', defaultSpeed: 1.5, enabled: true }; // 当前设置 let speedKeys = JSON.parse(GM_getValue('speedKeys', JSON.stringify(DEFAULT_KEYS))); let customSpeedSettings = JSON.parse(GM_getValue('customSpeedSettings', JSON.stringify(DEFAULT_CUSTOM_SPEED))); let isKeyDown = {}; // 记录每个按键的状态 let customSpeedValue = parseFloat(GM_getValue('customSpeedValue', customSpeedSettings.defaultSpeed)); // 当前自定义速度值 let customSpeedActive = false; // 自定义速度是否激活 // 保存视频原始速度的对象 const videoOriginalSpeeds = new WeakMap(); // 添加设置菜单项 GM_registerMenuCommand('设置视频倍速控制器', openSettings); // 初始化 function init() { // 初始化按键状态 Object.keys(speedKeys).forEach(key => { isKeyDown[key] = false; }); // 添加键盘事件监听器 document.addEventListener('keydown', handleKeyDown); document.addEventListener('keyup', handleKeyUp); // 监听新视频元素的添加 const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === 1 && node.tagName === 'VIDEO') { setupVideoListener(node); } else if (node.querySelectorAll) { node.querySelectorAll('video').forEach(setupVideoListener); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); // 处理页面上已有的视频元素 document.querySelectorAll('video').forEach(setupVideoListener); } // 为视频元素设置事件监听器 function setupVideoListener(video) { // 监听播放速度变化,保存原始速度 const observer = new MutationObserver(() => { if (!isAnyKeyDown() && !customSpeedActive && !videoOriginalSpeeds.has(video)) { videoOriginalSpeeds.set(video, video.playbackRate); } }); observer.observe(video, { attributes: true, attributeFilter: ['playbackrate'] }); video.addEventListener('ratechange', () => { if (!isAnyKeyDown() && !customSpeedActive) { videoOriginalSpeeds.set(video, video.playbackRate); } }); // 初始设置原始速度 if (!videoOriginalSpeeds.has(video)) { videoOriginalSpeeds.set(video, video.playbackRate); } video.addEventListener('play', () => { // 检查是否有按键被按下,如果有则设置对应速度 for (const key in speedKeys) { if (isKeyDown[key]) { video.playbackRate = speedKeys[key]; break; } } // 如果自定义速度激活,设置自定义速度 if (customSpeedActive) { video.playbackRate = customSpeedValue; } }); } // 检查是否有任何按键被按下 function isAnyKeyDown() { for (const key in isKeyDown) { if (isKeyDown[key]) return true; } return false; } // 处理按键按下事件 function handleKeyDown(event) { // 处理自定义速度快捷键 if (customSpeedSettings.enabled && isCustomSpeedKey(event) && !document.getElementById('customSpeedInputContainer')) { showCustomSpeedInput(); event.preventDefault(); return; } const key = event.key; // 处理预设快捷键 if (speedKeys.hasOwnProperty(key) && !isKeyDown[key]) { isKeyDown[key] = true; setAllVideoSpeed(speedKeys[key]); event.preventDefault(); } } // 处理按键松开事件 function handleKeyUp(event) { const key = event.key; // 处理预设快捷键 if (speedKeys.hasOwnProperty(key) && isKeyDown[key]) { isKeyDown[key] = false; // 如果还有其他按键按下,设置为对应速度,否则恢复原速 let restored = false; for (const k in speedKeys) { if (isKeyDown[k]) { setAllVideoSpeed(speedKeys[k]); restored = true; break; } } // 如果自定义速度激活,保持自定义速度 if (customSpeedActive) { setAllVideoSpeed(customSpeedValue); restored = true; } if (!restored) { restoreAllVideoSpeed(); } event.preventDefault(); } } // 检查是否按下了自定义速度快捷键 function isCustomSpeedKey(event) { const keys = customSpeedSettings.key.split('+'); if (keys.length !== 2) return false; const modifier = keys[0].trim(); const mainKey = keys[1].trim(); return ( event.key === mainKey && ((modifier === 'Ctrl' && event.ctrlKey) || (modifier === 'Shift' && event.shiftKey) || (modifier === 'Alt' && event.altKey)) ); } // 显示自定义速度输入框 // Spotify风格UI设置 const spotifyStyle = { primaryColor: '#1DB954', // Spotify绿 secondaryColor: 'rgba(29, 185, 84, 0.7)', // 透明度渐变 bgColor: '#191414', // Spotify深灰背景 textColor: '#FFFFFF', // 白色文字 secondaryTextColor: '#B3B3B3', // 浅灰辅助文字 largeFont: 'bold 24px "Circular", "Helvetica Neue", sans-serif', smallFont: '14px "Helvetica Neue", Arial, sans-serif', borderRadius: '24px', // Spotify风格圆角 transition: 'all 0.3s cubic-bezier(0.3, 0, 0, 1)' // Spotify风格动画 }; // 修改自定义速度输入框样式 function showCustomSpeedInput() { const container = document.createElement('div'); container.id = 'customSpeedInputContainer'; container.innerHTML = ` <div style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: ${spotifyStyle.bgColor}; padding: 30px; border-radius: ${spotifyStyle.borderRadius}; box-shadow: 0 8px 24px rgba(0,0,0,0.3); z-index: 10001; font-family: ${spotifyStyle.smallFont}; width: 400px; border: 1px solid rgba(255,255,255,0.1); color: ${spotifyStyle.textColor}; transition: ${spotifyStyle.transition};"> <h2 style="margin: 0 0 20px 0; font: ${spotifyStyle.largeFont};"> <span style="color: ${spotifyStyle.primaryColor}">自定义速度</span> CUSTOM SPEED </h2> <div style="margin-bottom: 25px;"> <label for="customSpeedInput" style="display: block; margin-bottom: 8px; color: ${spotifyStyle.secondaryTextColor}; font: ${spotifyStyle.smallFont};">播放速度 (0.1-16):</label> <input type="number" id="customSpeedInput" min="0.1" max="16" step="0.1" value="${customSpeedValue}" style="width: 100%; padding: 12px; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); border-radius: ${spotifyStyle.borderRadius}; font-size: 16px; color: ${spotifyStyle.textColor}; transition: ${spotifyStyle.transition}; outline: none;"> </div> <div style="display: flex; justify-content: flex-end; gap: 10px;"> <button id="closeCustomSpeed" style="background: none; color: ${spotifyStyle.secondaryTextColor}; border: 1px solid rgba(255,255,255,0.2); padding: 10px 20px; border-radius: ${spotifyStyle.borderRadius}; cursor: pointer; transition: ${spotifyStyle.transition}; &:hover { background: rgba(255,255,255,0.1); }">取消</button> <button id="applyCustomSpeed" style="background: ${spotifyStyle.primaryColor}; color: white; border: none; padding: 10px 20px; border-radius: ${spotifyStyle.borderRadius}; cursor: pointer; transition: ${spotifyStyle.transition}; &:hover { background: #1ED760; }">应用</button> </div> </div> <div id="customSpeedOverlay" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 10000; backdrop-filter: blur(4px);"></div> `; // 添加到页面和事件绑定... document.body.appendChild(container); // 绑定事件 container.querySelector('#applyCustomSpeed').addEventListener('click', applyCustomSpeed); container.querySelector('#closeCustomSpeed').addEventListener('click', closeCustomSpeedInput); container.querySelector('#customSpeedInput').addEventListener('keydown', (e) => { if (e.key === 'Enter') { applyCustomSpeed(); } }); container.querySelector('#customSpeedOverlay').addEventListener('click', closeCustomSpeedInput); // 聚焦到输入框 container.querySelector('#customSpeedInput').focus(); container.querySelector('#customSpeedInput').select(); } // 应用自定义速度 function applyCustomSpeed() { const input = document.querySelector('#customSpeedInput'); const speed = parseFloat(input.value); if (isNaN(speed) || speed <= 0) { alert('请输入有效的播放速度值'); return; } customSpeedValue = speed; customSpeedActive = true; // 保存自定义速度值 GM_setValue('customSpeedValue', customSpeedValue); // 设置所有视频速度 setAllVideoSpeed(customSpeedValue); // 关闭输入框 closeCustomSpeedInput(); } // 关闭自定义速度输入框 function closeCustomSpeedInput() { const container = document.querySelector('#customSpeedInputContainer'); const overlay = document.querySelector('#customSpeedOverlay'); if (container) container.remove(); if (overlay) overlay.remove(); } // 设置所有视频元素的播放速度 function setAllVideoSpeed(speed) { const videos = document.querySelectorAll('video'); videos.forEach(video => { // 保存当前速度作为原始速度(如果尚未保存) if (!videoOriginalSpeeds.has(video)) { videoOriginalSpeeds.set(video, video.playbackRate); } video.playbackRate = speed; }); } // 恢复所有视频元素到原始速度 function restoreAllVideoSpeed() { const videos = document.querySelectorAll('video'); videos.forEach(video => { const originalSpeed = videoOriginalSpeeds.get(video); if (originalSpeed !== undefined) { video.playbackRate = originalSpeed; } else { video.playbackRate = 1.0; // 默认速度 } }); } // 打开设置界面 // 修改设置窗口样式 function openSettings() { const settingsWindow = document.createElement('div'); settingsWindow.innerHTML = ` <div style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: ${spotifyStyle.bgColor}; padding: 30px; border-radius: ${spotifyStyle.borderRadius}; box-shadow: 0 8px 24px rgba(0,0,0,0.3); z-index: 10000; font-family: ${spotifyStyle.smallFont}; width: 500px; color: ${spotifyStyle.textColor};"> <h2 style="margin: 0 0 20px 0; font: ${spotifyStyle.largeFont};"> 视频倍速控制器<span style="font-size: 0.8em; color: ${spotifyStyle.primaryColor}">VIDEO SPEED CONTROL</span> </h2> <div style="margin-bottom: 20px;"> <h3 style="margin: 0 0 10px 0; color: #444;">预设快捷键设置</h3> <p style="margin: 0 0 10px 0; color: #666;">设置快捷键和对应的播放速度:</p> <div id="keySettingsContainer"> <!-- 快捷键设置项将通过JavaScript动态添加 --> </div> <button id="addKeySetting" style="background: ${spotifyStyle.primaryColor}; color: white; border: none; padding: 12px 24px; border-radius: ${spotifyStyle.borderRadius}; cursor: pointer; margin-top: 10px; transition: ${spotifyStyle.transition}; &:hover { background: #1ED760; }">添加快捷键</button> </div> <div style="margin-bottom: 20px; padding-top: 10px; border-top: 1px solid #eee;"> <h3 style="margin: 0 0 10px 0; color: #444;">自定义速度设置</h3> <div style="margin-bottom: 10px;"> <label style="display: block; margin-bottom: 5px; color: #555;"> <input type="checkbox" id="customSpeedEnabled" style="margin-right: 5px;"> 启用自定义速度功能 </label> </div> <div style="margin-bottom: 10px;"> <label for="customSpeedKey" style="display: block; margin-bottom: 5px; color: #555;">触发快捷键:</label> <input type="text" id="customSpeedKey" placeholder="例如: Shift+V" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"> <p style="margin: 5px 0 0 0; font-size: 12px; color: #888;">格式: Ctrl+V 或 Shift+V 或 Alt+V</p> </div> <div style="margin-bottom: 10px;"> <label for="customSpeedDefault" style="display: block; margin-bottom: 5px; color: #555;">默认播放速度:</label> <input type="number" id="customSpeedDefault" min="0.1" max="16" step="0.1" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"> </div> </div> <div style="display: flex; justify-content: space-between;"> <button id="saveSettings" style="background: ${spotifyStyle.primaryColor}; color: white; border: none; padding: 12px 24px; border-radius: ${spotifyStyle.borderRadius}; cursor: pointer; transition: ${spotifyStyle.transition}; &:hover { background: #1ED760; }">保存设置</button> <button id="resetSettings" style="background: ${spotifyStyle.bgColor}; color: ${spotifyStyle.textColor}; border: 1px solid rgba(255,255,255,0.2); padding: 12px 24px; border-radius: ${spotifyStyle.borderRadius}; cursor: pointer; transition: ${spotifyStyle.transition}; &:hover { background: rgba(255,255,255,0.1); }">恢复默认</button> <button id="closeSettings" style="background: ${spotifyStyle.bgColor}; color: ${spotifyStyle.textColor}; border: 1px solid rgba(255,255,255,0.2); padding: 12px 24px; border-radius: ${spotifyStyle.borderRadius}; cursor: pointer; transition: ${spotifyStyle.transition}; &:hover { background: rgba(255,255,255,0.1); }">关闭</button> </div> </div> <div id="settingsOverlay" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 9999; backdrop-filter: blur(4px);"></div> `; // 添加到页面 document.body.appendChild(settingsWindow); // 填充现有设置 const container = settingsWindow.querySelector('#keySettingsContainer'); Object.keys(speedKeys).forEach((key, index) => { addKeySettingRow(container, key, speedKeys[key], index); }); // 填充自定义速度设置 settingsWindow.querySelector('#customSpeedEnabled').checked = customSpeedSettings.enabled; settingsWindow.querySelector('#customSpeedKey').value = customSpeedSettings.key; settingsWindow.querySelector('#customSpeedDefault').value = customSpeedSettings.defaultSpeed; // 绑定事件 settingsWindow.querySelector('#addKeySetting').addEventListener('click', () => { const index = Object.keys(speedKeys).length; addKeySettingRow(container, '', 2.0, index); }); settingsWindow.querySelector('#saveSettings').addEventListener('click', saveSettings); settingsWindow.querySelector('#resetSettings').addEventListener('click', resetSettings); settingsWindow.querySelector('#closeSettings').addEventListener('click', closeSettings); settingsWindow.querySelector('#settingsOverlay').addEventListener('click', closeSettings); } // 添加快捷键设置行 function addKeySettingRow(container, key, speed, index) { const row = document.createElement('div'); row.style.display = 'flex'; row.style.marginBottom = '10px'; row.style.alignItems = 'center'; row.innerHTML = ` <select class="keySelect" style="flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px; margin-right: 10px;"> <option value="">请选择</option> <option value="Control">Ctrl</option> <option value="Shift">Shift</option> <option value="Alt">Alt</option> <option value=" ">空格</option> </select> <input type="number" class="speedInput" min="0.1" max="16" step="0.1" value="${speed}" style="flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px; margin-right: 10px;"> <button class="removeKeySetting" style="background: #1ED760; color: white; border: none; width: 24px; height: 24px; border-radius: 50%; cursor: pointer;">×</button> `; row.querySelector('.keySelect').value = key; container.appendChild(row); // 绑定删除事件 row.querySelector('.removeKeySetting').addEventListener('click', () => { container.removeChild(row); }); } // 保存设置 function saveSettings() { // 保存预设快捷键设置 const newSpeedKeys = {}; const rows = document.querySelectorAll('#keySettingsContainer > div'); let valid = true; rows.forEach(row => { const key = row.querySelector('.keySelect').value; const speed = parseFloat(row.querySelector('.speedInput').value); if (key && !isNaN(speed) && speed > 0) { newSpeedKeys[key] = speed; } else { valid = false; } }); if (!valid) { alert('请确保所有预设快捷键设置项都已正确填写'); return; } if (Object.keys(newSpeedKeys).length === 0) { alert('请至少设置一个快捷键'); return; } // 保存自定义速度设置 const customEnabled = document.querySelector('#customSpeedEnabled').checked; const customKey = document.querySelector('#customSpeedKey').value.trim(); const customDefault = parseFloat(document.querySelector('#customSpeedDefault').value); if (customEnabled && (!customKey || isNaN(customDefault) || customDefault <= 0)) { alert('请正确填写自定义速度设置'); return; } const newCustomSpeedSettings = { key: customKey, defaultSpeed: customDefault, enabled: customEnabled }; // 保存到存储 GM_setValue('speedKeys', JSON.stringify(newSpeedKeys)); GM_setValue('customSpeedSettings', JSON.stringify(newCustomSpeedSettings)); // 更新当前设置 speedKeys = newSpeedKeys; customSpeedSettings = newCustomSpeedSettings; // 更新按键状态对象 isKeyDown = {}; Object.keys(speedKeys).forEach(key => { isKeyDown[key] = false; }); alert('设置已保存!'); closeSettings(); } // 恢复默认设置 function resetSettings() { if (confirm('确定要恢复默认设置吗?')) { speedKeys = {...DEFAULT_KEYS}; customSpeedSettings = {...DEFAULT_CUSTOM_SPEED}; GM_setValue('speedKeys', JSON.stringify(speedKeys)); GM_setValue('customSpeedSettings', JSON.stringify(customSpeedSettings)); // 更新按键状态对象 isKeyDown = {}; Object.keys(speedKeys).forEach(key => { isKeyDown[key] = false; }); alert('已恢复默认设置!'); closeSettings(); } } // 关闭设置界面 function closeSettings() { const settingsWindow = document.querySelector('div[style*="position: fixed; top: 50%; left: 50%"]'); const overlay = document.querySelector('#settingsOverlay'); if (settingsWindow) settingsWindow.remove(); if (overlay) overlay.remove(); } // 页面加载完成后初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })(); // 添加Apple风格动画效果 function animateElement(element, scale = 1.05) { element.style.transition = 'all 0.3s cubic-bezier(0.25, 0.1, 0.25, 1)'; element.style.transform = `scale(${scale})`; setTimeout(() => { element.style.transform = 'scale(1)'; }, 300); }