Files
lxc-streamutils/docs/protocol.md
T
2026-08-19 12:30:30 -07:00

7.5 KiB
Raw Blame History

组件实时协议

当前协议版本为 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 结束连接。

componentId/publicId 标识组件实例,而不是组件类型。同一账户可以创建多个相同 componentKind 的实例;它们使用各自的设置、token 和 WebSocket 地址,客户端不能把同 kind 视为同一个订阅通道。

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 认证后按顺序接收:

  1. song.queue.snapshot.begin:包含 snapshotId、revision、当前歌曲和待唱总数。
  2. 零到多个 song.queue.snapshot.page:每页最多 100 个待唱项。
  3. song.queue.snapshot.end:提交该快照。

后续 song.queue.changed 携带连续 revision,operation 为 added、promoted、 completed、cancelled 或 cleared。客户端发现 revision 缺口必须重连取得新快照,不能猜测缺失队列状态。 cleared 表示当前歌曲与全部待唱项已在同一事务中取消,客户端必须立即清空活动投影。

礼物目录价格的原始单位是人民币的千分之一。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 作为流星主体,图片失效时必须使用本地星光占位。该组件不维护状态快照,重连后只展示新到达的实时事件。

guard_effect 只订阅 live.guard.buy。舰长、提督和总督事件进入该组件独立的广播通道;它不接收普通礼物或礼物连击,也不维护状态快照。

gift_effect 与 guard_effect 的浏览器渲染器分别维护有界 FIFO,并且每次只播放一个 active 特效。动画完成后按到达顺序推进下一项;这个队列仅用于 OBS 视觉节流,不是可靠业务队列,也不会在重连后恢复。

gift_menu 同样只消费一次性 live.gift 与 live.guard.buy。命中配置后投影为 gift-menu.triggered,payload 包含 itemIds、viewer 和 sourceEventId。一个特定礼物和一个同价电池规则可以同时命中多个菜单项;客户端应全部高亮,并滚动到第一个匹配项。未命中的投喂不会进入该组件通道。

表情分段

live.danmaku.payload.segments 是判别联合:

  • text:包含可直接显示的文字。
  • emoticon:包含回退文字、图片 URL、可选尺寸、动态标记和整条大表情标记。

图片失败时客户端必须回退到 text,不能让一张失效图片破坏整条弹幕。

背压与重连

  • 每个组件使用有界 tokio::broadcast channel。
  • 慢客户端发生 Lagged 时跳过已经丢失的旧帧,继续接收新事件;实时展示不保证历史重放。
  • 浏览器对非鉴权关闭使用指数退避重连,上限 12 秒。
  • 鉴权失败不会自动重试,避免对已撤销 token 形成无限请求。
  • 需要可靠业务处理的功能必须实现 EventHandler 并写入数据库,不能把 WebSocket 当作消息队列。