bilibili便捷工具

为b站新增鼠标滑过快速获取未读消息、直播页面仿视频快捷键等等便捷操作

目前为 2023-08-29 提交的版本。查看 最新版本

// ==UserScript==
// @license     AGPL License
// @name        bilibili便捷工具
// @namespace   Violentmonkey Scripts
// @match       https://www.bilibili.com/*
// @match       https://message.bilibili.com/*
// @match       https://t.bilibili.com/*
// @match       https://search.bilibili.com/*
// @match       https://live.bilibili.com/*
// @match       https://space.bilibili.com/*
// @grant       GM_xmlhttpRequest
// @run-at      document-start
// @noframes
// @version     1.6
// @author      n1nja88888
// @description 为b站新增鼠标滑过快速获取未读消息、直播页面仿视频快捷键等等便捷操作
// ==/UserScript==
'use strict'
console.log('n1nja88888 creates this world!')
// 覆盖添加监听器函数
!(function() {
    Element.prototype._addEventListener = Element.prototype.addEventListener
    Element.prototype._removeEventListener = Element.prototype.removeEventListener

    Element.prototype.addEventListener = function(type, listener, useCapture = false) {
        this._addEventListener(type, listener, useCapture)

        if (!this.eventListenerList) this.eventListenerList = {}
        if (!this.eventListenerList[type]) this.eventListenerList[type] = []

        this.eventListenerList[type].push({ type, listener, useCapture })
    }
    Element.prototype.removeEventListener = function(type, listener, useCapture = false) {
        this._removeEventListener(type, listener, useCapture)

        if (!this.eventListenerList) this.eventListenerList = {}
        if (!this.eventListenerList[type]) this.eventListenerList[type] = []
        for (let i = 0; i < this.eventListenerList[type].length; i++) {
            if (this.eventListenerList[type][i].listener === listener && this.eventListenerList[type][i].useCapture === useCapture) {
                this.eventListenerList[type].splice(i, 1)
                break
            }
        }

        if (this.eventListenerList[type].length == 0) delete this.eventListenerList[type]
    }
    Element.prototype.getEventListeners = function(type) {
        if (!this.eventListenerList) this.eventListenerList = {}

        if (!type) return this.eventListenerList
        return this.eventListenerList[type]
    }
    Element.prototype.clearEventListeners = function(a) {
        if (!this.eventListenerList) this.eventListenerList = {}
        if (!a) {
            for (let x in this.getEventListeners()) this.clearEventListeners(x)
            return
        }
        const el = this.getEventListeners(a)
        if (!el) return
        for (let i = el.length - 1; i >= 0; --i) {
            let ev = el[i]
            this.removeEventListener(a, ev.listener, ev.useCapture)
        }
    }
})()
// 判断网页活跃元素
function isActive(eleSet) {
    //  不能为null等
    if (!eleSet)
        return false
    //  判断是否是集合
    if (!eleSet[0])
        return document.activeElement === eleSet
    else {
        for (let i = 0; i < eleSet.length; i++) {
            if (document.activeElement === eleSet[i]) {
                return true
            }
        }
        return false
    }
}
// 判断是否是组合键
function isCombKey(e) {
    return e.ctrlKey || e.shiftKey || e.altKey || e.metaKey
}
// 轮询获取网页元素
async function getEleAsync(selector) {
    return new Promise(res => {
        const interval = setInterval(() => {
            const ele = document.querySelector(selector)
            if (ele) {
                clearInterval(interval)
                res(ele)
            }
        }, 0.5e3)
    })
}
// 鼠标查看未读消息
async function unreadMsg() {
    const msg = await getEleAsync('.right-entry--message')
    msg.addEventListener('mouseenter', () => {
        // 发送异步查看回复请求
        GM_xmlhttpRequest({
            url: 'https:// api.vc.bilibili.com/session_svr/v1/session_svr/single_unread?build=0&mobi_app=web&unread_type=0',
            responseType: 'json',
            onload(dataChat) {
                const chat = dataChat.response.data
                let chatCount = 0
                for (let prop in chat)
                    chatCount += chat[prop]
                GM_xmlhttpRequest({
                    url: 'https:// api.bilibili.com/x/msgfeed/unread?build=0&mobi_app=web',
                    responseType: 'json',
                    onload(data) {
                        // 未读消息对象
                        const unread = data.response.data
                        // 计算未读消息总数
                        let count = 0 - unread.up
                        for (let prop in unread)
                            count += unread[prop]
                        count += chatCount
                        let div = null
                        // 未读消息样式
                        div = document.getElementsByClassName('red-num--message')[0]
                        // 不为零直接添加,为零的话则判断之前是否已经存在未读消息样式,有的话则移除改元素,即 清零消息
                        if (!count) {
                            if (div)
                                msg.removeChild(div)
                        }
                        else {
                            if (!!div)
                                div.textContent = count
                            else {
                                div = document.createElement('div')
                                div.className = 'red-num--message'
                                div.textContent = count
                                msg.append(div)
                            }
                        }
                        // 按消息栏顺序储存各类未读消息
                        let counts = [unread.reply, unread.at, unread.like, unread.sys_msg, chatCount]
                        for (let i = 0; i < 5; i++) {
                            // 获取对应一栏
                            let ele = document.getElementsByClassName('message-inner-list__item')[i]
                            // 未读消息样式,这个需要在对应栏去寻找
                            let span = ele.getElementsByClassName('message-inner-list__item--num')[0]
                            // 不为零直接添加,为零的话则判断之前是否已经存在未读消息样式,有的话则移除改元素,即 清零消息
                            if (!counts[i]) {
                                if (!!span)
                                    ele.removeChild(span)
                            }
                            else {
                                if (!!span)
                                    span.textContent = counts[i]
                                else {
                                    span = document.createElement('span')
                                    span.className = 'message-inner-list__item--num'
                                    span.textContent = counts[i]
                                    ele.append(span)
                                }
                            }
                        }
                    }
                })
            }
        })
    })
}
// 快捷键
async function shortcuts() {
    // t键网页全屏
    // 网页全屏按钮
    const btn = await getEleAsync('.bpx-player-ctrl-web')
    // 获得网页全屏的函数
    const webFullScreen = btn.getEventListeners().click[0].listener
    // 添加按键按下事件监听器
    document.addEventListener('keydown', e => {
        if (!isActive(document.getElementsByTagName('input'))
            && !isActive(document.getElementsByTagName('textarea'))
            && ('t' === e.key || 'T' === e.key))
            webFullScreen()
    })
}
// 直播加强 f全屏 t网页全屏
async function liveAid() {
    // 关闭礼物栏
    localStorage.setItem('FULLSCREEN-GIFT-PANEL-SHOW', 0)
    // 获得播放器
    let player = await getEleAsync('#live-player')
    let funcs = getFuncs(player)
    // 移除聊天框原本的keydown,另外这里需要同步执行
    let chatBox = await getEleAsync('#chat-control-panel-vm textarea')
    chatBox.removeEventListener('keydown', chatBox.getEventListeners().keydown[0].listener)
    // 添加按键按下事件监听器
    document.addEventListener('keydown', e => {
        //  只监听单按键事件
        if (isCombKey(e))
            return
        switch (e.key) {
            // 快速定位到聊天框
            case 'Enter':
                if (!isActive(document.getElementsByTagName('input')) && !isActive(document.getElementsByTagName('textarea')) && !document.fullscreen) {
                    e.preventDefault()
                    chatBox.focus()
                }
                else if (isActive(chatBox)) {
                    e.preventDefault()
                    document.querySelector('#chat-control-panel-vm button').click()
                    chatBox.blur()
                }
                break
            case 'f':
            case 'F':
                if (!isActive(document.getElementsByTagName('input')) && !isActive(document.getElementsByTagName('textarea')))
                    funcs[0]()
                break
            case 't':
            case 'T':
                if (!isActive(document.getElementsByTagName('input')) && !isActive(document.getElementsByTagName('textarea')))
                    funcs[1]()
                break
            case 'd':
            case 'D':
                if (!isActive(document.getElementsByTagName('input')) && !isActive(document.getElementsByTagName('textarea')))
                    funcs[2]()
                break
            case ' ':
                if (!isActive(document.getElementsByTagName('input')) && !isActive(document.getElementsByTagName('textarea'))) {
                    e.preventDefault()
                    funcs[3]()
                    const video = getEleAsync('video')
                    if (!document.querySelector('video').paused) {
                        // 有时候暂停会导致再启动时 会导致之前的函数全部失效 所以得重新赋值
                        funcs = getFuncs(player)
                        // 选择最高分辨率
                        const interval = setInterval(() => {
                            player.dispatchEvent(new Event('mousemove'))
                            const quality = document.querySelector('.quality-wrap')
                            quality.dispatchEvent(new Event('mouseenter'))
                            const list = quality.querySelectorAll('.list-it')
                            if (!!list[0]) {
                                if (!list[0].classList.value.match('selected')) list[0].click()
                                player.dispatchEvent(new Event('mouseleave'))
                                clearInterval(interval)
                            }
                        }, 0.5e3)
                    }
                }
                break
        }
    })
    function getFuncs(player) {
        player.dispatchEvent(new Event('mousemove'))
        const funcs = [
            document.querySelectorAll('.right-area .tip-wrap .icon')[0].getEventListeners().click[0].listener, // 全屏函数
            document.querySelectorAll('.right-area .tip-wrap .icon')[1].getEventListeners().click[0].listener, // 网页全屏
            document.querySelectorAll('.right-area .tip-wrap .icon')[2].getEventListeners().click[0].listener, // 弹幕
            document.querySelectorAll('.left-area .icon')[0].getEventListeners().click[0].listener // 播放
        ]
        player.dispatchEvent(new Event('mouseleave'))
        return funcs
    }
}
function main() {
    // 只在直播页面执行
    if (!!location.href.match('live'))
        liveAid()
    else {
        unreadMsg()
        if (!!document.querySelector('video'))
            shortcuts()
    }
}
main()

QingJ © 2025

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