aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/BaseApiService.cs17
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj1
-rw-r--r--MediaBrowser.Api/SessionsService.cs54
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs12
-rw-r--r--MediaBrowser.Controller/Dto/DtoBuilder.cs11
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs6
-rw-r--r--MediaBrowser.Controller/Library/IUserManager.cs55
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj1
-rw-r--r--MediaBrowser.Controller/Session/ISessionManager.cs90
-rw-r--r--MediaBrowser.Model/Entities/BaseItemInfo.cs22
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj2
-rw-r--r--MediaBrowser.Model/Session/SessionInfo.cs (renamed from MediaBrowser.Model/Connectivity/ClientConnectionInfo.cs)12
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserManager.cs302
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj4
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionManager.cs372
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs58
-rw-r--r--MediaBrowser.ServerApplication/ApplicationHost.cs7
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardInfo.cs6
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardInfoWebSocketListener.cs7
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs18
-rw-r--r--MediaBrowser.WebDashboard/ApiClient.js3
-rw-r--r--MediaBrowser.WebDashboard/packages.config2
-rw-r--r--MediaBrowser.sln3
23 files changed, 664 insertions, 401 deletions
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs
index 0c95f6112..17c36254e 100644
--- a/MediaBrowser.Api/BaseApiService.cs
+++ b/MediaBrowser.Api/BaseApiService.cs
@@ -1,5 +1,7 @@
using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
using ServiceStack.Common.Web;
using ServiceStack.ServiceHost;
@@ -100,6 +102,8 @@ namespace MediaBrowser.Api
/// <value>The user manager.</value>
public IUserManager UserManager { get; set; }
+ public ISessionManager SessionManager { get; set; }
+
/// <summary>
/// Gets or sets the logger.
/// </summary>
@@ -122,11 +126,20 @@ namespace MediaBrowser.Api
{
var userId = auth["UserId"];
+ User user = null;
+
if (!string.IsNullOrEmpty(userId))
{
- var user = UserManager.GetUserById(new Guid(userId));
+ user = UserManager.GetUserById(new Guid(userId));
+ }
- UserManager.LogUserActivity(user, auth["Client"], auth["DeviceId"], auth["Device"] ?? string.Empty);
+ var deviceId = auth["DeviceId"];
+ var device = auth["Device"];
+ var client = auth["Client"];
+
+ if (!string.IsNullOrEmpty(client) && !string.IsNullOrEmpty(deviceId) && !string.IsNullOrEmpty(device))
+ {
+ SessionManager.LogConnectionActivity(client, deviceId, device, user);
}
}
}
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index b17412ee6..7ed030a87 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -88,6 +88,7 @@
<Compile Include="ScheduledTasks\ScheduledTasksWebSocketListener.cs" />
<Compile Include="ApiEntryPoint.cs" />
<Compile Include="SearchService.cs" />
+ <Compile Include="SessionsService.cs" />
<Compile Include="SystemService.cs" />
<Compile Include="TvShowsService.cs" />
<Compile Include="UserLibrary\ArtistsService.cs" />
diff --git a/MediaBrowser.Api/SessionsService.cs b/MediaBrowser.Api/SessionsService.cs
new file mode 100644
index 000000000..03a352307
--- /dev/null
+++ b/MediaBrowser.Api/SessionsService.cs
@@ -0,0 +1,54 @@
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Session;
+using ServiceStack.ServiceHost;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MediaBrowser.Api
+{
+ /// <summary>
+ /// Class GetSessions
+ /// </summary>
+ [Route("/Sessions", "GET")]
+ [Api(("Gets a list of sessions"))]
+ public class GetSessions : IReturn<List<SessionInfo>>
+ {
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is recent.
+ /// </summary>
+ /// <value><c>true</c> if this instance is recent; otherwise, <c>false</c>.</value>
+ public bool IsRecent { get; set; }
+ }
+
+ /// <summary>
+ /// Class SessionsService
+ /// </summary>
+ public class SessionsService : BaseApiService
+ {
+ /// <summary>
+ /// The _session manager
+ /// </summary>
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SessionsService"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public SessionsService(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <summary>
+ /// Gets the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>System.Object.</returns>
+ public object Get(GetSessions request)
+ {
+ var result = request.IsRecent ? _sessionManager.RecentConnections : _sessionManager.AllConnections;
+
+ return ToOptimizedResult(result.ToList());
+ }
+ }
+}
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index 5c1eff954..53f2e9bca 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using ServiceStack.ServiceHost;
@@ -401,6 +402,8 @@ namespace MediaBrowser.Api.UserLibrary
private readonly IItemRepository _itemRepo;
+ private readonly ISessionManager _sessionManager;
+
/// <summary>
/// Initializes a new instance of the <see cref="UserLibraryService" /> class.
/// </summary>
@@ -409,13 +412,14 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="userDataRepository">The user data repository.</param>
/// <param name="itemRepo">The item repo.</param>
/// <exception cref="System.ArgumentNullException">jsonSerializer</exception>
- public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo)
+ public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo, ISessionManager sessionManager)
: base()
{
_userManager = userManager;
_libraryManager = libraryManager;
_userDataRepository = userDataRepository;
_itemRepo = itemRepo;
+ _sessionManager = sessionManager;
}
/// <summary>
@@ -693,7 +697,7 @@ namespace MediaBrowser.Api.UserLibrary
if (auth != null)
{
- _userManager.OnPlaybackStart(user, item, auth["Client"], auth["DeviceId"], auth["Device"] ?? string.Empty);
+ _sessionManager.OnPlaybackStart(user, item, auth["Client"], auth["DeviceId"], auth["Device"] ?? string.Empty);
}
}
@@ -711,7 +715,7 @@ namespace MediaBrowser.Api.UserLibrary
if (auth != null)
{
- var task = _userManager.OnPlaybackProgress(user, item, request.PositionTicks, auth["Client"], auth["DeviceId"], auth["Device"] ?? string.Empty);
+ var task = _sessionManager.OnPlaybackProgress(user, item, request.PositionTicks, auth["Client"], auth["DeviceId"], auth["Device"] ?? string.Empty);
Task.WaitAll(task);
}
@@ -731,7 +735,7 @@ namespace MediaBrowser.Api.UserLibrary
if (auth != null)
{
- var task = _userManager.OnPlaybackStopped(user, item, request.PositionTicks, auth["Client"], auth["DeviceId"], auth["Device"] ?? string.Empty);
+ var task = _sessionManager.OnPlaybackStopped(user, item, request.PositionTicks, auth["Client"], auth["DeviceId"], auth["Device"] ?? string.Empty);
Task.WaitAll(task);
}
diff --git a/MediaBrowser.Controller/Dto/DtoBuilder.cs b/MediaBrowser.Controller/Dto/DtoBuilder.cs
index d84227059..167ff2f78 100644
--- a/MediaBrowser.Controller/Dto/DtoBuilder.cs
+++ b/MediaBrowser.Controller/Dto/DtoBuilder.cs
@@ -832,6 +832,7 @@ namespace MediaBrowser.Controller.Dto
{
Id = GetClientItemId(item),
Name = item.Name,
+ MediaType = item.MediaType,
Type = item.GetType().Name,
IsFolder = item.IsFolder,
RunTimeTicks = item.RunTimeTicks
@@ -844,16 +845,6 @@ namespace MediaBrowser.Controller.Dto
info.PrimaryImageTag = Kernel.Instance.ImageManager.GetImageCacheTag(item, ImageType.Primary, imagePath);
}
- if (item.BackdropImagePaths != null && item.BackdropImagePaths.Count > 0)
- {
- imagePath = item.BackdropImagePaths[0];
-
- if (!string.IsNullOrEmpty(imagePath))
- {
- info.BackdropImageTag = Kernel.Instance.ImageManager.GetImageCacheTag(item, ImageType.Backdrop, imagePath);
- }
- }
-
return info;
}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index e31939d59..43c629505 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -683,7 +683,7 @@ namespace MediaBrowser.Controller.Entities
/// Loads local trailers from the file system
/// </summary>
/// <returns>List{Video}.</returns>
- private List<Trailer> LoadLocalTrailers()
+ private IEnumerable<Trailer> LoadLocalTrailers()
{
if (LocationType != LocationType.FileSystem)
{
@@ -746,7 +746,7 @@ namespace MediaBrowser.Controller.Entities
/// Loads the theme songs.
/// </summary>
/// <returns>List{Audio.Audio}.</returns>
- private List<Audio.Audio> LoadThemeSongs()
+ private IEnumerable<Audio.Audio> LoadThemeSongs()
{
if (LocationType != LocationType.FileSystem)
{
@@ -809,7 +809,7 @@ namespace MediaBrowser.Controller.Entities
/// Loads the video backdrops.
/// </summary>
/// <returns>List{Video}.</returns>
- private List<Video> LoadThemeVideos()
+ private IEnumerable<Video> LoadThemeVideos()
{
if (LocationType != LocationType.FileSystem)
{
diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs
index 0fad1d05d..a0cea6200 100644
--- a/MediaBrowser.Controller/Library/IUserManager.cs
+++ b/MediaBrowser.Controller/Library/IUserManager.cs
@@ -1,6 +1,5 @@
using MediaBrowser.Common.Events;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Connectivity;
using System;
using System.Collections.Generic;
using System.Threading;
@@ -20,12 +19,6 @@ namespace MediaBrowser.Controller.Library
IEnumerable<User> Users { get; }
/// <summary>
- /// Gets the active connections.
- /// </summary>
- /// <value>The active connections.</value>
- IEnumerable<ClientConnectionInfo> RecentConnections { get; }
-
- /// <summary>
/// Occurs when [playback start].
/// </summary>
event EventHandler<PlaybackProgressEventArgs> PlaybackStart;
@@ -68,17 +61,6 @@ namespace MediaBrowser.Controller.Library
Task<bool> AuthenticateUser(User user, string password);
/// <summary>
- /// Logs the user activity.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="clientType">Type of the client.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">user</exception>
- Task LogUserActivity(User user, string clientType, string deviceId, string deviceName);
-
- /// <summary>
/// Refreshes metadata for each user
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
@@ -123,43 +105,6 @@ namespace MediaBrowser.Controller.Library
Task DeleteUser(User user);
/// <summary>
- /// Used to report that playback has started for an item
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="item">The item.</param>
- /// <param name="clientType">Type of the client.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <exception cref="System.ArgumentNullException"></exception>
- void OnPlaybackStart(User user, BaseItem item, string clientType, string deviceId, string deviceName);
-
- /// <summary>
- /// Used to report playback progress for an item
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="item">The item.</param>
- /// <param name="positionTicks">The position ticks.</param>
- /// <param name="clientType">Type of the client.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- Task OnPlaybackProgress(User user, BaseItem item, long? positionTicks, string clientType, string deviceId, string deviceName);
-
- /// <summary>
- /// Used to report that playback has ended for an item
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="item">The item.</param>
- /// <param name="positionTicks">The position ticks.</param>
- /// <param name="clientType">Type of the client.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- Task OnPlaybackStopped(User user, BaseItem item, long? positionTicks, string clientType, string deviceId, string deviceName);
-
- /// <summary>
/// Resets the password.
/// </summary>
/// <param name="user">The user.</param>
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 8c1cc5163..c54a446fb 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -70,6 +70,7 @@
<Link>Properties\SharedVersion.cs</Link>
</Compile>
<Compile Include="Configuration\IServerConfigurationManager.cs" />
+ <Compile Include="Session\ISessionManager.cs" />
<Compile Include="Drawing\ImageExtensions.cs" />
<Compile Include="Drawing\ImageHeader.cs" />
<Compile Include="Drawing\ImageManager.cs" />
diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs
new file mode 100644
index 000000000..66facdf6d
--- /dev/null
+++ b/MediaBrowser.Controller/Session/ISessionManager.cs
@@ -0,0 +1,90 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Session;
+
+namespace MediaBrowser.Controller.Session
+{
+ /// <summary>
+ /// Interface ISessionManager
+ /// </summary>
+ public interface ISessionManager
+ {
+ /// <summary>
+ /// Occurs when [playback start].
+ /// </summary>
+ event EventHandler<PlaybackProgressEventArgs> PlaybackStart;
+
+ /// <summary>
+ /// Occurs when [playback progress].
+ /// </summary>
+ event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
+
+ /// <summary>
+ /// Occurs when [playback stopped].
+ /// </summary>
+ event EventHandler<PlaybackProgressEventArgs> PlaybackStopped;
+
+ /// <summary>
+ /// Gets all connections.
+ /// </summary>
+ /// <value>All connections.</value>
+ IEnumerable<SessionInfo> AllConnections { get; }
+
+ /// <summary>
+ /// Gets the active connections.
+ /// </summary>
+ /// <value>The active connections.</value>
+ IEnumerable<SessionInfo> RecentConnections { get; }
+
+ /// <summary>
+ /// Logs the user activity.
+ /// </summary>
+ /// <param name="clientType">Type of the client.</param>
+ /// <param name="deviceId">The device id.</param>
+ /// <param name="deviceName">Name of the device.</param>
+ /// <param name="user">The user.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException">user</exception>
+ Task LogConnectionActivity(string clientType, string deviceId, string deviceName, User user);
+
+ /// <summary>
+ /// Used to report that playback has started for an item
+ /// </summary>
+ /// <param name="user">The user.</param>
+ /// <param name="item">The item.</param>
+ /// <param name="clientType">Type of the client.</param>
+ /// <param name="deviceId">The device id.</param>
+ /// <param name="deviceName">Name of the device.</param>
+ /// <exception cref="System.ArgumentNullException"></exception>
+ void OnPlaybackStart(User user, BaseItem item, string clientType, string deviceId, string deviceName);
+
+ /// <summary>
+ /// Used to report playback progress for an item
+ /// </summary>
+ /// <param name="user">The user.</param>
+ /// <param name="item">The item.</param>
+ /// <param name="positionTicks">The position ticks.</param>
+ /// <param name="clientType">Type of the client.</param>
+ /// <param name="deviceId">The device id.</param>
+ /// <param name="deviceName">Name of the device.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException"></exception>
+ Task OnPlaybackProgress(User user, BaseItem item, long? positionTicks, string clientType, string deviceId, string deviceName);
+
+ /// <summary>
+ /// Used to report that playback has ended for an item
+ /// </summary>
+ /// <param name="user">The user.</param>
+ /// <param name="item">The item.</param>
+ /// <param name="positionTicks">The position ticks.</param>
+ /// <param name="clientType">Type of the client.</param>
+ /// <param name="deviceId">The device id.</param>
+ /// <param name="deviceName">Name of the device.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException"></exception>
+ Task OnPlaybackStopped(User user, BaseItem item, long? positionTicks, string clientType, string deviceId, string deviceName);
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Model/Entities/BaseItemInfo.cs b/MediaBrowser.Model/Entities/BaseItemInfo.cs
index dc7b3bebe..0d8e35cc9 100644
--- a/MediaBrowser.Model/Entities/BaseItemInfo.cs
+++ b/MediaBrowser.Model/Entities/BaseItemInfo.cs
@@ -27,6 +27,12 @@ namespace MediaBrowser.Model.Entities
public string Type { get; set; }
/// <summary>
+ /// Gets or sets the type of the media.
+ /// </summary>
+ /// <value>The type of the media.</value>
+ public string MediaType { get; set; }
+
+ /// <summary>
/// Gets or sets a value indicating whether this instance is folder.
/// </summary>
/// <value><c>true</c> if this instance is folder; otherwise, <c>false</c>.</value>
@@ -45,12 +51,6 @@ namespace MediaBrowser.Model.Entities
public Guid? PrimaryImageTag { get; set; }
/// <summary>
- /// Gets or sets the backdrop image tag.
- /// </summary>
- /// <value>The backdrop image tag.</value>
- public Guid? BackdropImageTag { get; set; }
-
- /// <summary>
/// Gets a value indicating whether this instance has primary image.
/// </summary>
/// <value><c>true</c> if this instance has primary image; otherwise, <c>false</c>.</value>
@@ -59,15 +59,5 @@ namespace MediaBrowser.Model.Entities
{
get { return PrimaryImageTag.HasValue; }
}
-
- /// <summary>
- /// Gets a value indicating whether this instance has backdrop.
- /// </summary>
- /// <value><c>true</c> if this instance has backdrop; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool HasBackdrop
- {
- get { return BackdropImageTag.HasValue; }
- }
}
}
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 707e6ea4d..dbfe38698 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -56,7 +56,7 @@
<Compile Include="Querying\ArtistsQuery.cs" />
<Compile Include="Querying\ItemsByNameQuery.cs" />
<Compile Include="Entities\BaseItemInfo.cs" />
- <Compile Include="Connectivity\ClientConnectionInfo.cs" />
+ <Compile Include="Session\SessionInfo.cs" />
<Compile Include="Entities\ImageDownloadOptions.cs" />
<Compile Include="Logging\ILogManager.cs" />
<Compile Include="MediaInfo\BlurayDiscInfo.cs" />
diff --git a/MediaBrowser.Model/Connectivity/ClientConnectionInfo.cs b/MediaBrowser.Model/Session/SessionInfo.cs
index dc0c4508b..f74db9058 100644
--- a/MediaBrowser.Model/Connectivity/ClientConnectionInfo.cs
+++ b/MediaBrowser.Model/Session/SessionInfo.cs
@@ -1,14 +1,20 @@
using MediaBrowser.Model.Entities;
using System;
-namespace MediaBrowser.Model.Connectivity
+namespace MediaBrowser.Model.Session
{
/// <summary>
- /// Class ClientConnectionInfo
+ /// Class SessionInfo
/// </summary>
- public class ClientConnectionInfo
+ public class SessionInfo
{
/// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ public Guid Id { get; set; }
+
+ /// <summary>
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs
index 99485f726..dc863ca4d 100644
--- a/MediaBrowser.Server.Implementations/Library/UserManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs
@@ -7,7 +7,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Connectivity;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Concurrent;
@@ -17,6 +16,7 @@ using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Session;
namespace MediaBrowser.Server.Implementations.Library
{
@@ -28,8 +28,8 @@ namespace MediaBrowser.Server.Implementations.Library
/// <summary>
/// The _active connections
/// </summary>
- private readonly ConcurrentDictionary<string, ClientConnectionInfo> _activeConnections =
- new ConcurrentDictionary<string, ClientConnectionInfo>(StringComparer.OrdinalIgnoreCase);
+ private readonly ConcurrentDictionary<string, SessionInfo> _activeConnections =
+ new ConcurrentDictionary<string, SessionInfo>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// The _users
@@ -70,7 +70,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// Gets all connections.
/// </summary>
/// <value>All connections.</value>
- public IEnumerable<ClientConnectionInfo> AllConnections
+ public IEnumerable<SessionInfo> AllConnections
{
get { return _activeConnections.Values.OrderByDescending(c => c.LastActivityDate); }
}
@@ -79,7 +79,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// Gets the active connections.
/// </summary>
/// <value>The active connections.</value>
- public IEnumerable<ClientConnectionInfo> RecentConnections
+ public IEnumerable<SessionInfo> RecentConnections
{
get { return AllConnections.Where(c => (DateTime.UtcNow - c.LastActivityDate).TotalMinutes <= 5); }
}
@@ -89,8 +89,6 @@ namespace MediaBrowser.Server.Implementations.Library
/// </summary>
private readonly ILogger _logger;
- private readonly IUserDataRepository _userDataRepository;
-
/// <summary>
/// Gets or sets the configuration manager.
/// </summary>
@@ -109,11 +107,10 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="logger">The logger.</param>
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="userDataRepository">The user data repository.</param>
- public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserDataRepository userDataRepository)
+ public UserManager(ILogger logger, IServerConfigurationManager configurationManager)
{
_logger = logger;
ConfigurationManager = configurationManager;
- _userDataRepository = userDataRepository;
}
#region Events
@@ -223,116 +220,6 @@ namespace MediaBrowser.Server.Implementations.Library
}
/// <summary>
- /// Logs the user activity.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="clientType">Type of the client.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">user</exception>
- public Task LogUserActivity(User user, string clientType, string deviceId, string deviceName)
- {
- if (user == null)
- {
- throw new ArgumentNullException("user");
- }
-
- var activityDate = DateTime.UtcNow;
-
- var lastActivityDate = user.LastActivityDate;
-
- user.LastActivityDate = activityDate;
-
- LogConnection(user.Id, clientType, deviceId, deviceName, activityDate);
-
- // Don't log in the db anymore frequently than 10 seconds
- if (lastActivityDate.HasValue && (activityDate - lastActivityDate.Value).TotalSeconds < 10)
- {
- return Task.FromResult(true);
- }
-
- // Save this directly. No need to fire off all the events for this.
- return UserRepository.SaveUser(user, CancellationToken.None);
- }
-
- /// <summary>
- /// Updates the now playing item id.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="clientType">Type of the client.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <param name="item">The item.</param>
- /// <param name="currentPositionTicks">The current position ticks.</param>
- private void UpdateNowPlayingItemId(User user, string clientType, string deviceId, string deviceName, BaseItem item, long? currentPositionTicks = null)
- {
- var conn = GetConnection(user.Id, clientType, deviceId, deviceName);
-
- conn.NowPlayingPositionTicks = currentPositionTicks;
- conn.NowPlayingItem = DtoBuilder.GetBaseItemInfo(item);
- conn.LastActivityDate = DateTime.UtcNow;
- }
-
- /// <summary>
- /// Removes the now playing item id.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="clientType">Type of the client.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <param name="item">The item.</param>
- private void RemoveNowPlayingItemId(User user, string clientType, string deviceId, string deviceName, BaseItem item)
- {
- var conn = GetConnection(user.Id, clientType, deviceId, deviceName);
-
- if (conn.NowPlayingItem != null && conn.NowPlayingItem.Id.Equals(item.Id.ToString()))
- {
- conn.NowPlayingItem = null;
- conn.NowPlayingPositionTicks = null;
- }
- }
-
- /// <summary>
- /// Logs the connection.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="clientType">Type of the client.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <param name="lastActivityDate">The last activity date.</param>
- private void LogConnection(Guid userId, string clientType, string deviceId, string deviceName, DateTime lastActivityDate)
- {
- GetConnection(userId, clientType, deviceId, deviceName).LastActivityDate = lastActivityDate;
- }
-
- /// <summary>
- /// Gets the connection.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="clientType">Type of the client.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <returns>ClientConnectionInfo.</returns>
- private ClientConnectionInfo GetConnection(Guid userId, string clientType, string deviceId, string deviceName)
- {
- var key = clientType + deviceId;
-
- var connection = _activeConnections.GetOrAdd(key, keyName => new ClientConnectionInfo
- {
- UserId = userId.ToString(),
- Client = clientType,
- DeviceName = deviceName,
- DeviceId = deviceId
- });
-
- connection.DeviceName = deviceName;
- connection.UserId = userId.ToString();
-
- return connection;
- }
-
- /// <summary>
/// Loads the users from the repository
/// </summary>
/// <returns>IEnumerable{User}.</returns>
@@ -560,182 +447,5 @@ namespace MediaBrowser.Server.Implementations.Library
DateModified = DateTime.UtcNow
};
}
-
- /// <summary>
- /// Used to report that playback has started for an item
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="item">The item.</param>
- /// <param name="clientType">Type of the client.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <exception cref="System.ArgumentNullException"></exception>
- public void OnPlaybackStart(User user, BaseItem item, string clientType, string deviceId, string deviceName)
- {
- if (user == null)
- {
- throw new ArgumentNullException();
- }
- if (item == null)
- {
- throw new ArgumentNullException();
- }
-
- UpdateNowPlayingItemId(user, clientType, deviceId, deviceName, item);
-
- // Nothing to save here
- // Fire events to inform plugins
- EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs
- {
- Item = item,
- User = user
- }, _logger);
- }
-
- /// <summary>
- /// Used to report playback progress for an item
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="item">The item.</param>
- /// <param name="positionTicks">The position ticks.</param>
- /// <param name="clientType">Type of the client.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- public async Task OnPlaybackProgress(User user, BaseItem item, long? positionTicks, string clientType, string deviceId, string deviceName)
- {
- if (user == null)
- {
- throw new ArgumentNullException();
- }
- if (item == null)
- {
- throw new ArgumentNullException();
- }
-
- UpdateNowPlayingItemId(user, clientType, deviceId, deviceName, item, positionTicks);
-
- var key = item.GetUserDataKey();
-
- if (positionTicks.HasValue)
- {
- var data = await _userDataRepository.GetUserData(user.Id, key).ConfigureAwait(false);
-
- UpdatePlayState(item, data, positionTicks.Value, false);
- await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false);
- }
-
- EventHelper.QueueEventIfNotNull(PlaybackProgress, this, new PlaybackProgressEventArgs
- {
- Item = item,
- User = user,
- PlaybackPositionTicks = positionTicks
- }, _logger);
- }
-
- /// <summary>
- /// Used to report that playback has ended for an item
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="item">The item.</param>
- /// <param name="positionTicks">The position ticks.</param>
- /// <param name="clientType">Type of the client.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- public async Task OnPlaybackStopped(User user, BaseItem item, long? positionTicks, string clientType, string deviceId, string deviceName)
- {
- if (user == null)
- {
- throw new ArgumentNullException();
- }
- if (item == null)
- {
- throw new ArgumentNullException();
- }
-
- RemoveNowPlayingItemId(user, clientType, deviceId, deviceName, item);
-
- var key = item.GetUserDataKey();
-
- var data = await _userDataRepository.GetUserData(user.Id, key).ConfigureAwait(false);
-
- if (positionTicks.HasValue)
- {
- UpdatePlayState(item, data, positionTicks.Value, true);
- }
- else
- {
- // If the client isn't able to report this, then we'll just have to make an assumption
- data.PlayCount++;
- data.Played = true;
- }
-
- await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false);
-
- EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackProgressEventArgs
- {
- Item = item,
- User = user,
- PlaybackPositionTicks = positionTicks
- }, _logger);
- }
-
- /// <summary>
- /// Updates playstate position for an item but does not save
- /// </summary>
- /// <param name="item">The item</param>
- /// <param name="data">User data for the item</param>
- /// <param name="positionTicks">The current playback position</param>
- /// <param name="incrementPlayCount">Whether or not to increment playcount</param>
- private void UpdatePlayState(BaseItem item, UserItemData data, long positionTicks, bool incrementPlayCount)
- {
- // If a position has been reported, and if we know the duration
- if (positionTicks > 0 && item.RunTimeTicks.HasValue && item.RunTimeTicks > 0)
- {
- var pctIn = Decimal.Divide(positionTicks, item.RunTimeTicks.Value) * 100;
-
- // Don't track in very beginning
- if (pctIn < ConfigurationManager.Configuration.MinResumePct)
- {
- positionTicks = 0;
- incrementPlayCount = false;
- }
-
- // If we're at the end, assume completed
- else if (pctIn > ConfigurationManager.Configuration.MaxResumePct || positionTicks >= item.RunTimeTicks.Value)
- {
- positionTicks = 0;
- data.Played = true;
- }
-
- else
- {
- // Enforce MinResumeDuration
- var durationSeconds = TimeSpan.FromTicks(item.RunTimeTicks.Value).TotalSeconds;
-
- if (durationSeconds < ConfigurationManager.Configuration.MinResumeDurationSeconds)
- {
- positionTicks = 0;
- data.Played = true;
- }
- }
- }
-
- if (item is Audio)
- {
- data.PlaybackPositionTicks = 0;
- }
-
- data.PlaybackPositionTicks = positionTicks;
-
- if (incrementPlayCount)
- {
- data.PlayCount++;
- data.LastPlayedDate = DateTime.UtcNow;
- }
- }
}
}
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index f9e7f0385..45515b81f 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -156,6 +156,10 @@
<Compile Include="ServerApplicationPaths.cs" />
<Compile Include="ServerManager\ServerManager.cs" />
<Compile Include="ServerManager\WebSocketConnection.cs" />
+ <Compile Include="Session\SessionManager.cs">
+ <SubType>Code</SubType>
+ </Compile>
+ <Compile Include="Session\SessionWebSocketListener.cs" />
<Compile Include="Sorting\AlbumArtistComparer.cs" />
<Compile Include="Sorting\AlbumComparer.cs" />
<Compile Include="Sorting\ArtistComparer.cs" />
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
new file mode 100644
index 000000000..051c8fb68
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
@@ -0,0 +1,372 @@
+using MediaBrowser.Common.Events;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Session;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Session
+{
+ public class SessionManager : ISessionManager
+ {
+ private readonly IUserDataRepository _userDataRepository;
+
+ private readonly IUserRepository _userRepository;
+
+ /// <summary>
+ /// The _logger
+ /// </summary>
+ private readonly ILogger _logger;
+
+ /// <summary>
+ /// Gets or sets the configuration manager.
+ /// </summary>
+ /// <value>The configuration manager.</value>
+ private readonly IServerConfigurationManager _configurationManager;
+
+ /// <summary>
+ /// The _active connections
+ /// </summary>
+ private readonly ConcurrentDictionary<string, SessionInfo> _activeConnections =
+ new ConcurrentDictionary<string, SessionInfo>(StringComparer.OrdinalIgnoreCase);
+
+ private readonly ConcurrentDictionary<Guid, IWebSocketConnection> _websocketConnections =
+ new ConcurrentDictionary<Guid, IWebSocketConnection>();
+
+ /// <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<PlaybackProgressEventArgs> PlaybackStopped;
+
+ public SessionManager(IUserDataRepository userDataRepository, IServerConfigurationManager configurationManager, ILogger logger, IUserRepository userRepository)
+ {
+ _userDataRepository = userDataRepository;
+ _configurationManager = configurationManager;
+ _logger = logger;
+ _userRepository = userRepository;
+ }
+
+ /// <summary>
+ /// Gets all connections.
+ /// </summary>
+ /// <value>All connections.</value>
+ public IEnumerable<SessionInfo> AllConnections
+ {
+ get { return _activeConnections.Values.OrderByDescending(c => c.LastActivityDate); }
+ }
+
+ /// <summary>
+ /// Gets the active connections.
+ /// </summary>
+ /// <value>The active connections.</value>
+ public IEnumerable<SessionInfo> RecentConnections
+ {
+ get { return AllConnections.Where(c => (DateTime.UtcNow - c.LastActivityDate).TotalMinutes <= 5); }
+ }
+
+ private readonly Task _trueTaskResult = Task.FromResult(true);
+
+ /// <summary>
+ /// Logs the user activity.
+ /// </summary>
+ /// <param name="clientType">Type of the client.</param>
+ /// <param name="deviceId">The device id.</param>
+ /// <param name="deviceName">Name of the device.</param>
+ /// <param name="user">The user.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException">user</exception>
+ public Task LogConnectionActivity(string clientType, string deviceId, string deviceName, User user)
+ {
+ var activityDate = DateTime.UtcNow;
+
+ GetConnection(clientType, deviceId, deviceName, user).LastActivityDate = activityDate;
+
+ if (user == null)
+ {
+ return _trueTaskResult;
+ }
+
+ var lastActivityDate = user.LastActivityDate;
+
+ user.LastActivityDate = activityDate;
+
+ // Don't log in the db anymore frequently than 10 seconds
+ if (lastActivityDate.HasValue && (activityDate - lastActivityDate.Value).TotalSeconds < 10)
+ {
+ return _trueTaskResult;
+ }
+
+ // Save this directly. No need to fire off all the events for this.
+ return _userRepository.SaveUser(user, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Updates the now playing item id.
+ /// </summary>
+ /// <param name="user">The user.</param>
+ /// <param name="clientType">Type of the client.</param>
+ /// <param name="deviceId">The device id.</param>
+ /// <param name="deviceName">Name of the device.</param>
+ /// <param name="item">The item.</param>
+ /// <param name="currentPositionTicks">The current position ticks.</param>
+ private void UpdateNowPlayingItemId(User user, string clientType, string deviceId, string deviceName, BaseItem item, long? currentPositionTicks = null)
+ {
+ var conn = GetConnection(clientType, deviceId, deviceName, user);
+
+ conn.NowPlayingPositionTicks = currentPositionTicks;
+ conn.NowPlayingItem = DtoBuilder.GetBaseItemInfo(item);
+ conn.LastActivityDate = DateTime.UtcNow;
+ }
+
+ /// <summary>
+ /// Removes the now playing item id.
+ /// </summary>
+ /// <param name="user">The user.</param>
+ /// <param name="clientType">Type of the client.</param>
+ /// <param name="deviceId">The device id.</param>
+ /// <param name="deviceName">Name of the device.</param>
+ /// <param name="item">The item.</param>
+ private void RemoveNowPlayingItemId(User user, string clientType, string deviceId, string deviceName, BaseItem item)
+ {
+ var conn = GetConnection(clientType, deviceId, deviceName, user);
+
+ if (conn.NowPlayingItem != null && conn.NowPlayingItem.Id.Equals(item.Id.ToString()))
+ {
+ conn.NowPlayingItem = null;
+ conn.NowPlayingPositionTicks = null;
+ }
+ }
+
+ /// <summary>
+ /// Gets the connection.
+ /// </summary>
+ /// <param name="clientType">Type of the client.</param>
+ /// <param name="deviceId">The device id.</param>
+ /// <param name="deviceName">Name of the device.</param>
+ /// <param name="user">The user.</param>
+ /// <returns>SessionInfo.</returns>
+ private SessionInfo GetConnection(string clientType, string deviceId, string deviceName, User user)
+ {
+ var key = clientType + deviceId;
+
+ var connection = _activeConnections.GetOrAdd(key, keyName => new SessionInfo
+ {
+ Client = clientType,
+ DeviceId = deviceId,
+ Id = Guid.NewGuid()
+ });
+
+ connection.DeviceName = deviceName;
+
+ connection.UserId = user == null ? null : user.Id.ToString();
+
+ return connection;
+ }
+
+ /// <summary>
+ /// Used to report that playback has started for an item
+ /// </summary>
+ /// <param name="user">The user.</param>
+ /// <param name="item">The item.</param>
+ /// <param name="clientType">Type of the client.</param>
+ /// <param name="deviceId">The device id.</param>
+ /// <param name="deviceName">Name of the device.</param>
+ /// <exception cref="System.ArgumentNullException"></exception>
+ public void OnPlaybackStart(User user, BaseItem item, string clientType, string deviceId, string deviceName)
+ {
+ if (user == null)
+ {
+ throw new ArgumentNullException();
+ }
+ if (item == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ UpdateNowPlayingItemId(user, clientType, deviceId, deviceName, item);
+
+ // Nothing to save here
+ // Fire events to inform plugins
+ EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs
+ {
+ Item = item,
+ User = user
+ }, _logger);
+ }
+
+ /// <summary>
+ /// Used to report playback progress for an item
+ /// </summary>
+ /// <param name="user">The user.</param>
+ /// <param name="item">The item.</param>
+ /// <param name="positionTicks">The position ticks.</param>
+ /// <param name="clientType">Type of the client.</param>
+ /// <param name="deviceId">The device id.</param>
+ /// <param name="deviceName">Name of the device.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException"></exception>
+ public async Task OnPlaybackProgress(User user, BaseItem item, long? positionTicks, string clientType, string deviceId, string deviceName)
+ {
+ if (user == null)
+ {
+ throw new ArgumentNullException();
+ }
+ if (item == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ UpdateNowPlayingItemId(user, clientType, deviceId, deviceName, item, positionTicks);
+
+ var key = item.GetUserDataKey();
+
+ if (positionTicks.HasValue)
+ {
+ var data = await _userDataRepository.GetUserData(user.Id, key).ConfigureAwait(false);
+
+ UpdatePlayState(item, data, positionTicks.Value, false);
+ await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ EventHelper.QueueEventIfNotNull(PlaybackProgress, this, new PlaybackProgressEventArgs
+ {
+ Item = item,
+ User = user,
+ PlaybackPositionTicks = positionTicks
+ }, _logger);
+ }
+
+ /// <summary>
+ /// Used to report that playback has ended for an item
+ /// </summary>
+ /// <param name="user">The user.</param>
+ /// <param name="item">The item.</param>
+ /// <param name="positionTicks">The position ticks.</param>
+ /// <param name="clientType">Type of the client.</param>
+ /// <param name="deviceId">The device id.</param>
+ /// <param name="deviceName">Name of the device.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException"></exception>
+ public async Task OnPlaybackStopped(User user, BaseItem item, long? positionTicks, string clientType, string deviceId, string deviceName)
+ {
+ if (user == null)
+ {
+ throw new ArgumentNullException();
+ }
+ if (item == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ RemoveNowPlayingItemId(user, clientType, deviceId, deviceName, item);
+
+ var key = item.GetUserDataKey();
+
+ var data = await _userDataRepository.GetUserData(user.Id, key).ConfigureAwait(false);
+
+ if (positionTicks.HasValue)
+ {
+ UpdatePlayState(item, data, positionTicks.Value, true);
+ }
+ else
+ {
+ // If the client isn't able to report this, then we'll just have to make an assumption
+ data.PlayCount++;
+ data.Played = true;
+ }
+
+ await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false);
+
+ EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackProgressEventArgs
+ {
+ Item = item,
+ User = user,
+ PlaybackPositionTicks = positionTicks
+ }, _logger);
+ }
+
+ /// <summary>
+ /// Updates playstate position for an item but does not save
+ /// </summary>
+ /// <param name="item">The item</param>
+ /// <param name="data">User data for the item</param>
+ /// <param name="positionTicks">The current playback position</param>
+ /// <param name="incrementPlayCount">Whether or not to increment playcount</param>
+ private void UpdatePlayState(BaseItem item, UserItemData data, long positionTicks, bool incrementPlayCount)
+ {
+ // If a position has been reported, and if we know the duration
+ if (positionTicks > 0 && item.RunTimeTicks.HasValue && item.RunTimeTicks > 0)
+ {
+ var pctIn = Decimal.Divide(positionTicks, item.RunTimeTicks.Value) * 100;
+
+ // Don't track in very beginning
+ if (pctIn < _configurationManager.Configuration.MinResumePct)
+ {
+ positionTicks = 0;
+ incrementPlayCount = false;
+ }
+
+ // If we're at the end, assume completed
+ else if (pctIn > _configurationManager.Configuration.MaxResumePct || positionTicks >= item.RunTimeTicks.Value)
+ {
+ positionTicks = 0;
+ data.Played = true;
+ }
+
+ else
+ {
+ // Enforce MinResumeDuration
+ var durationSeconds = TimeSpan.FromTicks(item.RunTimeTicks.Value).TotalSeconds;
+
+ if (durationSeconds < _configurationManager.Configuration.MinResumeDurationSeconds)
+ {
+ positionTicks = 0;
+ data.Played = true;
+ }
+ }
+ }
+
+ if (item is Audio)
+ {
+ data.PlaybackPositionTicks = 0;
+ }
+
+ data.PlaybackPositionTicks = positionTicks;
+
+ if (incrementPlayCount)
+ {
+ data.PlayCount++;
+ data.LastPlayedDate = DateTime.UtcNow;
+ }
+ }
+
+ /// <summary>
+ /// Identifies the web socket.
+ /// </summary>
+ /// <param name="sessionId">The session id.</param>
+ /// <param name="webSocket">The web socket.</param>
+ public void IdentifyWebSocket(Guid sessionId, IWebSocketConnection webSocket)
+ {
+ _websocketConnections.AddOrUpdate(sessionId, webSocket, (key, existing) => webSocket);
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
new file mode 100644
index 000000000..7ce074cd7
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -0,0 +1,58 @@
+using System.Linq;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Session;
+using System;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Session
+{
+ /// <summary>
+ /// Class SessionWebSocketListener
+ /// </summary>
+ public class SessionWebSocketListener : IWebSocketListener
+ {
+ /// <summary>
+ /// The _true task result
+ /// </summary>
+ private readonly Task _trueTaskResult = Task.FromResult(true);
+
+ /// <summary>
+ /// The _session manager
+ /// </summary>
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SessionWebSocketListener"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public SessionWebSocketListener(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <summary>
+ /// Processes the message.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>Task.</returns>
+ public Task ProcessMessage(WebSocketMessageInfo message)
+ {
+ if (string.Equals(message.MessageType, "Identify", StringComparison.OrdinalIgnoreCase))
+ {
+ var vals = message.Data.Split('|');
+
+ var deviceId = vals[0];
+ var client = vals[1];
+
+ var session = _sessionManager.AllConnections.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId) && string.Equals(i.Client, client));
+
+ if (session != null)
+ {
+ ((SessionManager)_sessionManager).IdentifyWebSocket(session.Id, message.Connection);
+ }
+ }
+
+ return _trueTaskResult;
+ }
+ }
+}
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index 37e6d1b8d..a35ac44ea 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -20,6 +20,7 @@ using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
+using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Controller.Updates;
using MediaBrowser.Controller.Weather;
@@ -37,6 +38,7 @@ using MediaBrowser.Server.Implementations.Library;
using MediaBrowser.Server.Implementations.MediaEncoder;
using MediaBrowser.Server.Implementations.Providers;
using MediaBrowser.Server.Implementations.ServerManager;
+using MediaBrowser.Server.Implementations.Session;
using MediaBrowser.Server.Implementations.Sqlite;
using MediaBrowser.Server.Implementations.Udp;
using MediaBrowser.Server.Implementations.Updates;
@@ -251,7 +253,7 @@ namespace MediaBrowser.ServerApplication
ItemRepository = new SQLiteItemRepository(ApplicationPaths, JsonSerializer, LogManager);
RegisterSingleInstance(ItemRepository);
- UserManager = new UserManager(Logger, ServerConfigurationManager, UserDataRepository);
+ UserManager = new UserManager(Logger, ServerConfigurationManager);
RegisterSingleInstance(UserManager);
LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataRepository);
@@ -274,6 +276,9 @@ namespace MediaBrowser.ServerApplication
MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), ZipClient, ApplicationPaths, JsonSerializer);
RegisterSingleInstance(MediaEncoder);
+ var clientConnectionManager = new SessionManager(UserDataRepository, ServerConfigurationManager, Logger, UserRepository);
+ RegisterSingleInstance<ISessionManager>(clientConnectionManager);
+
HttpServer = await _httpServerCreationTask.ConfigureAwait(false);
RegisterSingleInstance(HttpServer, false);
diff --git a/MediaBrowser.WebDashboard/Api/DashboardInfo.cs b/MediaBrowser.WebDashboard/Api/DashboardInfo.cs
index 31f0f4728..500464ed8 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardInfo.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardInfo.cs
@@ -1,5 +1,5 @@
-using MediaBrowser.Model.Connectivity;
-using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Session;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Tasks;
using System;
@@ -33,7 +33,7 @@ namespace MediaBrowser.WebDashboard.Api
/// Gets or sets the active connections.
/// </summary>
/// <value>The active connections.</value>
- public ClientConnectionInfo[] ActiveConnections { get; set; }
+ public SessionInfo[] ActiveConnections { get; set; }
/// <summary>
/// Gets or sets the users.
diff --git a/MediaBrowser.WebDashboard/Api/DashboardInfoWebSocketListener.cs b/MediaBrowser.WebDashboard/Api/DashboardInfoWebSocketListener.cs
index 1c4037179..299ba4682 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardInfoWebSocketListener.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardInfoWebSocketListener.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
using System.ComponentModel.Composition;
using System.Threading.Tasks;
@@ -36,6 +37,7 @@ namespace MediaBrowser.WebDashboard.Api
/// </summary>
private readonly IUserManager _userManager;
private readonly ILibraryManager _libraryManager;
+ private readonly ISessionManager _sessionManager;
/// <summary>
/// Initializes a new instance of the <see cref="DashboardInfoWebSocketListener" /> class.
@@ -45,13 +47,14 @@ namespace MediaBrowser.WebDashboard.Api
/// <param name="taskManager">The task manager.</param>
/// <param name="userManager">The user manager.</param>
/// <param name="libraryManager">The library manager.</param>
- public DashboardInfoWebSocketListener(IServerApplicationHost appHost, ILogger logger, ITaskManager taskManager, IUserManager userManager, ILibraryManager libraryManager)
+ public DashboardInfoWebSocketListener(IServerApplicationHost appHost, ILogger logger, ITaskManager taskManager, IUserManager userManager, ILibraryManager libraryManager, ISessionManager sessionManager)
: base(logger)
{
_appHost = appHost;
_taskManager = taskManager;
_userManager = userManager;
_libraryManager = libraryManager;
+ _sessionManager = sessionManager;
}
/// <summary>
@@ -61,7 +64,7 @@ namespace MediaBrowser.WebDashboard.Api
/// <returns>Task{IEnumerable{TaskInfo}}.</returns>
protected override Task<DashboardInfo> GetDataToSend(object state)
{
- return DashboardService.GetDashboardInfo(_appHost, Logger, _taskManager, _userManager, _libraryManager);
+ return DashboardService.GetDashboardInfo(_appHost, Logger, _taskManager, _userManager, _libraryManager, _sessionManager);
}
}
}
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index ab03ca358..b33c152f1 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -7,6 +7,7 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Tasks;
using ServiceStack.ServiceHost;
@@ -127,6 +128,8 @@ namespace MediaBrowser.WebDashboard.Api
/// </summary>
private readonly IServerConfigurationManager _serverConfigurationManager;
+ private readonly ISessionManager _sessionManager;
+
/// <summary>
/// Initializes a new instance of the <see cref="DashboardService" /> class.
/// </summary>
@@ -135,13 +138,14 @@ namespace MediaBrowser.WebDashboard.Api
/// <param name="appHost">The app host.</param>
/// <param name="libraryManager">The library manager.</param>
/// <param name="serverConfigurationManager">The server configuration manager.</param>
- public DashboardService(ITaskManager taskManager, IUserManager userManager, IServerApplicationHost appHost, ILibraryManager libraryManager, IServerConfigurationManager serverConfigurationManager)
+ public DashboardService(ITaskManager taskManager, IUserManager userManager, IServerApplicationHost appHost, ILibraryManager libraryManager, IServerConfigurationManager serverConfigurationManager, ISessionManager sessionManager)
{
_taskManager = taskManager;
_userManager = userManager;
_appHost = appHost;
_libraryManager = libraryManager;
_serverConfigurationManager = serverConfigurationManager;
+ _sessionManager = sessionManager;
}
/// <summary>
@@ -180,7 +184,7 @@ namespace MediaBrowser.WebDashboard.Api
/// <returns>System.Object.</returns>
public object Get(GetDashboardInfo request)
{
- var result = GetDashboardInfo(_appHost, Logger, _taskManager, _userManager, _libraryManager).Result;
+ var result = GetDashboardInfo(_appHost, Logger, _taskManager, _userManager, _libraryManager, _sessionManager).Result;
return ResultFactory.GetOptimizedResult(RequestContext, result);
}
@@ -193,10 +197,16 @@ namespace MediaBrowser.WebDashboard.Api
/// <param name="taskManager">The task manager.</param>
/// <param name="userManager">The user manager.</param>
/// <param name="libraryManager">The library manager.</param>
+ /// <param name="connectionManager">The connection manager.</param>
/// <returns>DashboardInfo.</returns>
- public static async Task<DashboardInfo> GetDashboardInfo(IServerApplicationHost appHost, ILogger logger, ITaskManager taskManager, IUserManager userManager, ILibraryManager libraryManager)
+ public static async Task<DashboardInfo> GetDashboardInfo(IServerApplicationHost appHost,
+ ILogger logger,
+ ITaskManager taskManager,
+ IUserManager userManager,
+ ILibraryManager libraryManager,
+ ISessionManager connectionManager)
{
- var connections = userManager.RecentConnections.ToArray();
+ var connections = connectionManager.RecentConnections.ToArray();
var dtoBuilder = new UserDtoBuilder(logger);
diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js
index ae97e86f1..dead882b9 100644
--- a/MediaBrowser.WebDashboard/ApiClient.js
+++ b/MediaBrowser.WebDashboard/ApiClient.js
@@ -159,6 +159,9 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
webSocket.onopen = function () {
setTimeout(function () {
+
+ self.sendWebSocketMessage("Identify", deviceId + "|" + clientName);
+
$(self).trigger("websocketopen");
}, 500);
};
diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config
index d721e1dc7..01c9cc108 100644
--- a/MediaBrowser.WebDashboard/packages.config
+++ b/MediaBrowser.WebDashboard/packages.config
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="MediaBrowser.ApiClient.Javascript" version="3.0.99" targetFramework="net45" />
+ <package id="MediaBrowser.ApiClient.Javascript" version="3.0.100" targetFramework="net45" />
<package id="ServiceStack.Common" version="3.9.44" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.44" targetFramework="net45" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.sln b/MediaBrowser.sln
index f9f5e9436..eb3251f74 100644
--- a/MediaBrowser.sln
+++ b/MediaBrowser.sln
@@ -173,4 +173,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(Performance) = preSolution
+ HasPerformanceSessions = true
+ EndGlobalSection
EndGlobal