bilibili调速插件

bilibili调速插件,可以修改速度菜单栏选项,同时支持键盘操作,c键加速0.1x,x键减速0.1x

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         bilibili调速插件
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  bilibili调速插件,可以修改速度菜单栏选项,同时支持键盘操作,c键加速0.1x,x键减速0.1x
// @author       henyuer
// @match        https://www.bilibili.com/video/*
// @match        https://www.bilibili.com/bangumi/play/*
// @icon         https://www.bilibili.com/favicon.ico
// @grant        none
// ==/UserScript==

(function () {
    'use strict';
    const CURRENT_RATE = "current_rate"; //关键字,内容存储在本地
    let rates = ['2.0', '1.75', '1.5', '1.25', '1.0', '0.75', '0.5', '0.25']; //速率表
    let initRate; //上次关闭时的速率

    //获取上次观看速率
    let getCurrentRate = () => {
        let saved = parseFloat(localStorage.getItem(CURRENT_RATE))
        initRate = isNaN(saved) || saved <= 0 ? 1 : saved;
        console.log(initRate);
    }

    let isBangumi = () => {
        const path = window.location.pathname;
        if (path.startsWith("/bangumi/play")) return true;
        return false;
    }


    //重建速率选择表
    //仅替换li元素,尽量少的影响dom树
    let rebuildRateMenu = () => {
        const oldRateUl = document.querySelector(".bpx-player-ctrl-playbackrate-menu");
        while (oldRateUl.firstChild) {
            oldRateUl.removeChild(oldRateUl.firstChild);
        }
        rates.forEach(rate => {
            const li = document.createElement("li");
            li.classList.add("bpx-player-ctrl-playbackrate-menu-item");
            li.dataset.value = parseFloat(rate);
            li.innerHTML = rate + 'x';

            // 添加li的click事件,存储新的速率,其余操作应该父元素会冒泡执行
            li.addEventListener('click', () => {
                localStorage.setItem(CURRENT_RATE, rate);
            })
            oldRateUl.appendChild(li);
        })
    }

    let setInitSpeed = () => {
        const video = document.querySelector('video');
        if (!video) return;

        video.playbackRate = initRate;
    }

    //监测播放器控件是否插入
    let observeAndExe = () => {
        const ctrObserver = new MutationObserver(() => {
            if (document.querySelector(".bpx-player-ctrl-playbackrate-menu")) {
                getCurrentRate();
                rebuildRateMenu();
                setInitSpeed();
                ctrObserver.disconnect();
            }
        });
        const controlWrap = document.querySelector(".bpx-player-control-wrap");
        //普通video只需要监听controlWrap
        if (!isBangumi()) {
            ctrObserver.observe(controlWrap, { childList: true, subtree: true });
            return;
        }
        //bangumi需要监听到player wrap
        const playWrap = document.querySelector("#bilibili-player-wrap");
        const WrapObserver = new MutationObserver(() => {
            if (document.querySelector(".bpx-player-control-wrap")) {
                const curControlWrap = document.querySelector(".bpx-player-control-wrap");
                ctrObserver.observe(curControlWrap, { childList: true, subtree: true });
                WrapObserver.disconnect();
            }
        })
        WrapObserver.observe(playWrap, { childList: true, subtree: true });
    }

    observeAndExe();

    //DOM root添加键盘事件
    document.addEventListener('keydown', (e) => {
        //确保焦点不在可编辑区
        if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'BILI-COMMENTS' || e.target.isContentEditable) {
            return;
        }

        const video = document.querySelector('video');
        if (!video) return;

        let speed = video.playbackRate;
        if (e.key === 'x')
            speed = Math.max((speed - 0.1).toFixed(2), 0.1);
        else if (e.key === 'c')
            speed = Math.min((speed + 0.1).toFixed(2), 8.0);

        const lies = document.querySelectorAll(".bpx-player-ctrl-playbackrate-menu")
        if (lies.length > 0) {
            lies.forEach(li => {
                if (li.dataset.value === speed) li.click();
            })
        }
        else return;

        video.playbackRate = speed;
        localStorage.setItem(CURRENT_RATE, speed);

        e.preventDefault();
        e.stopImmediatePropagation();

    }, true)

})();