1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
#nullable disable
using System.Threading;
using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.SyncPlay.PlaybackRequests;
using MediaBrowser.Model.SyncPlay;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.SyncPlay.GroupStates
{
/// <summary>
/// Class AbstractGroupState.
/// </summary>
/// <remarks>
/// Class is not thread-safe, external locking is required when accessing methods.
/// </remarks>
public abstract class AbstractGroupState : IGroupState
{
/// <summary>
/// The logger.
/// </summary>
private readonly ILogger<AbstractGroupState> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="AbstractGroupState"/> class.
/// </summary>
/// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
protected AbstractGroupState(ILoggerFactory loggerFactory)
{
LoggerFactory = loggerFactory;
_logger = loggerFactory.CreateLogger<AbstractGroupState>();
}
/// <inheritdoc />
public abstract GroupStateType Type { get; }
/// <summary>
/// Gets the logger factory.
/// </summary>
protected ILoggerFactory LoggerFactory { get; }
/// <inheritdoc />
public abstract void SessionJoined(IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken);
/// <inheritdoc />
public abstract void SessionLeaving(IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken);
/// <inheritdoc />
public virtual void HandleRequest(IGroupPlaybackRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
UnhandledRequest(request);
}
/// <inheritdoc />
public virtual void HandleRequest(PlayGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
UnhandledRequest(request);
}
/// <inheritdoc />
public virtual void HandleRequest(SetPlaylistItemGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
var waitingState = new WaitingGroupState(LoggerFactory);
context.SetState(waitingState);
waitingState.HandleRequest(request, context, Type, session, cancellationToken);
}
/// <inheritdoc />
public virtual void HandleRequest(RemoveFromPlaylistGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
bool playingItemRemoved;
if (request.ClearPlaylist)
{
context.ClearPlayQueue(request.ClearPlayingItem);
playingItemRemoved = request.ClearPlayingItem;
}
else
{
playingItemRemoved = context.RemoveFromPlayQueue(request.PlaylistItemIds);
}
var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.RemoveItems);
var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
context.SendGroupUpdate(session, SyncPlayBroadcastType.AllGroup, update, cancellationToken);
if (playingItemRemoved && !context.PlayQueue.IsItemPlaying())
{
_logger.LogDebug("Play queue in group {GroupId} is now empty.", context.GroupId.ToString());
IGroupState idleState = new IdleGroupState(LoggerFactory);
context.SetState(idleState);
var stopRequest = new StopGroupRequest();
idleState.HandleRequest(stopRequest, context, Type, session, cancellationToken);
}
}
/// <inheritdoc />
public virtual void HandleRequest(MovePlaylistItemGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
var result = context.MoveItemInPlayQueue(request.PlaylistItemId, request.NewIndex);
if (!result)
{
_logger.LogError("Unable to move item in group {GroupId}.", context.GroupId.ToString());
return;
}
var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.MoveItem);
var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
context.SendGroupUpdate(session, SyncPlayBroadcastType.AllGroup, update, cancellationToken);
}
/// <inheritdoc />
public virtual void HandleRequest(QueueGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
var result = context.AddToPlayQueue(request.ItemIds, request.Mode);
if (!result)
{
_logger.LogError("Unable to add items to play queue in group {GroupId}.", context.GroupId.ToString());
return;
}
var reason = request.Mode switch
{
GroupQueueMode.QueueNext => PlayQueueUpdateReason.QueueNext,
_ => PlayQueueUpdateReason.Queue
};
var playQueueUpdate = context.GetPlayQueueUpdate(reason);
var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
context.SendGroupUpdate(session, SyncPlayBroadcastType.AllGroup, update, cancellationToken);
}
/// <inheritdoc />
public virtual void HandleRequest(UnpauseGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
UnhandledRequest(request);
}
/// <inheritdoc />
public virtual void HandleRequest(PauseGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
UnhandledRequest(request);
}
/// <inheritdoc />
public virtual void HandleRequest(StopGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
UnhandledRequest(request);
}
/// <inheritdoc />
public virtual void HandleRequest(SeekGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
UnhandledRequest(request);
}
/// <inheritdoc />
public virtual void HandleRequest(BufferGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
UnhandledRequest(request);
}
/// <inheritdoc />
public virtual void HandleRequest(ReadyGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
UnhandledRequest(request);
}
/// <inheritdoc />
public virtual void HandleRequest(NextItemGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
UnhandledRequest(request);
}
/// <inheritdoc />
public virtual void HandleRequest(PreviousItemGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
UnhandledRequest(request);
}
/// <inheritdoc />
public virtual void HandleRequest(SetRepeatModeGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
context.SetRepeatMode(request.Mode);
var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.RepeatMode);
var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
context.SendGroupUpdate(session, SyncPlayBroadcastType.AllGroup, update, cancellationToken);
}
/// <inheritdoc />
public virtual void HandleRequest(SetShuffleModeGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
context.SetShuffleMode(request.Mode);
var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.ShuffleMode);
var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
context.SendGroupUpdate(session, SyncPlayBroadcastType.AllGroup, update, cancellationToken);
}
/// <inheritdoc />
public virtual void HandleRequest(PingGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
// Collected pings are used to account for network latency when unpausing playback.
context.UpdatePing(session, request.Ping);
}
/// <inheritdoc />
public virtual void HandleRequest(IgnoreWaitGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
context.SetIgnoreGroupWait(session, request.IgnoreWait);
}
/// <summary>
/// Sends a group state update to all group.
/// </summary>
/// <param name="context">The context of the state.</param>
/// <param name="reason">The reason of the state change.</param>
/// <param name="session">The session.</param>
/// <param name="cancellationToken">The cancellation token.</param>
protected void SendGroupStateUpdate(IGroupStateContext context, IGroupPlaybackRequest reason, SessionInfo session, CancellationToken cancellationToken)
{
// Notify relevant state change event.
var stateUpdate = new GroupStateUpdate(Type, reason.Action);
var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.StateUpdate, stateUpdate);
context.SendGroupUpdate(session, SyncPlayBroadcastType.AllGroup, update, cancellationToken);
}
private void UnhandledRequest(IGroupPlaybackRequest request)
{
_logger.LogWarning("Unhandled request of type {RequestType} in {StateType} state.", request.Action, Type);
}
}
}
|