XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/bilibili-API-collect

add grpc docs (#741)

f5263d0
陈寒彤 <70561268+cxw620@users.noreply.github.com>
提交于

代码差异

5 个文件 +244 -29
Added docs/misc/device_identity.md +104 -0
@@ -0,0 +1,104 @@
1 # 设备各类标识算法(APP 端)
2
3 ## 设备唯一标识 BUVID
4
5 注意区分于 Web 端的 buvid3, buvid4.
6
7 BUVID 在 APP 首次安装于某设备, 且首次启动时生成.
8
9 APP 首次(即每次安装后)启动, 会向云端发送本机各类设备特征, 含 `AndroidId`, `DrmId` 等, 请求是否有匹配的 BUVID, 有就使用云端的, 否则使用本地生成的.
10
11 APP 请求是否有匹配的 BUVID 发送的本机各类设备特征包括(但不限于):
12
13 + `AndroidID`
14 + `DrmId`
15 + `IMEI`
16 + `OAID`
17 + 手机网卡 `MAC`
18 + 设备品牌
19 + 设备 Model
20 + 本地生成的 BUVID
21
22 ### 生成方法
23
24 1. 选定设备特征码, 可以是 `AndroidID`, `DrmId`, 手机网卡 `MAC` 等. 记为 `ID`. 特别地, `MAC` 应当去掉 `:`, `GUID`(即 UUID) 应当去掉 `-`.
25
26 2. 计算 `ID` 的 MD5. 记为 `ID_MD5`.
27
28 3. 从 `ID_MD5` 抽取第 3, 13, 23 位, 失败就默认为 000, 记为 `ID_E`.
29
30 4. 根据选定的设备特征码类型确定 BUVID Prefix, 见附录. 记为 `BUVID_Prefix`.
31
32 5. 按 `{BUVID_Prefix}{ID_E}{ID_MD5}` 的顺序连接起来, 共37位(2+3+32). 结果应当为大写.
33
34 ### Demo
35
36 #### Rust
37
38 代码及测试样例见 [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=40b5906cf3838a60efa83fa368b15147).
39
40 ## 设备指纹 fp (fp_local, fp_remote)
41
42 用于请求账户相关 REST API, 及 gRPC Metadata 生成.
43
44 在请求头中, `fp_local` 和 `fp_remote` 设置为同一值即可, 暂不清楚区别.
45
46 ### 生成方法
47
48 1. 获取 BUVID. 此处一般使用 XU Prefix 的 BUVID.
49
50 2. 获取设备 Model(`Build.MODEL`), 如 `NOH-AN01`.
51
52 3. 获取手机无线电固件版本号(`Build.getRadioVersion()`), 失败则留空. 如 `21C20B686S000C000,21C20B686S000C000`.
53
54 4. 按前述顺序拼接字符串, 计算得 MD5.
55
56 5. 获取年月日, 格式 `yyyyMMddhhmmss`, 拼接到 4 得到的字符串后.
57
58 6. 生成 16 位随机字符串, CharSet 为 `0123456789abcdef`, 拼接到 5 得到的字符串后, 记为 `fp_raw`.
59
60 7. 计算得到一个特殊字符串, 拼接到 `fp_raw` 后, 即得到最终的 `fp`, 特殊字符串算法见下:
61
62 ```rust
63 let mut veri_code = 0;
64 // 有点像 HEX 的操作
65 let fp_raw_sub_str = fp_raw
66 .as_bytes() // 将字符串 fp_raw 转换为字节数组
67 .chunks(2) // 按每两个字节一组进行切分
68 .map(|s| unsafe { ::std::str::from_utf8_unchecked(s) }) // 对每一组解析作为 UTF-8 字符串
69 .collect::<Vec<_>>(); // 将结果收集到 Vec 中
70 // 如果 fp_raw 的长度小于 62, 则向下取偶数减半作为循环终止条件, 否则终止条件为31
71 for i in 0..({
72 if fp_raw.len() < 62 {
73 fp_raw.len() - fp_raw.len() % 2 // 取偶数
74 } else {
75 62
76 }
77 } / 2)
78 {
79 // 将每组字符串转换为对应的 16 进制整数, 将转换得到的整数加到 veri_code 上.
80 veri_code += i32::from_str_radix(fp_raw_sub_str[i], 16).unwrap_or(0);
81 }
82 // 最后将 veri_code 对 256 取余, 格式化为两位的 16 进制字符串
83 let veri_code = format!("{:0>2x}", veri_code % 256);
84 ```
85
86 ### Demo
87
88 #### Rust
89
90 代码及测试样例见 [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=40b5906cf3838a60efa83fa368b15147).
91
92 ## 附录
93
94 ### BUVID Prefix
95
96 |设备特征码|BUVID Prefix|备注|
97 |:-:|:-:|:-:|
98 |`AndroidID`|`XX`||
99 |`DrmId`|`XU`||
100 |`IMEI`|`XZ`|已弃用|
101 |`GUID`|`XW`|已弃用|
102 |`MAC`|`XY`||
103 |`GoogleId`|`XG`|东南亚版本|
104 |`FacebookId`|`XF`|东南亚版本|
Modified grpc_api/bilibili/metadata/fawkes/fawkes.proto +3 -3
@@ -12,10 +12,10 @@ message FawkesReply {
12 12
13 13 //
14 14 message FawkesReq {
15 // 客户端在fawkes系统的唯一名
15 // 客户端在fawkes系统的唯一名, 如 `android64`
16 16 string appkey = 1;
17 // 客户端在fawkes系统中的环境参数
17 // 客户端在fawkes系统中的环境参数, 如 `prod`
18 18 string env = 2;
19 // 启动id
19 // 启动id, 32 位 0~9, a~z 组成的字符串
20 20 string session_id = 3;
21 21 }
Modified grpc_api/bilibili/metadata/metadata.proto +7 -7
@@ -5,18 +5,18 @@ package bilibili.metadata;
5 5 // 请求元数据
6 6 // gRPC头部:x-bili-metadata-bin
7 7 message Metadata {
8 // 登录Token
8 // 登录 access_key
9 9 string access_key = 1;
10 // 包类型
10 // 包类型, 如 `android`
11 11 string mobi_app = 2;
12 // 运行设备
12 // 运行设备, 留空即可
13 13 string device = 3;
14 // 构建id
14 // 构建id, 如 `7380300`
15 15 int32 build = 4;
16 // APP分发渠道
16 // APP分发渠道, 如 `master`
17 17 string channel = 5;
18 // 设备buvid
18 // 设备唯一标识
19 19 string buvid = 6;
20 // 平台类型
20 // 平台类型, 如 `android`
21 21 string platform = 7;
22 22 }
Renamed grpc_api/bilibili/metadata/parabox/parabox.proto +0 -0
此文件没有可显示的逐行差异。
Modified grpc_api/readme.md +130 -19
@@ -1,38 +1,149 @@
1 # grpc 接口定义(protobuf 结构体)
1 # gRPC 接口定义(protobuf 结构体)
2 2
3 3 注:
4 4
5 1. proto 结构体文件按照包名分类,同级放在同一目录中
5 1. proto 结构体文件按照包名分类, 同级放在同一目录中
6 6
7 2. 暂时无说明文档,稍后添加
7 2. gRPC 接口定义全部来自对官方粉版(即大陆版本) APP 的逆向工程, 一般不会有错误, 但是可能有更新, 有实际应用需求的建议自行反编译 APP, 定位到 `com.bapis.*` 自行补足.
8 8
9 3. 以下文件全部来自 apk 的逆向工程,如有疏漏请包涵
9 ## gRPC 主机
10 10
11 ## grpc 主机
11 B 站客户端的 gRPC 接口主机包括:
12 12
13 B 站客户端的 grpc 接口主机为以下服务器
13 + `grpc.biliapi.net` 原生 gRPC 接口
14 + `app.bilibili.com` Failover gRPC 接口
14 15
15 > grpc.biliapi.net
16 >
17 > app.bilibili.com
16 实际应用中, 后者速度相对更快. 但是需要设置如 gRPC 超时时间等参数时只能使用前者.
18 17
19 ## grpc 鉴权
18 ## gRPC 鉴权
20 19
21 需要在请求 http 头部中添加`access_key`,如下
20 需要在 Metadata 中添加 `authorization`: `identify_v1 {access_key}`.
22 21
23 ```
24 authorization:identify_v1 {access_key}
25 ```
22 ## gRPC Metadata
23
24 参考 [gRPC Go 官方文档](https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md) 对 `Metadata` 的说明.
25
26 gRPC 的 `Metadata` 简单理解,就是 HTTP 的 Header 中的 key-value 对, 本质上是一个 Map. 在 gRPC `Metadata` 中,key 永远是 String,但是 value 可以是 String 也可以是二进制数据. **需要存储二进制数据时, key 应当加上一个 `-bin` 后缀, 同时二进制 value 应当编码为 Base64**.
26 27
27 ## grpc 头部
28 一般而言, 设定 Binary 类型的 `Metadata` 时, 需要调用各个语言的 gRPC 库的相应方法, 库会帮我们编码二进制数据, 无需我们自行编码.
29
30 需要的 `Metadata` 包括(但不限于):
31
32 + Ascii 类
33 + `user-agent` 客户端 UA, 如 `Dalvik/2.1.0 (Linux; U; Android 12; {device_model} Build/{device_build}) {app_ver} os/android model/{device_model} mobi_app/{mobi_app} build/{app_build} channel/master innerVer/{app_build_inner} osVer/12 network/2 grpc-java-cronet/1.36.1`(其中 `grpc-java-cronet/1.36.1` 为原生 gRPC 接口才需要的). **必需**.
34 + `device_model` 设备 Model, 如 `NOH-AN01`.
35 + `device_build` 设备 Build, 如 `HUAWEINOH-AN01`.
36 + `app_ver` APP 版本号, 如 `7.38.0`.
37 + `mobi_app` APP 包类型, 参考 [APPKey.md](/docs/misc/sign/APPKey.md).
38 + `app_build` APP 版本号, 如 `7380300`.
39 + `app_build_inner` APP 版本号(内部), 如 `7380310`. 实际应用中设置为 `app_build` 即可.
40 + `x-bili-gaia-vtoken` 暂时留空.
41 + `x-bili-aurora-eid` 如 `UFUFQ1AA`. 算法见附录. 未登录留空. **必需**.
42 + `x-bili-mid` 用户 UID, 未登录默认为 0. **必需**.
43 + `x-bili-aurora-zone` 留空. **必需**.
44 + `x-bili-trace-id` 如 `06e903399574695df75be114ff63ac64:f75be114ff63ac64:0:0`. 算法见附录. **必需**.
45 + `authorization` 鉴权, 登录时设定为 `identify_v1 {access_key}`, 未登录时无需此项.
46 + `buvid` 设备唯一标识, 算法见 [device_identity.md](/docs/misc/device_identity.md). **必需(?)**.
47 + `bili-http-engine` 恒定为 `cronet`, 使用 `grpc.biliapi.net` 作为 gRPC 主机时无需此项.
48 + `te` 恒定为 `trailers`, Java gRPC 库固定添加, 使用 `app.bilibili.com` 作为 gRPC 主机时无需此项.
49 + Binary 类
50 + `x-bili-fawkes-req-bin` 设备 Fawkes 信息, 使用 [FawkesReq](bilibili/metadata/fawkes/fawkes.proto) 生成. **必需**.
51 + `x-bili-metadata-bin` 使用 [Metadata](bilibili/metadata/metadata.proto) 生成. **必需**.
52 + `x-bili-device-bin` 设备信息, 使用 [Device](bilibili/metadata/device/device.proto) 生成. **必需**.
53 + `x-bili-network-bin` 设备网络信息, 使用 [Network](bilibili/metadata/network/network.proto) 生成. **必需**.
54 + `x-bili-restriction-bin` 限制信息, 使用 [Restriction](bilibili/metadata/restriction/restriction.proto) 生成. 本项一般直接传空值即可. **必需**.
55 + `x-bili-locale-bin` 设备区域信息, 使用 [Locale](bilibili/metadata/locale/locale.proto) 生成. **必需**.
56 + `x-bili-exps-bin` 使用 [Exps](bilibili/metadata/pararbox/pararbox.proto) 生成. 本项一般直接传空值即可. **必需**.
28 57
29 - [bilibili.metadata](bilibili/metadata):客户端环境参数
30 - [bilibili.rpc](bilibili/rpc/status.proto):响应错误信息
31 58
32 59 ## 接口请求定义
33 60
34 _稍后补充_
61 等待补充, 参见 proto 文件注释. 以下仅介绍常用接口:
62
63 + [bilibili.app.playeronline.v1 -> PlayerOnline](bilibili/app/playeronline/v1/playeronline.proto) 视频在线人数接口.
64 + [bilibili.app.playerunite.v1 -> PlayViewUnite](bilibili/app/playerunite/v1/playerunite.proto) United 视频播放链接接口(同时适用于 PGC, UGC 视频).
65 + [bilibili.app.playurl.v1 -> PlayURL](bilibili/app/playurl/v1/playurl.proto) UGC 视频播放链接接口(V1 版本).
66 + [bilibili.pgc.gateway.player.v1 -> PlayView](bilibili/pgc/gateway/player/v1/playurl.proto) PGC 视频播放链接接口(V1 版本).
67 + [bilibili.pgc.gateway.player.v2 -> PlayView](bilibili/pgc/gateway/player/v2/playurl.proto) PGC 视频播放链接接口(V2 版本).
68 + [bilibili.polymer.app.search.v1 -> SearchAll, etc](bilibili/polymer/app/search/v1/search.proto) 搜索接口(V1 版本).
69 + [bilibili.app.dynamic.v2 -> DynAll, etc](bilibili/app/dynamic/v2/dynamic.proto) 动态接口(V2 版本).
70 + ...
35 71
36 ## 示例
72 ## 应用示例
73
74 ### Golang
37 75
38 76 B 站 gRPC API Golang 封装:[XiaoMiku01/bilibili-grpc-api-go](https://github.com/XiaoMiku01/bilibili-grpc-api-go)
77
78 ## 附录
79
80 <details>
81 <summary>点此展开</summary>
82
83 ### `x-bili-aurora-eid` 生成算法
84
85 ```rust
86 pub fn gen_aurora_eid(uid: u64) -> Option<String> {
87 if uid == 0 {
88 return None;
89 }
90 let mut result_byte = Vec::with_capacity(64);
91 // 1. 将 UID 字符串转为字节数组.
92 let mid_byte = uid.to_string().into_bytes();
93 // 2. 将字节数组逐位(记为第 i 位)与 b"ad1va46a7lza" 中第 (i % 12) 位进行异或操作, 作为结果数组第 i 位.
94 mid_byte.iter().enumerate().for_each(|(i, v)| {
95 result_byte.push(v ^ (b"ad1va46a7lza"[i % 12]))
96 });
97 // 3. 对字节数组执行 Base64 编码, 注意 no padding, 即得到 x-bili-aurora-eid.
98 Some(base64::Engine::encode(
99 &base64::engine::general_purpose::STANDARD_NO_PAD,
100 result_byte,
101 ))
102 }
103 ```
104
105 ### `x-bili-trace-id` 生成算法
106
107 ```rust
108 pub fn gen_trace_id() -> String {
109 // 1. 生成 32 位随机字符串 random_id , Charset 为 0~9, a~z.
110 let random_id = gen_random_string!(32);
111 let mut random_trace_id = String::with_capacity(40);
112 // 2. 取 random_id 前 24 位, 作为 random_trace_id.
113 random_trace_id.push_str(&random_id[0..24]);
114 // 3. 初始化一个长度为 3 的数组 b_arr, 初始值都为 0.
115 let mut b_arr: [i8; 3] = [0i8; 3];
116 // 并获取当前时间戳
117 let mut ts = chrono::Local::now().timestamp();
118 // 使用循环从高位到低位遍历 b_arr 数组, 循环体内执行以下逻辑:
119 // - 首先将 ts 右移 8 位
120 // - 然后根据条件向 b_arr 的第 i 位赋值:
121 // - 如果 (ts / 128) % 2的结果为0, 则 b_arr[i] = ts % 256
122 // - 否则 b_arr[i] = ts % 256 - 256
123 for i in (0..3).rev() {
124 ts >>= 8;
125 b_arr[i] = {
126 if ((ts / 128) % 2) == 0 {
127 (ts % 256) as i8
128 } else {
129 (ts % 256 - 256) as i8
130 }
131 }
132 }
133 // 4. 将数组 b_arr 中的每个元素逐个转换为两位的十六进制字符串并追加到 random_trace_id 中.
134 for i in 0..3 {
135 random_trace_id.push_str(&format!("{:0>2x}", b_arr[i]))
136 }
137 // 5. 将 random_id 的第 31, 32 个字符追加到 random_trace_id 中, 此时 random_trace_id 生成完毕, 应当为 32 位长度.
138 random_trace_id.push_str(&random_id[30..32]);
139 // 6. 最后, 按 `{random_trace_id}:{random_trace_id[16..32]}:0:0` 的顺序拼接起来, 即为 x-bili-trace-id
140 let mut random_trace_id_final = String::with_capacity(64);
141 random_trace_id_final.push_str(&random_trace_id);
142 random_trace_id_final.push_str(":");
143 random_trace_id_final.push_str(&random_trace_id[16..32]);
144 random_trace_id_final.push_str(":0:0");
145 random_trace_id_final
146 }
147 ```
148
149 </details>