aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Api/Controllers
diff options
context:
space:
mode:
authorcrobibero <cody@robibe.ro>2020-06-04 15:16:04 -0600
committercrobibero <cody@robibe.ro>2020-06-04 15:16:04 -0600
commita787efc66027b678cb09e83002ce3f9ba00e206d (patch)
treecc460c9a764461158c548eb72332d82d3c1d7280 /Jellyfin.Api/Controllers
parent341b947cdecdfc791c1bc3e72da1e68cd3754c3a (diff)
parent601e4a88c94ea0ccaab9f3c148d4bbdca2e532ee (diff)
Merge remote-tracking branch 'upstream/api-migration' into api-scheduled-tasks
Diffstat (limited to 'Jellyfin.Api/Controllers')
-rw-r--r--Jellyfin.Api/Controllers/ConfigurationController.cs125
-rw-r--r--Jellyfin.Api/Controllers/DevicesController.cs156
-rw-r--r--Jellyfin.Api/Controllers/NotificationsController.cs159
-rw-r--r--Jellyfin.Api/Controllers/StartupController.cs58
4 files changed, 479 insertions, 19 deletions
diff --git a/Jellyfin.Api/Controllers/ConfigurationController.cs b/Jellyfin.Api/Controllers/ConfigurationController.cs
new file mode 100644
index 000000000..8243bfce4
--- /dev/null
+++ b/Jellyfin.Api/Controllers/ConfigurationController.cs
@@ -0,0 +1,125 @@
+#nullable enable
+
+using System.Text.Json;
+using System.Threading.Tasks;
+using Jellyfin.Api.Constants;
+using Jellyfin.Api.Models.ConfigurationDtos;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Configuration;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+
+namespace Jellyfin.Api.Controllers
+{
+ /// <summary>
+ /// Configuration Controller.
+ /// </summary>
+ [Route("System")]
+ [Authorize]
+ public class ConfigurationController : BaseJellyfinApiController
+ {
+ private readonly IServerConfigurationManager _configurationManager;
+ private readonly IMediaEncoder _mediaEncoder;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ConfigurationController"/> class.
+ /// </summary>
+ /// <param name="configurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
+ /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
+ public ConfigurationController(
+ IServerConfigurationManager configurationManager,
+ IMediaEncoder mediaEncoder)
+ {
+ _configurationManager = configurationManager;
+ _mediaEncoder = mediaEncoder;
+ }
+
+ /// <summary>
+ /// Gets application configuration.
+ /// </summary>
+ /// <response code="200">Application configuration returned.</response>
+ /// <returns>Application configuration.</returns>
+ [HttpGet("Configuration")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult<ServerConfiguration> GetConfiguration()
+ {
+ return _configurationManager.Configuration;
+ }
+
+ /// <summary>
+ /// Updates application configuration.
+ /// </summary>
+ /// <param name="configuration">Configuration.</param>
+ /// <response code="200">Configuration updated.</response>
+ /// <returns>Update status.</returns>
+ [HttpPost("Configuration")]
+ [Authorize(Policy = Policies.RequiresElevation)]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult UpdateConfiguration([FromBody, BindRequired] ServerConfiguration configuration)
+ {
+ _configurationManager.ReplaceConfiguration(configuration);
+ return Ok();
+ }
+
+ /// <summary>
+ /// Gets a named configuration.
+ /// </summary>
+ /// <param name="key">Configuration key.</param>
+ /// <response code="200">Configuration returned.</response>
+ /// <returns>Configuration.</returns>
+ [HttpGet("Configuration/{Key}")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult<object> GetNamedConfiguration([FromRoute] string key)
+ {
+ return _configurationManager.GetConfiguration(key);
+ }
+
+ /// <summary>
+ /// Updates named configuration.
+ /// </summary>
+ /// <param name="key">Configuration key.</param>
+ /// <response code="200">Named configuration updated.</response>
+ /// <returns>Update status.</returns>
+ [HttpPost("Configuration/{Key}")]
+ [Authorize(Policy = Policies.RequiresElevation)]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public async Task<ActionResult> UpdateNamedConfiguration([FromRoute] string key)
+ {
+ var configurationType = _configurationManager.GetConfigurationType(key);
+ var configuration = await JsonSerializer.DeserializeAsync(Request.Body, configurationType);
+ _configurationManager.SaveConfiguration(key, configuration);
+ return Ok();
+ }
+
+ /// <summary>
+ /// Gets a default MetadataOptions object.
+ /// </summary>
+ /// <response code="200">Metadata options returned.</response>
+ /// <returns>Default MetadataOptions.</returns>
+ [HttpGet("Configuration/MetadataOptions/Default")]
+ [Authorize(Policy = Policies.RequiresElevation)]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult<MetadataOptions> GetDefaultMetadataOptions()
+ {
+ return new MetadataOptions();
+ }
+
+ /// <summary>
+ /// Updates the path to the media encoder.
+ /// </summary>
+ /// <param name="mediaEncoderPath">Media encoder path form body.</param>
+ /// <response code="200">Media encoder path updated.</response>
+ /// <returns>Status.</returns>
+ [HttpPost("MediaEncoder/Path")]
+ [Authorize(Policy = Policies.FirstTimeSetupOrElevated)]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult UpdateMediaEncoderPath([FromForm, BindRequired] MediaEncoderPathDto mediaEncoderPath)
+ {
+ _mediaEncoder.UpdateEncoderPath(mediaEncoderPath.Path, mediaEncoderPath.PathType);
+ return Ok();
+ }
+ }
+}
diff --git a/Jellyfin.Api/Controllers/DevicesController.cs b/Jellyfin.Api/Controllers/DevicesController.cs
new file mode 100644
index 000000000..1e7557903
--- /dev/null
+++ b/Jellyfin.Api/Controllers/DevicesController.cs
@@ -0,0 +1,156 @@
+#nullable enable
+
+using System;
+using Jellyfin.Api.Constants;
+using MediaBrowser.Controller.Devices;
+using MediaBrowser.Controller.Security;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Devices;
+using MediaBrowser.Model.Querying;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+
+namespace Jellyfin.Api.Controllers
+{
+ /// <summary>
+ /// Devices Controller.
+ /// </summary>
+ [Authorize]
+ public class DevicesController : BaseJellyfinApiController
+ {
+ private readonly IDeviceManager _deviceManager;
+ private readonly IAuthenticationRepository _authenticationRepository;
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DevicesController"/> class.
+ /// </summary>
+ /// <param name="deviceManager">Instance of <see cref="IDeviceManager"/> interface.</param>
+ /// <param name="authenticationRepository">Instance of <see cref="IAuthenticationRepository"/> interface.</param>
+ /// <param name="sessionManager">Instance of <see cref="ISessionManager"/> interface.</param>
+ public DevicesController(
+ IDeviceManager deviceManager,
+ IAuthenticationRepository authenticationRepository,
+ ISessionManager sessionManager)
+ {
+ _deviceManager = deviceManager;
+ _authenticationRepository = authenticationRepository;
+ _sessionManager = sessionManager;
+ }
+
+ /// <summary>
+ /// Get Devices.
+ /// </summary>
+ /// <param name="supportsSync">Gets or sets a value indicating whether [supports synchronize].</param>
+ /// <param name="userId">Gets or sets the user identifier.</param>
+ /// <response code="200">Devices retrieved.</response>
+ /// <returns>An <see cref="OkResult"/> containing the list of devices.</returns>
+ [HttpGet]
+ [Authorize(Policy = Policies.RequiresElevation)]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult<QueryResult<DeviceInfo>> GetDevices([FromQuery] bool? supportsSync, [FromQuery] Guid? userId)
+ {
+ var deviceQuery = new DeviceQuery { SupportsSync = supportsSync, UserId = userId ?? Guid.Empty };
+ return _deviceManager.GetDevices(deviceQuery);
+ }
+
+ /// <summary>
+ /// Get info for a device.
+ /// </summary>
+ /// <param name="id">Device Id.</param>
+ /// <response code="200">Device info retrieved.</response>
+ /// <response code="404">Device not found.</response>
+ /// <returns>An <see cref="OkResult"/> containing the device info on success, or a <see cref="NotFoundResult"/> if the device could not be found.</returns>
+ [HttpGet("Info")]
+ [Authorize(Policy = Policies.RequiresElevation)]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public ActionResult<DeviceInfo> GetDeviceInfo([FromQuery, BindRequired] string id)
+ {
+ var deviceInfo = _deviceManager.GetDevice(id);
+ if (deviceInfo == null)
+ {
+ return NotFound();
+ }
+
+ return deviceInfo;
+ }
+
+ /// <summary>
+ /// Get options for a device.
+ /// </summary>
+ /// <param name="id">Device Id.</param>
+ /// <response code="200">Device options retrieved.</response>
+ /// <response code="404">Device not found.</response>
+ /// <returns>An <see cref="OkResult"/> containing the device info on success, or a <see cref="NotFoundResult"/> if the device could not be found.</returns>
+ [HttpGet("Options")]
+ [Authorize(Policy = Policies.RequiresElevation)]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public ActionResult<DeviceOptions> GetDeviceOptions([FromQuery, BindRequired] string id)
+ {
+ var deviceInfo = _deviceManager.GetDeviceOptions(id);
+ if (deviceInfo == null)
+ {
+ return NotFound();
+ }
+
+ return deviceInfo;
+ }
+
+ /// <summary>
+ /// Update device options.
+ /// </summary>
+ /// <param name="id">Device Id.</param>
+ /// <param name="deviceOptions">Device Options.</param>
+ /// <response code="200">Device options updated.</response>
+ /// <response code="404">Device not found.</response>
+ /// <returns>An <see cref="OkResult"/> on success, or a <see cref="NotFoundResult"/> if the device could not be found.</returns>
+ [HttpPost("Options")]
+ [Authorize(Policy = Policies.RequiresElevation)]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public ActionResult UpdateDeviceOptions(
+ [FromQuery, BindRequired] string id,
+ [FromBody, BindRequired] DeviceOptions deviceOptions)
+ {
+ var existingDeviceOptions = _deviceManager.GetDeviceOptions(id);
+ if (existingDeviceOptions == null)
+ {
+ return NotFound();
+ }
+
+ _deviceManager.UpdateDeviceOptions(id, deviceOptions);
+ return Ok();
+ }
+
+ /// <summary>
+ /// Deletes a device.
+ /// </summary>
+ /// <param name="id">Device Id.</param>
+ /// <response code="200">Device deleted.</response>
+ /// <response code="404">Device not found.</response>
+ /// <returns>An <see cref="OkResult"/> on success, or a <see cref="NotFoundResult"/> if the device could not be found.</returns>
+ [HttpDelete]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult DeleteDevice([FromQuery, BindRequired] string id)
+ {
+ var existingDevice = _deviceManager.GetDevice(id);
+ if (existingDevice == null)
+ {
+ return NotFound();
+ }
+
+ var sessions = _authenticationRepository.Get(new AuthenticationInfoQuery { DeviceId = id }).Items;
+
+ foreach (var session in sessions)
+ {
+ _sessionManager.Logout(session);
+ }
+
+ return Ok();
+ }
+ }
+}
diff --git a/Jellyfin.Api/Controllers/NotificationsController.cs b/Jellyfin.Api/Controllers/NotificationsController.cs
new file mode 100644
index 000000000..8d82ca10f
--- /dev/null
+++ b/Jellyfin.Api/Controllers/NotificationsController.cs
@@ -0,0 +1,159 @@
+#nullable enable
+#pragma warning disable CA1801
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Jellyfin.Api.Models.NotificationDtos;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Notifications;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Notifications;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Jellyfin.Api.Controllers
+{
+ /// <summary>
+ /// The notification controller.
+ /// </summary>
+ public class NotificationsController : BaseJellyfinApiController
+ {
+ private readonly INotificationManager _notificationManager;
+ private readonly IUserManager _userManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NotificationsController" /> class.
+ /// </summary>
+ /// <param name="notificationManager">The notification manager.</param>
+ /// <param name="userManager">The user manager.</param>
+ public NotificationsController(INotificationManager notificationManager, IUserManager userManager)
+ {
+ _notificationManager = notificationManager;
+ _userManager = userManager;
+ }
+
+ /// <summary>
+ /// Gets a user's notifications.
+ /// </summary>
+ /// <param name="userId">The user's ID.</param>
+ /// <param name="isRead">An optional filter by notification read state.</param>
+ /// <param name="startIndex">The optional index to start at. All notifications with a lower index will be omitted from the results.</param>
+ /// <param name="limit">An optional limit on the number of notifications returned.</param>
+ /// <response code="200">Notifications returned.</response>
+ /// <returns>An <see cref="OkResult"/> containing a list of notifications.</returns>
+ [HttpGet("{UserID}")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult<NotificationResultDto> GetNotifications(
+ [FromRoute] string userId,
+ [FromQuery] bool? isRead,
+ [FromQuery] int? startIndex,
+ [FromQuery] int? limit)
+ {
+ return new NotificationResultDto();
+ }
+
+ /// <summary>
+ /// Gets a user's notification summary.
+ /// </summary>
+ /// <param name="userId">The user's ID.</param>
+ /// <response code="200">Summary of user's notifications returned.</response>
+ /// <returns>An <cref see="OkResult"/> containing a summary of the users notifications.</returns>
+ [HttpGet("{UserID}/Summary")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult<NotificationsSummaryDto> GetNotificationsSummary(
+ [FromRoute] string userId)
+ {
+ return new NotificationsSummaryDto();
+ }
+
+ /// <summary>
+ /// Gets notification types.
+ /// </summary>
+ /// <response code="200">All notification types returned.</response>
+ /// <returns>An <cref see="OkResult"/> containing a list of all notification types.</returns>
+ [HttpGet("Types")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public IEnumerable<NotificationTypeInfo> GetNotificationTypes()
+ {
+ return _notificationManager.GetNotificationTypes();
+ }
+
+ /// <summary>
+ /// Gets notification services.
+ /// </summary>
+ /// <response code="200">All notification services returned.</response>
+ /// <returns>An <cref see="OkResult"/> containing a list of all notification services.</returns>
+ [HttpGet("Services")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public IEnumerable<NameIdPair> GetNotificationServices()
+ {
+ return _notificationManager.GetNotificationServices();
+ }
+
+ /// <summary>
+ /// Sends a notification to all admins.
+ /// </summary>
+ /// <param name="name">The name of the notification.</param>
+ /// <param name="description">The description of the notification.</param>
+ /// <param name="url">The URL of the notification.</param>
+ /// <param name="level">The level of the notification.</param>
+ /// <response code="200">Notification sent.</response>
+ /// <returns>An <cref see="OkResult"/>.</returns>
+ [HttpPost("Admin")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult CreateAdminNotification(
+ [FromQuery] string name,
+ [FromQuery] string description,
+ [FromQuery] string? url,
+ [FromQuery] NotificationLevel? level)
+ {
+ var notification = new NotificationRequest
+ {
+ Name = name,
+ Description = description,
+ Url = url,
+ Level = level ?? NotificationLevel.Normal,
+ UserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id).ToArray(),
+ Date = DateTime.UtcNow,
+ };
+
+ _notificationManager.SendNotification(notification, CancellationToken.None);
+
+ return Ok();
+ }
+
+ /// <summary>
+ /// Sets notifications as read.
+ /// </summary>
+ /// <param name="userId">The userID.</param>
+ /// <param name="ids">A comma-separated list of the IDs of notifications which should be set as read.</param>
+ /// <response code="200">Notifications set as read.</response>
+ /// <returns>An <cref see="OkResult"/>.</returns>
+ [HttpPost("{UserID}/Read")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult SetRead(
+ [FromRoute] string userId,
+ [FromQuery] string ids)
+ {
+ return Ok();
+ }
+
+ /// <summary>
+ /// Sets notifications as unread.
+ /// </summary>
+ /// <param name="userId">The userID.</param>
+ /// <param name="ids">A comma-separated list of the IDs of notifications which should be set as unread.</param>
+ /// <response code="200">Notifications set as unread.</response>
+ /// <returns>An <cref see="OkResult"/>.</returns>
+ [HttpPost("{UserID}/Unread")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult SetUnread(
+ [FromRoute] string userId,
+ [FromQuery] string ids)
+ {
+ return Ok();
+ }
+ }
+}
diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs
index afc9b8f3d..ed1dc1ede 100644
--- a/Jellyfin.Api/Controllers/StartupController.cs
+++ b/Jellyfin.Api/Controllers/StartupController.cs
@@ -5,6 +5,7 @@ using Jellyfin.Api.Models.StartupDtos;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Jellyfin.Api.Controllers
@@ -30,22 +31,28 @@ namespace Jellyfin.Api.Controllers
}
/// <summary>
- /// Api endpoint for completing the startup wizard.
+ /// Completes the startup wizard.
/// </summary>
+ /// <response code="200">Startup wizard completed.</response>
+ /// <returns>An <see cref="OkResult"/> indicating success.</returns>
[HttpPost("Complete")]
- public void CompleteWizard()
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult CompleteWizard()
{
_config.Configuration.IsStartupWizardCompleted = true;
_config.SetOptimalValues();
_config.SaveConfiguration();
+ return Ok();
}
/// <summary>
- /// Endpoint for getting the initial startup wizard configuration.
+ /// Gets the initial startup wizard configuration.
/// </summary>
- /// <returns>The initial startup wizard configuration.</returns>
+ /// <response code="200">Initial startup wizard configuration retrieved.</response>
+ /// <returns>An <see cref="OkResult"/> containing the initial startup wizard configuration.</returns>
[HttpGet("Configuration")]
- public StartupConfigurationDto GetStartupConfiguration()
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult<StartupConfigurationDto> GetStartupConfiguration()
{
var result = new StartupConfigurationDto
{
@@ -58,13 +65,16 @@ namespace Jellyfin.Api.Controllers
}
/// <summary>
- /// Endpoint for updating the initial startup wizard configuration.
+ /// Sets the initial startup wizard configuration.
/// </summary>
/// <param name="uiCulture">The UI language culture.</param>
/// <param name="metadataCountryCode">The metadata country code.</param>
/// <param name="preferredMetadataLanguage">The preferred language for metadata.</param>
+ /// <response code="200">Configuration saved.</response>
+ /// <returns>An <see cref="OkResult"/> indicating success.</returns>
[HttpPost("Configuration")]
- public void UpdateInitialConfiguration(
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult UpdateInitialConfiguration(
[FromForm] string uiCulture,
[FromForm] string metadataCountryCode,
[FromForm] string preferredMetadataLanguage)
@@ -73,43 +83,51 @@ namespace Jellyfin.Api.Controllers
_config.Configuration.MetadataCountryCode = metadataCountryCode;
_config.Configuration.PreferredMetadataLanguage = preferredMetadataLanguage;
_config.SaveConfiguration();
+ return Ok();
}
/// <summary>
- /// Endpoint for (dis)allowing remote access and UPnP.
+ /// Sets remote access and UPnP.
/// </summary>
/// <param name="enableRemoteAccess">Enable remote access.</param>
/// <param name="enableAutomaticPortMapping">Enable UPnP.</param>
+ /// <response code="200">Configuration saved.</response>
+ /// <returns>An <see cref="OkResult"/> indicating success.</returns>
[HttpPost("RemoteAccess")]
- public void SetRemoteAccess([FromForm] bool enableRemoteAccess, [FromForm] bool enableAutomaticPortMapping)
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult SetRemoteAccess([FromForm] bool enableRemoteAccess, [FromForm] bool enableAutomaticPortMapping)
{
_config.Configuration.EnableRemoteAccess = enableRemoteAccess;
_config.Configuration.EnableUPnP = enableAutomaticPortMapping;
_config.SaveConfiguration();
+ return Ok();
}
/// <summary>
- /// Endpoint for returning the first user.
+ /// Gets the first user.
/// </summary>
+ /// <response code="200">Initial user retrieved.</response>
/// <returns>The first user.</returns>
[HttpGet("User")]
- public StartupUserDto GetFirstUser()
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult<StartupUserDto> GetFirstUser()
{
var user = _userManager.Users.First();
- return new StartupUserDto
- {
- Name = user.Name,
- Password = user.Password
- };
+ return new StartupUserDto { Name = user.Name, Password = user.Password };
}
/// <summary>
- /// Endpoint for updating the user name and password.
+ /// Sets the user name and password.
/// </summary>
/// <param name="startupUserDto">The DTO containing username and password.</param>
- /// <returns>The async task.</returns>
+ /// <response code="200">Updated user name and password.</response>
+ /// <returns>
+ /// A <see cref="Task" /> that represents the asynchronous update operation.
+ /// The task result contains an <see cref="OkResult"/> indicating success.
+ /// </returns>
[HttpPost("User")]
- public async Task UpdateUser([FromForm] StartupUserDto startupUserDto)
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public async Task<ActionResult> UpdateUser([FromForm] StartupUserDto startupUserDto)
{
var user = _userManager.Users.First();
@@ -121,6 +139,8 @@ namespace Jellyfin.Api.Controllers
{
await _userManager.ChangePassword(user, startupUserDto.Password).ConfigureAwait(false);
}
+
+ return Ok();
}
}
}