fix(player): hold the Watch Together anchor while the host reloads (#1809)

An in-place source switch — audio, subtitle, version or quality — detaches
the host's player for the duration of the reload. `_broadcast` falls back to
a position of 0 when no player is attached, so any state published in that
window names 0:00 as the authoritative position and every guest hard-seeks
to the start of the item.

Heartbeats already suppress themselves while detached, which is why this
hides: the paths that leak the zero are the ones that answer on demand.
`onStateRequested`, `onPeerJoined` and `onReconnected` all broadcast
regardless of whether a player is attached, so a guest entering the player,
joining, or reconnecting mid-reload is the trigger.

Fall back to the last broadcast anchor instead. That field is only assigned
for untargeted broadcasts, so it holds the last position the room was
actually told, and the reload's own re-attach path already re-anchors from
it once the player comes back.
This commit is contained in:
Tolu Adegbehingbe
2026-08-06 05:54:17 +02:00
committed by GitHub
parent db4f7a643b
commit f5488cb7ff
2 changed files with 35 additions and 1 deletions
@@ -777,7 +777,11 @@ class HostPlaybackCoordinator {
anchorPositionMs = _pendingStartPositionMs ?? player?.position.inMilliseconds ?? 0;
anchorHostTimeMs = _pendingStartAtMs!;
} else {
anchorPositionMs = anchorPositionOverrideMs ?? player?.position.inMilliseconds ?? 0;
// An in-place reload detaches the player, and the reply-on-demand paths
// still answer while it is gone. Without the last broadcast to fall back
// on, they publish an authoritative 0 and every guest hard-seeks to 0:00.
anchorPositionMs =
anchorPositionOverrideMs ?? player?.position.inMilliseconds ?? _lastBroadcast?.anchorPositionMs ?? 0;
anchorHostTimeMs = _nowMs();
}