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

停更,用这个👉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)

// ==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