# 弹幕元数据 | BAC Document
社区开源的第三方哔哩哔哩 API 文档

# 弹幕元数据

2020-09-25 B站更新了互动弹幕功能，包括UP主头像弹幕、关联视频弹幕、视频内嵌引导关注按钮三大功能

详情见：

- 【客户端更新】6.10版本更新！UP主支持发布关联视频弹幕
- 引导关注卡片

## 获取弹幕个人配置与互动弹幕及BAS（代码）弹幕专包（web端）

> https://api.bilibili.com/x/v2/dm/web/view

请求方式：GET

认证方式：仅可Cookie（SESSDATA）

url参数：

参数名 | 类型 | 内容 | 必要性 | 备注
--- | --- | --- | --- | ---
type | num | 弹幕类 | 必要 | 1：视频弹幕
oid | num | 视频cid | 必要 |
pid | num | 稿件avid | 非必要 |

proto回复：

消息`DmWebViewReply`：

名称 | 类型 | 含义 | 备注
--- | --- | --- | ---
state | int32 | 弹幕开放状态 | 0：开放弹幕 1：禁止弹幕
text | string | ？ |
textSide | string | ？ |
dmSge | DmSegConfig | 分段弹幕包信息？ |
flag | DanmakuFlagConfig | ？ |
specialDms | repeated string | BAS（代码）弹幕专包url |
checkBox | bool | ？ |
count | int64 | 实际弹幕总数 | 具有1500-6000不等的上限
commandDms | repeated CommandDm | 互动弹幕条目 |
dmSetting | DanmuWebPlayerConfig | 弹幕个人配置 | 仅登录后存在

消息`dmSge`：

名称 | 类型 | 含义 | 备注
--- | --- | --- | ---
pageSize | int64 | 分段时间？ | 单位为毫秒
total | int64 | 最大分页数？ |

消息`flag`：

名称 | 类型 | 含义 | 备注
--- | --- | --- | ---
recFlag | int32 | ？ |
recText | string | ？ |
recSwitch | int32 | ？ |

消息`commandDms`：

名称 | 类型 | 含义 | 备注
--- | --- | --- | ---
id | int64 | 弹幕dmid |
oid | int64 | 视频cid |
mid | int64 | 发送者mid |
command | string | 弹幕指令 | `#UP#`：UP主头像弹幕 `#LINK#`：关联视频弹幕 `#ATTENTION#`：视频内嵌引导关注按钮
content | string | 弹幕文字 |
progress | int32 | 弹幕出现时间 | 单位为毫秒
ctime | string | 创建时间？ | 此项为空
mtime | string | 修改时间？ | 此项为空
extra | string | 弹幕负载数据 | json序列字串
idStr | string | 弹幕dmid | 字串形式

`extra`json序列：

类型为【UP主头像弹幕】时：

字段 | 类型 | 内容 | 备注
--- | --- | --- | ---
icon | str | UP主头像url |

类型为【关联视频弹幕】时：

字段 | 类型 | 内容 | 备注
--- | --- | --- | ---
aid | num | 关联视频的avid |
title | str | 关联视频的标题 |
icon | str | 关联视频按钮图片url |
bvid | str | 关联视频的bvid |

类型为【视频内嵌引导关注按钮】时：

字段 | 类型 | 内容 | 备注
--- | --- | --- | ---
duration | num | 持续时间 | 单位为毫秒
posX | num | X坐标 | 区间：[118-549]
posY | num | Y坐标 | 区间：[82-293]
icon | str | 按钮图片url | 不应该是关注按钮吗，但这个是圆形的
type | num | 关注按钮类型 | 0：仅关注 1：仅三连 2：关注+三连

消息`dmSetting`：

名称 | 类型 | 含义 | 备注
--- | --- | --- | ---
dmSwitch | bool | 弹幕开关 | true：开启 false：关闭
aiSwitch | bool | 智能云屏蔽 | 同上
aiLevel | int32 | 智能云屏蔽级别 | 区间：[1-10]
blocktop | bool | 屏蔽类型-顶部 | true：不屏蔽 false：屏蔽
blockscroll | bool | 屏蔽类型-滚动 | 同上
blockbottom | bool | 屏蔽类型-底部 | 同上
blockcolor | bool | 屏蔽类型-彩色 | 同上
blockspecial | bool | 屏蔽类型-特殊 | 同上
preventshade | bool | 防挡弹幕（底部15%） | true：开启 false：关闭
dmask | bool | 智能防挡弹幕（人像蒙版） | 同上
opacity | float | 弹幕不透明度 | 区间：[0-1]
dmarea | int32 | 弹幕显示区域 | 100：不重叠 75：3/4屏 50：半瓶 25：1/4屏 0：不限
speedplus | float | 弹幕速度 | 区间：[0.4-1.6]
fontsize | float | 字体大小 | 区间：[0.4-1.6]
screensync | bool | 跟随屏幕缩放比例 |
speedsync | bool | 根据播放倍速调整速度 |
fontfamily | string | 字体类型？ | 未启用
bold | bool | 粗体？ | 未启用
fontborder | int32 | 描边类型 | 0：重墨 1：描边 2：45°投影
drawType | string | 渲染类型？ | 未启用

protobuf结构体：

web_dmview.proto

    syntax = "proto3";

    //分段弹幕包信息？
    message DmSegConfig {
        int64 pageSize = 1; //分段时间？
        int64 total = 2;    //最大分页数？
    }

    //
    message DanmakuFlagConfig {
        int32 recFlag = 1;   //
        string recText = 2;  //
        int32 recSwitch = 3; //
    }

    // 互动弹幕条目
    message CommandDm {
        int64 id = 1;       //弹幕dmid
        int64 oid = 2;      //视频cid
        int64 mid = 3;      //发送者mid
        string command = 4; //弹幕指令
        string content = 5; //弹幕文字
        int32 progress = 6; //弹幕出现时间
        string ctime = 7;   //
        string mtime = 8;   //
        string extra = 9;   //弹幕负载数据
        string idStr = 10;  //弹幕dmid（字串形式）
    }

    //弹幕个人配置
    message DanmuWebPlayerConfig{
        bool dmSwitch=1;      //弹幕开关
        bool aiSwitch=2;      //智能云屏蔽
        int32 aiLevel=3;      //智能云屏蔽级别
        bool blocktop=4;      //屏蔽类型-顶部
        bool blockscroll=5;   //屏蔽类型-滚动
        bool blockbottom=6;   //屏蔽类型-底部
        bool blockcolor=7;    //屏蔽类型-彩色
        bool blockspecial=8;  //屏蔽类型-特殊
        bool preventshade=9;  //防挡弹幕（底部15%）
        bool dmask=10;        //智能防挡弹幕（人像蒙版）
        float opacity=11;     //弹幕不透明度
        int32 dmarea=12;      //弹幕显示区域
        float speedplus=13;   //弹幕速度
        float fontsize=14;    //字体大小
        bool screensync=15;   //跟随屏幕缩放比例
        bool speedsync=16;    //根据播放倍速调整速度
        string fontfamily=17; //字体类型？
        bool bold=18;         //粗体？
        int32 fontborder=19;  //描边类型
        string drawType=20;   //渲染类型？
    }

    message DmWebViewReply {
        int32 state = 1;                     //弹幕开放状态
        string text = 2;                     //
        string textSide = 3;                 //
        DmSegConfig dmSge = 4;               //分段弹幕包信息？
        DanmakuFlagConfig flag = 5;          //
        repeated string specialDms = 6;      //BAS（代码）弹幕专包url
        bool checkBox = 7;                   //
        int64 count = 8;                     //实际弹幕总数
        repeated CommandDm commandDms = 9;   //互动弹幕条目
        DanmuWebPlayerConfig dmSetting = 10; //弹幕个人配置
    }

示例：

获取视频`av797164471(cid=236871317)`的弹幕元数据

    curl -G 'https://api.bilibili.com/x/v2/dm/web/view' \
    --data-urlencode 'type=1' \
    --data-urlencode 'oid=236871317' \
    --data-urlencode 'pid=797164471' \
    -b 'SESSDATA=xxx' \
    -o 'danmaku_view.bin'

响应正文为protubuf二进制数据

## 实例

### 获取互动弹幕

获取并显示视频`av797164471(cid=236871317)`的所有互动弹幕

    import web_dmview_pb2
    import requests

    AVID = 797164471
    CID = 236871317
    url = f'https://api.bilibili.com/x/v2/dm/web/view?type=1&oid={CID}&pid={AVID}'

    data = requests.get(url)
    target = web_dmview_pb2.DmWebViewReply()
    target.ParseFromString(data.content)

    print(f'互动弹幕数={len(target.commandDms)}')
    for i in target.commandDms:
    	print(f'''\
    ---弹幕ID={i.id}
    ---视频cid={i.oid}
    ---发送者mid={i.mid}
    ---弹幕指令={i.command}
    ---弹幕文字={i.content}
    ---弹幕出现时间={i.progress}
    ---弹幕负载数据={i.extra}
    ---弹幕ID（字串）={i.idStr}'''
    )

输出为：

    互动弹幕数=1
    ---弹幕ID=38469676112019463
    ---视频cid=236871317
    ---发送者mid=501183549
    ---弹幕指令=#UP#
    ---弹幕文字=这个视频没有恰饭！别紧张！
    ---弹幕出现时间=157818
    ---弹幕负载数据={"icon":"https://i1.hdslb.com/bfs/face/559abe31f561f71f3106d8ee7b2065cac50c1235.jpg"}
    ---弹幕ID（字串）=38469676112019463

### 获取BAS（代码）弹幕专包

BAS弹幕（`pool=2` `mode=9`）只能从此包获取，代码弹幕（`pool=2` `mode=8`）也能从此包获取

获取并显示视频`av2(cid=62131)`的所有BAS（代码）弹幕专包

    import web_dmview_pb2
    import requests

    AVID = 2
    CID = 62131
    url = f'https://api.bilibili.com/x/v2/dm/web/view?type=1&oid={CID}&pid={AVID}'

    data = requests.get(url)
    target = web_dmview_pb2.DmWebViewReply()
    target.ParseFromString(data.content)

    print(f'特殊弹幕包数={len(target.specialDms)}')
    for i in target.specialDms:
    	print(f'特殊弹幕包url={i}')

输出为：

    特殊弹幕包数=1
    特殊弹幕包url=https://i0.hdslb.com/bfs/dm/b0d5f08c12be59292aa0d4e09b6dd8e54c2ba886.bin

使用普通分段包弹幕的proto结构体反序列化此bin数据