Greasy Fork

Greasy Fork is available in English.

Bilibili显示当前视频字幕

获取当前视频的字幕,弹窗直观显示所有字幕,方便快速遍览字幕,切换字幕,按字幕找对应视频时间点。

当前为 2023-10-08 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Bilibili显示当前视频字幕
// @namespace    https://space.bilibili.com/526552477
// @version      2.7
// @description  获取当前视频的字幕,弹窗直观显示所有字幕,方便快速遍览字幕,切换字幕,按字幕找对应视频时间点。
// @match        https://www.bilibili.com/video/av*
// @match        https://www.bilibili.com/video/BV*
// @icon         data:image/x-icon;base64,AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAABMLAAATCwAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A1qEAANahAADWoQAG1qEAb9ahAMvWoQD01qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD01qEAy9ahAG/WoQAG1qEAANahAADWoQAA1qEAG9ahAM/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahANDWoQAb1qEAANahAAfWoQDQ1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahANHWoQAH1qEAbtahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAG7WoQDH1qEA/9ahAP/WoQD/1qEAtdahABjWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahABvWoQC11qEA/9ahAP/WoQD/1qEAx9ahAPnWoQD/1qEA/9ahAP/WoQAZ1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahABjWoQD/1qEA/9ahAP/WoQDz1qEA/9ahAP/WoQD/1qEA/9ahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEAANahAADWoQAA1qEAANahAErWoQDn1qEA5NahAErWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAErWoQDn1qEA5NahAErWoQAA1qEAANahAADWoQAA1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQAA1qEAANahAADWoQAA1qEA5tahAP/WoQD/1qEA59ahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEA5tahAP/WoQD/1qEA59ahAADWoQAA1qEAANahAADWoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAADWoQAA1qEAANahAADWoQD/1qEA/9ahAP/WoQD/1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQD/1qEA/9ahAP/WoQD/1qEAANahAADWoQAA1qEAANahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEAANahAADWoQAA1qEAANahAP/WoQD/1qEA/9ahAP/WoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAP/WoQD/1qEA/9ahAP/WoQAA1qEAANahAADWoQAA1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQAA1qEAANahAADWoQAA1qEA5tahAP/WoQD/1qEA5tahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEA5tahAP/WoQD/1qEA5tahAADWoQAA1qEAANahAADWoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAADWoQAA1qEAANahAADWoQBJ1qEA5tahAObWoQBJ1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQBJ1qEA5tahAObWoQBJ1qEAANahAADWoQAA1qEAANahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQD/1qEA/9ahAP/WoQD/1qEA+dahAP/WoQD/1qEA/9ahABnWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAGdahAP/WoQD/1qEA/9ahAPjWoQDH1qEA/9ahAP/WoQD/1qEAttahABnWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahABnWoQC21qEA/9ahAP/WoQD/1qEAx9ahAG3WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQBt1qEABtahAM/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA0NahAAfWoQAA1qEAG9ahAM/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAM/WoQAb1qEAANahAADWoQAA1qEABtahAG7WoQDH1qEA89ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA/9ahAP/WoQD/1qEA89ahAMfWoQBu1qEABtahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEADtahAMXWoQD/1qEA/9ahAP/WoQD/1qEAxdahAA/WoQAA1qEAANahAADWoQAA1qEADtahAMXWoQD/1qEA/9ahAP/WoQD/1qEAxdahAA/WoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAAbWoQDF1qEA/9ahAP/WoQD/1qEA/9ahAMXWoQAP1qEAANahAADWoQAA1qEAANahAADWoQAA1qEADtahAMXWoQD/1qEA/9ahAP/WoQD/1qEAxdahAAbWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAYtahAP/WoQD/1qEA/9ahAP/WoQDF1qEADtahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEADtahAMXWoQD/1qEA/9ahAP/WoQD/1qEAY9ahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQBf1qEA/9ahAP/WoQD/1qEAxdahAA7WoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEADtahAMXWoQD/1qEA/9ahAP/WoQBf1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAATWoQCg1qEA6tahAKjWoQAO1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEAANahAADWoQAA1qEADtahAKjWoQDr1qEAoNahAATWoQAA1qEAANahAADWoQAA1qEAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A///////////AAAADgAAAAQAAAAAAAAAAA///wAf//+AP///wD///8A////AP///wDw/w8A8P8PAPD/DwDw/w8A8P8PAPD/DwD///8A////AH///gA///wAAAAAAAAAAAgAAAAcAAAAP8A8A/+AfgH/gP8B/4H/gf+D/8H/////8=
// @author       Scipline
// @connect      api.bilibili.com
// @connect      aisubtitle.hdslb.com
// @grant        GM_addStyle
// @grant GM_download
// @grant GM_xmlhttpRequest
// @license Apache License 2.0
// ==/UserScript==
GM_addStyle(`
    button {
        background-color: #008CBA;
        color: white;
        border: none;
        padding: 13px 20px;
        text-align: center;
        text-decoration: none;
        display: inline-block;
        font-size: 16px;
        margin: 4px 2px;
        cursor: pointer;
    }
    #summary {
        position: fixed;
        top: 70px;
        left: 20px;
        max-height: 400px;
        width: 300px;
        background-color: white;
        border: 1px solid gray;
        padding: 10px;
        box-shadow: 5px 5px 5px gray;
        z-index: 999;
        overflow-y: auto;
    }
    #summary a {
        color: #008CBA;
        text-decoration: underline;
    }
`);
(async function () {
    'use strict';
    // 创建按钮并绑定点击事件
    var button = document.createElement("button");
    button.innerHTML = "显示字幕";
    button.style.position = "fixed";
    button.style.top = "60px";
    button.style.right = "20px";
    document.body.appendChild(button);

    // 创建弹窗内容
    var summary = document.createElement('div');
    summary.id = 'summary';
    summary.style.display = 'none';
    summary.style.position = 'fixed';
    // summary.style.top = '60px';
    // summary.style.right = '20px';
    // summary.style.width = '260px';
    // summary.style.height = '350px';
    summary.style.overflow = 'auto';

    // 添加弹窗到页面
    document.body.appendChild(summary);

    // 定义全局变量
    let subtitleText = [];
    let avid = null;
    let bvid = null;
    let title = null;
    // 绑定按钮点击事件
    button.addEventListener('click', async function () {
        if (summary.style.display === 'none') {
            if (subtitleText.length > 0 && document.querySelector("#viewbox_report > h1").innerText === title) { // 如果已经获取过字幕内容,则直接显示
                summary.style.display = 'block';
                button.innerHTML = "隐藏字幕";
            } else {
                try {
                    // 获取所有字幕
                    const subtitleJson = await getSubtitleJSON();
                    const subtitles = subtitleJson.data.subtitle.subtitles;
                    const tabCount = subtitles.length;
                    // 创建选项卡和字幕内容到弹窗
                    const tabsHTML = [];
                    const subtitlesHTML = [];
                    if (tabCount === 0) {
                        alert("当前视频没有找到字幕");
                        return;
                    }
                    for (let i = 0; i < tabCount; i++) {
                        tabsHTML.push(`<button class="tablinks${(i === 0) ? ' active' : ''}" onclick="openTab(event, 'subtitle${i}')">字幕${i + 1}</button>`);
                        subtitleText = await getSubtitleText(subtitles[i].subtitle_url);
                        subtitlesHTML.push(`<div id="subtitle${i}" class="tabcontent-container"${(i === 0) ? ' style="display:block;"' : ''}>${subtitleText}</div>`);
                        title = document.querySelector("#viewbox_report > h1").innerText;
                    }
                    summary.innerHTML = `
                        <div class="tab">
                            ${tabsHTML.join('')}
                        </div>
                        ${subtitlesHTML.join('')}
                    `;
                    // 显示弹窗和第一个字幕
                    summary.style.display = 'block';
                    button.innerHTML = "关闭字幕";
                    // 绑定超链接点击事件
                    const currentvideo =document.querySelector('video');
                    // 超链接方式刷新当前页面打开
                    // window.open(link.getAttribute('href'), '_self');
                    summary.querySelectorAll('a')
                        .forEach(function (link) {
                            link.addEventListener('click', function (e) {
                                // 阻止默认行为
                                e.preventDefault();
                                // 如果链接的 href 属性包含 "?t=",说明有指定时间点
                                if (this.href.indexOf("?t=") !== -1) {
                                    // 使用正则表达式匹配 t= 后面的数字并取出
                                    const regex = /\?t=(\d+)/;
                                    const match = regex.exec(this.href);
                                    // 如果成功匹配到了数字,就将其赋值给 startTime 变量,并打印输出
                                    if (match !== null && match[1] !== undefined) {
                                        const startTime = parseInt(match[1]);
                                        // 执行 JavaScript 代码
                                        eval(`document.querySelector('video').currentTime=${startTime};`);
                                    }
                                }
                            });
                        });
                } catch (error) {
                    // 显示错误信息
                    alert(error.message);
                }
            }
        } else {
            // 隐藏弹窗
            summary.style.display = 'none';
            button.innerHTML = "显示字幕";
        }
    });

    // 获取视频所有字幕的 JSON 数据
    async function getSubtitleJSON() {
        return new Promise((resolve, reject) => {
            const url = window.location.href;
            const avidRegex = /\/av([0-9]+)\//;
            const bvidRegex = /\/(BV[0-9a-zA-Z]+)\/?/;
            const avidMatch = url.match(avidRegex);
            const bvidMatch = url.match(bvidRegex);
            avid = avidMatch ? avidMatch[1] : null;
            bvid = bvidMatch ? bvidMatch[1] : null;

            // 构造获取视频信息的 API 链接
            let apiLink;
            if (avid) {
                apiLink = 'https://api.bilibili.com/x/player/pagelist?aid=' + avid;
            } else if (bvid) {
                apiLink = 'https://api.bilibili.com/x/player/pagelist?bvid=' + bvid;
            }


            // 发送获取视频信息的请求
            GM_xmlhttpRequest({
                method: 'GET',
                url: apiLink,
                onload: function (response) {
                    if (response.status === 200) {
                        const responseJson = JSON.parse(response.responseText);
                        const cid = responseJson.data[0].cid;

                        // 构造获取字幕链接的 API 链接
                        let subtitleLink = 'https://api.bilibili.com/x/player/v2?';
                        subtitleLink += 'cid=' + cid;
                        if (avid) {
                            subtitleLink += '&aid=' + avid;
                        } else if (bvid) {
                            subtitleLink += '&bvid=' + bvid;
                        }
                        subtitleLink += '&qn=32&type=&otype=json&ep_id=&fourk=1&fnver=0&fnval=16';

                        // 发送获取字幕链接的请求
                        GM_xmlhttpRequest({
                            method: 'GET',
                            url: subtitleLink,
                            onload: function (subtitleResponse) {
                                if (subtitleResponse.status === 200) {
                                    const subtitleJson = JSON.parse(subtitleResponse.responseText);
                                    resolve(subtitleJson);

                                } else {
                                    reject(new Error('获取字幕链接失败'));
                                }
                            }
                        });
                    } else {
                        reject(new Error('获取视频信息失败'));
                    }
                }
            });
        });
    }


    // 发送获取字幕文件内容的请求
    function getSubtitleText(subtitleUrl) {
        return new Promise((resolve, reject) => {
            subtitleUrl = "https://"+subtitleUrl;
            GM_xmlhttpRequest({
                method: 'GET',
                url: subtitleUrl,
                responseType: 'json',
                onload: function (response) {
                    if (response.status === 200) {
                        let count = 1;
                        const subtitles = response.response.body;
                        // 全部字幕内容
                        // const subtitleContent = subtitles.map(subtitle => subtitle.content).join(',');
                        // console.log(subtitleContent)
                        const subtitleText = subtitles.map(subtitle => {
                            // 部分字幕可能没有subtitle.sid参数,手动编号
                            const sid = count++;
                            const startTime = formatTime(subtitle.from);
                            const endTime = formatTime(subtitle.to);
                            const videourl = `https://www.bilibili.com/video/${avid ? 'av' + avid : bvid}?t=${Math.floor(subtitle.from)}`;
                            return `
              <div>
                <p>字幕序号:${sid}</p>
                <p>字幕内容:${subtitle.content}</p>
                <a href="${videourl}" target="_self">时间点:${startTime} - ${endTime}</a><br/>
              </div>
            `;
                        })
                            .join('');
                        resolve(subtitleText);
                    } else {
                        reject(new Error('获取字幕文件内容失败'));
                    }
                }
            });
        });
    }

    // 格式化时间,将秒数转化为分钟
    function formatTime(time) {
        const minutes = Math.floor(time / 60);
        let seconds = Math.floor(time % 60);
        seconds = seconds < 10 ? '0' + seconds : seconds;
        return minutes + ':' + seconds;
    }


})();
// 切换选项卡显示对应的字幕
unsafeWindow.openTab = function (evt, tabName) {
    var i, tabcontent, tablinks;
    tabcontent = document.getElementsByClassName("tabcontent-container");
    for (i = 0; i < tabcontent.length; i++) {
        tabcontent[i].style.display = "none";
    }
    tablinks = document.getElementsByClassName("tablinks");
    for (i = 0; i < tablinks.length; i++) {
        tablinks[i].className = tablinks[i].className.replace(" active", "");
    }
    document.getElementById(tabName)
        .style.display = "block";
    evt.currentTarget.className += " active";
};