import{_ as i,c as r,a as l,b as a,d as n,e,w as p,r as t,o as c}from"./app-Dgsdh8A6.js";const D={};function d(y,s){const o=t("RouteLink");return c(),r("div",null,[s[25]||(s[25]=l('

gRPC 接口定义(protobuf 结构体)

注:

  1. proto 结构体文件按照包名分类, 同级放在同一目录中

  2. gRPC 接口定义全部来自对官方粉版(即大陆版本) APP 的逆向工程, 一般不会有错误, 但是可能有更新, 有实际应用需求的建议自行反编译 APP, 定位到 com.bapis.* 自行补足.

gRPC 主机

B 站客户端的 gRPC 接口主机包括:

实际应用中, 后者速度相对更快. 但是需要设置如 gRPC 超时时间等参数时只能使用前者.

gRPC 鉴权

需要在 Metadata 中添加 authorization: identify_v1 {access_key}.

gRPC Metadata

参考 gRPC Go 官方文档Metadata 的说明.

gRPC 的 Metadata 简单理解,就是 HTTP 的 Header 中的 key-value 对, 本质上是一个 Map. 在 gRPC Metadata 中,key 永远是 String,但是 value 可以是 String 也可以是二进制数据. 需要存储二进制数据时, key 应当加上一个 -bin 后缀, 同时二进制 value 应当编码为 Base64.

一般而言, 设定 Binary 类型的 Metadata 时, 需要调用各个语言的 gRPC 库的相应方法, 库会帮我们编码二进制数据, 无需我们自行编码.

需要的 Metadata 包括(但不限于):

',14)),a("ul",null,[a("li",null,[s[23]||(s[23]=n("Ascii 类 ")),a("ul",null,[a("li",null,[s[7]||(s[7]=a("code",null,"user-agent",-1)),s[8]||(s[8]=n(" 客户端 UA, 如 ")),s[9]||(s[9]=a("code",null,"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",-1)),s[10]||(s[10]=n("(其中 ")),s[11]||(s[11]=a("code",null,"grpc-java-cronet/1.36.1",-1)),s[12]||(s[12]=n(" 为原生 gRPC 接口才需要的). ")),s[13]||(s[13]=a("strong",null,"必需",-1)),s[14]||(s[14]=n(". ")),a("ul",null,[s[4]||(s[4]=l("
  • device_model 设备 Model, 如 NOH-AN01.
  • device_build 设备 Build, 如 HUAWEINOH-AN01.
  • app_ver APP 版本号, 如 7.38.0.
  • ",3)),a("li",null,[s[1]||(s[1]=a("code",null,"mobi_app",-1)),s[2]||(s[2]=n(" APP 包类型, 参考 ")),e(o,{to:"/docs/misc/sign/APPKey.html"},{default:p(()=>s[0]||(s[0]=[n("APPKey.md")])),_:1,__:[0]}),s[3]||(s[3]=n("."))]),s[5]||(s[5]=a("li",null,[a("code",null,"app_build"),n(" APP 版本号, 如 "),a("code",null,"7380300"),n(".")],-1)),s[6]||(s[6]=a("li",null,[a("code",null,"app_build_inner"),n(" APP 版本号(内部), 如 "),a("code",null,"7380310"),n(". 实际应用中设置为 "),a("code",null,"app_build"),n(" 即可.")],-1))])]),s[21]||(s[21]=l("
  • x-bili-gaia-vtoken 暂时留空.
  • x-bili-aurora-eidUFUFQ1AA. 算法见附录. 未登录留空. 必需.
  • x-bili-mid 用户 UID, 未登录默认为 0. 必需.
  • x-bili-aurora-zone 留空. 必需.
  • x-bili-trace-id06e903399574695df75be114ff63ac64:f75be114ff63ac64:0:0. 算法见附录. 必需.
  • authorization 鉴权, 登录时设定为 identify_v1 {access_key}, 未登录时无需此项.
  • ",6)),a("li",null,[s[16]||(s[16]=a("code",null,"buvid",-1)),s[17]||(s[17]=n(" 设备唯一标识, 算法见 ")),e(o,{to:"/docs/misc/device_identity.html"},{default:p(()=>s[15]||(s[15]=[n("device_identity.md")])),_:1,__:[15]}),s[18]||(s[18]=n(". ")),s[19]||(s[19]=a("strong",null,"必需(?)",-1)),s[20]||(s[20]=n("."))]),s[22]||(s[22]=l("
  • bili-http-engine 恒定为 cronet, 使用 grpc.biliapi.net 作为 gRPC 主机时无需此项.
  • te 恒定为 trailers, Java gRPC 库固定添加, 使用 app.bilibili.com 作为 gRPC 主机时无需此项.
  • ",2))])]),s[24]||(s[24]=l('
  • Binary 类
  • ',1))]),s[26]||(s[26]=l(`

    接口请求定义

    等待补充, 参见 proto 文件注释. 以下仅介绍常用接口:

    应用示例

    Golang

    B 站 gRPC API Golang 封装:XiaoMiku01/bilibili-grpc-api-go

    附录

    点此展开

    x-bili-aurora-eid 生成算法

    pub fn gen_aurora_eid(uid: u64) -> Option<String> {
        if uid == 0 {
            return None;
        }
        let mut result_byte = Vec::with_capacity(64);
        // 1. 将 UID 字符串转为字节数组.
        let mid_byte = uid.to_string().into_bytes();
        // 2. 将字节数组逐位(记为第 i 位)与 b"ad1va46a7lza" 中第 (i % 12) 位进行异或操作, 作为结果数组第 i 位.
        mid_byte.iter().enumerate().for_each(|(i, v)| {
            result_byte.push(v ^ (b"ad1va46a7lza"[i % 12]))
        });
        // 3. 对字节数组执行 Base64 编码, 注意 no padding, 即得到 x-bili-aurora-eid.
        Some(base64::Engine::encode(
            &base64::engine::general_purpose::STANDARD_NO_PAD,
            result_byte,
        ))
    }

    x-bili-trace-id 生成算法

    pub fn gen_trace_id() -> String {
        // 1. 生成 32 位随机字符串 random_id , Charset 为 0~9, a~z. 
        let random_id = gen_random_string!(32);
        let mut random_trace_id = String::with_capacity(40);
        // 2. 取 random_id 前 24 位, 作为 random_trace_id.
        random_trace_id.push_str(&random_id[0..24]);
        // 3. 初始化一个长度为 3 的数组 b_arr, 初始值都为 0.
        let mut b_arr: [i8; 3] = [0i8; 3];
        // 并获取当前时间戳
        let mut ts = chrono::Local::now().timestamp();
        // 使用循环从高位到低位遍历 b_arr 数组, 循环体内执行以下逻辑:
        //  - 首先将 ts 右移 8 位
        //  - 然后根据条件向 b_arr 的第 i 位赋值: 
        //    - 如果 (ts / 128) % 2的结果为0, 则 b_arr[i] = ts % 256
        //    - 否则 b_arr[i] = ts % 256 - 256
        for i in (0..3).rev() {
            ts >>= 8;
            b_arr[i] = {
                if ((ts / 128) % 2) == 0 {
                    (ts % 256) as i8
                } else {
                    (ts % 256 - 256) as i8
                }
            }
        }
        // 4. 将数组 b_arr 中的每个元素逐个转换为两位的十六进制字符串并追加到 random_trace_id 中.
        for i in 0..3 {
            random_trace_id.push_str(&format!("{:0>2x}", b_arr[i]))
        }
        // 5. 将 random_id 的第 31, 32 个字符追加到 random_trace_id 中, 此时 random_trace_id 生成完毕, 应当为 32 位长度.
        random_trace_id.push_str(&random_id[30..32]);
        // 6. 最后, 按 \`{random_trace_id}:{random_trace_id[16..32]}:0:0\` 的顺序拼接起来, 即为 x-bili-trace-id
        let mut random_trace_id_final = String::with_capacity(64);
        random_trace_id_final.push_str(&random_trace_id);
        random_trace_id_final.push_str(":");
        random_trace_id_final.push_str(&random_trace_id[16..32]);
        random_trace_id_final.push_str(":0:0");
        random_trace_id_final
    }
    `,8))])}const u=i(D,[["render",d]]),b=JSON.parse('{"path":"/grpc_api/","title":"gRPC 接口定义(protobuf 结构体)","lang":"zh-CN","frontmatter":{},"git":{"updatedTime":1689310001000,"contributors":[{"name":"SocialSisterYi","username":"SocialSisterYi","email":"45892418+SocialSisterYi@users.noreply.github.com","commits":4,"url":"https://github.com/SocialSisterYi"},{"name":"XiaoMiku01","username":"XiaoMiku01","email":"loveava0612@outlook.com","commits":1,"url":"https://github.com/XiaoMiku01"},{"name":"cxw620","username":"cxw620","email":"70561268+cxw620@users.noreply.github.com","commits":1,"url":"https://github.com/cxw620"}],"changelog":[{"hash":"f5263d04570c9dcd1d1554a0c67651c532fe1b6a","time":1689310001000,"email":"70561268+cxw620@users.noreply.github.com","author":"陈寒彤","message":"add grpc docs (#741)"},{"hash":"0e17dd421a42892efac3c23ce0f5be5771b8e24e","time":1684813962000,"email":"loveava0612@outlook.com","author":"XiaoMiku01","message":"[gRpc] 添加评论区 At 用户列表接口 (#681)"},{"hash":"ce8d00fcd037deddf23521ae5985899f9012908b","time":1625578116000,"email":"45892418+SocialSisterYi@users.noreply.github.com","author":"社会易姐QwQ","message":"更新大量【proto定义】并勘误"},{"hash":"3acd3e2167b763e5a1726259cd2ee02510bd9570","time":1623347248000,"email":"45892418+SocialSisterYi@users.noreply.github.com","author":"社会易姐QwQ","message":"更新【评论区】proto结构体"},{"hash":"c454b3a4092275d8e3c871564fe7a5beb6dcc1e9","time":1612521120000,"email":"45892418+SocialSisterYi@users.noreply.github.com","author":"SocialSisterYi","message":"更新【proto文件】"},{"hash":"fe77d8aae7fccb0a57ad21f3de9e543c1e1b09dd","time":1609659680000,"email":"45892418+SocialSisterYi@users.noreply.github.com","author":"SocialSisterYi","message":"添加若干grpc接口的proto定义"}]},"filePathRelative":"grpc_api/readme.md"}');export{u as comp,b as data};