mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-12-26 17:36:52 +00:00
Add SessionInfoDto, DeviceInfoDto and implement JsonDelimitedArrayConverter.Write
This commit is contained in:
parent
569a41fc2a
commit
7a2427bf07
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SessionManager"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">Instance of <see cref="ILogger{SessionManager}"/> interface.</param>
|
||||
/// <param name="eventManager">Instance of <see cref="IEventManager"/> interface.</param>
|
||||
/// <param name="userDataManager">Instance of <see cref="IUserDataManager"/> interface.</param>
|
||||
/// <param name="serverConfigurationManager">Instance of <see cref="IServerConfigurationManager"/> interface.</param>
|
||||
/// <param name="libraryManager">Instance of <see cref="ILibraryManager"/> interface.</param>
|
||||
/// <param name="userManager">Instance of <see cref="IUserManager"/> interface.</param>
|
||||
/// <param name="musicManager">Instance of <see cref="IMusicManager"/> interface.</param>
|
||||
/// <param name="dtoService">Instance of <see cref="IDtoService"/> interface.</param>
|
||||
/// <param name="imageProcessor">Instance of <see cref="IImageProcessor"/> interface.</param>
|
||||
/// <param name="appHost">Instance of <see cref="IServerApplicationHost"/> interface.</param>
|
||||
/// <param name="deviceManager">Instance of <see cref="IDeviceManager"/> interface.</param>
|
||||
/// <param name="mediaSourceManager">Instance of <see cref="IMediaSourceManager"/> interface.</param>
|
||||
/// <param name="hostApplicationLifetime">Instance of <see cref="IHostApplicationLifetime"/> interface.</param>
|
||||
public SessionManager(
|
||||
ILogger<SessionManager> 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
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IReadOnlyList<SessionInfoDto> 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();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SendMessageToAdminSessions<T>(SessionMessageType name, T data, CancellationToken cancellationToken)
|
||||
{
|
||||
|
|
|
@ -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
|
|||
/// <returns>An <see cref="OkResult"/> containing the list of devices.</returns>
|
||||
[HttpGet]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public ActionResult<QueryResult<DeviceInfo>> GetDevices([FromQuery] Guid? userId)
|
||||
public ActionResult<QueryResult<DeviceInfoDto>> 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<DeviceInfo> GetDeviceInfo([FromQuery, Required] string id)
|
||||
public ActionResult<DeviceInfoDto> 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<DeviceOptions> GetDeviceOptions([FromQuery, Required] string id)
|
||||
public ActionResult<DeviceOptionsDto> GetDeviceOptions([FromQuery, Required] string id)
|
||||
{
|
||||
var deviceInfo = _deviceManager.GetDeviceOptions(id);
|
||||
if (deviceInfo is null)
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SessionController"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sessionManager">Instance of <see cref="ISessionManager"/> interface.</param>
|
||||
/// <param name="userManager">Instance of <see cref="IUserManager"/> interface.</param>
|
||||
/// <param name="deviceManager">Instance of <see cref="IDeviceManager"/> interface.</param>
|
||||
public SessionController(
|
||||
ISessionManager sessionManager,
|
||||
IUserManager userManager,
|
||||
IDeviceManager deviceManager)
|
||||
IUserManager userManager)
|
||||
{
|
||||
_sessionManager = sessionManager;
|
||||
_userManager = userManager;
|
||||
_deviceManager = deviceManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -57,77 +48,25 @@ public class SessionController : BaseJellyfinApiController
|
|||
/// <param name="deviceId">Filter by device Id.</param>
|
||||
/// <param name="activeWithinSeconds">Optional. Filter by sessions that were active in the last n seconds.</param>
|
||||
/// <response code="200">List of sessions returned.</response>
|
||||
/// <returns>An <see cref="IEnumerable{SessionInfo}"/> with the available sessions.</returns>
|
||||
/// <returns>An <see cref="IReadOnlyList{SessionInfoDto}"/> with the available sessions.</returns>
|
||||
[HttpGet("Sessions")]
|
||||
[Authorize]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public ActionResult<IEnumerable<SessionInfo>> GetSessions(
|
||||
public ActionResult<IReadOnlyList<SessionInfoDto>> 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);
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
namespace Jellyfin.Data.Dtos
|
||||
namespace Jellyfin.Data.Dtos;
|
||||
|
||||
/// <summary>
|
||||
/// A dto representing custom options for a device.
|
||||
/// </summary>
|
||||
public class DeviceOptionsDto
|
||||
{
|
||||
/// <summary>
|
||||
/// A dto representing custom options for a device.
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public class DeviceOptionsDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the device id.
|
||||
/// </summary>
|
||||
public string? DeviceId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the device id.
|
||||
/// </summary>
|
||||
public string? DeviceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom name.
|
||||
/// </summary>
|
||||
public string? CustomName { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the custom name.
|
||||
/// </summary>
|
||||
public string? CustomName { get; set; }
|
||||
}
|
||||
|
|
|
@ -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
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -166,7 +176,7 @@ namespace Jellyfin.Server.Implementations.Devices
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public QueryResult<DeviceInfo> GetDevicesForUser(Guid? userId)
|
||||
public QueryResult<DeviceInfoDto> GetDevicesForUser(Guid? userId)
|
||||
{
|
||||
IEnumerable<Device> 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<DeviceInfo>(array);
|
||||
return new QueryResult<DeviceInfoDto>(array);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// A class representing an authentication result.
|
||||
/// </summary>
|
||||
public class AuthenticationResult
|
||||
{
|
||||
public class AuthenticationResult
|
||||
{
|
||||
public UserDto User { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the user.
|
||||
/// </summary>
|
||||
public UserDto User { get; set; }
|
||||
|
||||
public SessionInfo SessionInfo { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the session info.
|
||||
/// </summary>
|
||||
public SessionInfoDto SessionInfo { get; set; }
|
||||
|
||||
public string AccessToken { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the access token.
|
||||
/// </summary>
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
public string ServerId { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the server id.
|
||||
/// </summary>
|
||||
public string ServerId { get; set; }
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Device manager interface.
|
||||
/// </summary>
|
||||
public interface IDeviceManager
|
||||
{
|
||||
public interface IDeviceManager
|
||||
{
|
||||
event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated;
|
||||
/// <summary>
|
||||
/// Event handler for updated device options.
|
||||
/// </summary>
|
||||
event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new device.
|
||||
/// </summary>
|
||||
/// <param name="device">The device to create.</param>
|
||||
/// <returns>A <see cref="Task{Device}"/> representing the creation of the device.</returns>
|
||||
Task<Device> CreateDevice(Device device);
|
||||
/// <summary>
|
||||
/// Creates a new device.
|
||||
/// </summary>
|
||||
/// <param name="device">The device to create.</param>
|
||||
/// <returns>A <see cref="Task{Device}"/> representing the creation of the device.</returns>
|
||||
Task<Device> CreateDevice(Device device);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the capabilities.
|
||||
/// </summary>
|
||||
/// <param name="deviceId">The device id.</param>
|
||||
/// <param name="capabilities">The capabilities.</param>
|
||||
void SaveCapabilities(string deviceId, ClientCapabilities capabilities);
|
||||
/// <summary>
|
||||
/// Saves the capabilities.
|
||||
/// </summary>
|
||||
/// <param name="deviceId">The device id.</param>
|
||||
/// <param name="capabilities">The capabilities.</param>
|
||||
void SaveCapabilities(string deviceId, ClientCapabilities capabilities);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the capabilities.
|
||||
/// </summary>
|
||||
/// <param name="deviceId">The device id.</param>
|
||||
/// <returns>ClientCapabilities.</returns>
|
||||
ClientCapabilities GetCapabilities(string deviceId);
|
||||
/// <summary>
|
||||
/// Gets the capabilities.
|
||||
/// </summary>
|
||||
/// <param name="deviceId">The device id.</param>
|
||||
/// <returns>ClientCapabilities.</returns>
|
||||
ClientCapabilities GetCapabilities(string? deviceId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device information.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <returns>DeviceInfo.</returns>
|
||||
DeviceInfo GetDevice(string id);
|
||||
/// <summary>
|
||||
/// Gets the device information.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <returns>DeviceInfoDto.</returns>
|
||||
DeviceInfoDto? GetDevice(string id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets devices based on the provided query.
|
||||
/// </summary>
|
||||
/// <param name="query">The device query.</param>
|
||||
/// <returns>A <see cref="Task{QueryResult}"/> representing the retrieval of the devices.</returns>
|
||||
QueryResult<Device> GetDevices(DeviceQuery query);
|
||||
/// <summary>
|
||||
/// Gets devices based on the provided query.
|
||||
/// </summary>
|
||||
/// <param name="query">The device query.</param>
|
||||
/// <returns>A <see cref="Task{QueryResult}"/> representing the retrieval of the devices.</returns>
|
||||
QueryResult<Device> GetDevices(DeviceQuery query);
|
||||
|
||||
QueryResult<DeviceInfo> GetDeviceInfos(DeviceQuery query);
|
||||
/// <summary>
|
||||
/// Gets device infromation based on the provided query.
|
||||
/// </summary>
|
||||
/// <param name="query">The device query.</param>
|
||||
/// <returns>A <see cref="Task{QueryResult}"/> representing the retrieval of the device information.</returns>
|
||||
QueryResult<DeviceInfo> GetDeviceInfos(DeviceQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the devices.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user's id, or <c>null</c>.</param>
|
||||
/// <returns>IEnumerable<DeviceInfo>.</returns>
|
||||
QueryResult<DeviceInfo> GetDevicesForUser(Guid? userId);
|
||||
/// <summary>
|
||||
/// Gets the device information.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user's id, or <c>null</c>.</param>
|
||||
/// <returns>IEnumerable<DeviceInfoDto>.</returns>
|
||||
QueryResult<DeviceInfoDto> GetDevicesForUser(Guid? userId);
|
||||
|
||||
Task DeleteDevice(Device device);
|
||||
/// <summary>
|
||||
/// Deletes a device.
|
||||
/// </summary>
|
||||
/// <param name="device">The device.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the deletion of the device.</returns>
|
||||
Task DeleteDevice(Device device);
|
||||
|
||||
Task UpdateDevice(Device device);
|
||||
/// <summary>
|
||||
/// Updates a device.
|
||||
/// </summary>
|
||||
/// <param name="device">The device.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the update of the device.</returns>
|
||||
Task UpdateDevice(Device device);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this instance [can access device] the specified user identifier.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to test.</param>
|
||||
/// <param name="deviceId">The device id to test.</param>
|
||||
/// <returns>Whether the user can access the device.</returns>
|
||||
bool CanAccessDevice(User user, string deviceId);
|
||||
/// <summary>
|
||||
/// Determines whether this instance [can access device] the specified user identifier.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to test.</param>
|
||||
/// <param name="deviceId">The device id to test.</param>
|
||||
/// <returns>Whether the user can access the device.</returns>
|
||||
bool CanAccessDevice(User user, string deviceId);
|
||||
|
||||
Task UpdateDeviceOptions(string deviceId, string deviceName);
|
||||
/// <summary>
|
||||
/// Updates the options of a device.
|
||||
/// </summary>
|
||||
/// <param name="deviceId">The device id.</param>
|
||||
/// <param name="deviceName">The device name.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the update of the device options.</returns>
|
||||
Task UpdateDeviceOptions(string deviceId, string? deviceName);
|
||||
|
||||
DeviceOptions GetDeviceOptions(string deviceId);
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the options of a device.
|
||||
/// </summary>
|
||||
/// <param name="deviceId">The device id.</param>
|
||||
/// <returns><see cref="DeviceOptions"/> of the device.</returns>
|
||||
DeviceOptionsDto? GetDeviceOptions(string deviceId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dto for client capabilites.
|
||||
/// </summary>
|
||||
/// <param name="capabilities">The client capabilities.</param>
|
||||
/// <returns><see cref="ClientCapabilitiesDto"/> of the device.</returns>
|
||||
ClientCapabilitiesDto ToClientCapabilitiesDto(ClientCapabilities capabilities);
|
||||
}
|
||||
|
|
|
@ -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
|
|||
/// <summary>
|
||||
/// Gets or sets the session information.
|
||||
/// </summary>
|
||||
public SessionInfo? SessionInfo { get; set; }
|
||||
public SessionInfoDto? SessionInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the server id.
|
||||
|
|
|
@ -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;
|
|||
/// <summary>
|
||||
/// Sessions message.
|
||||
/// </summary>
|
||||
public class SessionsMessage : OutboundWebSocketMessage<IReadOnlyList<SessionInfo>>
|
||||
public class SessionsMessage : OutboundWebSocketMessage<IReadOnlyList<SessionInfoDto>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SessionsMessage"/> class.
|
||||
/// </summary>
|
||||
/// <param name="data">Session info.</param>
|
||||
public SessionsMessage(IReadOnlyList<SessionInfo> data)
|
||||
public SessionsMessage(IReadOnlyList<SessionInfoDto> data)
|
||||
: base(data)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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
|
|||
/// <returns>SessionInfo.</returns>
|
||||
SessionInfo GetSession(string deviceId, string client, string version);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all sessions available to a user.
|
||||
/// </summary>
|
||||
/// <param name="userId">The session identifier.</param>
|
||||
/// <param name="deviceId">The device id.</param>
|
||||
/// <param name="activeWithinSeconds">Active within session limit.</param>
|
||||
/// <param name="controllableUserToCheck">Filter for sessions remote controllable for this user.</param>
|
||||
/// <returns>IReadOnlyList{SessionInfoDto}.</returns>
|
||||
IReadOnlyList<SessionInfoDto> GetSessions(Guid userId, string deviceId, int? activeWithinSeconds, Guid? controllableUserToCheck);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the session by authentication token.
|
||||
/// </summary>
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SessionInfo"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sessionManager">Instance of <see cref="ISessionManager"/> interface.</param>
|
||||
/// <param name="logger">Instance of <see cref="ILogger"/> interface.</param>
|
||||
public SessionInfo(ISessionManager sessionManager, ILogger logger)
|
||||
{
|
||||
_sessionManager = sessionManager;
|
||||
_logger = logger;
|
||||
|
||||
AdditionalUsers = Array.Empty<SessionUserInfo>();
|
||||
AdditionalUsers = [];
|
||||
PlayState = new PlayerStateInfo();
|
||||
SessionControllers = Array.Empty<ISessionController>();
|
||||
NowPlayingQueue = Array.Empty<QueueItem>();
|
||||
NowPlayingQueueFullItems = Array.Empty<BaseItemDto>();
|
||||
SessionControllers = [];
|
||||
NowPlayingQueue = [];
|
||||
NowPlayingQueueFullItems = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the play state.
|
||||
/// </summary>
|
||||
/// <value>The play state.</value>
|
||||
public PlayerStateInfo PlayState { get; set; }
|
||||
|
||||
public SessionUserInfo[] AdditionalUsers { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the additional users.
|
||||
/// </summary>
|
||||
/// <value>The additional users.</value>
|
||||
public IReadOnlyList<SessionUserInfo> AdditionalUsers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the client capabilities.
|
||||
/// </summary>
|
||||
/// <value>The client capabilities.</value>
|
||||
public ClientCapabilities Capabilities { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -67,7 +82,7 @@ namespace MediaBrowser.Controller.Session
|
|||
{
|
||||
if (Capabilities is null)
|
||||
{
|
||||
return Array.Empty<MediaType>();
|
||||
return [];
|
||||
}
|
||||
|
||||
return Capabilities.PlayableMediaTypes;
|
||||
|
@ -134,9 +149,17 @@ namespace MediaBrowser.Controller.Session
|
|||
/// <value>The now playing item.</value>
|
||||
public BaseItemDto NowPlayingItem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the now playing queue full items.
|
||||
/// </summary>
|
||||
/// <value>The now playing queue full items.</value>
|
||||
[JsonIgnore]
|
||||
public BaseItem FullNowPlayingItem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the now viewing item.
|
||||
/// </summary>
|
||||
/// <value>The now viewing item.</value>
|
||||
public BaseItemDto NowViewingItem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -156,8 +179,12 @@ namespace MediaBrowser.Controller.Session
|
|||
/// </summary>
|
||||
/// <value>The session controller.</value>
|
||||
[JsonIgnore]
|
||||
public ISessionController[] SessionControllers { get; set; }
|
||||
public IReadOnlyList<ISessionController> SessionControllers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transcoding info.
|
||||
/// </summary>
|
||||
/// <value>The transcoding info.</value>
|
||||
public TranscodingInfo TranscodingInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -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
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the session supports media control.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this session supports media control; otherwise, <c>false</c>.</value>
|
||||
public bool SupportsMediaControl
|
||||
{
|
||||
get
|
||||
|
@ -208,6 +239,10 @@ namespace MediaBrowser.Controller.Session
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the session supports remote control.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this session supports remote control; otherwise, <c>false</c>.</value>
|
||||
public bool SupportsRemoteControl
|
||||
{
|
||||
get
|
||||
|
@ -230,16 +265,40 @@ namespace MediaBrowser.Controller.Session
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the now playing queue.
|
||||
/// </summary>
|
||||
/// <value>The now playing queue.</value>
|
||||
public IReadOnlyList<QueueItem> NowPlayingQueue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the now playing queue full items.
|
||||
/// </summary>
|
||||
/// <value>The now playing queue full items.</value>
|
||||
public IReadOnlyList<BaseItemDto> NowPlayingQueueFullItems { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the session has a custom device name.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this session has a custom device name; otherwise, <c>false</c>.</value>
|
||||
public bool HasCustomDeviceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the playlist item id.
|
||||
/// </summary>
|
||||
/// <value>The splaylist item id.</value>
|
||||
public string PlaylistItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the server id.
|
||||
/// </summary>
|
||||
/// <value>The server id.</value>
|
||||
public string ServerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user primary image tag.
|
||||
/// </summary>
|
||||
/// <value>The user primary image tag.</value>
|
||||
public string UserPrimaryImageTag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -247,8 +306,14 @@ namespace MediaBrowser.Controller.Session
|
|||
/// </summary>
|
||||
/// <value>The supported commands.</value>
|
||||
public IReadOnlyList<GeneralCommandType> SupportedCommands
|
||||
=> Capabilities is null ? Array.Empty<GeneralCommandType>() : Capabilities.SupportedCommands;
|
||||
=> Capabilities is null ? [] : Capabilities.SupportedCommands;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures a controller of type exists.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Class to register.</typeparam>
|
||||
/// <param name="factory">The factory.</param>
|
||||
/// <returns>Tuple{ISessionController, bool}.</returns>
|
||||
public Tuple<ISessionController, bool> EnsureController<T>(Func<SessionInfo, ISessionController> 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<ISessionController, bool>(newController, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a controller to the session.
|
||||
/// </summary>
|
||||
/// <param name="controller">The controller.</param>
|
||||
public void AddController(ISessionController controller)
|
||||
{
|
||||
SessionControllers = [..SessionControllers, controller];
|
||||
SessionControllers = [.. SessionControllers, controller];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the session contains a user.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user id to check.</param>
|
||||
/// <returns><c>true</c> if this session contains the user; otherwise, <c>false</c>.</returns>
|
||||
public bool ContainsUser(Guid userId)
|
||||
{
|
||||
if (UserId.Equals(userId))
|
||||
|
@ -291,6 +365,11 @@ namespace MediaBrowser.Controller.Session
|
|||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts automatic progressing.
|
||||
/// </summary>
|
||||
/// <param name="progressInfo">The playback progress info.</param>
|
||||
/// <value>The supported commands.</value>
|
||||
public void StartAutomaticProgress(PlaybackProgressInfo progressInfo)
|
||||
{
|
||||
if (_disposed)
|
||||
|
@ -359,6 +438,9 @@ namespace MediaBrowser.Controller.Session
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops automatic progressing.
|
||||
/// </summary>
|
||||
public void StopAutomaticProgress()
|
||||
{
|
||||
lock (_progressLock)
|
||||
|
@ -373,6 +455,10 @@ namespace MediaBrowser.Controller.Session
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the instance async.
|
||||
/// </summary>
|
||||
/// <returns>ValueTask.</returns>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
_disposed = true;
|
||||
|
@ -380,7 +466,7 @@ namespace MediaBrowser.Controller.Session
|
|||
StopAutomaticProgress();
|
||||
|
||||
var controllers = SessionControllers.ToList();
|
||||
SessionControllers = Array.Empty<ISessionController>();
|
||||
SessionControllers = [];
|
||||
|
||||
foreach (var controller in controllers)
|
||||
{
|
||||
|
|
|
@ -1,69 +1,84 @@
|
|||
#nullable disable
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Model.Session;
|
||||
|
||||
namespace MediaBrowser.Model.Devices
|
||||
namespace MediaBrowser.Model.Devices;
|
||||
|
||||
/// <summary>
|
||||
/// A class for device Information.
|
||||
/// </summary>
|
||||
public class DeviceInfo
|
||||
{
|
||||
public class DeviceInfo
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeviceInfo"/> class.
|
||||
/// </summary>
|
||||
public DeviceInfo()
|
||||
{
|
||||
public DeviceInfo()
|
||||
{
|
||||
Capabilities = new ClientCapabilities();
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string CustomName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the access token.
|
||||
/// </summary>
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier.
|
||||
/// </summary>
|
||||
/// <value>The identifier.</value>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last name of the user.
|
||||
/// </summary>
|
||||
/// <value>The last name of the user.</value>
|
||||
public string LastUserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the application.
|
||||
/// </summary>
|
||||
/// <value>The name of the application.</value>
|
||||
public string AppName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application version.
|
||||
/// </summary>
|
||||
/// <value>The application version.</value>
|
||||
public string AppVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last user identifier.
|
||||
/// </summary>
|
||||
/// <value>The last user identifier.</value>
|
||||
public Guid LastUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date last modified.
|
||||
/// </summary>
|
||||
/// <value>The date last modified.</value>
|
||||
public DateTime DateLastActivity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the capabilities.
|
||||
/// </summary>
|
||||
/// <value>The capabilities.</value>
|
||||
public ClientCapabilities Capabilities { get; set; }
|
||||
|
||||
public string IconUrl { get; set; }
|
||||
Capabilities = new ClientCapabilities();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom name.
|
||||
/// </summary>
|
||||
/// <value>The custom name.</value>
|
||||
public string? CustomName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the access token.
|
||||
/// </summary>
|
||||
/// <value>The access token.</value>
|
||||
public string? AccessToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier.
|
||||
/// </summary>
|
||||
/// <value>The identifier.</value>
|
||||
public string? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last name of the user.
|
||||
/// </summary>
|
||||
/// <value>The last name of the user.</value>
|
||||
public string? LastUserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the application.
|
||||
/// </summary>
|
||||
/// <value>The name of the application.</value>
|
||||
public string? AppName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application version.
|
||||
/// </summary>
|
||||
/// <value>The application version.</value>
|
||||
public string? AppVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last user identifier.
|
||||
/// </summary>
|
||||
/// <value>The last user identifier.</value>
|
||||
public Guid? LastUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date last modified.
|
||||
/// </summary>
|
||||
/// <value>The date last modified.</value>
|
||||
public DateTime? DateLastActivity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the capabilities.
|
||||
/// </summary>
|
||||
/// <value>The capabilities.</value>
|
||||
public ClientCapabilities Capabilities { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon URL.
|
||||
/// </summary>
|
||||
/// <value>The icon URL.</value>
|
||||
public string? IconUrl { get; set; }
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Client capabilities dto.
|
||||
|
@ -18,13 +16,13 @@ public class ClientCapabilitiesDto
|
|||
/// Gets or sets the list of playable media types.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonCommaDelimitedArrayConverterFactory))]
|
||||
public IReadOnlyList<MediaType> PlayableMediaTypes { get; set; } = Array.Empty<MediaType>();
|
||||
public IReadOnlyList<MediaType> PlayableMediaTypes { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of supported commands.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonCommaDelimitedArrayConverterFactory))]
|
||||
public IReadOnlyList<GeneralCommandType> SupportedCommands { get; set; } = Array.Empty<GeneralCommandType>();
|
||||
public IReadOnlyList<GeneralCommandType> SupportedCommands { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether session supports media control.
|
||||
|
@ -51,18 +49,6 @@ public class ClientCapabilitiesDto
|
|||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Convert the dto to the full <see cref="ClientCapabilities"/> model.
|
||||
/// </summary>
|
83
MediaBrowser.Model/Dto/DeviceInfoDto.cs
Normal file
83
MediaBrowser.Model/Dto/DeviceInfoDto.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
using System;
|
||||
|
||||
namespace MediaBrowser.Model.Dto;
|
||||
|
||||
/// <summary>
|
||||
/// A DTO representing device information.
|
||||
/// </summary>
|
||||
public class DeviceInfoDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeviceInfoDto"/> class.
|
||||
/// </summary>
|
||||
public DeviceInfoDto()
|
||||
{
|
||||
Capabilities = new ClientCapabilitiesDto();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom name.
|
||||
/// </summary>
|
||||
/// <value>The custom name.</value>
|
||||
public string? CustomName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the access token.
|
||||
/// </summary>
|
||||
/// <value>The access token.</value>
|
||||
public string? AccessToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier.
|
||||
/// </summary>
|
||||
/// <value>The identifier.</value>
|
||||
public string? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last name of the user.
|
||||
/// </summary>
|
||||
/// <value>The last name of the user.</value>
|
||||
public string? LastUserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the application.
|
||||
/// </summary>
|
||||
/// <value>The name of the application.</value>
|
||||
public string? AppName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application version.
|
||||
/// </summary>
|
||||
/// <value>The application version.</value>
|
||||
public string? AppVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last user identifier.
|
||||
/// </summary>
|
||||
/// <value>The last user identifier.</value>
|
||||
public Guid? LastUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date last modified.
|
||||
/// </summary>
|
||||
/// <value>The date last modified.</value>
|
||||
public DateTime? DateLastActivity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the capabilities.
|
||||
/// </summary>
|
||||
/// <value>The capabilities.</value>
|
||||
public ClientCapabilitiesDto Capabilities { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon URL.
|
||||
/// </summary>
|
||||
/// <value>The icon URL.</value>
|
||||
public string? IconUrl { get; set; }
|
||||
}
|
186
MediaBrowser.Model/Dto/SessionInfoDto.cs
Normal file
186
MediaBrowser.Model/Dto/SessionInfoDto.cs
Normal file
|
@ -0,0 +1,186 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Model.Session;
|
||||
|
||||
namespace MediaBrowser.Model.Dto;
|
||||
|
||||
/// <summary>
|
||||
/// Session info DTO.
|
||||
/// </summary>
|
||||
public class SessionInfoDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the play state.
|
||||
/// </summary>
|
||||
/// <value>The play state.</value>
|
||||
public PlayerStateInfo? PlayState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the additional users.
|
||||
/// </summary>
|
||||
/// <value>The additional users.</value>
|
||||
public IReadOnlyList<SessionUserInfo>? AdditionalUsers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the client capabilities.
|
||||
/// </summary>
|
||||
/// <value>The client capabilities.</value>
|
||||
public ClientCapabilitiesDto? Capabilities { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the remote end point.
|
||||
/// </summary>
|
||||
/// <value>The remote end point.</value>
|
||||
public string? RemoteEndPoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the playable media types.
|
||||
/// </summary>
|
||||
/// <value>The playable media types.</value>
|
||||
public IReadOnlyList<MediaType> PlayableMediaTypes { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public string? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the username.
|
||||
/// </summary>
|
||||
/// <value>The username.</value>
|
||||
public string? UserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the client.
|
||||
/// </summary>
|
||||
/// <value>The type of the client.</value>
|
||||
public string? Client { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last activity date.
|
||||
/// </summary>
|
||||
/// <value>The last activity date.</value>
|
||||
public DateTime LastActivityDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last playback check in.
|
||||
/// </summary>
|
||||
/// <value>The last playback check in.</value>
|
||||
public DateTime LastPlaybackCheckIn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last paused date.
|
||||
/// </summary>
|
||||
/// <value>The last paused date.</value>
|
||||
public DateTime? LastPausedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the device.
|
||||
/// </summary>
|
||||
/// <value>The name of the device.</value>
|
||||
public string? DeviceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the device.
|
||||
/// </summary>
|
||||
/// <value>The type of the device.</value>
|
||||
public string? DeviceType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the now playing item.
|
||||
/// </summary>
|
||||
/// <value>The now playing item.</value>
|
||||
public BaseItemDto? NowPlayingItem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the now viewing item.
|
||||
/// </summary>
|
||||
/// <value>The now viewing item.</value>
|
||||
public BaseItemDto? NowViewingItem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the device id.
|
||||
/// </summary>
|
||||
/// <value>The device id.</value>
|
||||
public string? DeviceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application version.
|
||||
/// </summary>
|
||||
/// <value>The application version.</value>
|
||||
public string? ApplicationVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transcoding info.
|
||||
/// </summary>
|
||||
/// <value>The transcoding info.</value>
|
||||
public TranscodingInfo? TranscodingInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this session is active.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this session is active; otherwise, <c>false</c>.</value>
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the session supports media control.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this session supports media control; otherwise, <c>false</c>.</value>
|
||||
public bool SupportsMediaControl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the session supports remote control.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this session supports remote control; otherwise, <c>false</c>.</value>
|
||||
public bool SupportsRemoteControl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the now playing queue.
|
||||
/// </summary>
|
||||
/// <value>The now playing queue.</value>
|
||||
public IReadOnlyList<QueueItem>? NowPlayingQueue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the now playing queue full items.
|
||||
/// </summary>
|
||||
/// <value>The now playing queue full items.</value>
|
||||
public IReadOnlyList<BaseItemDto>? NowPlayingQueueFullItems { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the session has a custom device name.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this session has a custom device name; otherwise, <c>false</c>.</value>
|
||||
public bool HasCustomDeviceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the playlist item id.
|
||||
/// </summary>
|
||||
/// <value>The splaylist item id.</value>
|
||||
public string? PlaylistItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the server id.
|
||||
/// </summary>
|
||||
/// <value>The server id.</value>
|
||||
public string? ServerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user primary image tag.
|
||||
/// </summary>
|
||||
/// <value>The user primary image tag.</value>
|
||||
public string? UserPrimaryImageTag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the supported commands.
|
||||
/// </summary>
|
||||
/// <value>The supported commands.</value>
|
||||
public IReadOnlyList<GeneralCommandType> SupportedCommands { get; set; } = [];
|
||||
}
|
|
@ -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<T>();
|
||||
return [];
|
||||
}
|
||||
|
||||
var parsedValues = new object[stringEntries.Length];
|
||||
var convertedCount = 0;
|
||||
var typedValues = new List<T>();
|
||||
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<T[]>(ref reader, options);
|
||||
|
@ -75,7 +66,39 @@ namespace Jellyfin.Extensions.Json.Converters
|
|||
/// <inheritdoc />
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
|
|||
{
|
||||
var desiredValue = new GenericBodyArrayModel<string>
|
||||
{
|
||||
Value = new[] { "a", "b", "c" }
|
||||
Value = ["a", "b", "c"]
|
||||
};
|
||||
|
||||
var value = JsonSerializer.Deserialize<GenericBodyArrayModel<string>>(@"{ ""Value"": ""a,b,c"" }", _jsonOptions);
|
||||
|
@ -53,7 +53,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
|
|||
{
|
||||
var desiredValue = new GenericBodyArrayModel<string>
|
||||
{
|
||||
Value = new[] { "a", "b", "c" }
|
||||
Value = ["a", "b", "c"]
|
||||
};
|
||||
|
||||
var value = JsonSerializer.Deserialize<GenericBodyArrayModel<string>>(@"{ ""Value"": ""a, b, c"" }", _jsonOptions);
|
||||
|
@ -65,7 +65,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
|
|||
{
|
||||
var desiredValue = new GenericBodyArrayModel<GeneralCommandType>
|
||||
{
|
||||
Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown }
|
||||
Value = [GeneralCommandType.MoveUp, GeneralCommandType.MoveDown]
|
||||
};
|
||||
|
||||
var value = JsonSerializer.Deserialize<GenericBodyArrayModel<GeneralCommandType>>(@"{ ""Value"": ""MoveUp,MoveDown"" }", _jsonOptions);
|
||||
|
@ -77,7 +77,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
|
|||
{
|
||||
var desiredValue = new GenericBodyArrayModel<GeneralCommandType>
|
||||
{
|
||||
Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown }
|
||||
Value = [GeneralCommandType.MoveUp, GeneralCommandType.MoveDown]
|
||||
};
|
||||
|
||||
var value = JsonSerializer.Deserialize<GenericBodyArrayModel<GeneralCommandType>>(@"{ ""Value"": ""MoveUp,,MoveDown"" }", _jsonOptions);
|
||||
|
@ -89,7 +89,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
|
|||
{
|
||||
var desiredValue = new GenericBodyArrayModel<GeneralCommandType>
|
||||
{
|
||||
Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown }
|
||||
Value = [GeneralCommandType.MoveUp, GeneralCommandType.MoveDown]
|
||||
};
|
||||
|
||||
var value = JsonSerializer.Deserialize<GenericBodyArrayModel<GeneralCommandType>>(@"{ ""Value"": ""MoveUp,TotallyNotAVallidCommand,MoveDown"" }", _jsonOptions);
|
||||
|
@ -101,7 +101,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
|
|||
{
|
||||
var desiredValue = new GenericBodyArrayModel<GeneralCommandType>
|
||||
{
|
||||
Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown }
|
||||
Value = [GeneralCommandType.MoveUp, GeneralCommandType.MoveDown]
|
||||
};
|
||||
|
||||
var value = JsonSerializer.Deserialize<GenericBodyArrayModel<GeneralCommandType>>(@"{ ""Value"": ""MoveUp, MoveDown"" }", _jsonOptions);
|
||||
|
@ -113,7 +113,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
|
|||
{
|
||||
var desiredValue = new GenericBodyArrayModel<string>
|
||||
{
|
||||
Value = new[] { "a", "b", "c" }
|
||||
Value = ["a", "b", "c"]
|
||||
};
|
||||
|
||||
var value = JsonSerializer.Deserialize<GenericBodyArrayModel<string>>(@"{ ""Value"": [""a"",""b"",""c""] }", _jsonOptions);
|
||||
|
@ -125,7 +125,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters
|
|||
{
|
||||
var desiredValue = new GenericBodyArrayModel<GeneralCommandType>
|
||||
{
|
||||
Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown }
|
||||
Value = [GeneralCommandType.MoveUp, GeneralCommandType.MoveDown]
|
||||
};
|
||||
|
||||
var value = JsonSerializer.Deserialize<GenericBodyArrayModel<GeneralCommandType>>(@"{ ""Value"": [""MoveUp"", ""MoveDown""] }", _jsonOptions);
|
||||
|
|
Loading…
Reference in a new issue