您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
视频播放增强:1. 长按左键临时加速 2. B站自动开启AI字幕 3. 支持更多视频播放器
当前为
// ==UserScript== // @name 视频控制增强 // @namespace http://tampermonkey.net/ // @version 2.1 // @description 视频播放增强:1. 长按左键临时加速 2. B站自动开启AI字幕 3. 支持更多视频播放器 // @author Alonewinds // @match *://*/* // @exclude *://*/iframe/* // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @run-at document-start // @license MIT // ==/UserScript== (function() { 'use strict'; // 在脚本最开始添加一个检查 // 对于 B 站,只在主页面和播放器 iframe 中执行 // 对于其他网站,允许在所有环境中执行 if (window.location.hostname.includes('bilibili.com') && window.self !== window.top && window.location.hostname !== 'player.bilibili.com') { return; } // 默认配置 const config = { speedRate: GM_getValue('speedRate', 2.0), minPressTime: 200, subtitleEnabled: GM_getValue('subtitleEnabled', false), // 保持通用选择器,添加B站和YouTube选择器 selectors: { 'www.bilibili.com': '.bpx-player-video-area', 'www.youtube.com': '.html5-video-player', // 添加 YouTube 选择器 'default': '.video-controls, .progress-bar, [role="slider"]' } }; // 状态变量 let pressStartTime = 0; let originalSpeed = 1.0; let isPressed = false; let activeVideo = null; let isLongPress = false; let preventNextClick = false; // B站字幕相关变量 let subtitleEnabled = false; let subtitleAttempts = 0; let subtitleRetryTimer = null; let manuallyDisabled = false; let currentVideoId = ''; let clickHandler = null; // 在全局作用域注册菜单(只注册一次) GM_registerMenuCommand('设置倍速值', () => { // 检查是否在主页面或B站播放器 if (window.self !== window.top && window.location.hostname !== 'player.bilibili.com') return; const newSpeed = prompt('请输入新的倍速值(建议范围:1.1-4):', config.speedRate); if (newSpeed && !isNaN(newSpeed)) { config.speedRate = parseFloat(newSpeed); GM_setValue('speedRate', config.speedRate); const indicator = document.querySelector('.speed-indicator'); if (indicator) { indicator.innerHTML = `当前加速 ${config.speedRate}x <span class="speed-arrow">▶▶</span>`; } } }); // 仅在B站页面显示字幕菜单(只注册一次) if (window.location.hostname === 'www.bilibili.com') { GM_registerMenuCommand('B站字幕: ' + (config.subtitleEnabled ? '已开启' : '已关闭'), () => { // 检查是否在主页面或B站播放器 if (window.self !== window.top && window.location.hostname !== 'player.bilibili.com') return; config.subtitleEnabled = !config.subtitleEnabled; GM_setValue('subtitleEnabled', config.subtitleEnabled); alert('B站字幕' + (config.subtitleEnabled ? '开启,刷新页面生效' : '关闭,刷新页面生效')); }); } // ================ 倍速控制功能 ================ function findVideoElement(element) { if (!element) return null; if (element instanceof HTMLVideoElement) { return element; } const domain = window.location.hostname; // B站和YouTube使用区域限制 if (domain === 'www.bilibili.com') { const playerArea = document.querySelector('.bpx-player-video-area'); if (!playerArea?.contains(element)) return null; } else if (domain === 'www.youtube.com') { const ytPlayer = element.closest('.html5-video-player'); if (!ytPlayer?.contains(element)) return null; const video = ytPlayer.querySelector('video'); if (video) return video; } const controlSelector = config.selectors.default; if (element.closest(controlSelector)) { return null; } const container = element.closest('*:has(video)'); const video = container?.querySelector('video'); return video && window.getComputedStyle(video).display !== 'none' ? video : null; } function setYouTubeSpeed(video, speed) { if (window.location.hostname === 'www.youtube.com') { const player = video.closest('.html5-video-player'); if (player) { try { // 清理之前的监控器 if (player._speedInterval) { clearInterval(player._speedInterval); player._speedInterval = null; } // 设置速度 video.playbackRate = speed; if (speed !== 1.0) { // 只在加速时监控 // 增加检查间隔到 100ms player._speedInterval = setInterval(() => { if (video.playbackRate !== speed) { video.playbackRate = speed; } }, 100); // 从 50ms 改为 100ms // 添加超时清理 setTimeout(() => { if (player._speedInterval) { clearInterval(player._speedInterval); player._speedInterval = null; } }, 5000); // 5秒后自动清理 } video.dispatchEvent(new Event('ratechange')); } catch (e) { console.error('设置 YouTube 播放速度失败:', e); } } } else { video.playbackRate = speed; } } // ================ B站字幕功能 ================ function getVideoId() { const match = location.pathname.match(/\/video\/(.*?)\//); return match ? match[1] : ''; } function enableSubtitle() { if (!config.subtitleEnabled) return; if (window.location.hostname !== 'www.bilibili.com') return; if (subtitleAttempts >= 5 || manuallyDisabled) return; subtitleAttempts++; const button = document.querySelector("div.bpx-player-ctrl-btn.bpx-player-ctrl-subtitle > div.bpx-player-ctrl-btn-icon > span"); if (!button) { subtitleRetryTimer = setTimeout(enableSubtitle, 1000); return; } const parent = button.closest('.bpx-player-ctrl-subtitle'); if (!parent) return; if (clickHandler) { parent.removeEventListener('click', clickHandler); } clickHandler = (event) => { requestAnimationFrame(() => { if (!parent.classList.contains('bpx-player-ctrl-subtitle-on')) { manuallyDisabled = true; console.log('用户已手动关闭字幕,本视频将不再自动开启'); } }); }; parent.addEventListener('click', clickHandler); if (!parent.classList.contains('bpx-player-ctrl-subtitle-on') && !manuallyDisabled) { button.click(); subtitleEnabled = true; } } // ================ 事件处理 ================ function handleMouseDown(e) { if (e.button !== 0) return; const domain = window.location.hostname; let video = null; let playerArea = null; // B站和YouTube使用严格区域限制 if (domain === 'www.bilibili.com' || domain === 'www.youtube.com') { const selector = config.selectors[domain]; playerArea = document.querySelector(selector); if (!playerArea?.contains(e.target)) return; video = findVideoElement(e.target); } else { // 其他网站直接查找视频,不做区域限制 video = findVideoElement(e.target); if (video) { playerArea = video.closest('*:has(video)') || video.parentElement; } } if (!video) return; if (video.paused) { hideSpeedIndicator(); return; } pressStartTime = Date.now(); activeVideo = video; originalSpeed = video.playbackRate; isPressed = true; isLongPress = false; preventNextClick = false; const currentSpeedRate = GM_getValue('speedRate', config.speedRate); let indicator = document.querySelector('.speed-indicator'); if (!indicator) { indicator = document.createElement('div'); indicator.className = 'speed-indicator'; playerArea.appendChild(indicator); } // 使用统一的加速提示 indicator.innerHTML = `当前加速 ${currentSpeedRate}x <span class="speed-arrow">▶▶</span>`; indicator.style.display = 'block'; } function handleMouseUp(e) { if (!isPressed || !activeVideo) return; const pressDuration = Date.now() - pressStartTime; if (pressDuration >= config.minPressTime) { preventNextClick = true; setYouTubeSpeed(activeVideo, originalSpeed); hideSpeedIndicator(); } isPressed = false; isLongPress = false; activeVideo = null; } function handlePressDetection() { if (!isPressed || !activeVideo) return; const pressDuration = Date.now() - pressStartTime; if (pressDuration >= config.minPressTime) { if (activeVideo.playbackRate !== config.speedRate) { setYouTubeSpeed(activeVideo, config.speedRate); } isLongPress = true; } } function handleClick(e) { if (preventNextClick) { e.stopPropagation(); preventNextClick = false; return; } } function onVideoLoad() { if (window.location.hostname !== 'www.bilibili.com') return; const video = document.querySelector('video'); if (!video) { setTimeout(onVideoLoad, 500); return; } function reset() { const newVideoId = getVideoId(); if (newVideoId !== currentVideoId) { manuallyDisabled = false; currentVideoId = newVideoId; if (clickHandler) { const oldButton = document.querySelector('.bpx-player-ctrl-subtitle'); if (oldButton) { oldButton.removeEventListener('click', clickHandler); } clickHandler = null; } } subtitleEnabled = false; subtitleAttempts = 0; if (subtitleRetryTimer) { clearTimeout(subtitleRetryTimer); subtitleRetryTimer = null; } if (!manuallyDisabled) { setTimeout(enableSubtitle, 500); } } video.addEventListener('loadeddata', reset); video.addEventListener('play', () => { if (!subtitleEnabled && !manuallyDisabled) { reset(); } }); reset(); } // ================ 初始化 ================ function initializeEvents() { addSpeedIndicatorStyle(); document.addEventListener('mousedown', handleMouseDown, true); document.addEventListener('mouseup', handleMouseUp, true); document.addEventListener('click', handleClick, true); document.addEventListener('mouseleave', handleMouseUp, true); document.addEventListener('pause', (e) => { if (e.target instanceof HTMLVideoElement) { hideSpeedIndicator(); } }, true); function checkPress() { handlePressDetection(); requestAnimationFrame(checkPress); } checkPress(); if (window.location.hostname === 'www.bilibili.com') { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', onVideoLoad); } else { onVideoLoad(); } } } function addSpeedIndicatorStyle() { const style = document.createElement('style'); style.textContent = ` .speed-indicator { position: absolute; top: 15%; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.7); color: white; padding: 5px 10px; border-radius: 4px; z-index: 999999; display: none; font-size: 14px; } .speed-arrow { color: #00a1d6; margin-left: 2px; }`; document.head.appendChild(style); } function hideSpeedIndicator() { const indicator = document.querySelector('.speed-indicator'); if (indicator) { indicator.style.display = 'none'; } } initializeEvents(); })();