// ==UserScript== // @name B站播放器自定义倍速选项 // @namespace https://github.com/yourname // @version 1.1.0 // @description 为B站视频播放器添加自定义倍数选项(2.5x, 3.0x, 4.0x等),并支持 Shift+数字 快捷键 // @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 // @license MIT // ==/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; } const video = document.querySelector('video'); if (video && video.readyState >= 1) { obs.disconnect(); callback(); } }); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(() => { observer.disconnect(); if (document.querySelector('video')) { callback(); } }, 5000); } // 添加自定义倍速选项到菜单 function addCustomSpeedOptions() { let menuList = document.querySelector('.bpx-player-ctrl-playbackrate-menu'); if (!menuList) { 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 = `
${speed.label}
`; 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'); } // 根据倍速值更新菜单激活状态(快捷键用) function updateActiveMenuItemByValue(value) { const menuList = document.querySelector('.bpx-player-ctrl-playbackrate-menu'); if (!menuList) return; const items = menuList.querySelectorAll('.bpx-player-ctrl-playbackrate-menu-item'); items.forEach(item => { const val = parseFloat(item.getAttribute('data-value')); if (val === value) { item.classList.add('bpx-state-active'); } else { item.classList.remove('bpx-state-active'); } }); } // 绑定倍速按钮悬停事件(生成菜单) function bindSpeedButtonHover() { const speedButton = document.querySelector('.bpx-player-ctrl-playbackrate'); if (!speedButton) return; let menuAdded = false; const onSpeedButtonEnter = () => { if (!menuAdded) { setTimeout(() => { const success = addCustomSpeedOptions(); if (success) { menuAdded = true; } }, 100); } }; speedButton.addEventListener('mouseenter', onSpeedButtonEnter); 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; const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'attributes' && mutation.attributeName === 'playbackRate') { // 可在此记录或恢复 } }); }); observer.observe(currentVideo, { attributes: true }); 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 bindKeyboardShortcuts() { document.addEventListener('keydown', function(e) { // 必须按下 Shift 键,且不能与其他修饰键组合 if (!e.shiftKey || e.ctrlKey || e.altKey || e.metaKey) return; // 检测物理按键 Digit1 ~ Digit9 const code = e.code; if (code.startsWith('Digit')) { const digit = parseInt(code.charAt(5)); // 'Digit1' -> 1 if (digit >= 1 && digit <= 9) { const index = digit - 1; if (index < CUSTOM_SPEEDS.length) { const speed = CUSTOM_SPEEDS[index]; setPlaybackRate(speed.value); updateActiveMenuItemByValue(speed.value); // 阻止默认行为(如浏览器某些快捷键) e.preventDefault(); e.stopPropagation(); } } } }); } // ------------------------- // 初始化 function init() { waitForPlayer(() => { bindSpeedButtonHover(); watchVideoElement(); bindKeyboardShortcuts(); // 绑定快捷键 setTimeout(() => { addCustomSpeedOptions(); }, 1000); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();