aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Session/SessionManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/Session/SessionManager.cs')
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs948
1 files changed, 468 insertions, 480 deletions
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index fa0ab62d3..341d2c8e6 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -1,3 +1,7 @@
+#nullable disable
+
+#pragma warning disable CS1591
+
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -5,125 +9,139 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Entities.Security;
+using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
+using Jellyfin.Data.Queries;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Session;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Devices;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Session;
+using MediaBrowser.Model.SyncPlay;
+using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
namespace Emby.Server.Implementations.Session
{
/// <summary>
- /// Class SessionManager
+ /// Class SessionManager.
/// </summary>
public class SessionManager : ISessionManager, IDisposable
{
- /// <summary>
- /// The _user data repository
- /// </summary>
private readonly IUserDataManager _userDataManager;
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
+ private readonly ILogger<SessionManager> _logger;
+ private readonly IEventManager _eventManager;
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
private readonly IMusicManager _musicManager;
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
private readonly IMediaSourceManager _mediaSourceManager;
-
- private readonly IHttpClient _httpClient;
- private readonly IJsonSerializer _jsonSerializer;
private readonly IServerApplicationHost _appHost;
-
- private readonly IAuthenticationRepository _authRepo;
private readonly IDeviceManager _deviceManager;
/// <summary>
- /// The _active connections
+ /// The active connections.
/// </summary>
- private readonly ConcurrentDictionary<string, SessionInfo> _activeConnections =
- new ConcurrentDictionary<string, SessionInfo>(StringComparer.OrdinalIgnoreCase);
+ private readonly ConcurrentDictionary<string, SessionInfo> _activeConnections = new (StringComparer.OrdinalIgnoreCase);
- public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed;
-
- public event EventHandler<GenericEventArgs<AuthenticationResult>> AuthenticationSucceeded;
-
- /// <summary>
- /// Occurs when [playback start].
- /// </summary>
- public event EventHandler<PlaybackProgressEventArgs> PlaybackStart;
- /// <summary>
- /// Occurs when [playback progress].
- /// </summary>
- public event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
- /// <summary>
- /// Occurs when [playback stopped].
- /// </summary>
- public event EventHandler<PlaybackStopEventArgs> PlaybackStopped;
+ private Timer _idleTimer;
- public event EventHandler<SessionEventArgs> SessionStarted;
- public event EventHandler<SessionEventArgs> CapabilitiesChanged;
- public event EventHandler<SessionEventArgs> SessionEnded;
- public event EventHandler<SessionEventArgs> SessionActivity;
+ private DtoOptions _itemInfoDtoOptions;
+ private bool _disposed = false;
public SessionManager(
+ ILogger<SessionManager> logger,
+ IEventManager eventManager,
IUserDataManager userDataManager,
- ILoggerFactory loggerFactory,
ILibraryManager libraryManager,
IUserManager userManager,
IMusicManager musicManager,
IDtoService dtoService,
IImageProcessor imageProcessor,
- IJsonSerializer jsonSerializer,
IServerApplicationHost appHost,
- IHttpClient httpClient,
- IAuthenticationRepository authRepo,
IDeviceManager deviceManager,
IMediaSourceManager mediaSourceManager)
{
+ _logger = logger;
+ _eventManager = eventManager;
_userDataManager = userDataManager;
- _logger = loggerFactory.CreateLogger(nameof(SessionManager));
_libraryManager = libraryManager;
_userManager = userManager;
_musicManager = musicManager;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
- _jsonSerializer = jsonSerializer;
_appHost = appHost;
- _httpClient = httpClient;
- _authRepo = authRepo;
_deviceManager = deviceManager;
_mediaSourceManager = mediaSourceManager;
- _deviceManager.DeviceOptionsUpdated += _deviceManager_DeviceOptionsUpdated;
+
+ _deviceManager.DeviceOptionsUpdated += OnDeviceManagerDeviceOptionsUpdated;
}
- private void _deviceManager_DeviceOptionsUpdated(object sender, GenericEventArgs<Tuple<string, DeviceOptions>> e)
+ /// <inheritdoc />
+ public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed;
+
+ /// <inheritdoc />
+ public event EventHandler<GenericEventArgs<AuthenticationResult>> AuthenticationSucceeded;
+
+ /// <summary>
+ /// Occurs when playback has started.
+ /// </summary>
+ public event EventHandler<PlaybackProgressEventArgs> PlaybackStart;
+
+ /// <summary>
+ /// Occurs when playback has progressed.
+ /// </summary>
+ public event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
+
+ /// <summary>
+ /// Occurs when playback has stopped.
+ /// </summary>
+ public event EventHandler<PlaybackStopEventArgs> PlaybackStopped;
+
+ /// <inheritdoc />
+ public event EventHandler<SessionEventArgs> SessionStarted;
+
+ /// <inheritdoc />
+ public event EventHandler<SessionEventArgs> CapabilitiesChanged;
+
+ /// <inheritdoc />
+ public event EventHandler<SessionEventArgs> SessionEnded;
+
+ /// <inheritdoc />
+ public event EventHandler<SessionEventArgs> SessionActivity;
+
+ /// <inheritdoc />
+ public event EventHandler<SessionEventArgs> SessionControllerConnected;
+
+ /// <summary>
+ /// Gets all connections.
+ /// </summary>
+ /// <value>All connections.</value>
+ public IEnumerable<SessionInfo> Sessions => _activeConnections.Values.OrderByDescending(c => c.LastActivityDate);
+
+ private void OnDeviceManagerDeviceOptionsUpdated(object sender, GenericEventArgs<Tuple<string, DeviceOptions>> e)
{
foreach (var session in Sessions)
{
- if (string.Equals(session.DeviceId, e.Argument.Item1))
+ if (string.Equals(session.DeviceId, e.Argument.Item1, StringComparison.Ordinal))
{
if (!string.IsNullOrWhiteSpace(e.Argument.Item2.CustomName))
{
@@ -138,14 +156,37 @@ namespace Emby.Server.Implementations.Session
}
}
- private bool _disposed;
+ /// <inheritdoc />
public void Dispose()
{
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Releases unmanaged and optionally managed resources.
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ _idleTimer?.Dispose();
+ }
+
+ _idleTimer = null;
+
+ _deviceManager.DeviceOptionsUpdated -= OnDeviceManagerDeviceOptionsUpdated;
+
_disposed = true;
- _deviceManager.DeviceOptionsUpdated -= _deviceManager_DeviceOptionsUpdated;
}
- public void CheckDisposed()
+ private void CheckDisposed()
{
if (_disposed)
{
@@ -153,17 +194,11 @@ namespace Emby.Server.Implementations.Session
}
}
- /// <summary>
- /// Gets all connections.
- /// </summary>
- /// <value>All connections.</value>
- public IEnumerable<SessionInfo> Sessions => _activeConnections.Values.OrderByDescending(c => c.LastActivityDate).ToList();
-
private void OnSessionStarted(SessionInfo info)
{
if (!string.IsNullOrEmpty(info.DeviceId))
{
- var capabilities = GetSavedCapabilities(info.DeviceId);
+ var capabilities = _deviceManager.GetCapabilities(info.DeviceId);
if (capabilities != null)
{
@@ -171,33 +206,41 @@ namespace Emby.Server.Implementations.Session
}
}
- EventHelper.QueueEventIfNotNull(SessionStarted, this, new SessionEventArgs
- {
- SessionInfo = info
+ _eventManager.Publish(new SessionStartedEventArgs(info));
- }, _logger);
+ EventHelper.QueueEventIfNotNull(
+ SessionStarted,
+ this,
+ new SessionEventArgs
+ {
+ SessionInfo = info
+ },
+ _logger);
}
private void OnSessionEnded(SessionInfo info)
{
- EventHelper.QueueEventIfNotNull(SessionEnded, this, new SessionEventArgs
- {
- SessionInfo = info
+ EventHelper.QueueEventIfNotNull(
+ SessionEnded,
+ this,
+ new SessionEventArgs
+ {
+ SessionInfo = info
+ },
+ _logger);
- }, _logger);
+ _eventManager.Publish(new SessionEndedEventArgs(info));
info.Dispose();
}
- public void UpdateDeviceName(string sessionId, string deviceName)
+ /// <inheritdoc />
+ public void UpdateDeviceName(string sessionId, string reportedDeviceName)
{
var session = GetSession(sessionId);
-
- var key = GetSessionKey(session.Client, session.DeviceId);
-
if (session != null)
{
- session.DeviceName = deviceName;
+ session.DeviceName = reportedDeviceName;
}
}
@@ -210,10 +253,9 @@ namespace Emby.Server.Implementations.Session
/// <param name="deviceName">Name of the device.</param>
/// <param name="remoteEndPoint">The remote end point.</param>
/// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">user</exception>
- /// <exception cref="UnauthorizedAccessException"></exception>
- public SessionInfo LogSessionActivity(string appName,
+ /// <returns>SessionInfo.</returns>
+ public async Task<SessionInfo> LogSessionActivity(
+ string appName,
string appVersion,
string deviceId,
string deviceName,
@@ -226,61 +268,80 @@ namespace Emby.Server.Implementations.Session
{
throw new ArgumentNullException(nameof(appName));
}
+
if (string.IsNullOrEmpty(appVersion))
{
throw new ArgumentNullException(nameof(appVersion));
}
+
if (string.IsNullOrEmpty(deviceId))
{
throw new ArgumentNullException(nameof(deviceId));
}
var activityDate = DateTime.UtcNow;
- var session = GetSessionInfo(appName, appVersion, deviceId, deviceName, remoteEndPoint, user);
+ var session = await GetSessionInfo(appName, appVersion, deviceId, deviceName, remoteEndPoint, user).ConfigureAwait(false);
var lastActivityDate = session.LastActivityDate;
session.LastActivityDate = activityDate;
if (user != null)
{
var userLastActivityDate = user.LastActivityDate ?? DateTime.MinValue;
- user.LastActivityDate = activityDate;
if ((activityDate - userLastActivityDate).TotalSeconds > 60)
{
try
{
- _userManager.UpdateUser(user);
+ user.LastActivityDate = activityDate;
+ await _userManager.UpdateUserAsync(user).ConfigureAwait(false);
}
- catch (Exception ex)
+ catch (DbUpdateConcurrencyException e)
{
- _logger.LogError("Error updating user", ex);
+ _logger.LogDebug(e, "Error updating user's last activity date.");
}
}
}
if ((activityDate - lastActivityDate).TotalSeconds > 10)
{
- SessionActivity?.Invoke(this, new SessionEventArgs
- {
- SessionInfo = session
- });
+ SessionActivity?.Invoke(
+ this,
+ new SessionEventArgs
+ {
+ SessionInfo = session
+ });
}
return session;
}
+ /// <inheritdoc />
+ public void OnSessionControllerConnected(SessionInfo session)
+ {
+ EventHelper.QueueEventIfNotNull(
+ SessionControllerConnected,
+ this,
+ new SessionEventArgs
+ {
+ SessionInfo = session
+ },
+ _logger);
+ }
+
+ /// <inheritdoc />
public void CloseIfNeeded(SessionInfo session)
{
if (!session.SessionControllers.Any(i => i.IsSessionActive))
{
var key = GetSessionKey(session.Client, session.DeviceId);
- _activeConnections.TryRemove(key, out var removed);
+ _activeConnections.TryRemove(key, out _);
OnSessionEnded(session);
}
}
+ /// <inheritdoc />
public void ReportSessionEnded(string sessionId)
{
CheckDisposed();
@@ -290,7 +351,7 @@ namespace Emby.Server.Implementations.Session
{
var key = GetSessionKey(session.Client, session.DeviceId);
- _activeConnections.TryRemove(key, out var removed);
+ _activeConnections.TryRemove(key, out _);
OnSessionEnded(session);
}
@@ -304,11 +365,12 @@ namespace Emby.Server.Implementations.Session
/// <summary>
/// Updates the now playing item id.
/// </summary>
+ /// <returns>Task.</returns>
private async Task UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem, bool updateLastCheckInTime)
{
if (string.IsNullOrEmpty(info.MediaSourceId))
{
- info.MediaSourceId = info.ItemId.ToString("N");
+ info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture);
}
if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null)
@@ -320,8 +382,7 @@ namespace Emby.Server.Implementations.Session
var runtimeTicks = libraryItem.RunTimeTicks;
MediaSourceInfo mediaSource = null;
- var hasMediaSources = libraryItem as IHasMediaSources;
- if (hasMediaSources != null)
+ if (libraryItem is IHasMediaSources)
{
mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false);
@@ -373,7 +434,6 @@ namespace Emby.Server.Implementations.Session
/// Removes the now playing item id.
/// </summary>
/// <param name="session">The session.</param>
- /// <exception cref="ArgumentNullException">item</exception>
private void RemoveNowPlayingItem(SessionInfo session)
{
session.NowPlayingItem = null;
@@ -386,9 +446,7 @@ namespace Emby.Server.Implementations.Session
}
private static string GetSessionKey(string appName, string deviceId)
- {
- return appName + deviceId;
- }
+ => appName + deviceId;
/// <summary>
/// Gets the connection.
@@ -400,7 +458,13 @@ namespace Emby.Server.Implementations.Session
/// <param name="remoteEndPoint">The remote end point.</param>
/// <param name="user">The user.</param>
/// <returns>SessionInfo.</returns>
- private SessionInfo GetSessionInfo(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user)
+ private async Task<SessionInfo> GetSessionInfo(
+ string appName,
+ string appVersion,
+ string deviceId,
+ string deviceName,
+ string remoteEndPoint,
+ User user)
{
CheckDisposed();
@@ -408,18 +472,20 @@ namespace Emby.Server.Implementations.Session
{
throw new ArgumentNullException(nameof(deviceId));
}
+
var key = GetSessionKey(appName, deviceId);
CheckDisposed();
- var sessionInfo = _activeConnections.GetOrAdd(key, k =>
+ if (!_activeConnections.TryGetValue(key, out var sessionInfo))
{
- return CreateSession(k, appName, appVersion, deviceId, deviceName, remoteEndPoint, user);
- });
+ _activeConnections[key] = await CreateSession(key, appName, appVersion, deviceId, deviceName, remoteEndPoint, user).ConfigureAwait(false);
+ sessionInfo = _activeConnections[key];
+ }
- 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.UserId = user?.Id ?? Guid.Empty;
+ sessionInfo.UserName = user?.Username;
+ sessionInfo.UserPrimaryImageTag = user?.ProfileImage == null ? null : GetImageCacheTag(user);
sessionInfo.RemoteEndPoint = remoteEndPoint;
sessionInfo.Client = appName;
@@ -432,28 +498,35 @@ namespace Emby.Server.Implementations.Session
if (user == null)
{
- sessionInfo.AdditionalUsers = new SessionUserInfo[] { };
+ sessionInfo.AdditionalUsers = Array.Empty<SessionUserInfo>();
}
return sessionInfo;
}
- private SessionInfo CreateSession(string key, string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user)
+ private async Task<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"),
+ Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture),
ServerId = _appHost.SystemId
};
- var username = user == null ? null : user.Name;
+ var username = user?.Username;
- sessionInfo.UserId = user == null ? Guid.Empty : user.Id;
+ sessionInfo.UserId = user?.Id ?? Guid.Empty;
sessionInfo.UserName = username;
- sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user, ImageType.Primary);
+ sessionInfo.UserPrimaryImageTag = user?.ProfileImage == null ? null : GetImageCacheTag(user);
sessionInfo.RemoteEndPoint = remoteEndPoint;
if (string.IsNullOrEmpty(deviceName))
@@ -461,7 +534,7 @@ namespace Emby.Server.Implementations.Session
deviceName = "Network Device";
}
- var deviceOptions = _deviceManager.GetDeviceOptions(deviceId);
+ var deviceOptions = await _deviceManager.GetDeviceOptions(deviceId).ConfigureAwait(false);
if (string.IsNullOrEmpty(deviceOptions.CustomName))
{
sessionInfo.DeviceName = deviceName;
@@ -480,7 +553,7 @@ namespace Emby.Server.Implementations.Session
{
var users = new List<User>();
- if (!session.UserId.Equals(Guid.Empty))
+ if (session.UserId != Guid.Empty)
{
var user = _userManager.GetUserById(session.UserId);
@@ -499,15 +572,11 @@ namespace Emby.Server.Implementations.Session
return users;
}
- private Timer _idleTimer;
-
private void StartIdleCheckTimer()
{
- if (_idleTimer == null)
- {
- _idleTimer = new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
- }
+ _idleTimer ??= new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
}
+
private void StopIdleCheckTimer()
{
if (_idleTimer != null)
@@ -539,9 +608,9 @@ namespace Emby.Server.Implementations.Session
Item = session.NowPlayingItem,
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
- });
+ MediaSourceId = session.PlayState?.MediaSourceId,
+ PositionTicks = session.PlayState?.PositionTicks
+ }).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -575,11 +644,11 @@ namespace Emby.Server.Implementations.Session
}
/// <summary>
- /// Used to report that playback has started for an item
+ /// Used to report that playback has started for an item.
/// </summary>
/// <param name="info">The info.</param>
/// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">info</exception>
+ /// <exception cref="ArgumentNullException"><c>info</c> is <c>null</c>.</exception>
public async Task OnPlaybackStart(PlaybackStartInfo info)
{
CheckDisposed();
@@ -591,7 +660,7 @@ namespace Emby.Server.Implementations.Session
var session = GetSession(info.SessionId);
- var libraryItem = info.ItemId.Equals(Guid.Empty)
+ var libraryItem = info.ItemId == Guid.Empty
? null
: GetNowPlayingItem(session, info.ItemId);
@@ -614,9 +683,7 @@ namespace Emby.Server.Implementations.Session
}
}
- // Nothing to save here
- // Fire events to inform plugins
- EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs
+ var eventArgs = new PlaybackStartEventArgs
{
Item = libraryItem,
Users = users,
@@ -626,8 +693,17 @@ namespace Emby.Server.Implementations.Session
ClientName = session.Client,
DeviceId = session.DeviceId,
Session = session
+ };
- }, _logger);
+ await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
+
+ // Nothing to save here
+ // Fire events to inform plugins
+ EventHelper.QueueEventIfNotNull(
+ PlaybackStart,
+ this,
+ eventArgs,
+ _logger);
StartIdleCheckTimer();
}
@@ -644,12 +720,9 @@ namespace Emby.Server.Implementations.Session
data.PlayCount++;
data.LastPlayedDate = DateTime.UtcNow;
- if (item.SupportsPlayedStatus)
+ if (item.SupportsPlayedStatus && !item.SupportsPositionTicksResume)
{
- if (!(item is Video))
- {
- data.Played = true;
- }
+ data.Played = true;
}
else
{
@@ -659,14 +732,18 @@ namespace Emby.Server.Implementations.Session
_userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None);
}
+ /// <inheritdoc />
public Task OnPlaybackProgress(PlaybackProgressInfo info)
{
return OnPlaybackProgress(info, false);
}
/// <summary>
- /// Used to report playback progress for an item
+ /// Used to report playback progress for an item.
/// </summary>
+ /// <param name="info">The playback progress info.</param>
+ /// <param name="isAutomated">Whether this is an automated update.</param>
+ /// <returns>Task.</returns>
public async Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated)
{
CheckDisposed();
@@ -695,7 +772,7 @@ namespace Emby.Server.Implementations.Session
}
}
- PlaybackProgress?.Invoke(this, new PlaybackProgressEventArgs
+ var eventArgs = new PlaybackProgressEventArgs
{
Item = libraryItem,
Users = users,
@@ -709,7 +786,11 @@ namespace Emby.Server.Implementations.Session
PlaySessionId = info.PlaySessionId,
IsAutomated = isAutomated,
Session = session
- });
+ };
+
+ await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
+
+ PlaybackProgress?.Invoke(this, eventArgs);
if (!isAutomated)
{
@@ -743,14 +824,13 @@ namespace Emby.Server.Implementations.Session
{
_userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None);
}
-
}
private static bool UpdatePlaybackSettings(User user, PlaybackProgressInfo info, UserItemData data)
{
var changed = false;
- if (user.Configuration.RememberAudioSelections)
+ if (user.RememberAudioSelections)
{
if (data.AudioStreamIndex != info.AudioStreamIndex)
{
@@ -767,7 +847,7 @@ namespace Emby.Server.Implementations.Session
}
}
- if (user.Configuration.RememberSubtitleSelections)
+ if (user.RememberSubtitleSelections)
{
if (data.SubtitleStreamIndex != info.SubtitleStreamIndex)
{
@@ -788,12 +868,12 @@ namespace Emby.Server.Implementations.Session
}
/// <summary>
- /// Used to report that playback has ended for an item
+ /// Used to report that playback has ended for an item.
/// </summary>
/// <param name="info">The info.</param>
/// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">info</exception>
- /// <exception cref="ArgumentOutOfRangeException">positionTicks</exception>
+ /// <exception cref="ArgumentNullException"><c>info</c> is <c>null</c>.</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><c>info.PositionTicks</c> is <c>null</c> or negative.</exception>
public async Task OnPlaybackStopped(PlaybackStopInfo info)
{
CheckDisposed();
@@ -819,7 +899,7 @@ namespace Emby.Server.Implementations.Session
// Normalize
if (string.IsNullOrEmpty(info.MediaSourceId))
{
- info.MediaSourceId = info.ItemId.ToString("N");
+ info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture);
}
if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null)
@@ -830,8 +910,7 @@ namespace Emby.Server.Implementations.Session
{
MediaSourceInfo mediaSource = null;
- var hasMediaSources = libraryItem as IHasMediaSources;
- if (hasMediaSources != null)
+ if (libraryItem is IHasMediaSources)
{
mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false);
}
@@ -848,7 +927,8 @@ namespace Emby.Server.Implementations.Session
{
var msString = info.PositionTicks.HasValue ? (info.PositionTicks.Value / 10000).ToString(CultureInfo.InvariantCulture) : "unknown";
- _logger.LogInformation("Playback stopped reported by app {0} {1} playing {2}. Stopped at {3} ms",
+ _logger.LogInformation(
+ "Playback stopped reported by app {0} {1} playing {2}. Stopped at {3} ms",
session.Client,
session.ApplicationVersion,
info.Item.Name,
@@ -887,7 +967,7 @@ namespace Emby.Server.Implementations.Session
}
}
- EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackStopEventArgs
+ var eventArgs = new PlaybackStopEventArgs
{
Item = libraryItem,
Users = users,
@@ -899,8 +979,11 @@ namespace Emby.Server.Implementations.Session
ClientName = session.Client,
DeviceId = session.DeviceId,
Session = session
+ };
+
+ await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
- }, _logger);
+ EventHelper.QueueEventIfNotNull(PlaybackStopped, this, eventArgs, _logger);
}
private bool OnPlaybackStopped(User user, BaseItem item, long? positionTicks, bool playbackFailed)
@@ -936,14 +1019,17 @@ namespace Emby.Server.Implementations.Session
/// <param name="sessionId">The session identifier.</param>
/// <param name="throwOnMissing">if set to <c>true</c> [throw on missing].</param>
/// <returns>SessionInfo.</returns>
- /// <exception cref="ResourceNotFoundException"></exception>
+ /// <exception cref="ResourceNotFoundException">
+ /// No session with an Id equal to <c>sessionId</c> was found
+ /// and <c>throwOnMissing</c> is <c>true</c>.
+ /// </exception>
private SessionInfo GetSession(string sessionId, bool throwOnMissing = true)
{
- var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId));
-
+ var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId, StringComparison.Ordinal));
if (session == null && throwOnMissing)
{
- throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId));
+ throw new ResourceNotFoundException(
+ string.Format(CultureInfo.InvariantCulture, "Session {0} not found.", sessionId));
}
return session;
@@ -952,23 +1038,25 @@ namespace Emby.Server.Implementations.Session
private SessionInfo GetSessionToRemoteControl(string sessionId)
{
// Accept either device id or session id
- var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId));
+ var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId, StringComparison.Ordinal));
if (session == null)
{
- throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId));
+ throw new ResourceNotFoundException(
+ string.Format(CultureInfo.InvariantCulture, "Session {0} not found.", sessionId));
}
return session;
}
+ /// <inheritdoc />
public Task SendMessageCommand(string controllingSessionId, string sessionId, MessageCommand command, CancellationToken cancellationToken)
{
CheckDisposed();
var generalCommand = new GeneralCommand
{
- Name = GeneralCommandType.DisplayMessage.ToString()
+ Name = GeneralCommandType.DisplayMessage
};
generalCommand.Arguments["Header"] = command.Header;
@@ -982,6 +1070,7 @@ namespace Emby.Server.Implementations.Session
return SendGeneralCommand(controllingSessionId, sessionId, generalCommand, cancellationToken);
}
+ /// <inheritdoc />
public Task SendGeneralCommand(string controllingSessionId, string sessionId, GeneralCommand command, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -994,27 +1083,46 @@ namespace Emby.Server.Implementations.Session
AssertCanControl(session, controllingSession);
}
- return SendMessageToSession(session, "GeneralCommand", command, cancellationToken);
+ return SendMessageToSession(session, SessionMessageType.GeneralCommand, command, cancellationToken);
}
- private static async Task SendMessageToSession<T>(SessionInfo session, string name, T data, CancellationToken cancellationToken)
+ private static async Task SendMessageToSession<T>(SessionInfo session, SessionMessageType name, T data, CancellationToken cancellationToken)
{
- var controllers = session.SessionControllers.ToArray();
- var messageId = Guid.NewGuid().ToString("N");
+ var controllers = session.SessionControllers;
+ var messageId = Guid.NewGuid();
foreach (var controller in controllers)
{
- await controller.SendMessage(name, messageId, data, controllers, cancellationToken).ConfigureAwait(false);
+ await controller.SendMessage(name, messageId, data, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ private static Task SendMessageToSessions<T>(IEnumerable<SessionInfo> sessions, SessionMessageType name, T data, CancellationToken cancellationToken)
+ {
+ IEnumerable<Task> GetTasks()
+ {
+ var messageId = Guid.NewGuid();
+ foreach (var session in sessions)
+ {
+ var controllers = session.SessionControllers;
+ foreach (var controller in controllers)
+ {
+ yield return controller.SendMessage(name, messageId, data, cancellationToken);
+ }
+ }
}
+
+ return Task.WhenAll(GetTasks());
}
+ /// <inheritdoc />
public async Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken)
{
CheckDisposed();
var session = GetSessionToRemoteControl(sessionId);
- var user = !session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(session.UserId) : null;
+ var user = session.UserId == Guid.Empty ? null : _userManager.GetUserById(session.UserId);
List<BaseItem> items;
@@ -1039,7 +1147,7 @@ namespace Emby.Server.Implementations.Session
if (command.PlayCommand == PlayCommand.PlayShuffle)
{
- items = items.OrderBy(i => Guid.NewGuid()).ToList();
+ items.Shuffle();
command.PlayCommand = PlayCommand.PlayNow;
}
@@ -1049,30 +1157,32 @@ namespace Emby.Server.Implementations.Session
{
if (items.Any(i => i.GetPlayAccess(user) != PlayAccess.Full))
{
- throw new ArgumentException(string.Format("{0} is not allowed to play media.", user.Name));
+ throw new ArgumentException(
+ string.Format(CultureInfo.InvariantCulture, "{0} is not allowed to play media.", user.Username));
}
}
- if (user != null && command.ItemIds.Length == 1 && user.Configuration.EnableNextEpisodeAutoPlay)
+ if (user != null
+ && command.ItemIds.Length == 1
+ && user.EnableNextEpisodeAutoPlay
+ && _libraryManager.GetItemById(command.ItemIds[0]) is Episode episode)
{
- var episode = _libraryManager.GetItemById(command.ItemIds[0]) as Episode;
- if (episode != null)
+ var series = episode.Series;
+ if (series != null)
{
- var series = episode.Series;
- if (series != null)
+ var episodes = series.GetEpisodes(
+ user,
+ new DtoOptions(false)
+ {
+ EnableImages = false
+ })
+ .Where(i => !i.IsVirtualItem)
+ .SkipWhile(i => i.Id != episode.Id)
+ .ToList();
+
+ if (episodes.Count > 0)
{
- var episodes = series.GetEpisodes(user, new DtoOptions(false)
- {
- EnableImages = false
- })
- .Where(i => !i.IsVirtualItem)
- .SkipWhile(i => i.Id != episode.Id)
- .ToList();
-
- if (episodes.Count > 0)
- {
- command.ItemIds = episodes.Select(i => i.Id).ToArray();
- }
+ command.ItemIds = episodes.Select(i => i.Id).ToArray();
}
}
}
@@ -1087,22 +1197,36 @@ namespace Emby.Server.Implementations.Session
}
}
- await SendMessageToSession(session, "Play", command, cancellationToken).ConfigureAwait(false);
+ await SendMessageToSession(session, SessionMessageType.Play, command, cancellationToken).ConfigureAwait(false);
+ }
+
+ /// <inheritdoc />
+ public async Task SendSyncPlayCommand(string sessionId, SendCommand command, CancellationToken cancellationToken)
+ {
+ CheckDisposed();
+ var session = GetSession(sessionId);
+ await SendMessageToSession(session, SessionMessageType.SyncPlayCommand, command, cancellationToken).ConfigureAwait(false);
+ }
+
+ /// <inheritdoc />
+ public async Task SendSyncPlayGroupUpdate<T>(string sessionId, GroupUpdate<T> command, CancellationToken cancellationToken)
+ {
+ CheckDisposed();
+ var session = GetSession(sessionId);
+ await SendMessageToSession(session, SessionMessageType.SyncPlayGroupUpdate, command, cancellationToken).ConfigureAwait(false);
}
- private IList<BaseItem> TranslateItemForPlayback(Guid id, User user)
+ private IEnumerable<BaseItem> TranslateItemForPlayback(Guid id, User user)
{
var item = _libraryManager.GetItemById(id);
if (item == null)
{
_logger.LogError("A non-existant item Id {0} was passed into TranslateItemForPlayback", id);
- return new List<BaseItem>();
+ return Array.Empty<BaseItem>();
}
- var byName = item as IItemByName;
-
- if (byName != null)
+ if (item is IItemByName byName)
{
return byName.GetTaggedItems(new InternalItemsQuery(user)
{
@@ -1111,13 +1235,13 @@ namespace Emby.Server.Implementations.Session
DtoOptions = new DtoOptions(false)
{
EnableImages = false,
- Fields = new ItemFields[]
+ Fields = new[]
{
ItemFields.SortName
}
},
IsVirtualItem = false,
- OrderBy = new ValueTuple<string, SortOrder>[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) }
+ OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }
});
}
@@ -1138,12 +1262,11 @@ namespace Emby.Server.Implementations.Session
}
},
IsVirtualItem = false,
- OrderBy = new ValueTuple<string, SortOrder>[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) }
-
+ OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }
});
}
- return new List<BaseItem> { item };
+ return new[] { item };
}
private IEnumerable<BaseItem> TranslateItemForInstantMix(Guid id, User user)
@@ -1152,27 +1275,31 @@ namespace Emby.Server.Implementations.Session
if (item == null)
{
- _logger.LogError("A non-existant item Id {0} was passed into TranslateItemForInstantMix", id);
+ _logger.LogError("A non-existent item Id {0} was passed into TranslateItemForInstantMix", id);
return new List<BaseItem>();
}
return _musicManager.GetInstantMixFromItem(item, user, new DtoOptions(false) { EnableImages = false });
}
+ /// <inheritdoc />
public Task SendBrowseCommand(string controllingSessionId, string sessionId, BrowseRequest command, CancellationToken cancellationToken)
{
var generalCommand = new GeneralCommand
{
- Name = GeneralCommandType.DisplayContent.ToString()
+ Name = GeneralCommandType.DisplayContent,
+ Arguments =
+ {
+ ["ItemId"] = command.ItemId,
+ ["ItemName"] = command.ItemName,
+ ["ItemType"] = command.ItemType
+ }
};
- generalCommand.Arguments["ItemId"] = command.ItemId;
- generalCommand.Arguments["ItemName"] = command.ItemName;
- generalCommand.Arguments["ItemType"] = command.ItemType;
-
return SendGeneralCommand(controllingSessionId, sessionId, generalCommand, cancellationToken);
}
+ /// <inheritdoc />
public Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -1185,19 +1312,20 @@ namespace Emby.Server.Implementations.Session
AssertCanControl(session, controllingSession);
if (!controllingSession.UserId.Equals(Guid.Empty))
{
- command.ControllingUserId = controllingSession.UserId.ToString("N");
+ command.ControllingUserId = controllingSession.UserId.ToString("N", CultureInfo.InvariantCulture);
}
}
- return SendMessageToSession(session, "Playstate", command, cancellationToken);
+ return SendMessageToSession(session, SessionMessageType.Playstate, command, cancellationToken);
}
- private void AssertCanControl(SessionInfo session, SessionInfo controllingSession)
+ private static void AssertCanControl(SessionInfo session, SessionInfo controllingSession)
{
if (session == null)
{
throw new ArgumentNullException(nameof(session));
}
+
if (controllingSession == null)
{
throw new ArgumentNullException(nameof(controllingSession));
@@ -1209,26 +1337,11 @@ namespace Emby.Server.Implementations.Session
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- public async Task SendRestartRequiredNotification(CancellationToken cancellationToken)
+ public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
{
CheckDisposed();
- var sessions = Sessions.ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, "RestartRequired", string.Empty, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error in SendRestartRequiredNotification.", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
+ return SendMessageToSessions(Sessions, SessionMessageType.RestartRequired, string.Empty, cancellationToken);
}
/// <summary>
@@ -1240,22 +1353,7 @@ namespace Emby.Server.Implementations.Session
{
CheckDisposed();
- var sessions = Sessions.ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, "ServerShuttingDown", string.Empty, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error in SendServerShutdownNotification.", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
+ return SendMessageToSessions(Sessions, SessionMessageType.ServerShuttingDown, string.Empty, cancellationToken);
}
/// <summary>
@@ -1269,22 +1367,7 @@ namespace Emby.Server.Implementations.Session
_logger.LogDebug("Beginning SendServerRestartNotification");
- var sessions = Sessions.ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, "ServerRestarting", string.Empty, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error in SendServerRestartNotification.", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
+ return SendMessageToSessions(Sessions, SessionMessageType.ServerRestarting, string.Empty, cancellationToken);
}
/// <summary>
@@ -1300,12 +1383,12 @@ namespace Emby.Server.Implementations.Session
var session = GetSession(sessionId);
- if (session.UserId.Equals(userId))
+ if (session.UserId == userId)
{
throw new ArgumentException("The requested user is already the primary user of the session.");
}
- if (session.AdditionalUsers.All(i => !i.UserId.Equals(userId)))
+ if (session.AdditionalUsers.All(i => i.UserId != userId))
{
var user = _userManager.GetUserById(userId);
@@ -1314,7 +1397,7 @@ namespace Emby.Server.Implementations.Session
list.Add(new SessionUserInfo
{
UserId = userId,
- UserName = user.Name
+ UserName = user.Username
});
session.AdditionalUsers = list.ToArray();
@@ -1353,14 +1436,19 @@ namespace Emby.Server.Implementations.Session
/// <summary>
/// Authenticates the new session.
/// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task{SessionInfo}.</returns>
+ /// <param name="request">The authenticationrequest.</param>
+ /// <returns>The authentication result.</returns>
public Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request)
{
return AuthenticateNewSessionInternal(request, true);
}
- public Task<AuthenticationResult> CreateNewSession(AuthenticationRequest request)
+ /// <summary>
+ /// Directly authenticates the session without enforcing password.
+ /// </summary>
+ /// <param name="request">The authentication request.</param>
+ /// <returns>The authentication result.</returns>
+ public Task<AuthenticationResult> AuthenticateDirect(AuthenticationRequest request)
{
return AuthenticateNewSessionInternal(request, false);
}
@@ -1370,52 +1458,52 @@ namespace Emby.Server.Implementations.Session
CheckDisposed();
User user = null;
- if (!request.UserId.Equals(Guid.Empty))
+ if (request.UserId != Guid.Empty)
{
- user = _userManager.Users
- .FirstOrDefault(i => i.Id == request.UserId);
+ user = _userManager.GetUserById(request.UserId);
}
- if (user == null)
+ user ??= _userManager.GetUserByName(request.Username);
+
+ if (enforcePassword)
{
- user = _userManager.Users
- .FirstOrDefault(i => string.Equals(request.Username, i.Name, StringComparison.OrdinalIgnoreCase));
+ user = await _userManager.AuthenticateUser(
+ request.Username,
+ request.Password,
+ null,
+ request.RemoteEndPoint,
+ true).ConfigureAwait(false);
}
- if (user != null)
+ if (user == null)
{
- // TODO: Move this to userManager?
- if (!string.IsNullOrEmpty(request.DeviceId))
- {
- if (!_deviceManager.CanAccessDevice(user, request.DeviceId))
- {
- throw new SecurityException("User is not allowed access from this device.");
- }
- }
+ AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request));
+ throw new AuthenticationException("Invalid username or password entered.");
}
- if (enforcePassword)
+ if (!string.IsNullOrEmpty(request.DeviceId)
+ && !_deviceManager.CanAccessDevice(user, request.DeviceId))
{
- var result = await _userManager.AuthenticateUser(request.Username, request.Password, request.PasswordSha1, request.RemoteEndPoint, true).ConfigureAwait(false);
-
- if (result == null)
- {
- AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request));
-
- throw new SecurityException("Invalid user or password entered.");
- }
+ throw new SecurityException("User is not allowed access from this device.");
+ }
- user = result;
+ int sessionsCount = Sessions.Count(i => i.UserId.Equals(user.Id));
+ int maxActiveSessions = user.MaxActiveSessions;
+ _logger.LogInformation("Current/Max sessions for user {User}: {Sessions}/{Max}", user.Username, sessionsCount, maxActiveSessions);
+ if (maxActiveSessions >= 1 && sessionsCount >= maxActiveSessions)
+ {
+ throw new SecurityException("User is at their maximum number of sessions.");
}
- var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName);
+ var token = await GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName).ConfigureAwait(false);
- var session = LogSessionActivity(request.App,
+ var session = await LogSessionActivity(
+ request.App,
request.AppVersion,
request.DeviceId,
request.DeviceName,
request.RemoteEndPoint,
- user);
+ user).ConfigureAwait(false);
var returnResult = new AuthenticationResult
{
@@ -1430,21 +1518,21 @@ namespace Emby.Server.Implementations.Session
return returnResult;
}
- private string GetAuthorizationToken(User user, string deviceId, string app, string appVersion, string deviceName)
+ private async Task<string> GetAuthorizationToken(User user, string deviceId, string app, string appVersion, string deviceName)
{
- var existing = _authRepo.Get(new AuthenticationInfoQuery
- {
- DeviceId = deviceId,
- UserId = user.Id,
- Limit = 1
-
- }).Items.FirstOrDefault();
-
- var allExistingForDevice = _authRepo.Get(new AuthenticationInfoQuery
- {
- DeviceId = deviceId
+ var existing = (await _deviceManager.GetDevices(
+ new DeviceQuery
+ {
+ DeviceId = deviceId,
+ UserId = user.Id,
+ Limit = 1
+ }).ConfigureAwait(false)).Items.FirstOrDefault();
- }).Items;
+ var allExistingForDevice = (await _deviceManager.GetDevices(
+ new DeviceQuery
+ {
+ DeviceId = deviceId
+ }).ConfigureAwait(false)).Items;
foreach (var auth in allExistingForDevice)
{
@@ -1452,43 +1540,29 @@ namespace Emby.Server.Implementations.Session
{
try
{
- Logout(auth);
+ await Logout(auth).ConfigureAwait(false);
}
- catch
+ catch (Exception ex)
{
-
+ _logger.LogError(ex, "Error while logging out.");
}
}
}
if (existing != null)
{
- _logger.LogInformation("Reissuing access token: " + existing.AccessToken);
+ _logger.LogInformation("Reissuing access token: {Token}", existing.AccessToken);
return existing.AccessToken;
}
- var now = DateTime.UtcNow;
-
- var newToken = new AuthenticationInfo
- {
- AppName = app,
- AppVersion = appVersion,
- DateCreated = now,
- DateLastActivity = now,
- DeviceId = deviceId,
- DeviceName = deviceName,
- UserId = user.Id,
- AccessToken = Guid.NewGuid().ToString("N"),
- UserName = user.Name
- };
-
_logger.LogInformation("Creating new access token for user {0}", user.Id);
- _authRepo.Create(newToken);
+ var device = await _deviceManager.CreateDevice(new Device(user.Id, app, appVersion, deviceName, deviceId)).ConfigureAwait(false);
- return newToken.AccessToken;
+ return device.AccessToken;
}
- public void Logout(string accessToken)
+ /// <inheritdoc />
+ public async Task Logout(string accessToken)
{
CheckDisposed();
@@ -1497,29 +1571,30 @@ namespace Emby.Server.Implementations.Session
throw new ArgumentNullException(nameof(accessToken));
}
- var existing = _authRepo.Get(new AuthenticationInfoQuery
- {
- Limit = 1,
- AccessToken = accessToken
-
- }).Items.FirstOrDefault();
+ var existing = (await _deviceManager.GetDevices(
+ new DeviceQuery
+ {
+ Limit = 1,
+ AccessToken = accessToken
+ }).ConfigureAwait(false)).Items;
- if (existing != null)
+ if (existing.Count > 0)
{
- Logout(existing);
+ await Logout(existing[0]).ConfigureAwait(false);
}
}
- public void Logout(AuthenticationInfo existing)
+ /// <inheritdoc />
+ public async Task Logout(Device device)
{
CheckDisposed();
- _logger.LogInformation("Logging out access token {0}", existing.AccessToken);
+ _logger.LogInformation("Logging out access token {0}", device.AccessToken);
- _authRepo.Delete(existing);
+ await _deviceManager.DeleteDevice(device).ConfigureAwait(false);
var sessions = Sessions
- .Where(i => string.Equals(i.DeviceId, existing.DeviceId, StringComparison.OrdinalIgnoreCase))
+ .Where(i => string.Equals(i.DeviceId, device.DeviceId, StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (var session in sessions)
@@ -1530,34 +1605,30 @@ namespace Emby.Server.Implementations.Session
}
catch (Exception ex)
{
- _logger.LogError("Error reporting session ended", ex);
+ _logger.LogError(ex, "Error reporting session ended");
}
}
}
- public void RevokeUserTokens(Guid userId, string currentAccessToken)
+ /// <inheritdoc />
+ public async Task RevokeUserTokens(Guid userId, string currentAccessToken)
{
CheckDisposed();
- var existing = _authRepo.Get(new AuthenticationInfoQuery
+ var existing = await _deviceManager.GetDevices(new DeviceQuery
{
UserId = userId
- });
+ }).ConfigureAwait(false);
foreach (var info in existing.Items)
{
if (!string.Equals(currentAccessToken, info.AccessToken, StringComparison.OrdinalIgnoreCase))
{
- Logout(info);
+ await Logout(info).ConfigureAwait(false);
}
}
}
- public void RevokeToken(string token)
- {
- Logout(token);
- }
-
/// <summary>
/// Reports the capabilities.
/// </summary>
@@ -1572,7 +1643,8 @@ namespace Emby.Server.Implementations.Session
ReportCapabilities(session, capabilities, true);
}
- private void ReportCapabilities(SessionInfo session,
+ private void ReportCapabilities(
+ SessionInfo session,
ClientCapabilities capabilities,
bool saveCapabilities)
{
@@ -1580,36 +1652,19 @@ namespace Emby.Server.Implementations.Session
if (saveCapabilities)
{
- CapabilitiesChanged?.Invoke(this, new SessionEventArgs
- {
- SessionInfo = session
- });
+ CapabilitiesChanged?.Invoke(
+ this,
+ new SessionEventArgs
+ {
+ SessionInfo = session
+ });
- try
- {
- SaveCapabilities(session.DeviceId, capabilities);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error saving device capabilities", ex);
- }
+ _deviceManager.SaveCapabilities(session.DeviceId, capabilities);
}
}
- private ClientCapabilities GetSavedCapabilities(string deviceId)
- {
- return _deviceManager.GetCapabilities(deviceId);
- }
-
- private void SaveCapabilities(string deviceId, ClientCapabilities capabilities)
- {
- _deviceManager.SaveCapabilities(deviceId, capabilities);
- }
-
- private DtoOptions _itemInfoDtoOptions;
-
/// <summary>
- /// Converts a BaseItem to a BaseItemInfo
+ /// Converts a BaseItem to a BaseItemInfo.
/// </summary>
private BaseItemDto GetItemInfo(BaseItem item, MediaSourceInfo mediaSource)
{
@@ -1671,19 +1726,20 @@ namespace Emby.Server.Implementations.Session
return info;
}
- private string GetImageCacheTag(BaseItem item, ImageType type)
+ private string GetImageCacheTag(User user)
{
try
{
- return _imageProcessor.GetImageCacheTag(item, type);
+ return _imageProcessor.GetImageCacheTag(user);
}
- catch (Exception ex)
+ catch (Exception e)
{
- _logger.LogError("Error getting {0} image info", ex, type);
+ _logger.LogError(e, "Error getting image information for profile image");
return null;
}
}
+ /// <inheritdoc />
public void ReportNowViewingItem(string sessionId, string itemId)
{
if (string.IsNullOrEmpty(itemId))
@@ -1691,23 +1747,17 @@ namespace Emby.Server.Implementations.Session
throw new ArgumentNullException(nameof(itemId));
}
- //var item = _libraryManager.GetItemById(new Guid(itemId));
-
- //var info = GetItemInfo(item, null, null);
-
- //ReportNowViewingItem(sessionId, info);
- }
-
- public void ReportNowViewingItem(string sessionId, BaseItemDto item)
- {
- //var session = GetSession(sessionId);
+ var item = _libraryManager.GetItemById(new Guid(itemId));
+ var session = GetSession(sessionId);
- //session.NowViewingItem = item;
+ session.NowViewingItem = GetItemInfo(item, null);
}
+ /// <inheritdoc />
public void ReportTranscodingInfo(string deviceId, TranscodingInfo info)
{
- var session = Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId));
+ var session = Sessions.FirstOrDefault(i =>
+ string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase));
if (session != null)
{
@@ -1715,25 +1765,29 @@ namespace Emby.Server.Implementations.Session
}
}
+ /// <inheritdoc />
public void ClearTranscodingInfo(string deviceId)
{
ReportTranscodingInfo(deviceId, null);
}
+ /// <inheritdoc />
public SessionInfo GetSession(string deviceId, string client, string version)
{
- return Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId) &&
- string.Equals(i.Client, client));
+ return Sessions.FirstOrDefault(i =>
+ string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)
+ && string.Equals(i.Client, client, StringComparison.OrdinalIgnoreCase));
}
- public SessionInfo GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion)
+ /// <inheritdoc />
+ public Task<SessionInfo> GetSessionByAuthenticationToken(Device info, string deviceId, string remoteEndpoint, string appVersion)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
- var user = info.UserId.Equals(Guid.Empty)
+ var user = info.UserId == Guid.Empty
? null
: _userManager.GetUserById(info.UserId);
@@ -1758,33 +1812,38 @@ namespace Emby.Server.Implementations.Session
return LogSessionActivity(appName, appVersion, deviceId, deviceName, remoteEndpoint, user);
}
- public SessionInfo GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint)
+ /// <inheritdoc />
+ public async Task<SessionInfo> GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint)
{
- var result = _authRepo.Get(new AuthenticationInfoQuery
+ var items = (await _deviceManager.GetDevices(new DeviceQuery
{
- AccessToken = token
- });
-
- var info = result.Items.FirstOrDefault();
+ AccessToken = token,
+ Limit = 1
+ }).ConfigureAwait(false)).Items;
- if (info == null)
+ if (items.Count == 0)
{
return null;
}
- return GetSessionByAuthenticationToken(info, deviceId, remoteEndpoint, null);
+ return await GetSessionByAuthenticationToken(items[0], deviceId, remoteEndpoint, null).ConfigureAwait(false);
}
- public Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken)
+ /// <inheritdoc />
+ public Task SendMessageToAdminSessions<T>(SessionMessageType name, T data, CancellationToken cancellationToken)
{
CheckDisposed();
- var adminUserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id).ToList();
+ var adminUserIds = _userManager.Users
+ .Where(i => i.HasPermission(PermissionKind.IsAdministrator))
+ .Select(i => i.Id)
+ .ToList();
return SendMessageToUserSessions(adminUserIds, name, data, cancellationToken);
}
- public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, Func<T> dataFn, CancellationToken cancellationToken)
+ /// <inheritdoc />
+ public Task SendMessageToUserSessions<T>(List<Guid> userIds, SessionMessageType name, Func<T> dataFn, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -1795,97 +1854,26 @@ namespace Emby.Server.Implementations.Session
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.LogError("Error sending message", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
+ return SendMessageToSessions(sessions, name, dataFn(), cancellationToken);
}
- public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, T data, CancellationToken cancellationToken)
+ /// <inheritdoc />
+ public Task SendMessageToUserSessions<T>(List<Guid> userIds, SessionMessageType 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.LogError("Error sending message", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
+ var sessions = Sessions.Where(i => userIds.Any(i.ContainsUser));
+ return SendMessageToSessions(sessions, name, data, cancellationToken);
}
- public Task SendMessageToUserDeviceSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken)
+ /// <inheritdoc />
+ public Task SendMessageToUserDeviceSessions<T>(string deviceId, SessionMessageType name, T data, CancellationToken cancellationToken)
{
CheckDisposed();
- var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)).ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error sending message", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
- }
-
- public Task SendMessageToUserDeviceAndAdminSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken)
- {
- 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 SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error sending message", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
- }
-
- private bool IsAdminSession(SessionInfo s)
- {
- var user = _userManager.GetUserById(s.UserId);
+ var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase));
- return user != null && user.Policy.IsAdministrator;
+ return SendMessageToSessions(sessions, name, data, cancellationToken);
}
}
}