攔截B站快捷鍵w.a.s.d.e.r但不影響其他腳本或擴充功能,並新增逐幀進退功能與開關彈幕

攔截B站快捷鍵w.a.s.d.e.r,新增「,」(後退1幀)「.」(前進1幀)「T」(開關彈幕)

// ==UserScript==
// @name         攔截B站快捷鍵w.a.s.d.e.r但不影響其他腳本或擴充功能,並新增逐幀進退功能與開關彈幕
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  攔截B站快捷鍵w.a.s.d.e.r,新增「,」(後退1幀)「.」(前進1幀)「T」(開關彈幕)
// @author       shanlan(grok-4-fast-reasoning)
// @match        *://www.bilibili.com/video/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const rawAddEventListener = window.addEventListener;

    window.addEventListener = function(type, listener, options) {
        if (type === 'keydown' && typeof listener === 'function') {
            const wrapped = function(e) {
                if (['w','a','s','d','e','r'].includes(e.key.toLowerCase())) {
                    return;
                }
                return listener.apply(this, arguments);
            };
            return rawAddEventListener.call(this, type, wrapped, options);
        }
        return rawAddEventListener.call(this, type, listener, options);
    };

    let video = null;
    let frameTime = 1 / 60;

    function init() {
        if (window.__playinfo__?.data?.dash?.video?.[0]?.frame_rate) {
            frameTime = 1 / parseFloat(window.__playinfo__.data.dash.video[0].frame_rate);
        }

        video = document.querySelector('video');
        if (!video) {
            new MutationObserver(() => {
                video = document.querySelector('video');
                if (video) bindEvents();
            }).observe(document.body, { childList: true, subtree: true });
            return setTimeout(init, 500);
        }
        bindEvents();
    }

    function bindEvents() {
        document.addEventListener('keydown', (e) => {
            if (e.target !== document.body || !video || (e.key !== ',' && e.key !== '.' && e.key !== 't')) return;
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            if (e.key === 't') {
                const dmSwitch = document.querySelector('.bui-danmaku-switch-input');
                if (dmSwitch) {
                    dmSwitch.checked = !dmSwitch.checked;
                    dmSwitch.dispatchEvent(new Event('change', { bubbles: true }));
                }
            } else if (e.key === ',') {
                video.currentTime = Math.max(0, video.currentTime - frameTime);
                video.pause();
            } else {
                video.currentTime += frameTime;
                video.pause();
            }
        }, {capture: true});
    }

    setTimeout(init, 1000);

})();