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 sessions and groups. /// private readonly ConcurrentDictionary _sessionToGroupMap = 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) { var session = e.SessionInfo; if (!IsSessionInGroup(session)) return; LeaveGroup(session); } void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e) { var session = e.Session; if (!IsSessionInGroup(session)) return; LeaveGroup(session); } private bool IsSessionInGroup(SessionInfo session) { return _sessionToGroupMap.ContainsKey(session.Id); } private Guid? GetSessionGroup(SessionInfo session) { ISyncplayController group; _sessionToGroupMap.TryGetValue(session.Id, out group); if (group != null) { return group.GetGroupId(); } else { return null; } } /// public void NewGroup(SessionInfo session) { { if (IsSessionInGroup(session)) LeaveGroup(session); } var group = new SyncplayController(_logger, _sessionManager, this); _groups[group.GetGroupId().ToString()] = group; group.InitGroup(session); } /// public void JoinGroup(SessionInfo session, string groupId) { if (IsSessionInGroup(session)) { if (GetSessionGroup(session).Equals(groupId)) return; LeaveGroup(session); } 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(session.Id.ToString(), update, CancellationToken.None); return; } group.SessionJoin(session); } /// public void LeaveGroup(SessionInfo session) { ISyncplayController group; _sessionToGroupMap.TryGetValue(session.Id, out group); if (group == null) { _logger.LogWarning("Syncplaymanager HandleRequest: " + session.Id + " not in group."); var update = new SyncplayGroupUpdate(); update.Type = SyncplayGroupUpdateType.NotInGroup; _sessionManager.SendSyncplayGroupUpdate(session.Id.ToString(), update, CancellationToken.None); return; } group.SessionLeave(session); if (group.IsGroupEmpty()) { _groups.Remove(group.GetGroupId().ToString(), out _); } } /// public List ListGroups(SessionInfo session) { // Filter by playing item if the user is viewing something already if (session.NowPlayingItem != null) { return _groups.Values.Where( group => group.GetPlayingItemId().Equals(session.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 session, SyncplayRequestInfo request) { ISyncplayController group; _sessionToGroupMap.TryGetValue(session.Id, out group); if (group == null) { _logger.LogWarning("Syncplaymanager HandleRequest: " + session.Id + " not in group."); var update = new SyncplayGroupUpdate(); update.Type = SyncplayGroupUpdateType.NotInGroup; _sessionManager.SendSyncplayGroupUpdate(session.Id.ToString(), update, CancellationToken.None); return; } group.HandleRequest(session, request); } /// public void MapSessionToGroup(SessionInfo session, ISyncplayController group) { if (IsSessionInGroup(session)) { throw new InvalidOperationException("Session in other group already!"); } _sessionToGroupMap[session.Id] = group; } /// public void UnmapSessionFromGroup(SessionInfo session, ISyncplayController group) { if (!IsSessionInGroup(session)) { throw new InvalidOperationException("Session not in any group!"); } ISyncplayController tempGroup; _sessionToGroupMap.Remove(session.Id, out tempGroup); if (!tempGroup.GetGroupId().Equals(group.GetGroupId())) { throw new InvalidOperationException("Session was in wrong group!"); } } } }