B站|bilibili 分P视频详情页优化

调整bilibili 分P视频合集列表,使得可以根据窗口大小上下铺满,标题显示得更长;适配了宽屏显示;支持小窗大小设置;以及一些其他的调整

目前為 2024-04-19 提交的版本,檢視 最新版本

// ==UserScript==
// @name         B站|bilibili 分P视频详情页优化
// @license      MIT
// @namespace    https://sumver.cn
// @version      1.1.8
// @description  调整bilibili 分P视频合集列表,使得可以根据窗口大小上下铺满,标题显示得更长;适配了宽屏显示;支持小窗大小设置;以及一些其他的调整
// @author       lonelylizard
// @match        https://www.bilibili.com/video/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

// 添加控制菜单
// 宽屏适配开关
const menu_widescreen_status = GM_registerMenuCommand("宽屏适配开关", function() {
    let cur_choose = window.confirm("当前宽屏适配状态:\n" + (GM_getValue("widescreen_status") === true ? "已开启" : "已关闭")+"\n\n按【确认】修改状态,按【取消】保持设置不变\n注:只有在【视频合集宽度调整】未开启或设置为0时,宽屏模式才会生效\n设置后请手动刷新一次网页");
    if(cur_choose){
        if(GM_getValue("widescreen_status")){
            GM_setValue("widescreen_status",false)
        }else{
            GM_setValue("widescreen_status",true)
        }
    }
  });

// 小窗尺寸设置开关
const miniwin_status = GM_registerMenuCommand("小窗尺寸设置(试验)", function() {
    let num = window.prompt("小窗缩放倍率,在1.1~3之间是最合理的,\n推荐倍率1.6~2.0尺寸最佳\n如设置后出现问题(如误设置为100,请输入0即可消除错误尺寸")
    // 调整小窗大小
    if(typeof(Number(num)) === 'number'){
        if(num.toString().split('.').pop().length <=2){

            let ori_area = document.querySelector("#bilibili-player-placeholder")
                let o_height = ori_area.offsetHeight
                let o_width = ori_area.offsetWidth
                let sc_height = Math.round((o_height-56)/3 *num)
                let sc_width  = Math.round(o_width/3 *num)

            if(num!=0){

                GM_addStyle(`@media screen and (min-width: 1681px){
                    .bpx-player-container[data-revision="1"][data-screen=mini], .bpx-player-container[data-revision="2"][data-screen=mini]{
                        height:${sc_height}px !important;
                        width:${sc_width}px !important;
                    }
                }`);
                GM_addStyle(`
                    .bpx-player-container[data-revision="1"][data-screen=mini], .bpx-player-container[data-revision="2"][data-screen=mini]{
                        height:${sc_height}px !important;
                        width:${sc_width}px !important;
                    }`);

                // 保存设置
                GM_setValue("mini_height",sc_height)
                GM_setValue("mini_width",sc_width)
            }else{
                // 重置尺寸
                GM_setValue("mini_height",0)
                GM_setValue("mini_width",0)
                // 恢复小窗
                GM_addStyle(`@media screen and (min-width: 1681px){
                    .bpx-player-container[data-revision="1"][data-screen=mini], .bpx-player-container[data-revision="2"][data-screen=mini]{
                        height:${Math.round((o_height-56)/3)}px !important;
                        width:${Math.round((o_width-56)/3)}px !important;
                    }
                }`);
                GM_addStyle(`
                    .bpx-player-container[data-revision="1"][data-screen=mini], .bpx-player-container[data-revision="2"][data-screen=mini]{
                        height:${Math.round((o_height-56)/3)}px !important;
                        width:${Math.round((o_width-56)/3)}px !important;
                    }`);
            }

        }
    }
});

// 视频列表标题换行开关
const menu_title_wrap_status = GM_registerMenuCommand("视频列表标题换行开关(试验)", function() {
    let cur_choose = window.confirm("使得右侧视频合集支持标题换行显示\n当前状态:\n" + (GM_getValue("title_wrap_status") === true ? "已开启" : "已关闭")+"\n\n按【确认】修改状态,按【取消】保持设置不变\n\n设置后请手动刷新一次网页");
    if(cur_choose){
        if(GM_getValue("title_wrap_status")){
            GM_setValue("title_wrap_status",false)
        }else{
            GM_setValue("title_wrap_status",true)
        }
    }
  });

// 视频合集列表比例调整开关
const menu_area_ratio = GM_registerMenuCommand("视频合集列表宽度调整(试验)", function() {
    let area_ratio_prompt = window.prompt("输入0.5表示视频列表占屏幕一半,输入0.25表示占屏幕1/4,\n当前比率:\n" + (GM_getValue("area_ratio")!=0 ?GM_getValue("area_ratio"):"未开启")+"\n\n按【确认】修改状态,按【取消】保持设置不变\n\n如设置导致页面混乱,请输入0还原页面\n\n注意:该功能开启时,宽屏模式会自动关闭\n\n设置后请手动刷新一次网页");
    if(typeof(Number(area_ratio_prompt)) === 'number'){
        if(area_ratio_prompt.toString().split('.').pop().length <=2){
            GM_setValue("area_ratio",area_ratio_prompt)
        }
    }
  });


(function() {
    'use strict';

    // 去除右侧广告
    GM_addStyle(`#slide_ad{display:none`);
    GM_addStyle(`.ad-report {display:none !important;min-width:0px !important;min-height:0px !important}`);
    // 去除简介下广告
    GM_addStyle(`#activity_vote{display:none !important}`);
    // 去除右下角直播窗口
    GM_addStyle(`.pop-live-small-mode{display:none !important}`);
    // 去除右侧游戏广告卡片
    GM_addStyle(`.video-page-game-card-small{display:none !important}`);

    // 去除视频下方的广播广告
    GM_addStyle(`.reply-notice{display:none !important}`);


    // 让视频列表内的视频项标题显示得更多
    GM_addStyle(`.base-video-sections-v1 .video-section-list .video-episode-card__info-title[data-v-10d2177a]{width:100% !important}`);


    var targetNode = "";
    // 选择需要观察变动的节点
    if(document.getElementsByClassName('video-sections-content-list')[0]){
        // fn1,fn2,fn3情况共同拥有video-sections-content-list,一起监听
        targetNode = document.getElementsByClassName('video-sections-content-list')[0];
    }else if(document.getElementsByClassName('multi-page-v1')[0]){
        // fn4情况
        targetNode = document.getElementsByClassName('multi-page-v1')[0];
    }

    // 观察器的配置(需要观察什么变动)
    const config = {
        attributes: false, // 开启监听属性
        childList: true, // 开启监听子节点
        subtree: true // 开启监听子节点下面的所有节点
    };

    // 当观察到变动时执行的回调函数
    const callback = function(mutationsList, observer) {
        // Use traditional 'for loops' for IE 11
        for(let mutation of mutationsList) {
            if (mutation.type === 'childList') {
                // console.log('子节点有变化');
                if(document.querySelector(".base-video-sections-v1")){
                    fn1();
                }
                if(document.querySelector(".video-sections-v1")){
                    fn2();
                }
                if(document.querySelector("video-section-title")){
                    fn3();
                }
                if(document.querySelector(".multi-page-v1")){
                    fn4()
                }
                return;
            }
            // else if (mutation.type === 'attributes') {
            //     console.log('The ' + mutation.attributeName + ' 属性有变化');
            // }
        }
    };

    // 创建一个观察器实例并传入回调函数
    const observer = new MutationObserver(callback);

    // 以上述配置开始观察目标节点
    try{
        observer.observe(targetNode, config);
    }
    catch(err) {
        // console.log("节点不存在");
    }


    // 之后,可停止观察
    // observer.disconnect();


    // 针对不带封面的视频合集列表
    let fn1 = function(){
        if(document.querySelector(".base-video-sections-v1")){
            if(document.querySelector(".video-episode-card")){



                // 带分类视频和仅标题视频合集有相同的外层div,从这里区分开两种类型
                if(document.querySelector(".video-section-title")){
                    change_right_width("fn3")
                    change_title_wrap("fn3")
                    fn3()
                }else{
                    change_right_width("fn1")
                    change_title_wrap("fn1")
                    let list_height = document.querySelector(".video-section-list").scrollHeight;
                    let res_height = window.innerHeight;
                    let right_content_top_heigt = document.querySelector(".base-video-sections-v1").offsetTop;
                    let dif_height = res_height - right_content_top_heigt - 150;

                    // 计算列表高度,如果达不到一屏就不铺满
                    if(!document.querySelector(".transcript-box")){
                        if(list_height > dif_height){
                            GM_addStyle(`.video-sections-content-list{height: ${dif_height}px !important;max-height:1000px !important}`);
                        }else{
                            // let temp_height = list_height + 10
                            // GM_addStyle(`.video-sections-content-list{height: ${temp_height}px !important;max-height:1000px !important}`);
                            // 如果高度小于一屏,同时开始换行功能,会导致高度不正确,这里修改为去除高度属性,让其自适应
                            GM_addStyle(`.video-sections-content-list{height: unset !important;max-height:1000px !important}`);

                        }
                    }else{
                        // 兼容脚本:在侧边显示 Bilibili 视频字幕/文稿(原始版)
                        if(list_height > res_height){
                            GM_addStyle(`.video-sections-content-list{height: ${res_height-280}px !important;max-height:1000px !important}`);
                        }else{
                            // let temp_height = list_height + 10
                            // GM_addStyle(`.video-sections-content-list{height: ${temp_height}px !important;max-height:1000px !important}`);
                            GM_addStyle(`.video-sections-content-list{height: unset !important;max-height:1000px !important}`);
                        }
                    }

                    //加宽视频列表标题
                    GM_addStyle(`.video-episode-card__info-title{width:100% !important}`);

                    // 添加滑动鼠标一个浅的聚焦颜色
                    GM_addStyle(`.video-episode-card__info:hover{background:#DCE2E3}`);
                    // 去除字体蓝色提醒
                    GM_addStyle(`.video-episode-card__info-title:hover{color:#000 !important}`);
                    GM_addStyle(`.video-episode-card__info:hover .video-episode-card__info-title{color:#000 !important}`);
                }


            }
        }
    };

    // 针对带封面的视频合集列表
    let fn2 = function(){
        if(document.querySelector(".video-sections-v1")){
            if(document.querySelector(".video-episode-card")){

                change_right_width("fn2");

                change_title_wrap("fn2");

                let list_height = document.querySelector(".video-section-list").scrollHeight;

                let res_height = window.innerHeight;
                var right_content_top_heigt = document.querySelector(".video-sections-v1").offsetTop;
                var dif_height = res_height - right_content_top_heigt -160;

                // 计算列表高度,如果达不到一屏就不铺满

                if(!document.querySelector(".transcript-box")){
                    if(list_height > dif_height){
                        GM_addStyle(`.video-sections-content-list{height: ${dif_height}px !important;max-height:1000px !important}`);
                    }else{
                        // let temp_height = list_height + 10
                        // GM_addStyle(`.video-sections-content-list{height: ${temp_height}px !important;max-height:1000px !important}`);
                        GM_addStyle(`.video-sections-content-list{height: unset !important;max-height:1000px !important}`);
                    }
                }else{
                    // 兼容脚本:在侧边显示 Bilibili 视频字幕/文稿(原始版)
                    if(list_height > res_height){
                        GM_addStyle(`.video-sections-content-list{height: ${res_height-280}px !important;max-height:1000px !important}`);
                    }else{
                        // let temp_height = list_height + 10
                        // GM_addStyle(`.video-sections-content-list{height: ${temp_height}px !important;max-height:1000px !important}`);
                        GM_addStyle(`.video-sections-content-list{height: unset !important;max-height:1000px !important}`);

                    }
                }



                // 添加滑动鼠标一个浅的聚焦颜色
                GM_addStyle(`.video-episode-card:hover{background:#DCE2E3}`);
                // 去除字体蓝色提醒
                GM_addStyle(`.video-episode-card__info:hover .video-episode-card__info-title{color:#000 !important}`);
                GM_addStyle(`.video-episode-card:hover .video-episode-card__info-title{color:#000 !important}`);

            }
        }
    };

    // 针对带分类的视频合集列表
    let fn3 = function(){
        if(document.querySelector(".base-video-sections-v1")){
            if(document.querySelector(".video-section-title")){

                change_right_width("fn3");

                let list_height = window.innerHeight;
                let right_content_top_heigt = document.querySelector(".base-video-sections-v1").offsetTop;
                let dif_height = list_height - right_content_top_heigt - 150;
                // 兼容脚本:在侧边显示 Bilibili 视频字幕/文稿(原始版)
                if(!document.querySelector(".transcript-box")){
                    // 默认一屏
                    GM_addStyle(`.video-sections-content-list{height: ${dif_height}px !important;max-height:1000px !important}`);
                }else{
                    GM_addStyle(`.video-sections-content-list{height: ${list_height-280}px !important;max-height:1000px !important}`);
                }
                // 加宽视频列表标题
                GM_addStyle(`.video-episode-card__info-title{width:100% !important}`);
                // 分类颜色和视频标题一样,略做修改增加辨识度
                GM_addStyle(`.video-section-title{background-color: #D1D8DA !important}`);
                // 去除分类区域奇怪的圆角
                GM_addStyle(`.video-sections-content-list{border-radius: 0px !important}`);
                // 重设分割线
                GM_addStyle(`.border-bottom-line{background:#F1F2F3 !important}`);
                // 添加滑动鼠标一个浅的聚焦颜色
                GM_addStyle(`.video-episode-card__info:hover{background:#DCE2E3}`);
                // 去除字体蓝色提醒
                GM_addStyle(`.video-episode-card__info-title:hover{color:#000 !important}`);
                GM_addStyle(`.video-episode-card:hover .video-episode-card__info-title{color:#000 !important}`);
            }
        }
    };

    // 针对带选集(分P)的视频合集列表
    let fn4 = function(){
        if(document.querySelector(".multi-page-v1")){
            if(document.querySelector(".cur-list")){

                change_right_width("fn4");

                change_title_wrap("fn4");

                let res_height = window.innerHeight;
                let list_height =  document.querySelector(".list-box").scrollHeight;

                let right_content_top_heigt = document.querySelector(".multi-page-v1").offsetTop;
                let dif_height = res_height - right_content_top_heigt - 120;

                // 计算列表高度,如果达不到一屏就不铺满
                if(!document.querySelector(".transcript-box")){
                    if(list_height > dif_height){
                        GM_addStyle(`.cur-list {height: ${dif_height}px !important;max-height:1000px !important}`);
                    }else{
                        GM_addStyle(`.cur-list {height: ${list_height}px !important;max-height:1000px !important}`);
                    }
                }else{
                    // 兼容脚本:在侧边显示 Bilibili 视频字幕/文稿(原始版)
                    if(list_height > res_height){
                        GM_addStyle(`.cur-list {height: ${res_height-280}px !important;max-height:1000px !important}`);
                    }else{
                        let temp_height = list_height + 10
                        GM_addStyle(`.cur-list {height: ${list_height}px !important;max-height:1000px !important}`);
                    }
                }


                // 添加滑动鼠标一个浅的聚焦颜色
                GM_addStyle(`.list-box>li:hover{background:#DCE2E3 !important}`);
        }   }
    };

    // 宽屏适配+自定义设置比率
    let change_right_width = function(source){
        // 如果有自定义比率,则优先使用
        if (GM_getValue("area_ratio") && GM_getValue("area_ratio")!=0){
            // console.log("检测到自定义比例")
            let body_width = document.querySelector("#app").offsetWidth;
            let res_width = window.innerWidth;
            var dif_width = Math.round(res_width*GM_getValue("area_ratio"),2);
            //不同的合集页需要单独适配
            if(source == "fn1"){
                // 调整合集名称长度
                GM_addStyle(`.first-line-left>a {max-width: ${dif_width}px !important}`);
                // 调整合集宽度
                GM_addStyle(`.right-container {width: ${dif_width}px !important}`);

                GM_addStyle(`.left-container {width: max-content !important}`);
                GM_addStyle(`#bilibili-player {width: 100% !important;height:max-content}`);
                GM_addStyle(`#playerWrap {height: max-content !important}`);


                // 调整标题宽度
                GM_addStyle(`.video-episode-card__info-title {width: 100% !important}`);
            }
            if(source == "fn2"){
                // 调整合集名称长度
                GM_addStyle(`.first-line-left>a {max-width: ${dif_width}px !important}`);
                // 调整合集宽度
                GM_addStyle(`.right-container {width: ${dif_width}px !important}`);

                GM_addStyle(`.left-container {width: max-content !important}`);
                GM_addStyle(`#bilibili-player {width: 100% !important;height:max-content}`);
                GM_addStyle(`#playerWrap {height: max-content !important}`);

                //调整合集内视频标题的宽度
                GM_addStyle(`.video-sections-item {width: 95% !important}`);
            }
            if(source == "fn3"){
                // 调整合集名称长度
                GM_addStyle(`.first-line-left>a {max-width: ${dif_width}px !important}`);
                // 调整合集宽度
                GM_addStyle(`.right-container {width: ${dif_width}px !important}`);

                GM_addStyle(`.left-container {width: max-content !important}`);
                GM_addStyle(`#bilibili-player {width: 100% !important;height:max-content}`);
                GM_addStyle(`#playerWrap {height: max-content !important}`);

                // 调整标题宽度
                GM_addStyle(`.video-episode-card__info-title {width: 100% !important}`);
            }
            if(source == "fn4"){
                //分P视频无需调整标题宽度,因为选集名称固定为“视频选集”
                // 调整合集宽度
                GM_addStyle(`.right-container {width: ${dif_width}px !important}`);

                GM_addStyle(`.left-container {width: max-content !important}`);
                GM_addStyle(`#bilibili-player {width: 100% !important;height:max-content}`);
                GM_addStyle(`#playerWrap {height: max-content !important}`);

                // 合集内需要单独调整
                GM_addStyle(`#multi_page .cur-list .list-box>li {width: ${dif_width}px}`);
                // 分P视频合集的分P时长偏右,微调
                GM_addStyle(`.cur-list>ul>li>a{margin-right: 15px !important}`);

            }
        }else if(GM_getValue("widescreen_status")){
            // console.log("检测到开启了宽屏")
            let body_width = document.querySelector("#app").offsetWidth;
            let res_width = window.innerWidth;

            if(res_width-100 > body_width){
                //带鱼屏
                let left_div = document.querySelector(".left-container").offsetWidth;
                let right_div = document.querySelector(".right-container").offsetWidth;
                var dif_width = (body_width - (left_div+right_div))+right_div-100;
                // GM_addStyle(`.right-container {width: ${dif_width}px !important}`);
            }else{
                //非带鱼屏
                let left_div = document.querySelector(".left-container").offsetWidth;
                let right_div = document.querySelector(".right-container").offsetWidth;
                var dif_width = (res_width - (left_div+right_div))+right_div-80;
                // GM_addStyle(`.right-container {width: ${dif_width}px !important}`);
        }
            //不同的合集页需要单独适配
            if(source == "fn1"){
                // 调整合集名称长度
                GM_addStyle(`.first-line-left>a {max-width: ${dif_width-200}px !important}`);
                // 调整合集宽度
                GM_addStyle(`.right-container {width: ${dif_width}px !important}`);
                // 调整标题宽度
                GM_addStyle(`.video-episode-card__info-title {width: 100% !important}`);
            }
            if(source == "fn2"){
                // 调整合集名称长度
                GM_addStyle(`.first-line-left>a {max-width: ${dif_width-200}px !important}`);
                // 调整合集宽度
                GM_addStyle(`.right-container {width: ${dif_width}px !important}`);

                //调整合集内视频标题的宽度
                GM_addStyle(`.video-sections-item {width: 95% !important}`);
            }
            if(source == "fn3"){
                // 调整合集名称长度
                GM_addStyle(`.first-line-left>a {max-width: ${dif_width-200}px !important}`);
                // 调整合集宽度
                GM_addStyle(`.right-container {width: ${dif_width}px !important}`);
            }
            if(source == "fn4"){
                //分P视频无需调整标题宽度,因为选集名称固定为“视频选集”
                // 调整合集宽度
                GM_addStyle(`.right-container {width: ${dif_width}px !important}`);
                // 合集内需要单独调整
                GM_addStyle(`#multi_page .cur-list .list-box>li {width: ${dif_width}px}`);
                // 分P视频合集的分P时长偏右,微调
                GM_addStyle(`.cur-list>ul>li>a{margin-right: 15px !important}`);

            }
        }

    }

    // 视频合集换行功能,不限制标题行数
    let change_title_wrap = function(source){
        if(GM_getValue("title_wrap_status")){
            if(source=="fn1"){
                GM_addStyle(`.base-video-sections-v1 .video-section-list .video-episode-card__info-title{white-space:normal !important;flex-wrap:wrap !important;max-height:300px !important}`);
                GM_addStyle(`.base-video-sections-v1 .video-section-list .video-episode-card{height:100% !important}`);
                // 因为换行会改变整体高度,这里需要重写高度
                GM_addStyle(`.video-section-list{height:100% !important}`);
                GM_addStyle(`.video-episode-card__info{height:100% !important}`);
                // 换行后会导致视频列表项不明显,这里加宽了项之间的间距
                GM_addStyle(`.video-episode-card__info{margin-bottom:6px !important}`);

            }
            if(source=="fn2"){
                //不处理,因为默认支持2行,足够显示长标题
            }
            if(source=="fn3"){
                GM_addStyle(".video-episode-card__info-title{white-space:normal !important;max-height:unset !important}")
                GM_addStyle(".base-video-sections-v1 .video-section-list .video-episode-card__info{height:unset !important}")
                GM_addStyle(".base-video-sections-v1 .video-section-list .video-episode-card{height:unset !important}")
                //todo:现在这么设置意味着收起分类功能会失效,但是想着全展开也没啥不好的,暂时不做修复
                GM_addStyle(".video-section-list{height:100% !important}")
                // 换行后会导致视频列表项不明显,这里加宽了项之间的间距
                GM_addStyle(`.video-episode-card{margin-bottom:6px !important}`);

            }
            if(source=="fn4"){
                GM_addStyle(`.multi-page-v1 .cur-list .list-box li{white-space:normal !important;height:unset !important;}`);
                // // 换行后会导致视频列表项不明显,这里缩小标题文字换行间距
                GM_addStyle(`.part{line-height:normal !important}`)
            }

        }
    }

    // 窗口大小变化时调整合集列表大小
    const getWindowInfo = () => {
        fn1();
        fn2();
        fn3();
        fn4();
    };
    const debounce = (fn, delay) => {
        let timer;
        return function() {
            if (timer) {
                clearTimeout(timer);
            }
            timer = setTimeout(() => {
                fn();
            }, delay);
        }
    };
    const cancalDebounce = debounce(getWindowInfo, 500);
    window.addEventListener('resize', cancalDebounce);

    fn1();
    fn2();
    fn3();
    fn4();

    if(GM_getValue("mini_height") != 0 ){
        GM_addStyle(`@media screen and (min-width: 1681px){
            .bpx-player-container[data-revision="1"][data-screen=mini], .bpx-player-container[data-revision="2"][data-screen=mini]{
                height:${GM_getValue("mini_height")}px !important;
                width:${GM_getValue("mini_width")}px !important;
            }
        }`);
        GM_addStyle(`
            .bpx-player-container[data-revision="1"][data-screen=mini], .bpx-player-container[data-revision="2"][data-screen=mini]{
                height:${GM_getValue("mini_height")}px !important;
                width:${GM_getValue("mini_width")}px !important;
            }`);
    }


    window.addEventListener('pushState', function(e) {
        fn1();
        fn2();
        fn3();
        fn4();
      });

    window.addEventListener('replaceState', function(e) {
        fn1();
        fn2();
        fn3();
        fn4();
      });

    // B站视频详情页的自动播放下一个视频,或者点击其他视频,使用的是pushState不会刷新页面,这里需要重写pushState、replaceState为来实现监听页面视频是否切换
    const bindEventListener = function(type) {
        const historyEvent = history[type];
        return function() {
            const newEvent = historyEvent.apply(this, arguments);
           const e = new Event(type);
            e.arguments = arguments;
            window.dispatchEvent(e);
            return newEvent;
        };
     };
     history.pushState = bindEventListener('pushState');
     history.replaceState = bindEventListener('replaceState');

     // 浏览器前进、后退时,重新计算高度
     window.onpopstate = function(event) {
        fn1();
        fn2();
        fn3();
        fn4();
    };

})();