diff options
9 files changed, 293 insertions, 175 deletions
diff --git a/Emby.Server.Implementations/SyncPlay/GroupController.cs b/Emby.Server.Implementations/SyncPlay/GroupController.cs index 2d8668014..01e116617 100644 --- a/Emby.Server.Implementations/SyncPlay/GroupController.cs +++ b/Emby.Server.Implementations/SyncPlay/GroupController.cs @@ -127,16 +127,6 @@ namespace Emby.Server.Implementations.SyncPlay } /// <summary> - /// Checks if a session is in this group. - /// </summary> - /// <param name="sessionId">The session identifier to check.</param> - /// <returns><c>true</c> if the session is in this group; <c>false</c> otherwise.</returns> - private bool ContainsSession(string sessionId) - { - return Participants.ContainsKey(sessionId); - } - - /// <summary> /// Adds the session to the group. /// </summary> /// <param name="session">The session.</param> @@ -179,13 +169,17 @@ namespace Emby.Server.Implementations.SyncPlay .Select(session => session.Session) .ToArray(); case SyncPlayBroadcastType.AllExceptCurrentSession: - return Participants.Values.Select( - session => session.Session).Where( - session => !session.Id.Equals(from.Id)).ToArray(); + return Participants + .Values + .Select(session => session.Session) + .Where(session => !session.Id.Equals(from.Id)) + .ToArray(); case SyncPlayBroadcastType.AllReady: - return Participants.Values.Where( - session => !session.IsBuffering).Select( - session => session.Session).ToArray(); + return Participants + .Values + .Where(session => !session.IsBuffering) + .Select(session => session.Session) + .ToArray(); default: return Array.Empty<SessionInfo>(); } @@ -238,7 +232,8 @@ namespace Emby.Server.Implementations.SyncPlay } // Get list of users. - var users = Participants.Values + var users = Participants + .Values .Select(participant => _userManager.GetUserById(participant.Session.UserId)); // Find problematic users. @@ -265,6 +260,7 @@ namespace Emby.Server.Implementations.SyncPlay if (sessionIsPlayingAnItem) { var playlist = session.NowPlayingQueue.Select(item => item.Id).ToArray(); + PlayQueue.Reset(); PlayQueue.SetPlaylist(playlist); PlayQueue.SetPlayingItemById(session.FullNowPlayingItem.Id); RunTimeTicks = session.FullNowPlayingItem.RunTimeTicks ?? 0; @@ -366,7 +362,7 @@ namespace Emby.Server.Implementations.SyncPlay /// <inheritdoc /> public void SetIgnoreGroupWait(SessionInfo session, bool ignoreGroupWait) { - if (!ContainsSession(session.Id)) + if (!Participants.ContainsKey(session.Id)) { return; } @@ -444,8 +440,8 @@ namespace Emby.Server.Implementations.SyncPlay public long SanitizePositionTicks(long? positionTicks) { var ticks = positionTicks ?? 0; - ticks = ticks >= 0 ? ticks : 0; - ticks = ticks > RunTimeTicks ? RunTimeTicks : ticks; + ticks = Math.Max(ticks, 0); + ticks = Math.Min(ticks, RunTimeTicks); return ticks; } @@ -517,6 +513,7 @@ namespace Emby.Server.Implementations.SyncPlay return false; } + PlayQueue.Reset(); PlayQueue.SetPlaylist(playQueue); PlayQueue.SetPlayingItemByIndex(playingItemPosition); var item = _libraryManager.GetItemById(PlayQueue.GetPlayingItemId()); @@ -646,12 +643,33 @@ namespace Emby.Server.Implementations.SyncPlay /// <inheritdoc /> public void SetRepeatMode(string mode) { - PlayQueue.SetRepeatMode(mode); + switch (mode) + { + case "RepeatOne": + PlayQueue.SetRepeatMode(GroupRepeatMode.RepeatOne); + break; + case "RepeatAll": + PlayQueue.SetRepeatMode(GroupRepeatMode.RepeatAll); + break; + default: + // On unknown values, default to repeat none. + PlayQueue.SetRepeatMode(GroupRepeatMode.RepeatNone); + break; + } } /// <inheritdoc /> public void SetShuffleMode(string mode) { - PlayQueue.SetShuffleMode(mode); + switch (mode) + { + case "Shuffle": + PlayQueue.SetShuffleMode(GroupShuffleMode.Shuffle); + break; + default: + // On unknown values, default to sorted playlist. + PlayQueue.SetShuffleMode(GroupShuffleMode.Sorted); + break; + } } /// <inheritdoc /> @@ -663,8 +681,13 @@ namespace Emby.Server.Implementations.SyncPlay { var currentTime = DateTime.UtcNow; var elapsedTime = currentTime - LastActivity; - // Event may happen during the delay added to account for latency. - startPositionTicks += elapsedTime.Ticks > 0 ? elapsedTime.Ticks : 0; + // Elapsed time is negative if event happens + // during the delay added to account for latency. + // In this phase clients haven't started the playback yet. + // In other words, LastActivity is in the future, + // when playback unpause is supposed to happen. + // Adjust ticks only if playback actually started. + startPositionTicks += Math.Max(elapsedTime.Ticks, 0); } return new PlayQueueUpdate() diff --git a/Emby.Server.Implementations/SyncPlay/GroupStates/AbstractGroupState.cs b/Emby.Server.Implementations/SyncPlay/GroupStates/AbstractGroupState.cs index 1eb110772..26cd51b8d 100644 --- a/Emby.Server.Implementations/SyncPlay/GroupStates/AbstractGroupState.cs +++ b/Emby.Server.Implementations/SyncPlay/GroupStates/AbstractGroupState.cs @@ -212,7 +212,7 @@ namespace MediaBrowser.Controller.SyncPlay private void UnhandledRequest(IPlaybackGroupRequest request) { - _logger.LogWarning("HandleRequest: unhandled {0} request for {1} state.", request.GetRequestType(), this.GetGroupState()); + _logger.LogWarning("HandleRequest: unhandled {0} request for {1} state.", request.GetRequestType(), GetGroupState()); } } } diff --git a/Emby.Server.Implementations/SyncPlay/GroupStates/IdleGroupState.cs b/Emby.Server.Implementations/SyncPlay/GroupStates/IdleGroupState.cs index b8510715a..70fe3e006 100644 --- a/Emby.Server.Implementations/SyncPlay/GroupStates/IdleGroupState.cs +++ b/Emby.Server.Implementations/SyncPlay/GroupStates/IdleGroupState.cs @@ -16,7 +16,8 @@ namespace MediaBrowser.Controller.SyncPlay /// <summary> /// Default constructor. /// </summary> - public IdleGroupState(ILogger logger) : base(logger) + public IdleGroupState(ILogger logger) + : base(logger) { // Do nothing. } diff --git a/Emby.Server.Implementations/SyncPlay/GroupStates/PausedGroupState.cs b/Emby.Server.Implementations/SyncPlay/GroupStates/PausedGroupState.cs index 8c4bd20b1..ca2cb0988 100644 --- a/Emby.Server.Implementations/SyncPlay/GroupStates/PausedGroupState.cs +++ b/Emby.Server.Implementations/SyncPlay/GroupStates/PausedGroupState.cs @@ -17,7 +17,8 @@ namespace MediaBrowser.Controller.SyncPlay /// <summary> /// Default constructor. /// </summary> - public PausedGroupState(ILogger logger) : base(logger) + public PausedGroupState(ILogger logger) + : base(logger) { // Do nothing. } @@ -70,8 +71,12 @@ namespace MediaBrowser.Controller.SyncPlay var currentTime = DateTime.UtcNow; var elapsedTime = currentTime - context.LastActivity; context.LastActivity = currentTime; + // Elapsed time is negative if event happens + // during the delay added to account for latency. + // In this phase clients haven't started the playback yet. + // In other words, LastActivity is in the future, + // when playback unpause is supposed to happen. // Seek only if playback actually started. - // Pause request may be issued during the delay added to account for latency. context.PositionTicks += Math.Max(elapsedTime.Ticks, 0); var command = context.NewSyncPlayCommand(SendCommandType.Pause); diff --git a/Emby.Server.Implementations/SyncPlay/GroupStates/PlayingGroupState.cs b/Emby.Server.Implementations/SyncPlay/GroupStates/PlayingGroupState.cs index a3b0baf96..85119669d 100644 --- a/Emby.Server.Implementations/SyncPlay/GroupStates/PlayingGroupState.cs +++ b/Emby.Server.Implementations/SyncPlay/GroupStates/PlayingGroupState.cs @@ -22,7 +22,8 @@ namespace MediaBrowser.Controller.SyncPlay /// <summary> /// Default constructor. /// </summary> - public PlayingGroupState(ILogger logger) : base(logger) + public PlayingGroupState(ILogger logger) + : base(logger) { // Do nothing. } diff --git a/Emby.Server.Implementations/SyncPlay/GroupStates/WaitingGroupState.cs b/Emby.Server.Implementations/SyncPlay/GroupStates/WaitingGroupState.cs index ff1d379d7..8e970751f 100644 --- a/Emby.Server.Implementations/SyncPlay/GroupStates/WaitingGroupState.cs +++ b/Emby.Server.Implementations/SyncPlay/GroupStates/WaitingGroupState.cs @@ -32,7 +32,8 @@ namespace MediaBrowser.Controller.SyncPlay /// <summary> /// Default constructor. /// </summary> - public WaitingGroupState(ILogger logger) : base(logger) + public WaitingGroupState(ILogger logger) + : base(logger) { // Do nothing. } @@ -59,8 +60,12 @@ namespace MediaBrowser.Controller.SyncPlay var currentTime = DateTime.UtcNow; var elapsedTime = currentTime - context.LastActivity; context.LastActivity = currentTime; + // Elapsed time is negative if event happens + // during the delay added to account for latency. + // In this phase clients haven't started the playback yet. + // In other words, LastActivity is in the future, + // when playback unpause is supposed to happen. // Seek only if playback actually started. - // Event may happen during the delay added to account for latency. context.PositionTicks += Math.Max(elapsedTime.Ticks, 0); } @@ -355,6 +360,12 @@ namespace MediaBrowser.Controller.SyncPlay var currentTime = DateTime.UtcNow; var elapsedTime = currentTime - context.LastActivity; context.LastActivity = currentTime; + // Elapsed time is negative if event happens + // during the delay added to account for latency. + // In this phase clients haven't started the playback yet. + // In other words, LastActivity is in the future, + // when playback unpause is supposed to happen. + // Seek only if playback actually started. context.PositionTicks += Math.Max(elapsedTime.Ticks, 0); // Send pause command to all non-buffering sessions. @@ -484,7 +495,7 @@ namespace MediaBrowser.Controller.SyncPlay { // Client, that was buffering, resumed playback but did not update others in time. delayTicks = context.GetHighestPing() * 2 * TimeSpan.TicksPerMillisecond; - delayTicks = delayTicks < context.DefaultPing ? context.DefaultPing : delayTicks; + delayTicks = Math.Max(delayTicks, context.DefaultPing); context.LastActivity = currentTime.AddTicks(delayTicks); diff --git a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs index 6e3b49249..ab9be145f 100644 --- a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs +++ b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs @@ -160,6 +160,48 @@ namespace Emby.Server.Implementations.SyncPlay // TODO: probably remove this event, not used at the moment. } + private bool IsRequestValid<T>(SessionInfo session, GroupRequestType requestType, T request, bool checkRequest = true) + { + if (session == null || (request == null && checkRequest)) + { + return false; + } + + var user = _userManager.GetUserById(session.UserId); + + if (user.SyncPlayAccess == SyncPlayAccess.None) + { + _logger.LogWarning("IsRequestValid: {0} does not have access to SyncPlay. Requested {1}.", session.Id, requestType); + + var error = new GroupUpdate<string>() + { + // TODO: rename to a more generic error. Next PR will fix this. + Type = GroupUpdateType.JoinGroupDenied + }; + _sessionManager.SendSyncPlayGroupUpdate(session, error, CancellationToken.None); + return false; + } + + if (requestType.Equals(GroupRequestType.NewGroup) && user.SyncPlayAccess != SyncPlayAccess.CreateAndJoinGroups) + { + _logger.LogWarning("IsRequestValid: {0} does not have permission to create groups.", session.Id); + + var error = new GroupUpdate<string> + { + Type = GroupUpdateType.CreateGroupDenied + }; + _sessionManager.SendSyncPlayGroupUpdate(session, error, CancellationToken.None); + return false; + } + + return true; + } + + private bool IsRequestValid(SessionInfo session, GroupRequestType requestType) + { + return IsRequestValid(session, requestType, session, false); + } + private bool IsSessionInGroup(SessionInfo session) { return _sessionToGroupMap.ContainsKey(session.Id); @@ -174,17 +216,9 @@ namespace Emby.Server.Implementations.SyncPlay /// <inheritdoc /> public void NewGroup(SessionInfo session, NewGroupRequest request, CancellationToken cancellationToken) { - var user = _userManager.GetUserById(session.UserId); - - if (user.SyncPlayAccess != SyncPlayAccess.CreateAndJoinGroups) + // TODO: create abstract class for GroupRequests to avoid explicit request type here. + if (!IsRequestValid(session, GroupRequestType.NewGroup, request)) { - _logger.LogWarning("NewGroup: {0} does not have permission to create groups.", session.Id); - - var error = new GroupUpdate<string> - { - Type = GroupUpdateType.CreateGroupDenied - }; - _sessionManager.SendSyncPlayGroupUpdate(session, error, CancellationToken.None); return; } @@ -205,24 +239,17 @@ namespace Emby.Server.Implementations.SyncPlay /// <inheritdoc /> public void JoinGroup(SessionInfo session, Guid groupId, JoinGroupRequest request, CancellationToken cancellationToken) { - var user = _userManager.GetUserById(session.UserId); - - if (user.SyncPlayAccess == SyncPlayAccess.None) + // TODO: create abstract class for GroupRequests to avoid explicit request type here. + if (!IsRequestValid(session, GroupRequestType.JoinGroup, request)) { - _logger.LogWarning("JoinGroup: {0} does not have access to SyncPlay.", session.Id); - - var error = new GroupUpdate<string>() - { - Type = GroupUpdateType.JoinGroupDenied - }; - _sessionManager.SendSyncPlayGroupUpdate(session, error, CancellationToken.None); return; } + var user = _userManager.GetUserById(session.UserId); + lock (_groupsLock) { - ISyncPlayGroupController group; - _groups.TryGetValue(groupId, out group); + _groups.TryGetValue(groupId, out ISyncPlayGroupController group); if (group == null) { @@ -267,6 +294,12 @@ namespace Emby.Server.Implementations.SyncPlay /// <inheritdoc /> public void LeaveGroup(SessionInfo session, CancellationToken cancellationToken) { + // TODO: create abstract class for GroupRequests to avoid explicit request type here. + if (!IsRequestValid(session, GroupRequestType.LeaveGroup)) + { + return; + } + // TODO: determine what happens to users that are in a group and get their permissions revoked. lock (_groupsLock) { @@ -297,32 +330,27 @@ namespace Emby.Server.Implementations.SyncPlay /// <inheritdoc /> public List<GroupInfoDto> ListGroups(SessionInfo session) { - var user = _userManager.GetUserById(session.UserId); - - if (user.SyncPlayAccess == SyncPlayAccess.None) + // TODO: create abstract class for GroupRequests to avoid explicit request type here. + if (!IsRequestValid(session, GroupRequestType.ListGroups)) { return new List<GroupInfoDto>(); } - return _groups.Values.Where( - group => group.HasAccessToPlayQueue(user)).Select( - group => group.GetInfo()).ToList(); + var user = _userManager.GetUserById(session.UserId); + + return _groups + .Values + .Where(group => group.HasAccessToPlayQueue(user)) + .Select(group => group.GetInfo()) + .ToList(); } /// <inheritdoc /> public void HandleRequest(SessionInfo session, IPlaybackGroupRequest request, CancellationToken cancellationToken) { - var user = _userManager.GetUserById(session.UserId); - - if (user.SyncPlayAccess == SyncPlayAccess.None) + // TODO: create abstract class for GroupRequests to avoid explicit request type here. + if (!IsRequestValid(session, GroupRequestType.Playback, request)) { - _logger.LogWarning("HandleRequest: {0} does not have access to SyncPlay.", session.Id); - - var error = new GroupUpdate<string>() - { - Type = GroupUpdateType.JoinGroupDenied - }; - _sessionManager.SendSyncPlayGroupUpdate(session, error, CancellationToken.None); return; } @@ -349,6 +377,16 @@ namespace Emby.Server.Implementations.SyncPlay /// <inheritdoc /> public void AddSessionToGroup(SessionInfo session, ISyncPlayGroupController group) { + if (session == null) + { + throw new InvalidOperationException("Session is null!"); + } + + if (group == null) + { + throw new InvalidOperationException("Group is null!"); + } + if (IsSessionInGroup(session)) { throw new InvalidOperationException("Session in other group already!"); @@ -360,6 +398,16 @@ namespace Emby.Server.Implementations.SyncPlay /// <inheritdoc /> public void RemoveSessionFromGroup(SessionInfo session, ISyncPlayGroupController group) { + if (session == null) + { + throw new InvalidOperationException("Session is null!"); + } + + if (group == null) + { + throw new InvalidOperationException("Group is null!"); + } + if (!IsSessionInGroup(session)) { throw new InvalidOperationException("Session not in any group!"); diff --git a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs index 6373a5211..030005abe 100644 --- a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs +++ b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Controller.SyncPlay /// <summary> /// Class PlayQueueManager. /// </summary> - public class PlayQueueManager : IDisposable + public class PlayQueueManager { /// <summary> /// Gets or sets the playing item index. @@ -69,7 +69,11 @@ namespace MediaBrowser.Controller.SyncPlay /// <value>The progressive identifier.</value> private int ProgressiveId { get; set; } = 0; - private bool _disposed = false; + /// <summary> + /// Placeholder index for when no item is playing. + /// </summary> + /// <value>The no-playing item index.</value> + private const int NoPlayingItemIndex = -1; /// <summary> /// Initializes a new instance of the <see cref="PlayQueueManager" /> class. @@ -79,27 +83,6 @@ namespace MediaBrowser.Controller.SyncPlay Reset(); } - /// <inheritdoc /> - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// <summary> - /// Releases unmanaged and optionally managed resources. - /// </summary> - /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> - protected virtual void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - _disposed = true; - } - /// <summary> /// Gets the next available identifier. /// </summary> @@ -140,6 +123,26 @@ namespace MediaBrowser.Controller.SyncPlay } /// <summary> + /// Gets the current playing item, depending on the shuffle mode. + /// </summary> + /// <returns>The playing item.</returns> + private QueueItem GetPlayingItem() + { + if (PlayingItemIndex == NoPlayingItemIndex) + { + return null; + } + else if (ShuffleMode.Equals(GroupShuffleMode.Shuffle)) + { + return ShuffledPlaylist[PlayingItemIndex]; + } + else + { + return SortedPlaylist[PlayingItemIndex]; + } + } + + /// <summary> /// Gets the current playlist as an array, depending on the shuffle mode. /// </summary> /// <returns>The array of items in the playlist.</returns> @@ -155,30 +158,36 @@ namespace MediaBrowser.Controller.SyncPlay } /// <summary> - /// Sets a new playlist. Playing item is set to none. Resets shuffle mode and repeat mode as well. + /// Sets a new playlist. Playing item is reset. /// </summary> /// <param name="items">The new items of the playlist.</param> public void SetPlaylist(Guid[] items) { + SortedPlaylist.Clear(); + ShuffledPlaylist.Clear(); + SortedPlaylist = CreateQueueItemsFromArray(items); - PlayingItemIndex = -1; - ShuffleMode = GroupShuffleMode.Sorted; - RepeatMode = GroupRepeatMode.RepeatNone; + if (ShuffleMode.Equals(GroupShuffleMode.Shuffle)) + { + ShuffledPlaylist = SortedPlaylist.ToList(); + ShuffledPlaylist.Shuffle(); + } + + PlayingItemIndex = NoPlayingItemIndex; LastChange = DateTime.UtcNow; } /// <summary> - /// Appends new items to the playlist. The specified order is mantained for the sorted playlist, whereas items get shuffled for the shuffled playlist. + /// Appends new items to the playlist. The specified order is mantained. /// </summary> /// <param name="items">The items to add to the playlist.</param> public void Queue(Guid[] items) { var newItems = CreateQueueItemsFromArray(items); - SortedPlaylist.AddRange(newItems); + SortedPlaylist.AddRange(newItems); if (ShuffleMode.Equals(GroupShuffleMode.Shuffle)) { - newItems.Shuffle(); ShuffledPlaylist.AddRange(newItems); } @@ -190,17 +199,13 @@ namespace MediaBrowser.Controller.SyncPlay /// </summary> public void ShufflePlaylist() { - if (SortedPlaylist.Count() == 0) - { - return; - } - - if (PlayingItemIndex < 0) { + if (PlayingItemIndex == NoPlayingItemIndex) { ShuffledPlaylist = SortedPlaylist.ToList(); ShuffledPlaylist.Shuffle(); } - else + else if (ShuffleMode.Equals(GroupShuffleMode.Sorted)) { + // First time shuffle. var playingItem = SortedPlaylist[PlayingItemIndex]; ShuffledPlaylist = SortedPlaylist.ToList(); ShuffledPlaylist.RemoveAt(PlayingItemIndex); @@ -208,6 +213,15 @@ namespace MediaBrowser.Controller.SyncPlay ShuffledPlaylist = ShuffledPlaylist.Prepend(playingItem).ToList(); PlayingItemIndex = 0; } + else + { + // Re-shuffle playlist. + var playingItem = ShuffledPlaylist[PlayingItemIndex]; + ShuffledPlaylist.RemoveAt(PlayingItemIndex); + ShuffledPlaylist.Shuffle(); + ShuffledPlaylist = ShuffledPlaylist.Prepend(playingItem).ToList(); + PlayingItemIndex = 0; + } ShuffleMode = GroupShuffleMode.Shuffle; LastChange = DateTime.UtcNow; @@ -216,9 +230,9 @@ namespace MediaBrowser.Controller.SyncPlay /// <summary> /// Resets the playlist to sorted mode. Shuffle mode is changed. /// </summary> - public void SortShuffledPlaylist() + public void RestoreSortedPlaylist() { - if (PlayingItemIndex >= 0) + if (PlayingItemIndex != NoPlayingItemIndex) { var playingItem = ShuffledPlaylist[PlayingItemIndex]; PlayingItemIndex = SortedPlaylist.IndexOf(playingItem); @@ -231,12 +245,12 @@ namespace MediaBrowser.Controller.SyncPlay } /// <summary> - /// Clears the playlist. + /// Clears the playlist. Shuffle mode is preserved. /// </summary> /// <param name="clearPlayingItem">Whether to remove the playing item as well.</param> public void ClearPlaylist(bool clearPlayingItem) { - var playingItem = SortedPlaylist[PlayingItemIndex]; + var playingItem = GetPlayingItem(); SortedPlaylist.Clear(); ShuffledPlaylist.Clear(); LastChange = DateTime.UtcNow; @@ -246,13 +260,18 @@ namespace MediaBrowser.Controller.SyncPlay SortedPlaylist.Add(playingItem); if (ShuffleMode.Equals(GroupShuffleMode.Shuffle)) { - SortedPlaylist.Add(playingItem); + ShuffledPlaylist.Add(playingItem); } + PlayingItemIndex = 0; + } + else + { + PlayingItemIndex = NoPlayingItemIndex; } } /// <summary> - /// Adds new items to the playlist right after the playing item. The specified order is mantained for the sorted playlist, whereas items get shuffled for the shuffled playlist. + /// Adds new items to the playlist right after the playing item. The specified order is mantained. /// </summary> /// <param name="items">The items to add to the playlist.</param> public void QueueNext(Guid[] items) @@ -261,10 +280,10 @@ namespace MediaBrowser.Controller.SyncPlay if (ShuffleMode.Equals(GroupShuffleMode.Shuffle)) { - // Append items to sorted playlist as they are. - SortedPlaylist.AddRange(newItems); - // Shuffle items before adding to shuffled playlist. - newItems.Shuffle(); + var playingItem = GetPlayingItem(); + var sortedPlayingItemIndex = SortedPlaylist.IndexOf(playingItem); + // Append items to sorted and shuffled playlist as they are. + SortedPlaylist.InsertRange(sortedPlayingItemIndex + 1, newItems); ShuffledPlaylist.InsertRange(PlayingItemIndex + 1, newItems); } else @@ -281,16 +300,10 @@ namespace MediaBrowser.Controller.SyncPlay /// <returns>The playlist identifier of the playing item.</returns> public string GetPlayingItemPlaylistId() { - if (PlayingItemIndex < 0) - { - return null; - } - - var list = GetPlaylistAsList(); - - if (list.Count() > 0) + var playingItem = GetPlayingItem(); + if (playingItem != null) { - return list[PlayingItemIndex].PlaylistItemId; + return playingItem.PlaylistItemId; } else { @@ -304,16 +317,10 @@ namespace MediaBrowser.Controller.SyncPlay /// <returns>The playing item identifier.</returns> public Guid GetPlayingItemId() { - if (PlayingItemIndex < 0) - { - return Guid.Empty; - } - - var list = GetPlaylistAsList(); - - if (list.Count() > 0) + var playingItem = GetPlayingItem(); + if (playingItem != null) { - return list[PlayingItemIndex].ItemId; + return playingItem.ItemId; } else { @@ -342,7 +349,7 @@ namespace MediaBrowser.Controller.SyncPlay var playlistIds = GetPlaylistAsList().Select(queueItem => queueItem.PlaylistItemId).ToList(); PlayingItemIndex = playlistIds.IndexOf(playlistItemId); LastChange = DateTime.UtcNow; - return PlayingItemIndex != -1; + return PlayingItemIndex != NoPlayingItemIndex; } /// <summary> @@ -354,7 +361,7 @@ namespace MediaBrowser.Controller.SyncPlay var list = GetPlaylistAsList(); if (playlistIndex < 0 || playlistIndex > list.Count()) { - PlayingItemIndex = -1; + PlayingItemIndex = NoPlayingItemIndex; } else { @@ -371,13 +378,9 @@ namespace MediaBrowser.Controller.SyncPlay /// <returns><c>true</c> if playing item got removed; <c>false</c> otherwise.</returns> public bool RemoveFromPlaylist(string[] playlistItemIds) { - var playingItem = SortedPlaylist[PlayingItemIndex]; - if (ShuffleMode.Equals(GroupShuffleMode.Shuffle)) - { - playingItem = ShuffledPlaylist[PlayingItemIndex]; - } - + var playingItem = GetPlayingItem(); var playlistItemIdsList = playlistItemIds.ToList(); + SortedPlaylist.RemoveAll(item => playlistItemIdsList.Contains(item.PlaylistItemId)); ShuffledPlaylist.RemoveAll(item => playlistItemIdsList.Contains(item.PlaylistItemId)); @@ -392,7 +395,8 @@ namespace MediaBrowser.Controller.SyncPlay if (PlayingItemIndex < 0) { // Was first element, picking next if available. - PlayingItemIndex = SortedPlaylist.Count() > 0 ? 0 : -1; + // Default to no playing item otherwise. + PlayingItemIndex = SortedPlaylist.Count() > 0 ? 0 : NoPlayingItemIndex; } return true; @@ -419,18 +423,19 @@ namespace MediaBrowser.Controller.SyncPlay public bool MovePlaylistItem(string playlistItemId, int newIndex) { var list = GetPlaylistAsList(); - var playingItem = list[PlayingItemIndex]; + var playingItem = GetPlayingItem(); var playlistIds = list.Select(queueItem => queueItem.PlaylistItemId).ToList(); var oldIndex = playlistIds.IndexOf(playlistItemId); - if (oldIndex < 0) { + if (oldIndex < 0) + { return false; } var queueItem = list[oldIndex]; list.RemoveAt(oldIndex); - newIndex = newIndex > list.Count() ? list.Count() : newIndex; - newIndex = newIndex < 0 ? 0 : newIndex; + newIndex = Math.Min(newIndex, list.Count()); + newIndex = Math.Max(newIndex, 0); list.Insert(newIndex, queueItem); LastChange = DateTime.UtcNow; @@ -446,7 +451,7 @@ namespace MediaBrowser.Controller.SyncPlay ProgressiveId = 0; SortedPlaylist.Clear(); ShuffledPlaylist.Clear(); - PlayingItemIndex = -1; + PlayingItemIndex = NoPlayingItemIndex; ShuffleMode = GroupShuffleMode.Sorted; RepeatMode = GroupRepeatMode.RepeatNone; LastChange = DateTime.UtcNow; @@ -456,21 +461,9 @@ namespace MediaBrowser.Controller.SyncPlay /// Sets the repeat mode. /// </summary> /// <param name="mode">The new mode.</param> - public void SetRepeatMode(string mode) + public void SetRepeatMode(GroupRepeatMode mode) { - switch (mode) - { - case "RepeatOne": - RepeatMode = GroupRepeatMode.RepeatOne; - break; - case "RepeatAll": - RepeatMode = GroupRepeatMode.RepeatAll; - break; - default: - RepeatMode = GroupRepeatMode.RepeatNone; - break; - } - + RepeatMode = mode; LastChange = DateTime.UtcNow; } @@ -478,19 +471,15 @@ namespace MediaBrowser.Controller.SyncPlay /// Sets the shuffle mode. /// </summary> /// <param name="mode">The new mode.</param> - public void SetShuffleMode(string mode) + public void SetShuffleMode(GroupShuffleMode mode) { - switch (mode) + if (mode.Equals(GroupShuffleMode.Shuffle)) { - case "Shuffle": - ShufflePlaylist(); - break; - default: - if (!ShuffleMode.Equals(mode)) - { - SortShuffledPlaylist(); - } - break; + ShufflePlaylist(); + } + else + { + RestoreSortedPlaylist(); } } @@ -499,7 +488,14 @@ namespace MediaBrowser.Controller.SyncPlay /// </summary> public void ToggleShuffleMode() { - SetShuffleMode(ShuffleMode.Equals(GroupShuffleMode.Shuffle) ? "Shuffle" : ""); + if (ShuffleMode.Equals(GroupShuffleMode.Sorted)) + { + ShufflePlaylist(); + } + else + { + RestoreSortedPlaylist(); + } } /// <summary> diff --git a/MediaBrowser.Model/SyncPlay/GroupRequestType.cs b/MediaBrowser.Model/SyncPlay/GroupRequestType.cs new file mode 100644 index 000000000..e7361817c --- /dev/null +++ b/MediaBrowser.Model/SyncPlay/GroupRequestType.cs @@ -0,0 +1,33 @@ +namespace MediaBrowser.Model.SyncPlay +{ + /// <summary> + /// Enum GroupRequestType. + /// </summary> + public enum GroupRequestType + { + /// <summary> + /// A user is requesting to create a new group. + /// </summary> + NewGroup, + + /// <summary> + /// A user is requesting to join a group. + /// </summary> + JoinGroup, + + /// <summary> + /// A user is requesting to leave a group. + /// </summary> + LeaveGroup, + + /// <summary> + /// A user is requesting the list of available groups. + /// </summary> + ListGroups, + + /// <summary> + /// A user is sending a playback command to a group. + /// </summary> + Playback + } +} |
