// ==UserScript== // @name 哔哩哔哩(bilibili.com)播放页调整 // @license GPL-3.0 License // @namespace https://greasyfork.org/zh-CN/scripts/415804-bilibili%E6%92%AD%E6%94%BE%E9%A1%B5%E8%B0%83%E6%95%B4-%E8%87%AA%E7%94%A8 // @version 0.39 // @description 1.自动定位到播放器(进入播放页,可自动定位到播放器,可设置偏移量及是否在点击主播放器时定位);2.可设置是否自动选择最高画质;3.可设置播放器默认模式; // @author QIAN // @match *://*.bilibili.com/video/* // @match *://*.bilibili.com/bangumi/play/* // @match *://*.bilibili.com/list/* // @run-at document-start // @require https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js // @require https://unpkg.com/sweetalert2@11.7.2/dist/sweetalert2.min.js // @resource swalStyle https://unpkg.com/sweetalert2@11.7.2/dist/sweetalert2.min.css // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_getResourceText // @grant GM.info // @supportURL https://github.com/QIUZAIYOU/Bilibili-VideoPage-Adjustment // @homepageURL https://github.com/QIUZAIYOU/Bilibili-VideoPage-Adjustment // @icon https://www.bilibili.com/favicon.ico?v=1 // @downloadURL https://update.greasyfork.cloud/scripts/415804/%E5%93%94%E5%93%A9%E5%93%94%E5%93%A9%EF%BC%88bilibilicom%EF%BC%89%E6%92%AD%E6%94%BE%E9%A1%B5%E8%B0%83%E6%95%B4.user.js // @updateURL https://update.greasyfork.cloud/scripts/415804/%E5%93%94%E5%93%A9%E5%93%94%E5%93%A9%EF%BC%88bilibilicom%EF%BC%89%E6%92%AD%E6%94%BE%E9%A1%B5%E8%B0%83%E6%95%B4.meta.js // ==/UserScript== $(() => { 'use strict' let { currentUrl, theMainFunctionRunningTimes, thePrepFunctionRunningTimes, autoSelectScreenModeTimes, autoCancelMuteTimes, webfullUnlockTimes, insertGoToCommentsButtonTimes, autoSelectVideoHighestQualityTimes, functionExecutionsTimes = 0, } = { currentUrl: window.location.href, theMainFunctionRunningTimes: 0, thePrepFunctionRunningTimes: 0, autoSelectScreenModeTimes: 0, autoCancelMuteTimes: 0, webfullUnlockTimes: 0, insertGoToCommentsButtonTimes: 0, autoSelectVideoHighestQualityTimes: 0, } const { getValue, setValue, sleep, addStyle, historyListener, checkBrowserHistory, throttle, getClientHeight, checkElementExistence, isDocumentHidden, isLogin, logger, checkPageReadyState, pageReload, scrollToPlayer } = { getValue (name) { return GM_getValue(name) }, setValue (name, value) { GM_setValue(name, value) }, sleep (time) { return new Promise(resolve => setTimeout(resolve, time)) }, addStyle (id, tag, css) { tag = tag || 'style' const doc = document const styleDom = doc.getElementById(id) if (styleDom) return const style = doc.createElement(tag) style.rel = 'stylesheet' style.id = id tag === 'style' ? (style.innerHTML = css) : (style.href = css) document.head.appendChild(style) }, historyListener () { class Dep { constructor(name) { this.id = new Date() this.subs = [] } defined () { Dep.watch.add(this) } notify () { this.subs.forEach((e, i) => { if (typeof e.update === 'function') { try { e.update.apply(e) } catch (err) { console.warr(err) } } }) } } Dep.watch = null class Watch { constructor(name, fn) { this.name = name this.id = new Date() this.callBack = fn } add (dep) { dep.subs.push(this) } update () { var cb = this.callBack cb(this.name) } } var addHistoryMethod = (function () { var historyDep = new Dep() return function (name) { if (name === 'historychange') { return function (name, fn) { var event = new Watch(name, fn) Dep.watch = event historyDep.defined() Dep.watch = null } } else if (name === 'pushState' || name === 'replaceState') { var method = history[name] return function () { method.apply(history, arguments) historyDep.notify() // logger.info("访问历史|变化") } } } })() window.addHistoryListener = addHistoryMethod('historychange') history.pushState = addHistoryMethod('pushState') history.replaceState = addHistoryMethod('replaceState') window.addHistoryListener('history', function () { const throttleAutoLocation = throttle(m.autoLocation, 500) throttleAutoLocation() }) }, checkBrowserHistory () { window.addEventListener('popstate', () => { m.autoLocation() }) }, throttle (func, delay) { let wait = false return (...args) => { if (wait) { return } func(...args) wait = true setTimeout(() => { wait = false }, delay) } }, getClientHeight () { const bodyHeight = document.body.clientHeight || 0 const docHeight = document.documentElement.clientHeight || 0 return bodyHeight < docHeight ? bodyHeight : docHeight }, // 检查指定HTML元素是否存在 checkElementExistence (selector, maxAttempts, interval) { // functionExecutionsTimes += 1 // const funName = (new Error()).stack.split("\n")[2].trim().split(" ")[1].replace('Object.', '') // logger.debug(`(调用:${functionExecutionsTimes}) ${funName} -> ${selector}`) return new Promise(resolve => { let attempts = 0 const intervalId = setInterval(() => { attempts++ // logger.debug(`(尝试:${attempts}) -> ${selector}`) const element = $(selector) if (element.length) { clearInterval(intervalId) resolve(true) } else if (attempts === maxAttempts) { clearInterval(intervalId) resolve(false) } }, interval) }) }, isDocumentHidden () { const visibilityChangeEventNames = ['visibilitychange', 'mozvisibilitychange', 'webkitvisibilitychange', 'msvisibilitychange'] const documentHiddenPropertyName = visibilityChangeEventNames.find(name => name in document) || 'onfocusin' in document || 'onpageshow' in window ? 'hidden' : null if (documentHiddenPropertyName !== null) { const isHidden = () => document[documentHiddenPropertyName] const onChange = () => isHidden() // 添加各种事件监听器 visibilityChangeEventNames.forEach(eventName => document.addEventListener(eventName, onChange)) window.addEventListener('focus', onChange) window.addEventListener('blur', onChange) window.addEventListener('pageshow', onChange) window.addEventListener('pagehide', onChange) document.onfocusin = document.onfocusout = onChange return isHidden() } // 如果无法判断是否隐藏,则返回undefined return undefined }, isLogin () { return Boolean(document.cookie.replace(new RegExp(String.raw`(?:(?:^|.*;\s*)bili_jct\s*=\s*([^;]*).*$)|^.*$`), '$1') || null) }, logger: { info (content) { console.info('%c播放页调整', 'color:white;background:#006aff;padding:2px;border-radius:2px', content) }, warn (content) { console.warn('%c播放页调整', 'color:white;background:#ff6d00;padding:2px;border-radius:2px', content) }, error (content) { console.error('%c播放页调整', 'color:white;background:#f33;padding:2px;border-radius:2px', content) }, debug (content) { console.info('%c播放页调整(调试)', 'color:white;background:#cc00ff;padding:2px;border-radius:2px', content) }, }, checkPageReadyState (state) { return new Promise((resolve) => { const timer = setInterval(() => { if (document.readyState === state) { clearInterval(timer) resolve(true) } }, 100) }) }, pageReload () { if (auto_reload) location.reload(true) }, scrollToPlayer (offset) { $('html,body').scrollTop(offset) } } const { is_vip, player_type, offset_top, auto_locate, auto_locate_video, auto_locate_bangumi, click_player_auto_locate, player_offset_top, current_screen_mode, selected_screen_mode, auto_select_video_highest_quality, contain_quality_4k, contain_quality_8k, webfull_unlock, auto_reload } = { is_vip: getValue('is_vip'), player_type: getValue('player_type'), offset_top: Math.trunc(getValue('offset_top')), auto_locate: getValue('auto_locate'), auto_locate_video: getValue('auto_locate_video'), auto_locate_bangumi: getValue('auto_locate_bangumi'), click_player_auto_locate: getValue('click_player_auto_locate'), player_offset_top: Math.trunc(getValue('player_offset_top')), current_screen_mode: getValue('current_screen_mode'), selected_screen_mode: getValue('selected_screen_mode'), auto_select_video_highest_quality: getValue('auto_select_video_highest_quality'), contain_quality_4k: getValue('contain_quality_4k'), contain_quality_8k: getValue('contain_quality_8k'), webfull_unlock: getValue('webfull_unlock'), auto_reload: getValue('auto_reload') } const m = { // 初始化设置参数 initValue () { const value = [{ name: 'is_vip', value: false, }, { name: 'player_type', value: 'video', }, { name: 'offset_top', value: 7, }, { name: 'player_offset_top', value: 160, }, { name: 'auto_locate', value: true, }, { name: 'auto_locate_video', value: true, }, { name: 'auto_locate_bangumi', value: true, }, { name: 'click_player_auto_locate', value: true, }, { name: 'current_screen_mode', value: 'normal', }, { name: 'selected_screen_mode', value: 'wide', }, { name: 'auto_select_video_highest_quality', value: true, }, { name: 'contain_quality_4k', value: false, }, { name: 'contain_quality_8k', value: false, }, { name: 'webfull_unlock', value: false, }, { name: 'auto_reload', value: false, }] value.forEach(v => { if (getValue(v.name) === undefined) { setValue(v.name, v.value) } }) }, // 检查视频资源是否加载完毕并处于可播放状态 async checkVideoCanPlayThrough () { const BwpVideoPlayerExists = await checkElementExistence('bwp-video', 10, 10) // logger.debug(`bwp-video|${BwpVideoPlayerExists?'存在':'不存在'}`) if (BwpVideoPlayerExists) { return new Promise(resolve => { resolve(true) }) } const $video = $('#bilibili-player video') const videoReadyState = $video[0].readyState // logger.debug(`视频资源|${videoReadyState>=4?'可播放':'不可播放'}` if (videoReadyState >= 4) { return new Promise(resolve => { resolve(true) }) } else { return new Promise(resolve => { const checkTimeout = setTimeout(() => { // logger.error('视频资源|脚本检测失败|重载页面') pageReload() resolve(false) }, 7000) $video.on('canplaythrough', () => { // logger.info("视频资源加载|成功") let attempts = 100 const timer = setInterval(() => { const isHidden = $('#bilibili-player .bpx-player-container').attr('data-ctrl-hidden') if (isHidden === 'false') { clearInterval(timer) clearTimeout(checkTimeout) // logger.info(`视频可播放`) // logger.info(`控制条|出现(hidden:${isHidden})`) resolve(true) } else if (attempts <= 0) { clearInterval(timer) clearTimeout(checkTimeout) // logger.error("控制条|检查失败") resolve(false) } // logger.info("控制条|检查中") attempts-- }, 100) }) }) } }, // 获取当前视频类型(video/bangumi) getCurrentPlayerType () { const isVideo = currentUrl.includes('www.bilibili.com/video') || currentUrl.includes('www.bilibili.com/list/') const isBangumi = currentUrl.includes('www.bilibili.com/bangumi') setValue('player_type', isVideo ? 'video' : isBangumi && 'bangumi') }, // 获取当前屏幕模式(normal/wide/web/full) async getCurrentScreenMode () { const exists = await checkElementExistence('#bilibili-player .bpx-player-container', 10, 100) if (exists) { const screenMode = $('#bilibili-player .bpx-player-container').attr('data-screen') return Promise.resolve(screenMode) } else return Promise.resolve(false) }, // 监听屏幕模式变化(normal/wide/web/full) watchScreenModeChange () { const screenModObserver = new MutationObserver(mutations => { const playerDataScreen = $('#bilibili-player .bpx-player-container').attr('data-screen') setValue('current_screen_mode', playerDataScreen) }) screenModObserver.observe($('#bilibili-player .bpx-player-container')[0], { attributes: true, attributeFilter: ['data-screen'], }) }, // 判断自动切换屏幕模式是否切换成功 async checkScreenModeSuccess (expect_mode) { const current_screen_mode = await this.getCurrentScreenMode() const player_data_screen = $('#bilibili-player .bpx-player-container').attr('data-screen') const equal = new Set([ expect_mode, selected_screen_mode, current_screen_mode, player_data_screen, ]).size === 1 return Promise.resolve(equal) }, // 自动选择屏幕模式 async autoSelectScreenMode () { const current_screen_mode = await this.getCurrentScreenMode() if (current_screen_mode === 'wide') return { done: true, mode: selected_screen_mode } if (current_screen_mode === 'web') return { done: true, mode: selected_screen_mode } autoSelectScreenModeTimes++ if (autoSelectScreenModeTimes === 1) { const wideEnterBtn = document.querySelector('.bpx-player-ctrl-wide-enter') const webEnterBtn = document.querySelector('.bpx-player-ctrl-web-enter') const selectModeBtn = selected_screen_mode === 'wide' ? wideEnterBtn : webEnterBtn const expect_mode = selected_screen_mode === 'wide' ? 'wide' : 'web' let attempts = 50 selectModeBtn.click() const checkScreenMode = async (expect_mode) => { const success = await this.checkScreenModeSuccess(expect_mode) if (success) { clearInterval(checkScreenModeInterval) setValue('current_screen_mode', selected_screen_mode) return { done: true, mode: selected_screen_mode } } else { await sleep(1000) selectModeBtn.click() logger.warn('自动选择屏幕模式失败正在重试') attempts-- if (attempts === 0) { clearInterval(checkScreenModeInterval) pageReload() } } } let checkScreenModeInterval = setInterval(checkScreenMode, 100, expect_mode) return new Promise(resolve => { checkScreenMode(expect_mode).then(result => { resolve(result) }) }) } }, // 网页全屏解锁 fixedWebfullUnlockStyle () { webfullUnlockTimes++ async function resetPlayerLayout () { $('body').css({ 'padding-top': 0, position: 'auto', }) $('#playerWrap').css('display', 'block') $('#bilibili-player').css({ height: 'auto', position: 'unset', }) $('#playerWrap').append($('#bilibili-player')) $('.float-nav-exp .mini').css('display', '') // 临时设置默认屏幕模式为宽屏用以触发执行自动定位至播放器,定位完后再重新改为网页全屏 setValue('selected_screen_mode', 'wide') const playerDataScreen = await m.getCurrentScreenMode() if (playerDataScreen !== 'full') { m.autoLocation() } setValue('selected_screen_mode', 'web') } if (webfullUnlockTimes === 1) { const clientHeight = getClientHeight() $('body.webscreen-fix').css({ 'padding-top': clientHeight, position: 'unset', }) $('#bilibili-player.mode-webscreen').css({ height: clientHeight, position: 'absolute', }) $('#app').prepend($('#bilibili-player.mode-webscreen')) $('#playerWrap').css('display', 'none') logger.info('网页全屏解锁|成功') setValue('current_screen_mode', 'web') this.insertGoToCommentsButton() // 退出网页全屏 $('.bpx-player-ctrl-btn-icon.bpx-player-ctrl-web-leave').click(function () { resetPlayerLayout() }) // 再次进入网页全屏 $('.bpx-player-ctrl-btn-icon.bpx-player-ctrl-web-enter').click(function () { $('body').css({ 'padding-top': clientHeight, position: 'unset', }) $('#bilibili-player').css({ height: clientHeight, position: 'absolute', }) $('#app').prepend($('#bilibili-player')) $('#playerWrap').css('display', 'none') $('.float-nav-exp .mini').css('display', 'none') $('html,body').scrollTop(0) }) // 进入退出全屏 $('.bpx-player-ctrl-btn.bpx-player-ctrl-full').click(function () { resetPlayerLayout() }) // 进入宽屏 $('.bpx-player-ctrl-btn-icon.bpx-player-ctrl-wide-enter').click(function () { resetPlayerLayout() }) } }, // 插入跳转评论按钮 insertGoToCommentsButton () { insertGoToCommentsButtonTimes++ if (player_type === 'video' && webfull_unlock && insertGoToCommentsButtonTimes === 1) { const goToCommentsBtnHtml = '
' $('.bpx-player-control-bottom-right').append(goToCommentsBtnHtml) $('#goToComments').on('click', function (event) { event.stopPropagation() $('body,html').scrollTop($('#comment').offset().top - 10) logger.info('到达评论区') }) } }, // 添加返回播放器按钮 async insertBackToPlayerButton () { const playerDataScreen = await this.getCurrentScreenMode() if (player_type === 'video') { const locateButtonHtml = '
\n
' const floatNav = $('.fixed-sidenav-storage .back-to-top-wrap') // $('.fixed-sidenav-storage').css('bottom', '274px') const dataV = floatNav[0].attributes[1].name const locateButtonHtmlDataV = locateButtonHtml.replace('title="定位至播放器"', `title="定位至播放器" ${dataV}`) floatNav.prepend(locateButtonHtmlDataV) const locateButton = $('.storable-items .fixed-sidenav-storage-item.locate') locateButton.not(':first-child').remove() floatNav.on('click', '.locate', function () { $('html,body').scrollTop(playerDataScreen !== 'web' ? player_offset_top - offset_top : 0) }) } if (player_type === 'bangumi') { const floatNav = $('[class*="navTools_floatNavExp"] [class*="navTools_navMenu"]') const floatNavMenuItemClass = floatNav.children('a').children('div').attr('class').split(' ')[0] const locateButtonHtml = `
\n
` floatNav.prepend(locateButtonHtml) const locateButton = $(`${floatNavMenuItemClass}.locate`) locateButton.not(':first-child').remove() floatNav.on('click', '.locate', function () { $('html,body').scrollTop(playerDataScreen !== 'web' ? player_offset_top - offset_top : 0) }) } }, // 自动定位至播放器 autoLocation () { const $player = $('#bilibili-player') const player_offset_top = Math.trunc($player.offset().top) setValue('player_offset_top', player_offset_top) return new Promise(resolve => { const isAutoLocate = auto_locate && ((!auto_locate_video && !auto_locate_bangumi) || (auto_locate_video && player_type === 'video') || (auto_locate_bangumi && player_type === 'bangumi')) if (!isAutoLocate || getValue('selected_screen_mode') === 'web') { resolve(false) // 未开启功能或模式为网页全屏时直接返回,防止代码继续执行进入死循环 return } scrollToPlayer(player_offset_top - offset_top) const applyAutoLocationInterval = setInterval(() => { scrollToPlayer(player_offset_top - offset_top) logger.warn(`自动定位失败,继续尝试 ----------------- 当前文档顶部偏移量:${Math.trunc($(document).scrollTop())} 期望文档顶部偏移量:${player_offset_top - offset_top} 偏移量误差:${(player_offset_top - offset_top) - Math.trunc($(document).scrollTop())} 播放器顶部偏移量:${player_offset_top} 设置偏移量:${offset_top}`) }, 200) const checkAutoLocationStatus = setInterval(() => { const document_scroll_top = Math.trunc($(document).scrollTop()) const expect_offset_top = player_offset_top - offset_top const offset_deviation = Math.abs(expect_offset_top - document_scroll_top) const success = true ? offset_deviation < 5 : false if (success) { clearInterval(checkAutoLocationStatus) clearInterval(applyAutoLocationInterval) // logger.info(offset_deviation); resolve(true) } }, 100) }) }, // 点击播放器自动定位至播放器 async clickPlayerAutoLocation () { if (click_player_auto_locate) { const $player = $('#bilibili-player') const player_offset_top = Math.trunc($player.offset().top) $('#bilibili-player').on('click', handleClick) function handleClick (event) { event.stopPropagation() // logger.info(`1:${player_offset_top}, 2:${offset_top}, 3:${player_offset_top - offset_top}`) scrollToPlayer(player_offset_top - offset_top) } } }, // 点击时间锚点自动返回播放器 async jumpVideoTime () { const clickTarget = player_type === 'video' ? '#comment' : '#comment_module' const $clickTarget = $(clickTarget) scrollToPlayer(player_offset_top - offset_top) $clickTarget.unbind('click').on('click', '.video-time,.video-seek', function (event) { event.stopPropagation() $('html,body').scrollTop(scrollTop) const targetTime = $(this).attr(player_type === 'video' ? 'data-video-time' : 'data-time') const video = $('bwp-video') video.currentTime = targetTime video.play() }) }, // 自动取消静音 autoCancelMute () { autoCancelMuteTimes++ const cancelMuteButtn = $('.bpx-player-ctrl-muted-icon') const cancelMuteButtnDisplay = cancelMuteButtn.css('display') const cancelMuteButtnClass = cancelMuteButtn.attr('class') if (autoCancelMuteTimes === 1) { if (cancelMuteButtnDisplay === 'block') { cancelMuteButtn.click() logger.info('已自动取消静音') } } }, // 自动选择最高画质 autoSelectVideoHighestQuality () { autoSelectVideoHighestQualityTimes++ if (!auto_select_video_highest_quality) return if (autoSelectVideoHighestQualityTimes === 1) { let message const no4K8K = $('.bpx-player-ctrl-quality ul > li').filter(function () { const qualityText = $(this).children('span.bpx-player-ctrl-quality-text').text() return (!qualityText.includes('4K') && !qualityText.includes('8K')) }).eq(0) const yes8K = $('.bpx-player-ctrl-quality ul > li').filter(function () { return $(this).children('span.bpx-player-ctrl-quality-text').text().includes('8K') }).eq(0) const yes4K = $('.bpx-player-ctrl-quality ul > li').filter(function () { return $(this).children('span.bpx-player-ctrl-quality-text').text().includes('4K') }).eq(0) const notVip = $('.bpx-player-ctrl-quality ul > li').eq($('.bpx-player-ctrl-quality ul > li').children('.bpx-player-ctrl-quality-badge-bigvip').length) function autoSelectTargetQuality (target) { target.length ? target.click() : logger.error('最高画质丨切换失败丨未找到清晰度切换元素') } if (is_vip) { if (!contain_quality_4k && !contain_quality_8k) { autoSelectTargetQuality(no4K8K) message = '最高画质|VIP|不包含4K及8K|切换成功' } if (contain_quality_4k && !contain_quality_8k) { yes4K.length ? autoSelectTargetQuality(yes4K) : autoSelectTargetQuality(no4K8K) message = '最高画质|VIP|4K|切换成功' } if ((contain_quality_4k && contain_quality_8k) || (!contain_quality_4k && contain_quality_8k)) { yes8K.length ? autoSelectTargetQuality(yes8K) : autoSelectTargetQuality(no4K8K) message = '最高画质|VIP|8K|切换成功' } } else { autoSelectTargetQuality(notVip) message = '最高画质|非VIP|切换成功' } logger.info(message) } }, // 添加样式文件 addPluginStyle () { const style = ` #playerAdjustment { height: 500px; overflow: auto; overscroll-behavior: contain; padding-right: 10px; } .swal2-popup { width: 34em !important; padding: 1.25em !important; } .swal2-html-container { margin: 0 !important; padding: 16px 5px 0 !important; width: 100% !important; box-sizing: border-box !important; } .swal2-footer { flex-direction: column !important; } .swal2-close { top: 5px !important; right: 3px !important; } .swal2-actions { margin: 7px auto 0 !important; } .swal2-styled.swal2-confirm { background-color: #23ade5 !important; } .swal2-icon.swal2-info.swal2-icon-show { display: none !important; } .player-adjustment-container, .swal2-container { z-index: 999999999 !important; } .player-adjustment-popup { font-size: 14px !important; } .player-adjustment-setting-label { display: flex !important; align-items: center !important; justify-content: space-between !important; padding-top: 10px !important; } .player-adjustment-setting-checkbox { width: 16px !important; height: 16px !important; } .player-adjustment-setting-tips { width: 100% !important; display: flex !important; align-items: center !important; padding: 5px !important; margin-top: 10px !important; background: #f5f5f5 !important; box-sizing: border-box !important; font-size: 14px !important; color: #666 !important; border-radius: 2px !important; text-align: left !important; } .player-adjustment-setting-tips svg { margin-right: 5px !important; } label.player-adjustment-setting-label input { border: 1px solid #cecece !important; background: #fff !important; } label.player-adjustment-setting-label input[type=checkbox], label.player-adjustment-setting-label input[type=radio] { width: 16px !important; height: 16px !important; } label.player-adjustment-setting-label input:checked { border-color: #1986b3 !important; background: #23ade5 !important; } .auto-quality-sub-options, .auto-locate-sub-options { display: flex; align-items: center; padding-left: 15px; } .auto-quality-sub-options label.player-adjustment-setting-label.fourK, .auto-locate-sub-options label.player-adjustment-setting-label.video { margin-right: 10px; } .auto-quality-sub-options .player-adjustment-setting-label input[type="checkbox"] { margin-left: 5px !important; } .player-adjustment-setting-label.screen-mod input { margin-right: 5px !important; } #biliMainHeader { height:64px!important; } #viewbox_report { height:106px!important; padding-top:24px!important; } #v_upinfo { height:80px!important; } .members-info-v1 { padding-top:0!important; } .members-info-v1 .wide-members-header { height:0!important; } .members-info-v1 .wide-members-container .up-card .info-tag { display:none!important; } .membersinfo-wide { margin-top: -35px!important; } .fixed-sidenav-storage-item.locate { width:40px!important; height:40px!important; } .fixed-header .bili-header__bar{ position: absolute!important; inset-inline: 0!important; } ` const addStyleToHead = () => { addStyle('swal-pub-style', 'style', GM_getResourceText('swalStyle')) addStyle('player-adjustment-style', 'style', style) } if (document.head) { addStyleToHead() } else { const headObserver = new MutationObserver(() => { if (document.head) { headObserver.disconnect() addStyleToHead() } }) headObserver.observe(document.documentElement, { childList: true, subtree: true, }) } }, // 注册脚本设置控件 registerMenuCommand () { GM_registerMenuCommand('设置', () => { const html = `
-> 请如实勾选,否则影响自动选择清晰度
-> 只有勾选自动定位至播放器,才会执行自动定位的功能;勾选自动定位至播放器后,video 和 bangumi 两者全选或全不选,默认在这两种类型视频播放页都执行;否则勾选哪种类型,就只在这种类型的播放页才执行。 -> 播放器距离浏览器窗口默认距离为 ${Math.trunc( $('#bilibili-player').offset().top )};请填写小于 ${Math.trunc( $('#bilibili-player').offset().top )} 的正整数或 0;当值为 0 时,播放器上沿将紧贴浏览器窗口上沿、值为 ${Math.trunc( $('#bilibili-player').offset().top )} 时,将保持B站默认。
播放器默认模式
-> 若遇到不能自动选择播放器模式可尝试点击重置 ->*实验性功能(不稳,可能会有这样或那样的问题):勾选后网页全屏模式下可以滑动滚动条查看下方评论等内容,2秒延迟后解锁(番剧播放页不支持)
->新增迷你播放器显示,不过比较简陋,只支持暂停/播放操作,有条件的建议还是直接使用浏览器自带的小窗播放功能。
-> 网络条件好时可以启用此项,勾哪项选哪项,都勾选8k,否则选择4k及8k外最高画质。 -> (不建议开启)若脚本执行失败是否自动刷新页面重试,开启后可能会对使用体验起到一定改善作用,但若是因为B站页面改版导致脚本失效,则会陷入页面无限刷新的情况,此时则必须在页面加载时看准时机关闭此项才能恢复正常,请自行选择是否开启。
` Swal.fire({ title: '播放页调整设置', html: html, icon: 'info', showCloseButton: true, showDenyButton: true, confirmButtonText: '保存', denyButtonText: '重置', footer: '
如果发现脚本不能用,可能是播放页更新了,请耐心等待适配。

夜间哔哩 - 检查更新
', }).then(res => { res.isConfirmed && location.reload(true) if (res.isConfirmed) { location.reload(true) } else if (res.isDenied) { setValue('current_screen_mode', 'normal') location.reload(true) } }) $('#Is-Vip').change(e => { setValue('is_vip', e.target.checked) $('.fourK,.eightK').css('display', e.target.checked ? 'flex!important' : 'none!important') }) $('#Auto-Locate').change(e => { setValue('auto_locate', e.target.checked) }) $('#Auto-Locate-Video').change(e => { setValue('auto_locate_video', e.target.checked) }) $('#Auto-Locate-Bangumi').change(e => { setValue('auto_locate_bangumi', e.target.checked) }) $('#Top-Offset').change(e => { setValue('offset_top', e.target.value * 1) }) $('#Click-Player-Auto-Location').change(e => { setValue('click_player_auto_locate', e.target.checked) }) $('#Auto-Quality').change(e => { setValue('auto_select_video_highest_quality', e.target.checked) }) $('#Quality-4K').change(e => { setValue('contain_quality_4k', e.target.checked) }) $('#Quality-8K').change(e => { setValue('contain_quality_8k', e.target.checked) }) $('input[name="Screen-Mod"]').click(function () { setValue('selected_screen_mode', $(this).val()) }) $('#Webfull-Unlock').change(e => { setValue('webfull_unlock', e.target.checked) }) $('#Auto-Reload').change(e => { setValue('auto_reload', e.target.checked) }) }) }, // 冻结视频标题及UP主信息样式 freezeHeaderAndVideoTitleStyles () { $('#biliMainHeader').attr('style', 'height:64px!important') $('#viewbox_report').attr('style', 'height:106px!important;padding-top:24px!important') $('#v_upinfo').attr('style', 'height:80px!important') $('.members-info-v1').attr('style', 'padding-top:0!important') $('.members-info-v1 .wide-members-header').attr('style', 'height:0!important') $('.members-info-v1 .wide-members-container .up-card .info-tag').attr('style', 'display:none!important') }, // 判断当前窗口是否在最上方 isTopWindow () { return window.self === window.top }, // 前期准备函数 thePrepFunction () { thePrepFunctionRunningTimes++ if (thePrepFunctionRunningTimes === 1) { isLogin() checkBrowserHistory() historyListener() this.initValue() this.addPluginStyle() this.isTopWindow() && this.registerMenuCommand() this.getCurrentPlayerType() this.getCurrentScreenMode() } }, // 主函数 async theMainFunction () { try { theMainFunctionRunningTimes++ if (theMainFunctionRunningTimes === 1) { const videoPlayerExists = await checkElementExistence('#bilibili-player video', 5, 100) || await checkElementExistence('bwp-video', 5, 100) if (videoPlayerExists) { logger.info('播放器|存在') $('body').css('overflow', 'hidden') const isPlayable = await this.checkVideoCanPlayThrough() // console.time('播放页调整:判断按钮出现') const screenModeBtnExists = await checkElementExistence('#bilibili-player .bpx-player-ctrl-btn', 100, 100) // console.timeEnd('播放页调整:判断按钮出现') // const pageComplete = await checkPageReadyState('complete') if (isPlayable || (!isPlayable && screenModeBtnExists)) { logger.info('视频资源|可以播放') // console.time('播放页调整:切换模式耗时') this.watchScreenModeChange() await sleep(100) // close 为功能关闭,勿改 const selectedScreenMode = selected_screen_mode !== 'close' ? await this.autoSelectScreenMode() : 'close' // console.timeEnd('播放页调整:切换模式耗时') if (selectedScreenMode && selectedScreenMode.done || selectedScreenMode === 'close') { if (selectedScreenMode !== 'close') logger.info(`屏幕模式|${selectedScreenMode['mode'].toUpperCase()}|切换成功`) this.autoCancelMute() // console.time('播放页调整:选择画质耗时') this.autoSelectVideoHighestQuality() // console.timeEnd('播放页调整:选择画质耗时') this.clickPlayerAutoLocation() if (webfull_unlock && selectedScreenMode.mode === 'web') { this.fixedWebfullUnlockStyle() } // console.time('播放页调整:自动定位耗时') this.freezeHeaderAndVideoTitleStyles() await sleep(500) const autoLocationDone = await this.autoLocation() // console.timeEnd('播放页调整:自动定位耗时') if (auto_locate && autoLocationDone) { $('body').css('overflow', 'auto') logger.info('自动定位|成功') await sleep(100) this.insertBackToPlayerButton() this.jumpVideoTime() } if (!auto_locate || (auto_locate && auto_locate_bangumi && !auto_locate_video && player_type === 'video') || (auto_locate && auto_locate_video && !auto_locate_bangumi && player_type === 'bangumi')) { $('body').css('overflow', 'auto') logger.info('自动定位|未开启') } if (player_type === 'video') { const loaded = await checkElementExistence('#comment > .comment > .bili-comment', 10, 100) await sleep(100) if (loaded) { logger.info('页面加载|完毕') } else { pageReload() } } } else { logger.error(`屏幕模式|切换失败|autoSelectScreenMode()`) pageReload() } } else { logger.error('视频资源|加载失败') pageReload() } } else { logger.error('播放器|不存在') pageReload() } } } catch (error) { logger.error(error) pageReload() } }, } if (isLogin()) { m.thePrepFunction() const checkDocumentHidden = setInterval(() => { const dicumentHidden = isDocumentHidden() if (!dicumentHidden) { logger.info('当前标签|已激活|开始应用配置') clearInterval(checkDocumentHidden) m.theMainFunction() } else { logger.info('当前标签|未激活|等待激活') } }, 100) } else logger.warn('请登录|本脚本只能在登录状态下使用') })