Swap shows only the new media, no guessed endings, quieter screen

- A swap stops the previous media at once; buffer counts only for the
  media the player holds (client) and the current mediaId (server), so
  the old video can no longer start the new one.
- The automatic frozen-picture reload is gone (false alarms on still
  scenes); Reload video stays.
- The "Next episode" pill shows only from a known ending.
- A web link's thumbnail is its cover, under the video's own name.
- While playing, the unread chip stays off the picture; chat pop-ups
  can be All / Only when paused / Off.
- Server fetch failures say when the server has no cookies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
foxyzandClaude Opus 5.5 committed 2026-10-11 04:31:14 +03:00
1 parent ab133dd82f
commit 27b4ec2c96
25 files changed
+274 -485

No files matched your search

+5 -1
View File
@@ -429,7 +429,11 @@ class SyncEngine {
_setError(member, mediaId == this.mediaId ? error : '');
_seen(member);
if (jf != null) member.jf = Map.unmodifiable(jf);
member.buffered = member.out || !buffered.isFinite || buffered < 0 ? 0 : buffered;
// A report for an earlier media (sent before the swap reached the
// client) says nothing about this one's buffer.
final earlier = mediaId != 0 && mediaId != this.mediaId;
member.buffered =
member.out || earlier || !buffered.isFinite || buffered < 0 ? 0 : buffered;
final m = media;
// First report of the length wins; only for the media the client has open.
if (m != null && m.duration <= 0 && mediaId == this.mediaId &&
+18
View File
@@ -429,6 +429,24 @@ void main() {
expect(engine.members['b']!.inSet, isTrue);
expect(engine.transport, Transport.held);
});
test("the previous media's buffer never starts the new one", () {
join('a', 'alice');
join('b', 'bob');
engine.applyReady('a', true);
engine.applyReady('b', true);
engine.applySwap('a', ep2);
final oldEvent = engine.eventId - 1;
// Sent before the swap reached the clients, landing after it.
engine.applyReport('a', 30, 20.0, oldEvent, mediaId: 1);
engine.applyReport('b', 30, 20.0, oldEvent, mediaId: 1);
expect(engine.members['a']!.buffered, 0);
engine.tick();
expect(engine.transport, Transport.held);
engine.applyReport('a', 0, 6.0, engine.eventId, mediaId: 2);
engine.applyReport('b', 0, 6.0, engine.eventId, mediaId: 2);
expect(engine.transport, Transport.playing);
});
});
group('force with actor feed', () {