b站字幕信息获取

通过解析b站api.bilibili.com/x/player/的返回数据,获得当前视频的,基本信息、用户信息、IP地址信息、等级信息和媒体信息,可以获取到当前字幕的链接、背景音乐链接等等

效果展示:

油猴代码:

// ==UserScript==
// @name         Bilibili API 信息展示
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  多栏布局美化展示 Bilibili API 返回的精简信息
// @author       TheGreat
// @match        *://api.bilibili.com/x/player/**
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // 字段分组配置
    const fieldGroups = {
        basicInfo: {
            title: '基本信息',
            fields: ['aid', 'bvid', 'allow_bp', 'no_share', 'cid', 'max_limit', 'page_no', 'has_next']
        },
        userInfo: {
            title: '用户信息',
            fields: ['login_mid', 'login_mid_hash', 'is_owner', 'name', 'permission']
        },
        ipInfo: {
            title: 'IP信息',
            fields: ['ip_info']
        },
        levelInfo: {
            title: '等级信息',
            fields: ['level_info']
        },
        mediaInfo: {
            title: '媒体信息',
            fields: ['subtitle', 'bgm_info']
        }
    };

    // 需要显示的字段及其中文翻译(保持原有的翻译配置)
    const displayFields = {
        'aid': '视频ID',
        'bvid': 'BV号',
        'allow_bp': '允许充电',
        'no_share': '禁止分享',
        'cid': '分P ID',
        'max_limit': '最大限制',
        'page_no': '页码',
        'has_next': '是否有下一页',
        'ip_info': {
            label: 'IP信息',
            fields: {
                'ip': 'IP地址',
                'zone_ip': '区域IP',
                'zone_id': '区域ID',
                'country': '国家',
                'province': '省份',
                'city': '城市'
            }
        },
        'login_mid': '登录用户ID',
        'login_mid_hash': '用户ID哈希',
        'is_owner': '是否所有者',
        'name': '用户名',
        'permission': '权限',
        'level_info': {
            label: '等级信息',
            fields: {
                'current_level': '当前等级',
                'current_min': '当前最小值',
                'current_exp': '当前经验',
                'next_exp': '下一级经验',
                'level_up': '升级时间'
            }
        },
        'subtitle': {
            label: '字幕信息',
            fields: {
                'subtitles': {
                    label: '字幕列表',
                    arrayFields: {
                        'id': 'ID',
                        'lan': '语言',
                        'lan_doc': '语言说明',
                        'is_lock': '是否锁定',
                        'subtitle_url': '字幕链接',
                        'id_str': 'ID字符串',
                        'ai_type': 'AI类型',
                        'ai_status': 'AI状态'
                    }
                }
            }
        },
        'bgm_info': {
            label: '背景音乐信息',
            fields: {
                'music_id': '背景音乐ID',
                'music_title': '背景音乐标题',
                'jump_url': '背景音乐链接'
            }
        }
    };

    // 创建样式
    const style = document.createElement('style');
    style.textContent = `
        .json-container {
            font-family: Arial, sans-serif;
            padding: 20px;
            background: #fff;
            max-width: 1200px;
            margin: 0 auto;
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 20px;
        }
        .group-container {
            background: #f8f9fa;
            border-radius: 8px;
            padding: 15px;
        }
        .group-title {
            font-size: 16px;
            font-weight: bold;
            color: #1565C0;
            margin-bottom: 10px;
            padding-bottom: 5px;
            border-bottom: 2px solid #1565C0;
        }
        .json-item {
            margin: 5px 0;
            padding: 5px 10px;
            background: #fff;
            border-radius: 4px;
            border: 1px solid #e0e0e0;
        }
        .json-key {
            font-weight: 600;
            color: #2196F3;
            margin-right: 10px;
        }
        .json-value {
            color: #333;
        }
        .json-object {
            margin-left: 15px;
        }
        .open-link-btn {
            display: inline-block;
            margin-left: 10px;
            padding: 2px 8px;
            background: #2196F3;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            text-decoration: none;
            font-size: 12px;
        }
        .open-link-btn:hover {
            background: #1976D2;
        }
        /* 媒体信息组占据整行 */
        .group-container.media-info {
            grid-column: 1 / -1;
        }
        /* 响应式布局 */
        @media (max-width: 768px) {
            .json-container {
                grid-template-columns: 1fr;
            }
        }
    `;
    document.head.appendChild(style);

    function createLinkButton(url) {
        const btn = document.createElement('a');
        btn.href = url;
        btn.className = 'open-link-btn';
        btn.textContent = '打开链接';
        btn.target = '_blank';
        return btn;
    }

    function renderValue(key, value, translations, level = 0) {
        const item = document.createElement('div');
        item.className = 'json-item';
        item.style.marginLeft = `${level * 15}px`;

        const keySpan = document.createElement('span');
        keySpan.className = 'json-key';
        keySpan.textContent = translations.label || translations;

        const valueSpan = document.createElement('span');
        valueSpan.className = 'json-value';

        if (value === null) {
            valueSpan.textContent = 'null';
        } else if (typeof value === 'object') {
            if (Array.isArray(value)) {
                value.forEach((v, i) => {
                    if (translations.arrayFields) {
                        const arrayItem = document.createElement('div');
                        arrayItem.style.marginLeft = '15px';
                        Object.entries(translations.arrayFields).forEach(([fieldKey, fieldLabel]) => {
                            if (v[fieldKey] !== undefined) {
                                const field = renderValue(fieldKey, v[fieldKey], fieldLabel, level + 1);
                                arrayItem.appendChild(field);

                                if (fieldKey === 'subtitle_url' || fieldKey === 'subtitle_url_v2') {
                                    const button = createLinkButton(v[fieldKey]);
                                    field.appendChild(button);
                                }
                            }
                        });
                        item.appendChild(arrayItem);
                    }
                });
                return item;
            } else if (translations.fields) {
                Object.entries(translations.fields).forEach(([fieldKey, fieldTranslation]) => {
                    if (value[fieldKey] !== undefined) {
                        const field = renderValue(fieldKey, value[fieldKey], fieldTranslation, level + 1);
                        item.appendChild(field);

                        if (fieldKey === 'jump_url') {
                            const button = createLinkButton(value[fieldKey]);
                            field.appendChild(button);
                        }
                    }
                });
                return item;
            }
        } else {
            valueSpan.textContent = value.toString();
        }

        item.appendChild(keySpan);
        item.appendChild(valueSpan);
        return item;
    }

    function createGroup(title, className = '') {
        const group = document.createElement('div');
        group.className = `group-container ${className}`;

        const titleElement = document.createElement('div');
        titleElement.className = 'group-title';
        titleElement.textContent = title;

        group.appendChild(titleElement);
        return group;
    }

    function parseAndDisplay() {
        const pre = document.querySelector('pre');
        if (!pre) return;

        try {
            const jsonData = JSON.parse(pre.textContent);
            const container = document.createElement('div');
            container.className = 'json-container';

            const data = jsonData.data;
            if (data) {
                // 将媒体信息组放在最前面
                const mediaGroup = createGroup(fieldGroups.mediaInfo.title, 'media-info');
                fieldGroups.mediaInfo.fields.forEach(fieldKey => {
                    if (data[fieldKey] !== undefined && displayFields[fieldKey]) {
                        mediaGroup.appendChild(renderValue(fieldKey, data[fieldKey], displayFields[fieldKey]));
                    }
                });
                container.appendChild(mediaGroup);

                // 根据分组创建布局(跳过媒体信息组)
                Object.entries(fieldGroups).forEach(([groupKey, groupInfo]) => {
                    if (groupKey !== 'mediaInfo') {
                        const group = createGroup(groupInfo.title, '');
                        groupInfo.fields.forEach(fieldKey => {
                            if (data[fieldKey] !== undefined && displayFields[fieldKey]) {
                                group.appendChild(renderValue(fieldKey, data[fieldKey], displayFields[fieldKey]));
                            }
                        });
                        container.appendChild(group);
                    }
                });
            }

            document.body.innerHTML = '';
            document.body.appendChild(container);
        } catch (e) {
            console.error('JSON解析错误:', e);
        }
    }


    // 页面加载完成后执行
    window.addEventListener('load', parseAndDisplay);
})();

视频的api可以通过以下脚本获得:

// ==UserScript==
// @name         B站API请求捕获
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  捕获B站视频API请求
// @author       TheGreat
// @match        https://www.bilibili.com/video/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // 创建UI容器
    const createUI = () => {
        const div = document.createElement('div');
        div.id = 'api-capture';
        div.style.cssText = `
            position: fixed;
            top: 50px;
            right: 30px;
            background: #f9f9f9;
            padding: 0px;
            border: 1px solid #ccc;
            border-radius: 4px;
            z-index: 9999;
            max-width: 400px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            font-size: 12px;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        `;
        const toggleButton = document.createElement('button');
        toggleButton.textContent = '展开/折叠';
        toggleButton.style.cssText = `
            background: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            padding: 5px;
            cursor: pointer;
            margin-bottom: 0px;
        `;
        toggleButton.onclick = () => {
            const content = document.getElementById('api-content');
            content.style.display = content.style.display === 'none' ? 'block' : 'none';
            toggleButton.textContent = content.style.display === 'none' ? '展开' : '折叠';
        };

        const contentDiv = document.createElement('div');
        contentDiv.id = 'api-content';
        contentDiv.style.display = 'none'; // 默认折叠

        div.appendChild(toggleButton);
        div.appendChild(contentDiv);
        document.body.appendChild(div);
        return contentDiv;
    };

    const contentDiv = createUI();

    // 添加复制和打开按钮
    const addButtons = (url) => {
        const buttonContainer = document.createElement('div');
        buttonContainer.style.marginBottom = '10px';

        const copyButton = document.createElement('button');
        copyButton.textContent = '复制';
        copyButton.style.cssText = `
            background: #008CBA;
            color: white;
            border: none;
            border-radius: 4px;
            padding: 5px;
            cursor: pointer;
            margin-right: 5px;
        `;
        copyButton.onclick = () => {
            navigator.clipboard.writeText(url)
                .then(() => alert('已复制到剪贴板!'))
                .catch(err => console.error('复制失败:', err));
        };

        const openButton = document.createElement('button');
        openButton.textContent = '打开';
        openButton.style.cssText = `
            background: #FF5722;
            color: white;
            border: none;
            border-radius: 4px;
            padding: 5px;
            cursor: pointer;
        `;
        openButton.onclick = () => {
            window.open(url, '_blank');
        };

        buttonContainer.appendChild(copyButton);
        buttonContainer.appendChild(openButton);
        contentDiv.appendChild(buttonContainer);
    };

    // 拦截XMLHttpRequest
    const XHR = XMLHttpRequest.prototype;
    const open = XHR.open;
    const send = XHR.send;

    XHR.open = function(method, url) {
        this._url = url;
        return open.apply(this, arguments);
    };

    XHR.send = function() {
        if (this._url.includes('api.bilibili.com/x/player/wbi/v2')) {
            const urlDiv = document.createElement('div');
            urlDiv.style.marginBottom = '10px';
            urlDiv.textContent = this._url;
            contentDiv.insertBefore(urlDiv, contentDiv.firstChild);
            addButtons(this._url);

            // 只保留最近的5条记录
            while (contentDiv.children.length > 5) {
                contentDiv.removeChild(contentDiv.lastChild);
            }
        }
        return send.apply(this, arguments);
    };
})();

这个脚本可以在你网页访问b站的时,在右上角创建一个按钮,用于获取当前视频的api信息

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值