aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Server.Implementations/SyncPlay/GroupController.cs71
-rw-r--r--Emby.Server.Implementations/SyncPlay/GroupStates/AbstractGroupState.cs2
-rw-r--r--Emby.Server.Implementations/SyncPlay/GroupStates/IdleGroupState.cs3
-rw-r--r--Emby.Server.Implementations/SyncPlay/GroupStates/PausedGroupState.cs9
-rw-r--r--Emby.Server.Implementations/SyncPlay/GroupStates/PlayingGroupState.cs3
-rw-r--r--Emby.Server.Implementations/SyncPlay/GroupStates/WaitingGroupState.cs17
-rw-r--r--Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs124
-rw-r--r--MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs206
-rw-r--r--MediaBrowser.Model/SyncPlay/GroupRequestType.cs33
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
+ }
+}