斗鱼(douyu)快捷操作

滚轮音量调节;鼠标功能键全屏;快捷键:开关弹幕D、网页全屏W、隐藏有边框Q、↑↓键音量调节等

当前为 2025-07-20 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         斗鱼(douyu)快捷操作
// @namespace    hhh2000
// @version      0.4.4
// @description  滚轮音量调节;鼠标功能键全屏;快捷键:开关弹幕D、网页全屏W、隐藏有边框Q、↑↓键音量调节等
// @author       hhh2000
// @match        http*://www.douyu.com/*dyshid*
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js
// @run-at       document-end
// @grant        none
// @compatible   chrome
/* globals jQuery, $, waitForkeyElements */
/* eslint-disable no-multi-spaces, dot-notation */
/* eslint no-eval:0 */
// ==/UserScript==

'use strict';
(function() {
    let log = console.log
    let err = console.error

    // log = ()=>{}

    log(location.href)

    // const key = 'hasRunOnce_' + location.hostname
    // if (!GM_getValue(key, false)) {
    //     GM_setValue(key, true)
    //     // ✅ 这里写你只想运行一次的逻辑
    //     console.log('✅ 脚本第一次运行')
    // } else {
    //     console.log('⏭️ 脚本已运行过,跳过')
    //     return
    // }


    function getkeyCode(k) {
        var keyCodes = {
            300: '滚↑轮',
            301: '滚↓轮',
            302: '鼠标左键',
            303: '鼠标右键',
            304: '鼠标中键',
            305: '鼠标左前侧键',
            306: '鼠标左后侧键',
            307: '鼠标右前侧键',
            308: '鼠标右后侧键',
            309: '鼠标中前侧键',
            310: '鼠标中后侧键',

            0: "",
            3: "break",
            8: "Backspace",
            9: "Tab",
            12: "Clear",
            13: "Enter",
            16: "Shift",
            17: "Ctrl",
            18: "Alt",
            19: "PauseBreak",
            20: "CapsLock",
            27: "Escape",
            32: "Space",
            33: "PageUp",
            34: "PageDown",
            35: "End",
            36: "Home",
            37: "←",   //LeftArrow ↑ ↓ ← →
            38: "↑",   //UpArrow
            39: "→",   //RightArrow
            40: "↓",   //DownArrow
            45: "Insert",
            46: "Delete",
            48: "0",
            49: "1",
            50: "2",
            51: "3",
            52: "4",
            53: "5",
            54: "6",
            55: "7",
            56: "8",
            57: "9",
            65: "A",
            66: "B",
            67: "C",
            68: "D",
            69: "E",
            70: "F",
            71: "G",
            72: "H",
            73: "I",
            74: "J",
            75: "K",
            76: "L",
            77: "M",
            78: "N",
            79: "O",
            80: "P",
            81: "Q",
            82: "R",
            83: "S",
            84: "T",
            85: "U",
            86: "V",
            87: "W",
            88: "X",
            89: "Y",
            90: "Z",
            93: "ContextMenu",
            96: "NumPad0",
            97: "NumPad1",
            98: "NumPad2",
            99: "NumPad3",
            100: "NumPad4",
            101: "NumPad5",
            102: "NumPad6",
            103: "NumPad7",
            104: "NumPad8",
            105: "NumPad9",
            106: "NumPad_Multiply",
            107: "NumPad_Add",
            108: "NumPad_Separator",
            109: "NumPad_Subtract",
            110: "NumPad_Decimal",
            111: "NumPad_Divide",
            112: "F1",
            113: "F2",
            114: "F3",
            115: "F4",
            116: "F5",
            117: "F6",
            118: "F7",
            119: "F8",
            120: "F9",
            121: "F10",
            122: "F11",
            123: "F12",
            124: "F13",
            125: "F14",
            126: "F15",
            127: "F16",
            128: "F17",
            129: "F18",
            130: "F19",
            144: "NumLock",
            145: "ScrollLock",
            166: "BrowserBack",
            167: "BrowserForward",
            170: "BrowserSearch",
            172: "BrowserHome",
            173: "AudioVolumeMute",
            174: "AudioVolumeDown",
            175: "AudioVolumeUp",
            176: "MediaTrackNext",
            177: "MediaTrackPrevious",
            178: "MediaStop",
            179: "MediaPlayPause",
            180: "LaunchMail",
            181: "LaunchMediaPlayer",
            183: "LaunchApp2",
            186: ";",
            187: "=",
            188: ",",
            189: "-",
            190: ".",
            191: "/",
            192: "`",
            193: "ABNT_C1",
            194: "ABNT_C2",
            219: "[",
            220: "\\",
            221: "]",
            222: "'",
            223: "OEM_8",
            226: "OEM_102",
            229: "KeyInComposition",
        };
        return keyCodes[k];
    }
    let keycode = {
        'Enter': 13,
        'Ctrl': 17,
        'Esc': 27,
        'left': 37,
        'right': 39,
        'up': 38,
        'down': 40,
        'space': 32,
        'NumPad0': 96,
        'NumPad_Decimal': 110,
    }

    function set_progress(selector, curr_percent, inc_percent, limit_begin, limit_end){

        function calc_bar_offset(percent, bar_length, limit_begin, limit_end){
            let p = Math.max(Math.min(+percent, limit_end), limit_begin);
            let limit = limit_end - limit_begin;
            let bar_offset = (p-limit_begin) * bar_length / limit;
            //log(p, limit, bar_length, 128/100*p, bar_offset)
            return bar_offset;  //百分比对应进度条位置
        }

       // $('.controlbar-f41e38').removeClass('hide-6cf943')

        let e1 = new MouseEvent('mousedown'), e2 = new MouseEvent('mouseup')
        // log(selector)
        let rect = selector.getBoundingClientRect()

        let padding_top = +$(selector).css('padding-top').match(/\d+/)?.[0]
        let padding_bottom = +$(selector).css('padding-bottom').match(/\d+/)?.[0]
        let bar_offset = calc_bar_offset(curr_percent-inc_percent, (rect.height-padding_top-padding_bottom), limit_begin, limit_end)
        let clientY = rect.bottom - padding_bottom - bar_offset + 1

        let o1 = calc_bar_offset(curr_percent, (rect.height-padding_top-padding_bottom), limit_begin, limit_end)
        let o2 = calc_bar_offset(curr_percent+inc_percent, (rect.height-padding_top-padding_bottom), limit_begin, limit_end)
        let o3 = calc_bar_offset(curr_percent-inc_percent, (rect.height-padding_top-padding_bottom), limit_begin, limit_end)
        // log(`当前音量:${curr_percent}   加音量${curr_percent+inc_percent}   减音量${curr_percent-inc_percent}`)
        // log(`当前距离:${o1}   加距离${o2}   减距离${o3}`)

        let percent = Math.max(Math.min(curr_percent - inc_percent, limit_end), limit_begin)
        let step = (rect.height-padding_top-padding_bottom)/(limit_end - limit_begin)/2
        // 动态调整
        for(let i=0; i<Math.abs(inc_percent*10); ++i){
            //log(`------音量:${i}------`)
            //log(`原音量:${curr_percent}/${parseInt($('.tips-3df825').text().match(/\d+/))}`)
            e1.initMouseEvent('mousedown',1,1,window,1,0,0,0,clientY,0,0,0,0,0,null);
            e2.initMouseEvent('mouseup'  ,1,1,window,1,0,0,0,clientY,0,0,0,0,0,null);
            selector.dispatchEvent(e1); selector.dispatchEvent(e2);

            let next_percent = parseInt($('.tips-3df825').text().match(/\d+/))
            //log(`新音量:${percent}/${next_percent}/${$('.tips-3df825').text().match(/\d+/)}`)

            //log(`原音量:${percent} 新音量:${next_percent} 增减音量:${inc_percent} clientY: ${clientY} step: ${step}`)
            if(percent == next_percent) break
            else if(inc_percent > 0 && percent < next_percent) clientY += step
            else if(inc_percent < 0 && percent > next_percent) clientY -= step
            else break

        }
    }

    function adjust_progress(selector_str, inc_percent, range_bengin, range_end){
        //new
        // let container   = document.querySelector('.player__jsy1T');
        // container.classList.add('in__HeN9a', 'player-is-hover'); // 方便加样式
        // let volumeBar   = document.querySelector('.VolumeBar-164ea4');
        // volumeBar.classList.add('active-a5d033'); // 方便加样式

        // log(window.location.href)
        if(window.location.href.includes('www.douyu.com/beta/') === true){
            // log('===new===');
            function set_percent(percent) {
                let back = $('.volume-07c230 .back-85f312')[0]
                let rect = back.getBoundingClientRect()
                let clientY = rect.bottom - percent
                const e1 = new MouseEvent('mousedown', {
                    bubbles: true,
                    cancelable: true,
                    view: window,
                    detail: 0,
                    screenX: 0,
                    screenY: 0,
                    clientX: 0,
                    clientY: clientY,
                    ctrlKey: false,
                    altKey: false,
                    shiftKey: false,
                    metaKey: false,
                    button: 0,
                    relatedTarget: null
                })
                const e2 = new MouseEvent('mouseup', {
                    bubbles: true,
                    cancelable: true,
                    view: window,
                    detail: 0,
                    screenX: 0,
                    screenY: 0,
                    clientX: 0,
                    clientY: clientY,
                    ctrlKey: false,
                    altKey: false,
                    shiftKey: false,
                    metaKey: false,
                    button: 0,
                    relatedTarget: null
                })
                back?.dispatchEvent(e1); back?.dispatchEvent(e2);
            }

            const $box  = $('.volume-07c230');
            const $front = $box?.find('.front-99e2aa');
            const $point = $box?.find('.point-6ef744');
            const $label = $box?.find('.tips2-9bb064');
            const $tip   = $box?.find('.tips-d0d0f1');
            if ($front.length <= 0) return;

            let volumeBar = document.querySelector('.VolumeBar-164ea4');
            let e1 = new MouseEvent('mouseover'), e2 = new MouseEvent('mouseout')
            e1.initMouseEvent('mouseover',1,1,window,1,0,0,0,0,0,0,0,0,0,null)
            volumeBar?.dispatchEvent(e1)

            const curr_percent = parseInt($tip.text().match(/\d+/))  //tips-d0d0f1 - tips2-9bb064
            set_percent(curr_percent - parseInt(inc_percent))

            $tip.css({ visibility: 'visible', opacity: 1 })

            let t = $box.data('hhh_timeout')
            clearTimeout(t)
            t = setTimeout(()=>{
                $tip.css({ visibility: 'hidden', opacity: 0 })
                e2.initMouseEvent('mouseout',1,1,window,1,0,0,0,0,0,0,0,0,0,null)
                volumeBar?.dispatchEvent(e2)
            }, 1000)
            $box.data('hhh_timeout', t)

            // let timer

            // $tip[0].style.visibility = 'visible';
            // $tip[0].style.opacity = '1';
            // clearTimeout(timer);
            // timer = setTimeout(() => {
            //     $tip[0].style.transition = 'opacity .3s';
            //     $tip[0].style.opacity = '0';
            //     setTimeout(() => $tip[0].style.visibility = 'hidden', 300);
            //     volumeBar.classList.remove('active-a5d033')
            // }, 1000)

//             const MAX = range_end - range_bengin           // 滑条最大 px 高度
//             let vol = 20

//             /* 更新 UI 并显示提示 */
//             const setVol = v => {

//                 let vol = Math.max(0, Math.min(100, v));
//                 const h = (vol / 100) * MAX;
//                 front.style.height = `${h}px`;
//                 point.style.bottom = `${h + 7}px`;
//                 label.textContent = `音量${vol}%`;
//                 //
//                 let rect = point.getBoundingClientRect()
//                 let clientY = rect.bottom
//                 const e1 = new MouseEvent('mousedown', {
//                     bubbles: true,
//                     cancelable: true,
//                     view: window,
//                     detail: 0,
//                     screenX: 0,
//                     screenY: 0,
//                     clientX: 0,
//                     clientY: clientY,
//                     ctrlKey: false,
//                     altKey: false,
//                     shiftKey: false,
//                     metaKey: false,
//                     button: 0,
//                     relatedTarget: null
//                 })
//                 const e2 = new MouseEvent('mouseup', {
//                     bubbles: true,
//                     cancelable: true,
//                     view: window,
//                     detail: 0,
//                     screenX: 0,
//                     screenY: 0,
//                     clientX: 0,
//                     clientY: clientY,
//                     ctrlKey: false,
//                     altKey: false,
//                     shiftKey: false,
//                     metaKey: false,
//                     button: 0,
//                     relatedTarget: null
//                 })
//                 // let e1 = new MouseEvent('mousedown'), e2 = new MouseEvent('mouseup')
//                 // e1.initMouseEvent('mousedown',1,1,0,window,0,0,0,clientY,0,0,0,0,0,null);
//                 // e2.initMouseEvent('mouseup'  ,1,1,0,window,0,0,0,clientY,0,0,0,0,0,null);
//                 point?.dispatchEvent(e1); point?.dispatchEvent(e2);
//                 log('2',v, vol, parseInt($('.tips-d0d0f1').text().match(/\d+/)), clientY)

//                 $tip[0].style.visibility = 'visible';
//                 $tip[0].style.opacity = '1';
//                 clearTimeout(timer);
//                 timer = setTimeout(() => {
//                     tip.style.transition = 'opacity .3s';
//                     tip.style.opacity = '0';
//                     setTimeout(() => tip.style.visibility = 'hidden', 300);
//                     volumeBar.classList.remove('active-a5d033')
//                 }, 1000)
//             }

//             const curr_percent = parseInt($('.tips-d0d0f1').text().match(/\d+/))  //tips-d0d0f1 - tips2-9bb064
//             log(inc_percent, curr_percent)
//             setVol(curr_percent - (inc_percent < 0 ? -5 : 5))

            /* 绑定滚轮 */
            // document.querySelector('#__h5player').addEventListener('wheel', e => {
            //     e.preventDefault();
            //     // setVol(vol + (e.deltaY < 0 ? 5 : -5));
            //     log(e, inc_percent)
            //     setVol(vol + (e.deltaY < 0 ? 1 : -1));
            // });
        }else{  //old
            let e1 = new MouseEvent('mouseover'), e2 = new MouseEvent('mouseout')
            e1.initMouseEvent('mouseover',1,1,window,1,0,0,0,0,0,0,0,0,0,null)
            $('.volume-8e2726')?.[0]?.dispatchEvent(e1)
            $('.volume-silent-3eb726')?.[0]?.dispatchEvent(e1)
            //$('.VolumeBar-9010af').css('visibility', 'hidden')

            let selector = $(selector_str)[0] //VolumeBar-164ea4
            const curr_percent = parseInt($('.tips-3df825').text().match(/\d+/))  //tips-d0d0f1

            set_progress(selector, curr_percent, parseInt(inc_percent), 0, 100)
            $('.tips-3df825').css({'visibility': 'visible'})

            // $('.VolumeBar-9010af').css('visibility', 'visible')
            let t = $('.volume-bar-06542d').data('hhh_timeout')
            clearTimeout(t)
            t = setTimeout(()=>{
                e2.initMouseEvent('mouseout',1,1,window,1,0,0,0,0,0,0,0,0,0,null)
                $('.volume-8e2726')?.[0]?.dispatchEvent(e2)
                $('.volume-silent-3eb726')?.[0]?.dispatchEvent(e2)
            }, 1000)
            $('.volume-bar-06542d').data('hhh_timeout', t)
        }
    }

    //显示hint XXX
    function cut_InfoDisappear(){
        if($('.kui-message-information-item').css('animation-name') === 'InfoDisappear'){
            $('.kui-message-information-item').css('animation-name', 'InfoDisappear1')
        }else{
            $('.kui-message-information-item').css('animation-name', 'InfoDisappear')
        }
    }


    function waitForTrue(ifTrue, callback, time=100) {
        // log($('#player-control-video').length)
        // log(document.querySelectorAll('#__h5player').length)
        //log(ifTrue())
        if(--time < 0) {err('waitForTrue 超时 '+ifTrue); return false;}
        const fn = waitForTrue;
        //let fn = arguments.callee;
        if (ifTrue()) {
            callback(); return true;
        } else {
            setTimeout(function() { fn(ifTrue, callback, time); }, 50);
        }
    }

    //滚轮音量调节;鼠标功能键全屏;快捷键:开关弹幕D、网页全屏W、隐藏有边框Q、↑↓键音量调节
    function run(){


        log('douyu快捷操作加载完毕')

        $('.ActBase').hide()

        //非全屏滚轮音量调节
        $('#__h5player').off('wheel.hhh_douyu').on('wheel.hhh_douyu',function(e){
            let delta = e.originalEvent.wheelDelta
            // log(delta, e)
            if(delta >= 0){  //up
                adjust_progress('.volume-bar-06542d', -1, 0, 100)  //volume-bar-06542d
            }else{
                adjust_progress('.volume-bar-06542d',  1, 0, 100)
            }
            e.preventDefault();
            return false
        })

        //功能键全屏
        $('#__h5player').off('mousedown.hhh_douyu').on('mousedown.hhh_douyu',function(e){
            if(e.buttons > 2){
                $('.wfs-2a8e83, .wfs-exit-180268').not('.removed-9d4c42').click()
                return false
            }
        })

        //快捷键
        $('body').off('keydown.hhh_douyu').on('keydown.hhh_douyu',function(e){
            // log(e.keyCode, e)
            if(e.keyCode === keycode['up'] || e.keyCode === keycode['down']){  //↑↓音量
                if(e.keyCode === keycode['up']){
                    adjust_progress('.volume-bar-06542d', -5, 0, 100)
                }else{
                    adjust_progress('.volume-bar-06542d',  5, 0, 100)
                }
                //$('.kui-message-information-text font').text($('#volume-tip').text())  //set hint text
                //cut_InfoDisappear()
                return false
            }else if(e.keyCode === keycode['NumPad_Decimal'] || e.keyCode === keycode['NumPad0']){  //NumPad_Decimal numpad1 音量
                // log(e.target.className, e.target.nodeName, $(e.target).attr('type'))
                // log(e.originalEvent.path[0].nodeName)
                // log($(e.originalEvent.path[0]).attr('type'))
                let classnames = /editor/
                let nodenames = ['INPUT', 'TEXTAREA']
                let target_classnames = e.target.className
                let target_nodename = e.target.nodeName
                if(nodenames.includes(target_nodename) === false && target_classnames.match(classnames) === null){  //排除发送弹幕等情况
                    if(e.keyCode === keycode['NumPad_Decimal']){
                        adjust_progress('.volume-bar-06542d', -1, 0, 100)
                    }else{
                        adjust_progress('.volume-bar-06542d',  1, 0, 100)
                    }
                    return false
                }
            }else if(e.keyCode === 'W'.charCodeAt()){  //W 网页全屏
                $('.wfs-2a8e83, .wfs-exit-180268').not('.removed-9d4c42').click()
            }else if(e.keyCode === 'F'.charCodeAt()){  //W 全屏
                $('.fs-781153, .fs-exit-b6e6a7').not('.removed-9d4c42').click()
            }else if(e.keyCode === 'Q'.charCodeAt()){  //W 隐藏右边框
                $('.layout-Player-asidetoggleButton').click()
            }else if(e.keyCode === 'D'.charCodeAt()){  //D 开关弹幕
                $('.showdanmu-42b0ac, .hidedanmu-5d54e2').not('.removed-9d4c42').click()
                $('.showdanmuWrap-9c22cd>.icon-c8be96').click()
            }
        })
    }

    waitForTrue(()=>$('#player-control-video').length>0, ()=>{
        run()
    })

})();