Bilibili 自动开启字幕 (支持模糊匹配)

辣鸡 B 站到 2025 年还不支持自动开启字幕

// ==UserScript==
// @name         Bilibili 自动开启字幕 (支持模糊匹配)
// @namespace    http://tampermonkey.net/
// @description  辣鸡 B 站到 2025 年还不支持自动开启字幕
// @version      1.3.2
// @author       Yuna
// @match        *://www.bilibili.com/video/*
// @grant        GM_registerMenuCommand
// @run-at       document-end
// @license      MIT
// ==/UserScript==

'use strict';

const DEBUG = true;

function log ( message, notifyDelay = 3000 )
{
    if ( DEBUG )
    {
        notification( message, notifyDelay );
        console.log( "[Bilibili 自动开启字幕]", message );
    }
}


/** 尝试开启字幕
 * @returns bool  是否成功开启字幕
 */
function enableSubtitle ()
{
    /* 排序越是靠前的字幕优先级越高 */
    const priorityList = [
        { name: "英语", keyWord : "en-" },
        { name: "AI英语", keyWord : "ai-en" },
        { name: "中文字幕", keyWord : "zh-" },
        { name: "AI中文字幕", keyWord : "ai-zh" },
    ];

    for ( const item of priorityList )
    {
        const language = document.querySelector( `.bpx-player-ctrl-subtitle-language-item[data-lan *= "${ item.keyWord }"]` );

        if ( language === null )
        {
            log( `未找到 ${ item.name } 字幕` );
            continue;
        }

        if ( language.className.includes( "bpx-state-active" ) )
        {
            log( `${ item.name } 字幕已开启` );
            return true;
        }

        const subtitleButton = language.querySelector( ".bpx-player-ctrl-subtitle-language-item-text" );

        if ( subtitleButton === null )
        {
            log( `未找到 ${ item.name } 字幕按钮` );
            return false;
        }

        subtitleButton.click();

        log( `已开启 ${ item.name } 字幕` );
        return true;
    }

    log( "未找到可用字幕" );
    return false;
}

/**
 * 显示通知, Hook 到 Bilibili 字幕设置页面
 * @param message str   希望显示的消息
 * @param timeoutOfMS int   通知显示的时长, 单位为毫秒
 */
function notification ( message, timeoutOfMS = 3000 )
{
    const notificationBox = document.querySelector( ".bpx-player-toast-auto" );

    if ( notificationBox === null )
    {
        log( "未找到通知框" );
        return;
    }

    const notificationItem = document.createElement( "div" );
    notificationItem.innerHTML = `
        <div class = "bpx-player-toast-row bpx-player-toast-unfold">
            <div class = "bpx-player-toast-item"><span class = "bpx-player-toast-text">${ message }</span>
            </div>
        </div>
    `;

    notificationBox.appendChild( notificationItem );

    setTimeout( () => notificationItem.remove(), timeoutOfMS );
}

const videoPlayer = document.querySelector( ".bpx-player-video-wrap video" );

if ( videoPlayer )
{
    videoPlayer.addEventListener( "play", () =>
    {
        log( "视频播放中,尝试开启字幕" );
        
        if ( !enableSubtitle() )
        {
            log( "尝试开启字幕失败,3 秒后重试" );
            setTimeout( () =>
                        {
                            if ( !enableSubtitle() )
                            {
                                log( "再次尝试开启字幕失败" );
                            }
                        }, 3000 );
        }
    } );
}
else
{
    log( "未找到视频播放器" );
}