bilibili关灯

bilibili关灯(把被新版B站藏起来的关灯按钮揪出来,在关闭弹幕按钮左边,还可以用快捷键,默认'A')、非全屏滚轮音量控制、弹幕控制快捷操作等

目前為 2021-08-06 提交的版本,檢視 最新版本

// ==UserScript==
// @name         bilibili关灯
// @namespace    hhh2000
// @version      0.9.1
// @description  bilibili关灯(把被新版B站藏起来的关灯按钮揪出来,在关闭弹幕按钮左边,还可以用快捷键,默认'A')、非全屏滚轮音量控制、弹幕控制快捷操作等
// @author       hhh2000
-// @include      http*://www.bilibili.com/video/av*
-// @include      http*://www.bilibili.com/video/BV*
-// @include      http*://www.bilibili.com/watchlater/*
-// @include      http*://www.bilibili.com/medialist/play/*
-// @include      http*://www.bilibili.com/bangumi/play/ep*
-// @include      http*://www.bilibili.com/bangumi/play/ss*
-// @include      http*://bangumi.bilibili.com/anime/*/play*
-// @include      http*://bangumi.bilibili.com/movie/*
-// @exclude      http*://bangumi.bilibili.com/movie/
// @require      https://cdn.staticfile.org/jquery/1.12.4/jquery.min.js
// @run-at       document-end
// @grant        none
// ==/UserScript==

'use strict';
hhh_lightoff_main = {
    init() {
        var //
            ver,
            fps,
            h5Player;
        const [BILI_2_X, BILI_3_X, ALL] = ['bili_2.x', 'bili_3.x', 'all'];
        var //切换番剧和一般视频class
            bb = {},
            bb_type = '',
            bb_config = {
                bb_class_data: {  //其实这样不方便调试
                    //'player':{BILI_2_X:'.player', [BILI_3_X]:'.bpx-player'}, //main

                    'playTipWrap':{[BILI_2_X]:'.bilibili-player-dm-tip-wrap', [BILI_3_X]:'.bpx-player-video-perch'}, //paly/pause
                    'webFullScreen':{[BILI_2_X]:'.bilibili-player-video-web-fullscreen', [BILI_3_X]:'.squirtle-video-pagefullscreen.squirtle-video-item'}, //网页全屏
                    'wideScreen':{[BILI_2_X]:'.bilibili-player-video-btn-widescreen', [BILI_3_X]:'.squirtle-video-widescreen.squirtle-video-item'}, //宽屏

                    //弹幕
                    'danmukuTopClose':{[BILI_2_X]:'.bilibili-player-block-filter-type[data-name=ctlbar_danmuku_top_close]', [BILI_3_X]:'.bpx-player-block-filter-type.bpx-player-block-typeTop.bpx-player-active'}, //顶部弹幕
                    'danmukuTop'     :{[BILI_2_X]:'.bilibili-player-block-filter-type[ftype=top]', [BILI_3_X]:'.bpx-player-block-filter-type.bpx-player-block-typeTop'}, //顶部弹幕
                    'danmukuBottomClose':{[BILI_2_X]:'.bilibili-player-block-filter-type[data-name=ctlbar_danmuku_bottom_close]', [BILI_3_X]:'.bpx-player-block-filter-type.bpx-player-block-typeBottom.bpx-player-active'}, //底部弹幕
                    'danmukuBottom'     :{[BILI_2_X]:'.bilibili-player-block-filter-type[ftype=bottom]', [BILI_3_X]:'.bpx-player-block-filter-type.bpx-player-block-typeBottom'}, //底部弹幕
                    'progressVal'   :{[ALL]:'.bui-progress-val'}, //弹幕透明度读数
                    'progressWrap'  :{[ALL]:'.bui-progress-wrap'}, //弹幕透明度进度条
                    'settingOpacity':{[BILI_2_X]:'.bilibili-player-setting-opacity', [BILI_3_X]:'.bpx-player-dm-setting-ui-opacity'}, //弹幕透明度
                    'settingArea'   :{[BILI_2_X]:'.bilibili-player-setting-area', [BILI_3_X]:'.bpx-player-dm-setting-ui-area'}, //显示区域

                    //音量
                    'volumeHint'    :{[BILI_2_X]:'.bilibili-player-volumeHint', [BILI_3_X]:'.bpx-player-volume-hint'}, //音量显示
                    'volumeHintText':{[BILI_2_X]:'.bilibili-player-volumeHint-text', [BILI_3_X]:'.bpx-player-volume-hint-text'}, //音量显示百分比读数
                    'volumeHintIcon':{[BILI_2_X]:'.bilibili-player-volumeHint-icon', [BILI_3_X]:'.bpx-player-volume-hint-icon'}, //音量显示图标
                    'volumebarWrp'  :{[BILI_2_X]:'.bilibili-player-video-volumebar-wrp', [BILI_3_X]:'.squirtle-volume-bar-wrap'}, //音量条
                    'volumeNum'     :{[BILI_2_X]:'.bilibili-player-video-volume-num', [BILI_3_X]:'.squirtle-volume-num'}, //音量读数

                    //视频速度
                    'videoSpeedActive':{[BILI_2_X]:'.bilibili-player-video-btn-speed-menu-list.bilibili-player-active', [BILI_3_X]:'.squirtle-speed-select-list .squirtle-select-item.active'}, //视频速度
                    'videoSpeed':{[BILI_2_X]:'.bilibili-player-video-btn-speed-menu-list', [BILI_3_X]:'.squirtle-speed-select-list .squirtle-select-item'}, //视频速度
                    'videoSpeedName':{[BILI_2_X]:'.bilibili-player-video-btn-speed-name', [BILI_3_X]:'.squirtle-select-result.squirtle-speed-select-result'}, //视频速度

                    //弹幕设置等
                    'switchBody':{[ALL]:'.bui-switch-body'}, //系统关灯css设置
                    'switchDot':{[ALL]:'.bui-switch-dot'}, //系统弹幕设置按钮wrap进度条拖动点
                    'switchInput':{[ALL]:'.bui-switch-input'}, //弹幕设置switch按钮
                    'danmakuRoot':{[BILI_2_X]:'.bilibili-player-video-danmaku-root', [BILI_3_X]:'.bpx-player-dm-root'}, //系统弹幕设置条
                    'danmakuSwitch':{[BILI_2_X]:'.bilibili-player-video-danmaku-switch', [BILI_3_X]:'.bpx-player-dm-switch'}, //关闭弹幕按钮
                    'danmakuSetting':{[BILI_2_X]:'.bilibili-player-video-danmaku-setting', [BILI_3_X]:'bpx-player-dm-setting'}, //系统弹幕设置按钮
                    'danmakuSettingWrap':{[BILI_2_X]:'.bilibili-player-video-danmaku-setting-wrap', [BILI_3_X]:'.bpx-player-dm-setting-wrap'}, //系统弹幕设置按钮wrap
                    'videoWrap':{[BILI_2_X]:'.bilibili-player-video-wrap', [BILI_3_X]:'.bpx-player-video-area'}, //播放wrap
                    'videoContextMenu':{[BILI_2_X]:'.bilibili-player-video-wrap', [BILI_3_X]:'.bpx-player-video-perch'}, //播放contextmenu
                    'video':{[BILI_2_X]:'.bilibili-player-video', [BILI_3_X]:'.bpx-player-video-wrap'}, //播放
                    'videoTopMask':{[BILI_2_X]:'.bilibili-player-video-top-mask', [BILI_3_X]:'.bpx-player-top-mask'}, //全屏时鼠标悬停时产生的顶端mask

                    //系统设置等
                    'playArea':{[ALL]:'.bilibili-player-area'}, //哔哩哔哩播放器
                    'playVideo':{[BILI_2_X]:'.bilibili-player-video-btn'}, //系统设置
                    'playSetting':{[BILI_2_X]:'.bilibili-player-video-btn-setting'}, //系统播放设置
                    'playSettingWrap':{[BILI_2_X]:'.bilibili-player-video-btn-setting-wrap'}, //系统播放设置wrap
                    'playSettingAutoplay':{[BILI_2_X]:'.bilibili-player-video-btn-setting-left-autoplay input', [BILI_3_X]: '.squirtle-setting-autoplay'}, //自动播放
                    'playSettingRepeatInput':{[BILI_2_X]:'.bilibili-player-video-btn-setting-left-repeat input', [BILI_3_X]:'.squirtle-setting-loop'}, //洗脑循环按钮
                    'playSettingLightoff':{[BILI_2_X]:'.bilibili-player-video-btn-setting-right-others-content-lightoff input', [BILI_3_X]:'.squirtle-single-setting-other-choice.squirtle-lightoff'}, //关灯按钮
                    'bpxStateLightOff':{[BILI_3_X]:'.bpx-state-light-off'}, //关灯bpx

                    //右键菜单
                    //'playerContextMenu':{[BILI_2_X]:'.bilibili-player-context-menu-container', [BILI_3_X]:'.bpx-player-contextmenu.bpx-player-black.bpx-player-active'}, //右键菜单
                    'playerContextMenu':{[BILI_2_X]:'.bilibili-player-context-menu-container.black.bilibili-player-context-menu-origin', [BILI_3_X]:'.bpx-player-contextmenu.bpx-player-black.bpx-player-active'}, //右键菜单
                    'hotkeyPanel':{[BILI_2_X]:'.bilibili-player-hotkey-panel'}, //快捷键说明面板
                    'videoInfo':{[BILI_2_X]:'.bilibili-player-video-info', [BILI_3_X]:'.bpx-player-info'}, //视频统计信息
                    'videoInfoClose':{[BILI_2_X]:'.bilibili-player-video-info-close', [BILI_3_X]:'.bpx-player-info-close'}, //视频统计信息关闭按钮
                    'videoInfoContainer':{[BILI_2_X]:'.bilibili-player-video-info-container', [BILI_3_X]:'.bpx-player-info-container'}, //视频统计信息
                    'videoInfoShow':{[BILI_2_X]:'.bilibili-player-video-info-container active', [BILI_3_X]:'.bpx-player-info-container'}, //视频统计信息面板显示
                    'DOMNodeInsertedVideoInfoShow':{[BILI_2_X]:'.bilibili-player-video-info-container active', [BILI_3_X]:'.info-line'}, //
                },
                set_bb(_bb_type) {
                    bb_type = _bb_type;
                    for(var k in this.bb_class_data){
                        var class_str = this.bb_class_data[k][bb_type] || this.bb_class_data[k][[ALL]];
                        bb[k] = class_str;
                    }
                }
           };

        const [ON, OFF] = [true, false];
        var //config
            keycode = {
                'left': 37,
                'right': 39,
                'up': 38,
                'down': 40,
                'space': 32,
            }
            config = {
                //一些主要开关设置
                sets: {},
                getCheckboxSetting(key) {
                    return this.sets[key]['status'];
                },
                saveCheckboxSetting() {
                    for(var o in this){
                        if(o.indexOf('b_') === 0){
                            var op = this[o]['options'];
                            for(var k in op){
                                this.sets[k] = op[k];
                            }
                        }
                    }
                },
                b_playerCheckbox: {
                    options: {
                        lightOffWhenPlaying: { text: '播放时自动关灯', status: OFF },
                        lightOnWhenPause: { text: '暂停时自动开灯', status: OFF },
                        autoPlay: { text: '自动播放', status: OFF, fn: '', tips: '' },
                        lightOff: { text: '自动关灯', status: OFF, tips: '' },
                        volumeControlWhenNonFullScreen: { text: '非全屏滚轮音量调节', status: ON, tips: '' },
                        volumeControlWhenPause: { text: '非全屏暂停时滚轮音量调节', status: ON, tips: '' },
                        danmuOpacityControl: { text: '滚轮弹幕透明度控制', status: ON, tips: '' }, //ctrl+滚轮
                        removeVideoTopMask: { text: '去掉顶部mask', status: ON, tips: '' },
                        //不显示有明显变化的提示,关灯、关弹幕等,因为对有些人来说这些操作变化明显可见,提示反而多余且遮挡屏幕
                        hotKeyHint: { text: '快捷键屏幕提示', status: OFF, tips: '' },

                        dblclickFullScreen: { text: '双击或中键全屏', status: ON, tips: '' },
                        videoRepeat: { text: '洗脑循环', status: OFF, tips: '' },
                        rememberVideoRepeat: { text: '记忆洗脑循环', status: ON, tips: '' }, //优先级低于videoRepeat
                        keyVideoSpeed: { text: '键盘调节视频速度', status: ON, tips: '' },
                    },
                    btn: '设置'
                },
                //快捷键
                QDs: {}, //未使用
                getQD(key) {
                    return this.QDs[key];
                }, //未使用
                saveQD() {
                    for (let [key, { value, text }] of Object.entries(this.hotKeyMenu)) {
                        this.QDs[key] = {value: value, keyCode: value.charCodeAt(), text: text};
                    }
                }, //未使用
                hotKeyMenu: {  //只是右键菜单的数据,如需改动快捷键改run函数
                    'lightOff': { value: 'A', text: '关灯/开灯', },
                    'webFullscreen': { value: 'W', text: '网页全屏', },
                    'widescreen': { value: 'Q', text: '宽屏模式', },
                    'danmu': { value: 'D', text: '弹幕/关闭弹幕', },
                    'danmuTopBottom': { value: 'T/B', text: '顶部/底部弹幕', },
                    'videoRepeat': { value: 'R', text: '洗脑循环', },
                    'addsubDanmuOpacity': { value: 'Z/C', text: '减增弹幕透明度10%', },
                    'quarterArea': { value: '1', text: '1/4屏', },
                    'halfArea': { value: '2', text: '半屏', },
                    'threeQuarterArea': { value: '3', text: '3/4屏', },
                    'nonOverArea': { value: '4', text: '不重叠', },
                    'fullArea': { value: '5', text: '不限', },
                    'wheelDanmuOpacity': { value: 'Ctrl + 滚轮', text: '增减弹幕透明度5%', },
                    'fastForwardBackward30s': { value: 'Ctrl + ←/→', text: '快进/快退30s', },
                    '1/FPS': { value: 'Shift + ←/→', text: '逐帧操作', },
                    'videoSpeed': { value: 'Ctrl + ↑/↓', text: '调节视频速度', },
                    'dblFullscreen': { value: '双击/中键', text: '双击或中键全屏', },
                },
            };

        function log(e) {console.log(e)}
        function dir(e) {console.dir(e)}
        function waitForNode(nodeSelector, callback, times) {
            if(times < 0) return;
            var node = nodeSelector();
            if (node) {
                callback(node);
            } else {
                times-=1;
                setTimeout(function() { waitForNode(nodeSelector, callback, times); }, 100);
            }
        }
        function waitForTrue(ifTrue, callback) {
            if (ifTrue()) {
                callback();
            } else {
                setTimeout(function() { waitForTrue(ifTrue, callback); }, 100);
            }
        }
        function is_fullscreen() {
            if(bb_type === BILI_2_X){ return player.isFullScreen() }
            else if(bb_type === BILI_3_X){ return $('.bpx-player-container').attr('data-screen') === 'web' || $('.bpx-player-container').attr('data-screen') === 'full' }  //normal wide web full
        }
        function web_fullscreen() { $(bb['webFullScreen']).click() }
        function wide_screen() { $(bb['wideScreen']).click() }
        function is_lightoff() {
            if (bb_type === BILI_2_X) { return !player.getPlayerState().lightOn }
            else if(bb_type === BILI_3_X) { return $(bb['playSettingLightoff']).hasClass('active') }
        }
        function is_danmaku_show(){
            return $(bb['danmakuSwitch']+' '+bb['switchInput']+':last')[0].checked;
        }
        function lightoff() {
            $(bb['playSettingLightoff']).click()
        }
        //关灯按钮样式
        function lightoff_btn_css() {
            var body_brgb = 'rgb(160, 130, 110)';
            var dot_crgb = 'rgb(230, 200, 180)';
            var dot_brgb = 'rgb(50, 50, 50)';
            var dark_rgb = 'rgb(77, 77, 77)';
            if ($('#hhh_lightoff '+bb['switchInput'])[0].checked === false) {  //关灯
                $('#hhh_lightoff '+bb['switchBody']+':first').css('background-color', dark_rgb);
                $('#hhh_lightoff '+bb['switchBody']+':first>'+bb['switchDot']).css('color', dark_rgb);
            }
            else {
                $('#hhh_lightoff '+bb['switchBody']+':first').css('background-color', body_brgb);
                $('#hhh_lightoff '+bb['switchBody']+':first>'+bb['switchDot']).css({'color': dot_crgb, 'background-color': dot_brgb});
            }
        }
        //关灯按钮
        function lightoff_btn() {
            lightoff();
            if(is_lightoff() === $('#hhh_lightoff '+bb['switchInput'])[0].checked) {  //checked==true开灯 false关灯
                $('#hhh_lightoff '+bb['switchInput'])[0].checked = !$('#hhh_lightoff '+bb['switchInput'])[0].checked;
            }
            lightoff_btn_css();
        }

        //模拟B站音量调节
        //2.X版本可以直接调用系统函数window.player.volume(),但不能直接使用H5Player.volume
        //3.X版本去掉了window.player.volume(),但H5Player.volume功能发生变动,基本等价于window.player.volume()
        function volume(v, is_show_hint=true){
            function volume_bar(v){  //未使用
                if(v === undefined) return;
                v = v<0? 0: v>1? 1: v;
                $('.bilibili-player-video-volume-num').text(Math.round(v*100));
                $('.bilibili-player-video-volumebar-wrp .bui-bar.bui-bar-normal')[0].style.transform = `scaleY(${v})`;
                $('.bilibili-player-video-volumebar-wrp .bui-thumb')[0].style.transform = `translateY(${-48*v}px)`;
                v === 0? $('.bilibili-player-video-btn.bilibili-player-video-btn-volume').addClass('video-state-volume-min'): $('.bilibili-player-video-btn.bilibili-player-video-btn-volume').removeClass('video-state-volume-min')
            }
            function volume_hint_bar(v){
                if(v === undefined) return;
                v = v<0? 0: v>1? 1: v;
                var $volumeHintIcon = $(`#hhh_volumeHint ${bb['volumeHintIcon']}`);
                var volumeHintIconClassName = bb['volumeHintIcon'].substr(1);

                if(bb_type === BILI_2_X){
                    if(v <= 0) $volumeHintIcon.attr('class', `${volumeHintIconClassName} video-state-volume-min`);
                    else if(v >= 1) $volumeHintIcon.attr('class', `${volumeHintIconClassName} video-state-volume-max`);
                    else $volumeHintIcon.attr('class', volumeHintIconClassName);
                } else{
                    if(v <= 0){
                        $volumeHintIcon.find('.bpx-common-svg-icon:last').css('display','block');
                        $volumeHintIcon.find('.bpx-common-svg-icon:first').css('display','none');
                    } else{
                        $volumeHintIcon.find('.bpx-common-svg-icon:last').css('display','none');
                        $volumeHintIcon.find('.bpx-common-svg-icon:first').css('display','block');
                    }
                }

                if(v <= 0) showHint(this, '#hhh_volumeHint', '静音');
                else showHint(this, '#hhh_volumeHint', Math.round(v*100)+'%');
            }
            if(v === undefined) return h5Player.volume;
            v = v<0? 0: v>1? 1: v;
            bb_type === BILI_2_X? player.volume(v): h5Player.volume = v;
            if(is_show_hint === true) volume_hint_bar(h5Player.volume);
            return h5Player.volume;
        }

        //显示提示
        function showHint(parent, selector_str, text){
            $(bb['volumeHint']).css('display', 'none');  //隐藏所有提示,避免提示重叠
            $(`${selector_str}>${bb['volumeHintText']}`).text(text);  //百分比显示
            var Hint = $(selector_str);  //显示及渐隐效果(抄bilibili^^)
            clearTimeout(parent.showHintTimer),
                Hint.stop().css("opacity", 1).show(),
                parent.showHintTimer = window.setTimeout((function() {
                Hint.animate({
                    opacity: 0
                }, 300, (function() {
                    $(this).hide()
                }))
            }
            ), 1e3)
        }

        //非全屏滚轮音量调节 0~1 (b站默认滚轮操作某些情况会失效,一并处理全屏情况)
        //两个参数指定屏幕范围(按百分比),第三个参数表示滚动一下增加的音量百分比,参数四表示暂停时是否调解
        function wheel_volumeHint(screenLeft=0.3, screenRight=0.7, delta=3, isPauseVolume){
            //add wheelevent
            $(bb['videoWrap']).off('mousewheel.hhh_volumeHint');
            $(bb['videoWrap']).on('mousewheel.hhh_volumeHint', function(e){
                if(e.ctrlKey || e.altKey || e.shiftKey) return;
                //缺省屏幕百分比参数,默认0.3~0.7
                screenLeft = (screenLeft<0 || screenLeft>1)? 0.3: screenLeft;
                screenRight = (screenRight<0 || screenRight>1)? 0.7: screenRight;
                //缺省音量百分比,默认3
                delta = (delta<1 || delta>100)? 3: delta;

                //非暂停(可选) && 鼠标在屏幕指定位置时处理
                var pauseState = isPauseVolume ||  h5Player.paused === true;
                var Rect = $(bb['videoWrap'])[0].getBoundingClientRect();
                var offsetX = e.originalEvent.x - Rect.x;
                var inLimit = offsetX > Rect.width*screenLeft && offsetX < Rect.width*screenRight;

                if(is_fullscreen() || (pauseState && inLimit)) {
                    //阻止页面滚动 && 阻止冒泡
                    e.preventDefault();
                    e.stopPropagation();
                    var wheelDelta = e.originalEvent.wheelDelta;
                    var v = volume();
                    if(wheelDelta >= 120) {  //向上滚动,减少音量
                        volume((v+(delta/100)).toFixed(3));
                    } else if(wheelDelta <= -120) {  //向下滚动,增大音量
                        volume((v-(delta/100)).toFixed(3));
                    }
                }
            });
        }

        /*
         * 控制进度条
         * .bilibili-player-setting-opacity 透明度
         * .bilibili-player-setting-area 显示区域
         * .bilibili-player-setting-speedplus 弹幕速度 等
         * 利用系统mousedown事件
         * 0 ~ 100
         */
        function set_progress(selector_str, percent){
            var selector = document.querySelector(selector_str);
            var e1 = new MouseEvent('mousedown'); var e2 = new MouseEvent('mouseup');

            function calc_bar_len(percent, bar_width){
                var p = percent - 10;
                p = p<0? 0: p>90? 90: p;
                return p*bar_width/90;  //进度条对应百分比的系统算法
            }

            if(bb_type === BILI_3_X) $('.squirtle-controller-wrap').css({"display":"flex"});
            $(bb['danmakuSettingWrap']).css({"display":"block"});

            var selector_rect = selector.getClientRects();
            var clientX = selector_rect[0].left + calc_bar_len(percent, $(`${selector_str} ${bb['progressWrap']}`).innerWidth());
            e1.initMouseEvent('mousedown',1,1,window,1,0,0,clientX,0,0,0,0,0,0,null);
            e2.initMouseEvent('mouseup'  ,1,1,window,1,0,0,clientX,0,0,0,0,0,0,null);
            selector.dispatchEvent(e1); selector.dispatchEvent(e2);

            $(bb['danmakuSettingWrap']).css({"display":"none"});
            if(bb_type === BILI_3_X) $('.squirtle-controller-wrap').css({"display":"none"});

            $(bb['danmakuSetting']).mouseleave();  //激活设置,记忆进度条位置
            return $(`${selector_str} ${bb['progressVal']}`).text();
        }

        /*
         * 调节透明度
         * 利用系统mousedown事件
         * '正数': right,  '负数': left,  -100 ~ +100
         */
        function adjust_progress(selector_str, inc_percent){
            var curr_percent = Number($(`${selector_str} ${bb['progressVal']}`).text().slice(0,-1));
            return set_progress(selector_str, curr_percent + inc_percent);
        }

        //滚轮调节弹幕透明度(ctrl),参数表示滚动一下增加的透明度百分比
        function wheel_opacity(delta=5){
            $(bb['videoWrap']).off('mousewheel.hhh_opacity');
            $(bb['videoWrap']).on('mousewheel.hhh_opacity', function(e){
                if(e.ctrlKey === true) {
                    //阻止页面滚动 && 阻止冒泡
                    e.preventDefault();
                    e.stopPropagation();
                    //缺省透明度百分比,默认5
                    delta = (delta<1 || delta>100)? 5: delta;
                    var wheelDelta = e.originalEvent.wheelDelta;
                    var opacity;
                    if(wheelDelta >= 120) {  //向上滚动,增大透明度
                        opacity = adjust_progress(bb['settingOpacity'], delta);
                    } else if(wheelDelta <= -120) {  //向下滚动,减少透明度
                        opacity = adjust_progress(bb['settingOpacity'], -delta);
                    }
                    if(opacity !== undefined) showHint(document, '#hhh_opacityHint', '透 '+opacity);
                }
            });
        }

        //添加右键菜单自定义快捷键说明
        function add_custom_hotkey_menu(custom_hotkey) {
            //主class名,去掉"."
            var hotkey_panel_class = bb['hotkeyPanel'].replace(/\./g, ' ').substr(1);

            //生成自定义快捷键说明页面
            (function add_custom_hotkey(){
                $(bb['videoWrap'])[0].addEventListener('DOMNodeInserted', function(e) {
                    if(typeof e.target.className === 'string' && e.target.className.indexOf(`${hotkey_panel_class}-container active`) !== -1) {
                        this.removeEventListener('DOMNodeInserted', arguments.callee);
                        var $hotkey = $(e.target).attr('class', `${hotkey_panel_class}-container`);  //去掉 active 隐藏
                        $hotkey.clone().insertBefore($hotkey).attr({'class': '','id': 'sys_hotkey_panel'}).css('display', 'none');
                        $hotkey.clone().insertBefore($hotkey).attr({'class': '','id': 'hhh_hotkey_panel'}).css('display', 'none');

                        (function set_custom_hotkey($hotkey, hotkey) {
                            var $hotkey_panel = $hotkey.find(`.${hotkey_panel_class}:first`);
                            var $hotkey_item  = $hotkey.find(`.${hotkey_panel_class}-item:first`);
                            $hotkey_panel.empty();
                            for (let [key, { value, text }] of Object.entries(hotkey)) {
                                $hotkey_item.clone().appendTo($hotkey_panel);
                                var $hotkey_key = $hotkey_panel.find(`.${hotkey_panel_class}-key:last`);
                                $hotkey_key.text(value);
                                $hotkey_key.next().text(text);
                            }
                        })($('#hhh_hotkey_panel'), custom_hotkey);
                    }
                });
            })();
            //右键菜单弹出时添加项
            (function add_menu(){
                $('#bilibiliPlayer')[0].addEventListener('DOMSubtreeModified', function(e) {
                    let target = e.target;
                    if(typeof target.className === 'string' && target.className.indexOf(bb['playerContextMenu'].replace(/\./g, ' ').substr(1)) !== -1 &&  target.id === '') {  //还有一个id = __sizzle__
                        this.removeEventListener('DOMSubtreeModified', arguments.callee);

                        var $li_sys_hotkey = $(target).find('a:contains("快捷键说明"):first').parent();  //返回li,XXX: 此时contains("快捷键说明")多于一个
                        var $li_hhh_hotkey = $li_sys_hotkey.clone(false, false).insertAfter($li_sys_hotkey).css('display', '').find('a').text('快捷键说明(bilibili关灯)');  //插入自定义快捷键

                        $li_sys_hotkey.find('a').click(function(){
                            $('#sys_hotkey_panel').clone(true,true).replaceAll($(`.${hotkey_panel_class}-container:last`)).attr({'id': '', 'class': `${hotkey_panel_class}-container`, 'style': ''});
                        });
                        $li_hhh_hotkey.click(function(){
                            $('#hhh_hotkey_panel').clone(true,true).replaceAll($(`.${hotkey_panel_class}-container:last`)).attr({'id': '', 'class': `${hotkey_panel_class}-container`, 'style': ''});
                        });

                        add_menu();
                    }
                });
            })();

            //模拟右键菜单消息,激活菜单DOM
            let evt = new MouseEvent('contextmenu', { clientX:-9999, clientY:-9999 });
            $(bb['videoWrap'])[0].dispatchEvent(evt);

            //模拟点击菜单,激活热键菜单DOM
            if(bb_type === BILI_2_X){
                let evt = new MouseEvent('click',{ bubbles:true });
                $(`${bb['playerContextMenu']} a:contains("快捷键说明")`)[0].dispatchEvent(evt);
                //$(`${bb['playerContextMenu']} a:contains("快捷键说明")`).click();
            }
            else if(bb_type === BILI_3_X) {
                let evt = new MouseEvent('mousedown',{ bubbles:true });
                $(`${bb['playerContextMenu']} li:contains("快捷键说明")`)[0].dispatchEvent(evt);
            }
        }

        //取得版本号
        function get_ver() {
            if(bb_type === BILI_2_X) ver = player.getVersion().version;
            else ver = $('.bpx-common-opacity-60').text().split('-')[0];
        }

        //取得视频FPS(Frames Per Second)
        function get_video_fps() {
            $(bb['videoWrap'])[0].addEventListener('DOMNodeInserted', function(e) {
                //插入info面板时截取fps值
                if($(e.target).hasClass(bb['DOMNodeInsertedVideoInfoShow'].substr(1))) {
                    this.removeEventListener('DOMNodeInserted', arguments.callee);

                    let $video_info_close = $(bb['videoInfoClose']);
                    $video_info_close.click();  //模拟关闭统计信息面板,2.X版本一次即可关掉
                    $video_info_close.click();  //3.X版本执行两次才能关掉面板,保险起见3次
                    $video_info_close.click();  //估计是点击事件开始太快,系统未能处理

                    //text中取得fps值
                    var get_title_text = function(title) { return $(bb['videoInfoContainer']).find(`.info-title:contains("${title}")`).next().text(); }
                    fps = Number(get_title_text('Resolution').match(/\d+\.\d+/)) || Number(get_title_text('FPS')) || 30;
                }
            })

            //模拟右键菜单消息,激活菜单DOM
            let evt = new MouseEvent('contextmenu', { clientX:-9999, clientY:-9999 });
            $(bb['videoContextMenu'])[0].dispatchEvent(evt);

            //模拟点击菜单,激活热键菜单DOM
            if(bb_type === BILI_2_X){
                let evt = new MouseEvent('click',{ bubbles:true });
                $(`${bb['playerContextMenu']} a:contains("视频统计信息")`)[0].dispatchEvent(evt);
            }
            else if(bb_type === BILI_3_X) {
                let evt = new MouseEvent('mousedown',{ bubbles:true });
                $(`${bb['playerContextMenu']} li:contains("视频统计信息")`)[0].dispatchEvent(evt);
            }
        }

        //笨办法,激活系统音量设置,复制volumeHint DOM
        function pick_volume_hint(){
            var original_volume = h5Player.volume;
            //监视提取提示DOM
            $(bb['videoWrap'])[0].addEventListener('DOMNodeInserted', function(e) {
                if($(e.target).hasClass(bb['volumeHint'].substr(1))) {
                    this.removeEventListener('DOMNodeInserted', arguments.callee);

                    volume(original_volume);  //模拟鼠标拖动无法按1%精确控制音量,系统自身限制或者说bug

                    //添加 volumeHint wordsHint opacityHint DOM
                    $(bb['volumeHint']).clone().appendTo(bb['videoWrap']).attr('id','hhh_volumeHint');

                    $('#hhh_volumeHint').clone().appendTo(bb['videoWrap']).attr('id','hhh_wordsHint').css({'opacity': 0, 'display': 'none'}).find(bb['volumeHintIcon']).remove();
                    $('#hhh_wordsHint').clone().appendTo(bb['videoWrap']).attr('id','hhh_opacityHint');

                    if(bb_type === BILI_2_X){
                        $('#hhh_wordsHint').css({'width':'auto','margin-left':'0px','padding-left':'8px','padding-right':'15px','transform':'translate(-50%)'})
                                           .find(bb['volumeHintText']).css({'width': 'auto', 'padding-left': '10px'});
                        $('#hhh_opacityHint').find(bb['volumeHintText']).css({'padding-right': '6px'});
                    }
                    else{
                        $('#hhh_wordsHint, #hhh_opacityHint').find(bb['volumeHintText']).css({'padding': '0 10px'});
                    }

                    //隐藏提示
                    $(bb['volumeHint']).css({"visibility":"hidden"});
                    waitForTrue(()=> $(bb['volumeHint']).css('display') !== 'none', () => {
                        $(bb['volumeHint']).css('display', 'none').css('opacity', '0');
                        waitForTrue(()=> $(bb['volumeHint']).css('display') === 'none', () => {
                            $(bb['volumeHint']).css('visibility', 'visible');
                        });
                    });
                }
            });
            //激活系统音量设置,以复制volumeHint DOM
            let evt = new KeyboardEvent('keydown', { keyCode:keycode['up'] });
            window.dispatchEvent(evt);
        }

        //快进时显示醒目进度条
        function dynamicProgress(dynamicHeight, staticHeight){
            if(bb_type === BILI_2_X) {
                if ($('.bilibili-player-area').hasClass('progress-shadow-show') === true) {
                    $('.bilibili-player-video-progress-shadow .bui-track-video-progress').css('cssText', `height:${dynamicHeight}px !important`);
                    clearTimeout(document.showVideoProgress);
                    document.showVideoProgress = window.setTimeout((function() { $('.bilibili-player-video-progress-shadow .bui-track-video-progress').css('cssText', `height:${staticHeight}px !important`); }), 2000);
                }
            } else {
                if($('.bpx-player-container').hasClass('bpx-state-no-cursor') === true) {
                    $('.squirtle-progress-buffer').css({'height': `${dynamicHeight/2}px`});
                    $('.squirtle-progress-timeline').css({'height': `${dynamicHeight/2}px`});
                    $('.squirtle-progress-totalline').css({'height': `${dynamicHeight/2}px`});
                    clearTimeout(document.showVideoProgress);
                    document.showVideoProgress = window.setTimeout((function() {
                        $('.squirtle-progress-buffer').css({'height': `${staticHeight}px`});
                        $('.squirtle-progress-timeline').css({'height': `${staticHeight}px`});
                        $('.squirtle-progress-totalline').css({'height': `${staticHeight}px`});
                    }), 2000);
                }
            }
        }

        //视频速度
        function video_select_speed(level) {
            let data_x = ['0.5x', '0.75x', '1.0x', '1.25x', '1.5x', '2.0x'];
            let curr_x = $(bb['videoSpeedActive']).text();
            let x = data_x[data_x.indexOf(curr_x) + level];
            if(x !== undefined) $(`${bb["videoSpeed"]}:contains("${x}")`).click();
            showHint(parent, '#hhh_wordsHint', $(bb['videoSpeedName']).text());
        }

        //主程序
        function run(){

            //防止重复加载
            if ($('#hhh_lightoff').length === 1) return;

            //bpx test
            function bpx_test(){
                log('-------------------');
                log('ver: '+ver);
                log('fps: '+fps);
                log('------h5Player-----');
                dir(h5Player);
                log('currentTime  : '+h5Player.currentTime);
                log('duration     : '+h5Player.duration);
                log('playbackRate : '+h5Player.playbackRate);
                log('volume       : '+h5Player.volume);
                log('paused       : '+h5Player.paused);
                log('pause()      : '+'h5Player.pause()');
                log('videoHeight  : '+h5Player.videoHeight);
                log('videoWidth   : '+h5Player.videoWidth);
                log('-------------------');
            }

            /*-----------------------------------
             *初始化等
             *----------------------------------*/
            //保存设置信息 && 快捷键信息
            config.saveCheckboxSetting();

            //取得h5 video
            h5Player = $(`#bilibili-player ${bb['video']}>video`)[0];

            //插入关灯按钮
            $(`${bb['danmakuSwitch']}:first`).clone().prependTo(`${bb['danmakuRoot']}:first`)[0].id = 'hhh_lightoff';
            $(`#hhh_lightoff ${bb['switchDot']}`)[0].innerHTML = '灯';

            //初始化关灯按钮
            lightoff_btn_css();

            //激活系统弹幕设置,以此调用系统网页全屏等
            $(bb['danmakuSetting']).mouseenter().mouseleave();

            //激活系统播放设置,以此调用系统关灯等
            //去掉mouseleave(),否则如果太快执行mouseleave()无法激活关灯class,应该是mouseenter()未执行完就被mouseleave打断了
            $(`${bb['playVideo']}${bb['playSetting']}`).mouseenter();

            //避免显示设置页面
            waitForNode(() => document.querySelector(bb['playSettingWrap']), (node) => {
                $(node).css({"visibility":"hidden"});  //visible
            })

            //解决因为激活关灯class,导致全屏时滚轮操作(系统自带)无法调节音量的问题
            waitForTrue(()=> $(bb['playSettingWrap']).css('display') === 'block', () => {
                $(bb['playSettingWrap']).css('display', 'none').css('visibility', 'visible');
            });

            //取得视频fps
            get_video_fps();

            //取得版本号
            get_ver();

            //激活系统提示添加音量等自定义提示DOM
            pick_volume_hint();

            //添加自定义快捷键说明到右键菜单
            //非必要功能,BILI_3_X暂无
            if(bb_type === BILI_2_X) add_custom_hotkey_menu(config.hotKeyMenu);

            /*-----------------------------------
             *事件等
             *----------------------------------*/
            //点击关灯
            $(`#hhh_lightoff ${bb['switchInput']}:first`).click(function(){ lightoff_btn() });

            //双击或中键全屏
            if(config.getCheckboxSetting('dblclickFullScreen') === ON) {
                $(bb['playTipWrap']).dblclick(function(){ web_fullscreen() });
                $(bb['playTipWrap']).mousedown(function(e){
                    if(e.button === 1) {
                        e.preventDefault(); e.stopPropagation();
                        web_fullscreen();
                    }
                });
            }
            
            //判断当前鼠标点击焦点
            $('#app').click(function(e){
                if(e.originalEvent === undefined) return;
                let path = e.originalEvent.path;
                is_in_biliplayer = false;
                $(path).each(function(){
                    if(this.id === 'bilibiliPlayer') { is_in_biliplayer = true; return; }
                })
            })

            //非全屏滚轮音量调节
            //两个参数指定屏幕范围(按百分比),第三个参数表示滚动一下增加的音量百分比,参数四表示暂停时是否调节
            if(config.getCheckboxSetting('volumeControlWhenNonFullScreen') === ON) wheel_volumeHint(0.30, 0.70, 3, config.getCheckboxSetting('volumeControlWhenPause'));

            //因为遮挡弹幕,去掉全屏时鼠标悬停时产生的顶端mask
            if(config.getCheckboxSetting('removeVideoTopMask') === ON) $(bb['videoTopMask']).removeClass();

            //播放关灯,暂停开灯
            var video = $('video')[0];
            video.addEventListener('play', () => config.getCheckboxSetting('lightOffWhenPlaying') === ON && !is_lightoff() && lightoff_btn());
            video.addEventListener('pause', () => config.getCheckboxSetting('lightOnWhenPause') === ON && is_lightoff() && lightoff_btn());

            function set_value(key, value){ localStorage.setItem(key, value) }
            function get_value(key, default_value){ return localStorage.getItem(key) !== null? localStorage.getItem(key): default_value }

            //记忆洗脑循环
            if(config.getCheckboxSetting('rememberVideoRepeat') === ON && config.getCheckboxSetting('videoRepeat') !== ON) {
                $(bb['playSettingRepeatInput'])[0].checked = JSON.parse(get_value('hhh-rememberVideoRepeat', $(bb['playSettingRepeatInput'])[0].checked));  //localStorage只能保存字符串
                $(bb['playSettingRepeatInput']).click(function() {
                    set_value('hhh-rememberVideoRepeat', $(bb['playSettingRepeatInput'])[0].checked);
                });
            };

            //自动运行
            if(config.getCheckboxSetting('autoPlay') === ON && $(bb['playSettingAutoplay'])[0].checked === false) $(bb['playSettingAutoplay']).click();//开启自动播放
            if(config.getCheckboxSetting('videoRepeat') === ON) $(bb['playSettingRepeatInput']).click();  //开启洗脑循环
            if(config.getCheckboxSetting('lightOff') === ON) lightoff_btn();  //自动关灯

            //滚轮调节弹幕透明度(ctrl),参数表示滚动一下增加的透明度百分比,默认5
            if(config.getCheckboxSetting('danmuOpacityControl') === ON) wheel_opacity(5);

            //键盘关灯等
            var parent = document;
            var is_show_hint = config.getCheckboxSetting('hotKeyHint');
            $(document).off('keydown.hhh_lightoff');
            $(document).on('keydown.hhh_lightoff',function(e){
                if(e.shiftKey && (e.keyCode === keycode['left'] || e.keyCode === keycode['right'])) {  //逐帧 shift+ left/right
                    h5Player.pause();
                    h5Player.currentTime = e.keyCode === keycode['left']? h5Player.currentTime - 1/fps: h5Player.currentTime + 1/fps;
                } else if(e.ctrlKey && (e.keyCode === keycode['left'] || e.keyCode === keycode['right'])) {  //+ -30s ctrl+ left/right
                    h5Player.currentTime = e.keyCode === keycode['left']? h5Player.currentTime - 30: h5Player.currentTime + 30;
                    dynamicProgress(16, 2)
                } else if(e.ctrlKey && (e.keyCode === keycode['up'] || e.keyCode === keycode['down'])) {  //+ -播放速度
                    if(config.getCheckboxSetting('keyVideoSpeed') === OFF) return;
                    window.setTimeout((function() {  //长按时保持DOM更新
                        e.keyCode === keycode['up']? video_select_speed(1): video_select_speed(-1);
                    }),10);
                }

                //以下不使用功能键
                if(e.ctrlKey || e.altKey || e.shiftKey) return;

                if(e.keyCode === 'A'.charCodeAt()){  //开关灯
                    is_show_hint && (is_lightoff() ? showHint(parent, '#hhh_wordsHint', '开灯') : showHint(parent, '#hhh_wordsHint', '关灯'));
                    lightoff_btn();
                } else if(e.keyCode === 'W'.charCodeAt()) {  //网页全屏
                    web_fullscreen();
                } else if(e.keyCode === 'Q'.charCodeAt()) {  //宽屏模式
                    is_fullscreen() ? web_fullscreen() : wide_screen();
                } else if(e.keyCode === 'D'.charCodeAt()) {  //开关弹幕
                    $(bb['danmakuSwitch']+' '+bb['switchInput']+':last').click();
                    is_show_hint && (is_danmaku_show() === true ? showHint(parent, '#hhh_wordsHint', '开弹幕') : showHint(parent, '#hhh_wordsHint', '关弹幕'));
                } else if(e.keyCode === 'T'.charCodeAt()) {  //开关顶部弹幕
                    $(bb['danmukuTopClose']).length === 0 ? showHint(parent, '#hhh_wordsHint', '关闭顶部弹幕') : showHint(parent, '#hhh_wordsHint', '打开顶部弹幕');
                    $(bb['danmukuTop']).click();
                } else if(e.keyCode === 'B'.charCodeAt()) {  //开关底部弹幕
                    $(bb['danmukuBottomClose']).length === 0 ? showHint(parent, '#hhh_wordsHint', '关闭底部弹幕') : showHint(parent, '#hhh_wordsHint', '打开底部弹幕');
                    $(bb['danmukuBottom']).click();
                } else if(e.keyCode === 'R'.charCodeAt()) {  //开关洗脑循环
                    $(bb['playSettingRepeatInput']).click();
                } else if(e.keyCode === 'Z'.charCodeAt() || e.keyCode === 'C'.charCodeAt()) {  //+ -弹幕透明度
                    let inc_percent = e.keyCode === 'Z'.charCodeAt()? -10: 10;
                    window.setTimeout((function() {  //长按时保持DOM更新
                        let opacity = adjust_progress(bb['settingOpacity'], inc_percent);
                        showHint(parent, '#hhh_opacityHint', '透 ' + opacity);
                    }),10);
                } else if(e.keyCode >= '1'.charCodeAt() && e.keyCode <= '5'.charCodeAt()) {  //弹幕显示区域
                    var area_text = {0:'1/4屏',25:'半屏',50:'3/4屏',75:'不重叠',100:'不限'};
                    var percent = (e.keyCode - '1'.charCodeAt()) * 25;  //((0~4)*25)%
                    set_progress(bb['settingArea'], percent);
                    showHint(parent, '#hhh_wordsHint', area_text[percent]);
                } else if(e.keyCode === keycode['left'] || e.keyCode === keycode['right']){  //快进时显示醒目进度条
                    dynamicProgress(16, 2);
                } else if(e.keyCode === keycode['up'] || e.keyCode === keycode['down']) { //隐藏所有提示,避免提示重叠
                    $(bb['volumeHint']).css('display', 'none');
                } else if(e.keyCode === keycode['space']) {  //空格键2.X版本不合理,改成和3.X版本一样
                    if(bb_type===BILI_2_X){
                        if(is_in_biliplayer === false) e.stopPropagation();
                    }
                } else if(e.keyCode === 'X'.charCodeAt()) {  //TEST 显示、隐藏video-control
                    //if($(bb['playArea']).hasClass('video-control-show') === true) $(bb['videoWrap']).mouseout();
                    //else $(bb['videoWrap']).mousemove();
                } else if(e.keyCode === 'V'.charCodeAt()) {  //TEST
                    //if(bb_type===BILI_3_X) $('.bpx-player-dm-setting-wrap').css('display','block');
                    //$('.bilibili-player-video-danmaku-setting-wrap').css('display','block');
                    //$('.bilibili-player-area.video-state-blackside').attr('class', 'bilibili-player-area video-state-blackside video-state-pause video-control-show');
                }else{
                    //console.dir(e);
                }
            });


            //TEST
            bpx_test();

        }

        //初始化
        function init() {
            console.clear();
            new MutationObserver((mutations, observer) => {
                mutations.forEach(mutation => {
                    const target = mutation.target;
                    const stage = mutation.previousSibling && target.getAttribute('stage');
                    if(stage === '1' && $('.bilibili-player-video-wrap').length === 1){  //2.X
                        log("2.X加载完毕");
                        bb_config.set_bb(BILI_2_X);
                        run();
                    } else if(typeof target.className === 'string' && target.className === 'squirtle-fullscreen-wrap squirtle-block-wrap' && $('.bpx-player-video-area').length === 1) {  //3.X  pbp-tip
                        log("3.X加载完毕");
                        bb_config.set_bb(BILI_3_X);
                        run();
                    }
                });
            }).observe(document.body, {
                childList: true,
                subtree: true,
            });
        }

        init();
    }
}

hhh_lightoff_main.init();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址