// ==UserScript== // @name B站播放器自定义倍速选项 // @namespace https://github.com/yourname // @version 1.0.0 // @description 为B站视频播放器添加自定义倍数选项(2.5x, 3.0x, 4.0x等) // @author You // @match https://www.bilibili.com/video/* // @match https://www.bilibili.com/bangumi/play/* // @icon https://www.bilibili.com/favicon.ico // @grant none // @run-at document-end // ==/UserScript== (function() { 'use strict'; // 自定义倍数列表,按需修改这里即可 const CUSTOM_SPEEDS = [ { value: 3.0, label: '3.0x' }, { value: 4.0, label: '4.0x' }, // { value: 5.0, label: '5.0倍数'} ]; // 等待播放器加载完成的函数 function waitForPlayer(callback) { const observer = new MutationObserver((mutations, obs) => { // 查找倍速菜单容器 const menuContainer = document.querySelector('.bpx-player-ctrl-playbackrate'); if (menuContainer) { obs.disconnect(); callback(); return; } // 也检查video元素是否存在 const video = document.querySelector('video'); if (video && video.readyState >= 1) { obs.disconnect(); callback(); } }); observer.observe(document.body, { childList: true, subtree: true }); // 超时保护,5秒后不再等待 setTimeout(() => { observer.disconnect(); if (document.querySelector('video')) { callback(); } }, 5000); } // 添加自定义倍速选项到菜单 function addCustomSpeedOptions() { // 查找倍速菜单的ul容器 let menuList = document.querySelector('.bpx-player-ctrl-playbackrate-menu'); // 如果还没加载,等待一下 if (!menuList) { // 尝试点击倍速按钮来触发菜单生成(静默触发,不影响体验) const speedBtn = document.querySelector('.bpx-player-ctrl-playbackrate'); if (speedBtn && !menuList) { // 临时触发一下让DOM生成但不显示(通过class检测) // 实际上B站的菜单是hover时动态生成的,所以需要等待用户hover } return false; } // 检查是否已经添加过,避免重复添加 if (menuList.querySelector('.custom-speed-item')) { return true; } // 添加分割线 const divider = document.createElement('li'); divider.className = 'custom-speed-divider'; divider.style.borderTop = '1px solid rgba(255, 255, 255, 0.1)'; divider.style.margin = '4px 0'; divider.style.height = '1px'; menuList.appendChild(divider); // 添加自定义倍速选项 CUSTOM_SPEEDS.forEach(speed => { const menuItem = document.createElement('li'); menuItem.className = 'bpx-player-ctrl-playbackrate-menu-item custom-speed-item'; menuItem.setAttribute('data-value', speed.value); menuItem.innerHTML = `
`; // 添加点击事件 menuItem.addEventListener('click', (e) => { e.stopPropagation(); setPlaybackRate(speed.value); updateActiveMenuItem(menuList, menuItem); }); menuList.appendChild(menuItem); }); return true; } // 设置播放速度 function setPlaybackRate(rate) { const video = document.querySelector('video'); if (video) { video.playbackRate = rate; // 更新倍速按钮上显示的当前速度文字 const speedResult = document.querySelector('.bpx-player-ctrl-playbackrate-result'); if (speedResult) { speedResult.innerText = rate + 'x'; } // 可选:显示短暂提示 showToast(`${rate}x 倍速`); } } // 显示提示(可选) function showToast(message) { // 检查是否已有提示框 let toast = document.querySelector('.custom-speed-toast'); if (!toast) { toast = document.createElement('div'); toast.className = 'custom-speed-toast'; toast.style.cssText = ` position: fixed; bottom: 100px; right: 20px; background: rgba(0, 0, 0, 0.7); color: #fff; padding: 8px 16px; border-radius: 8px; font-size: 14px; z-index: 10000; pointer-events: none; transition: opacity 0.3s; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; `; document.body.appendChild(toast); } toast.innerText = message; toast.style.opacity = '1'; setTimeout(() => { toast.style.opacity = '0'; }, 1500); } // 更新菜单项的激活状态 function updateActiveMenuItem(menuList, activeItem) { const allItems = menuList.querySelectorAll('.bpx-player-ctrl-playbackrate-menu-item'); allItems.forEach(item => { item.classList.remove('bpx-state-active'); }); activeItem.classList.add('bpx-state-active'); } // 监听倍速菜单的打开事件(B站菜单是hover时动态生成的) function bindSpeedButtonHover() { const speedButton = document.querySelector('.bpx-player-ctrl-playbackrate'); if (!speedButton) return; let menuAdded = false; // 使用mouseenter事件,当鼠标进入倍速按钮区域时添加菜单项 const onSpeedButtonEnter = () => { if (!menuAdded) { // 稍微延迟等待菜单DOM生成 setTimeout(() => { const success = addCustomSpeedOptions(); if (success) { menuAdded = true; } }, 100); } }; speedButton.addEventListener('mouseenter', onSpeedButtonEnter); // 也通过MutationObserver监听菜单的添加 const observer = new MutationObserver(() => { if (!menuAdded) { const success = addCustomSpeedOptions(); if (success) { menuAdded = true; } } }); observer.observe(document.body, { childList: true, subtree: true }); } // 监听video元素,确保倍速设置后能保持(解决切换清晰度等问题) function watchVideoElement() { let currentVideo = document.querySelector('video'); if (!currentVideo) return; // 监听video元素的属性变化,如果playbackRate被重置则恢复 const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'attributes' && mutation.attributeName === 'playbackRate') { // 可以在这里记录或恢复,根据需求 } }); }); observer.observe(currentVideo, { attributes: true }); // 监听新视频加载(SPA页面切换) const pageObserver = new MutationObserver(() => { const newVideo = document.querySelector('video'); if (newVideo && newVideo !== currentVideo) { currentVideo = newVideo; observer.disconnect(); observer.observe(currentVideo, { attributes: true }); } }); pageObserver.observe(document.body, { childList: true, subtree: true }); } // 初始化 function init() { waitForPlayer(() => { bindSpeedButtonHover(); watchVideoElement(); // 额外尝试:如果菜单已经存在就直接添加 setTimeout(() => { addCustomSpeedOptions(); }, 1000); }); } // 页面完全加载后启动 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();