diff options
| author | gion <oancaionutandrei@gmail.com> | 2020-05-12 19:05:05 +0200 |
|---|---|---|
| committer | Ionut Andrei Oanca <oancaionutandrei@gmail.com> | 2020-10-16 11:38:50 +0200 |
| commit | e10799e0e8d7afcdf07585ac4b27dd060c973d8f (patch) | |
| tree | 9132965bbc0f06a2298a656f9aab3b87043012e5 /MediaBrowser.Controller/SyncPlay | |
| parent | 5487dfc145096faeaa9ee82d92ffa224ef69fc11 (diff) | |
Rewrite syncplay using a state design pattern
Diffstat (limited to 'MediaBrowser.Controller/SyncPlay')
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; + } + } +} |
