diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 72e164b521..6bcbe3ceba 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -1,7 +1,5 @@
#nullable disable
-#pragma warning disable CS1591
-
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -68,13 +66,29 @@ namespace Emby.Server.Implementations.Session
private Timer _inactiveTimer;
private DtoOptions _itemInfoDtoOptions;
- private bool _disposed = false;
+ private bool _disposed;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Instance of interface.
+ /// Instance of interface.
+ /// Instance of interface.
+ /// Instance of interface.
+ /// Instance of interface.
+ /// Instance of interface.
+ /// Instance of interface.
+ /// Instance of interface.
+ /// Instance of interface.
+ /// Instance of interface.
+ /// Instance of interface.
+ /// Instance of interface.
+ /// Instance of interface.
public SessionManager(
ILogger logger,
IEventManager eventManager,
IUserDataManager userDataManager,
- IServerConfigurationManager config,
+ IServerConfigurationManager serverConfigurationManager,
ILibraryManager libraryManager,
IUserManager userManager,
IMusicManager musicManager,
@@ -88,7 +102,7 @@ namespace Emby.Server.Implementations.Session
_logger = logger;
_eventManager = eventManager;
_userDataManager = userDataManager;
- _config = config;
+ _config = serverConfigurationManager;
_libraryManager = libraryManager;
_userManager = userManager;
_musicManager = musicManager;
@@ -508,7 +522,10 @@ namespace Emby.Server.Implementations.Session
deviceName = "Network Device";
}
- var deviceOptions = _deviceManager.GetDeviceOptions(deviceId);
+ var deviceOptions = _deviceManager.GetDeviceOptions(deviceId) ?? new()
+ {
+ DeviceId = deviceId
+ };
if (string.IsNullOrEmpty(deviceOptions.CustomName))
{
sessionInfo.DeviceName = deviceName;
@@ -1076,6 +1093,42 @@ namespace Emby.Server.Implementations.Session
return session;
}
+ private SessionInfoDto ToSessionInfoDto(SessionInfo sessionInfo)
+ {
+ return new SessionInfoDto
+ {
+ PlayState = sessionInfo.PlayState,
+ AdditionalUsers = sessionInfo.AdditionalUsers,
+ Capabilities = _deviceManager.ToClientCapabilitiesDto(sessionInfo.Capabilities),
+ RemoteEndPoint = sessionInfo.RemoteEndPoint,
+ PlayableMediaTypes = sessionInfo.PlayableMediaTypes,
+ Id = sessionInfo.Id,
+ UserId = sessionInfo.UserId,
+ UserName = sessionInfo.UserName,
+ Client = sessionInfo.Client,
+ LastActivityDate = sessionInfo.LastActivityDate,
+ LastPlaybackCheckIn = sessionInfo.LastPlaybackCheckIn,
+ LastPausedDate = sessionInfo.LastPausedDate,
+ DeviceName = sessionInfo.DeviceName,
+ DeviceType = sessionInfo.DeviceType,
+ NowPlayingItem = sessionInfo.NowPlayingItem,
+ NowViewingItem = sessionInfo.NowViewingItem,
+ DeviceId = sessionInfo.DeviceId,
+ ApplicationVersion = sessionInfo.ApplicationVersion,
+ TranscodingInfo = sessionInfo.TranscodingInfo,
+ IsActive = sessionInfo.IsActive,
+ SupportsMediaControl = sessionInfo.SupportsMediaControl,
+ SupportsRemoteControl = sessionInfo.SupportsRemoteControl,
+ NowPlayingQueue = sessionInfo.NowPlayingQueue,
+ NowPlayingQueueFullItems = sessionInfo.NowPlayingQueueFullItems,
+ HasCustomDeviceName = sessionInfo.HasCustomDeviceName,
+ PlaylistItemId = sessionInfo.PlaylistItemId,
+ ServerId = sessionInfo.ServerId,
+ UserPrimaryImageTag = sessionInfo.UserPrimaryImageTag,
+ SupportedCommands = sessionInfo.SupportedCommands
+ };
+ }
+
///
public Task SendMessageCommand(string controllingSessionId, string sessionId, MessageCommand command, CancellationToken cancellationToken)
{
@@ -1393,7 +1446,7 @@ namespace Emby.Server.Implementations.Session
UserName = user.Username
};
- session.AdditionalUsers = [..session.AdditionalUsers, newUser];
+ session.AdditionalUsers = [.. session.AdditionalUsers, newUser];
}
}
@@ -1505,7 +1558,7 @@ namespace Emby.Server.Implementations.Session
var returnResult = new AuthenticationResult
{
User = _userManager.GetUserDto(user, request.RemoteEndPoint),
- SessionInfo = session,
+ SessionInfo = ToSessionInfoDto(session),
AccessToken = token,
ServerId = _appHost.SystemId
};
@@ -1800,6 +1853,74 @@ namespace Emby.Server.Implementations.Session
return await GetSessionByAuthenticationToken(items[0], deviceId, remoteEndpoint, null).ConfigureAwait(false);
}
+ ///
+ public IReadOnlyList GetSessions(
+ Guid userId,
+ string deviceId,
+ int? activeWithinSeconds,
+ Guid? controllableUserToCheck)
+ {
+ var result = Sessions;
+ var user = _userManager.GetUserById(userId);
+ if (!string.IsNullOrEmpty(deviceId))
+ {
+ result = result.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase));
+ }
+
+ if (!controllableUserToCheck.IsNullOrEmpty())
+ {
+ result = result.Where(i => i.SupportsRemoteControl);
+
+ var controlledUser = _userManager.GetUserById(controllableUserToCheck.Value);
+ if (controlledUser is null)
+ {
+ return [];
+ }
+
+ if (!controlledUser.HasPermission(PermissionKind.EnableSharedDeviceControl))
+ {
+ // Controlled user has device sharing disabled
+ result = result.Where(i => !i.UserId.IsEmpty());
+ }
+
+ if (!user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers))
+ {
+ // User cannot control other user's sessions, validate user id.
+ result = result.Where(i => i.UserId.IsEmpty() || i.ContainsUser(controllableUserToCheck.Value));
+ }
+
+ result = result.Where(i =>
+ {
+ if (!string.IsNullOrWhiteSpace(i.DeviceId) && !_deviceManager.CanAccessDevice(user, i.DeviceId))
+ {
+ return false;
+ }
+
+ return true;
+ });
+ }
+ else if (!user.HasPermission(PermissionKind.IsAdministrator))
+ {
+ // Request isn't from administrator, limit to "own" sessions.
+ result = result.Where(i => i.UserId.IsEmpty() || i.ContainsUser(userId));
+
+ // Don't report acceleration type for non-admin users.
+ result = result.Select(r =>
+ {
+ r.TranscodingInfo.HardwareAccelerationType = HardwareAccelerationType.none;
+ return r;
+ });
+ }
+
+ if (activeWithinSeconds.HasValue && activeWithinSeconds.Value > 0)
+ {
+ var minActiveDate = DateTime.UtcNow.AddSeconds(0 - activeWithinSeconds.Value);
+ result = result.Where(i => i.LastActivityDate >= minActiveDate);
+ }
+
+ return result.Select(ToSessionInfoDto).ToList();
+ }
+
///
public Task SendMessageToAdminSessions(SessionMessageType name, T data, CancellationToken cancellationToken)
{
diff --git a/Jellyfin.Api/Controllers/DevicesController.cs b/Jellyfin.Api/Controllers/DevicesController.cs
index 2a2ab4ad16..50050262f0 100644
--- a/Jellyfin.Api/Controllers/DevicesController.cs
+++ b/Jellyfin.Api/Controllers/DevicesController.cs
@@ -1,15 +1,13 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
-using Jellyfin.Api.Constants;
using Jellyfin.Api.Helpers;
using Jellyfin.Data.Dtos;
-using Jellyfin.Data.Entities.Security;
using Jellyfin.Data.Queries;
using MediaBrowser.Common.Api;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Devices;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
@@ -47,7 +45,7 @@ public class DevicesController : BaseJellyfinApiController
/// An containing the list of devices.
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
- public ActionResult> GetDevices([FromQuery] Guid? userId)
+ public ActionResult> GetDevices([FromQuery] Guid? userId)
{
userId = RequestHelpers.GetUserId(User, userId);
return _deviceManager.GetDevicesForUser(userId);
@@ -63,7 +61,7 @@ public class DevicesController : BaseJellyfinApiController
[HttpGet("Info")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult GetDeviceInfo([FromQuery, Required] string id)
+ public ActionResult GetDeviceInfo([FromQuery, Required] string id)
{
var deviceInfo = _deviceManager.GetDevice(id);
if (deviceInfo is null)
@@ -84,7 +82,7 @@ public class DevicesController : BaseJellyfinApiController
[HttpGet("Options")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult GetDeviceOptions([FromQuery, Required] string id)
+ public ActionResult GetDeviceOptions([FromQuery, Required] string id)
{
var deviceInfo = _deviceManager.GetDeviceOptions(id);
if (deviceInfo is null)
diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs
index 942bdeb9e8..91a879b8ed 100644
--- a/Jellyfin.Api/Controllers/SessionController.cs
+++ b/Jellyfin.Api/Controllers/SessionController.cs
@@ -1,18 +1,13 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
-using Jellyfin.Api.Models.SessionDtos;
using Jellyfin.Data.Enums;
-using Jellyfin.Extensions;
using MediaBrowser.Common.Api;
-using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
@@ -32,22 +27,18 @@ public class SessionController : BaseJellyfinApiController
{
private readonly ISessionManager _sessionManager;
private readonly IUserManager _userManager;
- private readonly IDeviceManager _deviceManager;
///
/// Initializes a new instance of the class.
///
/// Instance of interface.
/// Instance of interface.
- /// Instance of interface.
public SessionController(
ISessionManager sessionManager,
- IUserManager userManager,
- IDeviceManager deviceManager)
+ IUserManager userManager)
{
_sessionManager = sessionManager;
_userManager = userManager;
- _deviceManager = deviceManager;
}
///
@@ -57,77 +48,25 @@ public class SessionController : BaseJellyfinApiController
/// Filter by device Id.
/// Optional. Filter by sessions that were active in the last n seconds.
/// List of sessions returned.
- /// An with the available sessions.
+ /// An with the available sessions.
[HttpGet("Sessions")]
[Authorize]
[ProducesResponseType(StatusCodes.Status200OK)]
- public ActionResult> GetSessions(
+ public ActionResult> GetSessions(
[FromQuery] Guid? controllableByUserId,
[FromQuery] string? deviceId,
[FromQuery] int? activeWithinSeconds)
{
- var result = _sessionManager.Sessions;
- var isRequestingFromAdmin = User.IsInRole(UserRoles.Administrator);
+ Guid? controllableUserToCheck = controllableByUserId is null ? null : RequestHelpers.GetUserId(User, controllableByUserId);
+ var result = _sessionManager.GetSessions(
+ User.GetUserId(),
+ deviceId,
+ activeWithinSeconds,
+ controllableUserToCheck);
- if (!string.IsNullOrEmpty(deviceId))
+ if (result.Count == 0)
{
- result = result.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase));
- }
-
- if (!controllableByUserId.IsNullOrEmpty())
- {
- result = result.Where(i => i.SupportsRemoteControl);
-
- var user = _userManager.GetUserById(controllableByUserId.Value);
- if (user is null)
- {
- return NotFound();
- }
-
- if (!user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers))
- {
- // User cannot control other user's sessions, validate user id.
- result = result.Where(i => i.UserId.IsEmpty() || i.ContainsUser(RequestHelpers.GetUserId(User, controllableByUserId)));
- }
-
- if (!user.HasPermission(PermissionKind.EnableSharedDeviceControl))
- {
- result = result.Where(i => !i.UserId.IsEmpty());
- }
-
- result = result.Where(i =>
- {
- if (!string.IsNullOrWhiteSpace(i.DeviceId))
- {
- if (!_deviceManager.CanAccessDevice(user, i.DeviceId))
- {
- return false;
- }
- }
-
- return true;
- });
- }
- else if (!isRequestingFromAdmin)
- {
- // Request isn't from administrator, limit to "own" sessions.
- result = result.Where(i => i.UserId.IsEmpty() || i.ContainsUser(User.GetUserId()));
- }
-
- if (activeWithinSeconds.HasValue && activeWithinSeconds.Value > 0)
- {
- var minActiveDate = DateTime.UtcNow.AddSeconds(0 - activeWithinSeconds.Value);
- result = result.Where(i => i.LastActivityDate >= minActiveDate);
- }
-
- // Request isn't from administrator, don't report acceleration type.
- if (!isRequestingFromAdmin)
- {
- result = result.Select(r =>
- {
- r.TranscodingInfo.HardwareAccelerationType = HardwareAccelerationType.none;
- return r;
- });
+ return NotFound();
}
return Ok(result);
diff --git a/Jellyfin.Data/Dtos/DeviceOptionsDto.cs b/Jellyfin.Data/Dtos/DeviceOptionsDto.cs
index 392ef5ff4e..aad5787097 100644
--- a/Jellyfin.Data/Dtos/DeviceOptionsDto.cs
+++ b/Jellyfin.Data/Dtos/DeviceOptionsDto.cs
@@ -1,23 +1,22 @@
-namespace Jellyfin.Data.Dtos
+namespace Jellyfin.Data.Dtos;
+
+///
+/// A dto representing custom options for a device.
+///
+public class DeviceOptionsDto
{
///
- /// A dto representing custom options for a device.
+ /// Gets or sets the id.
///
- public class DeviceOptionsDto
- {
- ///
- /// Gets or sets the id.
- ///
- public int Id { get; set; }
+ public int Id { get; set; }
- ///
- /// Gets or sets the device id.
- ///
- public string? DeviceId { get; set; }
+ ///
+ /// Gets or sets the device id.
+ ///
+ public string? DeviceId { get; set; }
- ///
- /// Gets or sets the custom name.
- ///
- public string? CustomName { get; set; }
- }
+ ///
+ /// Gets or sets the custom name.
+ ///
+ public string? CustomName { get; set; }
}
diff --git a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs
index 415c04bbf1..d3bff2936c 100644
--- a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs
+++ b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs
@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Jellyfin.Data.Dtos;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Entities.Security;
using Jellyfin.Data.Enums;
@@ -13,6 +14,7 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Devices;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session;
using Microsoft.EntityFrameworkCore;
@@ -68,7 +70,7 @@ namespace Jellyfin.Server.Implementations.Devices
}
///
- public async Task UpdateDeviceOptions(string deviceId, string deviceName)
+ public async Task UpdateDeviceOptions(string deviceId, string? deviceName)
{
DeviceOptions? deviceOptions;
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
@@ -105,29 +107,37 @@ namespace Jellyfin.Server.Implementations.Devices
}
///
- public DeviceOptions GetDeviceOptions(string deviceId)
+ public DeviceOptionsDto? GetDeviceOptions(string deviceId)
{
- _deviceOptions.TryGetValue(deviceId, out var deviceOptions);
+ if (_deviceOptions.TryGetValue(deviceId, out var deviceOptions))
+ {
+ return ToDeviceOptionsDto(deviceOptions);
+ }
- return deviceOptions ?? new DeviceOptions(deviceId);
+ return null;
}
///
- public ClientCapabilities GetCapabilities(string deviceId)
+ public ClientCapabilities GetCapabilities(string? deviceId)
{
+ if (deviceId is null)
+ {
+ return new();
+ }
+
return _capabilitiesMap.TryGetValue(deviceId, out ClientCapabilities? result)
? result
- : new ClientCapabilities();
+ : new();
}
///
- public DeviceInfo? GetDevice(string id)
+ public DeviceInfoDto? GetDevice(string id)
{
var device = _devices.Values.Where(d => d.DeviceId == id).OrderByDescending(d => d.DateLastActivity).FirstOrDefault();
_deviceOptions.TryGetValue(id, out var deviceOption);
var deviceInfo = device is null ? null : ToDeviceInfo(device, deviceOption);
- return deviceInfo;
+ return deviceInfo is null ? null : ToDeviceInfoDto(deviceInfo);
}
///
@@ -166,7 +176,7 @@ namespace Jellyfin.Server.Implementations.Devices
}
///
- public QueryResult GetDevicesForUser(Guid? userId)
+ public QueryResult GetDevicesForUser(Guid? userId)
{
IEnumerable devices = _devices.Values
.OrderByDescending(d => d.DateLastActivity)
@@ -187,9 +197,11 @@ namespace Jellyfin.Server.Implementations.Devices
{
_deviceOptions.TryGetValue(device.DeviceId, out var option);
return ToDeviceInfo(device, option);
- }).ToArray();
+ })
+ .Select(ToDeviceInfoDto)
+ .ToArray();
- return new QueryResult(array);
+ return new QueryResult(array);
}
///
@@ -235,13 +247,9 @@ namespace Jellyfin.Server.Implementations.Devices
private DeviceInfo ToDeviceInfo(Device authInfo, DeviceOptions? options = null)
{
var caps = GetCapabilities(authInfo.DeviceId);
- var user = _userManager.GetUserById(authInfo.UserId);
- if (user is null)
- {
- throw new ResourceNotFoundException("User with UserId " + authInfo.UserId + " not found");
- }
+ var user = _userManager.GetUserById(authInfo.UserId) ?? throw new ResourceNotFoundException("User with UserId " + authInfo.UserId + " not found");
- return new DeviceInfo
+ return new()
{
AppName = authInfo.AppName,
AppVersion = authInfo.AppVersion,
@@ -254,5 +262,48 @@ namespace Jellyfin.Server.Implementations.Devices
CustomName = options?.CustomName,
};
}
+
+ private DeviceOptionsDto ToDeviceOptionsDto(DeviceOptions options)
+ {
+ return new()
+ {
+ Id = options.Id,
+ DeviceId = options.DeviceId,
+ CustomName = options.CustomName,
+ };
+ }
+
+ private DeviceInfoDto ToDeviceInfoDto(DeviceInfo info)
+ {
+ return new()
+ {
+ Name = info.Name,
+ CustomName = info.CustomName,
+ AccessToken = info.AccessToken,
+ Id = info.Id,
+ LastUserName = info.LastUserName,
+ AppName = info.AppName,
+ AppVersion = info.AppVersion,
+ LastUserId = info.LastUserId,
+ DateLastActivity = info.DateLastActivity,
+ Capabilities = ToClientCapabilitiesDto(info.Capabilities),
+ IconUrl = info.IconUrl
+ };
+ }
+
+ ///
+ public ClientCapabilitiesDto ToClientCapabilitiesDto(ClientCapabilities capabilities)
+ {
+ return new()
+ {
+ PlayableMediaTypes = capabilities.PlayableMediaTypes,
+ SupportedCommands = capabilities.SupportedCommands,
+ SupportsMediaControl = capabilities.SupportsMediaControl,
+ SupportsPersistentIdentifier = capabilities.SupportsPersistentIdentifier,
+ DeviceProfile = capabilities.DeviceProfile,
+ AppStoreUrl = capabilities.AppStoreUrl,
+ IconUrl = capabilities.IconUrl
+ };
+ }
}
}
diff --git a/MediaBrowser.Controller/Authentication/AuthenticationResult.cs b/MediaBrowser.Controller/Authentication/AuthenticationResult.cs
index 635e4eb3d7..daf4d96313 100644
--- a/MediaBrowser.Controller/Authentication/AuthenticationResult.cs
+++ b/MediaBrowser.Controller/Authentication/AuthenticationResult.cs
@@ -1,20 +1,31 @@
#nullable disable
-#pragma warning disable CS1591
-
-using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
-namespace MediaBrowser.Controller.Authentication
+namespace MediaBrowser.Controller.Authentication;
+
+///
+/// A class representing an authentication result.
+///
+public class AuthenticationResult
{
- public class AuthenticationResult
- {
- public UserDto User { get; set; }
+ ///
+ /// Gets or sets the user.
+ ///
+ public UserDto User { get; set; }
- public SessionInfo SessionInfo { get; set; }
+ ///
+ /// Gets or sets the session info.
+ ///
+ public SessionInfoDto SessionInfo { get; set; }
- public string AccessToken { get; set; }
+ ///
+ /// Gets or sets the access token.
+ ///
+ public string AccessToken { get; set; }
- public string ServerId { get; set; }
- }
+ ///
+ /// Gets or sets the server id.
+ ///
+ public string ServerId { get; set; }
}
diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs
index 5566421cbe..cade53d994 100644
--- a/MediaBrowser.Controller/Devices/IDeviceManager.cs
+++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs
@@ -1,81 +1,117 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
using System;
using System.Threading.Tasks;
+using Jellyfin.Data.Dtos;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Entities.Security;
using Jellyfin.Data.Events;
using Jellyfin.Data.Queries;
using MediaBrowser.Model.Devices;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session;
-namespace MediaBrowser.Controller.Devices
+namespace MediaBrowser.Controller.Devices;
+
+///
+/// Device manager interface.
+///
+public interface IDeviceManager
{
- public interface IDeviceManager
- {
- event EventHandler>> DeviceOptionsUpdated;
+ ///
+ /// Event handler for updated device options.
+ ///
+ event EventHandler>> DeviceOptionsUpdated;
- ///
- /// Creates a new device.
- ///
- /// The device to create.
- /// A representing the creation of the device.
- Task CreateDevice(Device device);
+ ///
+ /// Creates a new device.
+ ///
+ /// The device to create.
+ /// A representing the creation of the device.
+ Task CreateDevice(Device device);
- ///
- /// Saves the capabilities.
- ///
- /// The device id.
- /// The capabilities.
- void SaveCapabilities(string deviceId, ClientCapabilities capabilities);
+ ///
+ /// Saves the capabilities.
+ ///
+ /// The device id.
+ /// The capabilities.
+ void SaveCapabilities(string deviceId, ClientCapabilities capabilities);
- ///
- /// Gets the capabilities.
- ///
- /// The device id.
- /// ClientCapabilities.
- ClientCapabilities GetCapabilities(string deviceId);
+ ///
+ /// Gets the capabilities.
+ ///
+ /// The device id.
+ /// ClientCapabilities.
+ ClientCapabilities GetCapabilities(string? deviceId);
- ///
- /// Gets the device information.
- ///
- /// The identifier.
- /// DeviceInfo.
- DeviceInfo GetDevice(string id);
+ ///
+ /// Gets the device information.
+ ///
+ /// The identifier.
+ /// DeviceInfoDto.
+ DeviceInfoDto? GetDevice(string id);
- ///
- /// Gets devices based on the provided query.
- ///
- /// The device query.
- /// A representing the retrieval of the devices.
- QueryResult GetDevices(DeviceQuery query);
+ ///
+ /// Gets devices based on the provided query.
+ ///
+ /// The device query.
+ /// A representing the retrieval of the devices.
+ QueryResult GetDevices(DeviceQuery query);
- QueryResult GetDeviceInfos(DeviceQuery query);
+ ///
+ /// Gets device infromation based on the provided query.
+ ///
+ /// The device query.
+ /// A representing the retrieval of the device information.
+ QueryResult GetDeviceInfos(DeviceQuery query);
- ///
- /// Gets the devices.
- ///
- /// The user's id, or null.
- /// IEnumerable<DeviceInfo>.
- QueryResult GetDevicesForUser(Guid? userId);
+ ///
+ /// Gets the device information.
+ ///
+ /// The user's id, or null.
+ /// IEnumerable<DeviceInfoDto>.
+ QueryResult GetDevicesForUser(Guid? userId);
- Task DeleteDevice(Device device);
+ ///
+ /// Deletes a device.
+ ///
+ /// The device.
+ /// A representing the deletion of the device.
+ Task DeleteDevice(Device device);
- Task UpdateDevice(Device device);
+ ///
+ /// Updates a device.
+ ///
+ /// The device.
+ /// A representing the update of the device.
+ Task UpdateDevice(Device device);
- ///
- /// Determines whether this instance [can access device] the specified user identifier.
- ///
- /// The user to test.
- /// The device id to test.
- /// Whether the user can access the device.
- bool CanAccessDevice(User user, string deviceId);
+ ///
+ /// Determines whether this instance [can access device] the specified user identifier.
+ ///
+ /// The user to test.
+ /// The device id to test.
+ /// Whether the user can access the device.
+ bool CanAccessDevice(User user, string deviceId);
- Task UpdateDeviceOptions(string deviceId, string deviceName);
+ ///
+ /// Updates the options of a device.
+ ///
+ /// The device id.
+ /// The device name.
+ /// A representing the update of the device options.
+ Task UpdateDeviceOptions(string deviceId, string? deviceName);
- DeviceOptions GetDeviceOptions(string deviceId);
- }
+ ///
+ /// Gets the options of a device.
+ ///
+ /// The device id.
+ /// of the device.
+ DeviceOptionsDto? GetDeviceOptions(string deviceId);
+
+ ///
+ /// Gets the dto for client capabilites.
+ ///
+ /// The client capabilities.
+ /// of the device.
+ ClientCapabilitiesDto ToClientCapabilitiesDto(ClientCapabilities capabilities);
}
diff --git a/MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs b/MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs
index 357ef9406d..1542c58b35 100644
--- a/MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs
+++ b/MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs
@@ -1,6 +1,5 @@
using System;
using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Controller.Events.Authentication;
@@ -29,7 +28,7 @@ public class AuthenticationResultEventArgs : EventArgs
///
/// Gets or sets the session information.
///
- public SessionInfo? SessionInfo { get; set; }
+ public SessionInfoDto? SessionInfo { get; set; }
///
/// Gets or sets the server id.
diff --git a/MediaBrowser.Controller/Net/WebSocketMessages/Outbound/SessionsMessage.cs b/MediaBrowser.Controller/Net/WebSocketMessages/Outbound/SessionsMessage.cs
index 3504831b87..8330745418 100644
--- a/MediaBrowser.Controller/Net/WebSocketMessages/Outbound/SessionsMessage.cs
+++ b/MediaBrowser.Controller/Net/WebSocketMessages/Outbound/SessionsMessage.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
@@ -8,13 +9,13 @@ namespace MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
///
/// Sessions message.
///
-public class SessionsMessage : OutboundWebSocketMessage>
+public class SessionsMessage : OutboundWebSocketMessage>
{
///
/// Initializes a new instance of the class.
///
/// Session info.
- public SessionsMessage(IReadOnlyList data)
+ public SessionsMessage(IReadOnlyList data)
: base(data)
{
}
diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs
index 5a47236f92..f2e98dd787 100644
--- a/MediaBrowser.Controller/Session/ISessionManager.cs
+++ b/MediaBrowser.Controller/Session/ISessionManager.cs
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using Jellyfin.Data.Entities.Security;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.SyncPlay;
@@ -292,6 +293,16 @@ namespace MediaBrowser.Controller.Session
/// SessionInfo.
SessionInfo GetSession(string deviceId, string client, string version);
+ ///
+ /// Gets all sessions available to a user.
+ ///
+ /// The session identifier.
+ /// The device id.
+ /// Active within session limit.
+ /// Filter for sessions remote controllable for this user.
+ /// IReadOnlyList{SessionInfoDto}.
+ IReadOnlyList GetSessions(Guid userId, string deviceId, int? activeWithinSeconds, Guid? controllableUserToCheck);
+
///
/// Gets the session by authentication token.
///
diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs
index 9e33588187..3ba1bfce42 100644
--- a/MediaBrowser.Controller/Session/SessionInfo.cs
+++ b/MediaBrowser.Controller/Session/SessionInfo.cs
@@ -1,7 +1,5 @@
#nullable disable
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
using System.Linq;
@@ -27,28 +25,45 @@ namespace MediaBrowser.Controller.Session
private readonly ISessionManager _sessionManager;
private readonly ILogger _logger;
- private readonly object _progressLock = new object();
+ private readonly object _progressLock = new();
private Timer _progressTimer;
private PlaybackProgressInfo _lastProgressInfo;
- private bool _disposed = false;
+ private bool _disposed;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Instance of interface.
+ /// Instance of interface.
public SessionInfo(ISessionManager sessionManager, ILogger logger)
{
_sessionManager = sessionManager;
_logger = logger;
- AdditionalUsers = Array.Empty();
+ AdditionalUsers = [];
PlayState = new PlayerStateInfo();
- SessionControllers = Array.Empty();
- NowPlayingQueue = Array.Empty();
- NowPlayingQueueFullItems = Array.Empty();
+ SessionControllers = [];
+ NowPlayingQueue = [];
+ NowPlayingQueueFullItems = [];
}
+ ///
+ /// Gets or sets the play state.
+ ///
+ /// The play state.
public PlayerStateInfo PlayState { get; set; }
- public SessionUserInfo[] AdditionalUsers { get; set; }
+ ///
+ /// Gets or sets the additional users.
+ ///
+ /// The additional users.
+ public IReadOnlyList AdditionalUsers { get; set; }
+ ///
+ /// Gets or sets the client capabilities.
+ ///
+ /// The client capabilities.
public ClientCapabilities Capabilities { get; set; }
///
@@ -67,7 +82,7 @@ namespace MediaBrowser.Controller.Session
{
if (Capabilities is null)
{
- return Array.Empty();
+ return [];
}
return Capabilities.PlayableMediaTypes;
@@ -134,9 +149,17 @@ namespace MediaBrowser.Controller.Session
/// The now playing item.
public BaseItemDto NowPlayingItem { get; set; }
+ ///
+ /// Gets or sets the now playing queue full items.
+ ///
+ /// The now playing queue full items.
[JsonIgnore]
public BaseItem FullNowPlayingItem { get; set; }
+ ///
+ /// Gets or sets the now viewing item.
+ ///
+ /// The now viewing item.
public BaseItemDto NowViewingItem { get; set; }
///
@@ -156,8 +179,12 @@ namespace MediaBrowser.Controller.Session
///
/// The session controller.
[JsonIgnore]
- public ISessionController[] SessionControllers { get; set; }
+ public IReadOnlyList SessionControllers { get; set; }
+ ///
+ /// Gets or sets the transcoding info.
+ ///
+ /// The transcoding info.
public TranscodingInfo TranscodingInfo { get; set; }
///
@@ -177,7 +204,7 @@ namespace MediaBrowser.Controller.Session
}
}
- if (controllers.Length > 0)
+ if (controllers.Count > 0)
{
return false;
}
@@ -186,6 +213,10 @@ namespace MediaBrowser.Controller.Session
}
}
+ ///
+ /// Gets a value indicating whether the session supports media control.
+ ///
+ /// true if this session supports media control; otherwise, false.
public bool SupportsMediaControl
{
get
@@ -208,6 +239,10 @@ namespace MediaBrowser.Controller.Session
}
}
+ ///
+ /// Gets a value indicating whether the session supports remote control.
+ ///
+ /// true if this session supports remote control; otherwise, false.
public bool SupportsRemoteControl
{
get
@@ -230,16 +265,40 @@ namespace MediaBrowser.Controller.Session
}
}
+ ///
+ /// Gets or sets the now playing queue.
+ ///
+ /// The now playing queue.
public IReadOnlyList NowPlayingQueue { get; set; }
+ ///
+ /// Gets or sets the now playing queue full items.
+ ///
+ /// The now playing queue full items.
public IReadOnlyList NowPlayingQueueFullItems { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the session has a custom device name.
+ ///
+ /// true if this session has a custom device name; otherwise, false.
public bool HasCustomDeviceName { get; set; }
+ ///
+ /// Gets or sets the playlist item id.
+ ///
+ /// The splaylist item id.
public string PlaylistItemId { get; set; }
+ ///
+ /// Gets or sets the server id.
+ ///
+ /// The server id.
public string ServerId { get; set; }
+ ///
+ /// Gets or sets the user primary image tag.
+ ///
+ /// The user primary image tag.
public string UserPrimaryImageTag { get; set; }
///
@@ -247,8 +306,14 @@ namespace MediaBrowser.Controller.Session
///
/// The supported commands.
public IReadOnlyList SupportedCommands
- => Capabilities is null ? Array.Empty() : Capabilities.SupportedCommands;
+ => Capabilities is null ? [] : Capabilities.SupportedCommands;
+ ///
+ /// Ensures a controller of type exists.
+ ///
+ /// Class to register.
+ /// The factory.
+ /// Tuple{ISessionController, bool}.
public Tuple EnsureController(Func factory)
{
var controllers = SessionControllers.ToList();
@@ -261,18 +326,27 @@ namespace MediaBrowser.Controller.Session
}
var newController = factory(this);
- _logger.LogDebug("Creating new {0}", newController.GetType().Name);
+ _logger.LogDebug("Creating new {Factory}", newController.GetType().Name);
controllers.Add(newController);
- SessionControllers = controllers.ToArray();
+ SessionControllers = [.. controllers];
return new Tuple(newController, true);
}
+ ///
+ /// Adds a controller to the session.
+ ///
+ /// The controller.
public void AddController(ISessionController controller)
{
- SessionControllers = [..SessionControllers, controller];
+ SessionControllers = [.. SessionControllers, controller];
}
+ ///
+ /// Gets a value indicating whether the session contains a user.
+ ///
+ /// The user id to check.
+ /// true if this session contains the user; otherwise, false.
public bool ContainsUser(Guid userId)
{
if (UserId.Equals(userId))
@@ -291,6 +365,11 @@ namespace MediaBrowser.Controller.Session
return false;
}
+ ///
+ /// Starts automatic progressing.
+ ///
+ /// The playback progress info.
+ /// The supported commands.
public void StartAutomaticProgress(PlaybackProgressInfo progressInfo)
{
if (_disposed)
@@ -359,6 +438,9 @@ namespace MediaBrowser.Controller.Session
}
}
+ ///
+ /// Stops automatic progressing.
+ ///
public void StopAutomaticProgress()
{
lock (_progressLock)
@@ -373,6 +455,10 @@ namespace MediaBrowser.Controller.Session
}
}
+ ///
+ /// Disposes the instance async.
+ ///
+ /// ValueTask.
public async ValueTask DisposeAsync()
{
_disposed = true;
@@ -380,7 +466,7 @@ namespace MediaBrowser.Controller.Session
StopAutomaticProgress();
var controllers = SessionControllers.ToList();
- SessionControllers = Array.Empty();
+ SessionControllers = [];
foreach (var controller in controllers)
{
diff --git a/MediaBrowser.Model/Devices/DeviceInfo.cs b/MediaBrowser.Model/Devices/DeviceInfo.cs
index 4962992a0a..1155986138 100644
--- a/MediaBrowser.Model/Devices/DeviceInfo.cs
+++ b/MediaBrowser.Model/Devices/DeviceInfo.cs
@@ -1,69 +1,84 @@
-#nullable disable
-#pragma warning disable CS1591
-
using System;
using MediaBrowser.Model.Session;
-namespace MediaBrowser.Model.Devices
+namespace MediaBrowser.Model.Devices;
+
+///
+/// A class for device Information.
+///
+public class DeviceInfo
{
- public class DeviceInfo
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DeviceInfo()
{
- public DeviceInfo()
- {
- Capabilities = new ClientCapabilities();
- }
-
- public string Name { get; set; }
-
- public string CustomName { get; set; }
-
- ///
- /// Gets or sets the access token.
- ///
- public string AccessToken { get; set; }
-
- ///
- /// Gets or sets the identifier.
- ///
- /// The identifier.
- public string Id { get; set; }
-
- ///
- /// Gets or sets the last name of the user.
- ///
- /// The last name of the user.
- public string LastUserName { get; set; }
-
- ///
- /// Gets or sets the name of the application.
- ///
- /// The name of the application.
- public string AppName { get; set; }
-
- ///
- /// Gets or sets the application version.
- ///
- /// The application version.
- public string AppVersion { get; set; }
-
- ///
- /// Gets or sets the last user identifier.
- ///
- /// The last user identifier.
- public Guid LastUserId { get; set; }
-
- ///
- /// Gets or sets the date last modified.
- ///
- /// The date last modified.
- public DateTime DateLastActivity { get; set; }
-
- ///
- /// Gets or sets the capabilities.
- ///
- /// The capabilities.
- public ClientCapabilities Capabilities { get; set; }
-
- public string IconUrl { get; set; }
+ Capabilities = new ClientCapabilities();
}
+
+ ///
+ /// Gets or sets the name.
+ ///
+ /// The name.
+ public string? Name { get; set; }
+
+ ///
+ /// Gets or sets the custom name.
+ ///
+ /// The custom name.
+ public string? CustomName { get; set; }
+
+ ///
+ /// Gets or sets the access token.
+ ///
+ /// The access token.
+ public string? AccessToken { get; set; }
+
+ ///
+ /// Gets or sets the identifier.
+ ///
+ /// The identifier.
+ public string? Id { get; set; }
+
+ ///
+ /// Gets or sets the last name of the user.
+ ///
+ /// The last name of the user.
+ public string? LastUserName { get; set; }
+
+ ///
+ /// Gets or sets the name of the application.
+ ///
+ /// The name of the application.
+ public string? AppName { get; set; }
+
+ ///
+ /// Gets or sets the application version.
+ ///
+ /// The application version.
+ public string? AppVersion { get; set; }
+
+ ///
+ /// Gets or sets the last user identifier.
+ ///
+ /// The last user identifier.
+ public Guid? LastUserId { get; set; }
+
+ ///
+ /// Gets or sets the date last modified.
+ ///
+ /// The date last modified.
+ public DateTime? DateLastActivity { get; set; }
+
+ ///
+ /// Gets or sets the capabilities.
+ ///
+ /// The capabilities.
+ public ClientCapabilities Capabilities { get; set; }
+
+ ///
+ /// Gets or sets the icon URL.
+ ///
+ /// The icon URL.
+ public string? IconUrl { get; set; }
}
diff --git a/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs b/MediaBrowser.Model/Dto/ClientCapabilitiesDto.cs
similarity index 77%
rename from Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs
rename to MediaBrowser.Model/Dto/ClientCapabilitiesDto.cs
index c699c469d9..5963ed270d 100644
--- a/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs
+++ b/MediaBrowser.Model/Dto/ClientCapabilitiesDto.cs
@@ -1,13 +1,11 @@
-using System;
using System.Collections.Generic;
-using System.ComponentModel;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
using Jellyfin.Extensions.Json.Converters;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Session;
-namespace Jellyfin.Api.Models.SessionDtos;
+namespace MediaBrowser.Model.Dto;
///
/// Client capabilities dto.
@@ -18,13 +16,13 @@ public class ClientCapabilitiesDto
/// Gets or sets the list of playable media types.
///
[JsonConverter(typeof(JsonCommaDelimitedArrayConverterFactory))]
- public IReadOnlyList PlayableMediaTypes { get; set; } = Array.Empty();
+ public IReadOnlyList PlayableMediaTypes { get; set; } = [];
///
/// Gets or sets the list of supported commands.
///
[JsonConverter(typeof(JsonCommaDelimitedArrayConverterFactory))]
- public IReadOnlyList SupportedCommands { get; set; } = Array.Empty();
+ public IReadOnlyList SupportedCommands { get; set; } = [];
///
/// Gets or sets a value indicating whether session supports media control.
@@ -51,18 +49,6 @@ public class ClientCapabilitiesDto
///
public string? IconUrl { get; set; }
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
- // TODO: Remove after 10.9
- [Obsolete("Unused")]
- [DefaultValue(false)]
- public bool? SupportsContentUploading { get; set; } = false;
-
- // TODO: Remove after 10.9
- [Obsolete("Unused")]
- [DefaultValue(false)]
- public bool? SupportsSync { get; set; } = false;
-#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
-
///
/// Convert the dto to the full model.
///
diff --git a/MediaBrowser.Model/Dto/DeviceInfoDto.cs b/MediaBrowser.Model/Dto/DeviceInfoDto.cs
new file mode 100644
index 0000000000..ac7a731a90
--- /dev/null
+++ b/MediaBrowser.Model/Dto/DeviceInfoDto.cs
@@ -0,0 +1,83 @@
+using System;
+
+namespace MediaBrowser.Model.Dto;
+
+///
+/// A DTO representing device information.
+///
+public class DeviceInfoDto
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DeviceInfoDto()
+ {
+ Capabilities = new ClientCapabilitiesDto();
+ }
+
+ ///
+ /// Gets or sets the name.
+ ///
+ /// The name.
+ public string? Name { get; set; }
+
+ ///
+ /// Gets or sets the custom name.
+ ///
+ /// The custom name.
+ public string? CustomName { get; set; }
+
+ ///
+ /// Gets or sets the access token.
+ ///
+ /// The access token.
+ public string? AccessToken { get; set; }
+
+ ///
+ /// Gets or sets the identifier.
+ ///
+ /// The identifier.
+ public string? Id { get; set; }
+
+ ///
+ /// Gets or sets the last name of the user.
+ ///
+ /// The last name of the user.
+ public string? LastUserName { get; set; }
+
+ ///
+ /// Gets or sets the name of the application.
+ ///
+ /// The name of the application.
+ public string? AppName { get; set; }
+
+ ///
+ /// Gets or sets the application version.
+ ///
+ /// The application version.
+ public string? AppVersion { get; set; }
+
+ ///
+ /// Gets or sets the last user identifier.
+ ///
+ /// The last user identifier.
+ public Guid? LastUserId { get; set; }
+
+ ///
+ /// Gets or sets the date last modified.
+ ///
+ /// The date last modified.
+ public DateTime? DateLastActivity { get; set; }
+
+ ///
+ /// Gets or sets the capabilities.
+ ///
+ /// The capabilities.
+ public ClientCapabilitiesDto Capabilities { get; set; }
+
+ ///
+ /// Gets or sets the icon URL.
+ ///
+ /// The icon URL.
+ public string? IconUrl { get; set; }
+}
diff --git a/MediaBrowser.Model/Dto/SessionInfoDto.cs b/MediaBrowser.Model/Dto/SessionInfoDto.cs
new file mode 100644
index 0000000000..2496c933a2
--- /dev/null
+++ b/MediaBrowser.Model/Dto/SessionInfoDto.cs
@@ -0,0 +1,186 @@
+using System;
+using System.Collections.Generic;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Model.Session;
+
+namespace MediaBrowser.Model.Dto;
+
+///
+/// Session info DTO.
+///
+public class SessionInfoDto
+{
+ ///
+ /// Gets or sets the play state.
+ ///
+ /// The play state.
+ public PlayerStateInfo? PlayState { get; set; }
+
+ ///
+ /// Gets or sets the additional users.
+ ///
+ /// The additional users.
+ public IReadOnlyList? AdditionalUsers { get; set; }
+
+ ///
+ /// Gets or sets the client capabilities.
+ ///
+ /// The client capabilities.
+ public ClientCapabilitiesDto? Capabilities { get; set; }
+
+ ///
+ /// Gets or sets the remote end point.
+ ///
+ /// The remote end point.
+ public string? RemoteEndPoint { get; set; }
+
+ ///
+ /// Gets or sets the playable media types.
+ ///
+ /// The playable media types.
+ public IReadOnlyList PlayableMediaTypes { get; set; } = [];
+
+ ///
+ /// Gets or sets the id.
+ ///
+ /// The id.
+ public string? Id { get; set; }
+
+ ///
+ /// Gets or sets the user id.
+ ///
+ /// The user id.
+ public Guid UserId { get; set; }
+
+ ///
+ /// Gets or sets the username.
+ ///
+ /// The username.
+ public string? UserName { get; set; }
+
+ ///
+ /// Gets or sets the type of the client.
+ ///
+ /// The type of the client.
+ public string? Client { get; set; }
+
+ ///
+ /// Gets or sets the last activity date.
+ ///
+ /// The last activity date.
+ public DateTime LastActivityDate { get; set; }
+
+ ///
+ /// Gets or sets the last playback check in.
+ ///
+ /// The last playback check in.
+ public DateTime LastPlaybackCheckIn { get; set; }
+
+ ///
+ /// Gets or sets the last paused date.
+ ///
+ /// The last paused date.
+ public DateTime? LastPausedDate { get; set; }
+
+ ///
+ /// Gets or sets the name of the device.
+ ///
+ /// The name of the device.
+ public string? DeviceName { get; set; }
+
+ ///
+ /// Gets or sets the type of the device.
+ ///
+ /// The type of the device.
+ public string? DeviceType { get; set; }
+
+ ///
+ /// Gets or sets the now playing item.
+ ///
+ /// The now playing item.
+ public BaseItemDto? NowPlayingItem { get; set; }
+
+ ///
+ /// Gets or sets the now viewing item.
+ ///
+ /// The now viewing item.
+ public BaseItemDto? NowViewingItem { get; set; }
+
+ ///
+ /// Gets or sets the device id.
+ ///
+ /// The device id.
+ public string? DeviceId { get; set; }
+
+ ///
+ /// Gets or sets the application version.
+ ///
+ /// The application version.
+ public string? ApplicationVersion { get; set; }
+
+ ///
+ /// Gets or sets the transcoding info.
+ ///
+ /// The transcoding info.
+ public TranscodingInfo? TranscodingInfo { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether this session is active.
+ ///
+ /// true if this session is active; otherwise, false.
+ public bool IsActive { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the session supports media control.
+ ///
+ /// true if this session supports media control; otherwise, false.
+ public bool SupportsMediaControl { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the session supports remote control.
+ ///
+ /// true if this session supports remote control; otherwise, false.
+ public bool SupportsRemoteControl { get; set; }
+
+ ///
+ /// Gets or sets the now playing queue.
+ ///
+ /// The now playing queue.
+ public IReadOnlyList? NowPlayingQueue { get; set; }
+
+ ///
+ /// Gets or sets the now playing queue full items.
+ ///
+ /// The now playing queue full items.
+ public IReadOnlyList? NowPlayingQueueFullItems { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the session has a custom device name.
+ ///
+ /// true if this session has a custom device name; otherwise, false.
+ public bool HasCustomDeviceName { get; set; }
+
+ ///
+ /// Gets or sets the playlist item id.
+ ///
+ /// The splaylist item id.
+ public string? PlaylistItemId { get; set; }
+
+ ///
+ /// Gets or sets the server id.
+ ///
+ /// The server id.
+ public string? ServerId { get; set; }
+
+ ///
+ /// Gets or sets the user primary image tag.
+ ///
+ /// The user primary image tag.
+ public string? UserPrimaryImageTag { get; set; }
+
+ ///
+ /// Gets or sets the supported commands.
+ ///
+ /// The supported commands.
+ public IReadOnlyList SupportedCommands { get; set; } = [];
+}
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs
index 1466d3a71a..b9477ce6b7 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs
@@ -1,5 +1,7 @@
using System;
+using System.Collections.Generic;
using System.ComponentModel;
+using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
@@ -35,38 +37,27 @@ namespace Jellyfin.Extensions.Json.Converters
var stringEntries = reader.GetString()!.Split(Delimiter, StringSplitOptions.RemoveEmptyEntries);
if (stringEntries.Length == 0)
{
- return Array.Empty();
+ return [];
}
- var parsedValues = new object[stringEntries.Length];
- var convertedCount = 0;
+ var typedValues = new List();
for (var i = 0; i < stringEntries.Length; i++)
{
try
{
- parsedValues[i] = _typeConverter.ConvertFromInvariantString(stringEntries[i].Trim()) ?? throw new FormatException();
- convertedCount++;
+ var parsedValue = _typeConverter.ConvertFromInvariantString(stringEntries[i].Trim());
+ if (parsedValue is not null)
+ {
+ typedValues.Add((T)parsedValue);
+ }
}
catch (FormatException)
{
- // TODO log when upgraded to .Net6
- // https://github.com/dotnet/runtime/issues/42975
- // _logger.LogDebug(e, "Error converting value.");
+ // Ignore unconvertable inputs
}
}
- var typedValues = new T[convertedCount];
- var typedValueIndex = 0;
- for (var i = 0; i < stringEntries.Length; i++)
- {
- if (parsedValues[i] is not null)
- {
- typedValues.SetValue(parsedValues[i], typedValueIndex);
- typedValueIndex++;
- }
- }
-
- return typedValues;
+ return [.. typedValues];
}
return JsonSerializer.Deserialize(ref reader, options);
@@ -75,7 +66,39 @@ namespace Jellyfin.Extensions.Json.Converters
///
public override void Write(Utf8JsonWriter writer, T[]? value, JsonSerializerOptions options)
{
- throw new NotImplementedException();
+ if (value is not null)
+ {
+ writer.WriteStartArray();
+ if (value.Length > 0)
+ {
+ var toWrite = value.Length - 1;
+ foreach (var it in value)
+ {
+ var wrote = false;
+ if (it is not null)
+ {
+ writer.WriteStringValue(it.ToString());
+ wrote = true;
+ }
+
+ if (toWrite > 0)
+ {
+ if (wrote)
+ {
+ writer.WriteStringValue(Delimiter.ToString());
+ }
+
+ toWrite--;
+ }
+ }
+ }
+
+ writer.WriteEndArray();
+ }
+ else
+ {
+ writer.WriteNullValue();
+ }
}
}
}
diff --git a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedArrayTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedArrayTests.cs
index 61105b42b2..9fc0158235 100644
--- a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedArrayTests.cs
+++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedArrayTests.cs
@@ -41,7 +41,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
{
var desiredValue = new GenericBodyArrayModel
{
- Value = new[] { "a", "b", "c" }
+ Value = ["a", "b", "c"]
};
var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""a,b,c"" }", _jsonOptions);
@@ -53,7 +53,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
{
var desiredValue = new GenericBodyArrayModel
{
- Value = new[] { "a", "b", "c" }
+ Value = ["a", "b", "c"]
};
var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""a, b, c"" }", _jsonOptions);
@@ -65,7 +65,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
{
var desiredValue = new GenericBodyArrayModel
{
- Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown }
+ Value = [GeneralCommandType.MoveUp, GeneralCommandType.MoveDown]
};
var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp,MoveDown"" }", _jsonOptions);
@@ -77,7 +77,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
{
var desiredValue = new GenericBodyArrayModel
{
- Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown }
+ Value = [GeneralCommandType.MoveUp, GeneralCommandType.MoveDown]
};
var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp,,MoveDown"" }", _jsonOptions);
@@ -89,7 +89,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
{
var desiredValue = new GenericBodyArrayModel
{
- Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown }
+ Value = [GeneralCommandType.MoveUp, GeneralCommandType.MoveDown]
};
var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp,TotallyNotAVallidCommand,MoveDown"" }", _jsonOptions);
@@ -101,7 +101,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
{
var desiredValue = new GenericBodyArrayModel
{
- Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown }
+ Value = [GeneralCommandType.MoveUp, GeneralCommandType.MoveDown]
};
var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp, MoveDown"" }", _jsonOptions);
@@ -113,7 +113,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
{
var desiredValue = new GenericBodyArrayModel
{
- Value = new[] { "a", "b", "c" }
+ Value = ["a", "b", "c"]
};
var value = JsonSerializer.Deserialize>(@"{ ""Value"": [""a"",""b"",""c""] }", _jsonOptions);
@@ -125,7 +125,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
{
var desiredValue = new GenericBodyArrayModel
{
- Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown }
+ Value = [GeneralCommandType.MoveUp, GeneralCommandType.MoveDown]
};
var value = JsonSerializer.Deserialize>(@"{ ""Value"": [""MoveUp"", ""MoveDown""] }", _jsonOptions);