using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using Microsoft.Extensions.Logging; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Syncplay; using MediaBrowser.Model.Syncplay; namespace Emby.Server.Implementations.Syncplay { /// /// Class SyncplayManager. /// public class SyncplayManager : ISyncplayManager, IDisposable { /// /// The logger. /// private readonly ILogger _logger; /// /// The session manager. /// private readonly ISessionManager _sessionManager; /// /// The map between users and groups. /// private readonly ConcurrentDictionary _userToGroupMap = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); /// /// The groups. /// private readonly ConcurrentDictionary _groups = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); private bool _disposed = false; public SyncplayManager( ILogger logger, ISessionManager sessionManager) { _logger = logger; _sessionManager = sessionManager; _sessionManager.SessionEnded += _sessionManager_SessionEnded; _sessionManager.PlaybackStopped += _sessionManager_PlaybackStopped; } /// /// Gets all groups. /// /// All groups. public IEnumerable Groups => _groups.Values; /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and optionally managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_disposed) { return; } _sessionManager.SessionEnded -= _sessionManager_SessionEnded; _sessionManager.PlaybackStopped -= _sessionManager_PlaybackStopped; _disposed = true; } private void CheckDisposed() { if (_disposed) { throw new ObjectDisposedException(GetType().Name); } } void _sessionManager_SessionEnded(object sender, SessionEventArgs e) { LeaveGroup(e.SessionInfo); } void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e) { LeaveGroup(e.Session); } private bool IsUserInGroup(SessionInfo user) { return _userToGroupMap.ContainsKey(user.Id); } private Guid? GetUserGroup(SessionInfo user) { ISyncplayController group; _userToGroupMap.TryGetValue(user.Id, out group); if (group != null) { return group.GetGroupId(); } else { return null; } } /// public void NewGroup(SessionInfo user) { if (IsUserInGroup(user)) { LeaveGroup(user); } var group = new SyncplayController(_logger, _sessionManager, this); _groups[group.GetGroupId().ToString()] = group; group.InitGroup(user); } /// public void JoinGroup(SessionInfo user, string groupId) { if (IsUserInGroup(user)) { if (GetUserGroup(user).Equals(groupId)) return; LeaveGroup(user); } ISyncplayController group; _groups.TryGetValue(groupId, out group); if (group == null) { _logger.LogError("Syncplaymanager JoinGroup: " + groupId + " does not exist."); var update = new SyncplayGroupUpdate(); update.Type = SyncplayGroupUpdateType.NotInGroup; _sessionManager.SendSyncplayGroupUpdate(user.Id.ToString(), update, CancellationToken.None); return; } group.UserJoin(user); } /// public void LeaveGroup(SessionInfo user) { ISyncplayController group; _userToGroupMap.TryGetValue(user.Id, out group); if (group == null) { _logger.LogWarning("Syncplaymanager HandleRequest: " + user.Id + " not in group."); var update = new SyncplayGroupUpdate(); update.Type = SyncplayGroupUpdateType.NotInGroup; _sessionManager.SendSyncplayGroupUpdate(user.Id.ToString(), update, CancellationToken.None); return; } group.UserLeave(user); if (group.IsGroupEmpty()) { _groups.Remove(group.GetGroupId().ToString(), out _); } } /// public List ListGroups(SessionInfo user) { // Filter by playing item if the user is viewing something already if (user.NowPlayingItem != null) { return _groups.Values.Where( group => group.GetPlayingItemId().Equals(user.FullNowPlayingItem.Id) ).Select( group => group.GetInfo() ).ToList(); } // Otherwise show all available groups else { return _groups.Values.Select( group => group.GetInfo() ).ToList(); } } /// public void HandleRequest(SessionInfo user, SyncplayRequestInfo request) { ISyncplayController group; _userToGroupMap.TryGetValue(user.Id, out group); if (group == null) { _logger.LogWarning("Syncplaymanager HandleRequest: " + user.Id + " not in group."); var update = new SyncplayGroupUpdate(); update.Type = SyncplayGroupUpdateType.NotInGroup; _sessionManager.SendSyncplayGroupUpdate(user.Id.ToString(), update, CancellationToken.None); return; } group.HandleRequest(user, request); } /// public void MapUserToGroup(SessionInfo user, ISyncplayController group) { if (IsUserInGroup(user)) { throw new InvalidOperationException("User in other group already!"); } _userToGroupMap[user.Id] = group; } /// public void UnmapUserFromGroup(SessionInfo user, ISyncplayController group) { if (!IsUserInGroup(user)) { throw new InvalidOperationException("User not in any group!"); } ISyncplayController tempGroup; _userToGroupMap.Remove(user.Id, out tempGroup); if (!tempGroup.GetGroupId().Equals(group.GetGroupId())) { throw new InvalidOperationException("User was in wrong group!"); } } } }