哔哩哔哩网页视频速度右键菜单

Add video playback speed to the right-click menu, hide the low-usage right-click menu

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name               哔哩哔哩网页视频速度右键菜单
// @namespace          https://greasyfork.org/zh-CN/scripts/408724-%E5%93%94%E5%93%A9%E5%93%94%E5%93%A9%E7%BD%91%E9%A1%B5%E8%A7%86%E9%A2%91%E9%80%9F%E5%BA%A6%E5%8F%B3%E9%94%AE%E8%8F%9C%E5%8D%95
// @version            2.3
// @description        停更,用这个👉https://github.com/the1812/Bilibili-Evolved。隐藏使用率极低的右键菜单,添加修改视频倍速的右键菜单,自定义速度请自行修改第29行(最高支持16倍速,最低支持0.07倍速,10以上的速度支持小数点后一位,10以下的速度支持小数点后两位,例:16.0x,14.1x,6.77x,8.87x,15.2x,1.39x,12.8x,10.3x,10.2x,2.86x)
// @description:en     Add video playback speed to the right-click menu, hide the low-usage right-click menu
// @description:zh-CN  停更,用这个👉https://github.com/the1812/Bilibili-Evolved。隐藏使用率极低的右键菜单,添加修改视频倍速的右键菜单,自定义速度请自行修改第29行(最高支持16倍速,最低支持0.07倍速,10以上的速度支持小数点后一位,10以下的速度支持小数点后两位,例:16.0x,14.1x,6.77x,8.87x,15.2x,1.39x,12.8x,10.3x,10.2x,2.86x)
// @author             beibeibeibei
// @match              *.bilibili.com/video/*
// @match              *.bilibili.com/s/video/*
// @match              *.bilibili.com/bangumi/play/*
// @match              *.bilibili.com/cheese/play/*
// @match              *.bilibili.com/blackboard/*
// @match              *.bilibili.com/watchlater*
// /@/r/e/q/u/i/r/e            https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js
// ==/UserScript==

(function () {
    'use strict';
    let $ = jQuery.noConflict();

    //速度列表     例: = [3, 4, 5];                     //整数
    //                  = [6.1, 7.2, 8.3];               //小数
    //                  = [10.4, 11.5, 13.6, 14.7, 15.8];//10以上的速度支持小数点后一位
    //                  = [1.04, 1.15, 1.36, 1.47, 1.58];//10以下的速度支持小数点后两位
    //                  = [1.04567891230, 2.09876543210];//自动转换为合理数值[1.05, 2.10]
    //                  = [-3.141, 0.0001, 16.012, +100];//不支持,将自动转换为最接近的可支持数字[0.07,16.0](做人要x10再x10,WTM直接量子阅读)
    //                  = [3.00, 3.00, 3.00, 3.00, 3.00];//不支持,将自动删除重复数字[3.00]
    //                  = [1.01, 2.02, 3.03, 4.04 ,5.05];//不限数量,建议最多5-12个左右(主要是屏幕显示不下)
    let new_speed_array = [3, 4, 5, 6];//                                                                        //line 51

    function new_speed() {
        //bilibili网页电脑端添加视频速度菜单
        //鼠标悬浮并持续移动在视频窗口上时,在视频窗口下边框从右至左数第7个左右找到”倍数“菜单
        //这个菜单会显示多个速度按钮,本代码会再添加多个速度按钮

        //原速度填0补齐,保证整数位数+"."+小数位数+"x"=5位
        for (let i = 0, l = $(".bilibili-player-video-btn-speed-menu").children().length; i < l; i++) {
            let text = $(".bilibili-player-video-btn-speed-menu").children().eq(i).text();
            if (text.length == 4) {
                $(".bilibili-player-video-btn-speed-menu").children().eq(i).text(text.replace("x", "") + "0x");
            }
        }

        //将所有原速度存储到数组中备用
        let old_speed_array = [];
        for (let i = 0, l = $(".bilibili-player-video-btn-speed-menu").children().length; i < l; i++) {
            old_speed_array.push($(".bilibili-player-video-btn-speed-menu").children().eq(i).attr("data-value"));
        }

        //定义新速度数组
        //let new_speed_array = [0.111, 0.112, 15.84, 15.83, 11.37];                                             //line 29
        //处理数组1:合理替换小于0.07或大于16.0的不正常值(可能会出现重复数据)
        for (let i = 0; i < new_speed_array.length; i++) {
            if (new_speed_array[i] < 0.07) { new_speed_array[i] = 0.07; } else if (new_speed_array[i] > 16.0) { new_speed_array[i] = 16.0; }
        }
        //处理数组2:保留一位或两位小数(可能会出现重复数据)(过于接近的速度只会出现一个) 例子: 去重后,15.84和15.83重复只会生效15.8;0.111和0.112重复只会生效0.11
        for (let i = 0; i < new_speed_array.length; i++) {
            new_speed_array[i] = parseFloat(new_speed_array[i].toFixed(new_speed_array[i] > 10 ? 1 : 2));
        }
        //处理数组3:去重
        new_speed_array = [...new Set(new_speed_array)];
        //处理数组4:删除原版已有的速度值(只能删除查到的第一个,所以提前保证数组没有重复元素,放在去重后执行)
        for (let i = 0; i < old_speed_array.length; i++) {
            if (new_speed_array.indexOf(old_speed_array[i]) != -1) {
                new_speed_array.splice(new_speed_array.indexOf(old_speed_array[i]), 1);
            }
        }
        //处理数组5:数组排序
        new_speed_array.sort(function (a, b) { return (b - a); });

        //定义新速度字符串数组,保证整数位数+"."+小数位数+"x"=5位 例:"3.00x","16.0x"
        let new_speed_text_array = [];
        for (let i = 0, l = new_speed_array.length; i < l; i++) {
            if (new_speed_array[i] > 10) {
                new_speed_text_array.push(new_speed_array[i].toFixed(1) + "x");// 例:["16.0x","12.8x"]
            } else {
                new_speed_text_array.push(new_speed_array[i].toFixed(2) + "x");// 例:["3.00x","1.75x"]
            }
        }

        //定义索引数组,提前判断新速度应该放置的位置 例:0是指第0个元素之前的位置,也就是最开头
        let new_speed_index_array = [];
        for (let i = 0, nl = new_speed_array.length; i < nl; i++) {
            for (let j = 0, ol = old_speed_array.length; j < ol; j++) {
                if (new_speed_array[i] < old_speed_array[old_speed_array.length - 1]) {//比原版最后一个速度0.5都小(原版是排好序的)
                    new_speed_index_array.push(old_speed_array.length);
                    break;
                }
                if (new_speed_array[i] > old_speed_array[j]) {
                    new_speed_index_array.push(j);
                    break;
                }
            }
        }

        //给所有速度添加正常的选中效果(取消所有选中,再选中自己,从而解决原版程序不清楚本插件添加了新速度的问题)(修改classname后选中效果原版css会自动识别)
        $(".bilibili-player-video-btn-speed-menu").children().on("click", function () {
            $(this).siblings(".bilibili-player-active").removeClass("bilibili-player-active");
            $(this).addClass("bilibili-player-active");
        });
        //添加占位li,解决选中状态错误的问题(例:点原速度2.0x时,点亮的按钮是第一个bilibili-player-video-btn-speed-menu-list(原版)和本身(插件),只判断了顺序,并没有判断值)
        for (let i = 0; i < old_speed_array.length; i++) {
            $(".bilibili-player-video-btn-speed-menu").prepend('<li class="bilibili-player-video-btn-speed-menu-list new_speed_tampermonkey" style="display: none;">占位</li>');
        }

        //添加新的速度按钮
        for (let i = 0, l = new_speed_array.length; i < l; i++) {
            $(".bilibili-player-video-btn-speed-menu").children('[data-value="' + old_speed_array[new_speed_index_array[i]] + '"]').before
                ('<li class="bilibili-player-video-btn-speed-menu-list new_speed_tampermonkey" data-value="' + new_speed_array[i] + '">' + new_speed_text_array[i] + '</li>');
            if (new_speed_index_array[i] == old_speed_array.length) {
                $(".bilibili-player-video-btn-speed-menu").append
                    ('<li class="bilibili-player-video-btn-speed-menu-list new_speed_tampermonkey" data-value="' + new_speed_array[i] + '">' + new_speed_text_array[i] + '</li>');
            }
        }

        //给新的速度按钮添加点击事件,触发速度1点击事件,再修改选中效果,修改文字显示,修改playbackRate(不包含占位按钮)
        $(".new_speed_tampermonkey[data-value]").on("click", function () {
            $('li[data-value="1"]').click();
            $(this).siblings(".bilibili-player-active").removeClass("bilibili-player-active");
            $(this).addClass("bilibili-player-active");
            $(".bilibili-player-video-btn-speed-name").text($(this).text());
            $("video")[0].playbackRate = $(this).attr("data-value");
        });

        //问题:当点击顺序为A→N→A(A:原版速度;N:新增速度)时,原版程序无法了解已经切换了速度从而导致切换失效,一直停留在N速度上(A速度切A速度那就是没切)
        //解决:在新建速度点击事件前再添加一个原版速度点击事件,即A→1N→A,让原版程序认为最后一次速度是1(再切1时会切不了,让问题只在1上发生)(line 117)
        //遗留:需要解决点击1→1N→1的顺序后,实际速度为N的问题

        //遗留解决:你说没切就没切,就硬切
        $('li[data-value="1"]').on("click", function () {
            $("video")[0].playbackRate = 1;
            $(".bilibili-player-video-btn-speed-name").text("倍速");
        });
        //结束
    }


    $(document).on("DOMNodeInserted", ".bilibili-player-video-btn.bilibili-player-video-btn-speed", function () {
        // 显示速度填0补齐到5位(整数位数+"."+小数位数+"x"=5位)
        let str2five = function (mutationsList, observer) {
            for (var mutation of mutationsList) {
                let text = $("button[aria-label=倍速]").text();
                if (mutation.type == 'attributes' && mutation.attributeName == "class" && text.length == 4 && text != "倍速") {
                    $("button[aria-label=倍速]").text(text.replace("x", "") + "0x");
                }
            }
        };

        //监听速度li列表的显示和隐藏(实际为监听className变化),触发显示速度文字再修改
        let observer = new MutationObserver(str2five);
        observer.observe($(".bilibili-player-video-btn.bilibili-player-video-btn-speed")[0], { attributes: true });
    });
    $(document).on("DOMNodeInserted", ".bilibili-player-video-control-bottom-right", function (e) {
        if (e.target.className == "bilibili-player-video-btn bilibili-player-video-btn-speed" && $(".new_speed_tampermonkey").length == 0) {
            new_speed();
        }
    });


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //上半部分:添加新速度//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //下半部分:添加右键菜单////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    let Rmenu_show = false;//默认不显示原右键菜单
    let Rmenu_selector = ".bilibili-player-context-menu-container:eq(1)";
    let Rmenu_selector2 = ".bilibili-player-context-menu-origin";//style不支持eq
    let border_top_li_style = "border-top:1px solid hsla(0,0%,100%,.12);";
    let old_menu_li_selector = ".context-line.context-menu-function";

    let new_menu_style = "background-color: rgba(255,255,255,0.12);";
    let new_menu_str1 = ["加速", "原速", "减速"];
    let new_menu_str2 = "★";
    let new_menu_selector = ".new_context_menu_tampermonkey";
    let new_menu_classname = new_menu_selector.substr(1);

    let more_setting_btn_selector = ".bilibili-player-video-btn-setting-left-more-text";
    let more_setting_selector = ".bilibili-player-video-btn-setting-right";
    let more_setting_children_prefix_str = more_setting_selector.substr(1);

    //拼接字符串:"显示原右键菜单(15秒后重新隐藏)"
    let UHCT_before = "显示原右键菜单(";
    let unhide_contextmenu_time = 15;//临时显示的限制时间,单位秒
    let UHCT_after = "秒后重新隐藏)";
    //拼接字符串的颜色
    let new_setting_color = "white";
    let new_setting_color_hover = "#00a1d6";
    let new_setting_selector = ".new_setting_tampermonkey";
    let new_setting_str = new_setting_selector.substr(1);

    //new_context_menu中有字符串处理
    function str2five__(str) {
        str = str.replace("x", "");
        str = parseFloat(str).toFixed(str.indexOf('.') == 1 ? 2 : 1);//一共5位,点和x占了两位,整数要占位,所以不是保留一位就是保留两位
        if (str < 0.07) { str = "0.07"; } else if (str > 16.0) { str = "16.0"; }
        return str + "x";
    }
    function str_before(str) {
        let before = "";
        if (parseFloat(str) < 1) {
            before = new_menu_str1[2];//减速
        } else if (parseFloat(str) > 1) {
            before = new_menu_str1[0];//加速
        } else if (parseFloat(str) == 1) {
            before = new_menu_str1[1];//原速
        }
        return before + str;
    }
    function str_after_(str, j) {
        return str + new_menu_str2.repeat(j);
    }

    //one绑定事件
    function new_setting() {
        $(more_setting_selector).append(
            '<div class="' + more_setting_children_prefix_str + "-" + new_setting_str + '">' +
            '    <style>.' + new_setting_str + '{color: ' + new_setting_color + ';}.' + new_setting_str + ':hover{color: ' + new_setting_color_hover + ';}</style>' +
            '    <ul>' +
            '        <li><a class="' + new_setting_str + '">' + UHCT_before + unhide_contextmenu_time + UHCT_after + '</a></li>' +
            '    </ul>' +
            '</div>');
    }

    //限时显示原右键菜单
    function time_limited_show_context_menu() {
        Rmenu_show = true;
        context_menu();
        setTimeout(function () {
            Rmenu_show = false;
            context_menu();
        }, (unhide_contextmenu_time * 10 * 10 * 10));
    }

    //on绑定事件
    function context_menu() {
        let context_menu_li = $(Rmenu_selector + " > ul > " + old_menu_li_selector);
        if (Rmenu_show) {
            context_menu_li.show();
        } else {
            context_menu_li.hide();
        }
        //右键在弹幕上了,用setTimeout延迟判断是否有弹幕的右键菜单,直接运行判断不到
        setTimeout(function () {
            if ($(".context-line.context-menu-danmaku").length > 0) {
                $(new_menu_selector).hide();
            } else {
                $(new_menu_selector).show();
            }
        });
    }

    //on绑定事件
    function new_context_menu() {
        if ($(new_menu_selector).length == 0) {
            $(Rmenu_selector).prepend(
                '<style>' + Rmenu_selector2 + ' ul+ul>li:first-child{' + border_top_li_style + '}</style>' +
                '<style>' + new_menu_selector + ' >li:hover{' + new_menu_style + '}</style>' +
                '<ul class="' + new_menu_classname + '">' +
                '</ul>'
            );
            let speed_text_array = [];
            $("ul.bilibili-player-video-btn-speed-menu").children("[data-value]").each(function (index) {
                speed_text_array.push($(this).text());
            });
            for (let i = 0, l = speed_text_array.length; i < l; i++) {
                $(Rmenu_selector).children(new_menu_selector).append('<li><a class="' + new_menu_classname + ' ' + i + '">' + speed_text_array[i] + '</a></li>');
                $(new_menu_selector + "." + i).on("click", function () {
                    $(".bilibili-player-video-btn-speed-menu-list[data-value]").eq(i).click();
                });
                $(new_menu_selector + "." + i).text(str2five__($(new_menu_selector + "." + i).text())); //位数对齐
                $(new_menu_selector + "." + i).text(str_before($(new_menu_selector + "." + i).text())); //添加前缀
                $(new_menu_selector + "." + i).text(str_after_($(new_menu_selector + "." + i).text(), (l - i))); //添加后缀
            }
        }
    }

    //缩放菜单
    function throttle(fn, delay) {
        delay = delay || 500;
        let runFlag = false;
        return function (e) {
            if (runFlag) {
                return false;
            }
            runFlag = true;
            setTimeout(() => {
                fn(e);
                runFlag = false;
            }, delay);
        };
    }
    function debounce(fn, delay) {
        delay = delay || 30;
        let handle;
        return function (e) {
            clearTimeout(handle);
            handle = setTimeout(() => {
                fn(e);
            }, delay);
        }
    }
    function zoom_menu() {
        let a = $(".bilibili-player-context-menu-container > ul");
        let b = $(".bilibili-player-hotkey-panel-container");
        let c = $(".bl-audio-panel.bl-audio-blue");
        let d = $(".bilibili-player-color-panel");
        //能正常点到右上角关闭就谢天谢地了,不缩放更费劲
        if ($(".drag-bar").length > 0) {
            a.css("zoom", "0.7");
            b.css("zoom", "0.68");
            c.css("zoom", "0.74");
            d.css("zoom", "0.85");
        } else {
            a.css("zoom", "1");
            b.css("zoom", "1");
            c.css("zoom", "1");
            d.css("zoom", "1");
        }
    }


    //点击“更多播放设置”触发,添加一个设置菜单
    $(document).one("click", more_setting_btn_selector, new_setting);
    //点击添加的设置菜单触发,设置显示或隐藏的开关变量并刷新显示状态
    $(document).on("click", new_setting_selector, time_limited_show_context_menu);
    //右击视频窗口间接触发,显示处理后的原右键菜单(处理成可见或不可见)
    $(document).on("DOMNodeInserted", Rmenu_selector, context_menu);
    //右击视频窗口间接触发,增加一个新的右键菜单(添加速度按钮)
    $(document).on("DOMNodeInserted", Rmenu_selector, new_context_menu);
    //屏幕滚动时触发,缩放一些窗口适应迷你播放器大小
    $(window).scroll(throttle(zoom_menu));
    //补丁:修复右侧推荐列表的视频无法生效的问题(并没有修,增加新右键菜单时使用on绑定就好了)


})();
//Search box placeholder comment
//Search box placeholder comment
//Search box placeholder comment