diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations')
| -rw-r--r-- | MediaBrowser.Server.Implementations/Dto/DtoService.cs | 3 | ||||
| -rw-r--r-- | MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/WebSocketEvents.cs) | 10 | ||||
| -rw-r--r-- | MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs | 140 | ||||
| -rw-r--r-- | MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj | 3 | ||||
| -rw-r--r-- | MediaBrowser.Server.Implementations/Session/SessionManager.cs | 62 | ||||
| -rw-r--r-- | MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs | 13 | ||||
| -rw-r--r-- | MediaBrowser.Server.Implementations/Session/WebSocketController.cs | 55 |
7 files changed, 272 insertions, 14 deletions
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 00808ad33..3c3e01151 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -340,7 +340,8 @@ namespace MediaBrowser.Server.Implementations.Dto PlayCount = data.PlayCount, Rating = data.Rating, Played = data.Played, - LastPlayedDate = data.LastPlayedDate + LastPlayedDate = data.LastPlayedDate, + Key = data.Key }; } private void SetBookProperties(BaseItemDto dto, Book item) diff --git a/MediaBrowser.Server.Implementations/EntryPoints/WebSocketEvents.cs b/MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs index 4349b6976..0925ca86c 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/WebSocketEvents.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs @@ -18,7 +18,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints /// <summary> /// Class WebSocketEvents /// </summary> - public class WebSocketEvents : IServerEntryPoint + public class ServerEventNotifier : IServerEntryPoint { /// <summary> /// The _server manager @@ -47,15 +47,15 @@ namespace MediaBrowser.Server.Implementations.EntryPoints private readonly IDtoService _dtoService; - private ISessionManager _sessionManager; + private readonly ISessionManager _sessionManager; /// <summary> - /// Initializes a new instance of the <see cref="WebSocketEvents" /> class. + /// Initializes a new instance of the <see cref="ServerEventNotifier" /> 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, ISessionManager sessionManager) + public ServerEventNotifier(IServerManager serverManager, IServerApplicationHost appHost, IUserManager userManager, IInstallationManager installationManager, ITaskManager taskManager, IDtoService dtoService, ISessionManager sessionManager) { _serverManager = serverManager; _userManager = userManager; @@ -131,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) { - _sessionManager.SendRestartRequiredMessage(CancellationToken.None); + _sessionManager.SendRestartRequiredNotification(CancellationToken.None); } /// <summary> diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs new file mode 100644 index 000000000..fc35e040d --- /dev/null +++ b/MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs @@ -0,0 +1,140 @@ +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Plugins; +using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Session; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.EntryPoints +{ + class UserDataChangeNotifier : IServerEntryPoint + { + private readonly ISessionManager _sessionManager; + private readonly ILogger _logger; + private readonly IDtoService _dtoService; + private readonly IUserDataManager _userDataManager; + + private readonly object _syncLock = new object(); + private Timer UpdateTimer { get; set; } + private const int UpdateDuration = 2000; + + private readonly Dictionary<Guid, List<string>> _changedKeys = new Dictionary<Guid, List<string>>(); + + public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, IDtoService dtoService, ILogger logger) + { + _userDataManager = userDataManager; + _sessionManager = sessionManager; + _dtoService = dtoService; + _logger = logger; + } + + public void Run() + { + _userDataManager.UserDataSaved += _userDataManager_UserDataSaved; + } + + void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e) + { + if (e.SaveReason == UserDataSaveReason.PlaybackProgress) + { + return; + } + + lock (_syncLock) + { + if (UpdateTimer == null) + { + UpdateTimer = new Timer(UpdateTimerCallback, null, UpdateDuration, + Timeout.Infinite); + } + else + { + UpdateTimer.Change(UpdateDuration, Timeout.Infinite); + } + + List<string> keys; + + if (!_changedKeys.TryGetValue(e.UserId, out keys)) + { + keys = new List<string>(); + _changedKeys[e.UserId] = keys; + } + + keys.Add(e.Key); + } + } + + private void UpdateTimerCallback(object state) + { + lock (_syncLock) + { + // Remove dupes in case some were saved multiple times + var changes = _changedKeys.ToList(); + _changedKeys.Clear(); + + SendNotifications(changes, CancellationToken.None); + + if (UpdateTimer != null) + { + UpdateTimer.Dispose(); + UpdateTimer = null; + } + } + } + + private async Task SendNotifications(List<KeyValuePair<Guid, List<string>>> changes, CancellationToken cancellationToken) + { + foreach (var pair in changes) + { + var userId = pair.Key; + var userSessions = _sessionManager.Sessions + .Where(u => u.User != null && u.User.Id == userId && u.SessionController != null && u.IsActive) + .ToList(); + + if (userSessions.Count > 0) + { + var dtoList = pair.Value + .Select(i => _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(userId, i))) + .ToList(); + + var info = new UserDataChangeInfo + { + UserId = userId.ToString("N"), + + UserDataList = dtoList + }; + + foreach (var userSession in userSessions) + { + try + { + await userSession.SessionController.SendUserDataChangeInfo(info, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error sending UserDataChanged message", ex); + } + } + } + + } + } + + public void Dispose() + { + if (UpdateTimer != null) + { + UpdateTimer.Dispose(); + UpdateTimer = null; + } + + _userDataManager.UserDataSaved -= _userDataManager_UserDataSaved; + } + } +} diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index b0f7553ea..86ea3b051 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -115,7 +115,8 @@ <Compile Include="EntryPoints\Notifications\WebSocketNotifier.cs" /> <Compile Include="EntryPoints\RefreshUsersMetadata.cs" /> <Compile Include="EntryPoints\UdpServerEntryPoint.cs" /> - <Compile Include="EntryPoints\WebSocketEvents.cs" /> + <Compile Include="EntryPoints\ServerEventNotifier.cs" /> + <Compile Include="EntryPoints\UserDataChangeNotifier.cs" /> <Compile Include="HttpServer\HttpResultFactory.cs" /> <Compile Include="HttpServer\HttpServer.cs" /> <Compile Include="HttpServer\NativeWebSocket.cs" /> diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index ac69b0dc5..6bb6edf7a 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -290,6 +290,7 @@ namespace MediaBrowser.Server.Implementations.Session var data = _userDataRepository.GetUserData(user.Id, key); UpdatePlayState(info.Item, data, info.PositionTicks.Value); + await _userDataRepository.SaveUserData(user.Id, key, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None).ConfigureAwait(false); } @@ -501,7 +502,62 @@ namespace MediaBrowser.Server.Implementations.Session return session.SessionController.SendPlaystateCommand(command, cancellationToken); } - public Task SendRestartRequiredMessage(CancellationToken cancellationToken) + /// <summary> + /// Sends the restart required message. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + public Task SendRestartRequiredNotification(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.SendRestartRequiredNotification(cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error in SendRestartRequiredNotification.", ex); + } + + })); + + return Task.WhenAll(tasks); + } + + /// <summary> + /// Sends the server shutdown notification. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + public Task SendServerShutdownNotification(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.SendServerShutdownNotification(cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error in SendServerShutdownNotification.", ex); + } + + })); + + return Task.WhenAll(tasks); + } + + /// <summary> + /// Sends the server restart notification. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + public Task SendServerRestartNotification(CancellationToken cancellationToken) { var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList(); @@ -509,11 +565,11 @@ namespace MediaBrowser.Server.Implementations.Session { try { - await session.SessionController.SendRestartRequiredMessage(cancellationToken).ConfigureAwait(false); + await session.SessionController.SendServerRestartNotification(cancellationToken).ConfigureAwait(false); } catch (Exception ex) { - _logger.ErrorException("Error in SendRestartRequiredMessage.", ex); + _logger.ErrorException("Error in SendServerRestartNotification.", ex); } })); diff --git a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs index e90dd8eb9..399cce945 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs @@ -84,15 +84,22 @@ namespace MediaBrowser.Server.Implementations.Session /// Processes the identity message. /// </summary> /// <param name="message">The message.</param> - private void ProcessIdentityMessage(WebSocketMessageInfo message) + private async void ProcessIdentityMessage(WebSocketMessageInfo message) { - _logger.Debug("Received Identity message"); + _logger.Debug("Received Identity message: " + message.Data); var vals = message.Data.Split('|'); var client = vals[0]; var deviceId = vals[1]; var version = vals[2]; + var deviceName = vals.Length > 3 ? vals[3] : string.Empty; + + if (!string.IsNullOrEmpty(deviceName)) + { + _logger.Debug("Logging session activity"); + await _sessionManager.LogSessionActivity(client, version, deviceId, deviceName, null).ConfigureAwait(false); + } var session = _sessionManager.Sessions .FirstOrDefault(i => string.Equals(i.DeviceId, deviceId) && @@ -156,7 +163,7 @@ namespace MediaBrowser.Server.Implementations.Session if (result == null) { - _logger.Error("Unable to session based on web socket message"); + _logger.Error("Unable to find session based on web socket message"); } return result; diff --git a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs index 46c8f752d..6bbebf156 100644 --- a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs +++ b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs @@ -134,7 +134,7 @@ namespace MediaBrowser.Server.Implementations.Session /// </summary> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - public Task SendRestartRequiredMessage(CancellationToken cancellationToken) + public Task SendRestartRequiredNotification(CancellationToken cancellationToken) { var socket = GetActiveSocket(); @@ -145,5 +145,58 @@ namespace MediaBrowser.Server.Implementations.Session }, cancellationToken); } + + + /// <summary> + /// Sends the user data change info. + /// </summary> + /// <param name="info">The info.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken) + { + var socket = GetActiveSocket(); + + return socket.SendAsync(new WebSocketMessage<UserDataChangeInfo> + { + MessageType = "UserDataChanged", + Data = info + + }, cancellationToken); + } + + /// <summary> + /// Sends the server shutdown notification. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + public Task SendServerShutdownNotification(CancellationToken cancellationToken) + { + var socket = GetActiveSocket(); + + return socket.SendAsync(new WebSocketMessage<string> + { + MessageType = "ServerShuttingDown", + Data = string.Empty + + }, cancellationToken); + } + + /// <summary> + /// Sends the server restart notification. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + public Task SendServerRestartNotification(CancellationToken cancellationToken) + { + var socket = GetActiveSocket(); + + return socket.SendAsync(new WebSocketMessage<string> + { + MessageType = "ServerRestarting", + Data = string.Empty + + }, cancellationToken); + } } } |
