using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; 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; namespace MediaBrowser.Server.Implementations.Session { public class WebSocketController : ISessionController { public SessionInfo Session { get; private set; } public List Sockets { get; private set; } private readonly IServerApplicationHost _appHost; private readonly ILogger _logger; public WebSocketController(SessionInfo session, IServerApplicationHost appHost, ILogger logger) { Session = session; _appHost = appHost; _logger = logger; Sockets = new List(); } public bool IsSessionActive { get { return Sockets.Any(i => i.State == WebSocketState.Open); } } private IEnumerable GetActiveSockets() { return Sockets .OrderByDescending(i => i.LastActivityDate) .Where(i => i.State == WebSocketState.Open); } private IWebSocketConnection GetActiveSocket() { var socket = GetActiveSockets() .FirstOrDefault(); if (socket == null) { throw new InvalidOperationException("The requested session does not have an open web socket."); } return socket; } public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken) { return SendMessage(new WebSocketMessage { MessageType = "Play", Data = command }, cancellationToken); } public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken) { return SendMessage(new WebSocketMessage { MessageType = "Playstate", Data = command }, cancellationToken); } public Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken) { return SendMessages(new WebSocketMessage { MessageType = "LibraryChanged", Data = info }, cancellationToken); } /// /// Sends the restart required message. /// /// The cancellation token. /// Task. public Task SendRestartRequiredNotification(CancellationToken cancellationToken) { return SendMessages(new WebSocketMessage { MessageType = "RestartRequired", Data = _appHost.GetSystemInfo() }, cancellationToken); } /// /// Sends the user data change info. /// /// The info. /// The cancellation token. /// Task. public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken) { return SendMessages(new WebSocketMessage { MessageType = "UserDataChanged", Data = info }, cancellationToken); } /// /// Sends the server shutdown notification. /// /// The cancellation token. /// Task. public Task SendServerShutdownNotification(CancellationToken cancellationToken) { return SendMessages(new WebSocketMessage { MessageType = "ServerShuttingDown", Data = string.Empty }, cancellationToken); } /// /// Sends the server restart notification. /// /// The cancellation token. /// Task. public Task SendServerRestartNotification(CancellationToken cancellationToken) { return SendMessages(new WebSocketMessage { MessageType = "ServerRestarting", Data = string.Empty }, cancellationToken); } public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken) { return SendMessage(new WebSocketMessage { MessageType = "GeneralCommand", Data = command }, cancellationToken); } public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken) { return SendMessages(new WebSocketMessage { MessageType = "SessionEnded", Data = sessionInfo }, cancellationToken); } public Task SendPlaybackStartNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken) { return SendMessages(new WebSocketMessage { MessageType = "PlaybackStart", Data = sessionInfo }, cancellationToken); } public Task SendPlaybackStoppedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken) { return SendMessages(new WebSocketMessage { MessageType = "PlaybackStopped", Data = sessionInfo }, cancellationToken); } private Task SendMessage(WebSocketMessage message, CancellationToken cancellationToken) { var socket = GetActiveSocket(); return socket.SendAsync(message, cancellationToken); } private Task SendMessages(WebSocketMessage message, CancellationToken cancellationToken) { var tasks = GetActiveSockets().Select(i => Task.Run(async () => { try { await i.SendAsync(message, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { _logger.ErrorException("Error sending web socket message", ex); } }, cancellationToken)); return Task.WhenAll(tasks); } } }