diff options
Diffstat (limited to 'MediaBrowser.Controller/SyncPlay')
3 files changed, 51 insertions, 6 deletions
diff --git a/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs b/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs index eb38eeb503..8f17039ae1 100644 --- a/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs +++ b/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs @@ -50,6 +50,11 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates /// </summary> private GroupStateType InitialState { get; set; } + /// <summary> + /// Gets or sets a value indicating whether the group position moved during this wait. + /// </summary> + private bool PositionJumped { get; set; } + /// <inheritdoc /> public override void SessionJoined(IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken) { @@ -136,6 +141,7 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates ResumePlaying = true; var setQueueStatus = context.SetPlayQueue(request.PlayingQueue, request.PlayingItemPosition, request.StartPositionTicks); + PositionJumped = setQueueStatus; if (!setQueueStatus) { _logger.LogError("Unable to set playing queue in group {GroupId}.", context.GroupId.ToString()); @@ -175,6 +181,7 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates ResumePlaying = true; var result = context.SetPlayingItem(request.PlaylistItemId); + PositionJumped = result; if (result) { var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.SetCurrentItem); @@ -214,6 +221,7 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates { ResumePlaying = true; context.RestartCurrentItem(); + PositionJumped = true; var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.NewPlaylist); var update = new SyncPlayPlayQueueUpdate(context.GroupId, playQueueUpdate); @@ -310,6 +318,7 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates // Seek. context.PositionTicks = ticks; context.LastActivity = DateTime.UtcNow; + PositionJumped = true; var command = context.NewSyncPlayCommand(SendCommandType.Seek); context.SendCommand(session, SyncPlayBroadcastType.AllGroup, command, cancellationToken); @@ -450,7 +459,13 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates { // Handle case where session reported as ready but in reality // it has no clue of the real position nor the playback state. - if (!request.IsPlaying && Math.Abs(delayTicks) > maxPlaybackOffsetTicks) + // A jump means the session has not applied the new position; without one it is + // catching up after buffering and is allowed to lag. + var maxOffsetTicks = request.IsPlaying && !PositionJumped + ? TimeSpan.FromMilliseconds(context.MaxCatchUpOffset).Ticks + : maxPlaybackOffsetTicks; + + if (Math.Abs(delayTicks) > maxOffsetTicks) { // Session not ready at all. context.SetBuffering(session, true); @@ -501,7 +516,7 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates { // Client, that was buffering, resumed playback but did not update others in time. delayTicks = context.GetHighestPing() * 2 * TimeSpan.TicksPerMillisecond; - delayTicks = Math.Max(delayTicks, context.DefaultPing); + delayTicks = Math.Max(delayTicks, TimeSpan.FromMilliseconds(context.DefaultPing).Ticks); context.LastActivity = currentTime.AddTicks(delayTicks); @@ -580,6 +595,7 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates } var newItem = context.NextItemInQueue(); + PositionJumped = newItem; if (newItem) { // Send playing-queue update. @@ -626,6 +642,7 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates } var newItem = context.PreviousItemInQueue(); + PositionJumped = newItem; if (newItem) { // Send playing-queue update. diff --git a/MediaBrowser.Controller/SyncPlay/IGroupStateContext.cs b/MediaBrowser.Controller/SyncPlay/IGroupStateContext.cs index ddf86be71f..e02d1bde45 100644 --- a/MediaBrowser.Controller/SyncPlay/IGroupStateContext.cs +++ b/MediaBrowser.Controller/SyncPlay/IGroupStateContext.cs @@ -34,6 +34,12 @@ namespace MediaBrowser.Controller.SyncPlay long MaxPlaybackOffset { get; } /// <summary> + /// Gets the maximum offset accepted for a session catching up after buffering, in milliseconds. + /// </summary> + /// <value>The maximum catch-up offset, in milliseconds.</value> + long MaxCatchUpOffset => 60000; + + /// <summary> /// Gets the group identifier. /// </summary> /// <value>The group identifier.</value> diff --git a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs index c0a168192e..258b92e4d9 100644 --- a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs +++ b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs @@ -157,7 +157,10 @@ namespace MediaBrowser.Controller.SyncPlay.Queue /// </summary> public void RestoreSortedPlaylist() { - if (PlayingItemIndex != NoPlayingItemIndex) + // The shuffled playlist is only populated while the shuffle mode is active, so there is + // nothing to map back when the playlist is already sorted. Guarding on its contents keeps + // a redundant request for the sorted mode from indexing an empty list. + if (PlayingItemIndex != NoPlayingItemIndex && _shuffledPlaylist.Count > 0) { var playingItem = _shuffledPlaylist[PlayingItemIndex]; PlayingItemIndex = _sortedPlaylist.IndexOf(playingItem); @@ -272,7 +275,7 @@ namespace MediaBrowser.Controller.SyncPlay.Queue public void SetPlayingItemByIndex(int playlistIndex) { var playlist = GetPlaylistInternal(); - if (playlistIndex < 0 || playlistIndex > playlist.Count) + if (playlistIndex < 0 || playlistIndex >= playlist.Count) { PlayingItemIndex = NoPlayingItemIndex; } @@ -293,6 +296,15 @@ namespace MediaBrowser.Controller.SyncPlay.Queue { var playingItem = GetPlayingItem(); + // Removed items that precede the playing item shift its index as well. + var removedBeforePlayingItem = 0; + if (playingItem is not null) + { + removedBeforePlayingItem = GetPlaylistInternal() + .Take(PlayingItemIndex) + .Count(item => playlistItemIds.Contains(item.PlaylistItemId)); + } + _sortedPlaylist.RemoveAll(item => playlistItemIds.Contains(item.PlaylistItemId)); _shuffledPlaylist.RemoveAll(item => playlistItemIds.Contains(item.PlaylistItemId)); @@ -303,12 +315,12 @@ namespace MediaBrowser.Controller.SyncPlay.Queue if (playlistItemIds.Contains(playingItem.PlaylistItemId)) { // Playing item has been removed, picking previous item. - PlayingItemIndex--; + PlayingItemIndex -= removedBeforePlayingItem + 1; if (PlayingItemIndex < 0) { // Was first element, picking next if available. // Default to no playing item otherwise. - PlayingItemIndex = _sortedPlaylist.Count > 0 ? 0 : NoPlayingItemIndex; + PlayingItemIndex = GetPlaylistInternal().Count > 0 ? 0 : NoPlayingItemIndex; } return true; @@ -444,6 +456,11 @@ namespace MediaBrowser.Controller.SyncPlay.Queue /// <returns><c>true</c> if the playing item changed; <c>false</c> otherwise.</returns> public bool Next() { + if (GetPlaylistInternal().Count == 0) + { + return false; + } + if (RepeatMode.Equals(GroupRepeatMode.RepeatOne)) { LastChange = DateTime.UtcNow; @@ -474,6 +491,11 @@ namespace MediaBrowser.Controller.SyncPlay.Queue /// <returns><c>true</c> if the playing item changed; <c>false</c> otherwise.</returns> public bool Previous() { + if (GetPlaylistInternal().Count == 0) + { + return false; + } + if (RepeatMode.Equals(GroupRepeatMode.RepeatOne)) { LastChange = DateTime.UtcNow; |
