6.6 KiB
组件实时协议
当前协议版本为 1。后端的权威定义在 domain.rs 的
ComponentMessage,浏览器渲染器只依赖本文列出的稳定字段。
连接地址与认证
组件流地址:
wss://danmaku.luoxingci.com/api/v1/components/<publicId>/stream
OBS 页面地址中的 token 位于 fragment:
https://danmaku.luoxingci.com/obs/<publicId>#token=<component-token>
fragment 不会进入首次 HTTP 请求或 Nginx access log。WebSocket 建立后,客户端必须在 8 秒内发送第一帧:
{
"type": "authenticate",
"token": "component-token"
}
服务端验证 token 摘要、组件归属和 events:subscribe scope。认证成功后返回:
{
"version": 1,
"type": "authenticated",
"componentId": "08d31d19-3e4b-4c11-9cf7-9bd786a39465",
"componentKind": "song_request",
"language": "zh-CN"
}
之后立即发送通用的 component.settings.snapshot,再发送该 kind 的状态快照和实时事件。弹幕姬还会发送
overlay.settings.snapshot 兼容帧。token 无效、被轮换或属于其他组件时,服务端以 policy
close 结束连接。
language 是组件所属账户的 locale。用户在控制台修改语言后,所有已连接组件会立即收到
component.language.updated,payload 为 { "language": "en-US" };新连接以认证帧为准。
设置快照中的 themeId 是稳定的主题标识。弹幕姬与点歌姬支持 jade-scroll 和
moonlit-water;礼物特效支持 jade-starfall 和 moonlit-water;礼物菜单支持 jade-banquet 和
moonlit-water。消费者应把未知主题降级为自身默认主题,不能因主题发布顺序不同而中断实时消息。
版本化事件信封
{
"version": 1,
"id": "d56c1a28-a6f1-4ad9-b02f-4c22dc4d3c27",
"componentId": "08d31d19-3e4b-4c11-9cf7-9bd786a39465",
"sourceId": "c8087bb0-0dde-40eb-a43d-a9a2574c2717",
"occurredAt": "2026-07-15T20:10:30.125Z",
"roomId": "123456",
"type": "live.danmaku",
"payload": {
"viewer": { "uid": "42", "name": "观众" },
"text": "晚上好",
"segments": [{ "type": "text", "text": "晚上好" }]
}
}
ownerId 永远不会序列化到浏览器。消费者应按 version 和 type
分派,并忽略不认识的 payload 字段,从而允许兼容地增加元数据。 sourceId
标识所属账户唯一的直播监听连接,不表示组件单独绑定了一个直播源;同一账户不同组件收到的实时事件具有相同的
sourceId。
事件类型
type |
主要 payload | 说明 |
|---|---|---|
component.settings.snapshot |
settings |
所有组件的设置快照 |
component.settings.updated |
settings |
所有组件的设置更新 |
component.language.updated |
language |
账户语言实时更新 |
overlay.settings.snapshot |
settings |
认证后当前设置快照 |
overlay.settings.updated |
settings |
控制台保存后的实时设置 |
live.danmaku |
viewer, text, segments |
普通文字和表情分段 |
live.enter |
viewer |
进房事件 |
live.gift |
viewer, gift, quantity, sourceEventId |
一次可记账的礼物事件 |
live.gift.combo |
viewer, gift, quantity, comboId |
同一连击的视觉更新 |
live.superchat |
viewer, message, price, sourceEventId |
醒目留言 |
live.guard.buy |
viewer, guardName, quantity, price |
舰长购买 |
live.like |
viewer |
点赞 |
live.share |
viewer |
分享 |
live.unknown |
command, metadata |
有界且已清理的未知事件 |
点歌姬状态
song_request 认证后按顺序接收:
song.queue.snapshot.begin:包含snapshotId、revision、当前歌曲和待唱总数。- 零到多个
song.queue.snapshot.page:每页最多 100 个待唱项。 song.queue.snapshot.end:提交该快照。
后续 song.queue.changed 携带连续 revision,operation 为 added、promoted、
completed、cancelled 或
rating-updated。客户端发现 revision 缺口必须重连取得新快照,不能猜测缺失队列状态。
礼物目录价格的原始单位是人民币的千分之一。gift.totalPrice 保留该整数单位,gift.priceCny
是供展示使用的人民币数值。
live.gift 与 live.gift.combo 不是两笔礼物。需要持久化计数的组件通常只消费
live.gift;连击事件用于更新同一张视觉卡片。
gift_effect 只订阅 live.gift 与 live.guard.buy。礼物档位由 sanitized settings 中的
highValueThreshold 和 featuredValueThreshold 决定;浏览器使用 gift.imageUrl 或
gift.animationUrl
作为流星主体,图片失效时必须使用本地星光占位。该组件不维护状态快照,重连后只展示新到达的实时事件。
gift_menu 同样只消费一次性 live.gift 与 live.guard.buy。命中配置后投影为
gift-menu.triggered,payload 包含 itemIds、viewer 和
sourceEventId。一个特定礼物和一个同价电池规则可以同时命中多个菜单项;客户端应全部高亮,并滚动到第一个匹配项。未命中的投喂不会进入该组件通道。
表情分段
live.danmaku.payload.segments 是判别联合:
text:包含可直接显示的文字。emoticon:包含回退文字、图片 URL、可选尺寸、动态标记和整条大表情标记。
图片失败时客户端必须回退到 text,不能让一张失效图片破坏整条弹幕。
背压与重连
- 每个组件使用有界
tokio::broadcastchannel。 - 慢客户端发生
Lagged时跳过已经丢失的旧帧,继续接收新事件;实时展示不保证历史重放。 - 浏览器对非鉴权关闭使用指数退避重连,上限 12 秒。
- 鉴权失败不会自动重试,避免对已撤销 token 形成无限请求。
- 需要可靠业务处理的功能必须实现
EventHandler并写入数据库,不能把 WebSocket 当作消息队列。