文章

一起看时主控切下一集,被控端怎样自动跟上

Watch Together 的媒体交接功能记录。主控切集后被控端如何确认目标媒体、复用 Barrier 对齐时间轴,以及哪些情况会让旧操作失效。

Watch Together 是跑在 Emby 上的同步观看插件,负责让两个人看同一部剧。此前它已经有一套 Barrier 机制,负责双方位于同一集之后的暂停、拖动和恢复。主控切到下一集时,被控端还停在旧的一集,Barrier 会把这个跨集差异当成时间轴偏差来处理,对齐就变成了错误的方向。媒体交接(Media Handoff)补上的就是这段缺失的路。

设计这个流程时摆过三个选项。把打开媒体当成 Barrier 的一种待确认命令;在同步引擎之外另起一个服务,负责播放列表和下一集的推导;或者做一个独立的交接流程,确认双方进入同一媒体之后,再回到 Barrier。最后选了第三种。Barrier 的职责保持在时间轴对齐上,跨集的事情交给交接流程;另外,命令发出和播放器真正打开媒体是两件事,分开确认之后,错误不会被掩盖。

交接的形状

交接状态只驻留内存,记录目标媒体、主控和被控端各自的会话身份,以及一个代际号。主控的媒体变化触发交接。发给被控端的打开命令复用 Emby 的播放命令接口,命令发送成功不算完成,要等被控端的会话快照里用户、会话和目标媒体三项都确认一致,交接状态才清除,随后进入 Barrier 对齐时间轴。

哪些情况会让交接失效

  • 主控连续切集,从 B 换到 C 时,旧的交接操作失效,迟到的 B 不能污染当前目标。
  • 被控端在交接过程中主动切到第三个媒体,本次自动交接取消,不会被一直拉回去。想回到主控的媒体,可以用跨媒体的重新同步。
  • 会话身份发生变化,旧的代际失效;打开命令带取消、五秒外部超时和有限重试,最终失败回到等待状态。
  • 自动连播的停止到开始会被识别为切集,不会先触发普通停止的副作用。
  • 重新同步只作用于被控端,即使由主控发起,也不会向主控发打开命令。

这批改动有意不做的部分也写进了记录。不启用周期性漂移拖动,不修改房间持久化格式,不支持多人、跨服务器和播放列表同步。交接状态只驻留内存,服务重启之后房间需要重新同步。

发布时遇到的偶发失败

这批功能第一次走 beta 发布时,CI 里一条回归测试偶发超时,发布流程在创建 GitHub Release 之前中止。那个 tag 保留下来,不复用;把这条命令锁相关的回归测试改成确定性的之后,重新走通道发布了 v1.6.0.1 beta。稳定基线仍是 v1.5.0.0,配置和房间数据不需要迁移。

验证边界

发布说明列出了八项必须在真实客户端上确认的行为,从自动播放下一集、主控手动切集,到被控端对打开命令的兼容性、切换后的会话信息更新时间、交接进入 Barrier 的实际时序,以及主控连续切集这类边界。自动测试能证明状态转换、代际失效和重试门控符合预期,真实客户端上的时序只能靠双端实机验收。完成这轮验证之后,再评估 1.6.1.0 稳定版。

从实现角度看,这个功能把切换媒体从时间轴对齐里拆了出来,代价是引入一个有界的运行时状态。它目前只覆盖双人同服务器的场景,掉线恢复和跨设备恢复都还没有做,这些留给后续阶段。

Sources

[1] https://github.com/hope140/EmbyWatchTogether EmbyWatchTogether 仓库 [2] https://github.com/hope140/EmbyWatchTogether/releases/tag/v1.6.0.1 v1.6.0.1 beta [3] https://github.com/hope140/EmbyWatchTogether/blob/beta/docs/adr/002-media-handoff.md 媒体交接的设计记录 [4] https://github.com/hope140/EmbyWatchTogether/blob/beta/docs/releases/v1.5.0.4.md 中止的发布尝试