Skip to content

Latest commit

 

History

History
350 lines (321 loc) · 11.8 KB

2.md

File metadata and controls

350 lines (321 loc) · 11.8 KB

Bilibili网页版 历史弹幕 解析

  • 搞这个只是兴趣驱动,加上没学过编程、算法之类的,所以使用过程中出现问题,还请自己先看看能否解决😊,问我十有八九都不会
  • 截至 2021.06.30 还能用

前言

好久没弄历史弹幕,最近搞东西,需要历史弹幕,发现B站更新了API
https://api.bilibili.com/x/v2/dm/web/history/seg.so?type=1&oid=CID号&date=年-月-日
其实旧API也可以用:https://api.bilibili.com/x/v1/dm/list.so?oid=CID号&date=年-月-日
上网搜了一下,发现似乎没人对新API的数据进行分析

原本就就此为止。
但因为疫情原因被隔离了,闲着也是闲着,既然有新的API,就试试看自己能不能找出解析代码
于是说干就干,一个晚上+一个上午,硬是把主要的代码找出来了,然后模仿这个代码,自己写了一个自己能看懂的入门代码

说明

获取弹幕原始数据并调用解析函数的代码主要在 jsc-player.js第43130行第43150行
解析弹幕的代码主要在 jsc-vendors~player.js第4212行第4426行

我当时是从 jsc-player.js 第43139行 开始,此处是获取解析后的数据(即已经整理好弹幕内容、时间之类的,可直接使用)。然后回溯。最终找到所需的代码。

JavaScript 代码

不要直接复制粘贴运行,请将代码内的 url 设置好再丢到控制台执行

点击展开
class Reader {
    offset = 0
    length = 0
    u8a = []
    constructor(u8a) {
        this.length = u8a.length;
        this.u8a = u8a;
        return this;
    }

    uint32() {
        let l = 4294967295;
        l = (127 & this.u8a[this.offset]) >>> 0;
        if (this.u8a[this.offset++] < 128) {
            return l;
        }

        l = (l | (127 & this.u8a[this.offset]) << 7) >>> 0;
        if (this.u8a[this.offset++] < 128) {
            return l;
        }

        l = (l | (127 & this.u8a[this.offset]) << 14) >>> 0;
        if (this.u8a[this.offset++] < 128) {
            return l;
        }

        l = (l | (127 & this.u8a[this.offset]) << 21) >>> 0;
        if (this.u8a[this.offset++] < 128) {
            return l;
        }

        l = (l | (15 & this.u8a[this.offset]) << 28) >>> 0;
        if (this.u8a[this.offset++] < 128) {
            return l;
        }

        this.offset += 5;
        if (this.offset > this.length) {
            this.offset = this.length;
            throw RangeError("index out of range: " + this.offset + " + " + (10 || 1) + " > " + this.length);
        }

        return l;
    }

    int32() {
        return 0 | this.uint32();
    }

    int64() {
        let t = { lo: 0, hi: 0 };
        let e = 0;
        if (this.length - this.offset <= 4) {
            for (; e < 3; ++e) {
                if (this.offset >= this.length) {
                    throw RangeError("index out of range: " + this.offset + " + " + 1 + " > " + this.length);
                }
                t.lo = (t.lo | (127 & this.u8a[this.offset]) << 7 * e) >>> 0;
                if (this.u8a[this.offset++] < 128) {
                    return this.toNumber(1, t);
                }
            }
            t.lo = (t.lo | (127 & this.u8a[this.offset++]) << 7 * e) >>> 0;
            return this.toNumber(1, t);
        }
        for (; e < 4; ++e) {
            t.lo = (t.lo | (127 & this.u8a[this.offset]) << 7 * e) >>> 0;
            if (this.u8a[this.offset++] < 128) {
                return this.toNumber(1, t);
            }
        }
        t.lo = (t.lo | (127 & this.u8a[this.offset]) << 28) >>> 0;
        t.hi = (t.hi | (127 & this.u8a[this.offset]) >> 4) >>> 0;
        if (this.u8a[this.offset++] < 128) {
            return this.toNumber(1, t);
        }

        e = 0;
        if (this.length - this.offset > 4) {
            for (; e < 5; ++e) {
                t.hi = (t.hi | (127 & this.u8a[this.offset]) << 7 * e + 3) >>> 0;
                if (this.u8a[this.offset++] < 128) {
                    return this.toNumber(1, t);
                }
            }
        } else {
            for (; e < 5; ++e) {
                if (this.offset >= this.length) {
                    throw RangeError("index out of range: " + this.offset + " + " + 1 + " > " + this.length);
                }
                t.hi = (t.hi | (127 & this.u8a[this.offset]) << 7 * e + 3) >>> 0;
                if (this.u8a[this.offset++] < 128) {
                    return this.toNumber(1, t);
                }
            }
        }
        throw Error("invalid varint encoding");
    }

    bytes() {
        let t = this.uint32();
        let e = this.offset;
        let i = this.offset + t;
        if (i > this.length) {
            throw RangeError("index out of range: " + this.offset + " + " + (t || 1) + " > " + this.length);
        }
        this.offset += t;
        return this.u8a.slice(e, i);
    }

    string() {
        let t = this.bytes();
        let i = t.length;
        let e = 0;
        if (i - e < 1)
            return "";

        if (i < 1) {
            return "";
        }
        let r;
        let n = null;
        let a = [];
        let o = 0;
        for (; e < i;) {
            r = t[e++];
            if (r < 128) {
                a[o++] = r;
            } else if (r > 191 && r < 224) {
                a[o++] = (31 & r) << 6 | 63 & t[e++];
            } else if (r > 239 && r < 365) {
                r = ((7 & r) << 18 | (63 & t[e++]) << 12 | (63 & t[e++]) << 6 | 63 & t[e++]) - 65536;
                a[o++] = 55296 + (r >> 10);
                a[o++] = 56320 + (1023 & r);
            } else {
                a[o++] = (15 & r) << 12 | (63 & t[e++]) << 6 | 63 & t[e++];
                if (o > 8191) {
                    n = n || [];
                    n.push(String.fromCharCode.apply(String, a));
                    o = 0;
                }
            }
        }
        return n ? (o && n.push(String.fromCharCode.apply(String, a.slice(0, o))),
            n.join("")) : String.fromCharCode.apply(String, a.slice(0, o));
    }

    toNumber(flag, t) {
        if (flag && t.hi >>> 31) {
            let e = 1 + ~t.lo >>> 0;
            let i = ~t.hi >>> 0;
            if (!e) {
                i = i + 1 >>> 0;
            }
            return -(e + 4294967296 * i);
        }
        return t.lo + 4294967296 * t.hi;
    }

    skip(t) {
        if ("number" == typeof t) {
            if (this.offset + t > this.length) {
                throw RangeError("index out of range: " + this.offset + " + " + (t || 1) + " > " + this.length);
            }
            this.offset += t;
        } else {
            do {
                if (this.offset >= this.length) {
                    throw RangeError("index out of range: " + this.offset + " + " + 1 + " > " + this.length);
                }
            } while (128 & this.u8a[this.offset++]);
        }
        return this;
    }
    skipType(t) {
        switch (t) {
            case 0: {
                this.skip();
                break;
            }
            case 1: {
                this.skip(8);
                break;
            }
            case 2: {
                this.skip(this.uint32());
                break;
            }
            case 3: {
                while (1) {
                    t = 7 & this.uint32();
                    if (4 != t) {
                        this.skip(t);
                    } else {
                        break;
                    }
                }
                break;
            }
            case 5: {
                this.skip(4);
                break;
            }
            default: {
                throw Error("invalid wire type " + t + " at offset " + this.offset);
            }
        }
        return this;
    }
}

function DanmakuDecode(u8a) {
    let M = [];
    let r = new Reader(u8a);
    while (r.offset < r.length) {
        let u32 = r.uint32();

        switch (u32 >>> 3) {
            case 1: {
                (function (offs) {
                    let m = {};
                    let c = r.offset + offs;
                    while (r.offset < c) {
                        let t = r.uint32();
                        switch (t >>> 3) {
                            case 1: {
                                m.id = r.int64();
                                break;
                            }
                            case 2: {
                                m.progress = r.int32();
                                break;
                            }
                            case 3: {
                                m.mode = r.int32();
                                break;
                            }
                            case 4: {
                                m.fontsize = r.int32();
                                break;
                            }
                            case 5: {
                                m.color = r.uint32();
                                break;
                            }
                            case 6: {
                                m.midHash = r.string();
                                break;
                            }
                            case 7: {
                                m.content = r.string();
                                break;
                            }
                            case 8: {
                                m.ctime = r.int64();
                                break;
                            }
                            case 9: {
                                m.weight = r.int32();
                                break;
                            }
                            case 10: {
                                m.action = r.string();
                                break;
                            }
                            case 11: {
                                m.pool = r.int32();
                                break;
                            }
                            case 12: {
                                m.idStr = r.string();
                                break;
                            }
                            case 13: {
                                m.attr = r.int32();
                                break;
                            }
                            default: {
                                r.skipType(t & 7);
                                break;
                            }
                        }
                    }
                    M.push(m);
                })(r.uint32());
                break;
            }
            default: {
                r.skipType(u32 & 7);
                break;
            }
        }
    }
    return M;
}

var url = "https://api.bilibili.com/x/v2/dm/web/history/seg.so?type=1&oid=视频的CID号&date=年-月(两位数)-日(两位数)";

var xhr = new XMLHttpRequest;
xhr.open("GET", url);
xhr.responseType = "arraybuffer";
xhr.withCredentials = true;
xhr.addEventListener("load", function () {
    let u8a = new Uint8Array(this.response);
    window.M = DanmakuDecode(u8a);
});
xhr.send();

2021.06.30