Skip to content

Syncplay source syncing#120

Draft
giorgiobrullo wants to merge 9 commits intolostb1t:mainfrom
giorgiobrullo:feat/syncplay-source-sync
Draft

Syncplay source syncing#120
giorgiobrullo wants to merge 9 commits intolostb1t:mainfrom
giorgiobrullo:feat/syncplay-source-sync

Conversation

@giorgiobrullo
Copy link
Copy Markdown

SyncPlay only syncs ItemId + position, not MediaSourceId. When Gelato injects multiple sources (different encodes from Stremio addons), group members can end up on different versions causing desync.

Currently, SyncPlay always forces users onto the first source stream. This is problematic because:

  • The first source may not be the one you want and you might have specifically chosen a different encode/remux before creating the group
  • Different group members can see sources in different orders, so "first source" isn't even consistent
  • If members end up on different sources, the files often have different durations, causing SyncPlay position sync to break or behave erratically
  • There's no way to switch versions while in a group, it always snaps back to the first source

This adds server-side source synchronization across SyncPlay groups.

What it does

  • Tracks group membership via an MVC action filter on SyncPlay create/join/leave
  • Caches the active source per group+item, seeds from group members' sessions on join
  • Overrides mediaSourceId for SyncPlay-initiated playback (which never carries it)
  • Bypasses per-user source filtering in SyncPlay so all members see all sources
  • Doesn't cache the item's own ID (it's the default and would block version switching)
  • Propagates explicit version switches to other group members

Test scenarios

  • Create group → source preserved (no switch to default)
  • Join group → get the same source as leader
  • Switch version in group → actually switches
  • Normal playback unaffected

giorgiobrullo and others added 9 commits February 28, 2026 18:05
When a SyncPlay group starts playback, each member independently
selects a media source version. This causes group members to end up
on different streams (different quality, provider, etc.).

Cache the first member's resolved source per item so that subsequent
members in the same SyncPlay session reuse it. Explicit user
selections still take priority and update the group cache.
Add SyncPlayGroupFilter that intercepts SyncPlay create/join/leave
controller actions to maintain a userId→groupId mapping in
SyncPlayGroupTracker. The media source cache now uses
group-scoped keys (sp:{groupId}:{itemId}) so two different
SyncPlay groups watching the same item won't pollute each other's
source selection.
When a user explicitly selects a different media source version
while in a SyncPlay group, send a PlayNow command with the new
MediaSourceId to all other group members via ISessionManager.
This ensures everyone switches to the same stream instead of
only updating the cache (which only affects future requests).

The propagation is fire-and-forget to avoid blocking the
initiator's playback request. A loop-prevention check ensures
we only propagate when the selected source actually differs
from the previously cached one.
…lock

Direct constructor injection of ISyncPlayManager and ISessionManager in
MediaSourceManagerDecorator caused a circular dependency deadlock during
service resolution, preventing Jellyfin from starting.

Switch to Lazy<> wrappers (same pattern used for GelatoManager and
SubtitleProvider) to break the cycle and defer resolution until first use.
…own ID

- SyncPlay members now see ALL sources regardless of which user triggered
  the catalog sync, so the cached source ID can always be matched
- Don't cache the item's own ID (sources[0]) — it's the default and
  caching it blocks version switching since SyncPlay commands never carry
  mediaSourceId
- Seed from ALL group members' sessions when cache is empty (not just
  current user), filter out item's own ID from seeds
- Only override mediaSourceId when cache holds a specific stream ID
- Add virtual source guard (gelato:// / stremio://) with cache eviction
- Add null-user safety for streaming endpoint calls
- Upgrade SyncPlay diagnostic logging to Info level

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Downgrade diagnostic logging from Info to Debug (keep Info only for
  group create/join tracking in filter)
- Extract itemIdStr to shared scope, remove duplicate computation
- Expand one-liner try-catch to multi-line for consistency
- Fix comment accuracy (per-user configs exist, not strictly server-wide)
- Remove redundant "no override" / "NOT caching" log messages
- Trim verbose log lines throughout

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the inline SyncPlay source resolution and cache update logic out of
GetPlaybackMediaSources into:
- ResolveSyncPlaySource: determines override source ID from cache/sessions
- UpdateSyncPlayCache: caches resolved source, propagates version switches
- SyncPlaySourceState: record struct passing state between the two

GetPlaybackMediaSources now reads as 4 lines instead of ~100 inline.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The session seeding could pick up a MediaSourceId from a group member's
session that was playing a different item, then try to use it as a source
for the current item — which would never match and fall back to default.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SyncPlay users now check a shared (user-agnostic) item cache key
alongside their per-user key. When one group member triggers
SyncStreams for an item, other members joining the same item
get an instant cache hit instead of re-fetching from AIOStreams.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant