Luke Pulverenti 11 лет назад
Родитель
Сommit
eb72c2db51

+ 0 - 11
MediaBrowser.Common/Net/IServerManager.cs

@@ -62,17 +62,6 @@ namespace MediaBrowser.Common.Net
         /// <exception cref="System.ArgumentNullException">messageType</exception>
         Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, CancellationToken cancellationToken);
 
-        /// <summary>
-        /// Sends the web socket message async.
-        /// </summary>
-        /// <typeparam name="T"></typeparam>
-        /// <param name="messageType">Type of the message.</param>
-        /// <param name="dataFunction">The data function.</param>
-        /// <param name="connections">The connections.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, IEnumerable<IWebSocketConnection> connections, CancellationToken cancellationToken);
-
         /// <summary>
         /// Adds the web socket listeners.
         /// </summary>

+ 6 - 0
MediaBrowser.Common/Net/IWebSocketConnection.cs

@@ -7,6 +7,12 @@ namespace MediaBrowser.Common.Net
 {
     public interface IWebSocketConnection : IDisposable
     {
+        /// <summary>
+        /// Gets the id.
+        /// </summary>
+        /// <value>The id.</value>
+        Guid Id { get; }
+
         /// <summary>
         /// Gets the last activity date.
         /// </summary>

+ 31 - 15
MediaBrowser.Controller/Session/ISessionController.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Model.Session;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Session;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -7,55 +8,70 @@ namespace MediaBrowser.Controller.Session
     public interface ISessionController
     {
         /// <summary>
-        /// Supportses the specified session.
+        /// Gets a value indicating whether [supports media remote control].
         /// </summary>
-        /// <param name="session">The session.</param>
-        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        bool Supports(SessionInfo session);
+        /// <value><c>true</c> if [supports media remote control]; otherwise, <c>false</c>.</value>
+        bool SupportsMediaRemoteControl { get; }
+
+        /// <summary>
+        /// Gets a value indicating whether this instance is session active.
+        /// </summary>
+        /// <value><c>true</c> if this instance is session active; otherwise, <c>false</c>.</value>
+        bool IsSessionActive { get; }
 
         /// <summary>
         /// Sends the system command.
         /// </summary>
-        /// <param name="session">The session.</param>
         /// <param name="command">The command.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task SendSystemCommand(SessionInfo session, SystemCommand command, CancellationToken cancellationToken);
+        Task SendSystemCommand(SystemCommand command, CancellationToken cancellationToken);
 
         /// <summary>
         /// Sends the message command.
         /// </summary>
-        /// <param name="session">The session.</param>
         /// <param name="command">The command.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task SendMessageCommand(SessionInfo session, MessageCommand command, CancellationToken cancellationToken);
+        Task SendMessageCommand(MessageCommand command, CancellationToken cancellationToken);
 
         /// <summary>
         /// Sends the play command.
         /// </summary>
-        /// <param name="session">The session.</param>
         /// <param name="command">The command.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task SendPlayCommand(SessionInfo session, PlayRequest command, CancellationToken cancellationToken);
+        Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken);
 
         /// <summary>
         /// Sends the browse command.
         /// </summary>
-        /// <param name="session">The session.</param>
         /// <param name="command">The command.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task SendBrowseCommand(SessionInfo session, BrowseRequest command, CancellationToken cancellationToken);
+        Task SendBrowseCommand(BrowseRequest command, CancellationToken cancellationToken);
 
         /// <summary>
         /// Sends the playstate command.
         /// </summary>
-        /// <param name="session">The session.</param>
         /// <param name="command">The command.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task SendPlaystateCommand(SessionInfo session, PlaystateRequest command, CancellationToken cancellationToken);
+        Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Sends the library update info.
+        /// </summary>
+        /// <param name="info">The info.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Sends the restart required message.
+        /// </summary>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        Task SendRestartRequiredMessage(CancellationToken cancellationToken);
     }
 }

+ 7 - 6
MediaBrowser.Controller/Session/ISessionManager.cs

@@ -13,12 +13,6 @@ namespace MediaBrowser.Controller.Session
     /// </summary>
     public interface ISessionManager
     {
-        /// <summary>
-        /// Adds the parts.
-        /// </summary>
-        /// <param name="remoteControllers">The remote controllers.</param>
-        void AddParts(IEnumerable<ISessionController> remoteControllers);
-
         /// <summary>
         /// Occurs when [playback start].
         /// </summary>
@@ -119,5 +113,12 @@ namespace MediaBrowser.Controller.Session
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         Task SendPlaystateCommand(Guid sessionId, PlaystateRequest command, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Sends the restart required message.
+        /// </summary>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        Task SendRestartRequiredMessage(CancellationToken cancellationToken);
     }
 }

+ 15 - 14
MediaBrowser.Controller/Session/SessionInfo.cs

@@ -1,9 +1,6 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Net;
+using MediaBrowser.Controller.Entities;
 using System;
 using System.Collections.Generic;
-using System.Linq;
 
 namespace MediaBrowser.Controller.Session
 {
@@ -14,7 +11,6 @@ namespace MediaBrowser.Controller.Session
     {
         public SessionInfo()
         {
-            WebSockets = new List<IWebSocketConnection>();
             QueueableMediaTypes = new List<string>();
         }
 
@@ -114,18 +110,18 @@ namespace MediaBrowser.Controller.Session
         /// <value>The device id.</value>
         public string DeviceId { get; set; }
 
-        /// <summary>
-        /// Gets or sets the web socket.
-        /// </summary>
-        /// <value>The web socket.</value>
-        public List<IWebSocketConnection> WebSockets { get; set; }
-
         /// <summary>
         /// Gets or sets the application version.
         /// </summary>
         /// <value>The application version.</value>
         public string ApplicationVersion { get; set; }
 
+        /// <summary>
+        /// Gets or sets the session controller.
+        /// </summary>
+        /// <value>The session controller.</value>
+        public ISessionController SessionController { get; set; }
+
         /// <summary>
         /// Gets a value indicating whether this instance is active.
         /// </summary>
@@ -134,9 +130,9 @@ namespace MediaBrowser.Controller.Session
         {
             get
             {
-                if (WebSockets.Count > 0)
+                if (SessionController != null)
                 {
-                    return WebSockets.Any(i => i.State == WebSocketState.Open);
+                    return SessionController.IsSessionActive;
                 }
 
                 return (DateTime.UtcNow - LastActivityDate).TotalMinutes <= 10;
@@ -151,7 +147,12 @@ namespace MediaBrowser.Controller.Session
         {
             get
             {
-                return WebSockets.Any(i => i.State == WebSocketState.Open);
+                if (SessionController != null)
+                {
+                    return SessionController.SupportsMediaRemoteControl;
+                }
+                
+                return false;
             }
         }
     }

+ 18 - 0
MediaBrowser.Model/Querying/ItemQuery.cs

@@ -206,6 +206,24 @@ namespace MediaBrowser.Model.Querying
         /// <value>The parent index number.</value>
         public int? ParentIndexNumber { get; set; }
 
+        /// <summary>
+        /// Gets or sets the min players.
+        /// </summary>
+        /// <value>The min players.</value>
+        public int? MinPlayers { get; set; }
+
+        /// <summary>
+        /// Gets or sets the name starts with or greater.
+        /// </summary>
+        /// <value>The name starts with or greater.</value>
+        public string NameStartsWithOrGreater { get; set; }
+
+        /// <summary>
+        /// Gets or sets the album artist starts with or greater.
+        /// </summary>
+        /// <value>The album artist starts with or greater.</value>
+        public string AlbumArtistStartsWithOrGreater { get; set; }
+        
         /// <summary>
         /// Initializes a new instance of the <see cref="ItemQuery" /> class.
         /// </summary>

+ 23 - 24
MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs

@@ -1,5 +1,4 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Controller.Session;
@@ -21,7 +20,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
         private readonly ILibraryManager _libraryManager;
 
         private readonly ISessionManager _sessionManager;
-        private readonly IServerManager _serverManager;
         private readonly IUserManager _userManager;
         private readonly ILogger _logger;
 
@@ -48,11 +46,10 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
         /// </summary>
         private const int LibraryUpdateDuration = 20000;
 
-        public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IServerManager serverManager, IUserManager userManager, ILogger logger)
+        public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger)
         {
             _libraryManager = libraryManager;
             _sessionManager = sessionManager;
-            _serverManager = serverManager;
             _userManager = userManager;
             _logger = logger;
         }
@@ -187,31 +184,33 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
         /// <param name="foldersAddedTo">The folders added to.</param>
         /// <param name="foldersRemovedFrom">The folders removed from.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
-        private async void SendChangeNotifications(IEnumerable<BaseItem> itemsAdded, IEnumerable<BaseItem> itemsUpdated, IEnumerable<BaseItem> itemsRemoved, IEnumerable<Folder> foldersAddedTo, IEnumerable<Folder> foldersRemovedFrom, CancellationToken cancellationToken)
+        private async void SendChangeNotifications(List<BaseItem> itemsAdded, List<BaseItem> itemsUpdated, List<BaseItem> itemsRemoved, List<Folder> foldersAddedTo, List<Folder> foldersRemovedFrom, CancellationToken cancellationToken)
         {
-            var currentSessions = _sessionManager.Sessions.ToList();
-
-            var users = currentSessions.Select(i => i.User)
-                .Where(i => i != null)
-                .Select(i => i.Id)
-                .Distinct()
-                .ToList();
-
-            foreach (var userId in users)
+            foreach (var user in _userManager.Users.ToList())
             {
-                var id = userId;
-                var webSockets = currentSessions.Where(u => u.User != null && u.User.Id == id)
-                    .SelectMany(i => i.WebSockets)
+                var id = user.Id;
+                var userSessions = _sessionManager.Sessions
+                    .Where(u => u.User != null && u.User.Id == id && u.SessionController != null && u.IsActive)
                     .ToList();
 
-                try
-                {
-                    await _serverManager.SendWebSocketMessageAsync("LibraryChanged", () => GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, id), webSockets, cancellationToken).ConfigureAwait(false);
-                }
-                catch (Exception ex)
+                if (userSessions.Count > 0)
                 {
-                    _logger.ErrorException("Error sending LibraryChanged message", ex);
+                    var info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo,
+                                                    foldersRemovedFrom, id);
+
+                    foreach (var userSession in userSessions)
+                    {
+                        try
+                        {
+                            await userSession.SessionController.SendLibraryUpdateInfo(info, cancellationToken).ConfigureAwait(false);
+                        }
+                        catch (Exception ex)
+                        {
+                            _logger.ErrorException("Error sending LibraryChanged message", ex);
+                        }
+                    }
                 }
+
             }
         }
 

+ 7 - 2
MediaBrowser.Server.Implementations/EntryPoints/WebSocketEvents.cs

@@ -8,8 +8,10 @@ using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Controller.Session;
 using MediaBrowser.Model.Tasks;
 using System;
+using System.Threading;
 
 namespace MediaBrowser.Server.Implementations.EntryPoints
 {
@@ -45,13 +47,15 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
 
         private readonly IDtoService _dtoService;
 
+        private ISessionManager _sessionManager;
+
         /// <summary>
         /// Initializes a new instance of the <see cref="WebSocketEvents" /> class.
         /// </summary>
         /// <param name="serverManager">The server manager.</param>
         /// <param name="logger">The logger.</param>
         /// <param name="userManager">The user manager.</param>
-        public WebSocketEvents(IServerManager serverManager, IServerApplicationHost appHost, IUserManager userManager, IInstallationManager installationManager, ITaskManager taskManager, IDtoService dtoService)
+        public WebSocketEvents(IServerManager serverManager, IServerApplicationHost appHost, IUserManager userManager, IInstallationManager installationManager, ITaskManager taskManager, IDtoService dtoService, ISessionManager sessionManager)
         {
             _serverManager = serverManager;
             _userManager = userManager;
@@ -59,6 +63,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
             _appHost = appHost;
             _taskManager = taskManager;
             _dtoService = dtoService;
+            _sessionManager = sessionManager;
         }
 
         public void Run()
@@ -126,7 +131,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
         /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
         void kernel_HasPendingRestartChanged(object sender, EventArgs e)
         {
-            _serverManager.SendWebSocketMessage("RestartRequired", _appHost.GetSystemInfo());
+            _sessionManager.SendRestartRequiredMessage(CancellationToken.None);
         }
 
         /// <summary>

+ 1 - 1
MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs

@@ -273,7 +273,7 @@ namespace MediaBrowser.Server.Implementations.ServerManager
         /// dataFunction
         /// or
         /// cancellationToken</exception>
-        public async Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, IEnumerable<IWebSocketConnection> connections, CancellationToken cancellationToken)
+        private async Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, IEnumerable<IWebSocketConnection> connections, CancellationToken cancellationToken)
         {
             if (string.IsNullOrEmpty(messageType))
             {

+ 7 - 0
MediaBrowser.Server.Implementations/ServerManager/WebSocketConnection.cs

@@ -56,6 +56,12 @@ namespace MediaBrowser.Server.Implementations.ServerManager
         /// <value>The last activity date.</value>
         public DateTime LastActivityDate { get; private set; }
 
+        /// <summary>
+        /// Gets the id.
+        /// </summary>
+        /// <value>The id.</value>
+        public Guid Id { get; private set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="WebSocketConnection" /> class.
         /// </summary>
@@ -83,6 +89,7 @@ namespace MediaBrowser.Server.Implementations.ServerManager
                 throw new ArgumentNullException("logger");
             }
 
+            Id = Guid.NewGuid();
             _jsonSerializer = jsonSerializer;
             _socket = socket;
             _socket.OnReceiveBytes = OnReceiveInternal;

+ 24 - 30
MediaBrowser.Server.Implementations/Session/SessionManager.cs

@@ -78,12 +78,6 @@ namespace MediaBrowser.Server.Implementations.Session
             _userRepository = userRepository;
         }
 
-        private List<ISessionController> _remoteControllers;
-        public void AddParts(IEnumerable<ISessionController> remoteControllers)
-        {
-            _remoteControllers = remoteControllers.ToList();
-        }
-
         /// <summary>
         /// Gets all connections.
         /// </summary>
@@ -433,18 +427,8 @@ namespace MediaBrowser.Server.Implementations.Session
             {
                 throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
             }
-            
-            return session;
-        }
 
-        /// <summary>
-        /// Gets the controllers.
-        /// </summary>
-        /// <param name="session">The session.</param>
-        /// <returns>IEnumerable{ISessionRemoteController}.</returns>
-        private IEnumerable<ISessionController> GetControllers(SessionInfo session)
-        {
-            return _remoteControllers.Where(i => i.Supports(session));
+            return session;
         }
 
         /// <summary>
@@ -458,9 +442,7 @@ namespace MediaBrowser.Server.Implementations.Session
         {
             var session = GetSessionForRemoteControl(sessionId);
 
-            var tasks = GetControllers(session).Select(i => i.SendSystemCommand(session, command, cancellationToken));
-
-            return Task.WhenAll(tasks);
+            return session.SessionController.SendSystemCommand(command, cancellationToken);
         }
 
         /// <summary>
@@ -474,9 +456,7 @@ namespace MediaBrowser.Server.Implementations.Session
         {
             var session = GetSessionForRemoteControl(sessionId);
 
-            var tasks = GetControllers(session).Select(i => i.SendMessageCommand(session, command, cancellationToken));
-
-            return Task.WhenAll(tasks);
+            return session.SessionController.SendMessageCommand(command, cancellationToken);
         }
 
         /// <summary>
@@ -490,9 +470,7 @@ namespace MediaBrowser.Server.Implementations.Session
         {
             var session = GetSessionForRemoteControl(sessionId);
 
-            var tasks = GetControllers(session).Select(i => i.SendPlayCommand(session, command, cancellationToken));
-
-            return Task.WhenAll(tasks);
+            return session.SessionController.SendPlayCommand(command, cancellationToken);
         }
 
         /// <summary>
@@ -506,9 +484,7 @@ namespace MediaBrowser.Server.Implementations.Session
         {
             var session = GetSessionForRemoteControl(sessionId);
 
-            var tasks = GetControllers(session).Select(i => i.SendBrowseCommand(session, command, cancellationToken));
-
-            return Task.WhenAll(tasks);
+            return session.SessionController.SendBrowseCommand(command, cancellationToken);
         }
 
         /// <summary>
@@ -522,7 +498,25 @@ namespace MediaBrowser.Server.Implementations.Session
         {
             var session = GetSessionForRemoteControl(sessionId);
 
-            var tasks = GetControllers(session).Select(i => i.SendPlaystateCommand(session, command, cancellationToken));
+            return session.SessionController.SendPlaystateCommand(command, cancellationToken);
+        }
+
+        public Task SendRestartRequiredMessage(CancellationToken cancellationToken)
+        {
+            var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
+
+            var tasks = sessions.Select(session => Task.Run(async () =>
+            {
+                try
+                {
+                    await session.SessionController.SendRestartRequiredMessage(cancellationToken).ConfigureAwait(false);
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error in SendRestartRequiredMessage.", ex);
+                }
+
+            }));
 
             return Task.WhenAll(tasks);
         }

+ 161 - 97
MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs

@@ -1,8 +1,8 @@
 using MediaBrowser.Common.Net;
+using MediaBrowser.Controller;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Session;
 using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
 using System;
 using System.Linq;
 using System.Threading.Tasks;
@@ -33,6 +33,7 @@ namespace MediaBrowser.Server.Implementations.Session
         /// The _dto service
         /// </summary>
         private readonly IDtoService _dtoService;
+        private readonly IServerApplicationHost _appHost;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="SessionWebSocketListener" /> class.
@@ -40,11 +41,12 @@ namespace MediaBrowser.Server.Implementations.Session
         /// <param name="sessionManager">The session manager.</param>
         /// <param name="logManager">The log manager.</param>
         /// <param name="dtoService">The dto service.</param>
-        public SessionWebSocketListener(ISessionManager sessionManager, ILogManager logManager, IDtoService dtoService)
+        public SessionWebSocketListener(ISessionManager sessionManager, ILogManager logManager, IDtoService dtoService, IServerApplicationHost appHost)
         {
             _sessionManager = sessionManager;
             _logger = logManager.GetLogger(GetType().Name);
             _dtoService = dtoService;
+            _appHost = appHost;
         }
 
         /// <summary>
@@ -56,48 +58,11 @@ namespace MediaBrowser.Server.Implementations.Session
         {
             if (string.Equals(message.MessageType, "Identity", StringComparison.OrdinalIgnoreCase))
             {
-                _logger.Debug("Received Identity message");
-
-                var vals = message.Data.Split('|');
-
-                var client = vals[0];
-                var deviceId = vals[1];
-                var version = vals[2];
-
-                var session = _sessionManager.Sessions
-                    .FirstOrDefault(i => string.Equals(i.DeviceId, deviceId) &&
-                        string.Equals(i.Client, client) &&
-                        string.Equals(i.ApplicationVersion, version));
-
-                if (session != null)
-                {
-                    var sockets = session.WebSockets.Where(i => i.State == WebSocketState.Open).ToList();
-                    sockets.Add(message.Connection);
-
-                    session.WebSockets = sockets;
-                }
-                else
-                {
-                    _logger.Warn("Unable to determine session based on identity message: {0}", message.Data);
-                }
+                ProcessIdentityMessage(message);
             }
             else if (string.Equals(message.MessageType, "Context", StringComparison.OrdinalIgnoreCase))
             {
-                var session = _sessionManager.Sessions.FirstOrDefault(i => i.WebSockets.Contains(message.Connection));
-
-                if (session != null)
-                {
-                    var vals = message.Data.Split('|');
-
-                    session.NowViewingItemType = vals[0];
-                    session.NowViewingItemId = vals[1];
-                    session.NowViewingItemName = vals[2];
-                    session.NowViewingContext = vals.Length > 3 ? vals[3] : null;
-                }
-                else
-                {
-                    _logger.Warn("Unable to determine session based on context message: {0}", message.Data);
-                }
+                ProcessContextMessage(message);
             }
             else if (string.Equals(message.MessageType, "PlaybackStart", StringComparison.OrdinalIgnoreCase))
             {
@@ -105,77 +70,96 @@ namespace MediaBrowser.Server.Implementations.Session
             }
             else if (string.Equals(message.MessageType, "PlaybackProgress", StringComparison.OrdinalIgnoreCase))
             {
-                var session = _sessionManager.Sessions.FirstOrDefault(i => i.WebSockets.Contains(message.Connection));
+                ReportPlaybackProgress(message);
+            }
+            else if (string.Equals(message.MessageType, "PlaybackStopped", StringComparison.OrdinalIgnoreCase))
+            {
+                ReportPlaybackStopped(message);
+            }
 
-                if (session != null && session.User != null)
-                {
-                    var vals = message.Data.Split('|');
+            return _trueTaskResult;
+        }
 
-                    var item = _dtoService.GetItemByDtoId(vals[0]);
+        /// <summary>
+        /// Processes the identity message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        private void ProcessIdentityMessage(WebSocketMessageInfo message)
+        {
+            _logger.Debug("Received Identity message");
 
-                    long? positionTicks = null;
+            var vals = message.Data.Split('|');
 
-                    if (vals.Length > 1)
-                    {
-                        long pos;
+            var client = vals[0];
+            var deviceId = vals[1];
+            var version = vals[2];
 
-                        if (long.TryParse(vals[1], out pos))
-                        {
-                            positionTicks = pos;
-                        }
-                    }
+            var session = _sessionManager.Sessions
+                .FirstOrDefault(i => string.Equals(i.DeviceId, deviceId) &&
+                    string.Equals(i.Client, client) &&
+                    string.Equals(i.ApplicationVersion, version));
 
-                    var isPaused = vals.Length > 2 && string.Equals(vals[2], "true", StringComparison.OrdinalIgnoreCase);
-                    var isMuted = vals.Length > 3 && string.Equals(vals[3], "true", StringComparison.OrdinalIgnoreCase);
+            if (session != null)
+            {
+                var controller = new WebSocketController(session, _appHost);
+                controller.Sockets.Add(message.Connection);
 
-                    var info = new PlaybackProgressInfo
-                    {
-                        Item = item,
-                        PositionTicks = positionTicks,
-                        IsMuted = isMuted,
-                        IsPaused = isPaused,
-                        SessionId = session.Id
-                    };
-
-                    _sessionManager.OnPlaybackProgress(info);
-                }
+                session.SessionController = controller;
             }
-            else if (string.Equals(message.MessageType, "PlaybackStopped", StringComparison.OrdinalIgnoreCase))
+            else
             {
-                _logger.Debug("Received PlaybackStopped message");
-                
-                var session = _sessionManager.Sessions.FirstOrDefault(i => i.WebSockets.Contains(message.Connection));
+                _logger.Warn("Unable to determine session based on identity message: {0}", message.Data);
+            }
+        }
 
-                if (session != null && session.User != null)
-                {
-                    var vals = message.Data.Split('|');
+        /// <summary>
+        /// Processes the context message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        private void ProcessContextMessage(WebSocketMessageInfo message)
+        {
+            var session = GetSessionFromMessage(message);
 
-                    var item = _dtoService.GetItemByDtoId(vals[0]);
+            if (session != null)
+            {
+                var vals = message.Data.Split('|');
 
-                    long? positionTicks = null;
+                session.NowViewingItemType = vals[0];
+                session.NowViewingItemId = vals[1];
+                session.NowViewingItemName = vals[2];
+                session.NowViewingContext = vals.Length > 3 ? vals[3] : null;
+            }
+        }
 
-                    if (vals.Length > 1)
-                    {
-                        long pos;
+        /// <summary>
+        /// Gets the session from message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        /// <returns>SessionInfo.</returns>
+        private SessionInfo GetSessionFromMessage(WebSocketMessageInfo message)
+        {
+            var result = _sessionManager.Sessions.FirstOrDefault(i =>
+            {
+                var controller = i.SessionController as WebSocketController;
 
-                        if (long.TryParse(vals[1], out pos))
-                        {
-                            positionTicks = pos;
-                        }
+                if (controller != null)
+                {
+                    if (controller.Sockets.Any(s => s.Id == message.Connection.Id))
+                    {
+                        return true;
                     }
+                }
 
-                    var info = new PlaybackStopInfo
-                    {
-                        Item = item,
-                        PositionTicks = positionTicks,
-                        SessionId = session.Id
-                    };
+                return false;
 
-                    _sessionManager.OnPlaybackStopped(info);
-                }
+            });
+
+            if (result == null)
+            {
+                _logger.Error("Unable to session based on web socket message");
             }
 
-            return _trueTaskResult;
+            return result;
         }
 
         /// <summary>
@@ -185,9 +169,8 @@ namespace MediaBrowser.Server.Implementations.Session
         private void ReportPlaybackStart(WebSocketMessageInfo message)
         {
             _logger.Debug("Received PlaybackStart message");
-            
-            var session = _sessionManager.Sessions
-                .FirstOrDefault(i => i.WebSockets.Contains(message.Connection));
+
+            var session = GetSessionFromMessage(message);
 
             if (session != null && session.User != null)
             {
@@ -206,7 +189,7 @@ namespace MediaBrowser.Server.Implementations.Session
                 {
                     queueableMediaTypes = vals[2];
                 }
-  
+
                 var info = new PlaybackInfo
                 {
                     CanSeek = canSeek,
@@ -218,5 +201,86 @@ namespace MediaBrowser.Server.Implementations.Session
                 _sessionManager.OnPlaybackStart(info);
             }
         }
+
+        /// <summary>
+        /// Reports the playback progress.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        private void ReportPlaybackProgress(WebSocketMessageInfo message)
+        {
+            var session = GetSessionFromMessage(message);
+
+            if (session != null && session.User != null)
+            {
+                var vals = message.Data.Split('|');
+
+                var item = _dtoService.GetItemByDtoId(vals[0]);
+
+                long? positionTicks = null;
+
+                if (vals.Length > 1)
+                {
+                    long pos;
+
+                    if (long.TryParse(vals[1], out pos))
+                    {
+                        positionTicks = pos;
+                    }
+                }
+
+                var isPaused = vals.Length > 2 && string.Equals(vals[2], "true", StringComparison.OrdinalIgnoreCase);
+                var isMuted = vals.Length > 3 && string.Equals(vals[3], "true", StringComparison.OrdinalIgnoreCase);
+
+                var info = new PlaybackProgressInfo
+                {
+                    Item = item,
+                    PositionTicks = positionTicks,
+                    IsMuted = isMuted,
+                    IsPaused = isPaused,
+                    SessionId = session.Id
+                };
+
+                _sessionManager.OnPlaybackProgress(info);
+            }
+        }
+
+        /// <summary>
+        /// Reports the playback stopped.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        private void ReportPlaybackStopped(WebSocketMessageInfo message)
+        {
+            _logger.Debug("Received PlaybackStopped message");
+
+            var session = GetSessionFromMessage(message);
+
+            if (session != null && session.User != null)
+            {
+                var vals = message.Data.Split('|');
+
+                var item = _dtoService.GetItemByDtoId(vals[0]);
+
+                long? positionTicks = null;
+
+                if (vals.Length > 1)
+                {
+                    long pos;
+
+                    if (long.TryParse(vals[1], out pos))
+                    {
+                        positionTicks = pos;
+                    }
+                }
+
+                var info = new PlaybackStopInfo
+                {
+                    Item = item,
+                    PositionTicks = positionTicks,
+                    SessionId = session.Id
+                };
+
+                _sessionManager.OnPlaybackStopped(info);
+            }
+        }
     }
 }

+ 71 - 14
MediaBrowser.Server.Implementations/Session/WebSocketController.cs

@@ -1,8 +1,12 @@
 using MediaBrowser.Common.Net;
+using MediaBrowser.Controller;
 using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Session;
+using MediaBrowser.Model.System;
 using System;
+using System.Collections.Generic;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
@@ -11,15 +15,39 @@ namespace MediaBrowser.Server.Implementations.Session
 {
     public class WebSocketController : ISessionController
     {
-        public bool Supports(SessionInfo session)
+        public SessionInfo Session { get; private set; }
+        public List<IWebSocketConnection> Sockets { get; private set; }
+
+        private readonly IServerApplicationHost _appHost;
+
+        public WebSocketController(SessionInfo session, IServerApplicationHost appHost)
         {
-            return session.WebSockets.Any(i => i.State == WebSocketState.Open);
+            Session = session;
+            _appHost = appHost;
+            Sockets = new List<IWebSocketConnection>();
         }
 
-        private IWebSocketConnection GetSocket(SessionInfo session)
+        public bool SupportsMediaRemoteControl
         {
-            var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
+            get
+            {
+                return Sockets.Any(i => i.State == WebSocketState.Open);
+            }
+        }
 
+        public bool IsSessionActive
+        {
+            get
+            {
+                return Sockets.Any(i => i.State == WebSocketState.Open);
+            }
+        }
+
+        private IWebSocketConnection GetActiveSocket()
+        {
+            var socket = Sockets
+                .OrderByDescending(i => i.LastActivityDate)
+                .FirstOrDefault(i => i.State == WebSocketState.Open);
 
             if (socket == null)
             {
@@ -29,9 +57,9 @@ namespace MediaBrowser.Server.Implementations.Session
             return socket;
         }
 
-        public Task SendSystemCommand(SessionInfo session, SystemCommand command, CancellationToken cancellationToken)
+        public Task SendSystemCommand(SystemCommand command, CancellationToken cancellationToken)
         {
-            var socket = GetSocket(session);
+            var socket = GetActiveSocket();
 
             return socket.SendAsync(new WebSocketMessage<string>
             {
@@ -41,9 +69,9 @@ namespace MediaBrowser.Server.Implementations.Session
             }, cancellationToken);
         }
 
-        public Task SendMessageCommand(SessionInfo session, MessageCommand command, CancellationToken cancellationToken)
+        public Task SendMessageCommand(MessageCommand command, CancellationToken cancellationToken)
         {
-            var socket = GetSocket(session);
+            var socket = GetActiveSocket();
 
             return socket.SendAsync(new WebSocketMessage<MessageCommand>
             {
@@ -53,9 +81,9 @@ namespace MediaBrowser.Server.Implementations.Session
             }, cancellationToken);
         }
 
-        public Task SendPlayCommand(SessionInfo session, PlayRequest command, CancellationToken cancellationToken)
+        public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
         {
-            var socket = GetSocket(session);
+            var socket = GetActiveSocket();
 
             return socket.SendAsync(new WebSocketMessage<PlayRequest>
             {
@@ -65,9 +93,9 @@ namespace MediaBrowser.Server.Implementations.Session
             }, cancellationToken);
         }
 
-        public Task SendBrowseCommand(SessionInfo session, BrowseRequest command, CancellationToken cancellationToken)
+        public Task SendBrowseCommand(BrowseRequest command, CancellationToken cancellationToken)
         {
-            var socket = GetSocket(session);
+            var socket = GetActiveSocket();
 
             return socket.SendAsync(new WebSocketMessage<BrowseRequest>
             {
@@ -77,9 +105,9 @@ namespace MediaBrowser.Server.Implementations.Session
             }, cancellationToken);
         }
 
-        public Task SendPlaystateCommand(SessionInfo session, PlaystateRequest command, CancellationToken cancellationToken)
+        public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
         {
-            var socket = GetSocket(session);
+            var socket = GetActiveSocket();
 
             return socket.SendAsync(new WebSocketMessage<PlaystateRequest>
             {
@@ -88,5 +116,34 @@ namespace MediaBrowser.Server.Implementations.Session
 
             }, cancellationToken);
         }
+
+        public Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken)
+        {
+            var socket = GetActiveSocket();
+            
+            return socket.SendAsync(new WebSocketMessage<LibraryUpdateInfo>
+            {
+                MessageType = "Playstate",
+                Data = info
+
+            }, cancellationToken);
+        }
+
+        /// <summary>
+        /// Sends the restart required message.
+        /// </summary>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        public Task SendRestartRequiredMessage(CancellationToken cancellationToken)
+        {
+            var socket = GetActiveSocket();
+
+            return socket.SendAsync(new WebSocketMessage<SystemInfo>
+            {
+                MessageType = "RestartRequired",
+                Data = _appHost.GetSystemInfo()
+
+            }, cancellationToken);
+        }
     }
 }

+ 0 - 2
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -427,8 +427,6 @@ namespace MediaBrowser.ServerApplication
 
             ProviderManager.AddParts(GetExports<BaseMetadataProvider>());
 
-            SessionManager.AddParts(GetExports<ISessionController>());
-
             ImageProcessor.AddParts(GetExports<IImageEnhancer>());
 
             LiveTvManager.AddParts(GetExports<ILiveTvService>());

+ 1 - 1
MediaBrowser.ServerApplication/Native/BrowserLauncher.cs

@@ -46,7 +46,7 @@ namespace MediaBrowser.ServerApplication.Native
         /// <param name="logger">The logger.</param>
         public static void OpenCommunity(ILogger logger)
         {
-            OpenUrl("http://community.mediabrowser.tv/", logger);
+            OpenUrl("http://mediabrowser3.com/community", logger);
         }
 
         /// <summary>

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.215</version>
+        <version>3.0.216</version>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.215" />
+            <dependency id="MediaBrowser.Common" version="3.0.216" />
             <dependency id="NLog" version="2.0.1.2" />
             <dependency id="ServiceStack.Text" version="3.9.58" />
             <dependency id="SimpleInjector" version="2.3.2" />

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.215</version>
+        <version>3.0.216</version>
         <title>MediaBrowser.Common</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.215</version>
+        <version>3.0.216</version>
         <title>Media Browser.Server.Core</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.215" />
+            <dependency id="MediaBrowser.Common" version="3.0.216" />
         </dependencies>
     </metadata>
     <files>