Merge pull request #3541 from jellyfin/pause
Keep playstate during syncplay group creation
This commit is contained in:
commit
203825f772
|
@ -194,26 +194,24 @@ namespace Emby.Server.Implementations.SyncPlay
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void InitGroup(SessionInfo session, CancellationToken cancellationToken)
|
||||
public void CreateGroup(SessionInfo session, CancellationToken cancellationToken)
|
||||
{
|
||||
_group.AddSession(session);
|
||||
_syncPlayManager.AddSessionToGroup(session, this);
|
||||
|
||||
_group.PlayingItem = session.FullNowPlayingItem;
|
||||
_group.IsPaused = true;
|
||||
_group.IsPaused = session.PlayState.IsPaused;
|
||||
_group.PositionTicks = session.PlayState.PositionTicks ?? 0;
|
||||
_group.LastActivity = DateTime.UtcNow;
|
||||
|
||||
var updateSession = NewSyncPlayGroupUpdate(GroupUpdateType.GroupJoined, DateToUTCString(DateTime.UtcNow));
|
||||
SendGroupUpdate(session, BroadcastType.CurrentSession, updateSession, cancellationToken);
|
||||
var pauseCommand = NewSyncPlayCommand(SendCommandType.Pause);
|
||||
SendCommand(session, BroadcastType.CurrentSession, pauseCommand, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SessionJoin(SessionInfo session, JoinGroupRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (session.NowPlayingItem?.Id == _group.PlayingItem.Id && request.PlayingItemId == _group.PlayingItem.Id)
|
||||
if (session.NowPlayingItem?.Id == _group.PlayingItem.Id)
|
||||
{
|
||||
_group.AddSession(session);
|
||||
_syncPlayManager.AddSessionToGroup(session, this);
|
||||
|
@ -224,7 +222,7 @@ namespace Emby.Server.Implementations.SyncPlay
|
|||
var updateOthers = NewSyncPlayGroupUpdate(GroupUpdateType.UserJoined, session.UserName);
|
||||
SendGroupUpdate(session, BroadcastType.AllExceptCurrentSession, updateOthers, cancellationToken);
|
||||
|
||||
// Client join and play, syncing will happen client side
|
||||
// Syncing will happen client-side
|
||||
if (!_group.IsPaused)
|
||||
{
|
||||
var playCommand = NewSyncPlayCommand(SendCommandType.Play);
|
||||
|
@ -262,10 +260,9 @@ namespace Emby.Server.Implementations.SyncPlay
|
|||
/// <inheritdoc />
|
||||
public void HandleRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
// The server's job is to mantain a consistent state to which clients refer to,
|
||||
// as also to notify clients of state changes.
|
||||
// The actual syncing of media playback happens client side.
|
||||
// Clients are aware of the server's time and use it to sync.
|
||||
// The server's job is to maintain a consistent state for clients to reference
|
||||
// and notify clients of state changes. The actual syncing of media playback
|
||||
// happens client side. Clients are aware of the server's time and use it to sync.
|
||||
switch (request.Type)
|
||||
{
|
||||
case PlaybackRequestType.Play:
|
||||
|
@ -277,13 +274,13 @@ namespace Emby.Server.Implementations.SyncPlay
|
|||
case PlaybackRequestType.Seek:
|
||||
HandleSeekRequest(session, request, cancellationToken);
|
||||
break;
|
||||
case PlaybackRequestType.Buffering:
|
||||
case PlaybackRequestType.Buffer:
|
||||
HandleBufferingRequest(session, request, cancellationToken);
|
||||
break;
|
||||
case PlaybackRequestType.BufferingDone:
|
||||
case PlaybackRequestType.Ready:
|
||||
HandleBufferingDoneRequest(session, request, cancellationToken);
|
||||
break;
|
||||
case PlaybackRequestType.UpdatePing:
|
||||
case PlaybackRequestType.Ping:
|
||||
HandlePingUpdateRequest(session, request);
|
||||
break;
|
||||
}
|
||||
|
@ -301,7 +298,7 @@ namespace Emby.Server.Implementations.SyncPlay
|
|||
{
|
||||
// Pick a suitable time that accounts for latency
|
||||
var delay = _group.GetHighestPing() * 2;
|
||||
delay = delay < _group.DefaulPing ? _group.DefaulPing : delay;
|
||||
delay = delay < _group.DefaultPing ? _group.DefaultPing : delay;
|
||||
|
||||
// Unpause group and set starting point in future
|
||||
// Clients will start playback at LastActivity (datetime) from PositionTicks (playback position)
|
||||
|
@ -337,8 +334,9 @@ namespace Emby.Server.Implementations.SyncPlay
|
|||
var currentTime = DateTime.UtcNow;
|
||||
var elapsedTime = currentTime - _group.LastActivity;
|
||||
_group.LastActivity = currentTime;
|
||||
|
||||
// Seek only if playback actually started
|
||||
// (a pause request may be issued during the delay added to account for latency)
|
||||
// Pause request may be issued during the delay added to account for latency
|
||||
_group.PositionTicks += elapsedTime.Ticks > 0 ? elapsedTime.Ticks : 0;
|
||||
|
||||
var command = NewSyncPlayCommand(SendCommandType.Pause);
|
||||
|
@ -451,7 +449,7 @@ namespace Emby.Server.Implementations.SyncPlay
|
|||
{
|
||||
// Client, that was buffering, resumed playback but did not update others in time
|
||||
delay = _group.GetHighestPing() * 2;
|
||||
delay = delay < _group.DefaulPing ? _group.DefaulPing : delay;
|
||||
delay = delay < _group.DefaultPing ? _group.DefaultPing : delay;
|
||||
|
||||
_group.LastActivity = currentTime.AddMilliseconds(
|
||||
delay);
|
||||
|
@ -495,7 +493,7 @@ namespace Emby.Server.Implementations.SyncPlay
|
|||
private void HandlePingUpdateRequest(SessionInfo session, PlaybackRequest request)
|
||||
{
|
||||
// Collected pings are used to account for network latency when unpausing playback
|
||||
_group.UpdatePing(session, request.Ping ?? _group.DefaulPing);
|
||||
_group.UpdatePing(session, request.Ping ?? _group.DefaultPing);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
@ -170,10 +170,11 @@ namespace Emby.Server.Implementations.SyncPlay
|
|||
{
|
||||
_logger.LogWarning("NewGroup: {0} does not have permission to create groups.", session.Id);
|
||||
|
||||
var error = new GroupUpdate<string>()
|
||||
var error = new GroupUpdate<string>
|
||||
{
|
||||
Type = GroupUpdateType.CreateGroupDenied
|
||||
};
|
||||
|
||||
_sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None);
|
||||
return;
|
||||
}
|
||||
|
@ -188,7 +189,7 @@ namespace Emby.Server.Implementations.SyncPlay
|
|||
var group = new SyncPlayController(_sessionManager, this);
|
||||
_groups[group.GetGroupId()] = group;
|
||||
|
||||
group.InitGroup(session, cancellationToken);
|
||||
group.CreateGroup(session, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,6 +206,7 @@ namespace Emby.Server.Implementations.SyncPlay
|
|||
{
|
||||
Type = GroupUpdateType.JoinGroupDenied
|
||||
};
|
||||
|
||||
_sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None);
|
||||
return;
|
||||
}
|
||||
|
@ -300,9 +302,9 @@ namespace Emby.Server.Implementations.SyncPlay
|
|||
group => group.GetPlayingItemId().Equals(filterItemId) && HasAccessToItem(user, group.GetPlayingItemId())).Select(
|
||||
group => group.GetInfo()).ToList();
|
||||
}
|
||||
// Otherwise show all available groups
|
||||
else
|
||||
{
|
||||
// Otherwise show all available groups
|
||||
return _groups.Values.Where(
|
||||
group => HasAccessToItem(user, group.GetPlayingItemId())).Select(
|
||||
group => group.GetInfo()).ToList();
|
||||
|
@ -322,6 +324,7 @@ namespace Emby.Server.Implementations.SyncPlay
|
|||
{
|
||||
Type = GroupUpdateType.JoinGroupDenied
|
||||
};
|
||||
|
||||
_sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None);
|
||||
return;
|
||||
}
|
||||
|
@ -366,7 +369,6 @@ namespace Emby.Server.Implementations.SyncPlay
|
|||
}
|
||||
|
||||
_sessionToGroupMap.Remove(session.Id, out var tempGroup);
|
||||
|
||||
if (!tempGroup.GetGroupId().Equals(group.GetGroupId()))
|
||||
{
|
||||
throw new InvalidOperationException("Session was in wrong group!");
|
||||
|
|
|
@ -27,13 +27,6 @@ namespace MediaBrowser.Api.SyncPlay
|
|||
/// <value>The Group id to join.</value>
|
||||
[ApiMember(Name = "GroupId", Description = "Group Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string GroupId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the playing item id.
|
||||
/// </summary>
|
||||
/// <value>The client's currently playing item id.</value>
|
||||
[ApiMember(Name = "PlayingItemId", Description = "Client's playing item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string PlayingItemId { get; set; }
|
||||
}
|
||||
|
||||
[Route("/SyncPlay/Leave", "POST", Summary = "Leave joined SyncPlay group")]
|
||||
|
@ -89,7 +82,7 @@ namespace MediaBrowser.Api.SyncPlay
|
|||
public long PositionTicks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this is a buffering or a buffering-done request.
|
||||
/// Gets or sets whether this is a buffering or a ready request.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if buffering is complete; <c>false</c> otherwise.</value>
|
||||
[ApiMember(Name = "BufferingDone", IsRequired = true, DataType = "bool", ParameterType = "query", Verb = "POST")]
|
||||
|
@ -150,25 +143,15 @@ namespace MediaBrowser.Api.SyncPlay
|
|||
var currentSession = GetSession(_sessionContext);
|
||||
|
||||
Guid groupId;
|
||||
Guid playingItemId = Guid.Empty;
|
||||
|
||||
if (!Guid.TryParse(request.GroupId, out groupId))
|
||||
{
|
||||
Logger.LogError("JoinGroup: {0} is not a valid format for GroupId. Ignoring request.", request.GroupId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Both null and empty strings mean that client isn't playing anything
|
||||
if (!string.IsNullOrEmpty(request.PlayingItemId) && !Guid.TryParse(request.PlayingItemId, out playingItemId))
|
||||
{
|
||||
Logger.LogError("JoinGroup: {0} is not a valid format for PlayingItemId. Ignoring request.", request.PlayingItemId);
|
||||
return;
|
||||
}
|
||||
|
||||
var joinRequest = new JoinGroupRequest()
|
||||
{
|
||||
GroupId = groupId,
|
||||
PlayingItemId = playingItemId
|
||||
GroupId = groupId
|
||||
};
|
||||
|
||||
_syncPlayManager.JoinGroup(currentSession, groupId, joinRequest, CancellationToken.None);
|
||||
|
@ -254,7 +237,7 @@ namespace MediaBrowser.Api.SyncPlay
|
|||
var currentSession = GetSession(_sessionContext);
|
||||
var syncPlayRequest = new PlaybackRequest()
|
||||
{
|
||||
Type = request.BufferingDone ? PlaybackRequestType.BufferingDone : PlaybackRequestType.Buffering,
|
||||
Type = request.BufferingDone ? PlaybackRequestType.Ready : PlaybackRequestType.Buffer,
|
||||
When = DateTime.Parse(request.When),
|
||||
PositionTicks = request.PositionTicks
|
||||
};
|
||||
|
@ -270,7 +253,7 @@ namespace MediaBrowser.Api.SyncPlay
|
|||
var currentSession = GetSession(_sessionContext);
|
||||
var syncPlayRequest = new PlaybackRequest()
|
||||
{
|
||||
Type = PlaybackRequestType.UpdatePing,
|
||||
Type = PlaybackRequestType.Ping,
|
||||
Ping = Convert.ToInt64(request.Ping)
|
||||
};
|
||||
_syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace MediaBrowser.Controller.SyncPlay
|
|||
/// <summary>
|
||||
/// Gets the default ping value used for sessions.
|
||||
/// </summary>
|
||||
public long DefaulPing { get; } = 500;
|
||||
public long DefaultPing { get; } = 500;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the group identifier.
|
||||
|
@ -70,16 +70,16 @@ namespace MediaBrowser.Controller.SyncPlay
|
|||
/// <param name="session">The session.</param>
|
||||
public void AddSession(SessionInfo session)
|
||||
{
|
||||
if (ContainsSession(session.Id.ToString()))
|
||||
if (ContainsSession(session.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var member = new GroupMember();
|
||||
member.Session = session;
|
||||
member.Ping = DefaulPing;
|
||||
member.Ping = DefaultPing;
|
||||
member.IsBuffering = false;
|
||||
Participants[session.Id.ToString()] = member;
|
||||
Participants[session.Id] = member;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -88,12 +88,12 @@ namespace MediaBrowser.Controller.SyncPlay
|
|||
/// <param name="session">The session.</param>
|
||||
public void RemoveSession(SessionInfo session)
|
||||
{
|
||||
if (!ContainsSession(session.Id.ToString()))
|
||||
if (!ContainsSession(session.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Participants.Remove(session.Id.ToString(), out _);
|
||||
Participants.Remove(session.Id, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -103,12 +103,12 @@ namespace MediaBrowser.Controller.SyncPlay
|
|||
/// <param name="ping">The ping.</param>
|
||||
public void UpdatePing(SessionInfo session, long ping)
|
||||
{
|
||||
if (!ContainsSession(session.Id.ToString()))
|
||||
if (!ContainsSession(session.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Participants[session.Id.ToString()].Ping = ping;
|
||||
Participants[session.Id].Ping = ping;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -117,7 +117,7 @@ namespace MediaBrowser.Controller.SyncPlay
|
|||
/// <value name="session">The highest ping in the group.</value>
|
||||
public long GetHighestPing()
|
||||
{
|
||||
long max = Int64.MinValue;
|
||||
long max = long.MinValue;
|
||||
foreach (var session in Participants.Values)
|
||||
{
|
||||
max = Math.Max(max, session.Ping);
|
||||
|
@ -133,12 +133,12 @@ namespace MediaBrowser.Controller.SyncPlay
|
|||
/// <param name="isBuffering">The state.</param>
|
||||
public void SetBuffering(SessionInfo session, bool isBuffering)
|
||||
{
|
||||
if (!ContainsSession(session.Id.ToString()))
|
||||
if (!ContainsSession(session.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Participants[session.Id.ToString()].IsBuffering = isBuffering;
|
||||
Participants[session.Id].IsBuffering = isBuffering;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace MediaBrowser.Controller.SyncPlay
|
|||
public class GroupMember
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets whether this member is buffering.
|
||||
/// Gets or sets a value indicating whether this member is buffering.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if member is buffering; <c>false</c> otherwise.</value>
|
||||
public bool IsBuffering { get; set; }
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace MediaBrowser.Controller.SyncPlay
|
|||
/// </summary>
|
||||
/// <param name="session">The session.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
void InitGroup(SessionInfo session, CancellationToken cancellationToken);
|
||||
void CreateGroup(SessionInfo session, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the session to the group.
|
||||
|
|
|
@ -12,11 +12,5 @@ namespace MediaBrowser.Model.SyncPlay
|
|||
/// </summary>
|
||||
/// <value>The Group id to join.</value>
|
||||
public Guid GroupId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the playing item id.
|
||||
/// </summary>
|
||||
/// <value>The client's currently playing item id.</value>
|
||||
public Guid PlayingItemId { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,16 +23,16 @@ namespace MediaBrowser.Model.SyncPlay
|
|||
/// <summary>
|
||||
/// A user is signaling that playback is buffering.
|
||||
/// </summary>
|
||||
Buffering = 3,
|
||||
Buffer = 3,
|
||||
|
||||
/// <summary>
|
||||
/// A user is signaling that playback resumed.
|
||||
/// </summary>
|
||||
BufferingDone = 4,
|
||||
Ready = 4,
|
||||
|
||||
/// <summary>
|
||||
/// A user is reporting its ping.
|
||||
/// </summary>
|
||||
UpdatePing = 5
|
||||
Ping = 5
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user