B站的api调用是比较繁琐的,想通过Vercel简化两个api的调用:

想要简化成:

  • 弹幕:/danmaku/?oid=<视频CID>
  • 视频播放URL:/video/<视频AV号或BV号>/playurl

Vercel入门

  • 初始化一个项目 npm init
  • 创建一个文件夹用作api的根目录,我这里就直接新建为 api 文件夹
  • 在该文件夹下创建 index.js ,这样之后可以使用xxx.vercel.app/api直接访问到 如果是其他名字就需要访问xxx.vercel.app/api/文件名
  • index.js 文件内容的一个简单演示,标准的 HTTP Handler 的语法:
/api/index.js
1
module.exports = (req, res) => {
2
const { name = 'World' } = req.query
3
res.send(`<h1>Hello ${name}!</h1>`)
4
}

如果想返回json格式,可以使用res.json()

/api/index.js
1
const { name = 'World' } = req.query
2
let obj = {
3
message:"Hello "+name+"!"
4
}
5
module.exports = (req, res) => {
6
res.json(obj)
7
}
  • 配置 Rewrites/Redirects 将api文件夹作为根目录: 在项目根目录创建一个 vercel.json 配置如下
/vercel.json
1
{
2
"rewrites": [
3
{
4
"source": "/",
5
"destination": "/api"
6
}
7
]
8
}
  • 将项目丢到Github
  • 进入Vercel官网,使用github账号登录,导入这个项目
  • 托管之后会分配一个域名,直接访问即可

实现

目录结构

├─api
├─danmaku
├─dm_pb2.py
├─dm.proto
├─index.py
└─requirements.txt
└─video
└─playurl.js
├─package.json
└─vercel.json

vercel.json

1
{
2
"rewrites": [
3
{
4
"source": "/danmaku(.*)",
5
"destination": "/api/danmaku/index.py"
6
},
7
{
8
"source": "/video/:id/playurl",
9
"destination": "/api/video/playurl.js"
10
}
11
],
12
"headers": [
13
{
14
"source": "/(.*)",
15
"headers": [
16
{
17
"key": "Access-Control-Allow-Credentials",
18
"value": "true"
19
},
20
{
21
"key": "Access-Control-Allow-Origin",
22
"value": "*"
23
},
24
{
25
"key": "Access-Control-Allow-Methods",
26
"value": "GET,OPTIONS,PATCH,DELETE,POST,PUT"
27
},
28
{
29
"key": "Access-Control-Allow-Headers",
30
"value": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version"
31
}
32
]
33
}
34
]
35
}

获取实时protobuf弹幕

B站弹幕使用了protobuf(一种定义消息格式的语法,它允许定义字段类型、顺序和规则)

  1. 用python处理是比较简单的,所以danmaku API我决定使用python,这里用到了3个依赖

    /api/danmaku/requirements.txt
    1
    fastapi
    2
    protobuf==5.27.2
    3
    requests
  2. 下载B站弹幕的.proto文件,放入/api/danmaku中:

    /api/danmaku/dm.proto(下载地址)
    1
    https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/grpc_api/bilibili/community/service/dm/v1/dm.proto
  3. 下载protobuf对应版本的编译器(我这里是5.27.2)
    https://github.com/protocolbuffers/protobuf/releases/tag/v27.2
    然后编译dm.proto文件

    cd ./api/danmaku/
    protoc -I=. --python_out=. dm.proto

    我们就得到一个dm_pb2.py文件

    /api/danmaku/dm_pb2.py
    1
    # -*- coding: utf-8 -*-
    2
    # Generated by the protocol buffer compiler. DO NOT EDIT!
    3
    # NO CHECKED-IN PROTOBUF GENCODE
    4
    # source: dm.proto
    5
    # Protobuf Python Version: 5.27.2
    6
    """Generated protocol buffer code."""
    7
    from google.protobuf import descriptor as _descriptor
    8
    from google.protobuf import descriptor_pool as _descriptor_pool
    9
    from google.protobuf import runtime_version as _runtime_version
    10
    from google.protobuf import symbol_database as _symbol_database
    11
    from google.protobuf.internal import builder as _builder
    12
    _runtime_version.ValidateProtobufRuntimeVersion(
    13
    _runtime_version.Domain.PUBLIC,
    14
    5,
    15
    27,
    16
    2,
    17
    '',
    18
    'dm.proto'
    19
    )
    20
    # @@protoc_insertion_point(imports)
    21
    22
    _sym_db = _symbol_database.Default()
    23
    24
    DESCRIPTOR = ...
  4. 编写index.py,代理https://api.bilibili.com/x/v2/dm/web/seg.so的请求并响应出json格式

    /api/danmaku/index.py
    1
    import sys
    2
    from os.path import dirname, abspath
    3
    sys.path.append(dirname(abspath(__file__)))
    4
    5
    import requests
    6
    import google.protobuf.json_format as json_format
    7
    import dm_pb2 as Danmaku
    8
    from fastapi import FastAPI
    9
    from fastapi.responses import JSONResponse, Response
    10
    11
    app, subapi = FastAPI() , FastAPI()
    12
    app.mount("/danmaku", subapi)
    13
    14
    @subapi.get("/")
    15
    def get_danmakus(oid, type=1, segment_index=1):
    16
    url = 'https://api.bilibili.com/x/v2/dm/web/seg.so'
    17
    params = {
    18
    'type': type,
    19
    'oid': oid,
    20
    'segment_index': segment_index
    21
    }
    22
    headers = {
    23
    'User-Agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0"
    24
    }
    25
    resp = requests.get(url, params,headers=headers)
    26
    if resp.status_code == 200:
    27
    danmaku_seg = Danmaku.DmSegMobileReply()
    28
    danmaku_seg.ParseFromString(resp.content)
    29
    return json_format.MessageToDict(danmaku_seg)
    30
    elif resp.content is not None:
    31
    return JSONResponse(json_format.MessageToDict(resp.content), resp.status_code)
    32
    else:
    33
    return Response(status_code=resp.status_code)
  5. 大功告成!

获取视频流URL

package.json添加如下配置,方便使用import export

/package.json
1
{
2
"type": "module",
3
}

添加axios依赖

yarn add axios

剩下的只需要一个js文件即可实现

/api/video/playurl.js
1
import axios from "axios"
2
3
export default async (req, res) => {
4
try {
5
const { id, p = 1 } = req.query
6
const [ aid, bvid ] = id.startsWith("BV") ? [null, id] : [id.substring('av'.length), null];
7
const { data: { cid, bvid:_bvid } } = (await axios.get(`https://api.bilibili.com/x/web-interface/view`,{params:{aid,bvid}})).data
8
const { data: { durl } } = await (await axios.get(`https://api.bilibili.com/x/player/playurl?cid=${cid}&bvid=${_bvid}&platform=html5&qn=6`)).data
9
res.writeHead(302, { 'Location': durl[p-1].url });
10
res.end();
11
} catch (error) {
12
res.writeHead(404, { 'Content-Type': 'text/plain' });
13
res.end('404 Not Found');
14
}
15
}