- // ==UserScript==
- // @name NEXT-哔哩哔哩(bilibili.com)播放页调整
- // @license GPL-3.0 License
- // @namespace https://gf.qytechs.cn/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.13
- // @description 1.自动定位到播放器(进入播放页,可自动定位到播放器,可设置偏移量及是否在点击主播放器时定位);2.可设置是否自动选择最高画质;3.可设置播放器默认模式;
- // @author QIAN
- // @match *://*.bilibili.com/video/*
- // @match *://*.bilibili.com/bangumi/play/*
- // @match *://*.bilibili.com/list/watchlater*
- // @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
- // ==/UserScript==
- $(() => {
- 'use strict'
- let {
- currentUrl,
- theMainFunctionRunningTimes,
- thePrepFunctionRunningTimes,
- autoSelectScreenModeTimes,
- autoCancelMuteTimes,
- webfullUnlockTimes,
- insertGoToCommentsButtonTimes,
- autoSelectVideoHightestQualityTimes,
- } = {
- currentUrl: window.location.href,
- theMainFunctionRunningTimes: 0,
- thePrepFunctionRunningTimes: 0,
- autoSelectScreenModeTimes: 0,
- autoCancelMuteTimes: 0,
- webfullUnlockTimes: 0,
- insertGoToCommentsButtonTimes: 0,
- autoSelectVideoHightestQualityTimes: 0,
- }
- const {
- getValue,
- setValue,
- sleep,
- addStyle,
- historyListener,
- checkBrowserHistory,
- throttle,
- getClientHeight,
- checkElementExistence,
- isLogin,
- logger,
- checkPageReadyState
- } = {
- 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) {
- return new Promise(resolve => {
- let attempts = 0
- const intervalId = setInterval(() => {
- attempts++
- const element = $(selector)
- if (element.length) {
- clearInterval(intervalId)
- resolve(true)
- } else if (attempts === maxAttempts) {
- clearInterval(intervalId)
- resolve(false)
- }
- }, interval)
- })
- },
- 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)
- },
- },
- checkPageReadyState(state) {
- return new Promise((resolve) => {
- const timer = setInterval(() => {
- if (document.readyState === state) {
- clearInterval(timer);
- resolve(true);
- }
- }, 100);
- });
- }
- }
- 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,
- } = {
- 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'),
- }
- 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,
- }, ]
- value.forEach(v => {
- if (getValue(v.name) === undefined) {
- setValue(v.name, v.value)
- }
- })
- },
- // 检查视频资源是否加载完毕并处于可播放状态
- async checkVideoCanPlayThrough() {
- const BwpVideoPlayerExists = await checkElementExistence('bwp-video', 10, 10)
- if (BwpVideoPlayerExists) {
- return new Promise(resolve => {
- resolve(true)
- })
- }
- return new Promise(resolve => {
- const checkTimeout = setTimeout(() => {
- // logger.error('视频资源|脚本检测失败|重载页面')
- // location.reload(true)
- resolve(false)
- }, 7000)
- $('#bilibili-player video').on('canplaythrough', () => {
- // logger.info("视频资源加载|成功")
- let attempts = 10
- 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/watchlater')
- 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)
- },
- // 自动选择屏幕模式
- autoSelectScreenMode() {
- autoSelectScreenModeTimes++
- if (autoSelectScreenModeTimes === 1) {
- const $wideEnterBtn = player_type === 'video' ? $('.bpx-player-ctrl-wide-enter') : $('.squirtle-widescreen-inactive')
- const $webEnterBtn = player_type === 'video' ? $('.bpx-player-ctrl-web-enter') : $('.squirtle-pagefullscreen-inactive')
- const selectModeBtn = selected_screen_mode === 'wide' ? $wideEnterBtn : $webEnterBtn
- const expect_mode = selected_screen_mode === 'wide' ? 'wide' : 'web'
- let attempts = 3
- 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 {
- selectModeBtn.click()
- logger.warn('自动选择屏幕模式失败正在重试')
- attempts--
- if (attempts === 0) location.reload(true)
- }
- }
- let checkScreenModeInterval = setInterval(checkScreenMode, 100, expect_mode)
- return new Promise(resolve => {
- checkScreenMode(expect_mode).then(result => {
- resolve(result)
- })
- })
- }
- },
- // 网页全屏解锁
- fixedWebfullUnlockStyle() {
- webfullUnlockTimes++
- 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() {
- $('body').css({
- 'padding-top': 0,
- position: 'auto',
- })
- $('#playerWrap').css('display', 'block')
- const playerWrapHeight = $('#playerWrap').height()
- $('#bilibili-player').css({
- height: playerWrapHeight,
- position: 'unset',
- })
- $('#playerWrap').append($('#bilibili-player.mode-webscreen'))
- setValue('selected_screen_mode', 'wide')
- this.autoLocation()
- setValue('selected_screen_mode', 'web')
- $('.float-nav-exp .mini').css('display', '')
- })
- // 再次进入网页全屏
- $('.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')
- $('#danmukuBox').css('margin-top', '20px')
- $('html,body').scrollTop(0)
- })
- }
- },
- // 插入跳转评论按钮
- insertGoToCommentsButton() {
- insertGoToCommentsButtonTimes++
- if (player_type === 'video' && webfull_unlock && insertGoToCommentsButtonTimes === 1) {
- const goToCommentsBtnHtml = `<div class="bpx-player-ctrl-btn bpx-player-ctrl-comment" role="button" aria-label="前往评论" tabindex="0"><div id="goToComments" class="bpx-player-ctrl-btn-icon"><span class="bpx-common-svg-icon"><svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="88" height="88" preserveAspectRatio="xMidYMid meet" style="width: 100%; height: 100%; transform: translate3d(0px, 0px, 0px);"><path d="M512 85.333c235.637 0 426.667 191.03 426.667 426.667S747.637 938.667 512 938.667a424.779 424.779 0 0 1-219.125-60.502A2786.56 2786.56 0 0 0 272.82 866.4l-104.405 28.48c-23.893 6.507-45.803-15.413-39.285-39.296l28.437-104.288c-11.008-18.688-18.219-31.221-21.803-37.91A424.885 424.885 0 0 1 85.333 512c0-235.637 191.03-426.667 426.667-426.667zm-102.219 549.76a32 32 0 1 0-40.917 49.216A223.179 223.179 0 0 0 512 736c52.97 0 103.19-18.485 143.104-51.67a32 32 0 1 0-40.907-49.215A159.19 159.19 0 0 1 512 672a159.19 159.19 0 0 1-102.219-36.907z" fill="#currentColor"/></svg></span></div></div>`
- $('.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 = `<div class="fixed-sidenav-storage-item locate" title="定位至播放器">\n<svg t="1643419779790" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1775" width="200" height="200" style="width: 50%;height: 100%;fill: currentColor;"><path d="M512 352c-88.008 0-160.002 72-160.002 160 0 88.008 71.994 160 160.002 160 88.01 0 159.998-71.992 159.998-160 0-88-71.988-160-159.998-160z m381.876 117.334c-19.21-177.062-162.148-320-339.21-339.198V64h-85.332v66.134c-177.062 19.198-320 162.136-339.208 339.198H64v85.334h66.124c19.208 177.062 162.144 320 339.208 339.208V960h85.332v-66.124c177.062-19.208 320-162.146 339.21-339.208H960v-85.334h-66.124zM512 810.666c-164.274 0-298.668-134.396-298.668-298.666 0-164.272 134.394-298.666 298.668-298.666 164.27 0 298.664 134.396 298.664 298.666S676.27 810.666 512 810.666z" p-id="1776"></path></svg></div>`
- const floatNav = $('.fixed-sidenav-storage .back-to-top-wrap')
- const locateButton = $('.storable-items .fixed-sidenav-storage-item.locate')
- // $('.fixed-sidenav-storage').css('bottom', '274px')
- const dataV = floatNav[0].attributes[1].name
- const locateButtonHtmlDataV = locateButtonHtml.replace(`title="定位至播放器"`, `title="定位至播放器" ${dataV}`)
- floatNav.prepend(locateButtonHtmlDataV)
- 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 locateButtonHtml = `<div class="tool-item locate" title="定位至播放器">\n<svg t="1643419779790" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1775" width="200" height="200" style="width: 50%;height: 100%;fill: currentColor;"><path d="M512 352c-88.008 0-160.002 72-160.002 160 0 88.008 71.994 160 160.002 160 88.01 0 159.998-71.992 159.998-160 0-88-71.988-160-159.998-160z m381.876 117.334c-19.21-177.062-162.148-320-339.21-339.198V64h-85.332v66.134c-177.062 19.198-320 162.136-339.208 339.198H64v85.334h66.124c19.208 177.062 162.144 320 339.208 339.208V960h85.332v-66.124c177.062-19.208 320-162.146 339.21-339.208H960v-85.334h-66.124zM512 810.666c-164.274 0-298.668-134.396-298.668-298.666 0-164.272 134.394-298.666 298.668-298.666 164.27 0 298.664 134.396 298.664 298.666S676.27 810.666 512 810.666z" p-id="1776"></path></svg></div>`
- const floatNav = $('.nav-tools')
- const locateButton = $('.nav-tools .tool-item.locate')
- floatNav.prepend(locateButtonHtml)
- 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 || selected_screen_mode === 'web') {
- resolve(false)
- return
- }
- const scrollToPlayer = () => {
- $('html,body').scrollTop(player_offset_top - offset_top)
- }
- scrollToPlayer()
- const applyAutoLocationInterval = setInterval(() => {
- scrollToPlayer()
- logger.warn(`自动定位失败,继续尝试
- -----------------
- 当前文档顶部偏移量:${$(document).scrollTop()}
- 期望文档顶部偏移量:${player_offset_top - offset_top}
- 播放器顶部偏移量:${player_offset_top}
- 设置偏移量:${offset_top}`)
- }, 200)
- const checkAutoLocationStatus = setInterval(() => {
- const document_scroll_top = $(document).scrollTop()
- const success = document_scroll_top === player_offset_top - offset_top
- if (success) {
- clearInterval(checkAutoLocationStatus)
- clearInterval(applyAutoLocationInterval)
- // logger.info("自动定位成功");
- resolve(true)
- }
- }, 100)
- })
- },
- // 点击播放器自动定位至播放器
- async clickPlayerAutoLocation() {
- const playerDataScreen = await this.getCurrentScreenMode();
- if (click_player_auto_locate) {
- $('#bilibili-player').on('click', handleClick);
-
- function handleClick(event) {
- event.stopPropagation();
- if ($(this).attr('status') === 'adjustment-mini') {
- logger.info('点击迷你播放器');
- } else {
- const scrollTop = playerDataScreen !== 'web' ? player_offset_top - offset_top : 0;
- $('html,body').scrollTop(scrollTop);
- }
- }
- }
- },
- // 点击时间锚点自动返回播放器
- jumpVideoTime() {
- const video = $('#bilibili-player video')[0]
- const clickTarget = player_type === 'video' ? '#comment' : '#comment_module'
- const $clickTarget = $(clickTarget)
- $clickTarget.unbind('click').on('click', '.video-time,.video-seek', function(event) {
- event.stopPropagation()
- const targetTime = $(this).attr(player_type === 'video' ? 'data-video-time' : 'data-time')
- video.currentTime = targetTime
- video.play()
- $('html,body').scrollTop(selected_screen_mode === 'web' ? 0 : player_offset_top - offset_top)
- })
- },
- // 自动取消静音
- autoCancelMute() {
- autoCancelMuteTimes++
- const cancelMuteButtn = player_type === 'video' ? $('.bpx-player-ctrl-muted-icon') : $('.squirtle-volume-wrap .squirtle-volume .squirtle-volume-icon')
- const cancelMuteButtnDisplay = cancelMuteButtn.css('display')
- const cancelMuteButtnClass = cancelMuteButtn.attr('class')
- if (autoCancelMuteTimes === 1) {
- if (player_type === 'video' && cancelMuteButtnDisplay === 'block') {
- cancelMuteButtn.click()
- logger.info('已自动取消静音')
- }
- if (player_type === 'bangumi' && cancelMuteButtnClass.includes('squirtle-volume-mute-state')) {
- cancelMuteButtn.click()
- logger.info('已自动取消静音')
- }
- }
- },
- // 自动选择最高画质
- autoSelectVideoHightestQuality() {
- autoSelectVideoHightestQualityTimes++;
- if (!auto_select_video_highest_quality) return;
- if (autoSelectVideoHightestQualityTimes === 1) {
- let qualityValue, message;
- switch (player_type) {
- case 'video':
- if (is_vip) {
- if (!contain_quality_4k && !contain_quality_8k) {
- qualityValue = $('.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'))
- });
- message = '最高画质|VIP|不包含4K及8K|切换成功';
- } else if (contain_quality_4k && contain_quality_8k) {
- qualityValue = $('.bpx-player-ctrl-quality > ul > li').filter(function() {
- return $(this).children('span.bpx-player-ctrl-quality-text').text().includes('8K')
- });
- message = '最高画质|VIP|8K|切换成功';
- } else if (contain_quality_4k && !contain_quality_8k) {
- qualityValue = $('.bpx-player-ctrl-quality > ul > li').filter(function() {
- return $(this).children('span.bpx-player-ctrl-quality-text').text().includes('4K')
- });
- message = '最高画质|VIP|4K|切换成功';
- } else if (!contain_quality_4k && contain_quality_8k) {
- qualityValue = $('.bpx-player-ctrl-quality > ul > li').filter(function() {
- return $(this).children('span.bpx-player-ctrl-quality-text').text().includes('8K')
- });
- message = '最高画质|VIP|8K|切换成功';
- }
- qualityValue.eq(0).click();
- } else {
- const selectVipItemLength = $('.bpx-player-ctrl-quality > ul > li').children('.bpx-player-ctrl-quality-badge-bigvip').length;
- $('.bpx-player-ctrl-quality > ul > li').eq(selectVipItemLength).click();
- message = '最高画质|非VIP|切换成功';
- }
- break;
- case 'bangumi':
- if (is_vip) {
- if (contain_quality_4k) {
- $('.squirtle-quality-wrap >.squirtle-video-quality > ul > li').eq(0).click();
- message = '最高画质|VIP|包含4K|切换成功';
- } else {
- qualityValue = $('.squirtle-quality-wrap > .squirtle-video-quality > ul > li').filter(function() {
- const qualityText = $(this).children('.squirtle-quality-text-c').children('.squirtle-quality-text').text();
- return (!qualityText.includes('4K') && !qualityText.includes('8K'))
- });
- qualityValue.eq(0).click()
- message = '最高画质|VIP|不包含4K|切换成功';
- }
- } else {
- const selectVipItemLength = $('.squirtle-quality-wrap >.squirtle-video-quality > ul > li').children('.squirtle-bigvip').length;
- $('.squirtle-quality-wrap >.squirtle-video-quality > ul > li').eq(selectVipItemLength).click();
- message = '最高画质|非VIP|切换成功';
- }
- break;
- default:
- break;
- }
- 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;
- }
- .fixed-sidenav-storage-item.locate {
- width:40px!important;
- height:40px!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 = `
- <div id="playerAdjustment" style="font-size: 1em;">
- <label class="player-adjustment-setting-label" style="padding-top:0!important;"> 是否为大会员
- <input type="checkbox" id="Is-Vip" ${
- getValue('is_vip') ? 'checked' : ''
- } class="player-adjustment-setting-checkbox">
- </label>
- <span class="player-adjustment-setting-tips"> -> 请如实勾选,否则影响自动选择清晰度</span>
- <label class="player-adjustment-setting-label"> 自动定位至播放器
- <input type="checkbox" id="Auto-Locate" ${
- getValue('auto_locate') ? 'checked' : ''
- } class="player-adjustment-setting-checkbox">
- </label>
- <div class="auto-locate-sub-options">
- <label class="player-adjustment-setting-label video"> 普通视频(video)
- <input type="checkbox" id="Auto-Locate-Video" ${
- getValue('auto_locate_video') ? 'checked' : ''
- } class="player-adjustment-setting-checkbox">
- </label>
- <label class="player-adjustment-setting-label bangumi"> 其他视频(bangumi)
- <input type="checkbox" id="Auto-Locate-Bangumi" ${
- getValue('auto_locate_bangumi') ? 'checked' : ''
- } class="player-adjustment-setting-checkbox">
- </label>
- </div>
- <span class="player-adjustment-setting-tips"> -> 只有勾选自动定位至播放器,才会执行自动定位的功能;勾选自动定位至播放器后,video 和 bangumi 两者全选或全不选,默认在这两种类型视频播放页都执行;否则勾选哪种类型,就只在这种类型的播放页才执行。</span>
- <label class="player-adjustment-setting-label" id="player-adjustment-Range-Wrapper">
- <span>播放器顶部偏移(px)</span>
- <input id="Top-Offset" value="${getValue(
- 'offset_top'
- )}" style="padding:5px;width: 200px;border: 1px solid #cecece;">
- </label>
- <span class="player-adjustment-setting-tips"> -> 播放器距离浏览器窗口默认距离为 ${Math.trunc(
- $('#bilibili-player').offset().top
- )};请填写小于 ${Math.trunc(
- $('#bilibili-player').offset().top
- )} 的正整数或 0;当值为 0 时,播放器上沿将紧贴浏览器窗口上沿、值为 ${Math.trunc(
- $('#bilibili-player').offset().top
- )} 时,将保持B站默认。 </span>
- <label class="player-adjustment-setting-label"> 点击播放器时定位
- <input type="checkbox" id="Click-Player-Auto-Location" ${
- getValue('click_player_auto_locate') ? 'checked' : ''
- } class="player-adjustment-setting-checkbox">
- </label>
- <div class="player-adjustment-setting-label screen-mod" style="display: flex;align-items: center;justify-content: space-between;"> 播放器默认模式 <div style="width: 215px;display: flex;align-items: center;justify-content: space-between;">
- <label class="player-adjustment-setting-label" style="padding-top:0!important;">
- <input type="radio" name="Screen-Mod" value="wide" ${
- getValue('selected_screen_mode') === 'wide'
- ? 'checked'
- : ''
- }>宽屏 </label>
- <label class="player-adjustment-setting-label" style="padding-top:0!important;">
- <input type="radio" name="Screen-Mod" value="web" ${
- getValue('selected_screen_mode') === 'web'
- ? 'checked'
- : ''
- }> 网页全屏 </label>
- </div>
- </div>
- <span class="player-adjustment-setting-tips"> -> 若遇到不能自动选择播放器模式可尝试点击重置</span>
- <label class="player-adjustment-setting-label"> 网页全屏模式解锁
- <input type="checkbox" id="Webfull-Unlock" ${
- getValue('webfull_unlock') ? 'checked' : ''
- } class="player-adjustment-setting-checkbox">
- </label>
- <span class="player-adjustment-setting-tips"> ->*实验性功能(不稳,可能会有这样或那样的问题):勾选后网页全屏模式下可以滑动滚动条查看下方评论等内容,2秒延迟后解锁(番剧播放页不支持)<br>->新增迷你播放器显示,不过比较简陋,只支持暂停/播放操作,有条件的建议还是直接使用浏览器自带的小窗播放功能。</span>
- <label class="player-adjustment-setting-label"> 自动选择最高画质
- <input type="checkbox" id="Auto-Quality" ${
- getValue('auto_select_video_highest_quality')
- ? 'checked'
- : ''
- } class="player-adjustment-setting-checkbox">
- </label>
- <div class="auto-quality-sub-options">
- <label class="player-adjustment-setting-label fourK"> 是否包含4K画质
- <input type="checkbox" id="Quality-4K" ${
- getValue('contain_quality_4k') ? 'checked' : ''
- } class="player-adjustment-setting-checkbox">
- </label>
- <label class="player-adjustment-setting-label eightK"> 是否包含8K画质
- <input type="checkbox" id="Quality-8K" ${
- getValue('contain_quality_8k') ? 'checked' : ''
- } class="player-adjustment-setting-checkbox">
- </label>
- </div>
- <span class="player-adjustment-setting-tips"> -> 网络条件好时可以启用此项,勾哪项选哪项,都勾选8k,否则选择4k及8k外最高画质。</span>
- </div>
- `
- Swal.fire({
- title: '播放页调整设置',
- html: html,
- icon: 'info',
- showCloseButton: true,
- showDenyButton: true,
- confirmButtonText: '保存',
- denyButtonText: '重置',
- footer: '<div style="text-align: center;">如果发现脚本不能用,可能是播放页更新了,请耐心等待适配。</div><hr style="border: none;height: 1px;margin: 12px 0;background: #eaeaea;"><div style="text-align: center;font-size: 1.25em;"><a href="//userstyles.world/style/241/nightmode-for-bilibili-com" target="_blank">夜间哔哩 - </a><a href="//gf.qytechs.cn/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" target="_blank">检查更新</a></div>',
- }).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)
- })
- })
- },
- // 冻结视频标题及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()
- const pageComplete = await checkPageReadyState('complete')
- if (isPlayable || (!isPlayable && pageComplete)) {
- logger.info(`视频资源|可以播放`)
- // console.time('播放页调整:切换模式耗时')
- this.watchScreenModeChange()
- await sleep(100)
- const selectedScreenMode = await this.autoSelectScreenMode()
- // console.timeEnd('播放页调整:切换模式耗时')
- if (selectedScreenMode.done) {
- logger.info(`屏幕模式|${selectedScreenMode['mode'].toUpperCase()}|切换成功`)
- this.autoCancelMute()
- // console.time('播放页调整:选择画质耗时')
- this.autoSelectVideoHightestQuality()
- // console.timeEnd('播放页调整:选择画质耗时')
- this.clickPlayerAutoLocation()
- if (webfull_unlock && selectedScreenMode.mode === 'web') {
- this.fixedWebfullUnlockStyle()
- }
- // console.time('播放页调整:自动定位耗时')
- this.freezeHeaderAndVideoTitleStyles()
- const autoLocationDone = await this.autoLocation()
- // console.timeEnd('播放页调整:自动定位耗时')
- if (auto_locate && autoLocationDone) {
- $('body').css('overflow', '')
- logger.info(`自动定位|成功`)
- 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', '')
- logger.info(`自动定位|未开启`)
- }
- if (player_type === 'video') {
- const loaded = await checkElementExistence('#comment > .comment > .bili-comment', 10, 100)
- await sleep(100)
- if (loaded) {
- logger.info(`页面加载|完毕`)
- } else {
- location.reload(true)
- }
- }
- } else {
- logger.error(`屏幕模式|${selectedScreenMode.mode}|切换失败`)
- location.reload(true)
- }
- } else {
- logger.error(`视频资源|加载失败`)
- location.reload(true)
- }
- } else {
- logger.error(`播放器|不存在`)
- location.reload(true)
- }
- }
- } catch (error) {
- logger.error(error)
- location.reload(true)
- }
- },
- }
- if (isLogin()) {
- m.thePrepFunction()
- m.theMainFunction()
- } else logger.warn('请登录(不可用)|本脚本只能在登录(不可用)状态下使用')
- })