aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/SyncPlay
diff options
context:
space:
mode:
authorgion <oancaionutandrei@gmail.com>2020-05-12 19:05:05 +0200
committerIonut Andrei Oanca <oancaionutandrei@gmail.com>2020-10-16 11:38:50 +0200
commite10799e0e8d7afcdf07585ac4b27dd060c973d8f (patch)
tree9132965bbc0f06a2298a656f9aab3b87043012e5 /MediaBrowser.Controller/SyncPlay
parent5487dfc145096faeaa9ee82d92ffa224ef69fc11 (diff)
Rewrite syncplay using a state design pattern
Diffstat (limited to 'MediaBrowser.Controller/SyncPlay')
-rw-r--r--MediaBrowser.Controller/SyncPlay/GroupInfo.cs6
-rw-r--r--MediaBrowser.Controller/SyncPlay/IPlaybackGroupRequest.cs24
-rw-r--r--MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs2
-rw-r--r--MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs2
-rw-r--r--MediaBrowser.Controller/SyncPlay/ISyncPlayState.cs95
-rw-r--r--MediaBrowser.Controller/SyncPlay/ISyncPlayStateContext.cs75
-rw-r--r--MediaBrowser.Controller/SyncPlay/PlaybackRequest/BufferGroupRequest.cs43
-rw-r--r--MediaBrowser.Controller/SyncPlay/PlaybackRequest/PauseGroupRequest.cs24
-rw-r--r--MediaBrowser.Controller/SyncPlay/PlaybackRequest/PingGroupRequest.cs31
-rw-r--r--MediaBrowser.Controller/SyncPlay/PlaybackRequest/PlayGroupRequest.cs24
-rw-r--r--MediaBrowser.Controller/SyncPlay/PlaybackRequest/ReadyGroupRequest.cs43
-rw-r--r--MediaBrowser.Controller/SyncPlay/PlaybackRequest/SeekGroupRequest.cs30
-rw-r--r--MediaBrowser.Controller/SyncPlay/SyncPlayAbstractState.cs65
13 files changed, 456 insertions, 8 deletions
diff --git a/MediaBrowser.Controller/SyncPlay/GroupInfo.cs b/MediaBrowser.Controller/SyncPlay/GroupInfo.cs
index a1cada25c..cdd24d0b5 100644
--- a/MediaBrowser.Controller/SyncPlay/GroupInfo.cs
+++ b/MediaBrowser.Controller/SyncPlay/GroupInfo.cs
@@ -31,12 +31,6 @@ namespace MediaBrowser.Controller.SyncPlay
public BaseItem PlayingItem { get; set; }
/// <summary>
- /// Gets or sets a value indicating whether playback is paused.
- /// </summary>
- /// <value>Playback is paused.</value>
- public bool IsPaused { get; set; }
-
- /// <summary>
/// Gets or sets a value indicating whether there are position ticks.
/// </summary>
/// <value>The position ticks.</value>
diff --git a/MediaBrowser.Controller/SyncPlay/IPlaybackGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/IPlaybackGroupRequest.cs
new file mode 100644
index 000000000..a6e87a007
--- /dev/null
+++ b/MediaBrowser.Controller/SyncPlay/IPlaybackGroupRequest.cs
@@ -0,0 +1,24 @@
+using System.Threading;
+using MediaBrowser.Model.SyncPlay;
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.SyncPlay
+{
+ /// <summary>
+ /// Interface IPlaybackGroupRequest.
+ /// </summary>
+ public interface IPlaybackGroupRequest
+ {
+ /// <summary>
+ /// Gets the playback request type.
+ /// </summary>
+ /// <value>The playback request type.</value>
+ PlaybackRequestType Type();
+
+ /// <summary>
+ /// Applies the request to a group.
+ /// </summary>
+ /// <value>The operation completion status.</value>
+ bool Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken);
+ }
+}
diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs
index d869c05bd..5ac2aeb24 100644
--- a/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs
+++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs
@@ -56,7 +56,7 @@ namespace MediaBrowser.Controller.SyncPlay
/// <param name="session">The session.</param>
/// <param name="request">The requested action.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- void HandleRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken);
+ void HandleRequest(SessionInfo session, IPlaybackGroupRequest request, CancellationToken cancellationToken);
/// <summary>
/// Gets the info about the group for the clients.
diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs
index 65770021d..6fa94e2ce 100644
--- a/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs
+++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs
@@ -48,7 +48,7 @@ namespace MediaBrowser.Controller.SyncPlay
/// <param name="session">The session.</param>
/// <param name="request">The request.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- void HandleRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken);
+ void HandleRequest(SessionInfo session, IPlaybackGroupRequest request, CancellationToken cancellationToken);
/// <summary>
/// Maps a session to a group.
diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayState.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayState.cs
new file mode 100644
index 000000000..55c9ee938
--- /dev/null
+++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayState.cs
@@ -0,0 +1,95 @@
+using System.Threading;
+using MediaBrowser.Model.SyncPlay;
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.SyncPlay
+{
+ /// <summary>
+ /// Interface ISyncPlayState.
+ /// </summary>
+ public interface ISyncPlayState
+ {
+ /// <summary>
+ /// Gets the group state.
+ /// </summary>
+ /// <value>The group state.</value>
+ GroupState GetGroupState();
+
+ /// <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="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);
+
+ /// <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="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);
+
+ /// <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="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);
+
+ /// <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="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);
+
+ /// <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="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);
+
+ /// <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="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);
+
+ /// <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="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);
+ }
+}
diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayStateContext.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayStateContext.cs
new file mode 100644
index 000000000..9bdb1ace6
--- /dev/null
+++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayStateContext.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.SyncPlay;
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.SyncPlay
+{
+ /// <summary>
+ /// Interface ISyncPlayStateContext.
+ /// </summary>
+ public interface ISyncPlayStateContext
+ {
+ /// <summary>
+ /// Gets the context's group.
+ /// </summary>
+ /// <value>The group.</value>
+ GroupInfo GetGroup();
+
+ /// <summary>
+ /// Sets a new state.
+ /// </summary>
+ /// <param name="state">The new state.</param>
+ void SetState(ISyncPlayState state);
+
+ /// <summary>
+ /// Sends a GroupUpdate message to the interested sessions.
+ /// </summary>
+ /// <param name="from">The current session.</param>
+ /// <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>
+ Task SendGroupUpdate<T>(SessionInfo from, SyncPlayBroadcastType type, GroupUpdate<T> message, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends a playback command to the interested sessions.
+ /// </summary>
+ /// <param name="from">The current session.</param>
+ /// <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>
+ 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>
+ SendCommand NewSyncPlayCommand(SendCommandType type);
+
+ /// <summary>
+ /// Builds a new group update message.
+ /// </summary>
+ /// <param name="type">The update type.</param>
+ /// <param name="data">The data to send.</param>
+ /// <value>The GroupUpdate.</value>
+ 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);
+
+ /// <summary>
+ /// Sanitizes the PositionTicks, considers the current playing item when available.
+ /// </summary>
+ /// <param name="positionTicks">The PositionTicks.</param>
+ /// <value>The sanitized PositionTicks.</value>
+ long SanitizePositionTicks(long? positionTicks);
+ }
+}
diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/BufferGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/BufferGroupRequest.cs
new file mode 100644
index 000000000..21dae8e4e
--- /dev/null
+++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/BufferGroupRequest.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Threading;
+using MediaBrowser.Model.SyncPlay;
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.SyncPlay
+{
+ /// <summary>
+ /// Class BufferingGroupRequest.
+ /// </summary>
+ public class BufferGroupRequest : IPlaybackGroupRequest
+ {
+ /// <summary>
+ /// Gets or sets when the request has been made by the client.
+ /// </summary>
+ /// <value>The date of the request.</value>
+ public DateTime When { get; set; }
+
+ /// <summary>
+ /// Gets or sets the position ticks.
+ /// </summary>
+ /// <value>The position ticks.</value>
+ public long PositionTicks { get; set; }
+
+ /// <summary>
+ /// Gets or sets the playing item id.
+ /// </summary>
+ /// <value>The playing item id.</value>
+ public Guid PlayingItemId { get; set; }
+
+ /// <inheritdoc />
+ public PlaybackRequestType Type()
+ {
+ return PlaybackRequestType.Buffer;
+ }
+
+ /// <inheritdoc />
+ public bool Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken)
+ {
+ return state.HandleRequest(context, false, this, session, cancellationToken);
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PauseGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PauseGroupRequest.cs
new file mode 100644
index 000000000..21a46add8
--- /dev/null
+++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PauseGroupRequest.cs
@@ -0,0 +1,24 @@
+using System.Threading;
+using MediaBrowser.Model.SyncPlay;
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.SyncPlay
+{
+ /// <summary>
+ /// Class PauseGroupRequest.
+ /// </summary>
+ public class PauseGroupRequest : IPlaybackGroupRequest
+ {
+ /// <inheritdoc />
+ public PlaybackRequestType Type()
+ {
+ return PlaybackRequestType.Pause;
+ }
+
+ /// <inheritdoc />
+ public bool Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken)
+ {
+ return state.HandleRequest(context, false, this, session, cancellationToken);
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PingGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PingGroupRequest.cs
new file mode 100644
index 000000000..2f78edfc5
--- /dev/null
+++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PingGroupRequest.cs
@@ -0,0 +1,31 @@
+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>
+ /// Class UpdatePingGroupRequest.
+ /// </summary>
+ public class PingGroupRequest : IPlaybackGroupRequest
+ {
+ /// <summary>
+ /// Gets or sets the ping time.
+ /// </summary>
+ /// <value>The ping time.</value>
+ public long Ping { get; set; }
+
+ /// <inheritdoc />
+ public PlaybackRequestType Type()
+ {
+ return PlaybackRequestType.Ping;
+ }
+
+ /// <inheritdoc />
+ public bool Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken)
+ {
+ return state.HandleRequest(context, false, this, session, cancellationToken);
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PlayGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PlayGroupRequest.cs
new file mode 100644
index 000000000..942229a77
--- /dev/null
+++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/PlayGroupRequest.cs
@@ -0,0 +1,24 @@
+using System.Threading;
+using MediaBrowser.Model.SyncPlay;
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.SyncPlay
+{
+ /// <summary>
+ /// Class PlayGroupRequest.
+ /// </summary>
+ public class PlayGroupRequest : IPlaybackGroupRequest
+ {
+ /// <inheritdoc />
+ public PlaybackRequestType Type()
+ {
+ return PlaybackRequestType.Play;
+ }
+
+ /// <inheritdoc />
+ public bool Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken)
+ {
+ return state.HandleRequest(context, false, this, session, cancellationToken);
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/ReadyGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/ReadyGroupRequest.cs
new file mode 100644
index 000000000..ee88ddddb
--- /dev/null
+++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/ReadyGroupRequest.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Threading;
+using MediaBrowser.Model.SyncPlay;
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.SyncPlay
+{
+ /// <summary>
+ /// Class BufferingDoneGroupRequest.
+ /// </summary>
+ public class ReadyGroupRequest : IPlaybackGroupRequest
+ {
+ /// <summary>
+ /// Gets or sets when the request has been made by the client.
+ /// </summary>
+ /// <value>The date of the request.</value>
+ public DateTime When { get; set; }
+
+ /// <summary>
+ /// Gets or sets the position ticks.
+ /// </summary>
+ /// <value>The position ticks.</value>
+ public long PositionTicks { get; set; }
+
+ /// <summary>
+ /// Gets or sets the playing item id.
+ /// </summary>
+ /// <value>The playing item id.</value>
+ public Guid PlayingItemId { get; set; }
+
+ /// <inheritdoc />
+ public PlaybackRequestType Type()
+ {
+ return PlaybackRequestType.Ready;
+ }
+
+ /// <inheritdoc />
+ public bool Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken)
+ {
+ return state.HandleRequest(context, false, this, session, cancellationToken);
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SeekGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SeekGroupRequest.cs
new file mode 100644
index 000000000..bb5e7a343
--- /dev/null
+++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequest/SeekGroupRequest.cs
@@ -0,0 +1,30 @@
+using System.Threading;
+using MediaBrowser.Model.SyncPlay;
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.SyncPlay
+{
+ /// <summary>
+ /// Class SeekGroupRequest.
+ /// </summary>
+ public class SeekGroupRequest : IPlaybackGroupRequest
+ {
+ /// <summary>
+ /// Gets or sets the position ticks.
+ /// </summary>
+ /// <value>The position ticks.</value>
+ public long PositionTicks { get; set; }
+
+ /// <inheritdoc />
+ public PlaybackRequestType Type()
+ {
+ return PlaybackRequestType.Seek;
+ }
+
+ /// <inheritdoc />
+ public bool Apply(ISyncPlayStateContext context, ISyncPlayState state, SessionInfo session, CancellationToken cancellationToken)
+ {
+ return state.HandleRequest(context, false, this, session, cancellationToken);
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/SyncPlay/SyncPlayAbstractState.cs b/MediaBrowser.Controller/SyncPlay/SyncPlayAbstractState.cs
new file mode 100644
index 000000000..0b72d1668
--- /dev/null
+++ b/MediaBrowser.Controller/SyncPlay/SyncPlayAbstractState.cs
@@ -0,0 +1,65 @@
+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;
+ }
+ }
+}