diff options
18 files changed, 377 insertions, 222 deletions
diff --git a/MediaBrowser.Common/Net/IServerManager.cs b/MediaBrowser.Common/Net/IServerManager.cs index 32be88a34..6965677bb 100644 --- a/MediaBrowser.Common/Net/IServerManager.cs +++ b/MediaBrowser.Common/Net/IServerManager.cs @@ -63,17 +63,6 @@ namespace MediaBrowser.Common.Net 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> /// <param name="listeners">The listeners.</param> diff --git a/MediaBrowser.Common/Net/IWebSocketConnection.cs b/MediaBrowser.Common/Net/IWebSocketConnection.cs index 434b8ff5c..482da131e 100644 --- a/MediaBrowser.Common/Net/IWebSocketConnection.cs +++ b/MediaBrowser.Common/Net/IWebSocketConnection.cs @@ -8,6 +8,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> /// <value>The last activity date.</value> diff --git a/MediaBrowser.Controller/Session/ISessionController.cs b/MediaBrowser.Controller/Session/ISessionController.cs index f37d63b72..597a14cc6 100644 --- a/MediaBrowser.Controller/Session/ISessionController.cs +++ b/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); } } diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index 6ee57eb46..b2d111e54 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -14,12 +14,6 @@ namespace MediaBrowser.Controller.Session 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> event EventHandler<PlaybackProgressEventArgs> PlaybackStart; @@ -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); } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs index d50f19c1f..ed2fcda67 100644 --- a/MediaBrowser.Controller/Session/SessionInfo.cs +++ b/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>(); } @@ -115,18 +111,18 @@ namespace MediaBrowser.Controller.Session 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> /// <value><c>true</c> if this instance is active; otherwise, <c>false</c>.</value> @@ -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; } } } diff --git a/MediaBrowser.Model/Querying/ItemQuery.cs b/MediaBrowser.Model/Querying/ItemQuery.cs index 69c79104a..20eac55e8 100644 --- a/MediaBrowser.Model/Querying/ItemQuery.cs +++ b/MediaBrowser.Model/Querying/ItemQuery.cs @@ -207,6 +207,24 @@ namespace MediaBrowser.Model.Querying 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> public ItemQuery() diff --git a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 1306017ca..7a3081ab5 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/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); + } + } } + } } diff --git a/MediaBrowser.Server.Implementations/EntryPoints/WebSocketEvents.cs b/MediaBrowser.Server.Implementations/EntryPoints/WebSocketEvents.cs index 29372a40a..4349b6976 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/WebSocketEvents.cs +++ b/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> diff --git a/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs b/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs index a840ae214..638e6fa0d 100644 --- a/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs +++ b/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)) { diff --git a/MediaBrowser.Server.Implementations/ServerManager/WebSocketConnection.cs b/MediaBrowser.Server.Implementations/ServerManager/WebSocketConnection.cs index 4bab10ee0..61c06ba6d 100644 --- a/MediaBrowser.Server.Implementations/ServerManager/WebSocketConnection.cs +++ b/MediaBrowser.Server.Implementations/ServerManager/WebSocketConnection.cs @@ -57,6 +57,12 @@ namespace MediaBrowser.Server.Implementations.ServerManager 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> /// <param name="socket">The socket.</param> @@ -83,6 +89,7 @@ namespace MediaBrowser.Server.Implementations.ServerManager throw new ArgumentNullException("logger"); } + Id = Guid.NewGuid(); _jsonSerializer = jsonSerializer; _socket = socket; _socket.OnReceiveBytes = OnReceiveInternal; diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index efb8dbe10..ac69b0dc5 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/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); } diff --git a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs index 0781e8228..e90dd8eb9 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/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); + } + } } } diff --git a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs index fb0bc9b7c..46c8f752d 100644 --- a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs +++ b/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); + } } } diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index c9d079fc7..a10b7c15b 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/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>()); diff --git a/MediaBrowser.ServerApplication/Native/BrowserLauncher.cs b/MediaBrowser.ServerApplication/Native/BrowserLauncher.cs index 8b0beab48..43a698434 100644 --- a/MediaBrowser.ServerApplication/Native/BrowserLauncher.cs +++ b/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> diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index f9ccca41e..18187392a 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/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" /> diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 2045689ae..a9cc361a9 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/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> diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 4d306b695..56e9222d4 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/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> |
