- // ==UserScript==
- // @name 哔哩哔哩(B站, bilibili)播放界面和部分操作优化
- // @description B站播放器速度自定义(0.25 ~ 3), 支持快捷键(z:正常, x:减少速度, c:增加速度), 鼠标中键切换全屏等
- // @namespace bili
- // @version 1.6.29
- // @author vizo
- // @license MIT
- // @include *bilibili.com/video/*
- // @include *bilibili.com/bangumi/play*
- // @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
- // @require https://unpkg.com/jquery@3.6.0/dist/jquery.min.js
- // @require https://unpkg.com/vio-utils@2.7.8/index.js
- // @require https://unpkg.com/@vizoy/tmk-utils@1.2.18/index.js
- // @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: 999888;
- 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-speed {
- opacity: 0.4;
- pointer-events: none;
- }
- .bilibili-player-video-btn-speed.on {
- opacity: 1;
- pointer-events: auto;
- }
- .bilibili-player-volumeHint,
- .bpx-player-volume-hint {
- display: none !important;
- }
- .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;
- }
- `)
-
- Object.assign(TMK, vio)
-
- const pks = ['video', 'bangumi', 'medialist']
- if (
- pks.some(v => location.pathname.includes(v))
- && !location.host.includes('search')
- && document.documentElement.scrollTop === 0
- ) {
- document.documentElement.classList.add('hideScroll')
- }
-
- const G = {
- timer2s: 0,
- timer3s: 0,
- focusChangeTime: 0,
- g2URL: '',
- }
-
- async function vdoWp() {
- const sltor = location.pathname.includes('bangumi') ?
- '.bpx-player-video-area' :
- '#bilibiliPlayer'
- return (await TMK.loadEl(sltor))
- }
- async function appendMsgLay() {
- let wp = await vdoWp()
- let msg = document.getElementById('spsy_msg')
- if (!wp.contains(msg)) {
- wp.insertAdjacentHTML('beforeend', `<div id="spsy_msg"></div>`)
- }
- }
- async function appendAxInfo() {
- let wp = await vdoWp()
- 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()) {
- const vd = await vdo()
- if (vd) {
- vd.playbackRate = speedVal
- }
- if (vd.nodeName === 'bwp-video' || vd?.nodeName?.includes('bwp-video')) {
- $('.bilibili-player-video-btn-speed').addClass('on')
- }
- setGMspeed(speedVal)
- setAxInfo()
- }
- // 设置音量
- async function setVolume(vol = getGMvolume()) {
- const vd = await vdo()
- if (vd) {
- vd.volume = vol
- }
- setGMvolume(vol)
- setAxInfo()
- }
-
- async function vdo() {
- const node = await TMK.loadEl('video, bwp-video')
- return node.length > 1 ? node[0] : node
- }
-
- function setVdoCfg() {
- appendMsgLay()
- appendAxInfo()
- setPlayerSpeed()
- setVolume()
- }
-
- function runVdoTimer() {
- setVdoCfg()
- setTimeout(runVdoTimer, 2000)
- }
-
- async function regWpMouseEvt() {
- const wwp = $('#playerWrap')
- const wp = wwp.length ? wwp : await vdoWp()
- $(wp).on('mouseenter', () => {
- $('html').addClass('hideScroll')
- })
- $(wp).on('mouseleave', () => {
- $('html').removeClass('hideScroll')
- })
- }
-
- function watchUrlFunc() {
- if (G.g2URL !== location.href) {
- G.g2URL = location.href
- eachVideoList()
- }
- setTimeout(watchUrlFunc, 500)
- }
-
- function tryAppendSoMod() {
- if ($('.so-fg5r').length) return
- 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 watchContains() {
- TMK.watchDom('.contain', () => {
- tryAppendSoMod()
- })
- }
-
- function eachVideoList() {
- 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')
- }
- })
-
- microScroll()
- }
-
- function microScroll() {
- clearTimeout(G.timer3s)
- G.timer3s = setTimeout(async () => {
- const st = document.documentElement.scrollTop
- window.scrollTo(0, st + 1)
- await vio.timeout(100)
- window.scrollTo(0, st)
- }, 500)
- }
-
- function regPageKey() {
- $('body').on('keydown', function(e) {
- try {
- if (/left/i.test(e.key)) {
- $('.page-item.prev > button')[0].click()
- }
- if (/right/i.test(e.key)) {
- $('.page-item.next > button')[0].click()
- }
- } catch(e) {}
- })
- }
-
- // 筛选搜索结果
- $('body').on('input', '.inp-s, .inp-e', function() {
- eachVideoList()
- })
-
- async function initVolumeLabel(val = getGMspeed()) {
- const vArr = [0.5, 0.75, 1, 1.25, 1.5, 2]
- if (!vArr.includes(val)) return
- const lbs = await TMK.loadEl('.bilibili-player-video-btn-speed-menu-list')
- $(lbs).each((i, v) => {
- const tis = $(v)
- const vol = tis.attr('data-value')
- if (vol && vol === String(val)) {
- v.click()
- }
- })
- }
-
-
- function regKbEvt() {
- window.addEventListener('keydown', async e => {
- if (e.target.nodeName !== 'BODY') return
-
- if (/^[zxc]$/.test(e.key) && !e.altKey && !e.ctrlKey) {
- await TMK.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)
- initVolumeLabel(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(TMK.floor(vol))
- showSpMsg(Math.trunc(vol * 100), '音量')
- }
- })
-
- // ctrl和alt按下后短时间内阻止zxc
- $('body').on('keydown', function(e) {
- if (/up|down/i.test(e.key)) {
- e.stopPropagation()
- e.preventDefault()
- }
- })
- }
-
- function regMouseEvt() {
- window.addEventListener('wheel', async (e) => {
- const wp = await vdoWp()
- const isContains = wp.contains(e.target)
-
- if (!isContains) return
-
- let vol = getGMvolume()
- $('video').each((i, v) => {
- if ($(v).attr('src')) {
- v.muted = false
- }
- })
-
- 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 = TMK.floor(vol)
- setVolume(vol)
-
- let pVol = Math.trunc(vol * 100)
- showSpMsg(pVol, '音量')
- })
-
- // 滚轮中键点击(滚轮点击)切换全屏
- $('body').on('mousedown', '.bilibili-player-video-wrap,.bpx-player-video-area', function(e) {
- if (e.button === 1) {
- e.preventDefault()
- toggleVideoFullscreen()
- }
- })
- }
-
- async function initFunc() {
- ['focus', 'blur'].forEach(v => {
- window.addEventListener(v, e => {
- G.focusChangeTime = Date.now()
- })
- })
-
- if (location.host.includes('search.bilibili.com')) {
- watchContains()
- watchUrlFunc()
- regPageKey()
- } else {
- runVdoTimer()
- regWpMouseEvt()
- regKbEvt()
- regMouseEvt()
- initVolumeLabel()
- }
- }
-
- initFunc()
-