diff options
| author | Ionut Andrei Oanca <oancaionutandrei@gmail.com> | 2020-09-24 23:04:21 +0200 |
|---|---|---|
| committer | Ionut Andrei Oanca <oancaionutandrei@gmail.com> | 2020-10-16 12:06:29 +0200 |
| commit | 8819a9d478e6fc11dbfdcff80d9a2dc175953373 (patch) | |
| tree | 8a159745dd08ebfa6d83e881c8eb6a07df0a589d /MediaBrowser.Controller | |
| parent | ed2eabec16aafdf795f5ea4f8834ffdc74bc149f (diff) | |
Add playlist-sync and group-wait to SyncPlay
Diffstat (limited to 'MediaBrowser.Controller')
27 files changed, 1331 insertions, 311 deletions
diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index 04c3004ee..9ad8557ce 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -143,22 +143,22 @@ namespace MediaBrowser.Controller.Session Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken); /// <summary> - /// Sends the SyncPlayCommand. + /// Sends a SyncPlayCommand to a session. /// </summary> - /// <param name="sessionId">The session id.</param> + /// <param name="session">The session.</param> /// <param name="command">The command.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - Task SendSyncPlayCommand(string sessionId, SendCommand command, CancellationToken cancellationToken); + Task SendSyncPlayCommand(SessionInfo session, SendCommand command, CancellationToken cancellationToken); /// <summary> - /// Sends the SyncPlayGroupUpdate. + /// Sends a SyncPlayGroupUpdate to a session. /// </summary> - /// <param name="sessionId">The session id.</param> + /// <param name="session">The session.</param> /// <param name="command">The group update.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - Task SendSyncPlayGroupUpdate<T>(string sessionId, GroupUpdate<T> command, CancellationToken cancellationToken); + Task SendSyncPlayGroupUpdate<T>(SessionInfo session, GroupUpdate<T> command, CancellationToken cancellationToken); /// <summary> /// Sends the browse command. diff --git a/MediaBrowser.Controller/SyncPlay/GroupInfo.cs b/MediaBrowser.Controller/SyncPlay/GroupInfo.cs deleted file mode 100644 index cdd24d0b5..000000000 --- a/MediaBrowser.Controller/SyncPlay/GroupInfo.cs +++ /dev/null @@ -1,154 +0,0 @@ -using System; -using System.Collections.Generic; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Session; - -namespace MediaBrowser.Controller.SyncPlay -{ - /// <summary> - /// Class GroupInfo. - /// </summary> - /// <remarks> - /// Class is not thread-safe, external locking is required when accessing methods. - /// </remarks> - public class GroupInfo - { - /// <summary> - /// The default ping value used for sessions. - /// </summary> - public const long DefaultPing = 500; - - /// <summary> - /// Gets the group identifier. - /// </summary> - /// <value>The group identifier.</value> - public Guid GroupId { get; } = Guid.NewGuid(); - - /// <summary> - /// Gets or sets the playing item. - /// </summary> - /// <value>The playing item.</value> - public BaseItem PlayingItem { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether there are position ticks. - /// </summary> - /// <value>The position ticks.</value> - public long PositionTicks { get; set; } - - /// <summary> - /// Gets or sets the last activity. - /// </summary> - /// <value>The last activity.</value> - public DateTime LastActivity { get; set; } - - /// <summary> - /// Gets the participants. - /// </summary> - /// <value>The participants, or members of the group.</value> - public Dictionary<string, GroupMember> Participants { get; } = - new Dictionary<string, GroupMember>(StringComparer.OrdinalIgnoreCase); - - /// <summary> - /// Checks if a session is in this group. - /// </summary> - /// <param name="sessionId">The session id to check.</param> - /// <returns><c>true</c> if the session is in this group; <c>false</c> otherwise.</returns> - public bool ContainsSession(string sessionId) - { - return Participants.ContainsKey(sessionId); - } - - /// <summary> - /// Adds the session to the group. - /// </summary> - /// <param name="session">The session.</param> - public void AddSession(SessionInfo session) - { - Participants.TryAdd( - session.Id, - new GroupMember - { - Session = session, - Ping = DefaultPing, - IsBuffering = false - }); - } - - /// <summary> - /// Removes the session from the group. - /// </summary> - /// <param name="session">The session.</param> - public void RemoveSession(SessionInfo session) - { - Participants.Remove(session.Id); - } - - /// <summary> - /// Updates the ping of a session. - /// </summary> - /// <param name="session">The session.</param> - /// <param name="ping">The ping.</param> - public void UpdatePing(SessionInfo session, long ping) - { - if (Participants.TryGetValue(session.Id, out GroupMember value)) - { - value.Ping = ping; - } - } - - /// <summary> - /// Gets the highest ping in the group. - /// </summary> - /// <returns>The highest ping in the group.</returns> - public long GetHighestPing() - { - long max = long.MinValue; - foreach (var session in Participants.Values) - { - max = Math.Max(max, session.Ping); - } - - return max; - } - - /// <summary> - /// Sets the session's buffering state. - /// </summary> - /// <param name="session">The session.</param> - /// <param name="isBuffering">The state.</param> - public void SetBuffering(SessionInfo session, bool isBuffering) - { - if (Participants.TryGetValue(session.Id, out GroupMember value)) - { - value.IsBuffering = isBuffering; - } - } - - /// <summary> - /// Gets the group buffering state. - /// </summary> - /// <returns><c>true</c> if there is a session buffering in the group; <c>false</c> otherwise.</returns> - public bool IsBuffering() - { - foreach (var session in Participants.Values) - { - if (session.IsBuffering) - { - return true; - } - } - - return false; - } - - /// <summary> - /// Checks if the group is empty. - /// </summary> - /// <returns><c>true</c> if the group is empty; <c>false</c> otherwise.</returns> - public bool IsEmpty() - { - return Participants.Count == 0; - } - } -} diff --git a/MediaBrowser.Controller/SyncPlay/GroupMember.cs b/MediaBrowser.Controller/SyncPlay/GroupMember.cs index cde6f8e8c..9a9d30277 100644 --- a/MediaBrowser.Controller/SyncPlay/GroupMember.cs +++ b/MediaBrowser.Controller/SyncPlay/GroupMember.cs @@ -8,21 +8,27 @@ namespace MediaBrowser.Controller.SyncPlay public class GroupMember { /// <summary> - /// 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; } - - /// <summary> /// Gets or sets the session. /// </summary> /// <value>The session.</value> public SessionInfo Session { get; set; } /// <summary> - /// Gets or sets the ping. + /// Gets or sets the ping, in milliseconds. /// </summary> /// <value>The ping.</value> public long Ping { get; set; } + + /// <summary> + /// 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; } + + /// <summary> + /// Gets or sets a value indicating whether this member is following group playback. + /// </summary> + /// <value><c>true</c> to ignore member on group wait; <c>false</c> if they're following group playback.</value> + public bool IgnoreGroupWait { get; set; } } } diff --git a/MediaBrowser.Controller/SyncPlay/IPlaybackGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/IPlaybackGroupRequest.cs index a6e87a007..35ca64c8d 100644 --- a/MediaBrowser.Controller/SyncPlay/IPlaybackGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/IPlaybackGroupRequest.cs @@ -12,13 +12,12 @@ namespace MediaBrowser.Controller.SyncPlay /// <summary> /// Gets the playback request type. /// </summary> - /// <value>The playback request type.</value> - PlaybackRequestType Type(); + /// <returns>The playback request type.</returns> + PlaybackRequestType GetRequestType(); /// <summary> /// Applies the request to a group. /// </summary> - /// <value>The operation completion status.</value> - bool Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken); + void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs index 5ac2aeb24..9a4e1ee1e 100644 --- a/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs +++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs @@ -1,39 +1,41 @@ using System; using System.Threading; +using Jellyfin.Data.Entities; using MediaBrowser.Controller.Session; using MediaBrowser.Model.SyncPlay; namespace MediaBrowser.Controller.SyncPlay { /// <summary> - /// Interface ISyncPlayController. + /// Interface ISyncPlayGroupController. /// </summary> - public interface ISyncPlayController + public interface ISyncPlayGroupController { /// <summary> - /// Gets the group id. + /// Gets the group identifier. /// </summary> - /// <value>The group id.</value> - Guid GetGroupId(); + /// <value>The group identifier.</value> + Guid GroupId { get; } /// <summary> - /// Gets the playing item id. + /// Gets the play queue. /// </summary> - /// <value>The playing item id.</value> - Guid GetPlayingItemId(); + /// <value>The play queue.</value> + PlayQueueManager PlayQueue { get; } /// <summary> /// Checks if the group is empty. /// </summary> - /// <value>If the group is empty.</value> + /// <returns>If the group is empty.</returns> bool IsGroupEmpty(); /// <summary> /// Initializes the group with the session's info. /// </summary> /// <param name="session">The session.</param> + /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> - void CreateGroup(SessionInfo session, CancellationToken cancellationToken); + void CreateGroup(SessionInfo session, NewGroupRequest request, CancellationToken cancellationToken); /// <summary> /// Adds the session to the group. @@ -44,6 +46,14 @@ namespace MediaBrowser.Controller.SyncPlay void SessionJoin(SessionInfo session, JoinGroupRequest request, CancellationToken cancellationToken); /// <summary> + /// Restores the state of a session that already joined the group. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="request">The request.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void SessionRestore(SessionInfo session, JoinGroupRequest request, CancellationToken cancellationToken); + + /// <summary> /// Removes the session from the group. /// </summary> /// <param name="session">The session.</param> @@ -61,7 +71,15 @@ namespace MediaBrowser.Controller.SyncPlay /// <summary> /// Gets the info about the group for the clients. /// </summary> - /// <value>The group info for the clients.</value> + /// <returns>The group info for the clients.</returns> GroupInfoDto GetInfo(); + + /// <summary> + /// Checks if a user has access to all content in the play queue. + /// </summary> + /// <param name="user">The user.</param> + /// <returns><c>true</c> if the user can access the play queue; <c>false</c> otherwise.</returns> + bool HasAccessToPlayQueue(User user); + } } diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs index 6fa94e2ce..9bef3f559 100644 --- a/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs +++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs @@ -15,8 +15,9 @@ namespace MediaBrowser.Controller.SyncPlay /// Creates a new group. /// </summary> /// <param name="session">The session that's creating the group.</param> + /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> - void NewGroup(SessionInfo session, CancellationToken cancellationToken); + void NewGroup(SessionInfo session, NewGroupRequest request, CancellationToken cancellationToken); /// <summary> /// Adds the session to a group. @@ -38,9 +39,8 @@ namespace MediaBrowser.Controller.SyncPlay /// Gets list of available groups for a session. /// </summary> /// <param name="session">The session.</param> - /// <param name="filterItemId">The item id to filter by.</param> - /// <value>The list of available groups.</value> - List<GroupInfoDto> ListGroups(SessionInfo session, Guid filterItemId); + /// <returns>The list of available groups.</returns> + List<GroupInfoDto> ListGroups(SessionInfo session); /// <summary> /// Handle a request by a session in a group. @@ -56,7 +56,7 @@ namespace MediaBrowser.Controller.SyncPlay /// <param name="session">The session.</param> /// <param name="group">The group.</param> /// <exception cref="InvalidOperationException"></exception> - void AddSessionToGroup(SessionInfo session, ISyncPlayController group); + void AddSessionToGroup(SessionInfo session, ISyncPlayGroupController group); /// <summary> /// Unmaps a session from a group. @@ -64,6 +64,6 @@ namespace MediaBrowser.Controller.SyncPlay /// <param name="session">The session.</param> /// <param name="group">The group.</param> /// <exception cref="InvalidOperationException"></exception> - void RemoveSessionFromGroup(SessionInfo session, ISyncPlayController group); + void RemoveSessionFromGroup(SessionInfo session, ISyncPlayGroupController group); } } diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayState.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayState.cs index 55c9ee938..290576e30 100644 --- a/MediaBrowser.Controller/SyncPlay/ISyncPlayState.cs +++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayState.cs @@ -16,80 +16,201 @@ namespace MediaBrowser.Controller.SyncPlay GroupState GetGroupState(); /// <summary> + /// Handle a session that joined the group. + /// </summary> + /// <param name="context">The context of the state.</param> + /// <param name="prevState">The previous state.</param> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void SessionJoined(ISyncPlayStateContext context, GroupState prevState, SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Handle a session that is leaving the group. + /// </summary> + /// <param name="context">The context of the state.</param> + /// <param name="prevState">The previous state.</param> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void SessionLeaving(ISyncPlayStateContext context, GroupState prevState, SessionInfo session, CancellationToken cancellationToken); + + /// <summary> /// Generic handle. Context's state can change. /// </summary> /// <param name="context">The context of the state.</param> - /// <param name="newState">Whether the state has been just set.</param> - /// <param name="request">The play action.</param> + /// <param name="prevState">The previous state.</param> + /// <param name="request">The generic action.</param> /// <param name="session">The session.</param> /// <param name="cancellationToken">The cancellation token.</param> - /// <value>The operation completion status.</value> - bool HandleRequest(ISyncPlayStateContext context, bool newState, IPlaybackGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, IPlaybackGroupRequest request, SessionInfo session, CancellationToken cancellationToken); /// <summary> /// Handles a play action requested by a session. Context's state can change. /// </summary> /// <param name="context">The context of the state.</param> - /// <param name="newState">Whether the state has been just set.</param> + /// <param name="prevState">The previous state.</param> /// <param name="request">The play action.</param> /// <param name="session">The session.</param> /// <param name="cancellationToken">The cancellation token.</param> - /// <value>The operation completion status.</value> - bool HandleRequest(ISyncPlayStateContext context, bool newState, PlayGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, PlayGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Handles a playlist-item change requested by a session. Context's state can change. + /// </summary> + /// <param name="context">The context of the state.</param> + /// <param name="prevState">The previous state.</param> + /// <param name="request">The playlist-item change action.</param> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, SetPlaylistItemGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Handles a remove-items change requested by a session. Context's state can change. + /// </summary> + /// <param name="context">The context of the state.</param> + /// <param name="prevState">The previous state.</param> + /// <param name="request">The remove-items change action.</param> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, RemoveFromPlaylistGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Handles a move-item change requested by a session. Context's state should not change. + /// </summary> + /// <param name="context">The context of the state.</param> + /// <param name="prevState">The previous state.</param> + /// <param name="request">The move-item change action.</param> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, MovePlaylistItemGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Handles a queue change requested by a session. Context's state should not change. + /// </summary> + /// <param name="context">The context of the state.</param> + /// <param name="prevState">The previous state.</param> + /// <param name="request">The queue action.</param> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, QueueGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Handles an unpause action requested by a session. Context's state can change. + /// </summary> + /// <param name="context">The context of the state.</param> + /// <param name="prevState">The previous state.</param> + /// <param name="request">The unpause action.</param> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, UnpauseGroupRequest request, SessionInfo session, CancellationToken cancellationToken); /// <summary> /// Handles a pause action requested by a session. Context's state can change. /// </summary> /// <param name="context">The context of the state.</param> - /// <param name="newState">Whether the state has been just set.</param> + /// <param name="prevState">The previous state.</param> /// <param name="request">The pause action.</param> /// <param name="session">The session.</param> /// <param name="cancellationToken">The cancellation token.</param> - /// <value>The operation completion status.</value> - bool HandleRequest(ISyncPlayStateContext context, bool newState, PauseGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, PauseGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Handles a stop action requested by a session. Context's state can change. + /// </summary> + /// <param name="context">The context of the state.</param> + /// <param name="prevState">The previous state.</param> + /// <param name="request">The stop action.</param> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, StopGroupRequest request, SessionInfo session, CancellationToken cancellationToken); /// <summary> /// Handles a seek action requested by a session. Context's state can change. /// </summary> /// <param name="context">The context of the state.</param> - /// <param name="newState">Whether the state has been just set.</param> + /// <param name="prevState">The previous state.</param> /// <param name="request">The seek action.</param> /// <param name="session">The session.</param> /// <param name="cancellationToken">The cancellation token.</param> - /// <value>The operation completion status.</value> - bool HandleRequest(ISyncPlayStateContext context, bool newState, SeekGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, SeekGroupRequest request, SessionInfo session, CancellationToken cancellationToken); /// <summary> /// Handles a buffering action requested by a session. Context's state can change. /// </summary> /// <param name="context">The context of the state.</param> - /// <param name="newState">Whether the state has been just set.</param> + /// <param name="prevState">The previous state.</param> /// <param name="request">The buffering action.</param> /// <param name="session">The session.</param> /// <param name="cancellationToken">The cancellation token.</param> - /// <value>The operation completion status.</value> - bool HandleRequest(ISyncPlayStateContext context, bool newState, BufferGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, BufferGroupRequest request, SessionInfo session, CancellationToken cancellationToken); /// <summary> /// Handles a buffering-done action requested by a session. Context's state can change. /// </summary> /// <param name="context">The context of the state.</param> - /// <param name="newState">Whether the state has been just set.</param> + /// <param name="prevState">The previous state.</param> /// <param name="request">The buffering-done action.</param> /// <param name="session">The session.</param> /// <param name="cancellationToken">The cancellation token.</param> - /// <value>The operation completion status.</value> - bool HandleRequest(ISyncPlayStateContext context, bool newState, ReadyGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, ReadyGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Handles a next-track action requested by a session. Context's state can change. + /// </summary> + /// <param name="context">The context of the state.</param> + /// <param name="prevState">The previous state.</param> + /// <param name="request">The next-track action.</param> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, NextTrackGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Handles a previous-track action requested by a session. Context's state can change. + /// </summary> + /// <param name="context">The context of the state.</param> + /// <param name="prevState">The previous state.</param> + /// <param name="request">The previous-track action.</param> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, PreviousTrackGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Handles a repeat-mode change requested by a session. Context's state should not change. + /// </summary> + /// <param name="context">The context of the state.</param> + /// <param name="prevState">The previous state.</param> + /// <param name="request">The repeat-mode action.</param> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, SetRepeatModeGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Handles a shuffle-mode change requested by a session. Context's state should not change. + /// </summary> + /// <param name="context">The context of the state.</param> + /// <param name="prevState">The previous state.</param> + /// <param name="request">The shuffle-mode action.</param> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, SetShuffleModeGroupRequest request, SessionInfo session, CancellationToken cancellationToken); /// <summary> /// Updates ping of a session. Context's state should not change. /// </summary> /// <param name="context">The context of the state.</param> - /// <param name="newState">Whether the state has been just set.</param> + /// <param name="prevState">The previous state.</param> /// <param name="request">The buffering-done action.</param> /// <param name="session">The session.</param> /// <param name="cancellationToken">The cancellation token.</param> - /// <value>The operation completion status.</value> - bool HandleRequest(ISyncPlayStateContext context, bool newState, PingGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, PingGroupRequest request, SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Updates whether the session should be considered during group wait. Context's state should not change. + /// </summary> + /// <param name="context">The context of the state.</param> + /// <param name="prevState">The previous state.</param> + /// <param name="request">The ignore-wait action.</param> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void HandleRequest(ISyncPlayStateContext context, GroupState prevState, IgnoreWaitGroupRequest request, SessionInfo session, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayStateContext.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayStateContext.cs index 9bdb1ace6..18a685749 100644 --- a/MediaBrowser.Controller/SyncPlay/ISyncPlayStateContext.cs +++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayStateContext.cs @@ -12,10 +12,34 @@ namespace MediaBrowser.Controller.SyncPlay public interface ISyncPlayStateContext { /// <summary> - /// Gets the context's group. + /// Gets the default ping value used for sessions, in milliseconds. /// </summary> - /// <value>The group.</value> - GroupInfo GetGroup(); + /// <value>The default ping value used for sessions, in milliseconds.</value> + long DefaultPing { get; } + + /// <summary> + /// Gets the group identifier. + /// </summary> + /// <value>The group identifier.</value> + Guid GroupId { get; } + + /// <summary> + /// Gets or sets the position ticks. + /// </summary> + /// <value>The position ticks.</value> + long PositionTicks { get; set; } + + /// <summary> + /// Gets or sets the last activity. + /// </summary> + /// <value>The last activity.</value> + DateTime LastActivity { get; set; } + + /// <summary> + /// Gets the play queue. + /// </summary> + /// <value>The play queue.</value> + PlayQueueManager PlayQueue { get; } /// <summary> /// Sets a new state. @@ -30,7 +54,7 @@ namespace MediaBrowser.Controller.SyncPlay /// <param name="type">The filtering type.</param> /// <param name="message">The message to send.</param> /// <param name="cancellationToken">The cancellation token.</param> - /// <value>The task.</value> + /// <returns>The task.</returns> Task SendGroupUpdate<T>(SessionInfo from, SyncPlayBroadcastType type, GroupUpdate<T> message, CancellationToken cancellationToken); /// <summary> @@ -40,14 +64,14 @@ namespace MediaBrowser.Controller.SyncPlay /// <param name="type">The filtering type.</param> /// <param name="message">The message to send.</param> /// <param name="cancellationToken">The cancellation token.</param> - /// <value>The task.</value> + /// <returns>The task.</returns> Task SendCommand(SessionInfo from, SyncPlayBroadcastType type, SendCommand message, CancellationToken cancellationToken); /// <summary> /// Builds a new playback command with some default values. /// </summary> /// <param name="type">The command type.</param> - /// <value>The SendCommand.</value> + /// <returns>The SendCommand.</returns> SendCommand NewSyncPlayCommand(SendCommandType type); /// <summary> @@ -55,21 +79,135 @@ namespace MediaBrowser.Controller.SyncPlay /// </summary> /// <param name="type">The update type.</param> /// <param name="data">The data to send.</param> - /// <value>The GroupUpdate.</value> + /// <returns>The GroupUpdate.</returns> GroupUpdate<T> NewSyncPlayGroupUpdate<T>(GroupUpdateType type, T data); /// <summary> /// Converts DateTime to UTC string. /// </summary> - /// <param name="date">The date to convert.</param> - /// <value>The UTC string.</value> - string DateToUTCString(DateTime date); + /// <param name="dateTime">The date to convert.</param> + /// <returns>The UTC string.</returns> + string DateToUTCString(DateTime dateTime); /// <summary> /// Sanitizes the PositionTicks, considers the current playing item when available. /// </summary> /// <param name="positionTicks">The PositionTicks.</param> - /// <value>The sanitized PositionTicks.</value> + /// <returns>The sanitized PositionTicks.</returns> long SanitizePositionTicks(long? positionTicks); + + /// <summary> + /// Updates the ping of a session, in milliseconds. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="ping">The ping, in milliseconds.</param> + void UpdatePing(SessionInfo session, long ping); + + /// <summary> + /// Gets the highest ping in the group, in milliseconds. + /// </summary> + /// <returns>The highest ping in the group.</returns> + long GetHighestPing(); + + /// <summary> + /// Sets the session's buffering state. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="isBuffering">The state.</param> + void SetBuffering(SessionInfo session, bool isBuffering); + + /// <summary> + /// Sets the buffering state of all the sessions. + /// </summary> + /// <param name="isBuffering">The state.</param> + void SetAllBuffering(bool isBuffering); + + /// <summary> + /// Gets the group buffering state. + /// </summary> + /// <returns><c>true</c> if there is a session buffering in the group; <c>false</c> otherwise.</returns> + bool IsBuffering(); + + /// <summary> + /// Sets the session's group wait state. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="ignoreGroupWait">The state.</param> + void SetIgnoreGroupWait(SessionInfo session, bool ignoreGroupWait); + + /// <summary> + /// Sets a new play queue. + /// </summary> + /// <param name="playQueue">The new play queue.</param> + /// <param name="playingItemPosition">The playing item position in the play queue.</param> + /// <param name="startPositionTicks">The start position ticks.</param> + /// <returns><c>true</c> if the play queue has been changed; <c>false</c> is something went wrong.</returns> + bool SetPlayQueue(Guid[] playQueue, int playingItemPosition, long startPositionTicks); + + /// <summary> + /// Sets the playing item. + /// </summary> + /// <param name="playlistItemId">The new playing item id.</param> + /// <returns><c>true</c> if the play queue has been changed; <c>false</c> is something went wrong.</returns> + bool SetPlayingItem(string playlistItemId); + + /// <summary> + /// Removes items from the play queue. + /// </summary> + /// <param name="playlistItemIds">The items to remove.</param> + /// <returns><c>true</c> if playing item got removed; <c>false</c> otherwise.</returns> + bool RemoveFromPlayQueue(string[] playlistItemIds); + + /// <summary> + /// Moves an item in the play queue. + /// </summary> + /// <param name="playlistItemId">The playlist id of the item to move.</param> + /// <param name="newIndex">The new position.</param> + /// <returns><c>true</c> if item has been moved; <c>false</c> is something went wrong.</returns> + bool MoveItemInPlayQueue(string playlistItemId, int newIndex); + + /// <summary> + /// Updates the play queue. + /// </summary> + /// <param name="newItems">The new items to add to the play queue.</param> + /// <param name="mode">The mode with which the items will be added.</param> + /// <returns><c>true</c> if the play queue has been changed; <c>false</c> is something went wrong.</returns> + bool AddToPlayQueue(Guid[] newItems, string mode); + + /// <summary> + /// Restarts current item in play queue. + /// </summary> + void RestartCurrentItem(); + + /// <summary> + /// Picks next item in play queue. + /// </summary> + /// <returns><c>true</c> if the item changed; <c>false</c> otherwise.</returns> + bool NextItemInQueue(); + + /// <summary> + /// Picks previous item in play queue. + /// </summary> + /// <returns><c>true</c> if the item changed; <c>false</c> otherwise.</returns> + bool PreviousItemInQueue(); + + /// <summary> + /// Sets the repeat mode. + /// </summary> + /// <param name="mode">The new mode.</param> + void SetRepeatMode(string mode); + + /// <summary> + /// Sets the shuffle mode. + /// </summary> + /// <param name="mode">The new mode.</param> + void SetShuffleMode(string mode); + + /// <summary> + /// Creates a play queue update. + /// </summary> + /// <param name="reason">The reason for the update.</param> + /// <returns>The play queue update.</returns> + PlayQueueUpdate GetPlayQueueUpdate(PlayQueueUpdateReason reason); } } diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/BufferGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/BufferGroupRequest.cs index 21dae8e4e..0815dd79b 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/BufferGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/BufferGroupRequest.cs @@ -23,21 +23,27 @@ namespace MediaBrowser.Controller.SyncPlay public long PositionTicks { get; set; } /// <summary> - /// Gets or sets the playing item id. + /// Gets or sets the client playback status. /// </summary> - /// <value>The playing item id.</value> - public Guid PlayingItemId { get; set; } + /// <value>The client playback status.</value> + public bool IsPlaying { get; set; } + + /// <summary> + /// Gets or sets the playlist item id of the playing item. + /// </summary> + /// <value>The playlist item id.</value> + public string PlaylistItemId { get; set; } /// <inheritdoc /> - public PlaybackRequestType Type() + public PlaybackRequestType GetRequestType() { return PlaybackRequestType.Buffer; } /// <inheritdoc /> - public bool Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) { - return state.HandleRequest(context, false, this, session, cancellationToken); + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); } } } diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/IgnoreWaitGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/IgnoreWaitGroupRequest.cs new file mode 100644 index 000000000..5466cbe2f --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/IgnoreWaitGroupRequest.cs @@ -0,0 +1,30 @@ +using System.Threading; +using MediaBrowser.Model.SyncPlay; +using MediaBrowser.Controller.Session; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Class IgnoreWaitGroupRequest. + /// </summary> + public class IgnoreWaitGroupRequest : IPlaybackGroupRequest + { + /// <summary> + /// Gets or sets the client group-wait status. + /// </summary> + /// <value>The client group-wait status.</value> + public bool IgnoreWait { get; set; } + + /// <inheritdoc /> + public PlaybackRequestType GetRequestType() + { + return PlaybackRequestType.IgnoreWait; + } + + /// <inheritdoc /> + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + { + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); + } + } +} diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/MovePlaylistItemGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/MovePlaylistItemGroupRequest.cs new file mode 100644 index 000000000..7a293c02f --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/MovePlaylistItemGroupRequest.cs @@ -0,0 +1,36 @@ +using System.Threading; +using MediaBrowser.Model.SyncPlay; +using MediaBrowser.Controller.Session; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Class MovePlaylistItemGroupRequest. + /// </summary> + public class MovePlaylistItemGroupRequest : IPlaybackGroupRequest + { + /// <summary> + /// Gets or sets the playlist id of the item. + /// </summary> + /// <value>The playlist id of the item.</value> + public string PlaylistItemId { get; set; } + + /// <summary> + /// Gets or sets the new position. + /// </summary> + /// <value>The new position.</value> + public int NewIndex { get; set; } + + /// <inheritdoc /> + public PlaybackRequestType GetRequestType() + { + return PlaybackRequestType.Queue; + } + + /// <inheritdoc /> + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + { + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); + } + } +} diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/NextTrackGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/NextTrackGroupRequest.cs new file mode 100644 index 000000000..d19df2c6a --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/NextTrackGroupRequest.cs @@ -0,0 +1,30 @@ +using System.Threading; +using MediaBrowser.Model.SyncPlay; +using MediaBrowser.Controller.Session; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Class NextTrackGroupRequest. + /// </summary> + public class NextTrackGroupRequest : IPlaybackGroupRequest + { + /// <summary> + /// Gets or sets the playing item id. + /// </summary> + /// <value>The playing item id.</value> + public string PlaylistItemId { get; set; } + + /// <inheritdoc /> + public PlaybackRequestType GetRequestType() + { + return PlaybackRequestType.NextTrack; + } + + /// <inheritdoc /> + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + { + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); + } + } +} diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PauseGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PauseGroupRequest.cs index 21a46add8..facb25155 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PauseGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PauseGroupRequest.cs @@ -10,15 +10,15 @@ namespace MediaBrowser.Controller.SyncPlay public class PauseGroupRequest : IPlaybackGroupRequest { /// <inheritdoc /> - public PlaybackRequestType Type() + public PlaybackRequestType GetRequestType() { return PlaybackRequestType.Pause; } /// <inheritdoc /> - public bool Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) { - return state.HandleRequest(context, false, this, session, cancellationToken); + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); } } } diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PingGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PingGroupRequest.cs index 2f78edfc5..631bf245b 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PingGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PingGroupRequest.cs @@ -2,7 +2,6 @@ using System.Threading; using MediaBrowser.Model.SyncPlay; using MediaBrowser.Controller.Session; -// FIXME: not really group related, can be moved up to SyncPlayController maybe? namespace MediaBrowser.Controller.SyncPlay { /// <summary> @@ -17,15 +16,15 @@ namespace MediaBrowser.Controller.SyncPlay public long Ping { get; set; } /// <inheritdoc /> - public PlaybackRequestType Type() + public PlaybackRequestType GetRequestType() { return PlaybackRequestType.Ping; } /// <inheritdoc /> - public bool Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) { - return state.HandleRequest(context, false, this, session, cancellationToken); + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); } } } diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PlayGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PlayGroupRequest.cs index 942229a77..f3dd769e4 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PlayGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PlayGroupRequest.cs @@ -1,3 +1,4 @@ +using System; using System.Threading; using MediaBrowser.Model.SyncPlay; using MediaBrowser.Controller.Session; @@ -9,16 +10,34 @@ namespace MediaBrowser.Controller.SyncPlay /// </summary> public class PlayGroupRequest : IPlaybackGroupRequest { + /// <summary> + /// Gets or sets the playing queue. + /// </summary> + /// <value>The playing queue.</value> + public Guid[] PlayingQueue { get; set; } + + /// <summary> + /// Gets or sets the playing item from the queue. + /// </summary> + /// <value>The playing item.</value> + public int PlayingItemPosition { get; set; } + + /// <summary> + /// Gets or sets the start position ticks. + /// </summary> + /// <value>The start position ticks.</value> + public long StartPositionTicks { get; set; } + /// <inheritdoc /> - public PlaybackRequestType Type() + public PlaybackRequestType GetRequestType() { return PlaybackRequestType.Play; } /// <inheritdoc /> - public bool Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) { - return state.HandleRequest(context, false, this, session, cancellationToken); + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); } } } diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PreviousTrackGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PreviousTrackGroupRequest.cs new file mode 100644 index 000000000..663011b42 --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PreviousTrackGroupRequest.cs @@ -0,0 +1,30 @@ +using System.Threading; +using MediaBrowser.Model.SyncPlay; +using MediaBrowser.Controller.Session; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Class PreviousTrackGroupRequest. + /// </summary> + public class PreviousTrackGroupRequest : IPlaybackGroupRequest + { + /// <summary> + /// Gets or sets the playing item id. + /// </summary> + /// <value>The playing item id.</value> + public string PlaylistItemId { get; set; } + + /// <inheritdoc /> + public PlaybackRequestType GetRequestType() + { + return PlaybackRequestType.PreviousTrack; + } + + /// <inheritdoc /> + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + { + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); + } + } +} diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/QueueGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/QueueGroupRequest.cs new file mode 100644 index 000000000..01c08cc86 --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/QueueGroupRequest.cs @@ -0,0 +1,37 @@ +using System; +using System.Threading; +using MediaBrowser.Model.SyncPlay; +using MediaBrowser.Controller.Session; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Class QueueGroupRequest. + /// </summary> + public class QueueGroupRequest : IPlaybackGroupRequest + { + /// <summary> + /// Gets or sets the items to queue. + /// </summary> + /// <value>The items to queue.</value> + public Guid[] ItemIds { get; set; } + + /// <summary> + /// Gets or sets the mode in which to add the new items. + /// </summary> + /// <value>The mode.</value> + public string Mode { get; set; } + + /// <inheritdoc /> + public PlaybackRequestType GetRequestType() + { + return PlaybackRequestType.Queue; + } + + /// <inheritdoc /> + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + { + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); + } + } +} diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/ReadyGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/ReadyGroupRequest.cs index ee88ddddb..16bc67c61 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/ReadyGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/ReadyGroupRequest.cs @@ -23,21 +23,27 @@ namespace MediaBrowser.Controller.SyncPlay public long PositionTicks { get; set; } /// <summary> - /// Gets or sets the playing item id. + /// Gets or sets the client playback status. /// </summary> - /// <value>The playing item id.</value> - public Guid PlayingItemId { get; set; } + /// <value>The client playback status.</value> + public bool IsPlaying { get; set; } + + /// <summary> + /// Gets or sets the playlist item id of the playing item. + /// </summary> + /// <value>The playlist item id.</value> + public string PlaylistItemId { get; set; } /// <inheritdoc /> - public PlaybackRequestType Type() + public PlaybackRequestType GetRequestType() { return PlaybackRequestType.Ready; } /// <inheritdoc /> - public bool Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) { - return state.HandleRequest(context, false, this, session, cancellationToken); + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); } } } diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/RemoveFromPlaylistGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/RemoveFromPlaylistGroupRequest.cs new file mode 100644 index 000000000..3fc77f677 --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/RemoveFromPlaylistGroupRequest.cs @@ -0,0 +1,30 @@ +using System.Threading; +using MediaBrowser.Model.SyncPlay; +using MediaBrowser.Controller.Session; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Class RemoveFromPlaylistGroupRequest. + /// </summary> + public class RemoveFromPlaylistGroupRequest : IPlaybackGroupRequest + { + /// <summary> + /// Gets or sets the playlist ids ot the items. + /// </summary> + /// <value>The playlist ids ot the items.</value> + public string[] PlaylistItemIds { get; set; } + + /// <inheritdoc /> + public PlaybackRequestType GetRequestType() + { + return PlaybackRequestType.Queue; + } + + /// <inheritdoc /> + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + { + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); + } + } +} diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SeekGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SeekGroupRequest.cs index bb5e7a343..24d9be507 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SeekGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SeekGroupRequest.cs @@ -16,15 +16,15 @@ namespace MediaBrowser.Controller.SyncPlay public long PositionTicks { get; set; } /// <inheritdoc /> - public PlaybackRequestType Type() + public PlaybackRequestType GetRequestType() { return PlaybackRequestType.Seek; } /// <inheritdoc /> - public bool Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) { - return state.HandleRequest(context, false, this, session, cancellationToken); + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); } } } diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SetCurrentItemGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SetCurrentItemGroupRequest.cs new file mode 100644 index 000000000..d70559899 --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SetCurrentItemGroupRequest.cs @@ -0,0 +1,30 @@ +using System.Threading; +using MediaBrowser.Model.SyncPlay; +using MediaBrowser.Controller.Session; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Class SetPlaylistItemGroupRequest. + /// </summary> + public class SetPlaylistItemGroupRequest : IPlaybackGroupRequest + { + /// <summary> + /// Gets or sets the playlist id of the playing item. + /// </summary> + /// <value>The playlist id of the playing item.</value> + public string PlaylistItemId { get; set; } + + /// <inheritdoc /> + public PlaybackRequestType GetRequestType() + { + return PlaybackRequestType.SetPlaylistItem; + } + + /// <inheritdoc /> + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + { + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); + } + } +} diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SetRepeatModeGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SetRepeatModeGroupRequest.cs new file mode 100644 index 000000000..5f36f60e4 --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SetRepeatModeGroupRequest.cs @@ -0,0 +1,30 @@ +using System.Threading; +using MediaBrowser.Model.SyncPlay; +using MediaBrowser.Controller.Session; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Class SetRepeatModeGroupRequest. + /// </summary> + public class SetRepeatModeGroupRequest : IPlaybackGroupRequest + { + /// <summary> + /// Gets or sets the repeat mode. + /// </summary> + /// <value>The repeat mode.</value> + public string Mode { get; set; } + + /// <inheritdoc /> + public PlaybackRequestType GetRequestType() + { + return PlaybackRequestType.SetRepeatMode; + } + + /// <inheritdoc /> + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + { + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); + } + } +} diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SetShuffleModeGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SetShuffleModeGroupRequest.cs new file mode 100644 index 000000000..472455fd3 --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SetShuffleModeGroupRequest.cs @@ -0,0 +1,30 @@ +using System.Threading; +using MediaBrowser.Model.SyncPlay; +using MediaBrowser.Controller.Session; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Class SetShuffleModeGroupRequest. + /// </summary> + public class SetShuffleModeGroupRequest : IPlaybackGroupRequest + { + /// <summary> + /// Gets or sets the shuffle mode. + /// </summary> + /// <value>The shuffle mode.</value> + public string Mode { get; set; } + + /// <inheritdoc /> + public PlaybackRequestType GetRequestType() + { + return PlaybackRequestType.SetShuffleMode; + } + + /// <inheritdoc /> + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + { + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); + } + } +} diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/StopGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/StopGroupRequest.cs new file mode 100644 index 000000000..f1581c98d --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/StopGroupRequest.cs @@ -0,0 +1,24 @@ +using System.Threading; +using MediaBrowser.Model.SyncPlay; +using MediaBrowser.Controller.Session; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Class StopGroupRequest. + /// </summary> + public class StopGroupRequest : IPlaybackGroupRequest + { + /// <inheritdoc /> + public PlaybackRequestType GetRequestType() + { + return PlaybackRequestType.Stop; + } + + /// <inheritdoc /> + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + { + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); + } + } +} diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/UnpauseGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/UnpauseGroupRequest.cs new file mode 100644 index 000000000..107295208 --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/UnpauseGroupRequest.cs @@ -0,0 +1,24 @@ +using System.Threading; +using MediaBrowser.Model.SyncPlay; +using MediaBrowser.Controller.Session; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Class UnpauseGroupRequest. + /// </summary> + public class UnpauseGroupRequest : IPlaybackGroupRequest + { + /// <inheritdoc /> + public PlaybackRequestType GetRequestType() + { + return PlaybackRequestType.Unpause; + } + + /// <inheritdoc /> + public void Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken) + { + state.HandleRequest(context, state.GetGroupState(), this, session, cancellationToken); + } + } +} diff --git a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs new file mode 100644 index 000000000..701982cc0 --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs @@ -0,0 +1,596 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using MediaBrowser.Model.SyncPlay; + +namespace MediaBrowser.Controller.SyncPlay +{ + static class ListShuffleExtension + { + private static Random rng = new Random(); + public static void Shuffle<T>(this IList<T> list) + { + int n = list.Count; + while (n > 1) + { + n--; + int k = rng.Next(n + 1); + T value = list[k]; + list[k] = list[n]; + list[n] = value; + } + } + } + + /// <summary> + /// Class PlayQueueManager. + /// </summary> + public class PlayQueueManager : IDisposable + { + /// <summary> + /// Gets or sets the playing item index. + /// </summary> + /// <value>The playing item index.</value> + public int PlayingItemIndex { get; private set; } + + /// <summary> + /// Gets or sets the last time the queue has been changed. + /// </summary> + /// <value>The last time the queue has been changed.</value> + public DateTime LastChange { get; private set; } + + /// <summary> + /// Gets the sorted playlist. + /// </summary> + /// <value>The sorted playlist, or play queue of the group.</value> + private List<QueueItem> SortedPlaylist { get; set; } = new List<QueueItem>(); + + /// <summary> + /// Gets the shuffled playlist. + /// </summary> + /// <value>The shuffled playlist, or play queue of the group.</value> + private List<QueueItem> ShuffledPlaylist { get; set; } = new List<QueueItem>(); + + /// <summary> + /// Gets or sets the shuffle mode. + /// </summary> + /// <value>The shuffle mode.</value> + public GroupShuffleMode ShuffleMode { get; private set; } = GroupShuffleMode.Sorted; + + /// <summary> + /// Gets or sets the repeat mode. + /// </summary> + /// <value>The repeat mode.</value> + public GroupRepeatMode RepeatMode { get; private set; } = GroupRepeatMode.RepeatNone; + + /// <summary> + /// Gets or sets the progressive id counter. + /// </summary> + /// <value>The progressive id.</value> + private int ProgressiveId { get; set; } = 0; + + private bool _disposed = false; + + /// <summary> + /// Initializes a new instance of the <see cref="PlayQueueManager" /> class. + /// </summary> + public PlayQueueManager() + { + Reset(); + } + + /// <inheritdoc /> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// <summary> + /// Releases unmanaged and optionally managed resources. + /// </summary> + /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + _disposed = true; + } + + /// <summary> + /// Gets the next available id. + /// </summary> + /// <returns>The next available id.</returns> + private int GetNextProgressiveId() { + return ProgressiveId++; + } + + /// <summary> + /// Creates a list from the array of items. Each item is given an unique playlist id. + /// </summary> + /// <returns>The list of queue items.</returns> + private List<QueueItem> CreateQueueItemsFromArray(Guid[] items) + { + return items.ToList() + .Select(item => new QueueItem() + { + ItemId = item, + PlaylistItemId = "syncPlayItem" + GetNextProgressiveId() + }) + .ToList(); + } + + /// <summary> + /// Gets the current playlist, depending on the shuffle mode. + /// </summary> + /// <returns>The playlist.</returns> + private List<QueueItem> GetPlaylistAsList() + { + if (ShuffleMode.Equals(GroupShuffleMode.Shuffle)) + { + return ShuffledPlaylist; + } + else + { + return SortedPlaylist; + } + } + + /// <summary> + /// Gets the current playlist as an array, depending on the shuffle mode. + /// </summary> + /// <returns>The array of items in the playlist.</returns> + public QueueItem[] GetPlaylist() { + if (ShuffleMode.Equals(GroupShuffleMode.Shuffle)) + { + return ShuffledPlaylist.ToArray(); + } + else + { + return SortedPlaylist.ToArray(); + } + } + + /// <summary> + /// Sets a new playlist. Playing item is set to none. Resets shuffle mode and repeat mode as well. + /// </summary> + /// <param name="items">The new items of the playlist.</param> + public void SetPlaylist(Guid[] items) + { + SortedPlaylist = CreateQueueItemsFromArray(items); + PlayingItemIndex = -1; + ShuffleMode = GroupShuffleMode.Sorted; + RepeatMode = GroupRepeatMode.RepeatNone; + LastChange = DateTime.UtcNow; + } + + /// <summary> + /// Appends new items to the playlist. The specified order is mantained for the sorted playlist, whereas items get shuffled for the shuffled playlist. + /// </summary> + /// <param name="items">The items to add to the playlist.</param> + public void Queue(Guid[] items) + { + var newItems = CreateQueueItemsFromArray(items); + SortedPlaylist.AddRange(newItems); + + if (ShuffleMode.Equals(GroupShuffleMode.Shuffle)) + { + newItems.Shuffle(); + ShuffledPlaylist.AddRange(newItems); + } + + LastChange = DateTime.UtcNow; + } + + /// <summary> + /// Shuffles the playlist. Shuffle mode is changed. + /// </summary> + public void ShufflePlaylist() + { + if (SortedPlaylist.Count() == 0) + { + return; + } + + if (PlayingItemIndex < 0) { + ShuffledPlaylist = SortedPlaylist.ToList(); + ShuffledPlaylist.Shuffle(); + } + else + { + var playingItem = SortedPlaylist[PlayingItemIndex]; + ShuffledPlaylist = SortedPlaylist.ToList(); + ShuffledPlaylist.RemoveAt(PlayingItemIndex); + ShuffledPlaylist.Shuffle(); + ShuffledPlaylist = ShuffledPlaylist.Prepend(playingItem).ToList(); + PlayingItemIndex = 0; + } + + ShuffleMode = GroupShuffleMode.Shuffle; + LastChange = DateTime.UtcNow; + } + + /// <summary> + /// Resets the playlist to sorted mode. Shuffle mode is changed. + /// </summary> + public void SortShuffledPlaylist() + { + if (PlayingItemIndex >= 0) + { + var playingItem = ShuffledPlaylist[PlayingItemIndex]; + PlayingItemIndex = SortedPlaylist.IndexOf(playingItem); + } + + ShuffledPlaylist.Clear(); + + ShuffleMode = GroupShuffleMode.Sorted; + LastChange = DateTime.UtcNow; + } + + /// <summary> + /// Clears the playlist. + /// </summary> + /// <param name="clearPlayingItem">Whether to remove the playing item as well.</param> + public void ClearPlaylist(bool clearPlayingItem) + { + var playingItem = SortedPlaylist[PlayingItemIndex]; + SortedPlaylist.Clear(); + ShuffledPlaylist.Clear(); + LastChange = DateTime.UtcNow; + + if (!clearPlayingItem && playingItem != null) + { + SortedPlaylist.Add(playingItem); + if (ShuffleMode.Equals(GroupShuffleMode.Shuffle)) + { + SortedPlaylist.Add(playingItem); + } + } + } + + /// <summary> + /// Adds new items to the playlist right after the playing item. The specified order is mantained for the sorted playlist, whereas items get shuffled for the shuffled playlist. + /// </summary> + /// <param name="items">The items to add to the playlist.</param> + public void QueueNext(Guid[] items) + { + var newItems = CreateQueueItemsFromArray(items); + + if (ShuffleMode.Equals(GroupShuffleMode.Shuffle)) + { + // Append items to sorted playlist as they are + SortedPlaylist.AddRange(newItems); + // Shuffle items before adding to shuffled playlist + newItems.Shuffle(); + ShuffledPlaylist.InsertRange(PlayingItemIndex + 1, newItems); + } + else + { + SortedPlaylist.InsertRange(PlayingItemIndex + 1, newItems); + } + + LastChange = DateTime.UtcNow; + } + + /// <summary> + /// Gets playlist id of the playing item, if any. + /// </summary> + /// <returns>The playlist id of the playing item.</returns> + public string GetPlayingItemPlaylistId() + { + if (PlayingItemIndex < 0) + { + return null; + } + + var list = GetPlaylistAsList(); + + if (list.Count() > 0) + { + return list[PlayingItemIndex].PlaylistItemId; + } + else + { + return null; + } + } + + /// <summary> + /// Gets the playing item id, if any. + /// </summary> + /// <returns>The playing item id.</returns> + public Guid GetPlayingItemId() + { + if (PlayingItemIndex < 0) + { + return Guid.Empty; + } + + var list = GetPlaylistAsList(); + + if (list.Count() > 0) + { + return list[PlayingItemIndex].ItemId; + } + else + { + return Guid.Empty; + } + } + + /// <summary> + /// Sets the playing item using its id. If not in the playlist, the playing item is reset. + /// </summary> + /// <param name="itemId">The new playing item id.</param> + public void SetPlayingItemById(Guid itemId) + { + var itemIds = GetPlaylistAsList().Select(queueItem => queueItem.ItemId).ToList(); + PlayingItemIndex = itemIds.IndexOf(itemId); + LastChange = DateTime.UtcNow; + } + + /// <summary> + /// Sets the playing item using its playlist id. If not in the playlist, the playing item is reset. + /// </summary> + /// <param name="playlistItemId">The new playing item id.</param> + /// <returns><c>true</c> if playing item has been set; <c>false</c> if item is not in the playlist.</returns> + public bool SetPlayingItemByPlaylistId(string playlistItemId) + { + var playlistIds = GetPlaylistAsList().Select(queueItem => queueItem.PlaylistItemId).ToList(); + PlayingItemIndex = playlistIds.IndexOf(playlistItemId); + LastChange = DateTime.UtcNow; + return PlayingItemIndex != -1; + } + + /// <summary> + /// Sets the playing item using its position. If not in range, the playing item is reset. + /// </summary> + /// <param name="playlistIndex">The new playing item index.</param> + public void SetPlayingItemByIndex(int playlistIndex) + { + var list = GetPlaylistAsList(); + if (playlistIndex < 0 || playlistIndex > list.Count()) + { + PlayingItemIndex = -1; + } + else + { + PlayingItemIndex = playlistIndex; + } + + LastChange = DateTime.UtcNow; + } + + /// <summary> + /// Removes items from the playlist. If not removed, the playing item is preserved. + /// </summary> + /// <param name="playlistItemIds">The items to remove.</param> + /// <returns><c>true</c> if playing item got removed; <c>false</c> otherwise.</returns> + public bool RemoveFromPlaylist(string[] playlistItemIds) + { + var playingItem = SortedPlaylist[PlayingItemIndex]; + if (ShuffleMode.Equals(GroupShuffleMode.Shuffle)) + { + playingItem = ShuffledPlaylist[PlayingItemIndex]; + } + + var playlistItemIdsList = playlistItemIds.ToList(); + SortedPlaylist.RemoveAll(item => playlistItemIdsList.Contains(item.PlaylistItemId)); + ShuffledPlaylist.RemoveAll(item => playlistItemIdsList.Contains(item.PlaylistItemId)); + + LastChange = DateTime.UtcNow; + + if (playingItem != null) + { + if (playlistItemIds.Contains(playingItem.PlaylistItemId)) + { + // Playing item has been removed, picking previous item + PlayingItemIndex--; + if (PlayingItemIndex < 0) + { + // Was first element, picking next if available + PlayingItemIndex = SortedPlaylist.Count() > 0 ? 0 : -1; + } + + return true; + } + else + { + // Restoring playing item + SetPlayingItemByPlaylistId(playingItem.PlaylistItemId); + return false; + } + } + else + { + return false; + } + } + + /// <summary> + /// Moves an item in the playlist to another position. + /// </summary> + /// <param name="playlistItemId">The item to move.</param> + /// <param name="newIndex">The new position.</param> + /// <returns><c>true</c> if the item has been moved; <c>false</c> otherwise.</returns> + public bool MovePlaylistItem(string playlistItemId, int newIndex) + { + var list = GetPlaylistAsList(); + var playingItem = list[PlayingItemIndex]; + + var playlistIds = list.Select(queueItem => queueItem.PlaylistItemId).ToList(); + var oldIndex = playlistIds.IndexOf(playlistItemId); + if (oldIndex < 0) { + return false; + } + + var queueItem = list[oldIndex]; + list.RemoveAt(oldIndex); + newIndex = newIndex > list.Count() ? list.Count() : newIndex; + newIndex = newIndex < 0 ? 0 : newIndex; + list.Insert(newIndex, queueItem); + + LastChange = DateTime.UtcNow; + PlayingItemIndex = list.IndexOf(playingItem); + return true; + } + + /// <summary> + /// Resets the playlist to its initial state. + /// </summary> + public void Reset() + { + ProgressiveId = 0; + SortedPlaylist.Clear(); + ShuffledPlaylist.Clear(); + PlayingItemIndex = -1; + ShuffleMode = GroupShuffleMode.Sorted; + RepeatMode = GroupRepeatMode.RepeatNone; + LastChange = DateTime.UtcNow; + } + + /// <summary> + /// Sets the repeat mode. + /// </summary> + /// <param name="mode">The new mode.</param> + public void SetRepeatMode(string mode) + { + switch (mode) + { + case "RepeatOne": + RepeatMode = GroupRepeatMode.RepeatOne; + break; + case "RepeatAll": + RepeatMode = GroupRepeatMode.RepeatAll; + break; + default: + RepeatMode = GroupRepeatMode.RepeatNone; + break; + } + + LastChange = DateTime.UtcNow; + } + + /// <summary> + /// Sets the shuffle mode. + /// </summary> + /// <param name="mode">The new mode.</param> + public void SetShuffleMode(string mode) + { + switch (mode) + { + case "Shuffle": + ShufflePlaylist(); + break; + default: + SortShuffledPlaylist(); + break; + } + } + + /// <summary> + /// Toggles the shuffle mode between sorted and shuffled. + /// </summary> + public void ToggleShuffleMode() + { + SetShuffleMode(ShuffleMode.Equals(GroupShuffleMode.Shuffle) ? "Shuffle" : ""); + } + + /// <summary> + /// Gets the next item in the playlist considering repeat mode and shuffle mode. + /// </summary> + /// <returns>The next item in the playlist.</returns> + public QueueItem GetNextItemPlaylistId() + { + int newIndex; + var playlist = GetPlaylistAsList(); + + switch (RepeatMode) + { + case GroupRepeatMode.RepeatOne: + newIndex = PlayingItemIndex; + break; + case GroupRepeatMode.RepeatAll: + newIndex = PlayingItemIndex + 1; + if (newIndex >= playlist.Count()) + { + newIndex = 0; + } + break; + default: + newIndex = PlayingItemIndex + 1; + break; + } + + if (newIndex < 0 || newIndex >= playlist.Count()) + { + return null; + } + + return playlist[newIndex]; + } + + /// <summary> + /// Sets the next item in the queue as playing item. + /// </summary> + /// <returns><c>true</c> if the playing item changed; <c>false</c> otherwise.</returns> + public bool Next() + { + if (RepeatMode.Equals(GroupRepeatMode.RepeatOne)) + { + LastChange = DateTime.UtcNow; + return true; + } + + PlayingItemIndex++; + if (PlayingItemIndex >= SortedPlaylist.Count()) + { + if (RepeatMode.Equals(GroupRepeatMode.RepeatAll)) + { + PlayingItemIndex = 0; + } + else + { + PlayingItemIndex--; + return false; + } + } + + LastChange = DateTime.UtcNow; + return true; + } + + /// <summary> + /// Sets the previous item in the queue as playing item. + /// </summary> + /// <returns><c>true</c> if the playing item changed; <c>false</c> otherwise.</returns> + public bool Previous() + { + if (RepeatMode.Equals(GroupRepeatMode.RepeatOne)) + { + LastChange = DateTime.UtcNow; + return true; + } + + PlayingItemIndex--; + if (PlayingItemIndex < 0) + { + if (RepeatMode.Equals(GroupRepeatMode.RepeatAll)) + { + PlayingItemIndex = SortedPlaylist.Count() - 1; + } + else + { + PlayingItemIndex++; + return false; + } + } + + LastChange = DateTime.UtcNow; + return true; + } + } +} diff --git a/MediaBrowser.Controller/SyncPlay/SyncPlayAbstractState.cs b/MediaBrowser.Controller/SyncPlay/SyncPlayAbstractState.cs deleted file mode 100644 index 0b72d1668..000000000 --- a/MediaBrowser.Controller/SyncPlay/SyncPlayAbstractState.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Threading; -using MediaBrowser.Controller.Session; -using MediaBrowser.Model.SyncPlay; - -namespace MediaBrowser.Controller.SyncPlay -{ - /// <summary> - /// Class SyncPlayAbstractState. - /// </summary> - /// <remarks> - /// Class is not thread-safe, external locking is required when accessing methods. - /// </remarks> - public abstract class SyncPlayAbstractState : ISyncPlayState - { - /// <inheritdoc /> - public abstract GroupState GetGroupState(); - - /// <inheritdoc /> - public virtual bool HandleRequest(ISyncPlayStateContext context, bool newState, IPlaybackGroupRequest request, SessionInfo session, CancellationToken cancellationToken) - { - return true; - } - - /// <inheritdoc /> - public virtual bool HandleRequest(ISyncPlayStateContext context, bool newState, PlayGroupRequest request, SessionInfo session, CancellationToken cancellationToken) - { - return true; - } - - /// <inheritdoc /> - public virtual bool HandleRequest(ISyncPlayStateContext context, bool newState, PauseGroupRequest request, SessionInfo session, CancellationToken cancellationToken) - { - return true; - } - - /// <inheritdoc /> - public virtual bool HandleRequest(ISyncPlayStateContext context, bool newState, SeekGroupRequest request, SessionInfo session, CancellationToken cancellationToken) - { - return true; - } - - /// <inheritdoc /> - public virtual bool HandleRequest(ISyncPlayStateContext context, bool newState, BufferGroupRequest request, SessionInfo session, CancellationToken cancellationToken) - { - return true; - } - - /// <inheritdoc /> - public virtual bool HandleRequest(ISyncPlayStateContext context, bool newState, ReadyGroupRequest request, SessionInfo session, CancellationToken cancellationToken) - { - return true; - } - - /// <inheritdoc /> - public virtual bool HandleRequest(ISyncPlayStateContext context, bool newState, PingGroupRequest request, SessionInfo session, CancellationToken cancellationToken) - { - GroupInfo group = context.GetGroup(); - - // Collected pings are used to account for network latency when unpausing playback - group.UpdatePing(session, request.Ping); - - return true; - } - } -} |
