diff options
Diffstat (limited to 'Emby.Server.Implementations/Session/SessionManager.cs')
| -rw-r--r-- | Emby.Server.Implementations/Session/SessionManager.cs | 918 |
1 files changed, 465 insertions, 453 deletions
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 6b70f2cda..9db4f4423 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -30,13 +30,14 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Threading; using MediaBrowser.Model.Extensions; +using MediaBrowser.Controller.Authentication; namespace Emby.Server.Implementations.Session { /// <summary> /// Class SessionManager /// </summary> - public class SessionManager : ISessionManager + public class SessionManager : ISessionManager, IDisposable { /// <summary> /// The _user data repository @@ -71,7 +72,7 @@ namespace Emby.Server.Implementations.Session public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed; - public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationSucceeded; + public event EventHandler<GenericEventArgs<AuthenticationResult>> AuthenticationSucceeded; /// <summary> /// Occurs when [playback start]. @@ -91,10 +92,6 @@ namespace Emby.Server.Implementations.Session public event EventHandler<SessionEventArgs> SessionEnded; public event EventHandler<SessionEventArgs> SessionActivity; - private IEnumerable<ISessionControllerFactory> _sessionFactories = new List<ISessionControllerFactory>(); - - private readonly SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1); - public SessionManager(IUserDataManager userDataManager, ILogger logger, ILibraryManager libraryManager, IUserManager userManager, IMusicManager musicManager, IDtoService dtoService, IImageProcessor imageProcessor, IJsonSerializer jsonSerializer, IServerApplicationHost appHost, IHttpClient httpClient, IAuthenticationRepository authRepo, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, ITimerFactory timerFactory) { _userDataManager = userDataManager; @@ -111,28 +108,41 @@ namespace Emby.Server.Implementations.Session _deviceManager = deviceManager; _mediaSourceManager = mediaSourceManager; _timerFactory = timerFactory; - _deviceManager.DeviceOptionsUpdated += _deviceManager_DeviceOptionsUpdated; } - void _deviceManager_DeviceOptionsUpdated(object sender, GenericEventArgs<DeviceInfo> e) + private void _deviceManager_DeviceOptionsUpdated(object sender, GenericEventArgs<Tuple<string, DeviceOptions>> e) { foreach (var session in Sessions) { - if (string.Equals(session.DeviceId, e.Argument.Id)) + if (string.Equals(session.DeviceId, e.Argument.Item1)) { - session.DeviceName = e.Argument.Name; + if (!string.IsNullOrWhiteSpace(e.Argument.Item2.CustomName)) + { + session.HasCustomDeviceName = true; + session.DeviceName = e.Argument.Item2.CustomName; + } + else + { + session.HasCustomDeviceName = false; + } } } } - /// <summary> - /// Adds the parts. - /// </summary> - /// <param name="sessionFactories">The session factories.</param> - public void AddParts(IEnumerable<ISessionControllerFactory> sessionFactories) + private bool _disposed; + public void Dispose() + { + _disposed = true; + _deviceManager.DeviceOptionsUpdated -= _deviceManager_DeviceOptionsUpdated; + } + + public void CheckDisposed() { - _sessionFactories = sessionFactories.ToList(); + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } } /// <summary> @@ -146,58 +156,44 @@ namespace Emby.Server.Implementations.Session private void OnSessionStarted(SessionInfo info) { - EventHelper.QueueEventIfNotNull(SessionStarted, this, new SessionEventArgs - { - SessionInfo = info - - }, _logger); - - if (!string.IsNullOrWhiteSpace(info.DeviceId)) + if (!string.IsNullOrEmpty(info.DeviceId)) { var capabilities = GetSavedCapabilities(info.DeviceId); if (capabilities != null) { - info.AppIconUrl = capabilities.IconUrl; ReportCapabilities(info, capabilities, false); } } - } - private async void OnSessionEnded(SessionInfo info) - { - try - { - await SendSessionEndedNotification(info, CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) + EventHelper.QueueEventIfNotNull(SessionStarted, this, new SessionEventArgs { - _logger.ErrorException("Error in SendSessionEndedNotification", ex); - } + SessionInfo = info + }, _logger); + } + + private void OnSessionEnded(SessionInfo info) + { EventHelper.QueueEventIfNotNull(SessionEnded, this, new SessionEventArgs { SessionInfo = info }, _logger); - var disposable = info.SessionController as IDisposable; + info.Dispose(); + } - if (disposable != null) - { - _logger.Debug("Disposing session controller {0}", disposable.GetType().Name); + public void UpdateDeviceName(string sessionId, string deviceName) + { + var session = GetSession(sessionId); - try - { - disposable.Dispose(); - } - catch (Exception ex) - { - _logger.ErrorException("Error disposing session controller", ex); - } - } + var key = GetSessionKey(session.Client, session.DeviceId); - info.Dispose(); + if (session != null) + { + session.DeviceName = deviceName; + } } /// <summary> @@ -212,13 +208,15 @@ namespace Emby.Server.Implementations.Session /// <returns>Task.</returns> /// <exception cref="System.ArgumentNullException">user</exception> /// <exception cref="System.UnauthorizedAccessException"></exception> - public async Task<SessionInfo> LogSessionActivity(string appName, + public SessionInfo LogSessionActivity(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user) { + CheckDisposed(); + if (string.IsNullOrEmpty(appName)) { throw new ArgumentNullException("appName"); @@ -231,13 +229,9 @@ namespace Emby.Server.Implementations.Session { throw new ArgumentNullException("deviceId"); } - if (string.IsNullOrEmpty(deviceName)) - { - throw new ArgumentNullException("deviceName"); - } var activityDate = DateTime.UtcNow; - var session = await GetSessionInfo(appName, appVersion, deviceId, deviceName, remoteEndPoint, user).ConfigureAwait(false); + var session = GetSessionInfo(appName, appVersion, deviceId, deviceName, remoteEndPoint, user); var lastActivityDate = session.LastActivityDate; session.LastActivityDate = activityDate; @@ -268,40 +262,39 @@ namespace Emby.Server.Implementations.Session }, _logger); } - var controller = session.SessionController; - if (controller != null) - { - controller.OnActivity(); - } - return session; } - public async void ReportSessionEnded(string sessionId) + public void CloseIfNeeded(SessionInfo session) { - await _sessionLock.WaitAsync(CancellationToken.None).ConfigureAwait(false); - - try + if (!session.SessionControllers.Any(i => i.IsSessionActive)) { - var session = GetSession(sessionId, false); + var key = GetSessionKey(session.Client, session.DeviceId); - if (session != null) - { - var key = GetSessionKey(session.Client, session.DeviceId); - - SessionInfo removed; - _activeConnections.TryRemove(key, out removed); + SessionInfo removed; + _activeConnections.TryRemove(key, out removed); - OnSessionEnded(session); - } + OnSessionEnded(session); } - finally + } + + public void ReportSessionEnded(string sessionId) + { + CheckDisposed(); + var session = GetSession(sessionId, false); + + if (session != null) { - _sessionLock.Release(); + var key = GetSessionKey(session.Client, session.DeviceId); + + SessionInfo removed; + _activeConnections.TryRemove(key, out removed); + + OnSessionEnded(session); } } - private Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, string liveStreamId) + private Task<MediaSourceInfo> GetMediaSource(BaseItem item, string mediaSourceId, string liveStreamId) { return _mediaSourceManager.GetMediaSource(item, mediaSourceId, liveStreamId, false, CancellationToken.None); } @@ -311,16 +304,16 @@ namespace Emby.Server.Implementations.Session /// </summary> private async Task UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem, bool updateLastCheckInTime) { - if (string.IsNullOrWhiteSpace(info.MediaSourceId)) + if (string.IsNullOrEmpty(info.MediaSourceId)) { - info.MediaSourceId = info.ItemId; + info.MediaSourceId = info.ItemId.ToString("N"); } - if (!string.IsNullOrWhiteSpace(info.ItemId) && info.Item == null && libraryItem != null) + if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null) { var current = session.NowPlayingItem; - if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase)) + if (current == null || !info.ItemId.Equals(current.Id)) { var runtimeTicks = libraryItem.RunTimeTicks; @@ -328,7 +321,7 @@ namespace Emby.Server.Implementations.Session var hasMediaSources = libraryItem as IHasMediaSources; if (hasMediaSources != null) { - mediaSource = await GetMediaSource(hasMediaSources, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); + mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); if (mediaSource != null) { @@ -364,6 +357,14 @@ namespace Emby.Server.Implementations.Session session.PlayState.SubtitleStreamIndex = info.SubtitleStreamIndex; session.PlayState.PlayMethod = info.PlayMethod; session.PlayState.RepeatMode = info.RepeatMode; + session.PlaylistItemId = info.PlaylistItemId; + + var nowPlayingQueue = info.NowPlayingQueue; + + if (nowPlayingQueue != null) + { + session.NowPlayingQueue = nowPlayingQueue; + } } /// <summary> @@ -397,99 +398,89 @@ namespace Emby.Server.Implementations.Session /// <param name="remoteEndPoint">The remote end point.</param> /// <param name="user">The user.</param> /// <returns>SessionInfo.</returns> - private async Task<SessionInfo> GetSessionInfo(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user) + private SessionInfo GetSessionInfo(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user) { - if (string.IsNullOrWhiteSpace(deviceId)) + CheckDisposed(); + + if (string.IsNullOrEmpty(deviceId)) { throw new ArgumentNullException("deviceId"); } var key = GetSessionKey(appName, deviceId); - await _sessionLock.WaitAsync(CancellationToken.None).ConfigureAwait(false); + CheckDisposed(); - var userId = user == null ? (Guid?)null : user.Id; - var username = user == null ? null : user.Name; - - try + SessionInfo sessionInfo = _activeConnections.GetOrAdd(key, k => { - SessionInfo sessionInfo; - DeviceInfo device = null; - - if (!_activeConnections.TryGetValue(key, out sessionInfo)) - { - sessionInfo = new SessionInfo(this, _logger) - { - Client = appName, - DeviceId = deviceId, - ApplicationVersion = appVersion, - Id = key.GetMD5().ToString("N") - }; - - sessionInfo.DeviceName = deviceName; - sessionInfo.UserId = userId; - sessionInfo.UserName = username; - sessionInfo.RemoteEndPoint = remoteEndPoint; + return CreateSession(k, appName, appVersion, deviceId, deviceName, remoteEndPoint, user); + }); - OnSessionStarted(sessionInfo); + sessionInfo.UserId = user == null ? Guid.Empty : user.Id; + sessionInfo.UserName = user == null ? null : user.Name; + sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary); + sessionInfo.RemoteEndPoint = remoteEndPoint; + sessionInfo.Client = appName; - _activeConnections.TryAdd(key, sessionInfo); + if (!sessionInfo.HasCustomDeviceName || string.IsNullOrEmpty(sessionInfo.DeviceName)) + { + sessionInfo.DeviceName = deviceName; + } - if (!string.IsNullOrEmpty(deviceId)) - { - var userIdString = userId.HasValue ? userId.Value.ToString("N") : null; - device = _deviceManager.RegisterDevice(deviceId, deviceName, appName, appVersion, userIdString); - } - } + sessionInfo.ApplicationVersion = appVersion; - device = device ?? _deviceManager.GetDevice(deviceId); + if (user == null) + { + sessionInfo.AdditionalUsers = new SessionUserInfo[] { }; + } - if (device == null) - { - var userIdString = userId.HasValue ? userId.Value.ToString("N") : null; - device = _deviceManager.RegisterDevice(deviceId, deviceName, appName, appVersion, userIdString); - } + return sessionInfo; + } - if (device != null) - { - if (!string.IsNullOrEmpty(device.CustomName)) - { - deviceName = device.CustomName; - } - } + private SessionInfo CreateSession(string key, string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user) + { + var sessionInfo = new SessionInfo(this, _logger) + { + Client = appName, + DeviceId = deviceId, + ApplicationVersion = appVersion, + Id = key.GetMD5().ToString("N"), + ServerId = _appHost.SystemId + }; - sessionInfo.DeviceName = deviceName; - sessionInfo.UserId = userId; - sessionInfo.UserName = username; - sessionInfo.RemoteEndPoint = remoteEndPoint; - sessionInfo.ApplicationVersion = appVersion; + var username = user == null ? null : user.Name; - if (!userId.HasValue) - { - sessionInfo.AdditionalUsers = new SessionUserInfo[] { }; - } + sessionInfo.UserId = user == null ? Guid.Empty : user.Id; + sessionInfo.UserName = username; + sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary); + sessionInfo.RemoteEndPoint = remoteEndPoint; - if (sessionInfo.SessionController == null) - { - sessionInfo.SessionController = _sessionFactories - .Select(i => i.GetSessionController(sessionInfo)) - .FirstOrDefault(i => i != null); - } + if (string.IsNullOrEmpty(deviceName)) + { + deviceName = "Network Device"; + } - return sessionInfo; + var deviceOptions = _deviceManager.GetDeviceOptions(deviceId); + if (string.IsNullOrEmpty(deviceOptions.CustomName)) + { + sessionInfo.DeviceName = deviceName; } - finally + else { - _sessionLock.Release(); + sessionInfo.DeviceName = deviceOptions.CustomName; + sessionInfo.HasCustomDeviceName = true; } + + OnSessionStarted(sessionInfo); + return sessionInfo; } private List<User> GetUsers(SessionInfo session) { var users = new List<User>(); - if (session.UserId.HasValue) + if (!session.UserId.Equals(Guid.Empty)) { - var user = _userManager.GetUserById(session.UserId.Value); + var user = _userManager.GetUserById(session.UserId); if (user == null) { @@ -498,11 +489,9 @@ namespace Emby.Server.Implementations.Session users.Add(user); - var additionalUsers = session.AdditionalUsers + users.AddRange(session.AdditionalUsers .Select(i => _userManager.GetUserById(i.UserId)) - .Where(i => i != null); - - users.AddRange(additionalUsers); + .Where(i => i != null)); } return users; @@ -546,7 +535,7 @@ namespace Emby.Server.Implementations.Session await OnPlaybackStopped(new PlaybackStopInfo { Item = session.NowPlayingItem, - ItemId = session.NowPlayingItem == null ? null : session.NowPlayingItem.Id, + ItemId = session.NowPlayingItem == null ? Guid.Empty : session.NowPlayingItem.Id, SessionId = session.Id, MediaSourceId = session.PlayState == null ? null : session.PlayState.MediaSourceId, PositionTicks = session.PlayState == null ? null : session.PlayState.PositionTicks @@ -568,12 +557,10 @@ namespace Emby.Server.Implementations.Session } } - private BaseItem GetNowPlayingItem(SessionInfo session, string itemId) + private BaseItem GetNowPlayingItem(SessionInfo session, Guid itemId) { - var idGuid = new Guid(itemId); - var item = session.FullNowPlayingItem; - if (item != null && item.Id == idGuid) + if (item != null && item.Id.Equals(itemId)) { return item; } @@ -593,6 +580,8 @@ namespace Emby.Server.Implementations.Session /// <exception cref="System.ArgumentNullException">info</exception> public async Task OnPlaybackStart(PlaybackStartInfo info) { + CheckDisposed(); + if (info == null) { throw new ArgumentNullException("info"); @@ -600,7 +589,7 @@ namespace Emby.Server.Implementations.Session var session = GetSession(info.SessionId); - var libraryItem = string.IsNullOrWhiteSpace(info.ItemId) + var libraryItem = info.ItemId.Equals(Guid.Empty) ? null : GetNowPlayingItem(session, info.ItemId); @@ -619,7 +608,7 @@ namespace Emby.Server.Implementations.Session { foreach (var user in users) { - OnPlaybackStart(user.Id, libraryItem); + OnPlaybackStart(user, libraryItem); } } @@ -633,12 +622,11 @@ namespace Emby.Server.Implementations.Session MediaInfo = info.Item, DeviceName = session.DeviceName, ClientName = session.Client, - DeviceId = session.DeviceId + DeviceId = session.DeviceId, + Session = session }, _logger); - await SendPlaybackStartNotification(session, CancellationToken.None).ConfigureAwait(false); - StartIdleCheckTimer(); } @@ -647,9 +635,9 @@ namespace Emby.Server.Implementations.Session /// </summary> /// <param name="userId">The user identifier.</param> /// <param name="item">The item.</param> - private void OnPlaybackStart(Guid userId, IHasUserData item) + private void OnPlaybackStart(User user, BaseItem item) { - var data = _userDataManager.GetUserData(userId, item); + var data = _userDataManager.GetUserData(user, item); data.PlayCount++; data.LastPlayedDate = DateTime.UtcNow; @@ -666,7 +654,7 @@ namespace Emby.Server.Implementations.Session data.Played = false; } - _userDataManager.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None); + _userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None); } public Task OnPlaybackProgress(PlaybackProgressInfo info) @@ -679,6 +667,8 @@ namespace Emby.Server.Implementations.Session /// </summary> public async Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated) { + CheckDisposed(); + if (info == null) { throw new ArgumentNullException("info"); @@ -686,7 +676,7 @@ namespace Emby.Server.Implementations.Session var session = GetSession(info.SessionId); - var libraryItem = string.IsNullOrWhiteSpace(info.ItemId) + var libraryItem = info.ItemId.Equals(Guid.Empty) ? null : GetNowPlayingItem(session, info.ItemId); @@ -694,7 +684,8 @@ namespace Emby.Server.Implementations.Session var users = GetUsers(session); - if (libraryItem != null) + // only update saved user data on actual check-ins, not automated ones + if (libraryItem != null && !isAutomated) { foreach (var user in users) { @@ -714,7 +705,8 @@ namespace Emby.Server.Implementations.Session DeviceId = session.DeviceId, IsPaused = info.IsPaused, PlaySessionId = info.PlaySessionId, - IsAutomated = isAutomated + IsAutomated = isAutomated, + Session = session }, _logger); @@ -728,39 +720,70 @@ namespace Emby.Server.Implementations.Session private void OnPlaybackProgress(User user, BaseItem item, PlaybackProgressInfo info) { - var data = _userDataManager.GetUserData(user.Id, item); + var data = _userDataManager.GetUserData(user, item); var positionTicks = info.PositionTicks; + var changed = false; + if (positionTicks.HasValue) { _userDataManager.UpdatePlayState(item, data, positionTicks.Value); + changed = true; + } - UpdatePlaybackSettings(user, info, data); + var tracksChanged = UpdatePlaybackSettings(user, info, data); + if (!tracksChanged) + { + changed = true; + } - _userDataManager.SaveUserData(user.Id, item, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None); + if (changed) + { + _userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None); } + } - private void UpdatePlaybackSettings(User user, PlaybackProgressInfo info, UserItemData data) + private bool UpdatePlaybackSettings(User user, PlaybackProgressInfo info, UserItemData data) { + var changed = false; + if (user.Configuration.RememberAudioSelections) { - data.AudioStreamIndex = info.AudioStreamIndex; + if (data.AudioStreamIndex != info.AudioStreamIndex) + { + data.AudioStreamIndex = info.AudioStreamIndex; + changed = true; + } } else { - data.AudioStreamIndex = null; + if (data.AudioStreamIndex.HasValue) + { + data.AudioStreamIndex = null; + changed = true; + } } if (user.Configuration.RememberSubtitleSelections) { - data.SubtitleStreamIndex = info.SubtitleStreamIndex; + if (data.SubtitleStreamIndex != info.SubtitleStreamIndex) + { + data.SubtitleStreamIndex = info.SubtitleStreamIndex; + changed = true; + } } else { - data.SubtitleStreamIndex = null; + if (data.SubtitleStreamIndex.HasValue) + { + data.SubtitleStreamIndex = null; + changed = true; + } } + + return changed; } /// <summary> @@ -772,6 +795,8 @@ namespace Emby.Server.Implementations.Session /// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception> public async Task OnPlaybackStopped(PlaybackStopInfo info) { + CheckDisposed(); + if (info == null) { throw new ArgumentNullException("info"); @@ -786,28 +811,28 @@ namespace Emby.Server.Implementations.Session session.StopAutomaticProgress(); - var libraryItem = string.IsNullOrWhiteSpace(info.ItemId) + var libraryItem = info.ItemId.Equals(Guid.Empty) ? null : GetNowPlayingItem(session, info.ItemId); // Normalize - if (string.IsNullOrWhiteSpace(info.MediaSourceId)) + if (string.IsNullOrEmpty(info.MediaSourceId)) { - info.MediaSourceId = info.ItemId; + info.MediaSourceId = info.ItemId.ToString("N"); } - if (!string.IsNullOrWhiteSpace(info.ItemId) && info.Item == null && libraryItem != null) + if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null) { var current = session.NowPlayingItem; - if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase)) + if (current == null || !info.ItemId.Equals(current.Id)) { MediaSourceInfo mediaSource = null; var hasMediaSources = libraryItem as IHasMediaSources; if (hasMediaSources != null) { - mediaSource = await GetMediaSource(hasMediaSources, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); + mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); } info.Item = GetItemInfo(libraryItem, mediaSource); @@ -829,6 +854,13 @@ namespace Emby.Server.Implementations.Session msString); } + if (info.NowPlayingQueue != null) + { + session.NowPlayingQueue = info.NowPlayingQueue; + } + + session.PlaylistItemId = info.PlaylistItemId; + RemoveNowPlayingItem(session); var users = GetUsers(session); @@ -838,11 +870,11 @@ namespace Emby.Server.Implementations.Session { foreach (var user in users) { - playedToCompletion = OnPlaybackStopped(user.Id, libraryItem, info.PositionTicks, info.Failed); + playedToCompletion = OnPlaybackStopped(user, libraryItem, info.PositionTicks, info.Failed); } } - if (!string.IsNullOrWhiteSpace(info.LiveStreamId)) + if (!string.IsNullOrEmpty(info.LiveStreamId)) { try { @@ -864,20 +896,19 @@ namespace Emby.Server.Implementations.Session MediaInfo = info.Item, DeviceName = session.DeviceName, ClientName = session.Client, - DeviceId = session.DeviceId + DeviceId = session.DeviceId, + Session = session }, _logger); - - await SendPlaybackStoppedNotification(session, CancellationToken.None).ConfigureAwait(false); } - private bool OnPlaybackStopped(Guid userId, BaseItem item, long? positionTicks, bool playbackFailed) + private bool OnPlaybackStopped(User user, BaseItem item, long? positionTicks, bool playbackFailed) { bool playedToCompletion = false; if (!playbackFailed) { - var data = _userDataManager.GetUserData(userId, item); + var data = _userDataManager.GetUserData(user, item); if (positionTicks.HasValue) { @@ -892,7 +923,7 @@ namespace Emby.Server.Implementations.Session playedToCompletion = true; } - _userDataManager.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackFinished, CancellationToken.None); + _userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackFinished, CancellationToken.None); } return playedToCompletion; @@ -932,6 +963,8 @@ namespace Emby.Server.Implementations.Session public Task SendMessageCommand(string controllingSessionId, string sessionId, MessageCommand command, CancellationToken cancellationToken) { + CheckDisposed(); + var generalCommand = new GeneralCommand { Name = GeneralCommandType.DisplayMessage.ToString() @@ -950,22 +983,37 @@ namespace Emby.Server.Implementations.Session public Task SendGeneralCommand(string controllingSessionId, string sessionId, GeneralCommand command, CancellationToken cancellationToken) { + CheckDisposed(); + var session = GetSessionToRemoteControl(sessionId); - if (!string.IsNullOrWhiteSpace(controllingSessionId)) + if (!string.IsNullOrEmpty(controllingSessionId)) { var controllingSession = GetSession(controllingSessionId); AssertCanControl(session, controllingSession); } - return session.SessionController.SendGeneralCommand(command, cancellationToken); + return SendMessageToSession(session, "GeneralCommand", command, cancellationToken); + } + + private async Task SendMessageToSession<T>(SessionInfo session, string name, T data, CancellationToken cancellationToken) + { + var controllers = session.SessionControllers.ToArray(); + var messageId = Guid.NewGuid().ToString("N"); + + foreach (var controller in controllers) + { + await controller.SendMessage(name, messageId, data, controllers, cancellationToken).ConfigureAwait(false); + } } public async Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken) { + CheckDisposed(); + var session = GetSessionToRemoteControl(sessionId); - var user = session.UserId.HasValue ? _userManager.GetUserById(session.UserId.Value) : null; + var user = !session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(session.UserId) : null; List<BaseItem> items; @@ -994,7 +1042,7 @@ namespace Emby.Server.Implementations.Session command.PlayCommand = PlayCommand.PlayNow; } - command.ItemIds = items.Select(i => i.Id.ToString("N")).ToArray(items.Count); + command.ItemIds = items.Select(i => i.Id).ToArray(items.Count); if (user != null) { @@ -1004,11 +1052,6 @@ namespace Emby.Server.Implementations.Session } } - if (items.Any(i => !session.PlayableMediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase))) - { - throw new ArgumentException(string.Format("{0} is unable to play the requested media type.", session.DeviceName ?? session.Id)); - } - if (user != null && command.ItemIds.Length == 1 && user.Configuration.EnableNextEpisodeAutoPlay) { var episode = _libraryManager.GetItemById(command.ItemIds[0]) as Episode; @@ -1027,26 +1070,26 @@ namespace Emby.Server.Implementations.Session if (episodes.Count > 0) { - command.ItemIds = episodes.Select(i => i.Id.ToString("N")).ToArray(episodes.Count); + command.ItemIds = episodes.Select(i => i.Id).ToArray(episodes.Count); } } } } - if (!string.IsNullOrWhiteSpace(controllingSessionId)) + if (!string.IsNullOrEmpty(controllingSessionId)) { var controllingSession = GetSession(controllingSessionId); AssertCanControl(session, controllingSession); - if (controllingSession.UserId.HasValue) + if (!controllingSession.UserId.Equals(Guid.Empty)) { - command.ControllingUserId = controllingSession.UserId.Value.ToString("N"); + command.ControllingUserId = controllingSession.UserId; } } - await session.SessionController.SendPlayCommand(command, cancellationToken).ConfigureAwait(false); + await SendMessageToSession(session, "Play", command, cancellationToken).ConfigureAwait(false); } - private List<BaseItem> TranslateItemForPlayback(string id, User user) + private IList<BaseItem> TranslateItemForPlayback(Guid id, User user) { var item = _libraryManager.GetItemById(id); @@ -1060,7 +1103,7 @@ namespace Emby.Server.Implementations.Session if (byName != null) { - var items = byName.GetTaggedItems(new InternalItemsQuery(user) + return byName.GetTaggedItems(new InternalItemsQuery(user) { IsFolder = false, Recursive = true, @@ -1072,19 +1115,16 @@ namespace Emby.Server.Implementations.Session ItemFields.SortName } }, - IsVirtualItem = false + IsVirtualItem = false, + OrderBy = new ValueTuple<string, SortOrder>[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) } }); - - return FilterToSingleMediaType(items) - .OrderBy(i => i.SortName) - .ToList(); } if (item.IsFolder) { var folder = (Folder)item; - var itemsResult = folder.GetItemList(new InternalItemsQuery(user) + return folder.GetItemList(new InternalItemsQuery(user) { Recursive = true, IsFolder = false, @@ -1096,28 +1136,16 @@ namespace Emby.Server.Implementations.Session ItemFields.SortName } }, - IsVirtualItem = false + IsVirtualItem = false, + OrderBy = new ValueTuple<string, SortOrder>[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) } }); - - return FilterToSingleMediaType(itemsResult) - .OrderBy(i => i.SortName) - .ToList(); } return new List<BaseItem> { item }; } - private IEnumerable<BaseItem> FilterToSingleMediaType(IEnumerable<BaseItem> items) - { - return items - .Where(i => !string.IsNullOrWhiteSpace(i.MediaType)) - .ToLookup(i => i.MediaType, StringComparer.OrdinalIgnoreCase) - .OrderByDescending(i => i.Count()) - .FirstOrDefault(); - } - - private IEnumerable<BaseItem> TranslateItemForInstantMix(string id, User user) + private IEnumerable<BaseItem> TranslateItemForInstantMix(Guid id, User user) { var item = _libraryManager.GetItemById(id); @@ -1146,19 +1174,21 @@ namespace Emby.Server.Implementations.Session public Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken) { + CheckDisposed(); + var session = GetSessionToRemoteControl(sessionId); - if (!string.IsNullOrWhiteSpace(controllingSessionId)) + if (!string.IsNullOrEmpty(controllingSessionId)) { var controllingSession = GetSession(controllingSessionId); AssertCanControl(session, controllingSession); - if (controllingSession.UserId.HasValue) + if (!controllingSession.UserId.Equals(Guid.Empty)) { - command.ControllingUserId = controllingSession.UserId.Value.ToString("N"); + command.ControllingUserId = controllingSession.UserId.ToString("N"); } } - return session.SessionController.SendPlaystateCommand(command, cancellationToken); + return SendMessageToSession(session, "Playstate", command, cancellationToken); } private void AssertCanControl(SessionInfo session, SessionInfo controllingSession) @@ -1180,20 +1210,22 @@ namespace Emby.Server.Implementations.Session /// <returns>Task.</returns> public async Task SendRestartRequiredNotification(CancellationToken cancellationToken) { - var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList(); + CheckDisposed(); + + var sessions = Sessions.ToList(); var tasks = sessions.Select(session => Task.Run(async () => { try { - await session.SessionController.SendRestartRequiredNotification(cancellationToken).ConfigureAwait(false); + await SendMessageToSession(session, "RestartRequired", string.Empty, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { _logger.ErrorException("Error in SendRestartRequiredNotification.", ex); } - }, cancellationToken)); + }, cancellationToken)).ToArray(); await Task.WhenAll(tasks).ConfigureAwait(false); } @@ -1205,20 +1237,22 @@ namespace Emby.Server.Implementations.Session /// <returns>Task.</returns> public Task SendServerShutdownNotification(CancellationToken cancellationToken) { - var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList(); + CheckDisposed(); + + var sessions = Sessions.ToList(); var tasks = sessions.Select(session => Task.Run(async () => { try { - await session.SessionController.SendServerShutdownNotification(cancellationToken).ConfigureAwait(false); + await SendMessageToSession(session, "ServerShuttingDown", string.Empty, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { _logger.ErrorException("Error in SendServerShutdownNotification.", ex); } - }, cancellationToken)); + }, cancellationToken)).ToArray(); return Task.WhenAll(tasks); } @@ -1230,85 +1264,24 @@ namespace Emby.Server.Implementations.Session /// <returns>Task.</returns> public Task SendServerRestartNotification(CancellationToken cancellationToken) { + CheckDisposed(); + _logger.Debug("Beginning SendServerRestartNotification"); - var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList(); + var sessions = Sessions.ToList(); var tasks = sessions.Select(session => Task.Run(async () => { try { - await session.SessionController.SendServerRestartNotification(cancellationToken).ConfigureAwait(false); + await SendMessageToSession(session, "ServerRestarting", string.Empty, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { _logger.ErrorException("Error in SendServerRestartNotification.", ex); } - }, cancellationToken)); - - return Task.WhenAll(tasks); - } - - public Task SendSessionEndedNotification(SessionInfo sessionInfo, CancellationToken cancellationToken) - { - var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList(); - var dto = GetSessionInfoDto(sessionInfo); - - var tasks = sessions.Select(session => Task.Run(async () => - { - try - { - await session.SessionController.SendSessionEndedNotification(dto, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error in SendSessionEndedNotification.", ex); - } - - }, cancellationToken)); - - return Task.WhenAll(tasks); - } - - public Task SendPlaybackStartNotification(SessionInfo sessionInfo, CancellationToken cancellationToken) - { - var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList(); - var dto = GetSessionInfoDto(sessionInfo); - - var tasks = sessions.Select(session => Task.Run(async () => - { - try - { - await session.SessionController.SendPlaybackStartNotification(dto, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error in SendPlaybackStartNotification.", ex); - } - - }, cancellationToken)); - - return Task.WhenAll(tasks); - } - - public Task SendPlaybackStoppedNotification(SessionInfo sessionInfo, CancellationToken cancellationToken) - { - var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList(); - var dto = GetSessionInfoDto(sessionInfo); - - var tasks = sessions.Select(session => Task.Run(async () => - { - try - { - await session.SessionController.SendPlaybackStoppedNotification(dto, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error in SendPlaybackStoppedNotification.", ex); - } - - }, cancellationToken)); + }, cancellationToken)).ToArray(); return Task.WhenAll(tasks); } @@ -1320,16 +1293,18 @@ namespace Emby.Server.Implementations.Session /// <param name="userId">The user identifier.</param> /// <exception cref="System.UnauthorizedAccessException">Cannot modify additional users without authenticating first.</exception> /// <exception cref="System.ArgumentException">The requested user is already the primary user of the session.</exception> - public void AddAdditionalUser(string sessionId, string userId) + public void AddAdditionalUser(string sessionId, Guid userId) { + CheckDisposed(); + var session = GetSession(sessionId); - if (session.UserId.HasValue && session.UserId.Value == new Guid(userId)) + if (session.UserId.Equals(userId)) { throw new ArgumentException("The requested user is already the primary user of the session."); } - if (session.AdditionalUsers.All(i => new Guid(i.UserId) != new Guid(userId))) + if (session.AdditionalUsers.All(i => !i.UserId.Equals(userId))) { var user = _userManager.GetUserById(userId); @@ -1352,16 +1327,18 @@ namespace Emby.Server.Implementations.Session /// <param name="userId">The user identifier.</param> /// <exception cref="System.UnauthorizedAccessException">Cannot modify additional users without authenticating first.</exception> /// <exception cref="System.ArgumentException">The requested user is already the primary user of the session.</exception> - public void RemoveAdditionalUser(string sessionId, string userId) + public void RemoveAdditionalUser(string sessionId, Guid userId) { + CheckDisposed(); + var session = GetSession(sessionId); - if (session.UserId.HasValue && session.UserId.Value == new Guid(userId)) + if (session.UserId.Equals(userId)) { throw new ArgumentException("The requested user is already the primary user of the session."); } - var user = session.AdditionalUsers.FirstOrDefault(i => new Guid(i.UserId) == new Guid(userId)); + var user = session.AdditionalUsers.FirstOrDefault(i => i.UserId.Equals(userId)); if (user != null) { @@ -1389,12 +1366,13 @@ namespace Emby.Server.Implementations.Session private async Task<AuthenticationResult> AuthenticateNewSessionInternal(AuthenticationRequest request, bool enforcePassword) { + CheckDisposed(); + User user = null; - if (!string.IsNullOrWhiteSpace(request.UserId)) + if (!request.UserId.Equals(Guid.Empty)) { - var idGuid = new Guid(request.UserId); user = _userManager.Users - .FirstOrDefault(i => i.Id == idGuid); + .FirstOrDefault(i => i.Id == request.UserId); } if (user == null) @@ -1405,14 +1383,10 @@ namespace Emby.Server.Implementations.Session if (user != null) { - if (!user.IsParentalScheduleAllowed()) - { - throw new SecurityException("User is not allowed access at this time."); - } - - if (!string.IsNullOrWhiteSpace(request.DeviceId)) + // TODO: Move this to userManager? + if (!string.IsNullOrEmpty(request.DeviceId)) { - if (!_deviceManager.CanAccessDevice(user.Id.ToString("N"), request.DeviceId)) + if (!_deviceManager.CanAccessDevice(user, request.DeviceId)) { throw new SecurityException("User is not allowed access from this device."); } @@ -1421,7 +1395,7 @@ namespace Emby.Server.Implementations.Session if (enforcePassword) { - var result = await _userManager.AuthenticateUser(request.Username, request.Password, request.PasswordSha1, request.PasswordMd5, request.RemoteEndPoint, true).ConfigureAwait(false); + var result = await _userManager.AuthenticateUser(request.Username, request.Password, request.PasswordSha1, request.RemoteEndPoint, true).ConfigureAwait(false); if (result == null) { @@ -1433,72 +1407,95 @@ namespace Emby.Server.Implementations.Session user = result; } - var token = GetAuthorizationToken(user.Id.ToString("N"), request.DeviceId, request.App, request.AppVersion, request.DeviceName); + var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName); - EventHelper.FireEventIfNotNull(AuthenticationSucceeded, this, new GenericEventArgs<AuthenticationRequest>(request), _logger); - - var session = await LogSessionActivity(request.App, + var session = LogSessionActivity(request.App, request.AppVersion, request.DeviceId, request.DeviceName, request.RemoteEndPoint, - user) - .ConfigureAwait(false); + user); - return new AuthenticationResult + var returnResult = new AuthenticationResult { User = _userManager.GetUserDto(user, request.RemoteEndPoint), - SessionInfo = GetSessionInfoDto(session), + SessionInfo = session, AccessToken = token, ServerId = _appHost.SystemId }; - } + EventHelper.FireEventIfNotNull(AuthenticationSucceeded, this, new GenericEventArgs<AuthenticationResult>(returnResult), _logger); + + return returnResult; + } - private string GetAuthorizationToken(string userId, string deviceId, string app, string appVersion, string deviceName) + private string GetAuthorizationToken(User user, string deviceId, string app, string appVersion, string deviceName) { var existing = _authRepo.Get(new AuthenticationInfoQuery { DeviceId = deviceId, - IsActive = true, - UserId = userId, + UserId = user.Id, Limit = 1 - }); - if (existing.Items.Length > 0) + }).Items.FirstOrDefault(); + + var allExistingForDevice = _authRepo.Get(new AuthenticationInfoQuery + { + DeviceId = deviceId + + }).Items; + + foreach (var auth in allExistingForDevice) + { + if (existing == null || !string.Equals(auth.AccessToken, existing.AccessToken, StringComparison.Ordinal)) + { + try + { + Logout(auth); + } + catch + { + + } + } + } + + if (existing != null) { - var token = existing.Items[0].AccessToken; - _logger.Info("Reissuing access token: " + token); - return token; + _logger.Info("Reissuing access token: " + existing.AccessToken); + return existing.AccessToken; } + var now = DateTime.UtcNow; + var newToken = new AuthenticationInfo { AppName = app, AppVersion = appVersion, - DateCreated = DateTime.UtcNow, + DateCreated = now, + DateLastActivity = now, DeviceId = deviceId, DeviceName = deviceName, - UserId = userId, - IsActive = true, - AccessToken = Guid.NewGuid().ToString("N") + UserId = user.Id, + AccessToken = Guid.NewGuid().ToString("N"), + UserName = user.Name }; - _logger.Info("Creating new access token for user {0}", userId); - _authRepo.Create(newToken, CancellationToken.None); + _logger.Info("Creating new access token for user {0}", user.Id); + _authRepo.Create(newToken); return newToken.AccessToken; } public void Logout(string accessToken) { - if (string.IsNullOrWhiteSpace(accessToken)) + CheckDisposed(); + + if (string.IsNullOrEmpty(accessToken)) { throw new ArgumentNullException("accessToken"); } - _logger.Info("Logging out access token {0}", accessToken); - var existing = _authRepo.Get(new AuthenticationInfoQuery { Limit = 1, @@ -1508,33 +1505,41 @@ namespace Emby.Server.Implementations.Session if (existing != null) { - existing.IsActive = false; + Logout(existing); + } + } - _authRepo.Update(existing, CancellationToken.None); + public void Logout(AuthenticationInfo existing) + { + CheckDisposed(); - var sessions = Sessions - .Where(i => string.Equals(i.DeviceId, existing.DeviceId, StringComparison.OrdinalIgnoreCase)) - .ToList(); + _logger.Info("Logging out access token {0}", existing.AccessToken); + + _authRepo.Delete(existing); - foreach (var session in sessions) + var sessions = Sessions + .Where(i => string.Equals(i.DeviceId, existing.DeviceId, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + foreach (var session in sessions) + { + try { - try - { - ReportSessionEnded(session.Id); - } - catch (Exception ex) - { - _logger.ErrorException("Error reporting session ended", ex); - } + ReportSessionEnded(session.Id); + } + catch (Exception ex) + { + _logger.ErrorException("Error reporting session ended", ex); } } } - public void RevokeUserTokens(string userId, string currentAccessToken) + public void RevokeUserTokens(Guid userId, string currentAccessToken) { + CheckDisposed(); + var existing = _authRepo.Get(new AuthenticationInfoQuery { - IsActive = true, UserId = userId }); @@ -1542,7 +1547,7 @@ namespace Emby.Server.Implementations.Session { if (!string.Equals(currentAccessToken, info.AccessToken, StringComparison.OrdinalIgnoreCase)) { - Logout(info.AccessToken); + Logout(info); } } } @@ -1559,6 +1564,8 @@ namespace Emby.Server.Implementations.Session /// <param name="capabilities">The capabilities.</param> public void ReportCapabilities(string sessionId, ClientCapabilities capabilities) { + CheckDisposed(); + var session = GetSession(sessionId); ReportCapabilities(session, capabilities, true); @@ -1570,24 +1577,22 @@ namespace Emby.Server.Implementations.Session { session.Capabilities = capabilities; - if (!string.IsNullOrWhiteSpace(capabilities.MessageCallbackUrl)) + if (!string.IsNullOrEmpty(capabilities.PushToken)) { - var controller = session.SessionController as HttpSessionController; - - if (controller == null) + if (string.Equals(capabilities.PushTokenType, "firebase", StringComparison.OrdinalIgnoreCase) && FirebaseSessionController.IsSupported(_appHost)) { - session.SessionController = new HttpSessionController(_httpClient, _jsonSerializer, session, capabilities.MessageCallbackUrl, this); + EnsureFirebaseController(session, capabilities.PushToken); } } - EventHelper.FireEventIfNotNull(CapabilitiesChanged, this, new SessionEventArgs + if (saveCapabilities) { - SessionInfo = session + EventHelper.FireEventIfNotNull(CapabilitiesChanged, this, new SessionEventArgs + { + SessionInfo = session - }, _logger); + }, _logger); - if (saveCapabilities) - { try { SaveCapabilities(session.DeviceId, capabilities); @@ -1599,6 +1604,11 @@ namespace Emby.Server.Implementations.Session } } + private void EnsureFirebaseController(SessionInfo session, string token) + { + session.EnsureController<FirebaseSessionController>(s => new FirebaseSessionController(_httpClient, _appHost, _jsonSerializer, s, token, this)); + } + private ClientCapabilities GetSavedCapabilities(string deviceId) { return _deviceManager.GetCapabilities(deviceId); @@ -1609,45 +1619,6 @@ namespace Emby.Server.Implementations.Session _deviceManager.SaveCapabilities(deviceId, capabilities); } - public SessionInfoDto GetSessionInfoDto(SessionInfo session) - { - var dto = new SessionInfoDto - { - Client = session.Client, - DeviceId = session.DeviceId, - DeviceName = session.DeviceName, - Id = session.Id, - LastActivityDate = session.LastActivityDate, - NowViewingItem = session.NowViewingItem, - ApplicationVersion = session.ApplicationVersion, - PlayableMediaTypes = session.PlayableMediaTypes, - AdditionalUsers = session.AdditionalUsers, - SupportedCommands = session.SupportedCommands, - UserName = session.UserName, - NowPlayingItem = session.NowPlayingItem, - SupportsRemoteControl = session.SupportsMediaControl, - PlayState = session.PlayState, - AppIconUrl = session.AppIconUrl, - TranscodingInfo = session.NowPlayingItem == null ? null : session.TranscodingInfo - }; - - dto.ServerId = _appHost.SystemId; - - if (session.UserId.HasValue) - { - dto.UserId = session.UserId.Value.ToString("N"); - - var user = _userManager.GetUserById(session.UserId.Value); - - if (user != null) - { - dto.UserPrimaryImageTag = GetImageCacheTag(user, ImageType.Primary); - } - } - - return dto; - } - private DtoOptions _itemInfoDtoOptions; /// <summary> @@ -1672,7 +1643,6 @@ namespace Emby.Server.Implementations.Session var fields = dtoOptions.Fields.ToList(); fields.Remove(ItemFields.BasicSyncInfo); - fields.Remove(ItemFields.SyncInfo); fields.Remove(ItemFields.CanDelete); fields.Remove(ItemFields.CanDownload); fields.Remove(ItemFields.ChildCount); @@ -1682,7 +1652,6 @@ namespace Emby.Server.Implementations.Session fields.Remove(ItemFields.DateLastSaved); fields.Remove(ItemFields.DisplayPreferencesId); fields.Remove(ItemFields.Etag); - fields.Remove(ItemFields.ExternalEtag); fields.Remove(ItemFields.InheritedParentalRatingValue); fields.Remove(ItemFields.ItemCounts); fields.Remove(ItemFields.MediaSourceCount); @@ -1698,8 +1667,7 @@ namespace Emby.Server.Implementations.Session fields.Remove(ItemFields.Settings); fields.Remove(ItemFields.SortName); fields.Remove(ItemFields.Tags); - fields.Remove(ItemFields.ThemeSongIds); - fields.Remove(ItemFields.ThemeVideoIds); + fields.Remove(ItemFields.ExtraIds); dtoOptions.Fields = fields.ToArray(fields.Count); @@ -1729,14 +1697,9 @@ namespace Emby.Server.Implementations.Session } } - private string GetDtoId(BaseItem item) - { - return _dtoService.GetDtoId(item); - } - public void ReportNowViewingItem(string sessionId, string itemId) { - if (string.IsNullOrWhiteSpace(itemId)) + if (string.IsNullOrEmpty(itemId)) { throw new ArgumentNullException("itemId"); } @@ -1776,46 +1739,31 @@ namespace Emby.Server.Implementations.Session string.Equals(i.Client, client)); } - public Task<SessionInfo> GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion) + public SessionInfo GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion) { if (info == null) { throw new ArgumentNullException("info"); } - var user = string.IsNullOrWhiteSpace(info.UserId) + var user = info.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(info.UserId); - appVersion = string.IsNullOrWhiteSpace(appVersion) + appVersion = string.IsNullOrEmpty(appVersion) ? info.AppVersion : appVersion; var deviceName = info.DeviceName; var appName = info.AppName; - if (!string.IsNullOrWhiteSpace(deviceId)) - { - // Replace the info from the token with more recent info - var device = _deviceManager.GetDevice(deviceId); - if (device != null) - { - deviceName = device.Name; - appName = device.AppName; - - if (!string.IsNullOrWhiteSpace(device.AppVersion)) - { - appVersion = device.AppVersion; - } - } - } - else + if (string.IsNullOrEmpty(deviceId)) { deviceId = info.DeviceId; } // Prevent argument exception - if (string.IsNullOrWhiteSpace(appVersion)) + if (string.IsNullOrEmpty(appVersion)) { appVersion = "1"; } @@ -1823,7 +1771,7 @@ namespace Emby.Server.Implementations.Session return LogSessionActivity(appName, appVersion, deviceId, deviceName, remoteEndpoint, user); } - public Task<SessionInfo> GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint) + public SessionInfo GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint) { var result = _authRepo.Get(new AuthenticationInfoQuery { @@ -1834,7 +1782,7 @@ namespace Emby.Server.Implementations.Session if (info == null) { - return Task.FromResult<SessionInfo>(null); + return null; } return GetSessionByAuthenticationToken(info, deviceId, remoteEndpoint, null); @@ -1842,51 +1790,115 @@ namespace Emby.Server.Implementations.Session public Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken) { - var adminUserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id.ToString("N")).ToList(); + CheckDisposed(); + + var adminUserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id).ToList(); return SendMessageToUserSessions(adminUserIds, name, data, cancellationToken); } - public Task SendMessageToUserSessions<T>(List<string> userIds, string name, T data, - CancellationToken cancellationToken) + public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, Func<T> dataFn, CancellationToken cancellationToken) + { + CheckDisposed(); + + var sessions = Sessions.Where(i => userIds.Any(i.ContainsUser)).ToList(); + + if (sessions.Count == 0) + { + return Task.CompletedTask; + } + + var data = dataFn(); + + var tasks = sessions.Select(session => Task.Run(async () => + { + try + { + await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error sending message", ex); + } + + }, cancellationToken)).ToArray(); + + return Task.WhenAll(tasks); + } + + public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, T data, CancellationToken cancellationToken) + { + CheckDisposed(); + + var sessions = Sessions.Where(i => userIds.Any(i.ContainsUser)).ToList(); + + var tasks = sessions.Select(session => Task.Run(async () => + { + try + { + await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error sending message", ex); + } + + }, cancellationToken)).ToArray(); + + return Task.WhenAll(tasks); + } + + public Task SendMessageToUserDeviceSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken) { - var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null && userIds.Any(i.ContainsUser)).ToList(); + CheckDisposed(); + + var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)).ToList(); var tasks = sessions.Select(session => Task.Run(async () => { try { - await session.SessionController.SendMessage(name, data, cancellationToken).ConfigureAwait(false); + await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { _logger.ErrorException("Error sending message", ex); } - }, cancellationToken)); + }, cancellationToken)).ToArray(); return Task.WhenAll(tasks); } - public Task SendMessageToUserDeviceSessions<T>(string deviceId, string name, T data, - CancellationToken cancellationToken) + public Task SendMessageToUserDeviceAndAdminSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken) { - var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null && string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)).ToList(); + CheckDisposed(); + + var sessions = Sessions + .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i)) + .ToList(); var tasks = sessions.Select(session => Task.Run(async () => { try { - await session.SessionController.SendMessage(name, data, cancellationToken).ConfigureAwait(false); + await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { _logger.ErrorException("Error sending message", ex); } - }, cancellationToken)); + }, cancellationToken)).ToArray(); return Task.WhenAll(tasks); } + + private bool IsAdminSession(SessionInfo s) + { + var user = _userManager.GetUserById(s.UserId); + + return user != null && user.Policy.IsAdministrator; + } } }
\ No newline at end of file |
