Design: coordinate parties on top of Emby's existing session remote-control commands (SendPlaystateCommand) rather than building a new player. Each member keeps their own Emby app/controls; the server periodically corrects drift and mirrors pause/unpause against one reference member's reported position. - src/WatchParty: plugin skeleton (BasePlugin, IServerEntryPoint, IService/[Route] API, in-memory party store, drift-correction loop) - web/control-panel: plain HTML/JS/CSS companion page to create/join parties and resync, served by the plugin itself - docs/architecture.md: full design, sources consulted, and an explicit list of SDK details that still need verifying against a real build (no .NET SDK / Emby SDK assemblies available in this environment to compile against) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
52 lines
1.7 KiB
C#
52 lines
1.7 KiB
C#
using System;
|
|
|
|
namespace WatchParty.Parties
|
|
{
|
|
public class PartyMember
|
|
{
|
|
public Guid UserId { get; set; }
|
|
|
|
public string UserName { get; set; }
|
|
|
|
/// <summary>
|
|
/// The Emby session this member is watching from. Resolved at join time
|
|
/// from the caller's active sessions - see
|
|
/// docs/architecture.md#which-session-is-mine for the current, fairly
|
|
/// blunt, v1 heuristic.
|
|
/// </summary>
|
|
public string SessionId { get; set; }
|
|
|
|
public DateTime JoinedUtc { get; set; }
|
|
|
|
/// <summary>Last position reported for this member, in ticks (100ns units, Emby convention).</summary>
|
|
public long LastKnownPositionTicks { get; set; }
|
|
|
|
public bool LastKnownIsPaused { get; set; }
|
|
|
|
/// <summary>When LastKnownPositionTicks/LastKnownIsPaused were last updated.</summary>
|
|
public DateTime LastUpdatedUtc { get; set; }
|
|
|
|
/// <summary>When we last sent this member a corrective command, for cooldown purposes.</summary>
|
|
public DateTime? LastResyncUtc { get; set; }
|
|
|
|
/// <summary>
|
|
/// Extrapolates where this member's playback should be *right now*,
|
|
/// assuming unchanged play/pause state since LastUpdatedUtc.
|
|
/// </summary>
|
|
public long EstimatedPositionTicks(DateTime utcNow)
|
|
{
|
|
if (LastKnownIsPaused)
|
|
{
|
|
return LastKnownPositionTicks;
|
|
}
|
|
|
|
var elapsed = utcNow - LastUpdatedUtc;
|
|
if (elapsed <= TimeSpan.Zero)
|
|
{
|
|
return LastKnownPositionTicks;
|
|
}
|
|
|
|
return LastKnownPositionTicks + elapsed.Ticks;
|
|
}
|
|
}
|
|
}
|