- 搞这个只是兴趣驱动,加上没学过编程、算法之类的,所以使用过程中出现问题,还请自己先看看能否解决😊,问我十有八九都不会
- 截至 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
- B站获取弹幕的原始代码
- jsc-vendors_player.js
- B站解析弹幕的原始代码
- formatted.jsc-player.js
- 格式化后代码
- formatted.jsc-vendors~player.js
- 格式化后代码
获取弹幕原始数据并调用解析函数的代码主要在 jsc-player.js 的 第43130行 到 第43150行
解析弹幕的代码主要在 jsc-vendors~player.js 的 第4212行 到 第4426行
我当时是从 jsc-player.js 第43139行 开始,此处是获取解析后的数据(即已经整理好弹幕内容、时间之类的,可直接使用)。然后回溯。最终找到所需的代码。
不要直接复制粘贴运行,请将代码内的 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