forked from Foxyz/sync_replay_project
Restructure step 1: lasting identity, presence and protocol 16
Server and core: version ranges on both sides with a coded refusal; a client id maps to the same member and token for the server's life; a drop is `gone` for a 10-minute grace keeping ready, presence, queue items, relay and avatar; `bye` leaves at once; ordered takeover (seq, replaced/stale); presence joining/in/away/gone with one PresencePolicy; joiners never pause playback and hold a held gate at most ~15 s; state.hold; feed ids, seq and replay; the relay lives through its source's grace and continues on re-dial; queue `by` lasting, auto items and cancel; MediaRef carried; client ids never logged. App: persisted client id and seq; Connection with unstable/lost/ replaced states, backoff with jitter, no lost/back pair per flap; a drop keeps the session (state, player, media, relay source); presence in the strip and members card; sit out = away intent; feed matched by id; auto sit-out (idle when not playing, minimised for a while; never when alone) replaces auto-unready; bye on close and Leave, not on update restart; relay source re-dials through its grace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
51c1c5ab78
commit
9f2586b7ff
85 files changed
+5198
-1902
No files matched your search
@@ -1,6 +1,7 @@
|
||||
import 'package:sync_relay_core/core.dart';
|
||||
|
||||
import '../jellyfin/session_media.dart' show compareMemberIds;
|
||||
import '../session/party.dart' show isPresent;
|
||||
|
||||
// Who puts an Up next item up. Pure: only what every client sees in
|
||||
// `state` decides, so all clients agree without a wire message (like the
|
||||
@@ -12,20 +13,22 @@ import '../jellyfin/session_media.dart' show compareMemberIds;
|
||||
typedef QueuePerformer = ({String? id, String? why});
|
||||
|
||||
/// - A link passed on by its adder, or the adder's file: only the adder
|
||||
/// (their app has the file, the cookies). Gone: skipped.
|
||||
/// (their app has the file, the cookies). Gone (disconnected, in its
|
||||
/// grace): it waits for them; left for good: skipped.
|
||||
/// - Jellyfin: the adder when it plays that server with its own account,
|
||||
/// else the lowest member id that does (`jf[serverId] == true`, not
|
||||
/// stale, not sitting out). Nobody: skipped.
|
||||
/// else the lowest member id that does (`jf[serverId] == true`,
|
||||
/// present: `in` or joining). Nobody: skipped.
|
||||
/// - A link everyone opens, or one the server fetches: the adder when
|
||||
/// present (not stale or sitting out), else the lowest such member id.
|
||||
/// present, else the lowest present member id.
|
||||
QueuePerformer queuePerformer(QueueItem item, List<MemberView> members) {
|
||||
MemberView? adder;
|
||||
for (final m in members) {
|
||||
if (m.id == item.by) adder = m;
|
||||
}
|
||||
bool active(MemberView m) => !m.stale && !m.out;
|
||||
bool active(MemberView m) => isPresent(m);
|
||||
if (item.how.adderOnly) {
|
||||
if (adder == null) return (id: null, why: '${_name(item)} left');
|
||||
if (adder.presence == Presence.gone) return (id: null, why: null);
|
||||
return (id: adder.id, why: null);
|
||||
}
|
||||
final ref = item.media.jellyfin;
|
||||
@@ -48,8 +51,9 @@ String? autoAdvanceLowest(Iterable<MemberView> members) {
|
||||
}
|
||||
|
||||
/// Who takes an item nobody can put up off the queue (`remove` with why):
|
||||
/// the lowest member id that is not stale.
|
||||
String? queueJanitor(List<MemberView> members) => autoAdvanceLowest(members.where((m) => !m.stale));
|
||||
/// the lowest member id that is connected (not gone).
|
||||
String? queueJanitor(List<MemberView> members) =>
|
||||
autoAdvanceLowest(members.where((m) => m.presence != Presence.gone));
|
||||
|
||||
/// How [item] plays, in a few words for its row: "through Foxyz" (you:
|
||||
/// "through you"), "server fetches", "everyone fetches", "Jellyfin",
|
||||
|
||||
Reference in new issue
Block a user