跳转到内容
文档
下载
简体中文

状态和事件

Sound Show 在认证后及每次变化时发送当前演出状态。请根据这些快照构建控制器,并使用命令参考请求更改。

state 消息会替换全部本地状态。它会在 hello_ok 后、整体或项目变化后自动发送,也会作为 request_state 的响应。本精简示例显示主要字段;真实项目包含已填充的数组及更多元数据。

{
"type": "state",
"protocol_version": 2,
"server_time_ms": 1712345678901,
"state": {
"project": { "name": "My show", "categories": [] },
"players": [],
"playable_runtime": [],
"playlists": { "current_index": 0, "playlists": [] },
"instructions": { "current_index": 0, "players": [] },
"timers": [],
"logical_devices": [],
"control": {
"next_sound_volume": 70,
"selected_category_id": null,
"low_volume_active": false,
"volume_level_presets": [],
"active_volume_level_ids": [],
"effective_volume_level_id": null
},
"capabilities": { "audio_controls": [], "manual_grid_layout": false },
"audio_controls": { "revision": 1, "logical_devices": [], "players": [] },
"lights": { "revision": 1, "lights": [] }
}
}

请处理缺失或 null 的项目、空数组、可选字段和新增的未知字段。这些文档描述当前 v2 实现。较早的 Sound Show 2 版本可能提供较少的字段、命令或功能;使用可选功能前,请检查 hello_ok.features 和状态中的能力信息。

完整状态之后,大多数运行时更新只替换一个域,即有名称的状态部分。空数组有明确意义,例如 players: [] 会清除当前播放器。不要将数组项合并到旧列表中。

消息 type 替换的本地字段 发送条件
control_snapshot control 音量级别命令的直接回复;启用 runtime_snapshots 时,所有操作者的控制更新。
players_snapshot players 播放器变化。
instructions_snapshot instructions 启用 runtime_snapshots 时。
playlists_snapshot playlists 启用 runtime_snapshots 时。
playable_runtime_snapshot playable_runtime 启用 runtime_snapshots 时。
logical_devices_snapshot logical_devices 输出变化。
timers_snapshot timers 计时器变化。
audio_controls_snapshot audio_controls 效果变化;比较 revision。
lights_snapshot lights 灯光变化;比较 revision。

每条域消息都包含对应名称的数据字段和 server_time_ms。例如:

{
"type": "players_snapshot",
"players": [],
"server_time_ms": 1712345678901
}

音频控制和灯光消息还包含域修订号:

{
"type": "audio_controls_snapshot",
"revision": 5,
"audio_controls": { "revision": 5, "logical_devices": [], "players": [] },
"server_time_ms": 1712345678901
}

对于这两个域,请忽略修订号低于该域已应用修订号的消息。不要跨域比较修订号。完整的 state 会重置基准;断开时应清除所有本地状态和修订号记录。其他域按接收顺序应用。

请在 hello.features 中请求 runtime_snapshots。未启用时,除直接控制回复外,控制、指令和播放列表变化会使用合并后的完整状态。对于没有此功能的客户端,仅可播放项活动变化不会触发完整状态。

运行时变化按 50 ms 周期合并。这不保证每 50 ms 都有消息,也不是记录所有中间变化的事件日志。完整状态可以替换待发送的域更新。处理所有当前快照类型的归约器可见 JavaScript 示例。

目的 使用的值
启动项目按钮 将 project.categories[].playables[].id 用作 playable_id。
选择分类 将 project.categories[].id 用作 category_id。
停止、暂停、跳转或调整一个运行实例 将 players[].id 用作 player_id。
控制输出 将 logical_devices[].id 用作 logical_device_id。
激活音量预设 将 control.volume_level_presets[].id 用作 level_id。
调整效果 将描述符的 id 用作 control_id,并指定当前播放器或逻辑设备目标。
设置灯光级别 将 lights.lights[].id 用作 project_light_id。
播放列表项目 当前播放列表和队列中从零开始的位置。
选择指令 当前指令播放器从零开始的位置,以及项目的 path。
控制计时器 将其当前从零开始的位置用作 timer_index。

播放器 ID 表示运行中的实例,不应作为永久项目标识保存。一个可播放项可以有多个活动播放器。请在当前播放器快照中查找匹配的 source_playable_id 或 state_playable_id,并明确选择所需实例。名称适合用于显示,但不一定唯一。

project.categories[] 包含分类的 id、name、color、is_hidden、可选的 icon_url 和 playables。可播放项包括:

{
"id": "playable-id",
"name": "Wind",
"kind": "sound",
"color": "#3b82f6",
"tags": [],
"is_searchable": true,
"runtime_state_key": "playable-id",
"target_category_id": null
}

常见类型有 sound、video、image、random、timer、playlist、action、webbrowser、category_link 和 instruction。请兼容新类型或 unsupported。可选元数据包括 icon_url、键盘快捷键和配置的点击操作 ID。

playable_runtime 中的条目包含 state_playable_id、play_count、is_locked 和 is_playing。将可播放项的 runtime_state_key(如果不存在则使用 id)与 state_playable_id 匹配。链接的可播放项可以共享活动及锁定状态。项目按钮的锁定不同于运行中播放器的控制锁定。

分类还可以包含可选的 grid_layout,其中有 enabled、columns、rows、pages、positions 和 revision。使用前请检查 capabilities.manual_grid_layout。客户端可以忽略此元数据并显示普通可播放项列表;没有远程布局编辑命令。

{
"id": "runtime-player-id",
"kind": "sound",
"title": "Wind",
"color": "#3b82f6",
"panel_position": 0,
"position_seconds": 12.3,
"duration_seconds": 95,
"start_time_seconds": 5,
"stop_time_seconds": 90,
"timeline_duration_seconds": 95,
"volume_percent": 70,
"muted": false,
"is_playing": true,
"is_locked": false,
"loop_enabled": true,
"output_logical_device_id": "main",
"source_playable_id": "playable-id",
"state_playable_id": "playable-id"
}

直接使用 volume_percent 显示。单独的原始 volume 字段即使存在,也不是可以乘以 100 得到百分比的值;应用可能使用非线性音量刻度。

时间线字段的单位是秒。start_time_seconds、stop_time_seconds 和 timeline_duration_seconds 可能缺失或为 null。跳转时可用它们将裁剪后的播放区间映射到源时间线。其他可选字段包括 remaining_playback_seconds、markers 和 icon_url。

logical_devices[] 提供 id、label、display_label、color、output_role、available、selected 和 volume_percent。显示使用 display_label,命令使用 id。

control 提供下次启动音量、所选分类,以及 volume_level_presets、active_volume_level_ids 和 effective_volume_level_id。预设包括 id、name、direction(both、up_only 或 down_only)、target_volume_percent、fade_down_ms 和 fade_up_ms。low_volume_active 用于兼容旧控制器。请从快照读取活动 ID,因为其他操作者也可能更改它们。

播放列表、指令和计时器

“播放列表、指令和计时器”章节
域 常用字段
playlists current_index 和 playlists[]。每个列表有 id、title、autoplay、loop_enabled 和 queue。队列项目包含 playable_id、title 和可选的 icon_url。运行时元数据可能包含 active_item_id、active_item_title 和 active_player_id。
instructions current_index 和 players[]。播放器有 id、title、has_instruction、waiting_for_go、next_instruction_text 和 items。项目提供 item_id、title、kind、depth、path、can_set_next、is_next_to_play 和 is_visibly_playing。
timers 包含数字 id、mode、is_running、is_locked、elapsed_ms、duration_ms、remaining_ms 和 is_over_time 的数组。计时器时间单位为毫秒。

预览 GO 时,优先选择层级最深、path 非空且 kind 不为 group 的下一项目;若没有,则使用任意层级最深的下一项目。选择下一项目时,请使用实际的 path,不要根据标题或深度推断路径。

音频控制和能力信息

“音频控制和能力信息”章节

capabilities.audio_controls[] 描述可用的效果控制项。每个描述符提供:

字段 含义
id, label 协议标识符和显示标签。
scopes 支持的目标类型:logical_device、player 或两者。
min, max, default_value 原生单位数值边界和重置值。
suggested_step 建议的调整步长。
unit, scale 显示单位和控制刻度。log2 表示以八度为单位的步长。
display_multiplier 显示时乘以该值;用户输入在发送前除以该值。

例如,比例 0.5 配合 display_multiplier: 100 时显示为 50%。请使用服务器公布的目录,不要将范围写死。

audio_controls 包含 revision、logical_devices[] 和 players[]。每个目标有 kind、id、label、available、locked,以及将控制 ID 映射到原生数值的 values 对象。线性控制按线性变化渐变;log2 控制按几何变化渐变。

lights 包含 revision 和 lights[]。每盏灯有 id、name、capability(当前为 level)和 available。底层适配器不可用时会省略 value_percent。请显示为不可用,不要将其视为零。

快照条目可能包含不透明的相对 icon_url,例如 /asset/file?id=opaque-asset-id。请以远程服务器的 HTTP 地址为基准解析,并通过 HTTP 获取。图标不是 WebSocket 消息。

使用提供的 URL,不要自行构造 ID 或发送文件路径。在当前服务器会话内缓存图标,并预计重启后 ID 会变化。项目变化可能导致资源不可用;图片请求失败时应提供替代显示。快照不公开本地文件系统路径。

已认证的连接可能收到:

{
"type": "error",
"code": "invalid_value",
"command_id": "eq-change-1",
"message": "Human-readable error description"
}

code 和 command_id 为可选项。音频或灯光命令错误可能包含 unknown_control、target_locked、target_not_found、invalid_value、target_unavailable 或 command_failed。旧命令处理路径可能只返回 message。不要将面向人的说明文字解析为稳定的错误代码。发生错误后连接可能仍然打开,请继续处理快照。

限制 客户端行为
首条消息须在 10 秒内发送 连接打开后立即发送 hello。
最多 16 个同时存在的 WebSocket 连接 复用连接,关闭不用的客户端。未认证的套接字也计入限制。
每条传入消息/帧最多 64 KiB 每条消息发送一条小型 JSON 命令。
突发额度 60 条命令,每个连接每秒补充 20 条 限制滑块发送频率,合并待发送值。ping 和刷新请求也消耗此额度。
同一来源地址在 60 秒内输错密码 5 次 更正密码并等待;新的升级请求会被阻止 60 秒,并返回 HTTP 429 和 Retry-After。

被限流器拒绝的命令不会执行,并返回 code: "rate_limited"。持续超额可能关闭连接。如果客户端能读取 HTTP 响应,请遵循 Retry-After。若连续认证失败后浏览器只报告连接失败,请至少等待 60 秒再试。不要盲目重试播放、切换或 GO 命令;重试可能触发第二次操作。

当 hello_ok.features 包含 ping 时,已认证客户端可以发送 {"type":"ping"} 并等待 {"type":"pong"}。内置遥控器每 10 秒发送一次 ping,并在该次检查时,如果超过 25 秒未收到有效消息则重新连接。未变化的域在其他时候可以保持静默。

对于持续连接的控制器:

  1. 套接字关闭或心跳失败时立即禁用命令发送。
  2. 清除缓存状态、播放器 ID 和修订号记录。
  3. 使用有上限的延迟重新连接,连续失败时增加等待时间。遵循认证错误和频率限制,不要持续重试。
  4. 发送新的 hello,等待新的完整 state,然后重新确定目标。
  5. 根据该状态重新启用控制。重连后不要重放排队的启动、切换或 GO 命令。

连接范围内的音量按住状态会在断开时释放。即使服务器地址未变,也应将重连视为新会话。

在 Sound Show 中更改允许外部来源会立即生效并断开当前客户端。关闭后,其他来源的浏览器无法重新连接。配置和浏览器限制请参阅自定义浏览器遥控器。