forked from Foxyz/sync_replay_project
Protocol 13: Up next preloads, so switching to it is instant
Every app gets the first Up next item ready itself (own Jellyfin plan, direct links, guests via /j when state.relayNext says the server's login can pass it on) and opens it in a hidden, muted, paused second player; the swap promotes that player instead of opening cold, so its buffer counts at once. Reports go out on open and when the buffer reaches the gate's need. SwitchTrace times each phase. Measured in the e2e harness: 1.5-3 s before, 35-103 ms with a preload. Relayed items still open cold for everyone but their source (ROADMAP). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4275abe470
commit
ddd7a21195
19 files changed
+1041
-142
No files matched your search
@@ -1,5 +1,5 @@
|
||||
abstract class RelayConstants {
|
||||
static const int protoVersion = 12;
|
||||
static const int protoVersion = 13;
|
||||
|
||||
// Relay (`/r/...`): a member's bytes through the server's buffer.
|
||||
static const int relaySliceBytes = 1 << 20;
|
||||
|
||||
@@ -711,6 +711,10 @@ class StateBroadcast {
|
||||
/// The server can relay the current Jellyfin media (`/j/...`) for viewers
|
||||
/// without their own access to it.
|
||||
final bool relay;
|
||||
/// The server can relay Up next's first item the same way (it is a
|
||||
/// Jellyfin ref on the server's login), so viewers without access get
|
||||
/// it ready before it goes up.
|
||||
final bool relayNext;
|
||||
/// Up next, in order (see `QueueItem`); empty when nothing is queued.
|
||||
final List<QueueItem> queue;
|
||||
/// The queued item someone asked to play now, waiting for the member
|
||||
@@ -727,6 +731,7 @@ class StateBroadcast {
|
||||
this.seekBy,
|
||||
this.seekSeq,
|
||||
this.relay = false,
|
||||
this.relayNext = false,
|
||||
this.queue = const [],
|
||||
this.queuePlay,
|
||||
});
|
||||
@@ -743,6 +748,7 @@ class StateBroadcast {
|
||||
if (seekBy != null) 'seekBy': seekBy,
|
||||
if (seekSeq != null) 'seekSeq': seekSeq,
|
||||
if (relay) 'relay': true,
|
||||
if (relayNext) 'relayNext': true,
|
||||
if (queue.isNotEmpty) 'queue': [for (final q in queue) q.toJson()],
|
||||
if (queuePlay != null) 'queuePlay': queuePlay,
|
||||
};
|
||||
@@ -767,6 +773,7 @@ class StateBroadcast {
|
||||
seekBy: json['seekBy'] as String?,
|
||||
seekSeq: json['seekSeq'] == null ? null : finiteInt(json['seekSeq'], 'seekSeq'),
|
||||
relay: (json['relay'] as bool?) ?? false,
|
||||
relayNext: json['relayNext'] == true,
|
||||
queue: json['queue'] is List
|
||||
? (json['queue'] as List).map(QueueItem.fromJson).whereType<QueueItem>().toList()
|
||||
: const [],
|
||||
|
||||
@@ -535,7 +535,7 @@ class SyncEngine {
|
||||
|
||||
/// [relay]: whether the server can relay the current Jellyfin media; the
|
||||
/// engine does not know the server's credential.
|
||||
StateBroadcast broadcast({bool relay = false}) {
|
||||
StateBroadcast broadcast({bool relay = false, bool relayNext = false}) {
|
||||
final bySeek = _seekEventId == eventId;
|
||||
return StateBroadcast(
|
||||
eventId: eventId,
|
||||
@@ -554,6 +554,7 @@ class SyncEngine {
|
||||
seekBy: bySeek ? _seekBy : null,
|
||||
seekSeq: bySeek ? _seekSeq : null,
|
||||
relay: relay,
|
||||
relayNext: relayNext,
|
||||
queue: queue.items,
|
||||
queuePlay: queue.play,
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user