refactor(watch-together): host-authoritative declarative sync protocol

Replaces the imperative play/pause/seek/positionSync message soup with a
single host-authored PlaybackState (seq-ordered, anchor-extrapolated,
phase machine: loading/waitingForPeers/paused/playing) that doubles as
the heartbeat, plus guest status reports and host-applied control
requests. Fixes the guest seek-back loop while the host loads (readiness
was keyed on a pre-load !buffering snapshot and heartbeats broadcast
frozen positions), adds real group buffering coordination (stall grace,
scheduled simultaneous resumes, 15s safety timeout), rate-nudge drift
correction with passthrough-aware seek fallback, session-scoped message
handling (no lost messages during episode-switch detach gaps), and an
expected-state ledger replacing the racy remote-action flag.
This commit is contained in:
edde746
2026-06-11 09:42:38 +02:00
parent e54bbd11f1
commit a5c7d5b52a
27 changed files with 5099 additions and 1816 deletions
+14 -2
View File
@@ -2895,9 +2895,18 @@ class TranslationsWatchTogetherEn {
/// en: '${name} is buffering'
String participantBuffering({required Object name}) => '${name} is buffering';
/// en: '${name} is on an older app version — sync unavailable'
String participantNeedsUpdate({required Object name}) => '${name} is on an older app version — sync unavailable';
/// en: 'Resuming without ${name}'
String resumingWithout({required Object name}) => 'Resuming without ${name}';
/// en: 'Waiting for others to load...'
String get waitingForParticipants => 'Waiting for others to load...';
/// en: 'Waiting for ${name}...'
String waitingForName({required Object name}) => 'Waiting for ${name}...';
/// en: 'Recent Rooms'
String get recentRooms => 'Recent Rooms';
@@ -5378,7 +5387,10 @@ extension on Translations {
'watchTogether.participantResumed' => ({required Object name}) => '${name} resumed',
'watchTogether.participantSeeked' => ({required Object name}) => '${name} seeked',
'watchTogether.participantBuffering' => ({required Object name}) => '${name} is buffering',
'watchTogether.participantNeedsUpdate' => ({required Object name}) => '${name} is on an older app version — sync unavailable',
'watchTogether.resumingWithout' => ({required Object name}) => 'Resuming without ${name}',
'watchTogether.waitingForParticipants' => 'Waiting for others to load...',
'watchTogether.waitingForName' => ({required Object name}) => 'Waiting for ${name}...',
'watchTogether.recentRooms' => 'Recent Rooms',
'watchTogether.renameRoom' => 'Rename Room',
'watchTogether.removeRoom' => 'Remove',
@@ -5500,11 +5512,11 @@ extension on Translations {
'companionRemote.remote.tabRemote' => 'Remote',
'companionRemote.remote.tabPlay' => 'Play',
'companionRemote.remote.tabMore' => 'More',
_ => null,
} ?? switch (path) {
'companionRemote.remote.menu' => 'Menu',
'companionRemote.remote.tabNavigation' => 'Tab Navigation',
'companionRemote.remote.tabDiscover' => 'Discover',
_ => null,
} ?? switch (path) {
'companionRemote.remote.tabLibraries' => 'Libraries',
'companionRemote.remote.tabSearch' => 'Search',
'companionRemote.remote.tabDownloads' => 'Downloads',