自定义哔哩哔哩视频播放速度,记住播放速度

可以使用按键 z(恢复1倍速)、x(减0.1倍速)、c(加0.1倍速)、s(记住或忘记播放速度), 跳过充电

// ==UserScript==
// @name         自定义哔哩哔哩视频播放速度,记住播放速度
// @namespace    http://tampermonkey.net/
// @version      1.0.3
// @description  可以使用按键 z(恢复1倍速)、x(减0.1倍速)、c(加0.1倍速)、s(记住或忘记播放速度), 跳过充电
// @author       felix
// @icon         chrome://favicon/http://www.bilibili.com/
// @match        https://www.bilibili.com/video/*
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// ==/UserScript==
(function () {
    'use strict';

    // console.log(`[${GM_info.script.name}]: 开始运行`);

    // ===================================================配置区=====================================================================================
    var STORAGE_KEY = {
        BILIBILI_VIDEO_SPEED_SIWTCH: "bilibili_video_speed_switch",
        BILIBILI_VIDEO_SPEED: "bilibili_video_speed"
    };

    var SETTING = {
        STEP_SIZE: 0.1,
        MAX_SPEED: 5,
        MIN_SPEED: 0.1,
        REMEMBER_SPEED_MENU_ID: null,
    };

    var DOM_NAME = {
        SPEED: ".bilibili-player-video-btn-speed-name",
        VIDEO: "bwp-video",
        VIDEO_2: "video",
        JUMP_BUTTON: ".bilibili-player-electric-panel-jump",
        CUSTOMER_TOAST: ".bilibili-player-volumeHint.felix",
        CUSTOMER_TOAST_SUB_DOM: ".bilibili-player-volumeHint.felix .bilibili-player-volumeHint-text",
    }

    var CONSTANT_DATA = {
        REMOVE_SPEED_TOAST_INTERVAL: null
    }

    // ===================================================加载区=====================================================================================

    setTimeout(loading, 3000);

    function loading() {
        addToast();
        loadSpeed();
        addInterval();
    }

    function addInterval() {
        addSpeedInterval();
        addJumpElectricInterval();
    }

    // 每3秒检测一次播放速度
    function addSpeedInterval() {
        return setInterval(() => {
            var videoSpeed = getVideoSpeed();
            var videoSpeedTextNumber = getVideoSpeedTextNumber();
            if (Number(videoSpeed) !== videoSpeedTextNumber) {
                console.log("change speed", videoSpeedTextNumber)
                changeSpeed(videoSpeedTextNumber, false);
            }
        }, 3000);
    }

    // 跳过充电
    function addJumpElectricInterval() {
        return setInterval(() => {
            if ($(DOM_NAME.JUMP_BUTTON).length > 0) {
                $(DOM_NAME.JUMP_BUTTON).trigger('click')
            }
        }, 500)
    }

    function loadSpeed() {
        var speedSwitch = localStorage.getItem(STORAGE_KEY.BILIBILI_VIDEO_SPEED_SIWTCH)
        if (speedSwitch == "true") {
            var speed = localStorage.getItem(STORAGE_KEY.BILIBILI_VIDEO_SPEED);
            if (speed) {
                changeSpeed(speed, false);
                loadRemoveSpeedMenu();
            } else {
                loadSaveSpeedMenu();
            }
        }
    }

    // =============================================== toast ==========================================================
    function hasToast() {
        var toastDom = $(DOM_NAME.CUSTOMER_TOAST)
        return toastDom != null && toastDom != undefined;
    }

    function addToast() {
        var div = $(
            `
                <div class="bilibili-player-volumeHint felix" style="opacity: 100; display: none; ">
                    <span class="bilibili-player-volumeHint-text">felix</span>
                </div>
            `
        )
        $(".bilibili-player-video-wrap").append(div);
    }

    function toast(str) {
        $(DOM_NAME.CUSTOMER_TOAST).attr("style", '{ "opacity": 100, "display": "block" }');
        $(DOM_NAME.CUSTOMER_TOAST_SUB_DOM).text(str);

        if (CONSTANT_DATA.REMOVE_SPEED_TOAST_INTERVAL) {
            clearInterval(CONSTANT_DATA.REMOVE_SPEED_TOAST_INTERVAL);
        }
        CONSTANT_DATA.REMOVE_SPEED_TOAST_INTERVAL = setInterval(() => {
            var opacity = $(DOM_NAME.CUSTOMER_TOAST).css("opacity")
            opacity = opacity - 0.05;
            if (opacity <= 0.7) {
                $(DOM_NAME.CUSTOMER_TOAST).css("opacity", 0);
                $(DOM_NAME.CUSTOMER_TOAST).css("dispaly", "none");
                clearInterval(CONSTANT_DATA.REMOVE_SPEED_TOAST_INTERVAL);
            } else {
                $(DOM_NAME.CUSTOMER_TOAST).css("opacity", opacity);
            }
        }, 200);
    }

    // ===================================================获取控件区=====================================================================================
    function getVideo() {
        var video = document.querySelector(DOM_NAME.VIDEO)
        return video ? video : document.querySelector(DOM_NAME.VIDEO_2);
    }



    // ===================================================键盘监听区=====================================================================================
    document.onkeydown = function (e) {
        if (e.target.nodeName !== 'BODY') return;
        if (/^[zxcZXCsS]$/.test(e.key)) {
            if (e.key === 'z' || e.key === 'Z') changeSpeed(1, true);
            if (e.key === 'x' || e.key === 'X') reduceSpeed();
            if (e.key === 'c' || e.key === 'C') addSpeed();
            if (e.key === 's' || e.key === 'S') changeSaveSpeedSwitch();
        }
    };

    // ==================================================播放速度=================================================================================
    // 加载记住播放速度菜单
    function loadSaveSpeedMenu() {
        if (SETTING.REMEMBER_SPEED_MENU_ID) {
            GM_unregisterMenuCommand(SETTING.REMEMBER_SPEED_MENU_ID);
        }
        SETTING.REMEMBER_SPEED_MENU_ID = GM_registerMenuCommand("记住播放速度", openSaveSpeedSwitch);
    }

    // 加载忘记播放速度菜单
    function loadRemoveSpeedMenu() {
        if (SETTING.REMEMBER_SPEED_MENU_ID) {
            GM_unregisterMenuCommand(SETTING.REMEMBER_SPEED_MENU_ID);
        }
        SETTING.REMEMBER_SPEED_MENU_ID = GM_registerMenuCommand("忘记播放速度", closeSaveSpeedSwitch);
    }

    function changeSaveSpeedSwitch() {
        var speedSwitch = localStorage.getItem(STORAGE_KEY.BILIBILI_VIDEO_SPEED_SIWTCH);
        if (speedSwitch == "true") {
            // 当前是记住
            closeSaveSpeedSwitch();
            toast("已忘记");
        } else {
            openSaveSpeedSwitch();
            toast("已记住");
        }
    }

    // 开启记住播放速度开关
    function openSaveSpeedSwitch() {
        localStorage.setItem(STORAGE_KEY.BILIBILI_VIDEO_SPEED_SIWTCH, true);
        setSpeedToStorage();
    }

    // 关闭记住播放速度开关
    function closeSaveSpeedSwitch() {
        localStorage.setItem(STORAGE_KEY.BILIBILI_VIDEO_SPEED_SIWTCH, false);
        removeSpeedFromStorage();
    }

    // 保存播放速度到localStorage
    function setSpeedToStorage() {
        localStorage.setItem(STORAGE_KEY.BILIBILI_VIDEO_SPEED, getVideoSpeed());
        loadRemoveSpeedMenu();
    }

    // 从localStorage中删除保存的播放速度
    function removeSpeedFromStorage() {
        localStorage.removeItem(STORAGE_KEY.BILIBILI_VIDEO_SPEED);
        loadSaveSpeedMenu();
    }

    // 获取当前播放速度
    function getVideoSpeed() {
        return getVideo().playbackRate;
    }

    // 获取当前展示的播放速度
    function getVideoSpeedTextNumber() {
        var speedText = $(DOM_NAME.SPEED).text()
        if (speedText == "倍速") {
            speedText = "1";
        }
        speedText = speedText.replaceAll("x", "")
        return Number(speedText);
    }

    // 改变展示的播放速度文字
    function changeSpeedText(playSpeed) {
        $(DOM_NAME.SPEED).text(playSpeed);
    }

    // 减速
    function reduceSpeed(stepSize) {
        if (!stepSize) {
            stepSize = SETTING.STEP_SIZE;
        }
        var playSpeed = Number(getVideoSpeedTextNumber() - stepSize).toFixed(1);
        changeSpeed(playSpeed, true);
    }

    // 加速
    function addSpeed(stepSize) {
        if (!stepSize) {
            stepSize = SETTING.STEP_SIZE;
        }
        var playSpeed = Number(getVideoSpeedTextNumber() + stepSize).toFixed(1);
        changeSpeed(playSpeed, true);
    }

    // 改变播放速度
    function changeSpeed(playSpeed, showToast) {
        if (playSpeed && playSpeed >= SETTING.MIN_SPEED && playSpeed <= SETTING.MAX_SPEED) {
            doChangeSpeed(playSpeed);
            changeSpeedText(playSpeed);
            var saveSpeedswitch = localStorage.getItem(STORAGE_KEY.BILIBILI_VIDEO_SPEED_SIWTCH);
            if (saveSpeedswitch) {
                setSpeedToStorage();
            }
            if (!hasToast()) {
                addToast();
            }
            if (showToast) {
                toast(playSpeed);
            }
        }
    }

    // 执行调整播放速度
    function doChangeSpeed(playSpeed) {
        getVideo().playbackRate = Number(playSpeed);
    }
})();