智能防挡弹幕 (#659)
* 智能防挡弹幕 * Create player.md * Update webmask.md * style: webmask.md --------- Co-authored-by: z0z0r4 <z0z0r4@outlook.com>
This commit is contained in:
parent
a4132acd0a
commit
1d557be039
80
docs/danmaku/webmask.md
Normal file
80
docs/danmaku/webmask.md
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# 智能防挡弹幕
|
||||||
|
|
||||||
|
B 站部分视频提供“智能防挡弹幕”功能,其原理是提供了一个 webmask 二进制文件,其中保存了视频中各个位置的 svg 格式蒙版。
|
||||||
|
|
||||||
|
首先需要获取 webmask 资源的地址。
|
||||||
|
|
||||||
|
## 获取 webmask 资源地址
|
||||||
|
|
||||||
|
通过 [web 播放器资源接口](../video/player.md) 获取 webmask 二进制文件的地址。
|
||||||
|
|
||||||
|
|
||||||
|
## webmask 资源
|
||||||
|
|
||||||
|
获取的 url 没有权鉴,不需要后面的参数也可以获取。
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -G https://upos-sz-staticcos-cmask.bilivideo.com/cmaskboss/825851971_30_0.webmask >> 825851971_30_0.webmask
|
||||||
|
```
|
||||||
|
|
||||||
|
下载后是二进制文件。
|
||||||
|
|
||||||
|
|
||||||
|
## webmask 二进制读取
|
||||||
|
|
||||||
|
参考:[andrewvy/webmask-renderer](https://github.com/andrewvy/webmask-renderer)
|
||||||
|
|
||||||
|
name | offset | length | type | desc
|
||||||
|
--- | ------ | ------ | ---- | ----
|
||||||
|
mask | 0 | 4 | char | 'MASK' 文件头
|
||||||
|
version| 4 | 4 | int | 是1
|
||||||
|
vU | 8 | 4 | ? | 不知道是干什么的
|
||||||
|
Ly | 12 | 4 | int | 后续数据的段数
|
||||||
|
time_1 | 16 | 8 | long | 第一段对应视频开始时间
|
||||||
|
offset_1 | 24 | 8 | long | 第一段蒙版信息开始处对应二进制偏移
|
||||||
|
...|...|...|...|...
|
||||||
|
time_{Ly} |16+(Ly-1)*16| 8 | long | 第 `Ly` 段对应视频开始时间
|
||||||
|
offset_{Ly} |24+(Ly-1)*16| 8 | long | 第 `Ly` 段蒙版信息开始处对应二进制偏移
|
||||||
|
segments_1| 由前面offset_1提供 | 由 `offset_2-offset_1` 计算得到 | binary | 蒙版信息块,使用 gzip 压缩
|
||||||
|
...|...|...|...|...
|
||||||
|
|
||||||
|
|
||||||
|
蒙版信息块是经过 gzip 压缩文本得到的二进制。解压缩后得到一个字节串。
|
||||||
|
|
||||||
|
前 16 字节是两个 long,记为 `left` 和 `right`,暂时不清楚其作用。`left` 似乎和平均每张蒙版的时间有关。`right` 总是 `i*10000`,`i` 是从 0 开始数的数据段次序。
|
||||||
|
|
||||||
|
后面是各个 svg 文本直接拼起来,可以直接通过 svg 格式头 `data:image/svg+xml;base64,` 分开。
|
||||||
|
|
||||||
|
|
||||||
|
Python 示例:
|
||||||
|
```python
|
||||||
|
from struct import unpack
|
||||||
|
import gzip
|
||||||
|
|
||||||
|
f = open('你的 webmask', 'rb')
|
||||||
|
buf = f.read()
|
||||||
|
_Ly = buf[12:16]
|
||||||
|
Ly = unpack('>i', _Ly)[0] # 大端序 int
|
||||||
|
|
||||||
|
times = []
|
||||||
|
offsets = []
|
||||||
|
for idx in range(Ly):
|
||||||
|
op = 16 + idx * 16 # 个人习惯,我算偏移时喜欢用 `op` 和 `ed` 作为开始和结束的名字。
|
||||||
|
time = unpack('>q', buf[op: op+8])[0]
|
||||||
|
offset = unpack('>q', buf[op+8: op+16])[0]
|
||||||
|
times.append(time)
|
||||||
|
offsets.append(offset)
|
||||||
|
|
||||||
|
frames = []
|
||||||
|
for idx in range(Ly):
|
||||||
|
op = offsets[idx]
|
||||||
|
if idx == Ly - 1: ed = len(buf)
|
||||||
|
else: ed = offsets[idx+1]
|
||||||
|
ba = buf[op: ed]
|
||||||
|
bad = gzip.decompress(ba)
|
||||||
|
badl = bad.split(b'data:image/svg+xml;base64,')
|
||||||
|
# badl[0]是16字节,`left` 和 `right`
|
||||||
|
frames.append(badl[1: ])
|
||||||
|
```
|
||||||
153
docs/video/player.md
Normal file
153
docs/video/player.md
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
# web 播放器信息
|
||||||
|
|
||||||
|
web 播放器的信息接口,提供正常播放需要的元数据,包括:智能防挡弹幕、字幕、章节看点等。
|
||||||
|
|
||||||
|
> https://api.bilibili.com/x/player/wbi/v2
|
||||||
|
|
||||||
|
*请求方式:GET*
|
||||||
|
|
||||||
|
**url 参数:**
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 内容 | 必要性 | 备注 |
|
||||||
|
| ------ | ---- | --------- | ----------- | ----------------- |
|
||||||
|
| aid | num | 稿件 avid | 必要 (可选) | aid 与 bvid 任选 |
|
||||||
|
| bvid | str | 稿件 bvid | 必要 (可选) | aid 与 bvid 任选 |
|
||||||
|
| cid | num | 稿件 cid | 必要 | |
|
||||||
|
| w_rid | str | 未知 | 不必要 | |
|
||||||
|
| wts | num | 当前 unix 时间戳 | 不必要 | |
|
||||||
|
|
||||||
|
|
||||||
|
**json 回复:**
|
||||||
|
|
||||||
|
根对象:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 内容 | 备注 |
|
||||||
|
| ------- | ---- | -------- | --------------------------- |
|
||||||
|
| code | num | 返回值 | 0:成功<br />-400:请求错误 |
|
||||||
|
| message | str | 错误信息 | 默认为 0 |
|
||||||
|
| ttl | num | 1 | |
|
||||||
|
| data | obj | 数据本体 | |
|
||||||
|
|
||||||
|
`data` 对象:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 内容 | 备注 |
|
||||||
|
| --------- | ----- | -------- | ---- |
|
||||||
|
|aid | num | 视频 aid | |
|
||||||
|
|bvid | str | 视频 bvid | |
|
||||||
|
|cid | num | 视频 cid | |
|
||||||
|
|dm_mask | obj | | webmask 信息(如果没有这一项,说明这个视频没有防挡功能) |
|
||||||
|
|subtitle | obj | | 字幕信息(需要登录,不登录此项内容为 `[]` )|
|
||||||
|
|view_points| array | | 章节看点信息 |
|
||||||
|
| 其他 | ... | | 主要是观看记录、使用者等级权限等个人信息 |
|
||||||
|
|
||||||
|
|
||||||
|
`dm_mask`对象(如果有):
|
||||||
|
|
||||||
|
| 字段 | 类型 | 内容 | 备注 |
|
||||||
|
| --------- | ----- | -------- | ---- |
|
||||||
|
|cid | num | 视频 cid | |
|
||||||
|
|plat | num | 未知 | |
|
||||||
|
|fps | num | webmask 取样 fps | |
|
||||||
|
|time | num | 未知 | |
|
||||||
|
|mask_url | str | webmask 资源 url | |
|
||||||
|
|
||||||
|
解析 webmask 请看 [智能防挡弹幕](../danmaku/webmask.md)
|
||||||
|
|
||||||
|
`subtitle`对象:
|
||||||
|
| 字段 | 类型 | 内容 | 备注 |
|
||||||
|
| --------- | ----- | -------- | ---- |
|
||||||
|
|allow_submit|bool | true | |
|
||||||
|
| lan | str | "" | |
|
||||||
|
|lan_doc | str | "" | | |
|
||||||
|
|subtitles| array | | 不登录为 `[]` |
|
||||||
|
|
||||||
|
`subtitles` 数组内的元素:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 内容 | 备注 |
|
||||||
|
| --------- | ----- | -------- | ---- |
|
||||||
|
| ai_status | num | | |
|
||||||
|
| ai_type | num | | |
|
||||||
|
| id | num | | |
|
||||||
|
|id_str | str| | 和 id 不一样 |
|
||||||
|
| is_lock | bool | |
|
||||||
|
| lan | str | 语言类型英文字母缩写 ||
|
||||||
|
| lan_doc | str| 语言类型中文名称 | |
|
||||||
|
|subtitle_url|str| 资源 url 地址 | |
|
||||||
|
|type| num | 0 | |
|
||||||
|
|
||||||
|
|
||||||
|
`view_point` 数组内的元素:
|
||||||
|
| 字段 | 类型 | 内容 | 备注 |
|
||||||
|
| --------- | ----- | -------- | ---- |
|
||||||
|
| content | num | 章节名 | |
|
||||||
|
| from | num | | |
|
||||||
|
| to | num | | |
|
||||||
|
| type | num | | |
|
||||||
|
| imgUrl | str | 图片资源地址 | |
|
||||||
|
| logoUrl | str | "" | |
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -G https://api.bilibili.com/x/player/wbi/v2?aid=515345690&cid=825851971
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "0",
|
||||||
|
"ttl": 1,
|
||||||
|
"data": {
|
||||||
|
"aid": 515345690,
|
||||||
|
"bvid": "BV1Fg411D7Jy",
|
||||||
|
... // 省略
|
||||||
|
"dm_mask": {
|
||||||
|
"cid": 825851971,
|
||||||
|
"plat": 0,
|
||||||
|
"fps": 30,
|
||||||
|
"time": 0,
|
||||||
|
"mask_url": "//upos-sz-staticcos-cmask.bilivideo.com/cmaskboss/825851971_30_0.webmask?trid=219266863a1442baa05086b4285ba923B&orderid=0,1&logo=00000000"
|
||||||
|
},
|
||||||
|
"view_points": [
|
||||||
|
{
|
||||||
|
"type": 2,
|
||||||
|
"from": 0,
|
||||||
|
"to": 27,
|
||||||
|
"content": "狗啃的",
|
||||||
|
"imgUrl": "http://i0.hdslb.com/bfs/vchapter/825851971_0.jpg",
|
||||||
|
"logoUrl": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 2,
|
||||||
|
"from": 27,
|
||||||
|
"to": 63,
|
||||||
|
"content": "椒牌泡菜",
|
||||||
|
"imgUrl": "http://i0.hdslb.com/bfs/vchapter/825851971_27.jpg",
|
||||||
|
"logoUrl": ""
|
||||||
|
}, ... // 省略
|
||||||
|
],
|
||||||
|
"subtitle": {
|
||||||
|
"allow_submit": true,
|
||||||
|
"lan": "",
|
||||||
|
"lan_doc": "",
|
||||||
|
"subtitles": [], // 未登录,下面是登录的版本
|
||||||
|
"subtitles":[
|
||||||
|
{
|
||||||
|
"id": 1042985852759993300,
|
||||||
|
"lan": "ai-zh",
|
||||||
|
"lan_doc": "中文(自动生成)",
|
||||||
|
"is_lock": false,
|
||||||
|
"subtitle_url": "//aisubtitle.hdslb.com/bfs/ai_subtitle/prod/5153456908258519712094280c7c2884b77929bab82f64530f?auth_key=1714795727-a8eb254b60bc4a73bc8662da51005340-0-1c305894e48e959979b163636461fb8f",
|
||||||
|
"type": 1,
|
||||||
|
"id_str": "1042985852759993344",
|
||||||
|
"ai_type": 0,
|
||||||
|
"ai_status": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user