< Summary - Jellyfin

Information
Class: MediaBrowser.Controller.SyncPlay.GroupStates.WaitingGroupState
Assembly: MediaBrowser.Controller
File(s): /srv/git/jellyfin/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 282
Coverable lines: 282
Total lines: 680
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 106
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
SessionJoined(...)0%2040%
SessionLeaving(...)0%4260%
HandleRequest(...)0%7280%
HandleRequest(...)0%7280%
HandleRequest(...)0%4260%
HandleRequest(...)0%620%
HandleRequest(...)0%620%
HandleRequest(...)0%4260%
HandleRequest(...)0%156120%
HandleRequest(...)0%812280%
HandleRequest(...)0%110100%
HandleRequest(...)0%110100%
HandleRequest(...)0%2040%

File(s)

/srv/git/jellyfin/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs

#LineLine coverage
 1#nullable disable
 2
 3using System;
 4using System.Threading;
 5using MediaBrowser.Controller.Session;
 6using MediaBrowser.Controller.SyncPlay.PlaybackRequests;
 7using MediaBrowser.Model.SyncPlay;
 8using Microsoft.Extensions.Logging;
 9
 10namespace MediaBrowser.Controller.SyncPlay.GroupStates
 11{
 12    /// <summary>
 13    /// Class WaitingGroupState.
 14    /// </summary>
 15    /// <remarks>
 16    /// Class is not thread-safe, external locking is required when accessing methods.
 17    /// </remarks>
 18    public class WaitingGroupState : AbstractGroupState
 19    {
 20        /// <summary>
 21        /// The logger.
 22        /// </summary>
 23        private readonly ILogger<WaitingGroupState> _logger;
 24
 25        /// <summary>
 26        /// Initializes a new instance of the <see cref="WaitingGroupState"/> class.
 27        /// </summary>
 28        /// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
 29        public WaitingGroupState(ILoggerFactory loggerFactory)
 030            : base(loggerFactory)
 31        {
 032            _logger = LoggerFactory.CreateLogger<WaitingGroupState>();
 033        }
 34
 35        /// <inheritdoc />
 36        public override GroupStateType Type { get; } = GroupStateType.Waiting;
 37
 38        /// <summary>
 39        /// Gets or sets a value indicating whether playback should resume when group is ready.
 40        /// </summary>
 41        public bool ResumePlaying { get; set; } = false;
 42
 43        /// <summary>
 44        /// Gets or sets a value indicating whether the initial state has been set.
 45        /// </summary>
 46        private bool InitialStateSet { get; set; } = false;
 47
 48        /// <summary>
 49        /// Gets or sets the group state before the first ever event.
 50        /// </summary>
 51        private GroupStateType InitialState { get; set; }
 52
 53        /// <inheritdoc />
 54        public override void SessionJoined(IGroupStateContext context, GroupStateType prevState, SessionInfo session, Ca
 55        {
 56            // Save state if first event.
 057            if (!InitialStateSet)
 58            {
 059                InitialState = prevState;
 060                InitialStateSet = true;
 61            }
 62
 063            if (prevState.Equals(GroupStateType.Playing))
 64            {
 065                ResumePlaying = true;
 66                // Pause group and compute the media playback position.
 067                var currentTime = DateTime.UtcNow;
 068                var elapsedTime = currentTime - context.LastActivity;
 069                context.LastActivity = currentTime;
 70                // Elapsed time is negative if event happens
 71                // during the delay added to account for latency.
 72                // In this phase clients haven't started the playback yet.
 73                // In other words, LastActivity is in the future,
 74                // when playback unpause is supposed to happen.
 75                // Seek only if playback actually started.
 076                context.PositionTicks += Math.Max(elapsedTime.Ticks, 0);
 77            }
 78
 79            // Prepare new session.
 080            var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.NewPlaylist);
 081            var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
 082            context.SendGroupUpdate(session, SyncPlayBroadcastType.CurrentSession, update, cancellationToken);
 83
 084            context.SetBuffering(session, true);
 85
 86            // Send pause command to all non-buffering sessions.
 087            var command = context.NewSyncPlayCommand(SendCommandType.Pause);
 088            context.SendCommand(session, SyncPlayBroadcastType.AllReady, command, cancellationToken);
 089        }
 90
 91        /// <inheritdoc />
 92        public override void SessionLeaving(IGroupStateContext context, GroupStateType prevState, SessionInfo session, C
 93        {
 94            // Save state if first event.
 095            if (!InitialStateSet)
 96            {
 097                InitialState = prevState;
 098                InitialStateSet = true;
 99            }
 100
 0101            context.SetBuffering(session, false);
 102
 0103            if (!context.IsBuffering())
 104            {
 0105                if (ResumePlaying)
 106                {
 0107                    _logger.LogDebug("Session {SessionId} left group {GroupId}, notifying others to resume.", session.Id
 108
 109                    // Client, that was buffering, left the group.
 0110                    var playingState = new PlayingGroupState(LoggerFactory);
 0111                    context.SetState(playingState);
 0112                    var unpauseRequest = new UnpauseGroupRequest();
 0113                    playingState.HandleRequest(unpauseRequest, context, Type, session, cancellationToken);
 114                }
 115                else
 116                {
 0117                    _logger.LogDebug("Session {SessionId} left group {GroupId}, returning to previous state.", session.I
 118
 119                    // Group is ready, returning to previous state.
 0120                    var pausedState = new PausedGroupState(LoggerFactory);
 0121                    context.SetState(pausedState);
 122                }
 123            }
 0124        }
 125
 126        /// <inheritdoc />
 127        public override void HandleRequest(PlayGroupRequest request, IGroupStateContext context, GroupStateType prevStat
 128        {
 129            // Save state if first event.
 0130            if (!InitialStateSet)
 131            {
 0132                InitialState = prevState;
 0133                InitialStateSet = true;
 134            }
 135
 0136            ResumePlaying = true;
 137
 0138            var setQueueStatus = context.SetPlayQueue(request.PlayingQueue, request.PlayingItemPosition, request.StartPo
 0139            if (!setQueueStatus)
 140            {
 0141                _logger.LogError("Unable to set playing queue in group {GroupId}.", context.GroupId.ToString());
 142
 143                // Ignore request and return to previous state.
 0144                IGroupState newState = prevState switch {
 0145                    GroupStateType.Playing => new PlayingGroupState(LoggerFactory),
 0146                    GroupStateType.Paused => new PausedGroupState(LoggerFactory),
 0147                    _ => new IdleGroupState(LoggerFactory)
 0148                };
 149
 0150                context.SetState(newState);
 0151                return;
 152            }
 153
 0154            var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.NewPlaylist);
 0155            var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
 0156            context.SendGroupUpdate(session, SyncPlayBroadcastType.AllGroup, update, cancellationToken);
 157
 158            // Reset status of sessions and await for all Ready events.
 0159            context.SetAllBuffering(true);
 160
 0161            _logger.LogDebug("Session {SessionId} set a new play queue in group {GroupId}.", session.Id, context.GroupId
 0162        }
 163
 164        /// <inheritdoc />
 165        public override void HandleRequest(SetPlaylistItemGroupRequest request, IGroupStateContext context, GroupStateTy
 166        {
 167            // Save state if first event.
 0168            if (!InitialStateSet)
 169            {
 0170                InitialState = prevState;
 0171                InitialStateSet = true;
 172            }
 173
 0174            ResumePlaying = true;
 175
 0176            var result = context.SetPlayingItem(request.PlaylistItemId);
 0177            if (result)
 178            {
 0179                var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.SetCurrentItem);
 0180                var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
 0181                context.SendGroupUpdate(session, SyncPlayBroadcastType.AllGroup, update, cancellationToken);
 182
 183                // Reset status of sessions and await for all Ready events.
 0184                context.SetAllBuffering(true);
 185            }
 186            else
 187            {
 188                // Return to old state.
 0189                IGroupState newState = prevState switch
 0190                {
 0191                    GroupStateType.Playing => new PlayingGroupState(LoggerFactory),
 0192                    GroupStateType.Paused => new PausedGroupState(LoggerFactory),
 0193                    _ => new IdleGroupState(LoggerFactory)
 0194                };
 195
 0196                context.SetState(newState);
 197
 0198                _logger.LogDebug("Unable to change current playing item in group {GroupId}.", context.GroupId.ToString()
 199            }
 0200        }
 201
 202        /// <inheritdoc />
 203        public override void HandleRequest(UnpauseGroupRequest request, IGroupStateContext context, GroupStateType prevS
 204        {
 205            // Save state if first event.
 0206            if (!InitialStateSet)
 207            {
 0208                InitialState = prevState;
 0209                InitialStateSet = true;
 210            }
 211
 0212            if (prevState.Equals(GroupStateType.Idle))
 213            {
 0214                ResumePlaying = true;
 0215                context.RestartCurrentItem();
 216
 0217                var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.NewPlaylist);
 0218                var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
 0219                context.SendGroupUpdate(session, SyncPlayBroadcastType.AllGroup, update, cancellationToken);
 220
 221                // Reset status of sessions and await for all Ready events.
 0222                context.SetAllBuffering(true);
 223
 0224                _logger.LogDebug("Group {GroupId} is waiting for all ready events.", context.GroupId.ToString());
 225            }
 226            else
 227            {
 0228                if (ResumePlaying)
 229                {
 0230                    _logger.LogDebug("Forcing the playback to start in group {GroupId}. Group-wait is disabled until nex
 231
 232                    // An Unpause request is forcing the playback to start, ignoring sessions that are not ready.
 0233                    context.SetAllBuffering(false);
 234
 235                    // Change state.
 0236                    var playingState = new PlayingGroupState(LoggerFactory)
 0237                    {
 0238                        IgnoreBuffering = true
 0239                    };
 0240                    context.SetState(playingState);
 0241                    playingState.HandleRequest(request, context, Type, session, cancellationToken);
 242                }
 243                else
 244                {
 245                    // Group would have gone to paused state, now will go to playing state when ready.
 0246                    ResumePlaying = true;
 247
 248                    // Notify relevant state change event.
 0249                    SendGroupStateUpdate(context, request, session, cancellationToken);
 250                }
 251            }
 0252        }
 253
 254        /// <inheritdoc />
 255        public override void HandleRequest(PauseGroupRequest request, IGroupStateContext context, GroupStateType prevSta
 256        {
 257            // Save state if first event.
 0258            if (!InitialStateSet)
 259            {
 0260                InitialState = prevState;
 0261                InitialStateSet = true;
 262            }
 263
 264            // Wait for sessions to be ready, then switch to paused state.
 0265            ResumePlaying = false;
 266
 267            // Notify relevant state change event.
 0268            SendGroupStateUpdate(context, request, session, cancellationToken);
 0269        }
 270
 271        /// <inheritdoc />
 272        public override void HandleRequest(StopGroupRequest request, IGroupStateContext context, GroupStateType prevStat
 273        {
 274            // Save state if first event.
 0275            if (!InitialStateSet)
 276            {
 0277                InitialState = prevState;
 0278                InitialStateSet = true;
 279            }
 280
 281            // Change state.
 0282            var idleState = new IdleGroupState(LoggerFactory);
 0283            context.SetState(idleState);
 0284            idleState.HandleRequest(request, context, Type, session, cancellationToken);
 0285        }
 286
 287        /// <inheritdoc />
 288        public override void HandleRequest(SeekGroupRequest request, IGroupStateContext context, GroupStateType prevStat
 289        {
 290            // Save state if first event.
 0291            if (!InitialStateSet)
 292            {
 0293                InitialState = prevState;
 0294                InitialStateSet = true;
 295            }
 296
 0297            if (prevState.Equals(GroupStateType.Playing))
 298            {
 0299                ResumePlaying = true;
 300            }
 0301            else if (prevState.Equals(GroupStateType.Paused))
 302            {
 0303                ResumePlaying = false;
 304            }
 305
 306            // Sanitize PositionTicks.
 0307            var ticks = context.SanitizePositionTicks(request.PositionTicks);
 308
 309            // Seek.
 0310            context.PositionTicks = ticks;
 0311            context.LastActivity = DateTime.UtcNow;
 312
 0313            var command = context.NewSyncPlayCommand(SendCommandType.Seek);
 0314            context.SendCommand(session, SyncPlayBroadcastType.AllGroup, command, cancellationToken);
 315
 316            // Reset status of sessions and await for all Ready events.
 0317            context.SetAllBuffering(true);
 318
 319            // Notify relevant state change event.
 0320            SendGroupStateUpdate(context, request, session, cancellationToken);
 0321        }
 322
 323        /// <inheritdoc />
 324        public override void HandleRequest(BufferGroupRequest request, IGroupStateContext context, GroupStateType prevSt
 325        {
 326            // Save state if first event.
 0327            if (!InitialStateSet)
 328            {
 0329                InitialState = prevState;
 0330                InitialStateSet = true;
 331            }
 332
 333            // Make sure the client is playing the correct item.
 0334            if (!request.PlaylistItemId.Equals(context.PlayQueue.GetPlayingItemPlaylistId()))
 335            {
 0336                _logger.LogDebug("Session {SessionId} reported wrong playlist item in group {GroupId}.", session.Id, con
 337
 0338                var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.SetCurrentItem);
 0339                var updateSession = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
 0340                context.SendGroupUpdate(session, SyncPlayBroadcastType.CurrentSession, updateSession, cancellationToken)
 0341                context.SetBuffering(session, true);
 342
 0343                return;
 344            }
 345
 0346            if (prevState.Equals(GroupStateType.Playing))
 347            {
 348                // Resume playback when all ready.
 0349                ResumePlaying = true;
 350
 0351                context.SetBuffering(session, true);
 352
 353                // Pause group and compute the media playback position.
 0354                var currentTime = DateTime.UtcNow;
 0355                var elapsedTime = currentTime - context.LastActivity;
 0356                context.LastActivity = currentTime;
 357                // Elapsed time is negative if event happens
 358                // during the delay added to account for latency.
 359                // In this phase clients haven't started the playback yet.
 360                // In other words, LastActivity is in the future,
 361                // when playback unpause is supposed to happen.
 362                // Seek only if playback actually started.
 0363                context.PositionTicks += Math.Max(elapsedTime.Ticks, 0);
 364
 365                // Send pause command to all non-buffering sessions.
 0366                var command = context.NewSyncPlayCommand(SendCommandType.Pause);
 0367                context.SendCommand(session, SyncPlayBroadcastType.AllReady, command, cancellationToken);
 368            }
 0369            else if (prevState.Equals(GroupStateType.Paused))
 370            {
 371                // Don't resume playback when all ready.
 0372                ResumePlaying = false;
 373
 0374                context.SetBuffering(session, true);
 375
 376                // Send pause command to buffering session.
 0377                var command = context.NewSyncPlayCommand(SendCommandType.Pause);
 0378                context.SendCommand(session, SyncPlayBroadcastType.CurrentSession, command, cancellationToken);
 379            }
 0380            else if (prevState.Equals(GroupStateType.Waiting))
 381            {
 382                // Another session is now buffering.
 0383                context.SetBuffering(session, true);
 384
 0385                if (!ResumePlaying)
 386                {
 387                    // Force update for this session that should be paused.
 0388                    var command = context.NewSyncPlayCommand(SendCommandType.Pause);
 0389                    context.SendCommand(session, SyncPlayBroadcastType.CurrentSession, command, cancellationToken);
 390                }
 391            }
 392
 393            // Notify relevant state change event.
 0394            SendGroupStateUpdate(context, request, session, cancellationToken);
 0395        }
 396
 397        /// <inheritdoc />
 398        public override void HandleRequest(ReadyGroupRequest request, IGroupStateContext context, GroupStateType prevSta
 399        {
 400            // Save state if first event.
 0401            if (!InitialStateSet)
 402            {
 0403                InitialState = prevState;
 0404                InitialStateSet = true;
 405            }
 406
 407            // Make sure the client is playing the correct item.
 0408            if (!request.PlaylistItemId.Equals(context.PlayQueue.GetPlayingItemPlaylistId()))
 409            {
 0410                _logger.LogDebug("Session {SessionId} reported wrong playlist item in group {GroupId}.", session.Id, con
 411
 0412                var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.SetCurrentItem);
 0413                var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
 0414                context.SendGroupUpdate(session, SyncPlayBroadcastType.CurrentSession, update, cancellationToken);
 0415                context.SetBuffering(session, true);
 416
 0417                return;
 418            }
 419
 420            // Compute elapsed time between the client reported time and now.
 421            // Elapsed time is used to estimate the client position when playback is unpaused.
 422            // Ideally, the request is received and handled without major delays.
 423            // However, to avoid waiting indefinitely when a client is not reporting a correct time,
 424            // the elapsed time is ignored after a certain threshold.
 0425            var currentTime = DateTime.UtcNow;
 0426            var elapsedTime = currentTime.Subtract(request.When);
 0427            var timeSyncThresholdTicks = TimeSpan.FromMilliseconds(context.TimeSyncOffset).Ticks;
 0428            if (Math.Abs(elapsedTime.Ticks) > timeSyncThresholdTicks)
 429            {
 0430                _logger.LogWarning("Session {SessionId} is not time syncing properly. Ignoring elapsed time.", session.I
 431
 0432                elapsedTime = TimeSpan.Zero;
 433            }
 434
 435            // Ignore elapsed time if client is paused.
 0436            if (!request.IsPlaying)
 437            {
 0438                elapsedTime = TimeSpan.Zero;
 439            }
 440
 0441            var requestTicks = context.SanitizePositionTicks(request.PositionTicks);
 0442            var clientPosition = TimeSpan.FromTicks(requestTicks) + elapsedTime;
 0443            var delayTicks = context.PositionTicks - clientPosition.Ticks;
 0444            var maxPlaybackOffsetTicks = TimeSpan.FromMilliseconds(context.MaxPlaybackOffset).Ticks;
 445
 0446            _logger.LogDebug("Session {SessionId} is at {PositionTicks} (delay of {Delay} seconds) in group {GroupId}.",
 447
 0448            if (ResumePlaying)
 449            {
 450                // Handle case where session reported as ready but in reality
 451                // it has no clue of the real position nor the playback state.
 0452                if (!request.IsPlaying && Math.Abs(delayTicks) > maxPlaybackOffsetTicks)
 453                {
 454                    // Session not ready at all.
 0455                    context.SetBuffering(session, true);
 456
 457                    // Correcting session's position.
 0458                    var command = context.NewSyncPlayCommand(SendCommandType.Seek);
 0459                    context.SendCommand(session, SyncPlayBroadcastType.CurrentSession, command, cancellationToken);
 460
 461                    // Notify relevant state change event.
 0462                    SendGroupStateUpdate(context, request, session, cancellationToken);
 463
 0464                    _logger.LogWarning("Session {SessionId} got lost in time, correcting.", session.Id);
 0465                    return;
 466                }
 467
 468                // Session is ready.
 0469                context.SetBuffering(session, false);
 470
 0471                if (context.IsBuffering())
 472                {
 473                    // Others are still buffering, tell this client to pause when ready.
 0474                    var command = context.NewSyncPlayCommand(SendCommandType.Pause);
 0475                    command.When = currentTime.AddTicks(delayTicks);
 0476                    context.SendCommand(session, SyncPlayBroadcastType.CurrentSession, command, cancellationToken);
 477
 0478                    _logger.LogInformation("Session {SessionId} will pause when ready in {Delay} seconds. Group {GroupId
 479                }
 480                else
 481                {
 482                    // If all ready, then start playback.
 483                    // Let other clients resume as soon as the buffering client catches up.
 0484                    if (delayTicks > context.GetHighestPing() * 2 * TimeSpan.TicksPerMillisecond)
 485                    {
 486                        // Client that was buffering is recovering, notifying others to resume.
 0487                        context.LastActivity = currentTime.AddTicks(delayTicks);
 0488                        var command = context.NewSyncPlayCommand(SendCommandType.Unpause);
 0489                        var filter = SyncPlayBroadcastType.AllExceptCurrentSession;
 0490                        if (!request.IsPlaying)
 491                        {
 0492                            filter = SyncPlayBroadcastType.AllGroup;
 493                        }
 494
 0495                        context.SendCommand(session, filter, command, cancellationToken);
 496
 0497                        _logger.LogInformation("Session {SessionId} is recovering, group {GroupId} will resume in {Delay
 498                    }
 499                    else
 500                    {
 501                        // Client, that was buffering, resumed playback but did not update others in time.
 0502                        delayTicks = context.GetHighestPing() * 2 * TimeSpan.TicksPerMillisecond;
 0503                        delayTicks = Math.Max(delayTicks, context.DefaultPing);
 504
 0505                        context.LastActivity = currentTime.AddTicks(delayTicks);
 506
 0507                        var command = context.NewSyncPlayCommand(SendCommandType.Unpause);
 0508                        context.SendCommand(session, SyncPlayBroadcastType.AllGroup, command, cancellationToken);
 509
 0510                        _logger.LogWarning("Session {SessionId} resumed playback, group {GroupId} has {Delay} seconds to
 511                    }
 512
 513                    // Change state.
 0514                    var playingState = new PlayingGroupState(LoggerFactory);
 0515                    context.SetState(playingState);
 0516                    playingState.HandleRequest(request, context, Type, session, cancellationToken);
 517                }
 518            }
 519            else
 520            {
 521                // Check that session is really ready, tolerate player imperfections under a certain threshold.
 0522                if (Math.Abs(context.PositionTicks - requestTicks) > maxPlaybackOffsetTicks)
 523                {
 524                    // Session still not ready.
 0525                    context.SetBuffering(session, true);
 526                    // Session is seeking to wrong position, correcting.
 0527                    var command = context.NewSyncPlayCommand(SendCommandType.Seek);
 0528                    context.SendCommand(session, SyncPlayBroadcastType.CurrentSession, command, cancellationToken);
 529
 530                    // Notify relevant state change event.
 0531                    SendGroupStateUpdate(context, request, session, cancellationToken);
 532
 0533                    _logger.LogWarning("Session {SessionId} is seeking to wrong position, correcting.", session.Id);
 0534                    return;
 535                }
 536
 537                // Session is ready.
 0538                context.SetBuffering(session, false);
 539
 0540                if (!context.IsBuffering())
 541                {
 0542                    _logger.LogDebug("Session {SessionId} is ready, group {GroupId} is ready.", session.Id, context.Grou
 543
 544                    // Group is ready, returning to previous state.
 0545                    var pausedState = new PausedGroupState(LoggerFactory);
 0546                    context.SetState(pausedState);
 547
 0548                    if (InitialState.Equals(GroupStateType.Playing))
 549                    {
 550                        // Group went from playing to waiting state and a pause request occurred while waiting.
 0551                        var pauseRequest = new PauseGroupRequest();
 0552                        pausedState.HandleRequest(pauseRequest, context, Type, session, cancellationToken);
 553                    }
 0554                    else if (InitialState.Equals(GroupStateType.Paused))
 555                    {
 0556                        pausedState.HandleRequest(request, context, Type, session, cancellationToken);
 557                    }
 558                }
 559            }
 0560        }
 561
 562        /// <inheritdoc />
 563        public override void HandleRequest(NextItemGroupRequest request, IGroupStateContext context, GroupStateType prev
 564        {
 565            // Save state if first event.
 0566            if (!InitialStateSet)
 567            {
 0568                InitialState = prevState;
 0569                InitialStateSet = true;
 570            }
 571
 0572            ResumePlaying = true;
 573
 574            // Make sure the client knows the playing item, to avoid duplicate requests.
 0575            if (!request.PlaylistItemId.Equals(context.PlayQueue.GetPlayingItemPlaylistId()))
 576            {
 0577                _logger.LogDebug("Session {SessionId} provided the wrong playlist item for group {GroupId}.", session.Id
 0578                return;
 579            }
 580
 0581            var newItem = context.NextItemInQueue();
 0582            if (newItem)
 583            {
 584                // Send playing-queue update.
 0585                var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.NextItem);
 0586                var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
 0587                context.SendGroupUpdate(session, SyncPlayBroadcastType.AllGroup, update, cancellationToken);
 588
 589                // Reset status of sessions and await for all Ready events.
 0590                context.SetAllBuffering(true);
 591            }
 592            else
 593            {
 594                // Return to old state.
 0595                IGroupState newState = prevState switch
 0596                {
 0597                    GroupStateType.Playing => new PlayingGroupState(LoggerFactory),
 0598                    GroupStateType.Paused => new PausedGroupState(LoggerFactory),
 0599                    _ => new IdleGroupState(LoggerFactory)
 0600                };
 601
 0602                context.SetState(newState);
 603
 0604                _logger.LogDebug("No next item available in group {GroupId}.", context.GroupId.ToString());
 605            }
 0606        }
 607
 608        /// <inheritdoc />
 609        public override void HandleRequest(PreviousItemGroupRequest request, IGroupStateContext context, GroupStateType 
 610        {
 611            // Save state if first event.
 0612            if (!InitialStateSet)
 613            {
 0614                InitialState = prevState;
 0615                InitialStateSet = true;
 616            }
 617
 0618            ResumePlaying = true;
 619
 620            // Make sure the client knows the playing item, to avoid duplicate requests.
 0621            if (!request.PlaylistItemId.Equals(context.PlayQueue.GetPlayingItemPlaylistId()))
 622            {
 0623                _logger.LogDebug("Session {SessionId} provided the wrong playlist item for group {GroupId}.", session.Id
 0624                return;
 625            }
 626
 0627            var newItem = context.PreviousItemInQueue();
 0628            if (newItem)
 629            {
 630                // Send playing-queue update.
 0631                var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.PreviousItem);
 0632                var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
 0633                context.SendGroupUpdate(session, SyncPlayBroadcastType.AllGroup, update, cancellationToken);
 634
 635                // Reset status of sessions and await for all Ready events.
 0636                context.SetAllBuffering(true);
 637            }
 638            else
 639            {
 640                // Return to old state.
 0641                IGroupState newState = prevState switch
 0642                {
 0643                    GroupStateType.Playing => new PlayingGroupState(LoggerFactory),
 0644                    GroupStateType.Paused => new PausedGroupState(LoggerFactory),
 0645                    _ => new IdleGroupState(LoggerFactory)
 0646                };
 647
 0648                context.SetState(newState);
 649
 0650                _logger.LogDebug("No previous item available in group {GroupId}.", context.GroupId.ToString());
 651            }
 0652        }
 653
 654        /// <inheritdoc />
 655        public override void HandleRequest(IgnoreWaitGroupRequest request, IGroupStateContext context, GroupStateType pr
 656        {
 0657            context.SetIgnoreGroupWait(session, request.IgnoreWait);
 658
 0659            if (!context.IsBuffering())
 660            {
 0661                _logger.LogDebug("Ignoring session {SessionId}, group {GroupId} is ready.", session.Id, context.GroupId.
 662
 0663                if (ResumePlaying)
 664                {
 665                    // Client, that was buffering, stopped following playback.
 0666                    var playingState = new PlayingGroupState(LoggerFactory);
 0667                    context.SetState(playingState);
 0668                    var unpauseRequest = new UnpauseGroupRequest();
 0669                    playingState.HandleRequest(unpauseRequest, context, Type, session, cancellationToken);
 670                }
 671                else
 672                {
 673                    // Group is ready, returning to previous state.
 0674                    var pausedState = new PausedGroupState(LoggerFactory);
 0675                    context.SetState(pausedState);
 676                }
 677            }
 0678        }
 679    }
 680}

Methods/Properties

.ctor(Microsoft.Extensions.Logging.ILoggerFactory)
SessionJoined(MediaBrowser.Controller.SyncPlay.IGroupStateContext,MediaBrowser.Model.SyncPlay.GroupStateType,MediaBrowser.Controller.Session.SessionInfo,System.Threading.CancellationToken)
SessionLeaving(MediaBrowser.Controller.SyncPlay.IGroupStateContext,MediaBrowser.Model.SyncPlay.GroupStateType,MediaBrowser.Controller.Session.SessionInfo,System.Threading.CancellationToken)
HandleRequest(MediaBrowser.Controller.SyncPlay.PlaybackRequests.PlayGroupRequest,MediaBrowser.Controller.SyncPlay.IGroupStateContext,MediaBrowser.Model.SyncPlay.GroupStateType,MediaBrowser.Controller.Session.SessionInfo,System.Threading.CancellationToken)
HandleRequest(MediaBrowser.Controller.SyncPlay.PlaybackRequests.SetPlaylistItemGroupRequest,MediaBrowser.Controller.SyncPlay.IGroupStateContext,MediaBrowser.Model.SyncPlay.GroupStateType,MediaBrowser.Controller.Session.SessionInfo,System.Threading.CancellationToken)
HandleRequest(MediaBrowser.Controller.SyncPlay.PlaybackRequests.UnpauseGroupRequest,MediaBrowser.Controller.SyncPlay.IGroupStateContext,MediaBrowser.Model.SyncPlay.GroupStateType,MediaBrowser.Controller.Session.SessionInfo,System.Threading.CancellationToken)
HandleRequest(MediaBrowser.Controller.SyncPlay.PlaybackRequests.PauseGroupRequest,MediaBrowser.Controller.SyncPlay.IGroupStateContext,MediaBrowser.Model.SyncPlay.GroupStateType,MediaBrowser.Controller.Session.SessionInfo,System.Threading.CancellationToken)
HandleRequest(MediaBrowser.Controller.SyncPlay.PlaybackRequests.StopGroupRequest,MediaBrowser.Controller.SyncPlay.IGroupStateContext,MediaBrowser.Model.SyncPlay.GroupStateType,MediaBrowser.Controller.Session.SessionInfo,System.Threading.CancellationToken)
HandleRequest(MediaBrowser.Controller.SyncPlay.PlaybackRequests.SeekGroupRequest,MediaBrowser.Controller.SyncPlay.IGroupStateContext,MediaBrowser.Model.SyncPlay.GroupStateType,MediaBrowser.Controller.Session.SessionInfo,System.Threading.CancellationToken)
HandleRequest(MediaBrowser.Controller.SyncPlay.PlaybackRequests.BufferGroupRequest,MediaBrowser.Controller.SyncPlay.IGroupStateContext,MediaBrowser.Model.SyncPlay.GroupStateType,MediaBrowser.Controller.Session.SessionInfo,System.Threading.CancellationToken)
HandleRequest(MediaBrowser.Controller.SyncPlay.PlaybackRequests.ReadyGroupRequest,MediaBrowser.Controller.SyncPlay.IGroupStateContext,MediaBrowser.Model.SyncPlay.GroupStateType,MediaBrowser.Controller.Session.SessionInfo,System.Threading.CancellationToken)
HandleRequest(MediaBrowser.Controller.SyncPlay.PlaybackRequests.NextItemGroupRequest,MediaBrowser.Controller.SyncPlay.IGroupStateContext,MediaBrowser.Model.SyncPlay.GroupStateType,MediaBrowser.Controller.Session.SessionInfo,System.Threading.CancellationToken)
HandleRequest(MediaBrowser.Controller.SyncPlay.PlaybackRequests.PreviousItemGroupRequest,MediaBrowser.Controller.SyncPlay.IGroupStateContext,MediaBrowser.Model.SyncPlay.GroupStateType,MediaBrowser.Controller.Session.SessionInfo,System.Threading.CancellationToken)
HandleRequest(MediaBrowser.Controller.SyncPlay.PlaybackRequests.IgnoreWaitGroupRequest,MediaBrowser.Controller.SyncPlay.IGroupStateContext,MediaBrowser.Model.SyncPlay.GroupStateType,MediaBrowser.Controller.Session.SessionInfo,System.Threading.CancellationToken)