Restructure step 6: what's next and watch tracking
- What's next is the server's: state.advance counts down (10 s) when the media finishes with an Up next item first, holds while its performer or warm source is gone, and sets queuePlay at its end; cancel is shared, a seek or swap ends it; the server skips items nobody can put up. Shared rules in core whats_next.dart; the app's janitor, per-screen countdown and end-of-episode card are gone. - The continuation (next Jellyfin episode, next file of a local show with its gaps) is an auto item under "Continues with", prepared like Up next's first item. - Watch tracking by time In (D3): only time played while this viewer is in counts; away, paused, held, jumps and stalls never do (P7, P9). WatchTracker + device WatchHistory (files, links); Jellyfin progress reads the same watch; records are the AniList seam. - A server restart no longer loses what played: apps hold their media and one offers it back (swap.restore, first offer wins), Up next is re-added, the source re-feeds, and the watch carries on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a87ee98faa
commit
741b258ac1
55 files changed
+2724
-964
No files matched your search
@@ -4,7 +4,6 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:sync_relay_core/core.dart';
|
||||
|
||||
import '../screens/home/picked_media.dart';
|
||||
import '../session/party.dart' show isPresent;
|
||||
import 'accounts.dart';
|
||||
import 'format.dart';
|
||||
import '../library/media_refs.dart';
|
||||
@@ -341,26 +340,3 @@ Duration nextPromptStart(JellyfinItem? item, Duration runtime, {Duration? ending
|
||||
const lead = Duration(minutes: 2);
|
||||
return runtime > lead ? runtime - lead : Duration.zero;
|
||||
}
|
||||
|
||||
/// Who performs the auto-advance swap, so several counting clients do not
|
||||
/// swap twice: the lowest member id among members that play the item with
|
||||
/// their own account (`jf[serverId] == true`, present: `in` or joining). Only what every
|
||||
/// client sees in `state` decides (who put the item up is not in it: a
|
||||
/// late joiner never saw that swap), so all clients agree.
|
||||
String? autoAdvanceSwapper(List<MemberView> members, String serverId) {
|
||||
final eligible = [
|
||||
for (final m in members)
|
||||
if (isPresent(m) && m.jf[serverId] == true) m
|
||||
];
|
||||
if (eligible.isEmpty) return null;
|
||||
eligible.sort((a, b) => compareMemberIds(a.id, b.id));
|
||||
return eligible.first.id;
|
||||
}
|
||||
|
||||
/// "m2" before "m10": numeric suffix order, then plain text.
|
||||
int compareMemberIds(String a, String b) {
|
||||
final na = int.tryParse(a.replaceFirst(RegExp(r'^\D+'), ''));
|
||||
final nb = int.tryParse(b.replaceFirst(RegExp(r'^\D+'), ''));
|
||||
if (na != null && nb != null && na != nb) return na.compareTo(nb);
|
||||
return a.compareTo(b);
|
||||
}
|
||||
Reference in new issue
Block a user