您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
B站播放器速度自定义(0.25 ~ 3), 支持快捷键(z:正常, x:减少速度, c:增加速度), 鼠标中键切换全屏等
当前为
// ==UserScript== // @name 哔哩哔哩(B站, bilibili)播放界面和部分操作优化 // @description B站播放器速度自定义(0.25 ~ 3), 支持快捷键(z:正常, x:减少速度, c:增加速度), 鼠标中键切换全屏等 // @namespace bili // @version 1.6.16 // @author vizo // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js // @include *bilibili.com/video/* // @include *bilibili.com/bangumi/* // @include *bilibili.com/medialist/* // @include *bilibili.com/cheese/play* // @include *search.bilibili.com* // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @run-at document-end // @noframes // ==/UserScript== GM_addStyle(` html { overflow-y: scroll; } html.hideScroll { overflow: hidden; margin-left: -3px; } body::-webkit-scrollbar { width: 6px; } body::-webkit-scrollbar-corner, body::-webkit-scrollbar-track { background-color: #f8f8f8; } body::-webkit-scrollbar-thumb { background: #c5c5c5; } #bilibili-player { position: relative; } #spsy_msg { width: 105px; height: 42px; text-align: center; line-height: 42px; border-radius: 4px; background: rgba(255,255,255,.8); color: #222; font-size: 16px; position: absolute; top: -80px; right: 0; bottom: 0; left: 0; margin: auto; z-index: 99999; pointer-events: none; display: none; } #bl_info_xz { height: 25px; line-height: 25px; font-size: 14px; padding: 0 5px; color: #00a1d6; position: absolute; top: -25px; right: 0; z-index: 2; } .bilibili-player-video-btn-volume, .bilibili-player-video-btn-speed { opacity: 0.4; pointer-events: none; } .bilibili-player-volumeHint { display: none !important; } .so-fg5r { } .so-fg5r .inp { width: 50px; color: #666; border: 1px solid #0ad; border-radius: 2px; padding: 2px 5px; outline: none; } .so-fg5r .inp:focus { border-color: #0ad; } .so-fg5r span { color: #666; } .video-item.hide_7s { display: none !important; } `) const pks = ['video', 'bangumi', 'medialist'] if ( pks.some(v => location.pathname.includes(v)) && !location.host.includes('search') ) { document.documentElement.classList.add('hideScroll') } const G = { vdo: null, timer1s: 0, timer2s: 0, timer3s: 0, focusChangeTime: 0, g2URL: '', } function timeout(ms = 0) { return new Promise(resolve => setTimeout(resolve, ms)) } function round(val, n = 2) { return Number(`${Math.round(`${val}e${n}`)}e-${n}`) } function floor(val, n = 2) { return Number(`${Math.floor(`${val}e${n}`)}e-${n}`) } function loadEl(fnz, timeout = 8000) { return new Promise(resolve => { const iFn = () => { let el = typeof fnz === 'function' ? fnz() : $(fnz) if (el.length) { return resolve(el) } setTimeout(iFn, 50) } iFn() setTimeout(() => { return resolve($(null)) }, timeout) }) } function watchDom(el, ...args) { if (typeof el === 'string') { el = document.querySelector(el) } let opt = args.length > 1 ? args[0] : {} let cb = args.length > 1 ? args[1] : args[0] return new MutationObserver((mutations, observer) => { cb(mutations) }).observe(el, Object.assign({ childList: true, attributes: true, characterData: true, subtree: true, }, opt)) } async function getVideoWrap() { const sltor = location.pathname.includes('bangumi') ? '#bilibili-player' : '#bilibiliPlayer' return (await loadEl(sltor))[0] } async function appendMsgLay() { let wp = await getVideoWrap() let msg = document.getElementById('spsy_msg') if (!wp.contains(msg)) { wp.insertAdjacentHTML('beforeend', `<div id="spsy_msg"></div>`) } } async function appendAxInfo() { let wp = await getVideoWrap() let inf = document.getElementById('bl_info_xz') if (!wp.contains(inf)) { wp.insertAdjacentHTML('beforeend', `<div id="bl_info_xz"></div>`) } } function setAxInfo() { let inf = $('#bl_info_xz') let vol = Math.trunc(getGMvolume() * 100) let speed = getGMspeed() if (inf.length) { speed = speed === 1 ? speed : `<span style="color:#f33;">${speed}</span>` inf.html(`<span>速度: ${speed} 音量: ${vol}</span>`) } } function toggleVideoFullscreen() { try { $('.bilibili-player-video-btn-fullscreen')[0].click() } catch (err) {} try { $('.squirtle-video-fullscreen > div')[0].click() } catch (err) {} } function setGMspeed(val) { return GM_setValue('--> bl_player_speed', val) } function setGMvolume(val) { GM_setValue('--> bl_player_volume', val) } function getGMspeed() { return +GM_getValue('--> bl_player_speed') || 1 } function getGMvolume() { let vol = GM_getValue('--> bl_player_volume') return vol !== undefined ? vol : 0.5 } // 判断是否全屏 function isFullScreen() { return document.isFullScreen || document.mozIsFullScreen || document.webkitIsFullScreen } // 显示信息 function showSpMsg(msg, type = '速度') { let mp = $('#spsy_msg') clearTimeout(G.timer2s) mp.fadeIn(180) mp.text(`${type} ${msg}`) G.timer2s = setTimeout(() => { mp.fadeOut(350) }, 800) } // 设置播放器播放速度 async function setPlayerSpeed(speedVal = getGMspeed()) { G.vdo.playbackRate = speedVal setGMspeed(speedVal) setAxInfo() } // 设置音量 async function setVolume(vol = getGMvolume()) { G.vdo.volume = vol setGMvolume(vol) setAxInfo() } function initVideoCfg() { clearTimeout(G.timer3s) G.timer3s = setTimeout(() => { appendMsgLay() appendAxInfo() setPlayerSpeed() setVolume() }, 10) } function debounce(func, delay) { let timer = null return function() { timer && clearTimeout(timer) timer = setTimeout(() => { func.apply(this, arguments) }, delay) } } function zTimer() { initVideoCfg() setTimeout(zTimer, 2000) } async function regWpMouseEvt() { const wwp = $('#playerWrap') const wp = wwp.length ? wwp : await getVideoWrap() $(wp).on('mouseenter', () => { $('html').addClass('hideScroll') }) $(wp).on('mouseleave', () => { $('html').removeClass('hideScroll') }) } function watchUrlFunc() { if (G.g2URL !== location.href) { G.g2URL = location.href eachSearchResultAndAddHideCls() } setTimeout(watchUrlFunc, 500) } function judgeIsAppendSoMod() { const html = ` <div class="so-fg5r"> <input class="inp inp-s" type="text"> <span> - </span> <input class="inp inp-e" type="text"> <span>分钟</span> </div> ` $('ul.filter-type.duration').append(html) } function eachSearchResultAndAddHideCls() { const min = $('.inp-s').val() || 0 const max = $('.inp-e').val() || 1e9 if (!min && !max) { $('.video-list > li.video-item').each((i, v) => { $(v).removeClass('hide_7s') }) return } $('.video-list > li.video-item').each((i, v) => { let tis = $(v) tis.removeClass('hide_7s') let sTime = tis.find('.so-imgTag_rb').text() let mth = sTime.match(/\d{2}(?=(\:\d{2}){2})/) let hour = mth ? mth[0] : 0 let minute = sTime.replace(/(?:\d{2}\:)?(\d{2})\:\d{2}/, '$1') let total = Number(hour) * 60 + Number(minute) if (total < Number(min) || total > Number(max)) { tis.closest('.video-item').addClass('hide_7s') } }) } // 滚轮中键点击(滚轮点击)切换全屏 $('body').on('mousedown', '.bilibili-player-video-wrap,.bpx-player-video-area', function(e) { if (e.button === 1) { e.preventDefault() toggleVideoFullscreen() } }) // 筛选搜索结果 $('body').on('input', '.inp-s, .inp-e', function() { eachSearchResultAndAddHideCls() }) // ctrl和alt按下后短时间内阻止zxc $('body').on('keydown', function(e) { if (/up|down/i.test(e.key)) { e.stopPropagation() e.preventDefault() } }) // 键盘快捷键 $('body').on('keyup', async function(e) { if (e.target.nodeName !== 'BODY') return if (/^[zxc]$/.test(e.key)) { await timeout(100) if (Date.now() - G.focusChangeTime < 1000) return let val = getGMspeed() if (e.key === 'z') { val = 1 } if (e.key === 'x') { val = Math.max(val - 0.25, 0.25) } if (e.key === 'c') { val = Math.min(val + 0.25, 3) } setPlayerSpeed(val) showSpMsg(val) } if (/up|down/i.test(e.key)) { let vl = getGMvolume() let vol = e.key.includes('Up') ? Math.min(vl + 0.03, 1) : Math.max(vl - 0.03, 0) setVolume(floor(vol)) showSpMsg(Math.trunc(vol * 100), '音量') } }) // 滚轮调节音量 window.addEventListener('wheel', async (e) => { const wp = await getVideoWrap() const isContains = wp.contains(e.target) if (!isContains) return let vol = getGMvolume() if (e.deltaY > 0) { // 减少音量 vol = Math.max(vol - (e.altKey ? 0.1 : 0.03), 0) } else { // 增加音量 vol = Math.min(vol + (e.altKey ? 0.1 : 0.03), 1) } vol = floor(vol) setVolume(vol) let pVol = Math.trunc(vol * 100) showSpMsg(pVol, '音量') }) async function initFunc() { ['focus', 'blur'].forEach(v => { window.addEventListener(v, e => { G.focusChangeTime = Date.now() }) }) if (location.host.includes('search.bilibili.com')) { judgeIsAppendSoMod() watchUrlFunc() } else { zTimer() G.vdo = (await loadEl('video'))[0] G.vdo.addEventListener('play', () => { initVideoCfg() regWpMouseEvt() }) regWpMouseEvt() } } initFunc()