Files
lxc-streamutils/docs/protocol.md
T
2026-07-16 00:12:26 -07:00

104 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 组件实时协议
当前协议版本为 `1`。后端的权威定义在 `domain.rs` 的
`ComponentMessage`,浏览器渲染器只依赖本文列出的稳定字段。
## 连接地址与认证
组件流地址:
```text
wss://danmaku.luoxingci.com/api/v1/components/<publicId>/stream
```
OBS 页面地址中的 token 位于 fragment:
```text
https://danmaku.luoxingci.com/obs/<publicId>#token=<component-token>
```
fragment 不会进入首次 HTTP 请求或 Nginx access
log。WebSocket 建立后,客户端必须在 8 秒内发送第一帧:
```json
{
"type": "authenticate",
"token": "component-token"
}
```
服务端验证 token 摘要、组件归属和 `events:subscribe` scope。认证成功后返回:
```json
{
"version": 1,
"type": "authenticated",
"componentId": "08d31d19-3e4b-4c11-9cf7-9bd786a39465"
}
```
之后立即发送
`overlay.settings.snapshot`,再开始发送实时事件。token 无效、被轮换或属于其他组件时,服务端以 policy
close 结束连接。
## 版本化事件信封
```json
{
"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 字段,从而允许兼容地增加元数据。
## 事件类型
| `type` | 主要 payload | 说明 |
| --------------------------- | --------------------------------------------- | ---------------------- |
| `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` | 有界且已清理的未知事件 |
礼物目录价格的原始单位是人民币的千分之一。`gift.totalPrice` 保留该整数单位,`gift.priceCny`
是供展示使用的人民币数值。
`live.gift` 与 `live.gift.combo` 不是两笔礼物。需要持久化计数的组件通常只消费
`live.gift`;连击事件用于更新同一张视觉卡片。
## 表情分段
`live.danmaku.payload.segments` 是判别联合:
- `text`:包含可直接显示的文字。
- `emoticon`:包含回退文字、图片 URL、可选尺寸、动态标记和整条大表情标记。
图片失败时客户端必须回退到 `text`,不能让一张失效图片破坏整条弹幕。
## 背压与重连
- 每个组件使用有界 `tokio::broadcast` channel。
- 慢客户端发生 `Lagged` 时跳过已经丢失的旧帧,继续接收新事件;实时展示不保证历史重放。
- 浏览器对非鉴权关闭使用指数退避重连,上限 12 秒。
- 鉴权失败不会自动重试,避免对已撤销 token 形成无限请求。
- 需要可靠业务处理的功能必须实现 `EventHandler` 并写入数据库,不能把 WebSocket 当作消息队列。