您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动开启中文字幕,提供倍速控制功能(Z/X/C键),记忆用户选择
// ==UserScript== // @name B站/哔哩哔哩/bilibili视频增强脚本-(中文字幕+倍速控制) // @namespace http://tampermonkey.net/ // @version 2.0.2 // @description 自动开启中文字幕,提供倍速控制功能(Z/X/C键),记忆用户选择 // @author anyphasy // @icon https://play-lh.googleusercontent.com/C1tISqYgtW_ejAmnGzvepbaYt7NJLagPelCZ_lzNv06RJPQgBx1_q3VX67z9wc48EgY=s1024 // @match *://www.bilibili.com/video/* // @match *://www.bilibili.com/list/watchlater* // @grant GM_setValue // @grant GM_getValue // @license EPL 1.0 // @grant GM_addStyle // ==/UserScript== (function() { 'use strict'; // 配置参数 const MAX_ATTEMPTS = 10; const INITIAL_DELAY = 800; const RETRY_DELAY = 2000; let attemptCount = 0; let isActive = false; let isEnabled = GM_getValue('bilibili_subtitle_enabled', true); let observer = null; let timeoutId = null; let playbackRates = [2, 1.5, 1.25, 1, 0.75, 0.5]; // 支持的倍速选项 let currentRate = GM_getValue('bilibili_playback_rate', 1); // 当前倍速 // 添加Element Plus样式 GM_addStyle(` .bpx-player-dm-notice { position: absolute; top: 10%; /* 将提示位置稍微下移,避免遮挡视频内容 */ left: 50%; transform: translate(-50%, -50%); background-color: rgba(0, 0, 0, 0.8); /* 增加背景不透明度 */ color: white; padding: 12px 20px; /* 增加内边距,让提示更大 */ border-radius: 8px; /* 增加圆角 */ font-size: 18px; /* 增大字体大小 */ font-weight: bold; /* 加粗字体 */ z-index: 9999; pointer-events: none; animation: fadeInOut 2.5s ease-in-out forwards; /* 稍微延长动画时间 */ min-width: 120px; /* 设置最小宽度 */ text-align: center; /* 文字居中 */ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); /* 添加阴影效果 */ } @keyframes fadeInOut { 0% { opacity: 0; } 10% { opacity: 1; } 90% { opacity: 1; } 100% { opacity: 0; } } `); // 初始化 function init() { console.log(`B站增强脚本已加载,字幕状态: ${isEnabled ? '开启' : '关闭'}, 当前倍速: ${currentRate}x`); // 清理函数 const cleanup = () => { if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; } if (observer) { observer.disconnect(); observer = null; } document.removeEventListener('keydown', handleKeyPress); }; // 添加页面卸载时的清理 window.addEventListener('beforeunload', cleanup); // 添加键盘监听器 document.addEventListener('keydown', handleKeyPress); console.log('快捷键监听器已添加 (Shift+A:字幕, Z/X/C:倍速)'); // 设置SPA路由变化监听 setupSPAObserver(); // 立即开始尝试(减少延迟) if (isEnabled) { timeoutId = setTimeout(tryClickChineseSubtitle, INITIAL_DELAY); } // 设置倍速控制 timeoutId = setTimeout(applySavedPlaybackRate, INITIAL_DELAY); } // 设置SPA路由监听 function setupSPAObserver() { let lastUrl = location.href; observer = new MutationObserver(() => { const currentUrl = location.href; if (currentUrl !== lastUrl) { lastUrl = currentUrl; resetState(); if (isEnabled) { timeoutId = setTimeout(tryClickChineseSubtitle, INITIAL_DELAY); } timeoutId = setTimeout(applySavedPlaybackRate, INITIAL_DELAY); } }); observer.observe(document, { subtree: true, childList: true }); } // 重置状态 function resetState() { if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; } attemptCount = 0; isActive = false; } // 快捷键处理 function handleKeyPress(event) { // Shift+C 切换字幕 if (event.shiftKey && event.key === 'A') { console.log("按下了Shift+A"); event.preventDefault(); toggleSubtitles(); } // 倍速控制 if (!event.ctrlKey && !event.altKey && !event.metaKey) { switch (event.key.toLowerCase()) { case 'z': console.log("按下了Z键 - 重置倍速"); event.preventDefault(); setPlaybackRate(1); break; case 'x': console.log("按下了X键 - 减小倍速"); event.preventDefault(); decreasePlaybackRate(); break; case 'c': console.log("按下了C键 - 增加倍速"); event.preventDefault(); increasePlaybackRate(); break; } } } // 切换字幕开关 function toggleSubtitles() { isEnabled = !isEnabled; GM_setValue('bilibili_subtitle_enabled', isEnabled); console.log(`字幕功能已${isEnabled ? '开启' : '关闭'}`); resetState(); if (isEnabled) { timeoutId = setTimeout(tryClickChineseSubtitle, INITIAL_DELAY); } else { closeSubtitles(); } } // 尝试点击中文字幕(优化版) function tryClickChineseSubtitle() { timeoutId = null; if (!isEnabled || isActive || attemptCount >= MAX_ATTEMPTS) { return; } attemptCount++; console.log(`尝试点击中文字幕 (第 ${attemptCount} 次)`); // 使用更高效的选择器 const subtitleItems = document.querySelectorAll('.bpx-player-ctrl-subtitle-language-item'); let foundChinese = false; for (const item of subtitleItems) { const textElement = item.querySelector('.bpx-player-ctrl-subtitle-language-item-text'); if (textElement && textElement.textContent.trim() === '中文') { foundChinese = true; console.log('找到中文字幕按钮'); if (!item.classList.contains('bpx-state-active')) { console.log('中文字幕未激活,执行点击'); textElement.click(); // 使用更快的验证机制 timeoutId = setTimeout(() => verifyActivation(item), 300); } else { console.log('中文字幕已激活'); isActive = true; } break; } } if (!foundChinese && attemptCount < MAX_ATTEMPTS) { console.log('未找到中文字幕按钮,稍后重试'); timeoutId = setTimeout(tryClickChineseSubtitle, RETRY_DELAY); } else if (!foundChinese) { console.log(`已达到最大尝试次数(${MAX_ATTEMPTS}),可能此视频无中文字幕`); } } // 验证激活状态 function verifyActivation(item) { timeoutId = null; if (item.classList.contains('bpx-state-active')) { console.log('中文字幕激活成功'); isActive = true; } else { console.log('中文字幕激活失败'); if (attemptCount < MAX_ATTEMPTS) { timeoutId = setTimeout(tryClickChineseSubtitle, RETRY_DELAY); } } } // 关闭字幕 function closeSubtitles() { const closeButton = document.querySelector('.bpx-player-ctrl-subtitle-close-switch[data-action="close"]'); if (closeButton && !closeButton.classList.contains('bpx-state-active')) { console.log('找到关闭字幕按钮,执行点击'); closeButton.click(); isActive = false; } else { console.log('关闭字幕按钮已激活或未找到'); } } // 倍速控制相关函数 function applySavedPlaybackRate() { const rateMenu = document.querySelector('.bpx-player-ctrl-playbackrate-menu'); if (!rateMenu) { if (attemptCount < MAX_ATTEMPTS) { timeoutId = setTimeout(applySavedPlaybackRate, RETRY_DELAY); attemptCount++; return; } console.log('未找到倍速菜单'); return; } const currentActive = rateMenu.querySelector('.bpx-state-active'); if (currentActive) { const activeRate = parseFloat(currentActive.dataset.value); if (Math.abs(activeRate - currentRate) > 0.01) { console.log(`当前倍速(${activeRate}x)与保存倍速(${currentRate}x)不一致,正在调整`); setPlaybackRate(currentRate); } else { console.log(`当前倍速(${activeRate}x)与保存倍速一致,无需调整`); } } else { console.log('未找到激活的倍速选项,尝试设置'); setPlaybackRate(currentRate); } } function setPlaybackRate(rate) { // 确保rate是支持的倍速 const supportedRate = playbackRates.find(r => Math.abs(r - rate) < 0.01) || 1; currentRate = supportedRate; GM_setValue('bilibili_playback_rate', currentRate); const rateMenu = document.querySelector('.bpx-player-ctrl-playbackrate-menu'); if (rateMenu) { const items = rateMenu.querySelectorAll('.bpx-player-ctrl-playbackrate-menu-item'); let found = false; items.forEach(item => { const itemRate = parseFloat(item.dataset.value); if (Math.abs(itemRate - currentRate) < 0.01) { found = true; if (!item.classList.contains('bpx-state-active')) { item.click(); console.log(`已设置倍速为 ${currentRate}x`); showRateMessage(`倍速已设为 ${currentRate}x`); } } }); if (!found) { console.log(`未找到 ${currentRate}x 的倍速选项`); } } else { console.log('未找到倍速菜单'); if (attemptCount < MAX_ATTEMPTS) { timeoutId = setTimeout(() => setPlaybackRate(rate), RETRY_DELAY); attemptCount++; } } } function increasePlaybackRate() { const currentIndex = playbackRates.findIndex(r => Math.abs(r - currentRate) < 0.01); if (currentIndex > 0) { const newRate = playbackRates[currentIndex - 1]; setPlaybackRate(newRate); } else { showRateMessage('已经是最大倍速 (2x)'); console.log('已经是最大倍速 (2x)'); } } function decreasePlaybackRate() { const currentIndex = playbackRates.findIndex(r => Math.abs(r - currentRate) < 0.01); if (currentIndex < playbackRates.length - 1) { const newRate = playbackRates[currentIndex + 1]; setPlaybackRate(newRate); } else { showRateMessage('已经是最小倍速 (0.5x)'); console.log('已经是最小倍速 (0.5x)'); } } function showRateMessage(message) { // 移除旧的消息 const oldNotice = document.querySelector('.bpx-player-dm-notice'); if (oldNotice) oldNotice.remove(); // 创建新消息元素 const notice = document.createElement('div'); notice.className = 'bpx-player-dm-notice'; notice.textContent = message; // 添加到播放器容器 const playerContainer = document.querySelector('.bpx-player-container') || document.body; playerContainer.appendChild(notice); // 2秒后自动移除 setTimeout(() => { if (notice.parentNode) { notice.parentNode.removeChild(notice); } }, 2000); } // 启动脚本 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();