From 035d29fb357006c29ffb40e0a53c1e999237cdd1 Mon Sep 17 00:00:00 2001 From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Thu, 13 Aug 2020 15:35:04 -0500 Subject: Migrate to new API standard --- Jellyfin.Api/Controllers/UserController.cs | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 272312522..131fffb7a 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -216,6 +216,47 @@ namespace Jellyfin.Api.Controllers } } + /// + /// Authenticates a user with quick connect. + /// + /// The request. + /// User authenticated. + /// Missing token. + /// A containing an with information about the new session. + [HttpPost("AuthenticateWithQuickConnect")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> AuthenticateWithQuickConnect([FromBody, Required] QuickConnectDto request) + { + if (request.Token == null) + { + return BadRequest("Access token is required."); + } + + var auth = _authContext.GetAuthorizationInfo(Request); + + try + { + var authRequest = new AuthenticationRequest + { + App = auth.Client, + AppVersion = auth.Version, + DeviceId = auth.DeviceId, + DeviceName = auth.Device, + }; + + var result = await _sessionManager.AuthenticateQuickConnect( + authRequest, + request.Token).ConfigureAwait(false); + + return result; + } + catch (SecurityException e) + { + // rethrow adding IP address to message + throw new SecurityException($"[{HttpContext.Connection.RemoteIpAddress}] {e.Message}", e); + } + } + /// /// Updates a user's password. /// -- cgit v1.2.3 From eaa57115347f6f70d478f2ca39601d2e70efbdaf Mon Sep 17 00:00:00 2001 From: ConfusedPolarBear <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Sun, 16 Aug 2020 17:21:08 -0500 Subject: Apply suggestions from code review Co-authored-by: Cody Robibero --- Jellyfin.Api/Controllers/QuickConnectController.cs | 15 +++++---------- Jellyfin.Api/Controllers/UserController.cs | 5 ----- Jellyfin.Api/Models/UserDtos/QuickConnectDto.cs | 1 + 3 files changed, 6 insertions(+), 15 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/QuickConnectController.cs b/Jellyfin.Api/Controllers/QuickConnectController.cs index d45ea058d..fd5453595 100644 --- a/Jellyfin.Api/Controllers/QuickConnectController.cs +++ b/Jellyfin.Api/Controllers/QuickConnectController.cs @@ -46,7 +46,7 @@ namespace Jellyfin.Api.Controllers public ActionResult GetStatus() { _quickConnect.ExpireRequests(); - return Ok(_quickConnect.State); + return _quickConnect.State; } /// @@ -60,7 +60,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult Initiate([FromQuery] string? friendlyName) { - return Ok(_quickConnect.TryConnect(friendlyName)); + return _quickConnect.TryConnect(friendlyName); } /// @@ -78,7 +78,7 @@ namespace Jellyfin.Api.Controllers try { var result = _quickConnect.CheckRequestStatus(secret); - return Ok(result); + return result; } catch (ResourceNotFoundException) { @@ -135,12 +135,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status400BadRequest)] public ActionResult Authorize([FromQuery, Required] string? code) { - if (code == null) - { - return BadRequest("Missing code"); - } - - return Ok(_quickConnect.AuthorizeRequest(Request, code)); + return _quickConnect.AuthorizeRequest(Request, code); } /// @@ -153,7 +148,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult Deauthorize() { - var userId = _authContext.GetAuthorizationInfo(Request).UserId; + var userId = ClaimHelpers.GetUserId(request.HttpContext.User); return _quickConnect.DeleteAllDevices(userId); } } diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 131fffb7a..355816bd3 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -227,11 +227,6 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public async Task> AuthenticateWithQuickConnect([FromBody, Required] QuickConnectDto request) { - if (request.Token == null) - { - return BadRequest("Access token is required."); - } - var auth = _authContext.GetAuthorizationInfo(Request); try diff --git a/Jellyfin.Api/Models/UserDtos/QuickConnectDto.cs b/Jellyfin.Api/Models/UserDtos/QuickConnectDto.cs index 8f53d5f37..ac0949732 100644 --- a/Jellyfin.Api/Models/UserDtos/QuickConnectDto.cs +++ b/Jellyfin.Api/Models/UserDtos/QuickConnectDto.cs @@ -8,6 +8,7 @@ /// /// Gets or sets the quick connect token. /// + [Required] public string? Token { get; set; } } } -- cgit v1.2.3 From 5f1a86324170387f12602d77dad7249faf30548f Mon Sep 17 00:00:00 2001 From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Mon, 17 Aug 2020 16:36:45 -0500 Subject: Apply suggestions from code review --- .../QuickConnect/QuickConnectManager.cs | 38 +++++++++------------- .../Session/SessionManager.cs | 2 +- Jellyfin.Api/Controllers/QuickConnectController.cs | 34 +++++++++---------- Jellyfin.Api/Controllers/UserController.cs | 4 +-- .../QuickConnect/IQuickConnect.cs | 12 +++---- .../QuickConnect/QuickConnectResult.cs | 5 --- 6 files changed, 40 insertions(+), 55 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs b/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs index 949c3b505..52e934229 100644 --- a/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs +++ b/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs @@ -3,17 +3,16 @@ using System.Collections.Concurrent; using System.Globalization; using System.Linq; using System.Security.Cryptography; +using MediaBrowser.Common; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; +using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.QuickConnect; using MediaBrowser.Controller.Security; using MediaBrowser.Model.QuickConnect; -using Microsoft.AspNetCore.Http; -using MediaBrowser.Common; using Microsoft.Extensions.Logging; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Authentication; namespace Emby.Server.Implementations.QuickConnect { @@ -60,7 +59,7 @@ namespace Emby.Server.Implementations.QuickConnect public int CodeLength { get; set; } = 6; /// - public string TokenNamePrefix { get; set; } = "QuickConnect-"; + public string TokenName { get; set; } = "QuickConnect"; /// public QuickConnectState State { get; private set; } = QuickConnectState.Unavailable; @@ -82,7 +81,7 @@ namespace Emby.Server.Implementations.QuickConnect /// public void Activate() { - DateActivated = DateTime.Now; + DateActivated = DateTime.UtcNow; SetState(QuickConnectState.Active); } @@ -101,7 +100,7 @@ namespace Emby.Server.Implementations.QuickConnect } /// - public QuickConnectResult TryConnect(string friendlyName) + public QuickConnectResult TryConnect() { ExpireRequests(); @@ -111,14 +110,11 @@ namespace Emby.Server.Implementations.QuickConnect throw new AuthenticationException("Quick connect is not active on this server"); } - _logger.LogDebug("Got new quick connect request from {friendlyName}", friendlyName); - var code = GenerateCode(); var result = new QuickConnectResult() { Secret = GenerateSecureRandom(), - FriendlyName = friendlyName, - DateAdded = DateTime.Now, + DateAdded = DateTime.UtcNow, Code = code }; @@ -162,13 +158,11 @@ namespace Emby.Server.Implementations.QuickConnect } /// - public bool AuthorizeRequest(HttpRequest request, string code) + public bool AuthorizeRequest(Guid userId, string code) { ExpireRequests(); AssertActive(); - var auth = _authContext.GetAuthorizationInfo(request); - if (!_currentRequests.TryGetValue(code, out QuickConnectResult result)) { throw new ResourceNotFoundException("Unable to find request"); @@ -182,21 +176,21 @@ namespace Emby.Server.Implementations.QuickConnect result.Authentication = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); // Change the time on the request so it expires one minute into the future. It can't expire immediately as otherwise some clients wouldn't ever see that they have been authenticated. - var added = result.DateAdded ?? DateTime.Now.Subtract(new TimeSpan(0, Timeout, 0)); - result.DateAdded = added.Subtract(new TimeSpan(0, Timeout - 1, 0)); + var added = result.DateAdded ?? DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(Timeout)); + result.DateAdded = added.Subtract(TimeSpan.FromMinutes(Timeout - 1)); _authenticationRepository.Create(new AuthenticationInfo { - AppName = TokenNamePrefix + result.FriendlyName, + AppName = TokenName, AccessToken = result.Authentication, DateCreated = DateTime.UtcNow, DeviceId = _appHost.SystemId, DeviceName = _appHost.FriendlyName, AppVersion = _appHost.ApplicationVersionString, - UserId = auth.UserId + UserId = userId }); - _logger.LogInformation("Allowing device {FriendlyName} to login as user {Username} with quick connect code {Code}", result.FriendlyName, auth.User.Username, result.Code); + _logger.LogDebug("Authorizing device with code {Code} to login as user {userId}", code, userId); return true; } @@ -210,7 +204,7 @@ namespace Emby.Server.Implementations.QuickConnect UserId = user }); - var tokens = raw.Items.Where(x => x.AppName.StartsWith(TokenNamePrefix, StringComparison.CurrentCulture)); + var tokens = raw.Items.Where(x => x.AppName.StartsWith(TokenName, StringComparison.CurrentCulture)); var removed = 0; foreach (var token in tokens) @@ -256,7 +250,7 @@ namespace Emby.Server.Implementations.QuickConnect public void ExpireRequests(bool expireAll = false) { // Check if quick connect should be deactivated - if (State == QuickConnectState.Active && DateTime.Now > DateActivated.AddMinutes(Timeout) && !expireAll) + if (State == QuickConnectState.Active && DateTime.UtcNow > DateActivated.AddMinutes(Timeout) && !expireAll) { _logger.LogDebug("Quick connect time expired, deactivating"); SetState(QuickConnectState.Available); @@ -270,7 +264,7 @@ namespace Emby.Server.Implementations.QuickConnect for (int i = 0; i < values.Count; i++) { var added = values[i].DateAdded ?? DateTime.UnixEpoch; - if (DateTime.Now > added.AddMinutes(Timeout) || expireAll) + if (DateTime.UtcNow > added.AddMinutes(Timeout) || expireAll) { code = values[i].Code; _logger.LogDebug("Removing expired request {code}", code); diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 8a8223ee7..fbe8e065c 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1433,7 +1433,7 @@ namespace Emby.Server.Implementations.Session Limit = 1 }); - if (result.TotalRecordCount < 1) + if (result.TotalRecordCount == 0) { throw new SecurityException("Unknown quick connect token"); } diff --git a/Jellyfin.Api/Controllers/QuickConnectController.cs b/Jellyfin.Api/Controllers/QuickConnectController.cs index 1625bcffe..b1ee2ff53 100644 --- a/Jellyfin.Api/Controllers/QuickConnectController.cs +++ b/Jellyfin.Api/Controllers/QuickConnectController.cs @@ -1,8 +1,8 @@ +using System; using System.ComponentModel.DataAnnotations; using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.QuickConnect; using MediaBrowser.Model.QuickConnect; @@ -18,22 +18,18 @@ namespace Jellyfin.Api.Controllers public class QuickConnectController : BaseJellyfinApiController { private readonly IQuickConnect _quickConnect; - private readonly IUserManager _userManager; private readonly IAuthorizationContext _authContext; /// /// Initializes a new instance of the class. /// /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. public QuickConnectController( IQuickConnect quickConnect, - IUserManager userManager, IAuthorizationContext authContext) { _quickConnect = quickConnect; - _userManager = userManager; _authContext = authContext; } @@ -53,15 +49,14 @@ namespace Jellyfin.Api.Controllers /// /// Initiate a new quick connect request. /// - /// Device friendly name. /// Quick connect request successfully created. /// Quick connect is not active on this server. /// A with a secret and code for future use or an error message. [HttpGet("Initiate")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult Initiate([FromQuery] string? friendlyName) + public ActionResult Initiate() { - return _quickConnect.TryConnect(friendlyName); + return _quickConnect.TryConnect(); } /// @@ -74,12 +69,11 @@ namespace Jellyfin.Api.Controllers [HttpGet("Connect")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult Connect([FromQuery] string? secret) + public ActionResult Connect([FromQuery, Required] string secret) { try { - var result = _quickConnect.CheckRequestStatus(secret); - return result; + return _quickConnect.CheckRequestStatus(secret); } catch (ResourceNotFoundException) { @@ -117,9 +111,9 @@ namespace Jellyfin.Api.Controllers [HttpPost("Available")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult Available([FromQuery] QuickConnectState? status) + public ActionResult Available([FromQuery] QuickConnectState status = QuickConnectState.Available) { - _quickConnect.SetState(status ?? QuickConnectState.Available); + _quickConnect.SetState(status); return NoContent(); } @@ -127,16 +121,22 @@ namespace Jellyfin.Api.Controllers /// Authorizes a pending quick connect request. /// /// Quick connect code to authorize. + /// User id. /// Quick connect result authorized successfully. - /// Missing quick connect code. + /// User is not allowed to authorize quick connect requests. /// Boolean indicating if the authorization was successful. [HttpPost("Authorize")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - public ActionResult Authorize([FromQuery, Required] string? code) + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public ActionResult Authorize([FromQuery, Required] string code, [FromQuery, Required] Guid userId) { - return _quickConnect.AuthorizeRequest(Request, code); + if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) + { + return Forbid("User is not allowed to authorize quick connect requests."); + } + + return _quickConnect.AuthorizeRequest(userId, code); } /// diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 355816bd3..d67f82219 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -239,11 +239,9 @@ namespace Jellyfin.Api.Controllers DeviceName = auth.Device, }; - var result = await _sessionManager.AuthenticateQuickConnect( + return await _sessionManager.AuthenticateQuickConnect( authRequest, request.Token).ConfigureAwait(false); - - return result; } catch (SecurityException e) { diff --git a/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs b/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs index fd7e973f6..959a2d771 100644 --- a/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs +++ b/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs @@ -1,6 +1,5 @@ using System; using MediaBrowser.Model.QuickConnect; -using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller.QuickConnect { @@ -15,9 +14,9 @@ namespace MediaBrowser.Controller.QuickConnect int CodeLength { get; set; } /// - /// Gets or sets the string to prefix internal access tokens with. + /// Gets or sets the name of internal access tokens. /// - string TokenNamePrefix { get; set; } + string TokenName { get; set; } /// /// Gets the current state of quick connect. @@ -48,9 +47,8 @@ namespace MediaBrowser.Controller.QuickConnect /// /// Initiates a new quick connect request. /// - /// Friendly device name to display in the request UI. /// A quick connect result with tokens to proceed or throws an exception if not active. - QuickConnectResult TryConnect(string friendlyName); + QuickConnectResult TryConnect(); /// /// Checks the status of an individual request. @@ -62,10 +60,10 @@ namespace MediaBrowser.Controller.QuickConnect /// /// Authorizes a quick connect request to connect as the calling user. /// - /// HTTP request object. + /// User id. /// Identifying code for the request. /// A boolean indicating if the authorization completed successfully. - bool AuthorizeRequest(HttpRequest request, string code); + bool AuthorizeRequest(Guid userId, string code); /// /// Expire quick connect requests that are over the time limit. If is true, all requests are unconditionally expired. diff --git a/MediaBrowser.Model/QuickConnect/QuickConnectResult.cs b/MediaBrowser.Model/QuickConnect/QuickConnectResult.cs index a10d60d57..0fa40b6a7 100644 --- a/MediaBrowser.Model/QuickConnect/QuickConnectResult.cs +++ b/MediaBrowser.Model/QuickConnect/QuickConnectResult.cs @@ -22,11 +22,6 @@ namespace MediaBrowser.Model.QuickConnect /// public string? Code { get; set; } - /// - /// Gets or sets the device friendly name. - /// - public string? FriendlyName { get; set; } - /// /// Gets or sets the private access token. /// -- cgit v1.2.3 From 2f33bee2a94f7175050f83d568f8bd65a01a86f8 Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 1 Sep 2020 17:26:49 -0600 Subject: Set openapi schema type to file where possible --- .../Attributes/ProducesAudioFileAttribute.cs | 18 ++++++++ Jellyfin.Api/Attributes/ProducesFileAttribute.cs | 28 ++++++++++++ .../Attributes/ProducesImageFileAttribute.cs | 18 ++++++++ .../Attributes/ProducesPlaylistFileAttribute.cs | 18 ++++++++ .../Attributes/ProducesVideoFileAttribute.cs | 18 ++++++++ Jellyfin.Api/Controllers/AudioController.cs | 2 + .../Controllers/ConfigurationController.cs | 3 ++ Jellyfin.Api/Controllers/DashboardController.cs | 3 ++ Jellyfin.Api/Controllers/DlnaServerController.cs | 25 +++++++---- Jellyfin.Api/Controllers/DynamicHlsController.cs | 7 +++ Jellyfin.Api/Controllers/HlsSegmentController.cs | 4 ++ Jellyfin.Api/Controllers/ImageController.cs | 11 ++++- Jellyfin.Api/Controllers/ItemLookupController.cs | 10 +++-- Jellyfin.Api/Controllers/LibraryController.cs | 3 ++ Jellyfin.Api/Controllers/LiveTvController.cs | 4 ++ Jellyfin.Api/Controllers/MediaInfoController.cs | 2 + Jellyfin.Api/Controllers/SubtitleController.cs | 4 ++ Jellyfin.Api/Controllers/SystemController.cs | 9 ++-- .../Controllers/UniversalAudioController.cs | 2 + Jellyfin.Api/Controllers/UserController.cs | 6 +-- Jellyfin.Api/Controllers/VideoHlsController.cs | 2 + Jellyfin.Api/Controllers/VideosController.cs | 4 +- .../Extensions/ApiServiceCollectionExtensions.cs | 3 ++ Jellyfin.Server/Filters/FileResponseFilter.cs | 52 ++++++++++++++++++++++ 24 files changed, 232 insertions(+), 24 deletions(-) create mode 100644 Jellyfin.Api/Attributes/ProducesAudioFileAttribute.cs create mode 100644 Jellyfin.Api/Attributes/ProducesFileAttribute.cs create mode 100644 Jellyfin.Api/Attributes/ProducesImageFileAttribute.cs create mode 100644 Jellyfin.Api/Attributes/ProducesPlaylistFileAttribute.cs create mode 100644 Jellyfin.Api/Attributes/ProducesVideoFileAttribute.cs create mode 100644 Jellyfin.Server/Filters/FileResponseFilter.cs (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Attributes/ProducesAudioFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesAudioFileAttribute.cs new file mode 100644 index 000000000..3adb700eb --- /dev/null +++ b/Jellyfin.Api/Attributes/ProducesAudioFileAttribute.cs @@ -0,0 +1,18 @@ +namespace Jellyfin.Api.Attributes +{ + /// + /// Produces file attribute of "image/*". + /// + public class ProducesAudioFileAttribute : ProducesFileAttribute + { + private const string ContentType = "audio/*"; + + /// + /// Initializes a new instance of the class. + /// + public ProducesAudioFileAttribute() + : base(ContentType) + { + } + } +} diff --git a/Jellyfin.Api/Attributes/ProducesFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesFileAttribute.cs new file mode 100644 index 000000000..62a576ede --- /dev/null +++ b/Jellyfin.Api/Attributes/ProducesFileAttribute.cs @@ -0,0 +1,28 @@ +using System; + +namespace Jellyfin.Api.Attributes +{ + /// + /// Internal produces image attribute. + /// + [AttributeUsage(AttributeTargets.Method)] + public class ProducesFileAttribute : Attribute + { + private readonly string[] _contentTypes; + + /// + /// Initializes a new instance of the class. + /// + /// Content types this endpoint produces. + public ProducesFileAttribute(params string[] contentTypes) + { + _contentTypes = contentTypes; + } + + /// + /// Gets the configured content types. + /// + /// the configured content types. + public string[] GetContentTypes() => _contentTypes; + } +} diff --git a/Jellyfin.Api/Attributes/ProducesImageFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesImageFileAttribute.cs new file mode 100644 index 000000000..e15813676 --- /dev/null +++ b/Jellyfin.Api/Attributes/ProducesImageFileAttribute.cs @@ -0,0 +1,18 @@ +namespace Jellyfin.Api.Attributes +{ + /// + /// Produces file attribute of "image/*". + /// + public class ProducesImageFileAttribute : ProducesFileAttribute + { + private const string ContentType = "image/*"; + + /// + /// Initializes a new instance of the class. + /// + public ProducesImageFileAttribute() + : base(ContentType) + { + } + } +} diff --git a/Jellyfin.Api/Attributes/ProducesPlaylistFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesPlaylistFileAttribute.cs new file mode 100644 index 000000000..5d928ab91 --- /dev/null +++ b/Jellyfin.Api/Attributes/ProducesPlaylistFileAttribute.cs @@ -0,0 +1,18 @@ +namespace Jellyfin.Api.Attributes +{ + /// + /// Produces file attribute of "image/*". + /// + public class ProducesPlaylistFileAttribute : ProducesFileAttribute + { + private const string ContentType = "application/x-mpegURL"; + + /// + /// Initializes a new instance of the class. + /// + public ProducesPlaylistFileAttribute() + : base(ContentType) + { + } + } +} diff --git a/Jellyfin.Api/Attributes/ProducesVideoFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesVideoFileAttribute.cs new file mode 100644 index 000000000..d8b2856dc --- /dev/null +++ b/Jellyfin.Api/Attributes/ProducesVideoFileAttribute.cs @@ -0,0 +1,18 @@ +namespace Jellyfin.Api.Attributes +{ + /// + /// Produces file attribute of "video/*". + /// + public class ProducesVideoFileAttribute : ProducesFileAttribute + { + private const string ContentType = "video/*"; + + /// + /// Initializes a new instance of the class. + /// + public ProducesVideoFileAttribute() + : base(ContentType) + { + } + } +} diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index 802cd026e..48fd198b5 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.StreamingDtos; using MediaBrowser.Controller.MediaEncoding; @@ -88,6 +89,7 @@ namespace Jellyfin.Api.Controllers [HttpHead("{itemId}/stream.{container}", Name = "HeadAudioStreamByContainer")] [HttpHead("{itemId}/stream", Name = "HeadAudioStream")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesAudioFile] public async Task GetAudioStream( [FromRoute] Guid itemId, [FromRoute] string? container, diff --git a/Jellyfin.Api/Controllers/ConfigurationController.cs b/Jellyfin.Api/Controllers/ConfigurationController.cs index 20fb0ec87..606e27970 100644 --- a/Jellyfin.Api/Controllers/ConfigurationController.cs +++ b/Jellyfin.Api/Controllers/ConfigurationController.cs @@ -1,6 +1,8 @@ using System.ComponentModel.DataAnnotations; +using System.Net.Mime; using System.Text.Json; using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using Jellyfin.Api.Models.ConfigurationDtos; using MediaBrowser.Common.Json; @@ -73,6 +75,7 @@ namespace Jellyfin.Api.Controllers /// Configuration. [HttpGet("Configuration/{key}")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesFile(MediaTypeNames.Application.Json)] public ActionResult GetNamedConfiguration([FromRoute] string? key) { return _configurationManager.GetConfiguration(key); diff --git a/Jellyfin.Api/Controllers/DashboardController.cs b/Jellyfin.Api/Controllers/DashboardController.cs index 33abe3ccd..c1bcc71e3 100644 --- a/Jellyfin.Api/Controllers/DashboardController.cs +++ b/Jellyfin.Api/Controllers/DashboardController.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Mime; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Models; using MediaBrowser.Common.Plugins; using MediaBrowser.Controller; @@ -123,6 +125,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("web/ConfigurationPage")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesFile(MediaTypeNames.Text.Html, "application/x-javascript")] public ActionResult GetDashboardConfigurationPage([FromQuery] string? name) { IPlugin? plugin = null; diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index ee87d917b..dd91c7058 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Net.Mime; using System.Threading.Tasks; using Emby.Dlna; using Emby.Dlna.Main; @@ -17,8 +19,6 @@ namespace Jellyfin.Api.Controllers [Route("Dlna")] public class DlnaServerController : BaseJellyfinApiController { - private const string XMLContentType = "text/xml; charset=UTF-8"; - private readonly IDlnaManager _dlnaManager; private readonly IContentDirectory _contentDirectory; private readonly IConnectionManager _connectionManager; @@ -44,7 +44,8 @@ namespace Jellyfin.Api.Controllers /// An containing the description xml. [HttpGet("{serverId}/description")] [HttpGet("{serverId}/description.xml", Name = "GetDescriptionXml_2")] - [Produces(XMLContentType)] + [Produces(MediaTypeNames.Text.Xml)] + [ProducesFile(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetDescriptionXml([FromRoute] string serverId) { @@ -62,7 +63,8 @@ namespace Jellyfin.Api.Controllers /// An containing the dlna content directory xml. [HttpGet("{serverId}/ContentDirectory")] [HttpGet("{serverId}/ContentDirectory.xml", Name = "GetContentDirectory_2")] - [Produces(XMLContentType)] + [Produces(MediaTypeNames.Text.Xml)] + [ProducesFile(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] public ActionResult GetContentDirectory([FromRoute] string serverId) @@ -77,7 +79,8 @@ namespace Jellyfin.Api.Controllers /// Dlna media receiver registrar xml. [HttpGet("{serverId}/MediaReceiverRegistrar")] [HttpGet("{serverId}/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_2")] - [Produces(XMLContentType)] + [Produces(MediaTypeNames.Text.Xml)] + [ProducesFile(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] public ActionResult GetMediaReceiverRegistrar([FromRoute] string serverId) @@ -92,7 +95,8 @@ namespace Jellyfin.Api.Controllers /// Dlna media receiver registrar xml. [HttpGet("{serverId}/ConnectionManager")] [HttpGet("{serverId}/ConnectionManager.xml", Name = "GetConnectionManager_2")] - [Produces(XMLContentType)] + [Produces(MediaTypeNames.Text.Xml)] + [ProducesFile(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] public ActionResult GetConnectionManager([FromRoute] string serverId) @@ -183,7 +187,9 @@ namespace Jellyfin.Api.Controllers /// Icon stream. [HttpGet("{serverId}/icons/{fileName}")] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetIconId([FromRoute] string serverId, [FromRoute] string fileName) + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesImageFile] + public ActionResult GetIconId([FromRoute] string serverId, [FromRoute] string fileName) { return GetIconInternal(fileName); } @@ -194,12 +200,13 @@ namespace Jellyfin.Api.Controllers /// The icon filename. /// Icon stream. [HttpGet("icons/{fileName}")] - public ActionResult GetIcon([FromRoute] string fileName) + [ProducesImageFile] + public ActionResult GetIcon([FromRoute] string fileName) { return GetIconInternal(fileName); } - private ActionResult GetIconInternal(string fileName) + private ActionResult GetIconInternal(string fileName) { var icon = _dlnaManager.GetIcon(fileName); if (icon == null) diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index b115ac6cd..230bb8295 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.PlaybackDtos; @@ -166,6 +167,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Videos/{itemId}/master.m3u8")] [HttpHead("Videos/{itemId}/master.m3u8", Name = "HeadMasterHlsVideoPlaylist")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesPlaylistFile] public async Task GetMasterHlsVideoPlaylist( [FromRoute] Guid itemId, [FromRoute] string? container, @@ -333,6 +335,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Audio/{itemId}/master.m3u8")] [HttpHead("Audio/{itemId}/master.m3u8", Name = "HeadMasterHlsAudioPlaylist")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesPlaylistFile] public async Task GetMasterHlsAudioPlaylist( [FromRoute] Guid itemId, [FromRoute] string? container, @@ -498,6 +501,7 @@ namespace Jellyfin.Api.Controllers /// A containing the audio file. [HttpGet("Videos/{itemId}/main.m3u8")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesPlaylistFile] public async Task GetVariantHlsVideoPlaylist( [FromRoute] Guid itemId, [FromRoute] string? container, @@ -663,6 +667,7 @@ namespace Jellyfin.Api.Controllers /// A containing the audio file. [HttpGet("Audio/{itemId}/main.m3u8")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesPlaylistFile] public async Task GetVariantHlsAudioPlaylist( [FromRoute] Guid itemId, [FromRoute] string? container, @@ -830,6 +835,7 @@ namespace Jellyfin.Api.Controllers /// A containing the audio file. [HttpGet("Videos/{itemId}/hls1/{playlistId}/{segmentId}.{container}")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesVideoFile] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "playlistId", Justification = "Imported from ServiceStack")] public async Task GetHlsVideoSegment( [FromRoute] Guid itemId, @@ -999,6 +1005,7 @@ namespace Jellyfin.Api.Controllers /// A containing the audio file. [HttpGet("Audio/{itemId}/hls1/{playlistId}/{segmentId}.{container}")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesAudioFile] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "playlistId", Justification = "Imported from ServiceStack")] public async Task GetHlsAudioSegment( [FromRoute] Guid itemId, diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs index 816252f80..e534be50a 100644 --- a/Jellyfin.Api/Controllers/HlsSegmentController.cs +++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using MediaBrowser.Common.Configuration; @@ -54,6 +55,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Audio/{itemId}/hls/{segmentId}/stream.mp3", Name = "GetHlsAudioSegmentLegacyMp3")] [HttpGet("Audio/{itemId}/hls/{segmentId}/stream.aac", Name = "GetHlsAudioSegmentLegacyAac")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesAudioFile] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")] public ActionResult GetHlsAudioSegmentLegacy([FromRoute] string itemId, [FromRoute] string segmentId) { @@ -74,6 +76,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Videos/{itemId}/hls/{playlistId}/stream.m3u8")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesPlaylistFile] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")] public ActionResult GetHlsPlaylistLegacy([FromRoute] string itemId, [FromRoute] string playlistId) { @@ -112,6 +115,7 @@ namespace Jellyfin.Api.Controllers // [Authenticated] [HttpGet("Videos/{itemId}/hls/{playlistId}/{segmentId}.{segmentContainer}")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesVideoFile] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")] public ActionResult GetHlsVideoSegmentLegacy( [FromRoute] string itemId, diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index a204fe35c..dbd569e07 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using MediaBrowser.Controller.Configuration; @@ -351,6 +352,7 @@ namespace Jellyfin.Api.Controllers [HttpHead("Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "HeadItemImage_2")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesImageFile] public async Task GetItemImage( [FromRoute] Guid itemId, [FromRoute] ImageType imageType, @@ -507,6 +509,7 @@ namespace Jellyfin.Api.Controllers [HttpHead("Artists/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadArtistImage")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesImageFile] public async Task GetArtistImage( [FromRoute] string name, [FromRoute] ImageType imageType, @@ -585,6 +588,7 @@ namespace Jellyfin.Api.Controllers [HttpHead("Genres/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadGenreImage")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesImageFile] public async Task GetGenreImage( [FromRoute] string name, [FromRoute] ImageType imageType, @@ -663,6 +667,7 @@ namespace Jellyfin.Api.Controllers [HttpHead("MusicGenres/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadMusicGenreImage")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesImageFile] public async Task GetMusicGenreImage( [FromRoute] string name, [FromRoute] ImageType imageType, @@ -741,6 +746,7 @@ namespace Jellyfin.Api.Controllers [HttpHead("Persons/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadPersonImage")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesImageFile] public async Task GetPersonImage( [FromRoute] string name, [FromRoute] ImageType imageType, @@ -819,6 +825,7 @@ namespace Jellyfin.Api.Controllers [HttpHead("Studios/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadStudioImage")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesImageFile] public async Task GetStudioImage( [FromRoute] string name, [FromRoute] ImageType imageType, @@ -897,6 +904,7 @@ namespace Jellyfin.Api.Controllers [HttpHead("Users/{userId}/Images/{imageType}/{imageIndex?}", Name = "HeadUserImage")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesImageFile] public async Task GetUserImage( [FromRoute] Guid userId, [FromRoute] ImageType imageType, @@ -1297,8 +1305,7 @@ namespace Jellyfin.Api.Controllers return NoContent(); } - var stream = new FileStream(imagePath, FileMode.Open, FileAccess.Read); - return File(stream, imageContentType); + return PhysicalFile(imagePath, imageContentType); } } } diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs index afde4a433..2bde3443a 100644 --- a/Jellyfin.Api/Controllers/ItemLookupController.cs +++ b/Jellyfin.Api/Controllers/ItemLookupController.cs @@ -7,6 +7,7 @@ using System.Net.Mime; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; @@ -18,6 +19,7 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -248,6 +250,8 @@ namespace Jellyfin.Api.Controllers /// The task result contains an containing the images file stream. /// [HttpGet("Items/RemoteSearch/Image")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesImageFile] public async Task GetRemoteSearchImage( [FromQuery, Required] string imageUrl, [FromQuery, Required] string providerName) @@ -274,10 +278,7 @@ namespace Jellyfin.Api.Controllers } await DownloadImage(providerName, imageUrl, urlHash, pointerCachePath).ConfigureAwait(false); - - // Read the pointer file again - await using var fileStream = System.IO.File.OpenRead(pointerCachePath); - return new FileStreamResult(fileStream, MediaTypeNames.Application.Octet); + return PhysicalFile(pointerCachePath, MimeTypes.GetMimeType(pointerCachePath)); } /// @@ -293,6 +294,7 @@ namespace Jellyfin.Api.Controllers /// [HttpPost("Items/RemoteSearch/Apply/{id}")] [Authorize(Policy = Policies.RequiresElevation)] + [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task ApplySearchCriteria( [FromRoute] Guid itemId, [FromBody, Required] RemoteSearchResult searchResult, diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index a30873e9e..8c727cc59 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -8,6 +8,7 @@ using System.Net; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; @@ -104,6 +105,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesFile("video/*", "audio/*")] public ActionResult GetFile([FromRoute] Guid itemId) { var item = _libraryManager.GetItemById(itemId); @@ -618,6 +620,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.Download)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesFile("video/*", "audio/*")] public async Task GetDownload([FromRoute] Guid itemId) { var item = _libraryManager.GetItemById(itemId); diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 3ccfc826d..07a176edd 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -9,6 +9,7 @@ using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; @@ -1067,6 +1068,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("ListingProviders/SchedulesDirect/Countries")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesFile(MediaTypeNames.Application.Json)] public async Task GetSchedulesDirectCountries() { var client = _httpClientFactory.CreateClient(); @@ -1175,6 +1177,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("LiveRecordings/{recordingId}/stream")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesVideoFile] public async Task GetLiveRecordingFile([FromRoute] string recordingId) { var path = _liveTvManager.GetEmbyTvActiveRecordingPath(recordingId); @@ -1205,6 +1208,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("LiveStreamFiles/{streamId}/stream.{container}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesVideoFile] public async Task GetLiveStreamFile([FromRoute] string streamId, [FromRoute] string container) { var liveStreamInfo = await _mediaSourceManager.GetDirectStreamProviderByUniqueId(streamId, CancellationToken.None).ConfigureAwait(false); diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs index 1e154a039..2a204e650 100644 --- a/Jellyfin.Api/Controllers/MediaInfoController.cs +++ b/Jellyfin.Api/Controllers/MediaInfoController.cs @@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Net.Mime; using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.MediaInfoDtos; @@ -286,6 +287,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [Produces(MediaTypeNames.Application.Octet)] + [ProducesFile(MediaTypeNames.Application.Octet)] public ActionResult GetBitrateTestBytes([FromQuery] int size = 102400) { const int MaxSize = 10_000_000; diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs index 988acccc3..a82431623 100644 --- a/Jellyfin.Api/Controllers/SubtitleController.cs +++ b/Jellyfin.Api/Controllers/SubtitleController.cs @@ -9,6 +9,7 @@ using System.Net.Mime; using System.Text; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -162,6 +163,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [Produces(MediaTypeNames.Application.Octet)] + [ProducesFile("text/*")] public async Task GetRemoteSubtitles([FromRoute, Required] string? id) { var result = await _subtitleManager.GetRemoteSubtitles(id, CancellationToken.None).ConfigureAwait(false); @@ -185,6 +187,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/Stream.{format}")] [HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/{startPositionTicks?}/Stream.{format}", Name = "GetSubtitle_2")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesFile("text/*")] public async Task GetSubtitle( [FromRoute, Required] Guid itemId, [FromRoute, Required] string? mediaSourceId, @@ -251,6 +254,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/subtitles.m3u8")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesPlaylistFile] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")] public async Task GetSubtitlePlaylist( [FromRoute] Guid itemId, diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs index bbfd163de..256f39d0c 100644 --- a/Jellyfin.Api/Controllers/SystemController.cs +++ b/Jellyfin.Api/Controllers/SystemController.cs @@ -3,8 +3,10 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; +using System.Net.Mime; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; @@ -190,16 +192,13 @@ namespace Jellyfin.Api.Controllers [HttpGet("Logs/Log")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesFile(MediaTypeNames.Text.Plain)] public ActionResult GetLogFile([FromQuery, Required] string? name) { var file = _fileSystem.GetFiles(_appPaths.LogDirectoryPath) .First(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase)); - // For older files, assume fully static - var fileShare = file.LastWriteTimeUtc < DateTime.UtcNow.AddHours(-1) ? FileShare.Read : FileShare.ReadWrite; - - FileStream stream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, fileShare); - return File(stream, "text/plain"); + return File(file.FullName, MediaTypeNames.Text.Plain); } /// diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index b13cf9fa5..b00653d9b 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.StreamingDtos; @@ -91,6 +92,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status302Found)] + [ProducesAudioFile] public async Task GetUniversalAudioStream( [FromRoute] Guid itemId, [FromRoute] string? container, diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index d67f82219..746710475 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -125,7 +125,7 @@ namespace Jellyfin.Api.Controllers /// Deletes a user. /// /// The user id. - /// User deleted. + /// User deleted. /// User not found. /// A indicating success or a if the user was not found. [HttpDelete("{userId}")] @@ -255,7 +255,7 @@ namespace Jellyfin.Api.Controllers /// /// The user id. /// The request. - /// Password successfully reset. + /// Password successfully reset. /// User is not allowed to update the password. /// User not found. /// A indicating success or a or a on failure. @@ -313,7 +313,7 @@ namespace Jellyfin.Api.Controllers /// /// The user id. /// The request. - /// Password successfully reset. + /// Password successfully reset. /// User is not allowed to update the password. /// User not found. /// A indicating success or a or a on failure. diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index 76188f46d..92e82727e 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.PlaybackDtos; @@ -161,6 +162,7 @@ namespace Jellyfin.Api.Controllers /// A containing the hls file. [HttpGet("Videos/{itemId}/live.m3u8")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesPlaylistFile] public async Task GetLiveHlsStream( [FromRoute] Guid itemId, [FromQuery] string? container, diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index f42810c94..18023664b 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; @@ -159,7 +160,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success, or a if the video doesn't exist. [HttpDelete("{itemId}/AlternateSources")] [Authorize(Policy = Policies.RequiresElevation)] - [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task DeleteAlternateSources([FromRoute] Guid itemId) { @@ -326,6 +327,7 @@ namespace Jellyfin.Api.Controllers [HttpHead("{itemId}/{stream=stream}.{container?}", Name = "HeadVideoStream_2")] [HttpHead("{itemId}/stream", Name = "HeadVideoStream")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesVideoFile] public async Task GetVideoStream( [FromRoute] Guid itemId, [FromRoute] string? container, diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 0160a05f9..04b611123 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -16,6 +16,7 @@ using Jellyfin.Api.Auth.LocalAccessPolicy; using Jellyfin.Api.Auth.RequiresElevationPolicy; using Jellyfin.Api.Constants; using Jellyfin.Api.Controllers; +using Jellyfin.Server.Filters; using Jellyfin.Server.Formatters; using Jellyfin.Server.Models; using MediaBrowser.Common; @@ -249,6 +250,8 @@ namespace Jellyfin.Server.Extensions // TODO - remove when all types are supported in System.Text.Json c.AddSwaggerTypeMappings(); + + c.OperationFilter(); }); } diff --git a/Jellyfin.Server/Filters/FileResponseFilter.cs b/Jellyfin.Server/Filters/FileResponseFilter.cs new file mode 100644 index 000000000..8ea35c281 --- /dev/null +++ b/Jellyfin.Server/Filters/FileResponseFilter.cs @@ -0,0 +1,52 @@ +using System; +using System.Linq; +using Jellyfin.Api.Attributes; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Jellyfin.Server.Filters +{ + /// + public class FileResponseFilter : IOperationFilter + { + private const string SuccessCode = "200"; + private static readonly OpenApiMediaType _openApiMediaType = new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Type = "file" + } + }; + + /// + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + foreach (var attribute in context.ApiDescription.ActionDescriptor.EndpointMetadata) + { + if (attribute is ProducesFileAttribute producesFileAttribute) + { + // Get operation response values. + var (_, value) = operation.Responses + .FirstOrDefault(o => o.Key.Equals(SuccessCode, StringComparison.Ordinal)); + + // Operation doesn't have a response. + if (value == null) + { + continue; + } + + // Clear existing responses. + value.Content.Clear(); + + // Add all content-types as file. + foreach (var contentType in producesFileAttribute.GetContentTypes()) + { + value.Content.Add(contentType, _openApiMediaType); + } + + break; + } + } + } + } +} -- cgit v1.2.3 From 59d47ec3f52d8592f6a2aac405ee214cfda10081 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 5 Sep 2020 17:07:25 -0600 Subject: Make all FromRoute required --- Jellyfin.Api/Controllers/AlbumsController.cs | 5 +- Jellyfin.Api/Controllers/ArtistsController.cs | 3 +- Jellyfin.Api/Controllers/AudioController.cs | 5 +- Jellyfin.Api/Controllers/ChannelsController.cs | 4 +- Jellyfin.Api/Controllers/CollectionController.cs | 4 +- .../Controllers/ConfigurationController.cs | 4 +- .../Controllers/DisplayPreferencesController.cs | 4 +- Jellyfin.Api/Controllers/DlnaController.cs | 6 +- Jellyfin.Api/Controllers/DlnaServerController.cs | 18 +-- Jellyfin.Api/Controllers/DynamicHlsController.cs | 32 ++--- Jellyfin.Api/Controllers/GenresController.cs | 2 +- Jellyfin.Api/Controllers/HlsSegmentController.cs | 12 +- Jellyfin.Api/Controllers/ImageController.cs | 156 ++++++++++----------- Jellyfin.Api/Controllers/InstantMixController.cs | 12 +- Jellyfin.Api/Controllers/ItemLookupController.cs | 4 +- Jellyfin.Api/Controllers/ItemRefreshController.cs | 2 +- Jellyfin.Api/Controllers/ItemUpdateController.cs | 6 +- Jellyfin.Api/Controllers/ItemsController.cs | 4 +- Jellyfin.Api/Controllers/LibraryController.cs | 16 +-- Jellyfin.Api/Controllers/LiveTvController.cs | 26 ++-- Jellyfin.Api/Controllers/MediaInfoController.cs | 4 +- Jellyfin.Api/Controllers/MusicGenresController.cs | 2 +- Jellyfin.Api/Controllers/PackageController.cs | 6 +- Jellyfin.Api/Controllers/PersonsController.cs | 3 +- Jellyfin.Api/Controllers/PlaylistsController.cs | 28 ++-- Jellyfin.Api/Controllers/PlaystateController.cs | 18 +-- Jellyfin.Api/Controllers/PluginsController.cs | 10 +- Jellyfin.Api/Controllers/RemoteImageController.cs | 6 +- .../Controllers/ScheduledTasksController.cs | 2 +- Jellyfin.Api/Controllers/SessionController.cs | 6 +- Jellyfin.Api/Controllers/StudiosController.cs | 2 +- Jellyfin.Api/Controllers/SubtitleController.cs | 16 +-- Jellyfin.Api/Controllers/SuggestionsController.cs | 2 +- .../Controllers/UniversalAudioController.cs | 4 +- Jellyfin.Api/Controllers/UserController.cs | 14 +- Jellyfin.Api/Controllers/UserLibraryController.cs | 20 +-- Jellyfin.Api/Controllers/UserViewsController.cs | 4 +- Jellyfin.Api/Controllers/VideoHlsController.cs | 2 +- Jellyfin.Api/Controllers/VideosController.cs | 8 +- Jellyfin.Api/Controllers/YearsController.cs | 2 +- 40 files changed, 244 insertions(+), 240 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/AlbumsController.cs b/Jellyfin.Api/Controllers/AlbumsController.cs index 190d4bd07..9b68d056f 100644 --- a/Jellyfin.Api/Controllers/AlbumsController.cs +++ b/Jellyfin.Api/Controllers/AlbumsController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; @@ -52,7 +53,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Albums/{albumId}/Similar")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSimilarAlbums( - [FromRoute] string albumId, + [FromRoute][Required] string albumId, [FromQuery] Guid? userId, [FromQuery] string? excludeArtistIds, [FromQuery] int? limit) @@ -84,7 +85,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Artists/{artistId}/Similar")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSimilarArtists( - [FromRoute] string artistId, + [FromRoute][Required] string artistId, [FromQuery] Guid? userId, [FromQuery] string? excludeArtistIds, [FromQuery] int? limit) diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs index 3f72830cd..b3dad14f3 100644 --- a/Jellyfin.Api/Controllers/ArtistsController.cs +++ b/Jellyfin.Api/Controllers/ArtistsController.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using System.Linq; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; @@ -469,7 +470,7 @@ namespace Jellyfin.Api.Controllers /// An containing the artist. [HttpGet("{name}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetArtistByName([FromRoute] string name, [FromQuery] Guid? userId) + public ActionResult GetArtistByName([FromRoute][Required] string name, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions().AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index 802cd026e..a81efe966 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.StreamingDtos; @@ -89,8 +90,8 @@ namespace Jellyfin.Api.Controllers [HttpHead("{itemId}/stream", Name = "HeadAudioStream")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetAudioStream( - [FromRoute] Guid itemId, - [FromRoute] string? container, + [FromRoute][Required] Guid itemId, + [FromRoute][Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, diff --git a/Jellyfin.Api/Controllers/ChannelsController.cs b/Jellyfin.Api/Controllers/ChannelsController.cs index bdd7dfd96..93d9a9d00 100644 --- a/Jellyfin.Api/Controllers/ChannelsController.cs +++ b/Jellyfin.Api/Controllers/ChannelsController.cs @@ -90,7 +90,7 @@ namespace Jellyfin.Api.Controllers /// Channel features returned. /// An containing the channel features. [HttpGet("{channelId}/Features")] - public ActionResult GetChannelFeatures([FromRoute] string channelId) + public ActionResult GetChannelFeatures([FromRoute][Required] string channelId) { return _channelManager.GetChannelFeatures(channelId); } @@ -114,7 +114,7 @@ namespace Jellyfin.Api.Controllers /// [HttpGet("{channelId}/Items")] public async Task>> GetChannelItems( - [FromRoute] Guid channelId, + [FromRoute][Required] Guid channelId, [FromQuery] Guid? folderId, [FromQuery] Guid? userId, [FromQuery] int? startIndex, diff --git a/Jellyfin.Api/Controllers/CollectionController.cs b/Jellyfin.Api/Controllers/CollectionController.cs index c5910d6e8..0b1f655da 100644 --- a/Jellyfin.Api/Controllers/CollectionController.cs +++ b/Jellyfin.Api/Controllers/CollectionController.cs @@ -88,7 +88,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("{collectionId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task AddToCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds) + public async Task AddToCollection([FromRoute][Required] Guid collectionId, [FromQuery, Required] string? itemIds) { await _collectionManager.AddToCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(true); return NoContent(); @@ -103,7 +103,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpDelete("{collectionId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task RemoveFromCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds) + public async Task RemoveFromCollection([FromRoute][Required] Guid collectionId, [FromQuery, Required] string? itemIds) { await _collectionManager.RemoveFromCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(false); return NoContent(); diff --git a/Jellyfin.Api/Controllers/ConfigurationController.cs b/Jellyfin.Api/Controllers/ConfigurationController.cs index 20fb0ec87..f13b6d38d 100644 --- a/Jellyfin.Api/Controllers/ConfigurationController.cs +++ b/Jellyfin.Api/Controllers/ConfigurationController.cs @@ -73,7 +73,7 @@ namespace Jellyfin.Api.Controllers /// Configuration. [HttpGet("Configuration/{key}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetNamedConfiguration([FromRoute] string? key) + public ActionResult GetNamedConfiguration([FromRoute][Required] string? key) { return _configurationManager.GetConfiguration(key); } @@ -87,7 +87,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Configuration/{key}")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task UpdateNamedConfiguration([FromRoute] string? key) + public async Task UpdateNamedConfiguration([FromRoute][Required] string? key) { var configurationType = _configurationManager.GetConfigurationType(key); var configuration = await JsonSerializer.DeserializeAsync(Request.Body, configurationType, _serializerOptions).ConfigureAwait(false); diff --git a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs index c3b67eec3..6422a45fd 100644 --- a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs +++ b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs @@ -43,7 +43,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "displayPreferencesId", Justification = "Imported from ServiceStack")] public ActionResult GetDisplayPreferences( - [FromRoute] string? displayPreferencesId, + [FromRoute][Required] string? displayPreferencesId, [FromQuery] [Required] Guid userId, [FromQuery] [Required] string? client) { @@ -97,7 +97,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "displayPreferencesId", Justification = "Imported from ServiceStack")] public ActionResult UpdateDisplayPreferences( - [FromRoute] string? displayPreferencesId, + [FromRoute][Required] string? displayPreferencesId, [FromQuery, Required] Guid userId, [FromQuery, Required] string? client, [FromBody, Required] DisplayPreferencesDto displayPreferences) diff --git a/Jellyfin.Api/Controllers/DlnaController.cs b/Jellyfin.Api/Controllers/DlnaController.cs index 397299a73..69c948174 100644 --- a/Jellyfin.Api/Controllers/DlnaController.cs +++ b/Jellyfin.Api/Controllers/DlnaController.cs @@ -59,7 +59,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Profiles/{profileId}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetProfile([FromRoute] string profileId) + public ActionResult GetProfile([FromRoute][Required] string profileId) { var profile = _dlnaManager.GetProfile(profileId); if (profile == null) @@ -80,7 +80,7 @@ namespace Jellyfin.Api.Controllers [HttpDelete("Profiles/{profileId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteProfile([FromRoute] string profileId) + public ActionResult DeleteProfile([FromRoute][Required] string profileId) { var existingDeviceProfile = _dlnaManager.GetProfile(profileId); if (existingDeviceProfile == null) @@ -117,7 +117,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Profiles/{profileId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UpdateProfile([FromRoute] string profileId, [FromBody] DeviceProfile deviceProfile) + public ActionResult UpdateProfile([FromRoute][Required] string profileId, [FromBody] DeviceProfile deviceProfile) { var existingDeviceProfile = _dlnaManager.GetProfile(profileId); if (existingDeviceProfile == null) diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index 9ebd89819..832fcc8c6 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -45,7 +45,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("{serverId}/description.xml", Name = "GetDescriptionXml_2")] [Produces(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetDescriptionXml([FromRoute] string serverId) + public ActionResult GetDescriptionXml([FromRoute][Required] string serverId) { var url = GetAbsoluteUri(); var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase)); @@ -65,7 +65,7 @@ namespace Jellyfin.Api.Controllers [Produces(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetContentDirectory([FromRoute] string serverId) + public ActionResult GetContentDirectory([FromRoute][Required] string serverId) { return Ok(_contentDirectory.GetServiceXml()); } @@ -81,7 +81,7 @@ namespace Jellyfin.Api.Controllers [Produces(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetMediaReceiverRegistrar([FromRoute] string serverId) + public ActionResult GetMediaReceiverRegistrar([FromRoute][Required] string serverId) { return Ok(_mediaReceiverRegistrar.GetServiceXml()); } @@ -97,7 +97,7 @@ namespace Jellyfin.Api.Controllers [Produces(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetConnectionManager([FromRoute] string serverId) + public ActionResult GetConnectionManager([FromRoute][Required] string serverId) { return Ok(_connectionManager.GetServiceXml()); } @@ -108,7 +108,7 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Control response. [HttpPost("{serverId}/ContentDirectory/Control")] - public async Task> ProcessContentDirectoryControlRequest([FromRoute] string serverId) + public async Task> ProcessContentDirectoryControlRequest([FromRoute][Required] string serverId) { return await ProcessControlRequestInternalAsync(serverId, Request.Body, _contentDirectory).ConfigureAwait(false); } @@ -119,7 +119,7 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Control response. [HttpPost("{serverId}/ConnectionManager/Control")] - public async Task> ProcessConnectionManagerControlRequest([FromRoute] string serverId) + public async Task> ProcessConnectionManagerControlRequest([FromRoute][Required] string serverId) { return await ProcessControlRequestInternalAsync(serverId, Request.Body, _connectionManager).ConfigureAwait(false); } @@ -130,7 +130,7 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Control response. [HttpPost("{serverId}/MediaReceiverRegistrar/Control")] - public async Task> ProcessMediaReceiverRegistrarControlRequest([FromRoute] string serverId) + public async Task> ProcessMediaReceiverRegistrarControlRequest([FromRoute][Required] string serverId) { return await ProcessControlRequestInternalAsync(serverId, Request.Body, _mediaReceiverRegistrar).ConfigureAwait(false); } @@ -185,7 +185,7 @@ namespace Jellyfin.Api.Controllers /// Icon stream. [HttpGet("{serverId}/icons/{fileName}")] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetIconId([FromRoute] string serverId, [FromRoute] string fileName) + public ActionResult GetIconId([FromRoute][Required] string serverId, [FromRoute][Required] string fileName) { return GetIconInternal(fileName); } @@ -196,7 +196,7 @@ namespace Jellyfin.Api.Controllers /// The icon filename. /// Icon stream. [HttpGet("icons/{fileName}")] - public ActionResult GetIcon([FromRoute] string fileName) + public ActionResult GetIcon([FromRoute][Required] string fileName) { return GetIconInternal(fileName); } diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 0c884d58d..ec711ce34 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -167,8 +167,8 @@ namespace Jellyfin.Api.Controllers [HttpHead("Videos/{itemId}/master.m3u8", Name = "HeadMasterHlsVideoPlaylist")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetMasterHlsVideoPlaylist( - [FromRoute] Guid itemId, - [FromRoute] string? container, + [FromRoute][Required] Guid itemId, + [FromRoute][Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -334,8 +334,8 @@ namespace Jellyfin.Api.Controllers [HttpHead("Audio/{itemId}/master.m3u8", Name = "HeadMasterHlsAudioPlaylist")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetMasterHlsAudioPlaylist( - [FromRoute] Guid itemId, - [FromRoute] string? container, + [FromRoute][Required] Guid itemId, + [FromRoute][Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -499,8 +499,8 @@ namespace Jellyfin.Api.Controllers [HttpGet("Videos/{itemId}/main.m3u8")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetVariantHlsVideoPlaylist( - [FromRoute] Guid itemId, - [FromRoute] string? container, + [FromRoute][Required] Guid itemId, + [FromRoute][Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -664,8 +664,8 @@ namespace Jellyfin.Api.Controllers [HttpGet("Audio/{itemId}/main.m3u8")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetVariantHlsAudioPlaylist( - [FromRoute] Guid itemId, - [FromRoute] string? container, + [FromRoute][Required] Guid itemId, + [FromRoute][Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -832,10 +832,10 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "playlistId", Justification = "Imported from ServiceStack")] public async Task GetHlsVideoSegment( - [FromRoute] Guid itemId, - [FromRoute] string playlistId, - [FromRoute] int segmentId, - [FromRoute] string container, + [FromRoute][Required] Guid itemId, + [FromRoute][Required] string playlistId, + [FromRoute][Required] int segmentId, + [FromRoute][Required] string container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -1001,10 +1001,10 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "playlistId", Justification = "Imported from ServiceStack")] public async Task GetHlsAudioSegment( - [FromRoute] Guid itemId, - [FromRoute] string playlistId, - [FromRoute] int segmentId, - [FromRoute] string container, + [FromRoute][Required] Guid itemId, + [FromRoute][Required] string playlistId, + [FromRoute][Required] int segmentId, + [FromRoute][Required] string container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs index 55ad71200..230aff417 100644 --- a/Jellyfin.Api/Controllers/GenresController.cs +++ b/Jellyfin.Api/Controllers/GenresController.cs @@ -260,7 +260,7 @@ namespace Jellyfin.Api.Controllers /// An containing the genre. [HttpGet("{genreName}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetGenre([FromRoute] string genreName, [FromQuery] Guid? userId) + public ActionResult GetGenre([FromRoute][Required] string genreName, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions() .AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs index 816252f80..304b5ce82 100644 --- a/Jellyfin.Api/Controllers/HlsSegmentController.cs +++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs @@ -55,7 +55,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Audio/{itemId}/hls/{segmentId}/stream.aac", Name = "GetHlsAudioSegmentLegacyAac")] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")] - public ActionResult GetHlsAudioSegmentLegacy([FromRoute] string itemId, [FromRoute] string segmentId) + public ActionResult GetHlsAudioSegmentLegacy([FromRoute][Required] string itemId, [FromRoute][Required] string segmentId) { // TODO: Deprecate with new iOS app var file = segmentId + Path.GetExtension(Request.Path); @@ -75,7 +75,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")] - public ActionResult GetHlsPlaylistLegacy([FromRoute] string itemId, [FromRoute] string playlistId) + public ActionResult GetHlsPlaylistLegacy([FromRoute][Required] string itemId, [FromRoute][Required] string playlistId) { var file = playlistId + Path.GetExtension(Request.Path); file = Path.Combine(_serverConfigurationManager.GetTranscodePath(), file); @@ -114,10 +114,10 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")] public ActionResult GetHlsVideoSegmentLegacy( - [FromRoute] string itemId, - [FromRoute] string playlistId, - [FromRoute] string segmentId, - [FromRoute] string segmentContainer) + [FromRoute][Required] string itemId, + [FromRoute][Required] string playlistId, + [FromRoute][Required] string segmentId, + [FromRoute][Required] string segmentContainer) { var file = segmentId + Path.GetExtension(Request.Path); var transcodeFolderPath = _serverConfigurationManager.GetTranscodePath(); diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index a204fe35c..3cde4062d 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -90,9 +90,9 @@ namespace Jellyfin.Api.Controllers [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")] public async Task PostUserImage( - [FromRoute] Guid userId, - [FromRoute] ImageType imageType, - [FromRoute] int? index = null) + [FromRoute][Required] Guid userId, + [FromRoute][Required] ImageType imageType, + [FromRoute][Required] int? index = null) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) { @@ -137,9 +137,9 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult DeleteUserImage( - [FromRoute] Guid userId, - [FromRoute] ImageType imageType, - [FromRoute] int? index = null) + [FromRoute][Required] Guid userId, + [FromRoute][Required] ImageType imageType, + [FromRoute][Required] int? index = null) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) { @@ -175,9 +175,9 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task DeleteItemImage( - [FromRoute] Guid itemId, - [FromRoute] ImageType imageType, - [FromRoute] int? imageIndex = null) + [FromRoute][Required] Guid itemId, + [FromRoute][Required] ImageType imageType, + [FromRoute][Required] int? imageIndex = null) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -205,9 +205,9 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")] public async Task SetItemImage( - [FromRoute] Guid itemId, - [FromRoute] ImageType imageType, - [FromRoute] int? imageIndex = null) + [FromRoute][Required] Guid itemId, + [FromRoute][Required] ImageType imageType, + [FromRoute][Required] int? imageIndex = null) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -238,9 +238,9 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task UpdateItemImageIndex( - [FromRoute] Guid itemId, - [FromRoute] ImageType imageType, - [FromRoute] int imageIndex, + [FromRoute][Required] Guid itemId, + [FromRoute][Required] ImageType imageType, + [FromRoute][Required] int imageIndex, [FromQuery] int newIndex) { var item = _libraryManager.GetItemById(itemId); @@ -264,7 +264,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetItemImageInfos([FromRoute] Guid itemId) + public async Task>> GetItemImageInfos([FromRoute][Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -352,10 +352,10 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetItemImage( - [FromRoute] Guid itemId, - [FromRoute] ImageType imageType, - [FromRoute] int? maxWidth, - [FromRoute] int? maxHeight, + [FromRoute][Required] Guid itemId, + [FromRoute][Required] ImageType imageType, + [FromRoute][Required] int? maxWidth, + [FromRoute][Required] int? maxHeight, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -368,7 +368,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute][Required] int? imageIndex = null) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -430,23 +430,23 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetItemImage2( - [FromRoute] Guid itemId, - [FromRoute] ImageType imageType, - [FromRoute] int? maxWidth, - [FromRoute] int? maxHeight, + [FromRoute][Required] Guid itemId, + [FromRoute][Required] ImageType imageType, + [FromRoute][Required] int? maxWidth, + [FromRoute][Required] int? maxHeight, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, - [FromRoute] string tag, + [FromRoute][Required] string tag, [FromQuery] bool? cropWhitespace, - [FromRoute] string format, + [FromRoute][Required] string format, [FromQuery] bool? addPlayedIndicator, - [FromRoute] double? percentPlayed, - [FromRoute] int? unplayedCount, + [FromRoute][Required] double? percentPlayed, + [FromRoute][Required] int? unplayedCount, [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute][Required] int? imageIndex = null) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -508,14 +508,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetArtistImage( - [FromRoute] string name, - [FromRoute] ImageType imageType, - [FromRoute] string tag, - [FromRoute] string format, - [FromRoute] int? maxWidth, - [FromRoute] int? maxHeight, - [FromRoute] double? percentPlayed, - [FromRoute] int? unplayedCount, + [FromRoute][Required] string name, + [FromRoute][Required] ImageType imageType, + [FromRoute][Required] string tag, + [FromRoute][Required] string format, + [FromRoute][Required] int? maxWidth, + [FromRoute][Required] int? maxHeight, + [FromRoute][Required] double? percentPlayed, + [FromRoute][Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -524,7 +524,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute][Required] int? imageIndex = null) { var item = _libraryManager.GetArtist(name); if (item == null) @@ -586,14 +586,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetGenreImage( - [FromRoute] string name, - [FromRoute] ImageType imageType, - [FromRoute] string tag, - [FromRoute] string format, - [FromRoute] int? maxWidth, - [FromRoute] int? maxHeight, - [FromRoute] double? percentPlayed, - [FromRoute] int? unplayedCount, + [FromRoute][Required] string name, + [FromRoute][Required] ImageType imageType, + [FromRoute][Required] string tag, + [FromRoute][Required] string format, + [FromRoute][Required] int? maxWidth, + [FromRoute][Required] int? maxHeight, + [FromRoute][Required] double? percentPlayed, + [FromRoute][Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -602,7 +602,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute][Required] int? imageIndex = null) { var item = _libraryManager.GetGenre(name); if (item == null) @@ -664,14 +664,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetMusicGenreImage( - [FromRoute] string name, - [FromRoute] ImageType imageType, - [FromRoute] string tag, - [FromRoute] string format, - [FromRoute] int? maxWidth, - [FromRoute] int? maxHeight, - [FromRoute] double? percentPlayed, - [FromRoute] int? unplayedCount, + [FromRoute][Required] string name, + [FromRoute][Required] ImageType imageType, + [FromRoute][Required] string tag, + [FromRoute][Required] string format, + [FromRoute][Required] int? maxWidth, + [FromRoute][Required] int? maxHeight, + [FromRoute][Required] double? percentPlayed, + [FromRoute][Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -680,7 +680,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute][Required] int? imageIndex = null) { var item = _libraryManager.GetMusicGenre(name); if (item == null) @@ -742,14 +742,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetPersonImage( - [FromRoute] string name, - [FromRoute] ImageType imageType, - [FromRoute] string tag, - [FromRoute] string format, - [FromRoute] int? maxWidth, - [FromRoute] int? maxHeight, - [FromRoute] double? percentPlayed, - [FromRoute] int? unplayedCount, + [FromRoute][Required] string name, + [FromRoute][Required] ImageType imageType, + [FromRoute][Required] string tag, + [FromRoute][Required] string format, + [FromRoute][Required] int? maxWidth, + [FromRoute][Required] int? maxHeight, + [FromRoute][Required] double? percentPlayed, + [FromRoute][Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -758,7 +758,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute][Required] int? imageIndex = null) { var item = _libraryManager.GetPerson(name); if (item == null) @@ -820,14 +820,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetStudioImage( - [FromRoute] string name, - [FromRoute] ImageType imageType, - [FromRoute] string tag, - [FromRoute] string format, - [FromRoute] int? maxWidth, - [FromRoute] int? maxHeight, - [FromRoute] double? percentPlayed, - [FromRoute] int? unplayedCount, + [FromRoute][Required] string name, + [FromRoute][Required] ImageType imageType, + [FromRoute][Required] string tag, + [FromRoute][Required] string format, + [FromRoute][Required] int? maxWidth, + [FromRoute][Required] int? maxHeight, + [FromRoute][Required] double? percentPlayed, + [FromRoute][Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -836,7 +836,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute][Required] int? imageIndex = null) { var item = _libraryManager.GetStudio(name); if (item == null) @@ -898,8 +898,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetUserImage( - [FromRoute] Guid userId, - [FromRoute] ImageType imageType, + [FromRoute][Required] Guid userId, + [FromRoute][Required] ImageType imageType, [FromQuery] string? tag, [FromQuery] string? format, [FromQuery] int? maxWidth, @@ -914,7 +914,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute][Required] int? imageIndex = null) { var user = _userManager.GetUserById(userId); if (user == null) diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs index 73bd30c4d..11f2b2495 100644 --- a/Jellyfin.Api/Controllers/InstantMixController.cs +++ b/Jellyfin.Api/Controllers/InstantMixController.cs @@ -64,7 +64,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Songs/{id}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromSong( - [FromRoute] Guid id, + [FromRoute][Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -101,7 +101,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Albums/{id}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromAlbum( - [FromRoute] Guid id, + [FromRoute][Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -138,7 +138,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Playlists/{id}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromPlaylist( - [FromRoute] Guid id, + [FromRoute][Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -211,7 +211,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Artists/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromArtists( - [FromRoute] Guid id, + [FromRoute][Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -248,7 +248,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("MusicGenres/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromMusicGenres( - [FromRoute] Guid id, + [FromRoute][Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -285,7 +285,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Items/{id}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromItem( - [FromRoute] Guid id, + [FromRoute][Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs index afde4a433..f1169f3d2 100644 --- a/Jellyfin.Api/Controllers/ItemLookupController.cs +++ b/Jellyfin.Api/Controllers/ItemLookupController.cs @@ -72,7 +72,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> GetExternalIdInfos([FromRoute] Guid itemId) + public ActionResult> GetExternalIdInfos([FromRoute][Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -294,7 +294,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/RemoteSearch/Apply/{id}")] [Authorize(Policy = Policies.RequiresElevation)] public async Task ApplySearchCriteria( - [FromRoute] Guid itemId, + [FromRoute][Required] Guid itemId, [FromBody, Required] RemoteSearchResult searchResult, [FromQuery] bool replaceAllImages = true) { diff --git a/Jellyfin.Api/Controllers/ItemRefreshController.cs b/Jellyfin.Api/Controllers/ItemRefreshController.cs index 3f5d305c1..0d1e02a78 100644 --- a/Jellyfin.Api/Controllers/ItemRefreshController.cs +++ b/Jellyfin.Api/Controllers/ItemRefreshController.cs @@ -53,7 +53,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult Post( - [FromRoute] Guid itemId, + [FromRoute][Required] Guid itemId, [FromQuery] MetadataRefreshMode metadataRefreshMode = MetadataRefreshMode.None, [FromQuery] MetadataRefreshMode imageRefreshMode = MetadataRefreshMode.None, [FromQuery] bool replaceAllMetadata = false, diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs index ec52f4996..0d064fffb 100644 --- a/Jellyfin.Api/Controllers/ItemUpdateController.cs +++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs @@ -68,7 +68,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/{itemId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task UpdateItem([FromRoute] Guid itemId, [FromBody, Required] BaseItemDto request) + public async Task UpdateItem([FromRoute][Required] Guid itemId, [FromBody, Required] BaseItemDto request) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -141,7 +141,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Items/{itemId}/MetadataEditor")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetMetadataEditorInfo([FromRoute] Guid itemId) + public ActionResult GetMetadataEditorInfo([FromRoute][Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); @@ -195,7 +195,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/{itemId}/ContentType")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UpdateItemContentType([FromRoute] Guid itemId, [FromQuery, Required] string? contentType) + public ActionResult UpdateItemContentType([FromRoute][Required] Guid itemId, [FromQuery, Required] string? contentType) { var item = _libraryManager.GetItemById(itemId); if (item == null) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index f9273bad6..cc8051365 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -144,7 +144,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{uId}/Items", Name = "GetItems_2")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetItems( - [FromRoute] Guid? uId, + [FromRoute][Required] Guid? uId, [FromQuery] Guid? userId, [FromQuery] string? maxOfficialRating, [FromQuery] bool? hasThemeSong, @@ -529,7 +529,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/Items/Resume")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetResumeItems( - [FromRoute] Guid userId, + [FromRoute][Required] Guid userId, [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery] string? searchTerm, diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index a30873e9e..ac28fd6ca 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -104,7 +104,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetFile([FromRoute] Guid itemId) + public ActionResult GetFile([FromRoute][Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -144,7 +144,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult GetThemeSongs( - [FromRoute] Guid itemId, + [FromRoute][Required] Guid itemId, [FromQuery] Guid? userId, [FromQuery] bool inheritFromParent = false) { @@ -210,7 +210,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult GetThemeVideos( - [FromRoute] Guid itemId, + [FromRoute][Required] Guid itemId, [FromQuery] Guid? userId, [FromQuery] bool inheritFromParent = false) { @@ -275,7 +275,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetThemeMedia( - [FromRoute] Guid itemId, + [FromRoute][Required] Guid itemId, [FromQuery] Guid? userId, [FromQuery] bool inheritFromParent = false) { @@ -438,7 +438,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> GetAncestors([FromRoute] Guid itemId, [FromQuery] Guid? userId) + public ActionResult> GetAncestors([FromRoute][Required] Guid itemId, [FromQuery] Guid? userId) { var item = _libraryManager.GetItemById(itemId); @@ -555,7 +555,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Library/Movies/Updated")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult PostUpdatedMovies([FromRoute] string? tmdbId, [FromRoute] string? imdbId) + public ActionResult PostUpdatedMovies([FromRoute][Required] string? tmdbId, [FromRoute][Required] string? imdbId) { var movies = _libraryManager.GetItemList(new InternalItemsQuery { @@ -618,7 +618,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.Download)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetDownload([FromRoute] Guid itemId) + public async Task GetDownload([FromRoute][Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -687,7 +687,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSimilarItems( - [FromRoute] Guid itemId, + [FromRoute][Required] Guid itemId, [FromQuery] string? excludeArtistIds, [FromQuery] Guid? userId, [FromQuery] int? limit, diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index eace0f911..e086e9029 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -209,7 +209,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Channels/{channelId}")] [ProducesResponseType(StatusCodes.Status200OK)] [Authorize(Policy = Policies.DefaultAuthorization)] - public ActionResult GetChannel([FromRoute] Guid channelId, [FromQuery] Guid? userId) + public ActionResult GetChannel([FromRoute][Required] Guid channelId, [FromQuery] Guid? userId) { var user = userId.HasValue && !userId.Equals(Guid.Empty) ? _userManager.GetUserById(userId.Value) @@ -406,7 +406,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Recordings/{recordingId}")] [ProducesResponseType(StatusCodes.Status200OK)] [Authorize(Policy = Policies.DefaultAuthorization)] - public ActionResult GetRecording([FromRoute] Guid recordingId, [FromQuery] Guid? userId) + public ActionResult GetRecording([FromRoute][Required] Guid recordingId, [FromQuery] Guid? userId) { var user = userId.HasValue && !userId.Equals(Guid.Empty) ? _userManager.GetUserById(userId.Value) @@ -428,7 +428,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Tuners/{tunerId}/Reset")] [ProducesResponseType(StatusCodes.Status204NoContent)] [Authorize(Policy = Policies.DefaultAuthorization)] - public ActionResult ResetTuner([FromRoute] string tunerId) + public ActionResult ResetTuner([FromRoute][Required] string tunerId) { AssertUserCanManageLiveTv(); _liveTvManager.ResetTuner(tunerId, CancellationToken.None); @@ -744,7 +744,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetProgram( - [FromRoute] string programId, + [FromRoute][Required] string programId, [FromQuery] Guid? userId) { var user = userId.HasValue && !userId.Equals(Guid.Empty) @@ -765,7 +765,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteRecording([FromRoute] Guid recordingId) + public ActionResult DeleteRecording([FromRoute][Required] Guid recordingId) { AssertUserCanManageLiveTv(); @@ -792,7 +792,7 @@ namespace Jellyfin.Api.Controllers [HttpDelete("Timers/{timerId}")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task CancelTimer([FromRoute] string timerId) + public async Task CancelTimer([FromRoute][Required] string timerId) { AssertUserCanManageLiveTv(); await _liveTvManager.CancelTimer(timerId).ConfigureAwait(false); @@ -810,7 +810,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "timerId", Justification = "Imported from ServiceStack")] - public async Task UpdateTimer([FromRoute] string timerId, [FromBody] TimerInfoDto timerInfo) + public async Task UpdateTimer([FromRoute][Required] string timerId, [FromBody] TimerInfoDto timerInfo) { AssertUserCanManageLiveTv(); await _liveTvManager.UpdateTimer(timerInfo, CancellationToken.None).ConfigureAwait(false); @@ -844,7 +844,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> GetSeriesTimer([FromRoute] string timerId) + public async Task> GetSeriesTimer([FromRoute][Required] string timerId) { var timer = await _liveTvManager.GetSeriesTimer(timerId, CancellationToken.None).ConfigureAwait(false); if (timer == null) @@ -884,7 +884,7 @@ namespace Jellyfin.Api.Controllers [HttpDelete("SeriesTimers/{timerId}")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task CancelSeriesTimer([FromRoute] string timerId) + public async Task CancelSeriesTimer([FromRoute][Required] string timerId) { AssertUserCanManageLiveTv(); await _liveTvManager.CancelSeriesTimer(timerId).ConfigureAwait(false); @@ -902,7 +902,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "timerId", Justification = "Imported from ServiceStack")] - public async Task UpdateSeriesTimer([FromRoute] string timerId, [FromBody] SeriesTimerInfoDto seriesTimerInfo) + public async Task UpdateSeriesTimer([FromRoute][Required] string timerId, [FromBody] SeriesTimerInfoDto seriesTimerInfo) { AssertUserCanManageLiveTv(); await _liveTvManager.UpdateSeriesTimer(seriesTimerInfo, CancellationToken.None).ConfigureAwait(false); @@ -934,7 +934,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status404NotFound)] [Obsolete("This endpoint is obsolete.")] - public ActionResult GetRecordingGroup([FromRoute] Guid? groupId) + public ActionResult GetRecordingGroup([FromRoute][Required] Guid? groupId) { return NotFound(); } @@ -1176,7 +1176,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("LiveRecordings/{recordingId}/stream")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetLiveRecordingFile([FromRoute] string recordingId) + public async Task GetLiveRecordingFile([FromRoute][Required] string recordingId) { var path = _liveTvManager.GetEmbyTvActiveRecordingPath(recordingId); @@ -1206,7 +1206,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("LiveStreamFiles/{streamId}/stream.{container}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetLiveStreamFile([FromRoute] string streamId, [FromRoute] string container) + public async Task GetLiveStreamFile([FromRoute][Required] string streamId, [FromRoute][Required] string container) { var liveStreamInfo = await _mediaSourceManager.GetDirectStreamProviderByUniqueId(streamId, CancellationToken.None).ConfigureAwait(false); if (liveStreamInfo == null) diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs index 1e154a039..8bb0ace15 100644 --- a/Jellyfin.Api/Controllers/MediaInfoController.cs +++ b/Jellyfin.Api/Controllers/MediaInfoController.cs @@ -68,7 +68,7 @@ namespace Jellyfin.Api.Controllers /// A containing a with the playback information. [HttpGet("Items/{itemId}/PlaybackInfo")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> GetPlaybackInfo([FromRoute] Guid itemId, [FromQuery, Required] Guid? userId) + public async Task> GetPlaybackInfo([FromRoute][Required] Guid itemId, [FromQuery, Required] Guid? userId) { return await _mediaInfoHelper.GetPlaybackInfo( itemId, @@ -100,7 +100,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/{itemId}/PlaybackInfo")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetPostedPlaybackInfo( - [FromRoute] Guid itemId, + [FromRoute][Required] Guid itemId, [FromQuery] Guid? userId, [FromQuery] long? maxStreamingBitrate, [FromQuery] long? startTimeTicks, diff --git a/Jellyfin.Api/Controllers/MusicGenresController.cs b/Jellyfin.Api/Controllers/MusicGenresController.cs index 0d319137a..493386e93 100644 --- a/Jellyfin.Api/Controllers/MusicGenresController.cs +++ b/Jellyfin.Api/Controllers/MusicGenresController.cs @@ -258,7 +258,7 @@ namespace Jellyfin.Api.Controllers /// An containing a with the music genre. [HttpGet("{genreName}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetMusicGenre([FromRoute] string genreName, [FromQuery] Guid? userId) + public ActionResult GetMusicGenre([FromRoute][Required] string genreName, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions().AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/PackageController.cs b/Jellyfin.Api/Controllers/PackageController.cs index 3d6a87909..ea85e1909 100644 --- a/Jellyfin.Api/Controllers/PackageController.cs +++ b/Jellyfin.Api/Controllers/PackageController.cs @@ -44,7 +44,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Packages/{name}")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetPackageInfo( - [FromRoute] [Required] string? name, + [FromRoute][Required] string? name, [FromQuery] string? assemblyGuid) { var packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false); @@ -84,7 +84,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [Authorize(Policy = Policies.RequiresElevation)] public async Task InstallPackage( - [FromRoute] [Required] string? name, + [FromRoute][Required] string? name, [FromQuery] string? assemblyGuid, [FromQuery] string? version) { @@ -115,7 +115,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult CancelPackageInstallation( - [FromRoute] [Required] Guid packageId) + [FromRoute][Required] Guid packageId) { _installationManager.CancelInstallation(packageId); return NoContent(); diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs index b6ccec666..be5fb7f7c 100644 --- a/Jellyfin.Api/Controllers/PersonsController.cs +++ b/Jellyfin.Api/Controllers/PersonsController.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Linq; using Jellyfin.Api.Constants; @@ -262,7 +263,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("{name}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetPerson([FromRoute] string name, [FromQuery] Guid? userId) + public ActionResult GetPerson([FromRoute][Required] string name, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions() .AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs index f4c6a9253..07bb108e3 100644 --- a/Jellyfin.Api/Controllers/PlaylistsController.cs +++ b/Jellyfin.Api/Controllers/PlaylistsController.cs @@ -84,7 +84,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task AddToPlaylist( - [FromRoute] Guid playlistId, + [FromRoute][Required] Guid playlistId, [FromQuery] string? ids, [FromQuery] Guid? userId) { @@ -103,9 +103,9 @@ namespace Jellyfin.Api.Controllers [HttpPost("{playlistId}/Items/{itemId}/Move/{newIndex}")] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task MoveItem( - [FromRoute] string? playlistId, - [FromRoute] string? itemId, - [FromRoute] int newIndex) + [FromRoute][Required] string? playlistId, + [FromRoute][Required] string? itemId, + [FromRoute][Required] int newIndex) { await _playlistManager.MoveItemAsync(playlistId, itemId, newIndex).ConfigureAwait(false); return NoContent(); @@ -120,7 +120,7 @@ namespace Jellyfin.Api.Controllers /// An on success. [HttpDelete("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task RemoveFromPlaylist([FromRoute] string? playlistId, [FromQuery] string? entryIds) + public async Task RemoveFromPlaylist([FromRoute][Required] string? playlistId, [FromQuery] string? entryIds) { await _playlistManager.RemoveFromPlaylistAsync(playlistId, RequestHelpers.Split(entryIds, ',', true)).ConfigureAwait(false); return NoContent(); @@ -143,15 +143,15 @@ namespace Jellyfin.Api.Controllers /// The original playlist items. [HttpGet("{playlistId}/Items")] public ActionResult> GetPlaylistItems( - [FromRoute] Guid playlistId, - [FromRoute] Guid userId, - [FromRoute] int? startIndex, - [FromRoute] int? limit, - [FromRoute] string? fields, - [FromRoute] bool? enableImages, - [FromRoute] bool? enableUserData, - [FromRoute] int? imageTypeLimit, - [FromRoute] string? enableImageTypes) + [FromRoute][Required] Guid playlistId, + [FromRoute][Required] Guid userId, + [FromRoute][Required] int? startIndex, + [FromRoute][Required] int? limit, + [FromRoute][Required] string? fields, + [FromRoute][Required] bool? enableImages, + [FromRoute][Required] bool? enableUserData, + [FromRoute][Required] int? imageTypeLimit, + [FromRoute][Required] string? enableImageTypes) { var playlist = (Playlist)_libraryManager.GetItemById(playlistId); if (playlist == null) diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 22f2ca5c3..797c5aa36 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -71,8 +71,8 @@ namespace Jellyfin.Api.Controllers [HttpPost("Users/{userId}/PlayedItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult MarkPlayedItem( - [FromRoute] Guid userId, - [FromRoute] Guid itemId, + [FromRoute][Required] Guid userId, + [FromRoute][Required] Guid itemId, [FromQuery] DateTime? datePlayed) { var user = _userManager.GetUserById(userId); @@ -96,7 +96,7 @@ namespace Jellyfin.Api.Controllers /// A containing the . [HttpDelete("Users/{userId}/PlayedItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult MarkUnplayedItem([FromRoute] Guid userId, [FromRoute] Guid itemId) + public ActionResult MarkUnplayedItem([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) { var user = _userManager.GetUserById(userId); var session = RequestHelpers.GetSession(_sessionManager, _authContext, Request); @@ -195,8 +195,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] public async Task OnPlaybackStart( - [FromRoute] Guid userId, - [FromRoute] Guid itemId, + [FromRoute][Required] Guid userId, + [FromRoute][Required] Guid itemId, [FromQuery] string? mediaSourceId, [FromQuery] int? audioStreamIndex, [FromQuery] int? subtitleStreamIndex, @@ -245,8 +245,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] public async Task OnPlaybackProgress( - [FromRoute] Guid userId, - [FromRoute] Guid itemId, + [FromRoute][Required] Guid userId, + [FromRoute][Required] Guid itemId, [FromQuery] string? mediaSourceId, [FromQuery] long? positionTicks, [FromQuery] int? audioStreamIndex, @@ -297,8 +297,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] public async Task OnPlaybackStopped( - [FromRoute] Guid userId, - [FromRoute] Guid itemId, + [FromRoute][Required] Guid userId, + [FromRoute][Required] Guid itemId, [FromQuery] string? mediaSourceId, [FromQuery] string? nextMediaType, [FromQuery] long? positionTicks, diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs index a82f2621a..d0de1a422 100644 --- a/Jellyfin.Api/Controllers/PluginsController.cs +++ b/Jellyfin.Api/Controllers/PluginsController.cs @@ -64,7 +64,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UninstallPlugin([FromRoute] Guid pluginId) + public ActionResult UninstallPlugin([FromRoute][Required] Guid pluginId) { var plugin = _appHost.Plugins.FirstOrDefault(p => p.Id == pluginId); if (plugin == null) @@ -86,7 +86,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("{pluginId}/Configuration")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetPluginConfiguration([FromRoute] Guid pluginId) + public ActionResult GetPluginConfiguration([FromRoute][Required] Guid pluginId) { if (!(_appHost.Plugins.FirstOrDefault(p => p.Id == pluginId) is IHasPluginConfiguration plugin)) { @@ -113,7 +113,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("{pluginId}/Configuration")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task UpdatePluginConfiguration([FromRoute] Guid pluginId) + public async Task UpdatePluginConfiguration([FromRoute][Required] Guid pluginId) { if (!(_appHost.Plugins.FirstOrDefault(p => p.Id == pluginId) is IHasPluginConfiguration plugin)) { @@ -172,7 +172,7 @@ namespace Jellyfin.Api.Controllers [Obsolete("This endpoint should not be used.")] [HttpPost("RegistrationRecords/{name}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetRegistrationStatus([FromRoute] string? name) + public ActionResult GetRegistrationStatus([FromRoute][Required] string? name) { return new MBRegistrationRecord { @@ -194,7 +194,7 @@ namespace Jellyfin.Api.Controllers [Obsolete("Paid plugins are not supported")] [HttpGet("Registrations/{name}")] [ProducesResponseType(StatusCodes.Status501NotImplemented)] - public ActionResult GetRegistration([FromRoute] string? name) + public ActionResult GetRegistration([FromRoute][Required] string? name) { // TODO Once we have proper apps and plugins and decide to break compatibility with paid plugins, // delete all these registration endpoints. They are only kept for compatibility. diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs index 81aefd15c..597d3f2f6 100644 --- a/Jellyfin.Api/Controllers/RemoteImageController.cs +++ b/Jellyfin.Api/Controllers/RemoteImageController.cs @@ -70,7 +70,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetRemoteImages( - [FromRoute] Guid itemId, + [FromRoute][Required] Guid itemId, [FromQuery] ImageType? type, [FromQuery] int? startIndex, [FromQuery] int? limit, @@ -133,7 +133,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> GetRemoteImageProviders([FromRoute] Guid itemId) + public ActionResult> GetRemoteImageProviders([FromRoute][Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -209,7 +209,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task DownloadRemoteImage( - [FromRoute] Guid itemId, + [FromRoute][Required] Guid itemId, [FromQuery, Required] ImageType type, [FromQuery] string? imageUrl) { diff --git a/Jellyfin.Api/Controllers/ScheduledTasksController.cs b/Jellyfin.Api/Controllers/ScheduledTasksController.cs index e672070c0..ea6288de0 100644 --- a/Jellyfin.Api/Controllers/ScheduledTasksController.cs +++ b/Jellyfin.Api/Controllers/ScheduledTasksController.cs @@ -94,7 +94,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Running/{taskId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult StartTask([FromRoute] string? taskId) + public ActionResult StartTask([FromRoute][Required] string? taskId) { var task = _taskManager.ScheduledTasks.FirstOrDefault(o => o.Id.Equals(taskId, StringComparison.OrdinalIgnoreCase)); diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index ba8d51598..4f0f241c6 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -336,7 +336,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult AddUserToSession( [FromRoute, Required] string? sessionId, - [FromRoute] Guid userId) + [FromRoute][Required] Guid userId) { _sessionManager.AddAdditionalUser(sessionId, userId); return NoContent(); @@ -353,8 +353,8 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult RemoveUserFromSession( - [FromRoute] string? sessionId, - [FromRoute] Guid userId) + [FromRoute][Required] string? sessionId, + [FromRoute][Required] Guid userId) { _sessionManager.RemoveAdditionalUser(sessionId, userId); return NoContent(); diff --git a/Jellyfin.Api/Controllers/StudiosController.cs b/Jellyfin.Api/Controllers/StudiosController.cs index 6f2787d93..b3bb06500 100644 --- a/Jellyfin.Api/Controllers/StudiosController.cs +++ b/Jellyfin.Api/Controllers/StudiosController.cs @@ -259,7 +259,7 @@ namespace Jellyfin.Api.Controllers /// An containing the studio. [HttpGet("{name}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetStudio([FromRoute] string name, [FromQuery] Guid? userId) + public ActionResult GetStudio([FromRoute][Required] string name, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions().AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs index 988acccc3..1096a06d2 100644 --- a/Jellyfin.Api/Controllers/SubtitleController.cs +++ b/Jellyfin.Api/Controllers/SubtitleController.cs @@ -86,8 +86,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult DeleteSubtitle( - [FromRoute] Guid itemId, - [FromRoute] int index) + [FromRoute][Required] Guid itemId, + [FromRoute][Required] int index) { var item = _libraryManager.GetItemById(itemId); @@ -112,7 +112,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] public async Task>> SearchRemoteSubtitles( - [FromRoute] Guid itemId, + [FromRoute][Required] Guid itemId, [FromRoute, Required] string? language, [FromQuery] bool? isPerfectMatch) { @@ -132,7 +132,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task DownloadRemoteSubtitles( - [FromRoute] Guid itemId, + [FromRoute][Required] Guid itemId, [FromRoute, Required] string? subtitleId) { var video = (Video)_libraryManager.GetItemById(itemId); @@ -193,7 +193,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] long? endPositionTicks, [FromQuery] bool copyTimestamps = false, [FromQuery] bool addVttTimeMap = false, - [FromRoute] long startPositionTicks = 0) + [FromRoute][Required] long startPositionTicks = 0) { if (string.Equals(format, "js", StringComparison.OrdinalIgnoreCase)) { @@ -253,9 +253,9 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")] public async Task GetSubtitlePlaylist( - [FromRoute] Guid itemId, - [FromRoute] int index, - [FromRoute] string? mediaSourceId, + [FromRoute][Required] Guid itemId, + [FromRoute][Required] int index, + [FromRoute][Required] string? mediaSourceId, [FromQuery, Required] int segmentLength) { var item = (Video)_libraryManager.GetItemById(itemId); diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs index 42db6b6a1..4fff3cd3e 100644 --- a/Jellyfin.Api/Controllers/SuggestionsController.cs +++ b/Jellyfin.Api/Controllers/SuggestionsController.cs @@ -53,7 +53,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/Suggestions")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSuggestions( - [FromRoute] Guid userId, + [FromRoute][Required] Guid userId, [FromQuery] string? mediaType, [FromQuery] string? type, [FromQuery] int? startIndex, diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index b13cf9fa5..d57f23d9c 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -92,8 +92,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status302Found)] public async Task GetUniversalAudioStream( - [FromRoute] Guid itemId, - [FromRoute] string? container, + [FromRoute][Required] Guid itemId, + [FromRoute][Required] string? container, [FromQuery] string? mediaSourceId, [FromQuery] string? deviceId, [FromQuery] Guid? userId, diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index d67f82219..a9cab9cde 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -108,7 +108,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.IgnoreParentalControl)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetUserById([FromRoute] Guid userId) + public ActionResult GetUserById([FromRoute][Required] Guid userId) { var user = _userManager.GetUserById(userId); @@ -132,7 +132,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteUser([FromRoute] Guid userId) + public ActionResult DeleteUser([FromRoute][Required] Guid userId) { var user = _userManager.GetUserById(userId); _sessionManager.RevokeUserTokens(user.Id, null); @@ -265,7 +265,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task UpdateUserPassword( - [FromRoute] Guid userId, + [FromRoute][Required] Guid userId, [FromBody] UpdateUserPassword request) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) @@ -323,7 +323,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult UpdateUserEasyPassword( - [FromRoute] Guid userId, + [FromRoute][Required] Guid userId, [FromBody] UpdateUserEasyPassword request) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) @@ -365,7 +365,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task UpdateUser( - [FromRoute] Guid userId, + [FromRoute][Required] Guid userId, [FromBody] UserDto updateUser) { if (updateUser == null) @@ -409,7 +409,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult UpdateUserPolicy( - [FromRoute] Guid userId, + [FromRoute][Required] Guid userId, [FromBody] UserPolicy newPolicy) { if (newPolicy == null) @@ -464,7 +464,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult UpdateUserConfiguration( - [FromRoute] Guid userId, + [FromRoute][Required] Guid userId, [FromBody] UserConfiguration userConfig) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, false)) diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index f55ff6f3d..809fbf43a 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -70,7 +70,7 @@ namespace Jellyfin.Api.Controllers /// An containing the d item. [HttpGet("Users/{userId}/Items/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> GetItem([FromRoute] Guid userId, [FromRoute] Guid itemId) + public async Task> GetItem([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -93,7 +93,7 @@ namespace Jellyfin.Api.Controllers /// An containing the user's root folder. [HttpGet("Users/{userId}/Items/Root")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetRootFolder([FromRoute] Guid userId) + public ActionResult GetRootFolder([FromRoute][Required] Guid userId) { var user = _userManager.GetUserById(userId); var item = _libraryManager.GetUserRootFolder(); @@ -110,7 +110,7 @@ namespace Jellyfin.Api.Controllers /// An containing the intros to play. [HttpGet("Users/{userId}/Items/{itemId}/Intros")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task>> GetIntros([FromRoute] Guid userId, [FromRoute] Guid itemId) + public async Task>> GetIntros([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -138,7 +138,7 @@ namespace Jellyfin.Api.Controllers /// An containing the . [HttpPost("Users/{userId}/FavoriteItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult MarkFavoriteItem([FromRoute] Guid userId, [FromRoute] Guid itemId) + public ActionResult MarkFavoriteItem([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) { return MarkFavorite(userId, itemId, true); } @@ -152,7 +152,7 @@ namespace Jellyfin.Api.Controllers /// An containing the . [HttpDelete("Users/{userId}/FavoriteItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult UnmarkFavoriteItem([FromRoute] Guid userId, [FromRoute] Guid itemId) + public ActionResult UnmarkFavoriteItem([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) { return MarkFavorite(userId, itemId, false); } @@ -166,7 +166,7 @@ namespace Jellyfin.Api.Controllers /// An containing the . [HttpDelete("Users/{userId}/Items/{itemId}/Rating")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult DeleteUserItemRating([FromRoute] Guid userId, [FromRoute] Guid itemId) + public ActionResult DeleteUserItemRating([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) { return UpdateUserItemRatingInternal(userId, itemId, null); } @@ -181,7 +181,7 @@ namespace Jellyfin.Api.Controllers /// An containing the . [HttpPost("Users/{userId}/Items/{itemId}/Rating")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult UpdateUserItemRating([FromRoute] Guid userId, [FromRoute] Guid itemId, [FromQuery] bool? likes) + public ActionResult UpdateUserItemRating([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId, [FromQuery] bool? likes) { return UpdateUserItemRatingInternal(userId, itemId, likes); } @@ -195,7 +195,7 @@ namespace Jellyfin.Api.Controllers /// The items local trailers. [HttpGet("Users/{userId}/Items/{itemId}/LocalTrailers")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetLocalTrailers([FromRoute] Guid userId, [FromRoute] Guid itemId) + public ActionResult> GetLocalTrailers([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -230,7 +230,7 @@ namespace Jellyfin.Api.Controllers /// An containing the special features. [HttpGet("Users/{userId}/Items/{itemId}/SpecialFeatures")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetSpecialFeatures([FromRoute] Guid userId, [FromRoute] Guid itemId) + public ActionResult> GetSpecialFeatures([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -264,7 +264,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/Items/Latest")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetLatestMedia( - [FromRoute] Guid userId, + [FromRoute][Required] Guid userId, [FromQuery] Guid? parentId, [FromQuery] string? fields, [FromQuery] string? includeItemTypes, diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs index 6df7cc779..fb78707f8 100644 --- a/Jellyfin.Api/Controllers/UserViewsController.cs +++ b/Jellyfin.Api/Controllers/UserViewsController.cs @@ -64,7 +64,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/Views")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetUserViews( - [FromRoute] Guid userId, + [FromRoute][Required] Guid userId, [FromQuery] bool? includeExternalContent, [FromQuery] string? presetViews, [FromQuery] bool includeHidden = false) @@ -126,7 +126,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/GroupingOptions")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> GetGroupingOptions([FromRoute] Guid userId) + public ActionResult> GetGroupingOptions([FromRoute][Required] Guid userId) { var user = _userManager.GetUserById(userId); if (user == null) diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index 76188f46d..77f21fcf3 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -162,7 +162,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Videos/{itemId}/live.m3u8")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetLiveHlsStream( - [FromRoute] Guid itemId, + [FromRoute][Required] Guid itemId, [FromQuery] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 83b03f965..224cc7b72 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -115,7 +115,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("{itemId}/AdditionalParts")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetAdditionalPart([FromRoute] Guid itemId, [FromQuery] Guid? userId) + public ActionResult> GetAdditionalPart([FromRoute][Required] Guid itemId, [FromQuery] Guid? userId) { var user = userId.HasValue && !userId.Equals(Guid.Empty) ? _userManager.GetUserById(userId.Value) @@ -162,7 +162,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task DeleteAlternateSources([FromRoute] Guid itemId) + public async Task DeleteAlternateSources([FromRoute][Required] Guid itemId) { var video = (Video)_libraryManager.GetItemById(itemId); @@ -331,8 +331,8 @@ namespace Jellyfin.Api.Controllers [HttpHead("{itemId}/stream", Name = "HeadVideoStream")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetVideoStream( - [FromRoute] Guid itemId, - [FromRoute] string? container, + [FromRoute][Required] Guid itemId, + [FromRoute][Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, diff --git a/Jellyfin.Api/Controllers/YearsController.cs b/Jellyfin.Api/Controllers/YearsController.cs index eb91ac23e..620edf905 100644 --- a/Jellyfin.Api/Controllers/YearsController.cs +++ b/Jellyfin.Api/Controllers/YearsController.cs @@ -179,7 +179,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("{year}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetYear([FromRoute] int year, [FromQuery] Guid? userId) + public ActionResult GetYear([FromRoute][Required] int year, [FromQuery] Guid? userId) { var item = _libraryManager.GetYear(year); if (item == null) -- cgit v1.2.3 From 29fc882037162b8c7a79557556be9d535e94779f Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 6 Sep 2020 09:07:27 -0600 Subject: merge all attributes --- Jellyfin.Api/Controllers/AlbumsController.cs | 4 +- Jellyfin.Api/Controllers/ArtistsController.cs | 2 +- Jellyfin.Api/Controllers/AudioController.cs | 4 +- Jellyfin.Api/Controllers/ChannelsController.cs | 4 +- Jellyfin.Api/Controllers/CollectionController.cs | 4 +- .../Controllers/ConfigurationController.cs | 4 +- .../Controllers/DisplayPreferencesController.cs | 4 +- Jellyfin.Api/Controllers/DlnaController.cs | 6 +- Jellyfin.Api/Controllers/DlnaServerController.cs | 18 +-- Jellyfin.Api/Controllers/DynamicHlsController.cs | 32 ++--- Jellyfin.Api/Controllers/GenresController.cs | 2 +- Jellyfin.Api/Controllers/HlsSegmentController.cs | 12 +- Jellyfin.Api/Controllers/ImageController.cs | 156 ++++++++++----------- Jellyfin.Api/Controllers/InstantMixController.cs | 12 +- Jellyfin.Api/Controllers/ItemLookupController.cs | 4 +- Jellyfin.Api/Controllers/ItemRefreshController.cs | 2 +- Jellyfin.Api/Controllers/ItemUpdateController.cs | 6 +- Jellyfin.Api/Controllers/ItemsController.cs | 4 +- Jellyfin.Api/Controllers/LibraryController.cs | 16 +-- Jellyfin.Api/Controllers/LiveTvController.cs | 26 ++-- Jellyfin.Api/Controllers/MediaInfoController.cs | 4 +- Jellyfin.Api/Controllers/MusicGenresController.cs | 2 +- Jellyfin.Api/Controllers/PackageController.cs | 6 +- Jellyfin.Api/Controllers/PersonsController.cs | 2 +- Jellyfin.Api/Controllers/PlaylistsController.cs | 28 ++-- Jellyfin.Api/Controllers/PlaystateController.cs | 18 +-- Jellyfin.Api/Controllers/PluginsController.cs | 10 +- Jellyfin.Api/Controllers/RemoteImageController.cs | 6 +- .../Controllers/ScheduledTasksController.cs | 2 +- Jellyfin.Api/Controllers/SessionController.cs | 6 +- Jellyfin.Api/Controllers/StudiosController.cs | 2 +- Jellyfin.Api/Controllers/SubtitleController.cs | 16 +-- Jellyfin.Api/Controllers/SuggestionsController.cs | 2 +- .../Controllers/UniversalAudioController.cs | 4 +- Jellyfin.Api/Controllers/UserController.cs | 14 +- Jellyfin.Api/Controllers/UserLibraryController.cs | 20 +-- Jellyfin.Api/Controllers/UserViewsController.cs | 4 +- Jellyfin.Api/Controllers/VideoHlsController.cs | 2 +- Jellyfin.Api/Controllers/VideosController.cs | 8 +- Jellyfin.Api/Controllers/YearsController.cs | 2 +- 40 files changed, 240 insertions(+), 240 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/AlbumsController.cs b/Jellyfin.Api/Controllers/AlbumsController.cs index 9b68d056f..357f646a2 100644 --- a/Jellyfin.Api/Controllers/AlbumsController.cs +++ b/Jellyfin.Api/Controllers/AlbumsController.cs @@ -53,7 +53,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Albums/{albumId}/Similar")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSimilarAlbums( - [FromRoute][Required] string albumId, + [FromRoute, Required] string albumId, [FromQuery] Guid? userId, [FromQuery] string? excludeArtistIds, [FromQuery] int? limit) @@ -85,7 +85,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Artists/{artistId}/Similar")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSimilarArtists( - [FromRoute][Required] string artistId, + [FromRoute, Required] string artistId, [FromQuery] Guid? userId, [FromQuery] string? excludeArtistIds, [FromQuery] int? limit) diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs index b3dad14f3..d38214116 100644 --- a/Jellyfin.Api/Controllers/ArtistsController.cs +++ b/Jellyfin.Api/Controllers/ArtistsController.cs @@ -470,7 +470,7 @@ namespace Jellyfin.Api.Controllers /// An containing the artist. [HttpGet("{name}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetArtistByName([FromRoute][Required] string name, [FromQuery] Guid? userId) + public ActionResult GetArtistByName([FromRoute, Required] string name, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions().AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index a81efe966..3bec43720 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -90,8 +90,8 @@ namespace Jellyfin.Api.Controllers [HttpHead("{itemId}/stream", Name = "HeadAudioStream")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetAudioStream( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] string? container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, diff --git a/Jellyfin.Api/Controllers/ChannelsController.cs b/Jellyfin.Api/Controllers/ChannelsController.cs index 36dff2ad3..33a969f85 100644 --- a/Jellyfin.Api/Controllers/ChannelsController.cs +++ b/Jellyfin.Api/Controllers/ChannelsController.cs @@ -91,7 +91,7 @@ namespace Jellyfin.Api.Controllers /// Channel features returned. /// An containing the channel features. [HttpGet("{channelId}/Features")] - public ActionResult GetChannelFeatures([FromRoute][Required] string channelId) + public ActionResult GetChannelFeatures([FromRoute, Required] string channelId) { return _channelManager.GetChannelFeatures(channelId); } @@ -115,7 +115,7 @@ namespace Jellyfin.Api.Controllers /// [HttpGet("{channelId}/Items")] public async Task>> GetChannelItems( - [FromRoute][Required] Guid channelId, + [FromRoute, Required] Guid channelId, [FromQuery] Guid? folderId, [FromQuery] Guid? userId, [FromQuery] int? startIndex, diff --git a/Jellyfin.Api/Controllers/CollectionController.cs b/Jellyfin.Api/Controllers/CollectionController.cs index 0b1f655da..f78690b06 100644 --- a/Jellyfin.Api/Controllers/CollectionController.cs +++ b/Jellyfin.Api/Controllers/CollectionController.cs @@ -88,7 +88,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("{collectionId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task AddToCollection([FromRoute][Required] Guid collectionId, [FromQuery, Required] string? itemIds) + public async Task AddToCollection([FromRoute, Required] Guid collectionId, [FromQuery, Required] string? itemIds) { await _collectionManager.AddToCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(true); return NoContent(); @@ -103,7 +103,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpDelete("{collectionId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task RemoveFromCollection([FromRoute][Required] Guid collectionId, [FromQuery, Required] string? itemIds) + public async Task RemoveFromCollection([FromRoute, Required] Guid collectionId, [FromQuery, Required] string? itemIds) { await _collectionManager.RemoveFromCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(false); return NoContent(); diff --git a/Jellyfin.Api/Controllers/ConfigurationController.cs b/Jellyfin.Api/Controllers/ConfigurationController.cs index f13b6d38d..5fd4c712a 100644 --- a/Jellyfin.Api/Controllers/ConfigurationController.cs +++ b/Jellyfin.Api/Controllers/ConfigurationController.cs @@ -73,7 +73,7 @@ namespace Jellyfin.Api.Controllers /// Configuration. [HttpGet("Configuration/{key}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetNamedConfiguration([FromRoute][Required] string? key) + public ActionResult GetNamedConfiguration([FromRoute, Required] string? key) { return _configurationManager.GetConfiguration(key); } @@ -87,7 +87,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Configuration/{key}")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task UpdateNamedConfiguration([FromRoute][Required] string? key) + public async Task UpdateNamedConfiguration([FromRoute, Required] string? key) { var configurationType = _configurationManager.GetConfigurationType(key); var configuration = await JsonSerializer.DeserializeAsync(Request.Body, configurationType, _serializerOptions).ConfigureAwait(false); diff --git a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs index 6422a45fd..6bb7b1910 100644 --- a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs +++ b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs @@ -43,7 +43,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "displayPreferencesId", Justification = "Imported from ServiceStack")] public ActionResult GetDisplayPreferences( - [FromRoute][Required] string? displayPreferencesId, + [FromRoute, Required] string? displayPreferencesId, [FromQuery] [Required] Guid userId, [FromQuery] [Required] string? client) { @@ -97,7 +97,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "displayPreferencesId", Justification = "Imported from ServiceStack")] public ActionResult UpdateDisplayPreferences( - [FromRoute][Required] string? displayPreferencesId, + [FromRoute, Required] string? displayPreferencesId, [FromQuery, Required] Guid userId, [FromQuery, Required] string? client, [FromBody, Required] DisplayPreferencesDto displayPreferences) diff --git a/Jellyfin.Api/Controllers/DlnaController.cs b/Jellyfin.Api/Controllers/DlnaController.cs index e6f0fb41e..052a6aff2 100644 --- a/Jellyfin.Api/Controllers/DlnaController.cs +++ b/Jellyfin.Api/Controllers/DlnaController.cs @@ -60,7 +60,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Profiles/{profileId}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetProfile([FromRoute][Required] string profileId) + public ActionResult GetProfile([FromRoute, Required] string profileId) { var profile = _dlnaManager.GetProfile(profileId); if (profile == null) @@ -81,7 +81,7 @@ namespace Jellyfin.Api.Controllers [HttpDelete("Profiles/{profileId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteProfile([FromRoute][Required] string profileId) + public ActionResult DeleteProfile([FromRoute, Required] string profileId) { var existingDeviceProfile = _dlnaManager.GetProfile(profileId); if (existingDeviceProfile == null) @@ -118,7 +118,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Profiles/{profileId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UpdateProfile([FromRoute][Required] string profileId, [FromBody] DeviceProfile deviceProfile) + public ActionResult UpdateProfile([FromRoute, Required] string profileId, [FromBody] DeviceProfile deviceProfile) { var existingDeviceProfile = _dlnaManager.GetProfile(profileId); if (existingDeviceProfile == null) diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index 2f93ca2c2..8cdea4367 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -46,7 +46,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("{serverId}/description.xml", Name = "GetDescriptionXml_2")] [Produces(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetDescriptionXml([FromRoute][Required] string serverId) + public ActionResult GetDescriptionXml([FromRoute, Required] string serverId) { var url = GetAbsoluteUri(); var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase)); @@ -66,7 +66,7 @@ namespace Jellyfin.Api.Controllers [Produces(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetContentDirectory([FromRoute][Required] string serverId) + public ActionResult GetContentDirectory([FromRoute, Required] string serverId) { return Ok(_contentDirectory.GetServiceXml()); } @@ -82,7 +82,7 @@ namespace Jellyfin.Api.Controllers [Produces(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetMediaReceiverRegistrar([FromRoute][Required] string serverId) + public ActionResult GetMediaReceiverRegistrar([FromRoute, Required] string serverId) { return Ok(_mediaReceiverRegistrar.GetServiceXml()); } @@ -98,7 +98,7 @@ namespace Jellyfin.Api.Controllers [Produces(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetConnectionManager([FromRoute][Required] string serverId) + public ActionResult GetConnectionManager([FromRoute, Required] string serverId) { return Ok(_connectionManager.GetServiceXml()); } @@ -109,7 +109,7 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Control response. [HttpPost("{serverId}/ContentDirectory/Control")] - public async Task> ProcessContentDirectoryControlRequest([FromRoute][Required] string serverId) + public async Task> ProcessContentDirectoryControlRequest([FromRoute, Required] string serverId) { return await ProcessControlRequestInternalAsync(serverId, Request.Body, _contentDirectory).ConfigureAwait(false); } @@ -120,7 +120,7 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Control response. [HttpPost("{serverId}/ConnectionManager/Control")] - public async Task> ProcessConnectionManagerControlRequest([FromRoute][Required] string serverId) + public async Task> ProcessConnectionManagerControlRequest([FromRoute, Required] string serverId) { return await ProcessControlRequestInternalAsync(serverId, Request.Body, _connectionManager).ConfigureAwait(false); } @@ -131,7 +131,7 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Control response. [HttpPost("{serverId}/MediaReceiverRegistrar/Control")] - public async Task> ProcessMediaReceiverRegistrarControlRequest([FromRoute][Required] string serverId) + public async Task> ProcessMediaReceiverRegistrarControlRequest([FromRoute, Required] string serverId) { return await ProcessControlRequestInternalAsync(serverId, Request.Body, _mediaReceiverRegistrar).ConfigureAwait(false); } @@ -186,7 +186,7 @@ namespace Jellyfin.Api.Controllers /// Icon stream. [HttpGet("{serverId}/icons/{fileName}")] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetIconId([FromRoute][Required] string serverId, [FromRoute][Required] string fileName) + public ActionResult GetIconId([FromRoute, Required] string serverId, [FromRoute, Required] string fileName) { return GetIconInternal(fileName); } @@ -197,7 +197,7 @@ namespace Jellyfin.Api.Controllers /// The icon filename. /// Icon stream. [HttpGet("icons/{fileName}")] - public ActionResult GetIcon([FromRoute][Required] string fileName) + public ActionResult GetIcon([FromRoute, Required] string fileName) { return GetIconInternal(fileName); } diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index ec711ce34..d81c7996e 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -167,8 +167,8 @@ namespace Jellyfin.Api.Controllers [HttpHead("Videos/{itemId}/master.m3u8", Name = "HeadMasterHlsVideoPlaylist")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetMasterHlsVideoPlaylist( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] string? container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -334,8 +334,8 @@ namespace Jellyfin.Api.Controllers [HttpHead("Audio/{itemId}/master.m3u8", Name = "HeadMasterHlsAudioPlaylist")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetMasterHlsAudioPlaylist( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] string? container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -499,8 +499,8 @@ namespace Jellyfin.Api.Controllers [HttpGet("Videos/{itemId}/main.m3u8")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetVariantHlsVideoPlaylist( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] string? container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -664,8 +664,8 @@ namespace Jellyfin.Api.Controllers [HttpGet("Audio/{itemId}/main.m3u8")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetVariantHlsAudioPlaylist( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] string? container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -832,10 +832,10 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "playlistId", Justification = "Imported from ServiceStack")] public async Task GetHlsVideoSegment( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] string playlistId, - [FromRoute][Required] int segmentId, - [FromRoute][Required] string container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string playlistId, + [FromRoute, Required] int segmentId, + [FromRoute, Required] string container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -1001,10 +1001,10 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "playlistId", Justification = "Imported from ServiceStack")] public async Task GetHlsAudioSegment( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] string playlistId, - [FromRoute][Required] int segmentId, - [FromRoute][Required] string container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string playlistId, + [FromRoute, Required] int segmentId, + [FromRoute, Required] string container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs index 53be16370..de6aa86c9 100644 --- a/Jellyfin.Api/Controllers/GenresController.cs +++ b/Jellyfin.Api/Controllers/GenresController.cs @@ -261,7 +261,7 @@ namespace Jellyfin.Api.Controllers /// An containing the genre. [HttpGet("{genreName}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetGenre([FromRoute][Required] string genreName, [FromQuery] Guid? userId) + public ActionResult GetGenre([FromRoute, Required] string genreName, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions() .AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs index 58c583d09..e96df83fa 100644 --- a/Jellyfin.Api/Controllers/HlsSegmentController.cs +++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs @@ -56,7 +56,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Audio/{itemId}/hls/{segmentId}/stream.aac", Name = "GetHlsAudioSegmentLegacyAac")] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")] - public ActionResult GetHlsAudioSegmentLegacy([FromRoute][Required] string itemId, [FromRoute][Required] string segmentId) + public ActionResult GetHlsAudioSegmentLegacy([FromRoute, Required] string itemId, [FromRoute, Required] string segmentId) { // TODO: Deprecate with new iOS app var file = segmentId + Path.GetExtension(Request.Path); @@ -76,7 +76,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")] - public ActionResult GetHlsPlaylistLegacy([FromRoute][Required] string itemId, [FromRoute][Required] string playlistId) + public ActionResult GetHlsPlaylistLegacy([FromRoute, Required] string itemId, [FromRoute, Required] string playlistId) { var file = playlistId + Path.GetExtension(Request.Path); file = Path.Combine(_serverConfigurationManager.GetTranscodePath(), file); @@ -115,10 +115,10 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")] public ActionResult GetHlsVideoSegmentLegacy( - [FromRoute][Required] string itemId, - [FromRoute][Required] string playlistId, - [FromRoute][Required] string segmentId, - [FromRoute][Required] string segmentContainer) + [FromRoute, Required] string itemId, + [FromRoute, Required] string playlistId, + [FromRoute, Required] string segmentId, + [FromRoute, Required] string segmentContainer) { var file = segmentId + Path.GetExtension(Request.Path); var transcodeFolderPath = _serverConfigurationManager.GetTranscodePath(); diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index e28957c9b..453da5711 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -91,9 +91,9 @@ namespace Jellyfin.Api.Controllers [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")] public async Task PostUserImage( - [FromRoute][Required] Guid userId, - [FromRoute][Required] ImageType imageType, - [FromRoute][Required] int? index = null) + [FromRoute, Required] Guid userId, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] int? index = null) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) { @@ -138,9 +138,9 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult DeleteUserImage( - [FromRoute][Required] Guid userId, - [FromRoute][Required] ImageType imageType, - [FromRoute][Required] int? index = null) + [FromRoute, Required] Guid userId, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] int? index = null) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) { @@ -176,9 +176,9 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task DeleteItemImage( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] ImageType imageType, - [FromRoute][Required] int? imageIndex = null) + [FromRoute, Required] Guid itemId, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -206,9 +206,9 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")] public async Task SetItemImage( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] ImageType imageType, - [FromRoute][Required] int? imageIndex = null) + [FromRoute, Required] Guid itemId, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -239,9 +239,9 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task UpdateItemImageIndex( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] ImageType imageType, - [FromRoute][Required] int imageIndex, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] int imageIndex, [FromQuery] int newIndex) { var item = _libraryManager.GetItemById(itemId); @@ -265,7 +265,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetItemImageInfos([FromRoute][Required] Guid itemId) + public async Task>> GetItemImageInfos([FromRoute, Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -353,10 +353,10 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetItemImage( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] ImageType imageType, - [FromRoute][Required] int? maxWidth, - [FromRoute][Required] int? maxHeight, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] int? maxWidth, + [FromRoute, Required] int? maxHeight, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -369,7 +369,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute][Required] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -431,23 +431,23 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetItemImage2( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] ImageType imageType, - [FromRoute][Required] int? maxWidth, - [FromRoute][Required] int? maxHeight, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] int? maxWidth, + [FromRoute, Required] int? maxHeight, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, - [FromRoute][Required] string tag, + [FromRoute, Required] string tag, [FromQuery] bool? cropWhitespace, - [FromRoute][Required] string format, + [FromRoute, Required] string format, [FromQuery] bool? addPlayedIndicator, - [FromRoute][Required] double? percentPlayed, - [FromRoute][Required] int? unplayedCount, + [FromRoute, Required] double? percentPlayed, + [FromRoute, Required] int? unplayedCount, [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute][Required] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -509,14 +509,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetArtistImage( - [FromRoute][Required] string name, - [FromRoute][Required] ImageType imageType, - [FromRoute][Required] string tag, - [FromRoute][Required] string format, - [FromRoute][Required] int? maxWidth, - [FromRoute][Required] int? maxHeight, - [FromRoute][Required] double? percentPlayed, - [FromRoute][Required] int? unplayedCount, + [FromRoute, Required] string name, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] string tag, + [FromRoute, Required] string format, + [FromRoute, Required] int? maxWidth, + [FromRoute, Required] int? maxHeight, + [FromRoute, Required] double? percentPlayed, + [FromRoute, Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -525,7 +525,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute][Required] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetArtist(name); if (item == null) @@ -587,14 +587,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetGenreImage( - [FromRoute][Required] string name, - [FromRoute][Required] ImageType imageType, - [FromRoute][Required] string tag, - [FromRoute][Required] string format, - [FromRoute][Required] int? maxWidth, - [FromRoute][Required] int? maxHeight, - [FromRoute][Required] double? percentPlayed, - [FromRoute][Required] int? unplayedCount, + [FromRoute, Required] string name, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] string tag, + [FromRoute, Required] string format, + [FromRoute, Required] int? maxWidth, + [FromRoute, Required] int? maxHeight, + [FromRoute, Required] double? percentPlayed, + [FromRoute, Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -603,7 +603,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute][Required] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetGenre(name); if (item == null) @@ -665,14 +665,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetMusicGenreImage( - [FromRoute][Required] string name, - [FromRoute][Required] ImageType imageType, - [FromRoute][Required] string tag, - [FromRoute][Required] string format, - [FromRoute][Required] int? maxWidth, - [FromRoute][Required] int? maxHeight, - [FromRoute][Required] double? percentPlayed, - [FromRoute][Required] int? unplayedCount, + [FromRoute, Required] string name, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] string tag, + [FromRoute, Required] string format, + [FromRoute, Required] int? maxWidth, + [FromRoute, Required] int? maxHeight, + [FromRoute, Required] double? percentPlayed, + [FromRoute, Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -681,7 +681,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute][Required] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetMusicGenre(name); if (item == null) @@ -743,14 +743,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetPersonImage( - [FromRoute][Required] string name, - [FromRoute][Required] ImageType imageType, - [FromRoute][Required] string tag, - [FromRoute][Required] string format, - [FromRoute][Required] int? maxWidth, - [FromRoute][Required] int? maxHeight, - [FromRoute][Required] double? percentPlayed, - [FromRoute][Required] int? unplayedCount, + [FromRoute, Required] string name, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] string tag, + [FromRoute, Required] string format, + [FromRoute, Required] int? maxWidth, + [FromRoute, Required] int? maxHeight, + [FromRoute, Required] double? percentPlayed, + [FromRoute, Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -759,7 +759,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute][Required] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetPerson(name); if (item == null) @@ -821,14 +821,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetStudioImage( - [FromRoute][Required] string name, - [FromRoute][Required] ImageType imageType, - [FromRoute][Required] string tag, - [FromRoute][Required] string format, - [FromRoute][Required] int? maxWidth, - [FromRoute][Required] int? maxHeight, - [FromRoute][Required] double? percentPlayed, - [FromRoute][Required] int? unplayedCount, + [FromRoute, Required] string name, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] string tag, + [FromRoute, Required] string format, + [FromRoute, Required] int? maxWidth, + [FromRoute, Required] int? maxHeight, + [FromRoute, Required] double? percentPlayed, + [FromRoute, Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -837,7 +837,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute][Required] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetStudio(name); if (item == null) @@ -899,8 +899,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetUserImage( - [FromRoute][Required] Guid userId, - [FromRoute][Required] ImageType imageType, + [FromRoute, Required] Guid userId, + [FromRoute, Required] ImageType imageType, [FromQuery] string? tag, [FromQuery] string? format, [FromQuery] int? maxWidth, @@ -915,7 +915,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute][Required] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var user = _userManager.GetUserById(userId); if (user == null) diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs index 11f2b2495..01bfbba4e 100644 --- a/Jellyfin.Api/Controllers/InstantMixController.cs +++ b/Jellyfin.Api/Controllers/InstantMixController.cs @@ -64,7 +64,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Songs/{id}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromSong( - [FromRoute][Required] Guid id, + [FromRoute, Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -101,7 +101,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Albums/{id}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromAlbum( - [FromRoute][Required] Guid id, + [FromRoute, Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -138,7 +138,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Playlists/{id}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromPlaylist( - [FromRoute][Required] Guid id, + [FromRoute, Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -211,7 +211,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Artists/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromArtists( - [FromRoute][Required] Guid id, + [FromRoute, Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -248,7 +248,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("MusicGenres/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromMusicGenres( - [FromRoute][Required] Guid id, + [FromRoute, Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -285,7 +285,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Items/{id}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromItem( - [FromRoute][Required] Guid id, + [FromRoute, Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs index f1169f3d2..f7b515cec 100644 --- a/Jellyfin.Api/Controllers/ItemLookupController.cs +++ b/Jellyfin.Api/Controllers/ItemLookupController.cs @@ -72,7 +72,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> GetExternalIdInfos([FromRoute][Required] Guid itemId) + public ActionResult> GetExternalIdInfos([FromRoute, Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -294,7 +294,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/RemoteSearch/Apply/{id}")] [Authorize(Policy = Policies.RequiresElevation)] public async Task ApplySearchCriteria( - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid itemId, [FromBody, Required] RemoteSearchResult searchResult, [FromQuery] bool replaceAllImages = true) { diff --git a/Jellyfin.Api/Controllers/ItemRefreshController.cs b/Jellyfin.Api/Controllers/ItemRefreshController.cs index 87086c681..49865eb5e 100644 --- a/Jellyfin.Api/Controllers/ItemRefreshController.cs +++ b/Jellyfin.Api/Controllers/ItemRefreshController.cs @@ -54,7 +54,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult Post( - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] MetadataRefreshMode metadataRefreshMode = MetadataRefreshMode.None, [FromQuery] MetadataRefreshMode imageRefreshMode = MetadataRefreshMode.None, [FromQuery] bool replaceAllMetadata = false, diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs index 0d064fffb..4308a434d 100644 --- a/Jellyfin.Api/Controllers/ItemUpdateController.cs +++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs @@ -68,7 +68,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/{itemId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task UpdateItem([FromRoute][Required] Guid itemId, [FromBody, Required] BaseItemDto request) + public async Task UpdateItem([FromRoute, Required] Guid itemId, [FromBody, Required] BaseItemDto request) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -141,7 +141,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Items/{itemId}/MetadataEditor")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetMetadataEditorInfo([FromRoute][Required] Guid itemId) + public ActionResult GetMetadataEditorInfo([FromRoute, Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); @@ -195,7 +195,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/{itemId}/ContentType")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UpdateItemContentType([FromRoute][Required] Guid itemId, [FromQuery, Required] string? contentType) + public ActionResult UpdateItemContentType([FromRoute, Required] Guid itemId, [FromQuery, Required] string? contentType) { var item = _libraryManager.GetItemById(itemId); if (item == null) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 170606b11..06ab176b2 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -145,7 +145,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{uId}/Items", Name = "GetItems_2")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetItems( - [FromRoute][Required] Guid? uId, + [FromRoute, Required] Guid? uId, [FromQuery] Guid? userId, [FromQuery] string? maxOfficialRating, [FromQuery] bool? hasThemeSong, @@ -530,7 +530,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/Items/Resume")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetResumeItems( - [FromRoute][Required] Guid userId, + [FromRoute, Required] Guid userId, [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery] string? searchTerm, diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index ac28fd6ca..f1f52961d 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -104,7 +104,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetFile([FromRoute][Required] Guid itemId) + public ActionResult GetFile([FromRoute, Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -144,7 +144,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult GetThemeSongs( - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] Guid? userId, [FromQuery] bool inheritFromParent = false) { @@ -210,7 +210,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult GetThemeVideos( - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] Guid? userId, [FromQuery] bool inheritFromParent = false) { @@ -275,7 +275,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetThemeMedia( - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] Guid? userId, [FromQuery] bool inheritFromParent = false) { @@ -438,7 +438,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> GetAncestors([FromRoute][Required] Guid itemId, [FromQuery] Guid? userId) + public ActionResult> GetAncestors([FromRoute, Required] Guid itemId, [FromQuery] Guid? userId) { var item = _libraryManager.GetItemById(itemId); @@ -555,7 +555,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Library/Movies/Updated")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult PostUpdatedMovies([FromRoute][Required] string? tmdbId, [FromRoute][Required] string? imdbId) + public ActionResult PostUpdatedMovies([FromRoute, Required] string? tmdbId, [FromRoute, Required] string? imdbId) { var movies = _libraryManager.GetItemList(new InternalItemsQuery { @@ -618,7 +618,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.Download)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetDownload([FromRoute][Required] Guid itemId) + public async Task GetDownload([FromRoute, Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -687,7 +687,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSimilarItems( - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] string? excludeArtistIds, [FromQuery] Guid? userId, [FromQuery] int? limit, diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index fb3f0f5f5..8678844d2 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -210,7 +210,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Channels/{channelId}")] [ProducesResponseType(StatusCodes.Status200OK)] [Authorize(Policy = Policies.DefaultAuthorization)] - public ActionResult GetChannel([FromRoute][Required] Guid channelId, [FromQuery] Guid? userId) + public ActionResult GetChannel([FromRoute, Required] Guid channelId, [FromQuery] Guid? userId) { var user = userId.HasValue && !userId.Equals(Guid.Empty) ? _userManager.GetUserById(userId.Value) @@ -407,7 +407,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Recordings/{recordingId}")] [ProducesResponseType(StatusCodes.Status200OK)] [Authorize(Policy = Policies.DefaultAuthorization)] - public ActionResult GetRecording([FromRoute][Required] Guid recordingId, [FromQuery] Guid? userId) + public ActionResult GetRecording([FromRoute, Required] Guid recordingId, [FromQuery] Guid? userId) { var user = userId.HasValue && !userId.Equals(Guid.Empty) ? _userManager.GetUserById(userId.Value) @@ -429,7 +429,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Tuners/{tunerId}/Reset")] [ProducesResponseType(StatusCodes.Status204NoContent)] [Authorize(Policy = Policies.DefaultAuthorization)] - public ActionResult ResetTuner([FromRoute][Required] string tunerId) + public ActionResult ResetTuner([FromRoute, Required] string tunerId) { AssertUserCanManageLiveTv(); _liveTvManager.ResetTuner(tunerId, CancellationToken.None); @@ -745,7 +745,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetProgram( - [FromRoute][Required] string programId, + [FromRoute, Required] string programId, [FromQuery] Guid? userId) { var user = userId.HasValue && !userId.Equals(Guid.Empty) @@ -766,7 +766,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteRecording([FromRoute][Required] Guid recordingId) + public ActionResult DeleteRecording([FromRoute, Required] Guid recordingId) { AssertUserCanManageLiveTv(); @@ -793,7 +793,7 @@ namespace Jellyfin.Api.Controllers [HttpDelete("Timers/{timerId}")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task CancelTimer([FromRoute][Required] string timerId) + public async Task CancelTimer([FromRoute, Required] string timerId) { AssertUserCanManageLiveTv(); await _liveTvManager.CancelTimer(timerId).ConfigureAwait(false); @@ -811,7 +811,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "timerId", Justification = "Imported from ServiceStack")] - public async Task UpdateTimer([FromRoute][Required] string timerId, [FromBody] TimerInfoDto timerInfo) + public async Task UpdateTimer([FromRoute, Required] string timerId, [FromBody] TimerInfoDto timerInfo) { AssertUserCanManageLiveTv(); await _liveTvManager.UpdateTimer(timerInfo, CancellationToken.None).ConfigureAwait(false); @@ -845,7 +845,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> GetSeriesTimer([FromRoute][Required] string timerId) + public async Task> GetSeriesTimer([FromRoute, Required] string timerId) { var timer = await _liveTvManager.GetSeriesTimer(timerId, CancellationToken.None).ConfigureAwait(false); if (timer == null) @@ -885,7 +885,7 @@ namespace Jellyfin.Api.Controllers [HttpDelete("SeriesTimers/{timerId}")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task CancelSeriesTimer([FromRoute][Required] string timerId) + public async Task CancelSeriesTimer([FromRoute, Required] string timerId) { AssertUserCanManageLiveTv(); await _liveTvManager.CancelSeriesTimer(timerId).ConfigureAwait(false); @@ -903,7 +903,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "timerId", Justification = "Imported from ServiceStack")] - public async Task UpdateSeriesTimer([FromRoute][Required] string timerId, [FromBody] SeriesTimerInfoDto seriesTimerInfo) + public async Task UpdateSeriesTimer([FromRoute, Required] string timerId, [FromBody] SeriesTimerInfoDto seriesTimerInfo) { AssertUserCanManageLiveTv(); await _liveTvManager.UpdateSeriesTimer(seriesTimerInfo, CancellationToken.None).ConfigureAwait(false); @@ -935,7 +935,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status404NotFound)] [Obsolete("This endpoint is obsolete.")] - public ActionResult GetRecordingGroup([FromRoute][Required] Guid? groupId) + public ActionResult GetRecordingGroup([FromRoute, Required] Guid? groupId) { return NotFound(); } @@ -1177,7 +1177,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("LiveRecordings/{recordingId}/stream")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetLiveRecordingFile([FromRoute][Required] string recordingId) + public async Task GetLiveRecordingFile([FromRoute, Required] string recordingId) { var path = _liveTvManager.GetEmbyTvActiveRecordingPath(recordingId); @@ -1207,7 +1207,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("LiveStreamFiles/{streamId}/stream.{container}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetLiveStreamFile([FromRoute][Required] string streamId, [FromRoute][Required] string container) + public async Task GetLiveStreamFile([FromRoute, Required] string streamId, [FromRoute, Required] string container) { var liveStreamInfo = await _mediaSourceManager.GetDirectStreamProviderByUniqueId(streamId, CancellationToken.None).ConfigureAwait(false); if (liveStreamInfo == null) diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs index 8bb0ace15..cc6eba4ae 100644 --- a/Jellyfin.Api/Controllers/MediaInfoController.cs +++ b/Jellyfin.Api/Controllers/MediaInfoController.cs @@ -68,7 +68,7 @@ namespace Jellyfin.Api.Controllers /// A containing a with the playback information. [HttpGet("Items/{itemId}/PlaybackInfo")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> GetPlaybackInfo([FromRoute][Required] Guid itemId, [FromQuery, Required] Guid? userId) + public async Task> GetPlaybackInfo([FromRoute, Required] Guid itemId, [FromQuery, Required] Guid? userId) { return await _mediaInfoHelper.GetPlaybackInfo( itemId, @@ -100,7 +100,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/{itemId}/PlaybackInfo")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetPostedPlaybackInfo( - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] Guid? userId, [FromQuery] long? maxStreamingBitrate, [FromQuery] long? startTimeTicks, diff --git a/Jellyfin.Api/Controllers/MusicGenresController.cs b/Jellyfin.Api/Controllers/MusicGenresController.cs index df97c0ab9..570ae8fdc 100644 --- a/Jellyfin.Api/Controllers/MusicGenresController.cs +++ b/Jellyfin.Api/Controllers/MusicGenresController.cs @@ -259,7 +259,7 @@ namespace Jellyfin.Api.Controllers /// An containing a with the music genre. [HttpGet("{genreName}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetMusicGenre([FromRoute][Required] string genreName, [FromQuery] Guid? userId) + public ActionResult GetMusicGenre([FromRoute, Required] string genreName, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions().AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/PackageController.cs b/Jellyfin.Api/Controllers/PackageController.cs index ea85e1909..7e406b418 100644 --- a/Jellyfin.Api/Controllers/PackageController.cs +++ b/Jellyfin.Api/Controllers/PackageController.cs @@ -44,7 +44,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Packages/{name}")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetPackageInfo( - [FromRoute][Required] string? name, + [FromRoute, Required] string? name, [FromQuery] string? assemblyGuid) { var packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false); @@ -84,7 +84,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [Authorize(Policy = Policies.RequiresElevation)] public async Task InstallPackage( - [FromRoute][Required] string? name, + [FromRoute, Required] string? name, [FromQuery] string? assemblyGuid, [FromQuery] string? version) { @@ -115,7 +115,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult CancelPackageInstallation( - [FromRoute][Required] Guid packageId) + [FromRoute, Required] Guid packageId) { _installationManager.CancelInstallation(packageId); return NoContent(); diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs index be5fb7f7c..8bd610dad 100644 --- a/Jellyfin.Api/Controllers/PersonsController.cs +++ b/Jellyfin.Api/Controllers/PersonsController.cs @@ -263,7 +263,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("{name}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetPerson([FromRoute][Required] string name, [FromQuery] Guid? userId) + public ActionResult GetPerson([FromRoute, Required] string name, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions() .AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs index 07bb108e3..69e0b8e07 100644 --- a/Jellyfin.Api/Controllers/PlaylistsController.cs +++ b/Jellyfin.Api/Controllers/PlaylistsController.cs @@ -84,7 +84,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task AddToPlaylist( - [FromRoute][Required] Guid playlistId, + [FromRoute, Required] Guid playlistId, [FromQuery] string? ids, [FromQuery] Guid? userId) { @@ -103,9 +103,9 @@ namespace Jellyfin.Api.Controllers [HttpPost("{playlistId}/Items/{itemId}/Move/{newIndex}")] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task MoveItem( - [FromRoute][Required] string? playlistId, - [FromRoute][Required] string? itemId, - [FromRoute][Required] int newIndex) + [FromRoute, Required] string? playlistId, + [FromRoute, Required] string? itemId, + [FromRoute, Required] int newIndex) { await _playlistManager.MoveItemAsync(playlistId, itemId, newIndex).ConfigureAwait(false); return NoContent(); @@ -120,7 +120,7 @@ namespace Jellyfin.Api.Controllers /// An on success. [HttpDelete("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task RemoveFromPlaylist([FromRoute][Required] string? playlistId, [FromQuery] string? entryIds) + public async Task RemoveFromPlaylist([FromRoute, Required] string? playlistId, [FromQuery] string? entryIds) { await _playlistManager.RemoveFromPlaylistAsync(playlistId, RequestHelpers.Split(entryIds, ',', true)).ConfigureAwait(false); return NoContent(); @@ -143,15 +143,15 @@ namespace Jellyfin.Api.Controllers /// The original playlist items. [HttpGet("{playlistId}/Items")] public ActionResult> GetPlaylistItems( - [FromRoute][Required] Guid playlistId, - [FromRoute][Required] Guid userId, - [FromRoute][Required] int? startIndex, - [FromRoute][Required] int? limit, - [FromRoute][Required] string? fields, - [FromRoute][Required] bool? enableImages, - [FromRoute][Required] bool? enableUserData, - [FromRoute][Required] int? imageTypeLimit, - [FromRoute][Required] string? enableImageTypes) + [FromRoute, Required] Guid playlistId, + [FromRoute, Required] Guid userId, + [FromRoute, Required] int? startIndex, + [FromRoute, Required] int? limit, + [FromRoute, Required] string? fields, + [FromRoute, Required] bool? enableImages, + [FromRoute, Required] bool? enableUserData, + [FromRoute, Required] int? imageTypeLimit, + [FromRoute, Required] string? enableImageTypes) { var playlist = (Playlist)_libraryManager.GetItemById(playlistId); if (playlist == null) diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 091f884d2..5c15e9a0d 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -72,8 +72,8 @@ namespace Jellyfin.Api.Controllers [HttpPost("Users/{userId}/PlayedItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult MarkPlayedItem( - [FromRoute][Required] Guid userId, - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId, [FromQuery] DateTime? datePlayed) { var user = _userManager.GetUserById(userId); @@ -97,7 +97,7 @@ namespace Jellyfin.Api.Controllers /// A containing the . [HttpDelete("Users/{userId}/PlayedItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult MarkUnplayedItem([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) + public ActionResult MarkUnplayedItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); var session = RequestHelpers.GetSession(_sessionManager, _authContext, Request); @@ -196,8 +196,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] public async Task OnPlaybackStart( - [FromRoute][Required] Guid userId, - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId, [FromQuery] string? mediaSourceId, [FromQuery] int? audioStreamIndex, [FromQuery] int? subtitleStreamIndex, @@ -246,8 +246,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] public async Task OnPlaybackProgress( - [FromRoute][Required] Guid userId, - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId, [FromQuery] string? mediaSourceId, [FromQuery] long? positionTicks, [FromQuery] int? audioStreamIndex, @@ -298,8 +298,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] public async Task OnPlaybackStopped( - [FromRoute][Required] Guid userId, - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId, [FromQuery] string? mediaSourceId, [FromQuery] string? nextMediaType, [FromQuery] long? positionTicks, diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs index d0de1a422..342b0328d 100644 --- a/Jellyfin.Api/Controllers/PluginsController.cs +++ b/Jellyfin.Api/Controllers/PluginsController.cs @@ -64,7 +64,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UninstallPlugin([FromRoute][Required] Guid pluginId) + public ActionResult UninstallPlugin([FromRoute, Required] Guid pluginId) { var plugin = _appHost.Plugins.FirstOrDefault(p => p.Id == pluginId); if (plugin == null) @@ -86,7 +86,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("{pluginId}/Configuration")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetPluginConfiguration([FromRoute][Required] Guid pluginId) + public ActionResult GetPluginConfiguration([FromRoute, Required] Guid pluginId) { if (!(_appHost.Plugins.FirstOrDefault(p => p.Id == pluginId) is IHasPluginConfiguration plugin)) { @@ -113,7 +113,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("{pluginId}/Configuration")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task UpdatePluginConfiguration([FromRoute][Required] Guid pluginId) + public async Task UpdatePluginConfiguration([FromRoute, Required] Guid pluginId) { if (!(_appHost.Plugins.FirstOrDefault(p => p.Id == pluginId) is IHasPluginConfiguration plugin)) { @@ -172,7 +172,7 @@ namespace Jellyfin.Api.Controllers [Obsolete("This endpoint should not be used.")] [HttpPost("RegistrationRecords/{name}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetRegistrationStatus([FromRoute][Required] string? name) + public ActionResult GetRegistrationStatus([FromRoute, Required] string? name) { return new MBRegistrationRecord { @@ -194,7 +194,7 @@ namespace Jellyfin.Api.Controllers [Obsolete("Paid plugins are not supported")] [HttpGet("Registrations/{name}")] [ProducesResponseType(StatusCodes.Status501NotImplemented)] - public ActionResult GetRegistration([FromRoute][Required] string? name) + public ActionResult GetRegistration([FromRoute, Required] string? name) { // TODO Once we have proper apps and plugins and decide to break compatibility with paid plugins, // delete all these registration endpoints. They are only kept for compatibility. diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs index 597d3f2f6..bdc817126 100644 --- a/Jellyfin.Api/Controllers/RemoteImageController.cs +++ b/Jellyfin.Api/Controllers/RemoteImageController.cs @@ -70,7 +70,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetRemoteImages( - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] ImageType? type, [FromQuery] int? startIndex, [FromQuery] int? limit, @@ -133,7 +133,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> GetRemoteImageProviders([FromRoute][Required] Guid itemId) + public ActionResult> GetRemoteImageProviders([FromRoute, Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -209,7 +209,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task DownloadRemoteImage( - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery, Required] ImageType type, [FromQuery] string? imageUrl) { diff --git a/Jellyfin.Api/Controllers/ScheduledTasksController.cs b/Jellyfin.Api/Controllers/ScheduledTasksController.cs index ea6288de0..3206f2734 100644 --- a/Jellyfin.Api/Controllers/ScheduledTasksController.cs +++ b/Jellyfin.Api/Controllers/ScheduledTasksController.cs @@ -94,7 +94,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Running/{taskId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult StartTask([FromRoute][Required] string? taskId) + public ActionResult StartTask([FromRoute, Required] string? taskId) { var task = _taskManager.ScheduledTasks.FirstOrDefault(o => o.Id.Equals(taskId, StringComparison.OrdinalIgnoreCase)); diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 4f0f241c6..cff7c1501 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -336,7 +336,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult AddUserToSession( [FromRoute, Required] string? sessionId, - [FromRoute][Required] Guid userId) + [FromRoute, Required] Guid userId) { _sessionManager.AddAdditionalUser(sessionId, userId); return NoContent(); @@ -353,8 +353,8 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult RemoveUserFromSession( - [FromRoute][Required] string? sessionId, - [FromRoute][Required] Guid userId) + [FromRoute, Required] string? sessionId, + [FromRoute, Required] Guid userId) { _sessionManager.RemoveAdditionalUser(sessionId, userId); return NoContent(); diff --git a/Jellyfin.Api/Controllers/StudiosController.cs b/Jellyfin.Api/Controllers/StudiosController.cs index 0208ebfbb..cdd5f958e 100644 --- a/Jellyfin.Api/Controllers/StudiosController.cs +++ b/Jellyfin.Api/Controllers/StudiosController.cs @@ -260,7 +260,7 @@ namespace Jellyfin.Api.Controllers /// An containing the studio. [HttpGet("{name}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetStudio([FromRoute][Required] string name, [FromQuery] Guid? userId) + public ActionResult GetStudio([FromRoute, Required] string name, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions().AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs index 1096a06d2..2c82b5423 100644 --- a/Jellyfin.Api/Controllers/SubtitleController.cs +++ b/Jellyfin.Api/Controllers/SubtitleController.cs @@ -86,8 +86,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult DeleteSubtitle( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] int index) + [FromRoute, Required] Guid itemId, + [FromRoute, Required] int index) { var item = _libraryManager.GetItemById(itemId); @@ -112,7 +112,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] public async Task>> SearchRemoteSubtitles( - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid itemId, [FromRoute, Required] string? language, [FromQuery] bool? isPerfectMatch) { @@ -132,7 +132,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task DownloadRemoteSubtitles( - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid itemId, [FromRoute, Required] string? subtitleId) { var video = (Video)_libraryManager.GetItemById(itemId); @@ -193,7 +193,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] long? endPositionTicks, [FromQuery] bool copyTimestamps = false, [FromQuery] bool addVttTimeMap = false, - [FromRoute][Required] long startPositionTicks = 0) + [FromRoute, Required] long startPositionTicks = 0) { if (string.Equals(format, "js", StringComparison.OrdinalIgnoreCase)) { @@ -253,9 +253,9 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")] public async Task GetSubtitlePlaylist( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] int index, - [FromRoute][Required] string? mediaSourceId, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] int index, + [FromRoute, Required] string? mediaSourceId, [FromQuery, Required] int segmentLength) { var item = (Video)_libraryManager.GetItemById(itemId); diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs index 52593b1ce..d7c81a3ab 100644 --- a/Jellyfin.Api/Controllers/SuggestionsController.cs +++ b/Jellyfin.Api/Controllers/SuggestionsController.cs @@ -54,7 +54,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/Suggestions")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSuggestions( - [FromRoute][Required] Guid userId, + [FromRoute, Required] Guid userId, [FromQuery] string? mediaType, [FromQuery] string? type, [FromQuery] int? startIndex, diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index a3678454f..f7f2d0174 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -93,8 +93,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status302Found)] public async Task GetUniversalAudioStream( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] string? container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string? container, [FromQuery] string? mediaSourceId, [FromQuery] string? deviceId, [FromQuery] Guid? userId, diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index a9cab9cde..95067bc17 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -108,7 +108,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.IgnoreParentalControl)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetUserById([FromRoute][Required] Guid userId) + public ActionResult GetUserById([FromRoute, Required] Guid userId) { var user = _userManager.GetUserById(userId); @@ -132,7 +132,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteUser([FromRoute][Required] Guid userId) + public ActionResult DeleteUser([FromRoute, Required] Guid userId) { var user = _userManager.GetUserById(userId); _sessionManager.RevokeUserTokens(user.Id, null); @@ -265,7 +265,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task UpdateUserPassword( - [FromRoute][Required] Guid userId, + [FromRoute, Required] Guid userId, [FromBody] UpdateUserPassword request) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) @@ -323,7 +323,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult UpdateUserEasyPassword( - [FromRoute][Required] Guid userId, + [FromRoute, Required] Guid userId, [FromBody] UpdateUserEasyPassword request) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) @@ -365,7 +365,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task UpdateUser( - [FromRoute][Required] Guid userId, + [FromRoute, Required] Guid userId, [FromBody] UserDto updateUser) { if (updateUser == null) @@ -409,7 +409,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult UpdateUserPolicy( - [FromRoute][Required] Guid userId, + [FromRoute, Required] Guid userId, [FromBody] UserPolicy newPolicy) { if (newPolicy == null) @@ -464,7 +464,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult UpdateUserConfiguration( - [FromRoute][Required] Guid userId, + [FromRoute, Required] Guid userId, [FromBody] UserConfiguration userConfig) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, false)) diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index f93e80393..48262f062 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -71,7 +71,7 @@ namespace Jellyfin.Api.Controllers /// An containing the d item. [HttpGet("Users/{userId}/Items/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> GetItem([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) + public async Task> GetItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -94,7 +94,7 @@ namespace Jellyfin.Api.Controllers /// An containing the user's root folder. [HttpGet("Users/{userId}/Items/Root")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetRootFolder([FromRoute][Required] Guid userId) + public ActionResult GetRootFolder([FromRoute, Required] Guid userId) { var user = _userManager.GetUserById(userId); var item = _libraryManager.GetUserRootFolder(); @@ -111,7 +111,7 @@ namespace Jellyfin.Api.Controllers /// An containing the intros to play. [HttpGet("Users/{userId}/Items/{itemId}/Intros")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task>> GetIntros([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) + public async Task>> GetIntros([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -139,7 +139,7 @@ namespace Jellyfin.Api.Controllers /// An containing the . [HttpPost("Users/{userId}/FavoriteItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult MarkFavoriteItem([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) + public ActionResult MarkFavoriteItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { return MarkFavorite(userId, itemId, true); } @@ -153,7 +153,7 @@ namespace Jellyfin.Api.Controllers /// An containing the . [HttpDelete("Users/{userId}/FavoriteItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult UnmarkFavoriteItem([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) + public ActionResult UnmarkFavoriteItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { return MarkFavorite(userId, itemId, false); } @@ -167,7 +167,7 @@ namespace Jellyfin.Api.Controllers /// An containing the . [HttpDelete("Users/{userId}/Items/{itemId}/Rating")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult DeleteUserItemRating([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) + public ActionResult DeleteUserItemRating([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { return UpdateUserItemRatingInternal(userId, itemId, null); } @@ -182,7 +182,7 @@ namespace Jellyfin.Api.Controllers /// An containing the . [HttpPost("Users/{userId}/Items/{itemId}/Rating")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult UpdateUserItemRating([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId, [FromQuery] bool? likes) + public ActionResult UpdateUserItemRating([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId, [FromQuery] bool? likes) { return UpdateUserItemRatingInternal(userId, itemId, likes); } @@ -196,7 +196,7 @@ namespace Jellyfin.Api.Controllers /// The items local trailers. [HttpGet("Users/{userId}/Items/{itemId}/LocalTrailers")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetLocalTrailers([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) + public ActionResult> GetLocalTrailers([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -231,7 +231,7 @@ namespace Jellyfin.Api.Controllers /// An containing the special features. [HttpGet("Users/{userId}/Items/{itemId}/SpecialFeatures")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetSpecialFeatures([FromRoute][Required] Guid userId, [FromRoute][Required] Guid itemId) + public ActionResult> GetSpecialFeatures([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -265,7 +265,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/Items/Latest")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetLatestMedia( - [FromRoute][Required] Guid userId, + [FromRoute, Required] Guid userId, [FromQuery] Guid? parentId, [FromQuery] string? fields, [FromQuery] string? includeItemTypes, diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs index 8582a5a21..d575bfc3b 100644 --- a/Jellyfin.Api/Controllers/UserViewsController.cs +++ b/Jellyfin.Api/Controllers/UserViewsController.cs @@ -65,7 +65,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/Views")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetUserViews( - [FromRoute][Required] Guid userId, + [FromRoute, Required] Guid userId, [FromQuery] bool? includeExternalContent, [FromQuery] string? presetViews, [FromQuery] bool includeHidden = false) @@ -127,7 +127,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/GroupingOptions")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> GetGroupingOptions([FromRoute][Required] Guid userId) + public ActionResult> GetGroupingOptions([FromRoute, Required] Guid userId) { var user = _userManager.GetUserById(userId); if (user == null) diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index cf667bf43..dabf04dee 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -163,7 +163,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Videos/{itemId}/live.m3u8")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetLiveHlsStream( - [FromRoute][Required] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 224cc7b72..5c65399cb 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -115,7 +115,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("{itemId}/AdditionalParts")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetAdditionalPart([FromRoute][Required] Guid itemId, [FromQuery] Guid? userId) + public ActionResult> GetAdditionalPart([FromRoute, Required] Guid itemId, [FromQuery] Guid? userId) { var user = userId.HasValue && !userId.Equals(Guid.Empty) ? _userManager.GetUserById(userId.Value) @@ -162,7 +162,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task DeleteAlternateSources([FromRoute][Required] Guid itemId) + public async Task DeleteAlternateSources([FromRoute, Required] Guid itemId) { var video = (Video)_libraryManager.GetItemById(itemId); @@ -331,8 +331,8 @@ namespace Jellyfin.Api.Controllers [HttpHead("{itemId}/stream", Name = "HeadVideoStream")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetVideoStream( - [FromRoute][Required] Guid itemId, - [FromRoute][Required] string? container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, diff --git a/Jellyfin.Api/Controllers/YearsController.cs b/Jellyfin.Api/Controllers/YearsController.cs index b83b09c35..4ecf0407b 100644 --- a/Jellyfin.Api/Controllers/YearsController.cs +++ b/Jellyfin.Api/Controllers/YearsController.cs @@ -180,7 +180,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("{year}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetYear([FromRoute][Required] int year, [FromQuery] Guid? userId) + public ActionResult GetYear([FromRoute, Required] int year, [FromQuery] Guid? userId) { var item = _libraryManager.GetYear(year); if (item == null) -- cgit v1.2.3 From 63ebae2f9eb015b1b7a834417c2958c81e82bbf8 Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 9 Sep 2020 14:28:30 -0600 Subject: Remove nullable from required --- Jellyfin.Api/Controllers/ApiKeyController.cs | 2 +- Jellyfin.Api/Controllers/CollectionController.cs | 4 ++-- Jellyfin.Api/Controllers/DevicesController.cs | 8 ++++---- Jellyfin.Api/Controllers/DisplayPreferencesController.cs | 4 ++-- Jellyfin.Api/Controllers/DynamicHlsController.cs | 2 +- Jellyfin.Api/Controllers/ImageController.cs | 8 ++++---- Jellyfin.Api/Controllers/ItemUpdateController.cs | 2 +- Jellyfin.Api/Controllers/MediaInfoController.cs | 4 ++-- Jellyfin.Api/Controllers/SearchController.cs | 2 +- Jellyfin.Api/Controllers/SessionController.cs | 4 ++-- Jellyfin.Api/Controllers/SystemController.cs | 2 +- Jellyfin.Api/Controllers/TvShowsController.cs | 4 ++-- Jellyfin.Api/Controllers/UserController.cs | 2 +- Jellyfin.Api/Controllers/VideosController.cs | 2 +- 14 files changed, 25 insertions(+), 25 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/ApiKeyController.cs b/Jellyfin.Api/Controllers/ApiKeyController.cs index bf973b8dd..e8d6ccdf2 100644 --- a/Jellyfin.Api/Controllers/ApiKeyController.cs +++ b/Jellyfin.Api/Controllers/ApiKeyController.cs @@ -65,7 +65,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Keys")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult CreateKey([FromQuery, Required] string? app) + public ActionResult CreateKey([FromQuery, Required] string app) { _authRepo.Create(new AuthenticationInfo { diff --git a/Jellyfin.Api/Controllers/CollectionController.cs b/Jellyfin.Api/Controllers/CollectionController.cs index f78690b06..2fc697a6a 100644 --- a/Jellyfin.Api/Controllers/CollectionController.cs +++ b/Jellyfin.Api/Controllers/CollectionController.cs @@ -88,7 +88,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("{collectionId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task AddToCollection([FromRoute, Required] Guid collectionId, [FromQuery, Required] string? itemIds) + public async Task AddToCollection([FromRoute, Required] Guid collectionId, [FromQuery, Required] string itemIds) { await _collectionManager.AddToCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(true); return NoContent(); @@ -103,7 +103,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpDelete("{collectionId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task RemoveFromCollection([FromRoute, Required] Guid collectionId, [FromQuery, Required] string? itemIds) + public async Task RemoveFromCollection([FromRoute, Required] Guid collectionId, [FromQuery, Required] string itemIds) { await _collectionManager.RemoveFromCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(false); return NoContent(); diff --git a/Jellyfin.Api/Controllers/DevicesController.cs b/Jellyfin.Api/Controllers/DevicesController.cs index 1aed20ade..74380c2ef 100644 --- a/Jellyfin.Api/Controllers/DevicesController.cs +++ b/Jellyfin.Api/Controllers/DevicesController.cs @@ -65,7 +65,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [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 == null) @@ -87,7 +87,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [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 == null) @@ -111,7 +111,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult UpdateDeviceOptions( - [FromQuery, Required] string? id, + [FromQuery, Required] string id, [FromBody, Required] DeviceOptions deviceOptions) { var existingDeviceOptions = _deviceManager.GetDeviceOptions(id); @@ -134,7 +134,7 @@ namespace Jellyfin.Api.Controllers [HttpDelete] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteDevice([FromQuery, Required] string? id) + public ActionResult DeleteDevice([FromQuery, Required] string id) { var existingDevice = _deviceManager.GetDevice(id); if (existingDevice == null) diff --git a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs index 86aa52d05..874467c75 100644 --- a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs +++ b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs @@ -45,7 +45,7 @@ namespace Jellyfin.Api.Controllers public ActionResult GetDisplayPreferences( [FromRoute, Required] string displayPreferencesId, [FromQuery, Required] Guid userId, - [FromQuery, Required] string? client) + [FromQuery, Required] string client) { var displayPreferences = _displayPreferencesManager.GetDisplayPreferences(userId, client); var itemPreferences = _displayPreferencesManager.GetItemDisplayPreferences(displayPreferences.UserId, Guid.Empty, displayPreferences.Client); @@ -99,7 +99,7 @@ namespace Jellyfin.Api.Controllers public ActionResult UpdateDisplayPreferences( [FromRoute, Required] string displayPreferencesId, [FromQuery, Required] Guid userId, - [FromQuery, Required] string? client, + [FromQuery, Required] string client, [FromBody, Required] DisplayPreferencesDto displayPreferences) { HomeSectionType[] defaults = diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 778c18560..670b41611 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -179,7 +179,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? segmentContainer, [FromQuery] int? segmentLength, [FromQuery] int? minSegments, - [FromQuery, Required] string? mediaSourceId, + [FromQuery, Required] string mediaSourceId, [FromQuery] string? deviceId, [FromQuery] string? audioCodec, [FromQuery] bool? enableAutoStreamCopy, diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index ab4bf3d88..7afec1219 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -833,10 +833,10 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] ImageType imageType, [FromRoute, Required] string tag, [FromRoute, Required] string format, - [FromRoute, Required] int? maxWidth, - [FromRoute, Required] int? maxHeight, - [FromRoute, Required] double? percentPlayed, - [FromRoute, Required] int? unplayedCount, + [FromQuery] int? maxWidth, + [FromQuery] int? maxHeight, + [FromQuery] double? percentPlayed, + [FromQuery] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs index 4308a434d..0a6ed31ae 100644 --- a/Jellyfin.Api/Controllers/ItemUpdateController.cs +++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs @@ -195,7 +195,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/{itemId}/ContentType")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UpdateItemContentType([FromRoute, Required] Guid itemId, [FromQuery, Required] string? contentType) + public ActionResult UpdateItemContentType([FromRoute, Required] Guid itemId, [FromQuery] string contentType) { var item = _libraryManager.GetItemById(itemId); if (item == null) diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs index ee171e856..a984afc96 100644 --- a/Jellyfin.Api/Controllers/MediaInfoController.cs +++ b/Jellyfin.Api/Controllers/MediaInfoController.cs @@ -69,7 +69,7 @@ namespace Jellyfin.Api.Controllers /// A containing a with the playback information. [HttpGet("Items/{itemId}/PlaybackInfo")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> GetPlaybackInfo([FromRoute, Required] Guid itemId, [FromQuery, Required] Guid? userId) + public async Task> GetPlaybackInfo([FromRoute, Required] Guid itemId, [FromQuery, Required] Guid userId) { return await _mediaInfoHelper.GetPlaybackInfo( itemId, @@ -270,7 +270,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("LiveStreams/Close")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task CloseLiveStream([FromQuery, Required] string? liveStreamId) + public async Task CloseLiveStream([FromQuery, Required] string liveStreamId) { await _mediaSourceManager.CloseLiveStream(liveStreamId).ConfigureAwait(false); return NoContent(); diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs index e159a9666..62c870cb1 100644 --- a/Jellyfin.Api/Controllers/SearchController.cs +++ b/Jellyfin.Api/Controllers/SearchController.cs @@ -81,7 +81,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery] Guid? userId, - [FromQuery, Required] string? searchTerm, + [FromQuery, Required] string searchTerm, [FromQuery] string? includeItemTypes, [FromQuery] string? excludeItemTypes, [FromQuery] string? mediaTypes, diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 9da8b3ed4..b00675d67 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -309,7 +309,7 @@ namespace Jellyfin.Api.Controllers public ActionResult SendMessageCommand( [FromRoute, Required] string sessionId, [FromQuery, Required] string text, - [FromQuery, Required] string? header, + [FromQuery] string? header, [FromQuery] long? timeoutMs) { var command = new MessageCommand @@ -375,7 +375,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult PostCapabilities( - [FromQuery, Required] string? id, + [FromQuery] string? id, [FromQuery] string? playableMediaTypes, [FromQuery] string? supportedCommands, [FromQuery] bool supportsMediaControl = false, diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs index e878e8f36..a5dc123dd 100644 --- a/Jellyfin.Api/Controllers/SystemController.cs +++ b/Jellyfin.Api/Controllers/SystemController.cs @@ -193,7 +193,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesFile(MediaTypeNames.Text.Plain)] - public ActionResult GetLogFile([FromQuery, Required] string? name) + public ActionResult GetLogFile([FromQuery, Required] string name) { var file = _fileSystem.GetFiles(_appPaths.LogDirectoryPath) .First(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase)); diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs index b9c3bd1e7..d158f6c34 100644 --- a/Jellyfin.Api/Controllers/TvShowsController.cs +++ b/Jellyfin.Api/Controllers/TvShowsController.cs @@ -69,7 +69,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("NextUp")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetNextUp( - [FromQuery, Required] Guid? userId, + [FromQuery] Guid? userId, [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery] string? fields, @@ -127,7 +127,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Upcoming")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetUpcomingEpisodes( - [FromQuery, Required] Guid? userId, + [FromQuery] Guid? userId, [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery] string? fields, diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 435588146..f3e4b1bea 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -156,7 +156,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> AuthenticateUser( [FromRoute, Required] Guid userId, - [FromQuery, Required] string? pw, + [FromQuery, Required] string pw, [FromQuery] string? password) { var user = _userManager.GetUserById(userId); diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 2608ba916..cce4cfbe3 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -203,7 +203,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public async Task MergeVersions([FromQuery, Required] string? itemIds) + public async Task MergeVersions([FromQuery, Required] string itemIds) { var items = RequestHelpers.Split(itemIds, ',', true) .Select(i => _libraryManager.GetItemById(i)) -- cgit v1.2.3 From 7576824cee0dc0d8e1729ae0a7e8e4f256b71efd Mon Sep 17 00:00:00 2001 From: cvium Date: Thu, 10 Sep 2020 14:16:41 +0200 Subject: Standardize use of IsLocal and RemoteIp --- .../HttpServer/Security/SessionContext.cs | 2 +- Jellyfin.Api/Auth/BaseAuthorizationHandler.cs | 3 +- Jellyfin.Api/Controllers/MediaInfoController.cs | 3 +- Jellyfin.Api/Controllers/SystemController.cs | 5 ++- .../Controllers/UniversalAudioController.cs | 3 +- Jellyfin.Api/Controllers/UserController.cs | 21 +++++----- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 10 ++--- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 3 +- Jellyfin.Api/Helpers/RequestHelpers.cs | 3 +- .../IpBasedAccessValidationMiddleware.cs | 4 +- .../Middleware/ResponseTimeMiddleware.cs | 3 +- .../Extensions/HttpContextExtensions.cs | 46 ++++++---------------- 12 files changed, 45 insertions(+), 61 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs index 8777c59b7..86914dea2 100644 --- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs @@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.HttpServer.Security var authorization = _authContext.GetAuthorizationInfo(requestContext); var user = authorization.User; - return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.Request.RemoteIp(), user); + return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.GetNormalizedRemoteIp(), user); } public SessionInfo GetSession(object requestContext) diff --git a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs index aa366f567..d732b6bc6 100644 --- a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs +++ b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs @@ -1,6 +1,7 @@ using System.Security.Claims; using Jellyfin.Api.Helpers; using Jellyfin.Data.Enums; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; @@ -69,7 +70,7 @@ namespace Jellyfin.Api.Auth return false; } - var ip = RequestHelpers.NormalizeIp(_httpContextAccessor.HttpContext.Connection.RemoteIpAddress).ToString(); + var ip = _httpContextAccessor.HttpContext.GetNormalizedRemoteIp(); var isInLocalNetwork = _networkManager.IsInLocalNetwork(ip); // User cannot access remotely and user is remote if (!user.HasPermission(PermissionKind.EnableRemoteAccess) && !isInLocalNetwork) diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs index cc6eba4ae..f32bdb161 100644 --- a/Jellyfin.Api/Controllers/MediaInfoController.cs +++ b/Jellyfin.Api/Controllers/MediaInfoController.cs @@ -8,6 +8,7 @@ using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.MediaInfoDtos; using Jellyfin.Api.Models.VideoDtos; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; @@ -164,7 +165,7 @@ namespace Jellyfin.Api.Controllers enableTranscoding, allowVideoStreamCopy, allowAudioStreamCopy, - Request.HttpContext.Connection.RemoteIpAddress.ToString()); + Request.HttpContext.GetNormalizedRemoteIp()); } _mediaInfoHelper.SortMediaSources(info, maxStreamingBitrate); diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs index bbfd163de..34c7bb18d 100644 --- a/Jellyfin.Api/Controllers/SystemController.cs +++ b/Jellyfin.Api/Controllers/SystemController.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Constants; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; @@ -176,8 +177,8 @@ namespace Jellyfin.Api.Controllers { return new EndPointInfo { - IsLocal = Request.HttpContext.Connection.LocalIpAddress.Equals(Request.HttpContext.Connection.RemoteIpAddress), - IsInNetwork = _network.IsInLocalNetwork(Request.HttpContext.Connection.RemoteIpAddress.ToString()) + IsLocal = HttpContext.IsLocal(), + IsInNetwork = _network.IsInLocalNetwork(HttpContext.GetNormalizedRemoteIp()) }; } diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index f7f2d0174..e3e3166b0 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.StreamingDtos; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; @@ -158,7 +159,7 @@ namespace Jellyfin.Api.Controllers true, true, true, - Request.HttpContext.Connection.RemoteIpAddress.ToString()); + Request.HttpContext.GetNormalizedRemoteIp()); } _mediaInfoHelper.SortMediaSources(info, maxStreamingBitrate); diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 95067bc17..85430e63f 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -7,6 +7,7 @@ using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.UserDtos; using Jellyfin.Data.Enums; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Configuration; @@ -117,7 +118,7 @@ namespace Jellyfin.Api.Controllers return NotFound("User not found"); } - var result = _userManager.GetUserDto(user, HttpContext.Connection.RemoteIpAddress.ToString()); + var result = _userManager.GetUserDto(user, HttpContext.GetNormalizedRemoteIp()); return result; } @@ -203,7 +204,7 @@ namespace Jellyfin.Api.Controllers DeviceName = auth.Device, Password = request.Pw, PasswordSha1 = request.Password, - RemoteEndPoint = HttpContext.Connection.RemoteIpAddress.ToString(), + RemoteEndPoint = HttpContext.GetNormalizedRemoteIp(), Username = request.Username }).ConfigureAwait(false); @@ -212,7 +213,7 @@ namespace Jellyfin.Api.Controllers catch (SecurityException e) { // rethrow adding IP address to message - throw new SecurityException($"[{HttpContext.Connection.RemoteIpAddress}] {e.Message}", e); + throw new SecurityException($"[{HttpContext.GetNormalizedRemoteIp()}] {e.Message}", e); } } @@ -246,7 +247,7 @@ namespace Jellyfin.Api.Controllers catch (SecurityException e) { // rethrow adding IP address to message - throw new SecurityException($"[{HttpContext.Connection.RemoteIpAddress}] {e.Message}", e); + throw new SecurityException($"[{HttpContext.GetNormalizedRemoteIp()}] {e.Message}", e); } } @@ -290,7 +291,7 @@ namespace Jellyfin.Api.Controllers user.Username, request.CurrentPw, request.CurrentPw, - HttpContext.Connection.RemoteIpAddress.ToString(), + HttpContext.GetNormalizedRemoteIp(), false).ConfigureAwait(false); if (success == null) @@ -496,7 +497,7 @@ namespace Jellyfin.Api.Controllers await _userManager.ChangePassword(newUser, request.Password).ConfigureAwait(false); } - var result = _userManager.GetUserDto(newUser, HttpContext.Connection.RemoteIpAddress.ToString()); + var result = _userManager.GetUserDto(newUser, HttpContext.GetNormalizedRemoteIp()); return result; } @@ -511,8 +512,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public async Task> ForgotPassword([FromBody] string? enteredUsername) { - var isLocal = HttpContext.Connection.RemoteIpAddress.Equals(HttpContext.Connection.LocalIpAddress) - || _networkManager.IsInLocalNetwork(HttpContext.Connection.RemoteIpAddress.ToString()); + var isLocal = HttpContext.IsLocal() + || _networkManager.IsInLocalNetwork(HttpContext.GetNormalizedRemoteIp()); var result = await _userManager.StartForgotPasswordProcess(enteredUsername, isLocal).ConfigureAwait(false); @@ -559,7 +560,7 @@ namespace Jellyfin.Api.Controllers if (filterByNetwork) { - if (!_networkManager.IsInLocalNetwork(HttpContext.Connection.RemoteIpAddress.ToString())) + if (!_networkManager.IsInLocalNetwork(HttpContext.GetNormalizedRemoteIp())) { users = users.Where(i => i.HasPermission(PermissionKind.EnableRemoteAccess)); } @@ -567,7 +568,7 @@ namespace Jellyfin.Api.Controllers var result = users .OrderBy(u => u.Username) - .Select(i => _userManager.GetUserDto(i, HttpContext.Connection.RemoteIpAddress.ToString())); + .Select(i => _userManager.GetUserDto(i, HttpContext.GetNormalizedRemoteIp())); return result; } diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index 6a8829d46..af0519ffa 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Models.StreamingDtos; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; @@ -198,12 +199,12 @@ namespace Jellyfin.Api.Helpers if (!string.IsNullOrWhiteSpace(subtitleGroup)) { - AddSubtitles(state, subtitleStreams, builder, _httpContextAccessor.HttpContext.Request.HttpContext.User); + AddSubtitles(state, subtitleStreams, builder, _httpContextAccessor.HttpContext.User); } AppendPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup); - if (EnableAdaptiveBitrateStreaming(state, isLiveStream, enableAdaptiveBitrateStreaming, _httpContextAccessor.HttpContext.Request.HttpContext.Connection.RemoteIpAddress)) + if (EnableAdaptiveBitrateStreaming(state, isLiveStream, enableAdaptiveBitrateStreaming, _httpContextAccessor.HttpContext.GetNormalizedRemoteIp())) { var requestedVideoBitrate = state.VideoRequest == null ? 0 : state.VideoRequest.VideoBitRate ?? 0; @@ -334,11 +335,10 @@ namespace Jellyfin.Api.Helpers } } - private bool EnableAdaptiveBitrateStreaming(StreamState state, bool isLiveStream, bool enableAdaptiveBitrateStreaming, IPAddress ipAddress) + private bool EnableAdaptiveBitrateStreaming(StreamState state, bool isLiveStream, bool enableAdaptiveBitrateStreaming, string ipAddress) { // Within the local network this will likely do more harm than good. - var ip = RequestHelpers.NormalizeIp(ipAddress).ToString(); - if (_networkManager.IsInLocalNetwork(ip)) + if (_networkManager.IsInLocalNetwork(ipAddress)) { return false; } diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index 3a736d1e8..1207fb513 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; @@ -498,7 +499,7 @@ namespace Jellyfin.Api.Helpers true, true, true, - httpRequest.HttpContext.Connection.RemoteIpAddress.ToString()); + httpRequest.HttpContext.GetNormalizedRemoteIp()); } else { diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs index fbaa69270..d15b5603e 100644 --- a/Jellyfin.Api/Helpers/RequestHelpers.cs +++ b/Jellyfin.Api/Helpers/RequestHelpers.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; using Jellyfin.Data.Enums; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Querying; @@ -119,7 +120,7 @@ namespace Jellyfin.Api.Helpers authorization.Version, authorization.DeviceId, authorization.Device, - request.HttpContext.Connection.RemoteIpAddress.ToString(), + request.HttpContext.GetNormalizedRemoteIp(), user); if (session == null) diff --git a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs index 59b5fb1ed..4bda8f273 100644 --- a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs +++ b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs @@ -32,13 +32,13 @@ namespace Jellyfin.Server.Middleware /// The async task. public async Task Invoke(HttpContext httpContext, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager) { - if (httpContext.Request.IsLocal()) + if (httpContext.IsLocal()) { await _next(httpContext).ConfigureAwait(false); return; } - var remoteIp = httpContext.Request.RemoteIp(); + var remoteIp = httpContext.GetNormalizedRemoteIp(); if (serverConfigurationManager.Configuration.EnableRemoteAccess) { diff --git a/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs b/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs index 3122d92cb..74874da1b 100644 --- a/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs +++ b/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using System.Globalization; using System.Threading.Tasks; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; @@ -69,7 +70,7 @@ namespace Jellyfin.Server.Middleware _logger.LogWarning( "Slow HTTP Response from {url} to {remoteIp} in {elapsed:g} with Status Code {statusCode}", context.Request.GetDisplayUrl(), - context.Connection.RemoteIpAddress, + context.GetNormalizedRemoteIp(), watch.Elapsed, context.Response.StatusCode); } diff --git a/MediaBrowser.Common/Extensions/HttpContextExtensions.cs b/MediaBrowser.Common/Extensions/HttpContextExtensions.cs index e0cf3f9ac..8d2908882 100644 --- a/MediaBrowser.Common/Extensions/HttpContextExtensions.cs +++ b/MediaBrowser.Common/Extensions/HttpContextExtensions.cs @@ -1,5 +1,3 @@ -using System.Net; -using MediaBrowser.Common.Net; using Microsoft.AspNetCore.Http; namespace MediaBrowser.Common.Extensions @@ -10,54 +8,32 @@ namespace MediaBrowser.Common.Extensions public static class HttpContextExtensions { /// - /// Checks the origin of the HTTP request. + /// Checks the origin of the HTTP context. /// - /// The incoming HTTP request. + /// The incoming HTTP context. /// true if the request is coming from LAN, false otherwise. - public static bool IsLocal(this HttpRequest request) + public static bool IsLocal(this HttpContext context) { - return (request.HttpContext.Connection.LocalIpAddress == null - && request.HttpContext.Connection.RemoteIpAddress == null) - || request.HttpContext.Connection.LocalIpAddress.Equals(request.HttpContext.Connection.RemoteIpAddress); + return (context.Connection.LocalIpAddress == null + && context.Connection.RemoteIpAddress == null) + || context.Connection.LocalIpAddress.Equals(context.Connection.RemoteIpAddress); } /// - /// Extracts the remote IP address of the caller of the HTTP request. + /// Extracts the remote IP address of the caller of the HTTP context. /// - /// The HTTP request. + /// The HTTP context. /// The remote caller IP address. - public static string RemoteIp(this HttpRequest request) + public static string GetNormalizedRemoteIp(this HttpContext context) { - var cachedRemoteIp = request.HttpContext.Items["RemoteIp"]?.ToString(); - if (!string.IsNullOrEmpty(cachedRemoteIp)) - { - return cachedRemoteIp; - } - - IPAddress ip; - - // "Real" remote ip might be in X-Forwarded-For of X-Real-Ip - // (if the server is behind a reverse proxy for example) - if (!IPAddress.TryParse(request.Headers[CustomHeaderNames.XForwardedFor].ToString(), out ip)) - { - if (!IPAddress.TryParse(request.Headers[CustomHeaderNames.XRealIP].ToString(), out ip)) - { - ip = request.HttpContext.Connection.RemoteIpAddress; - - // Default to the loopback address if no RemoteIpAddress is specified (i.e. during integration tests) - ip ??= IPAddress.Loopback; - } - } + var ip = context.Connection.RemoteIpAddress; if (ip.IsIPv4MappedToIPv6) { ip = ip.MapToIPv4(); } - var normalizedIp = ip.ToString(); - - request.HttpContext.Items["RemoteIp"] = normalizedIp; - return normalizedIp; + return ip.ToString(); } } } -- cgit v1.2.3 From fed58a0327efe29905376cdbdd3e51b0a9598bfe Mon Sep 17 00:00:00 2001 From: cvium Date: Thu, 10 Sep 2020 11:05:46 +0200 Subject: Add Dto to ForgotPassword --- Jellyfin.Api/Controllers/UserController.cs | 6 +++--- Jellyfin.Api/Models/UserDtos/ForgotPasswordDto.cs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 Jellyfin.Api/Models/UserDtos/ForgotPasswordDto.cs (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 630e9df6a..50bb8bb2a 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -505,17 +505,17 @@ namespace Jellyfin.Api.Controllers /// /// Initiates the forgot password process for a local user. /// - /// The entered username. + /// The forgot password request containing the entered username. /// Password reset process started. /// A containing a . [HttpPost("ForgotPassword")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> ForgotPassword([FromBody] string? enteredUsername) + public async Task> ForgotPassword([FromBody, Required] ForgotPasswordDto forgotPasswordRequest) { var isLocal = HttpContext.IsLocal() || _networkManager.IsInLocalNetwork(HttpContext.GetNormalizedRemoteIp()); - var result = await _userManager.StartForgotPasswordProcess(enteredUsername, isLocal).ConfigureAwait(false); + var result = await _userManager.StartForgotPasswordProcess(forgotPasswordRequest.EnteredUsername, isLocal).ConfigureAwait(false); return result; } diff --git a/Jellyfin.Api/Models/UserDtos/ForgotPasswordDto.cs b/Jellyfin.Api/Models/UserDtos/ForgotPasswordDto.cs new file mode 100644 index 000000000..b31c6539c --- /dev/null +++ b/Jellyfin.Api/Models/UserDtos/ForgotPasswordDto.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; + +namespace Jellyfin.Api.Models.UserDtos +{ + /// + /// Forgot Password request body DTO. + /// + public class ForgotPasswordDto + { + /// + /// Gets or sets the entered username to have its password reset. + /// + [Required] + public string? EnteredUsername { get; set; } + } +} -- cgit v1.2.3 From 72263613d039ccfb70b70bae9f53da53bc8757c4 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 29 Oct 2020 20:30:33 -0400 Subject: Convert some code in UserManager to async --- Jellyfin.Api/Controllers/ImageController.cs | 6 +++--- Jellyfin.Api/Controllers/UserController.cs | 18 +++++++----------- .../Users/UserManager.cs | 22 ++++++++++------------ MediaBrowser.Controller/Library/IUserManager.cs | 9 ++++++--- 4 files changed, 26 insertions(+), 29 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 05efe2355..4a67c1aed 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -109,7 +109,7 @@ namespace Jellyfin.Api.Controllers var userDataPath = Path.Combine(_serverConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username); if (user.ProfileImage != null) { - _userManager.ClearProfileImage(user); + await _userManager.ClearProfileImageAsync(user).ConfigureAwait(false); } user.ProfileImage = new Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType))); @@ -138,7 +138,7 @@ namespace Jellyfin.Api.Controllers [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - public ActionResult DeleteUserImage( + public async Task DeleteUserImage( [FromRoute, Required] Guid userId, [FromRoute, Required] ImageType imageType, [FromRoute] int? index = null) @@ -158,7 +158,7 @@ namespace Jellyfin.Api.Controllers _logger.LogError(e, "Error deleting user profile image:"); } - _userManager.ClearProfileImage(user); + await _userManager.ClearProfileImageAsync(user).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 50bb8bb2a..7b0897bfb 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -381,17 +381,13 @@ namespace Jellyfin.Api.Controllers var user = _userManager.GetUserById(userId); - if (string.Equals(user.Username, updateUser.Name, StringComparison.Ordinal)) - { - await _userManager.UpdateUserAsync(user).ConfigureAwait(false); - _userManager.UpdateConfiguration(user.Id, updateUser.Configuration); - } - else + if (!string.Equals(user.Username, updateUser.Name, StringComparison.Ordinal)) { await _userManager.RenameUser(user, updateUser.Name).ConfigureAwait(false); - _userManager.UpdateConfiguration(updateUser.Id, updateUser.Configuration); } + await _userManager.UpdateConfigurationAsync(user.Id, updateUser.Configuration).ConfigureAwait(false); + return NoContent(); } @@ -409,7 +405,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - public ActionResult UpdateUserPolicy( + public async Task UpdateUserPolicy( [FromRoute, Required] Guid userId, [FromBody] UserPolicy newPolicy) { @@ -447,7 +443,7 @@ namespace Jellyfin.Api.Controllers _sessionManager.RevokeUserTokens(user.Id, currentToken); } - _userManager.UpdatePolicy(userId, newPolicy); + await _userManager.UpdatePolicyAsync(userId, newPolicy).ConfigureAwait(false); return NoContent(); } @@ -464,7 +460,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - public ActionResult UpdateUserConfiguration( + public async Task UpdateUserConfiguration( [FromRoute, Required] Guid userId, [FromBody] UserConfiguration userConfig) { @@ -473,7 +469,7 @@ namespace Jellyfin.Api.Controllers return Forbid("User configuration update not allowed"); } - _userManager.UpdateConfiguration(userId, userConfig); + await _userManager.UpdateConfigurationAsync(userId, userConfig).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index b41a5ee5c..40b89ed28 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -434,9 +434,7 @@ namespace Jellyfin.Server.Implementations.Users if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy && user != null) { - UpdatePolicy(user.Id, hasNewUserPolicy.GetNewUserPolicy()); - - await UpdateUserAsync(user).ConfigureAwait(false); + await UpdatePolicyAsync(user.Id, hasNewUserPolicy.GetNewUserPolicy()).ConfigureAwait(false); } } } @@ -615,9 +613,9 @@ namespace Jellyfin.Server.Implementations.Users } /// - public void UpdateConfiguration(Guid userId, UserConfiguration config) + public async Task UpdateConfigurationAsync(Guid userId, UserConfiguration config) { - using var dbContext = _dbProvider.CreateContext(); + await using var dbContext = _dbProvider.CreateContext(); var user = dbContext.Users .Include(u => u.Permissions) .Include(u => u.Preferences) @@ -644,13 +642,13 @@ namespace Jellyfin.Server.Implementations.Users user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes); dbContext.Update(user); - dbContext.SaveChanges(); + await dbContext.SaveChangesAsync().ConfigureAwait(false); } /// - public void UpdatePolicy(Guid userId, UserPolicy policy) + public async Task UpdatePolicyAsync(Guid userId, UserPolicy policy) { - using var dbContext = _dbProvider.CreateContext(); + await using var dbContext = _dbProvider.CreateContext(); var user = dbContext.Users .Include(u => u.Permissions) .Include(u => u.Preferences) @@ -715,15 +713,15 @@ namespace Jellyfin.Server.Implementations.Users user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders); dbContext.Update(user); - dbContext.SaveChanges(); + await dbContext.SaveChangesAsync().ConfigureAwait(false); } /// - public void ClearProfileImage(User user) + public async Task ClearProfileImageAsync(User user) { - using var dbContext = _dbProvider.CreateContext(); + await using var dbContext = _dbProvider.CreateContext(); dbContext.Remove(user.ProfileImage); - dbContext.SaveChanges(); + await dbContext.SaveChangesAsync().ConfigureAwait(false); user.ProfileImage = null; } diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 6a4f5cf67..8fd3b8c34 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -158,7 +158,8 @@ namespace MediaBrowser.Controller.Library /// /// The user's Id. /// The request containing the new user configuration. - void UpdateConfiguration(Guid userId, UserConfiguration config); + /// A task representing the update. + Task UpdateConfigurationAsync(Guid userId, UserConfiguration config); /// /// This method updates the user's policy. @@ -167,12 +168,14 @@ namespace MediaBrowser.Controller.Library /// /// The user's Id. /// The request containing the new user policy. - void UpdatePolicy(Guid userId, UserPolicy policy); + /// A task representing the update. + Task UpdatePolicyAsync(Guid userId, UserPolicy policy); /// /// Clears the user's profile image. /// /// The user. - void ClearProfileImage(User user); + /// A task representing the clearing of the profile image. + Task ClearProfileImageAsync(User user); } } -- cgit v1.2.3 From 3756611d0155b77d1a17be63e71a3181904d4bc3 Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 5 Nov 2020 13:23:22 -0700 Subject: Add /Users/Me endpoint --- Jellyfin.Api/Controllers/UserController.cs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 7b0897bfb..0f7c25d0e 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -530,6 +530,33 @@ namespace Jellyfin.Api.Controllers return result; } + /// + /// Gets the user based on auth token. + /// + /// User returned. + /// Token is not owned by a user. + /// A for the authenticated user. + [HttpGet("Me")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public ActionResult GetCurrentUser() + { + var userId = ClaimHelpers.GetUserId(Request.HttpContext.User); + if (userId == null) + { + return BadRequest(); + } + + var user = _userManager.GetUserById(userId.Value); + if (user == null) + { + return BadRequest(); + } + + return _userManager.GetUserDto(user); + } + private IEnumerable Get(bool? isHidden, bool? isDisabled, bool filterByDevice, bool filterByNetwork) { var users = _userManager.Users; -- cgit v1.2.3 From f5973d57e8c070692450f0e04e01615c78c954d9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 21 Nov 2020 14:26:03 +0100 Subject: Remove UTF8 bom from some files --- Jellyfin.Api/Controllers/ApiKeyController.cs | 2 +- Jellyfin.Api/Controllers/ArtistsController.cs | 2 +- Jellyfin.Api/Controllers/BrandingController.cs | 2 +- Jellyfin.Api/Controllers/CollectionController.cs | 2 +- Jellyfin.Api/Controllers/DashboardController.cs | 2 +- Jellyfin.Api/Controllers/FilterController.cs | 2 +- Jellyfin.Api/Controllers/GenresController.cs | 2 +- Jellyfin.Api/Controllers/HlsSegmentController.cs | 2 +- Jellyfin.Api/Controllers/ImageController.cs | 2 +- Jellyfin.Api/Controllers/InstantMixController.cs | 2 +- Jellyfin.Api/Controllers/ItemLookupController.cs | 2 +- Jellyfin.Api/Controllers/ItemUpdateController.cs | 2 +- Jellyfin.Api/Controllers/LocalizationController.cs | 2 +- Jellyfin.Api/Controllers/MediaInfoController.cs | 2 +- Jellyfin.Api/Controllers/MoviesController.cs | 2 +- Jellyfin.Api/Controllers/MusicGenresController.cs | 2 +- Jellyfin.Api/Controllers/PersonsController.cs | 2 +- Jellyfin.Api/Controllers/PlaylistsController.cs | 2 +- Jellyfin.Api/Controllers/PlaystateController.cs | 2 +- Jellyfin.Api/Controllers/PluginsController.cs | 2 +- Jellyfin.Api/Controllers/StudiosController.cs | 2 +- Jellyfin.Api/Controllers/SuggestionsController.cs | 2 +- Jellyfin.Api/Controllers/SyncPlayController.cs | 2 +- Jellyfin.Api/Controllers/SystemController.cs | 2 +- Jellyfin.Api/Controllers/TimeSyncController.cs | 2 +- Jellyfin.Api/Controllers/UniversalAudioController.cs | 2 +- Jellyfin.Api/Controllers/UserController.cs | 2 +- Jellyfin.Api/Controllers/UserViewsController.cs | 2 +- Jellyfin.Api/Controllers/YearsController.cs | 2 +- 29 files changed, 29 insertions(+), 29 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/ApiKeyController.cs b/Jellyfin.Api/Controllers/ApiKeyController.cs index e8d6ccdf2..8c43d786a 100644 --- a/Jellyfin.Api/Controllers/ApiKeyController.cs +++ b/Jellyfin.Api/Controllers/ApiKeyController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using System.Globalization; using Jellyfin.Api.Constants; diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs index f684c649a..c65dc8620 100644 --- a/Jellyfin.Api/Controllers/ArtistsController.cs +++ b/Jellyfin.Api/Controllers/ArtistsController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using System.Linq; using Jellyfin.Api.Constants; diff --git a/Jellyfin.Api/Controllers/BrandingController.cs b/Jellyfin.Api/Controllers/BrandingController.cs index 1d4836f27..d3ea41201 100644 --- a/Jellyfin.Api/Controllers/BrandingController.cs +++ b/Jellyfin.Api/Controllers/BrandingController.cs @@ -1,4 +1,4 @@ -using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Branding; using Microsoft.AspNetCore.Http; diff --git a/Jellyfin.Api/Controllers/CollectionController.cs b/Jellyfin.Api/Controllers/CollectionController.cs index 2a342c2cb..2a7b2b5c6 100644 --- a/Jellyfin.Api/Controllers/CollectionController.cs +++ b/Jellyfin.Api/Controllers/CollectionController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Jellyfin.Api.Constants; diff --git a/Jellyfin.Api/Controllers/DashboardController.cs b/Jellyfin.Api/Controllers/DashboardController.cs index a859ac114..ccc81dfc5 100644 --- a/Jellyfin.Api/Controllers/DashboardController.cs +++ b/Jellyfin.Api/Controllers/DashboardController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs index c97a1ed14..31cb9e273 100644 --- a/Jellyfin.Api/Controllers/FilterController.cs +++ b/Jellyfin.Api/Controllers/FilterController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Jellyfin.Api.Constants; using Jellyfin.Api.ModelBinders; diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs index 2dd504770..d2b41e0a8 100644 --- a/Jellyfin.Api/Controllers/GenresController.cs +++ b/Jellyfin.Api/Controllers/GenresController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using System.Linq; using Jellyfin.Api.Constants; diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs index ccdbbb297..f51987732 100644 --- a/Jellyfin.Api/Controllers/HlsSegmentController.cs +++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.IO; diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 366f70163..f48c1df72 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs index d17a26db4..6913afd0f 100644 --- a/Jellyfin.Api/Controllers/InstantMixController.cs +++ b/Jellyfin.Api/Controllers/InstantMixController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs index a7c1a6388..6c38f77ce 100644 --- a/Jellyfin.Api/Controllers/ItemLookupController.cs +++ b/Jellyfin.Api/Controllers/ItemLookupController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs index 0a6ed31ae..9e1a39853 100644 --- a/Jellyfin.Api/Controllers/ItemUpdateController.cs +++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; diff --git a/Jellyfin.Api/Controllers/LocalizationController.cs b/Jellyfin.Api/Controllers/LocalizationController.cs index ef2e7e8b1..3d8b9e0ca 100644 --- a/Jellyfin.Api/Controllers/LocalizationController.cs +++ b/Jellyfin.Api/Controllers/LocalizationController.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Jellyfin.Api.Constants; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs index 186024585..b42e6686e 100644 --- a/Jellyfin.Api/Controllers/MediaInfoController.cs +++ b/Jellyfin.Api/Controllers/MediaInfoController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Buffers; using System.ComponentModel.DataAnnotations; using System.Linq; diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs index ebc148fe5..75dfd4e68 100644 --- a/Jellyfin.Api/Controllers/MoviesController.cs +++ b/Jellyfin.Api/Controllers/MoviesController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; diff --git a/Jellyfin.Api/Controllers/MusicGenresController.cs b/Jellyfin.Api/Controllers/MusicGenresController.cs index 8c6104302..e7d0a61c5 100644 --- a/Jellyfin.Api/Controllers/MusicGenresController.cs +++ b/Jellyfin.Api/Controllers/MusicGenresController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using System.Linq; using Jellyfin.Api.Constants; diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs index 9dc79b388..aaad36551 100644 --- a/Jellyfin.Api/Controllers/PersonsController.cs +++ b/Jellyfin.Api/Controllers/PersonsController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using System.Linq; using Jellyfin.Api.Constants; diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs index bc47ecbd1..3e55434c0 100644 --- a/Jellyfin.Api/Controllers/PlaylistsController.cs +++ b/Jellyfin.Api/Controllers/PlaylistsController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 5c15e9a0d..7a60b0deb 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs index 0f8ceba29..98f1bc2d2 100644 --- a/Jellyfin.Api/Controllers/PluginsController.cs +++ b/Jellyfin.Api/Controllers/PluginsController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; diff --git a/Jellyfin.Api/Controllers/StudiosController.cs b/Jellyfin.Api/Controllers/StudiosController.cs index af28b4f59..5090bf1de 100644 --- a/Jellyfin.Api/Controllers/StudiosController.cs +++ b/Jellyfin.Api/Controllers/StudiosController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs index 69292186e..9f1dec712 100644 --- a/Jellyfin.Api/Controllers/SuggestionsController.cs +++ b/Jellyfin.Api/Controllers/SuggestionsController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using System.Linq; using Jellyfin.Api.Constants; diff --git a/Jellyfin.Api/Controllers/SyncPlayController.cs b/Jellyfin.Api/Controllers/SyncPlayController.cs index e16a10ba4..346431e60 100644 --- a/Jellyfin.Api/Controllers/SyncPlayController.cs +++ b/Jellyfin.Api/Controllers/SyncPlayController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Threading; diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs index 4cb1984a2..92875d735 100644 --- a/Jellyfin.Api/Controllers/SystemController.cs +++ b/Jellyfin.Api/Controllers/SystemController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; diff --git a/Jellyfin.Api/Controllers/TimeSyncController.cs b/Jellyfin.Api/Controllers/TimeSyncController.cs index 2dc744e7c..27c7186fc 100644 --- a/Jellyfin.Api/Controllers/TimeSyncController.cs +++ b/Jellyfin.Api/Controllers/TimeSyncController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using MediaBrowser.Model.SyncPlay; using Microsoft.AspNetCore.Http; diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 255532307..34c9f32fa 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Globalization; diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 0f7c25d0e..9805b84b1 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs index 60fd1df01..e1483ce9d 100644 --- a/Jellyfin.Api/Controllers/UserViewsController.cs +++ b/Jellyfin.Api/Controllers/UserViewsController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Globalization; diff --git a/Jellyfin.Api/Controllers/YearsController.cs b/Jellyfin.Api/Controllers/YearsController.cs index 9c3ecb4ce..ec7c3de97 100644 --- a/Jellyfin.Api/Controllers/YearsController.cs +++ b/Jellyfin.Api/Controllers/YearsController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; -- cgit v1.2.3 From 391870ebae9085335cb404154e03ad17b0b25d3d Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sun, 6 Dec 2020 20:05:55 -0500 Subject: Add required attributes to parameters --- Jellyfin.Api/Controllers/UserController.cs | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 0f7c25d0e..ff047c68a 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -267,7 +267,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task UpdateUserPassword( [FromRoute, Required] Guid userId, - [FromBody] UpdateUserPassword request) + [FromBody, Required] UpdateUserPassword request) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) { @@ -325,7 +325,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult UpdateUserEasyPassword( [FromRoute, Required] Guid userId, - [FromBody] UpdateUserEasyPassword request) + [FromBody, Required] UpdateUserEasyPassword request) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) { @@ -367,13 +367,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task UpdateUser( [FromRoute, Required] Guid userId, - [FromBody] UserDto updateUser) + [FromBody, Required] UserDto updateUser) { - if (updateUser == null) - { - return BadRequest(); - } - if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, false)) { return Forbid("User update not allowed."); @@ -407,13 +402,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task UpdateUserPolicy( [FromRoute, Required] Guid userId, - [FromBody] UserPolicy newPolicy) + [FromBody, Required] UserPolicy newPolicy) { - if (newPolicy == null) - { - return BadRequest(); - } - var user = _userManager.GetUserById(userId); // If removing admin access @@ -462,7 +452,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task UpdateUserConfiguration( [FromRoute, Required] Guid userId, - [FromBody] UserConfiguration userConfig) + [FromBody, Required] UserConfiguration userConfig) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, false)) { @@ -483,7 +473,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("New")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> CreateUserByName([FromBody] CreateUserByName request) + public async Task> CreateUserByName([FromBody, Required] CreateUserByName request) { var newUser = await _userManager.CreateUserAsync(request.Name).ConfigureAwait(false); -- cgit v1.2.3 From 5150598c6d3d0ddb4435d73d5bea80d4b1fd2769 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 7 Dec 2020 08:55:42 -0700 Subject: Fix API forbidden response --- Jellyfin.Api/Controllers/ImageController.cs | 8 ++++---- Jellyfin.Api/Controllers/QuickConnectController.cs | 4 ++-- Jellyfin.Api/Controllers/UserController.cs | 18 +++++++++--------- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 65de81d7a..e828a0801 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -98,7 +98,7 @@ namespace Jellyfin.Api.Controllers { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) { - return Forbid("User is not allowed to update the image."); + return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image."); } var user = _userManager.GetUserById(userId); @@ -144,7 +144,7 @@ namespace Jellyfin.Api.Controllers { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) { - return Forbid("User is not allowed to update the image."); + return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image."); } var user = _userManager.GetUserById(userId); @@ -190,7 +190,7 @@ namespace Jellyfin.Api.Controllers { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) { - return Forbid("User is not allowed to delete the image."); + return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to delete the image."); } var user = _userManager.GetUserById(userId); @@ -229,7 +229,7 @@ namespace Jellyfin.Api.Controllers { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) { - return Forbid("User is not allowed to delete the image."); + return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to delete the image."); } var user = _userManager.GetUserById(userId); diff --git a/Jellyfin.Api/Controllers/QuickConnectController.cs b/Jellyfin.Api/Controllers/QuickConnectController.cs index 73da2f906..4ac849181 100644 --- a/Jellyfin.Api/Controllers/QuickConnectController.cs +++ b/Jellyfin.Api/Controllers/QuickConnectController.cs @@ -88,7 +88,7 @@ namespace Jellyfin.Api.Controllers { if (_quickConnect.State == QuickConnectState.Unavailable) { - return Forbid("Quick connect is unavailable"); + return StatusCode(StatusCodes.Status403Forbidden, "Quick connect is unavailable"); } _quickConnect.Activate(); @@ -126,7 +126,7 @@ namespace Jellyfin.Api.Controllers var userId = ClaimHelpers.GetUserId(Request.HttpContext.User); if (!userId.HasValue) { - return Forbid("Unknown user id"); + return StatusCode(StatusCodes.Status403Forbidden, "Unknown user id"); } return _quickConnect.AuthorizeRequest(userId.Value, code); diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 9805b84b1..4cf8a9095 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -169,7 +169,7 @@ namespace Jellyfin.Api.Controllers if (!string.IsNullOrEmpty(password) && string.IsNullOrEmpty(pw)) { - return Forbid("Only sha1 password is not allowed."); + return StatusCode(StatusCodes.Status403Forbidden, "Only sha1 password is not allowed."); } // Password should always be null @@ -271,7 +271,7 @@ namespace Jellyfin.Api.Controllers { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) { - return Forbid("User is not allowed to update the password."); + return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the password."); } var user = _userManager.GetUserById(userId); @@ -296,7 +296,7 @@ namespace Jellyfin.Api.Controllers if (success == null) { - return Forbid("Invalid user or password entered."); + return StatusCode(StatusCodes.Status403Forbidden, "Invalid user or password entered."); } await _userManager.ChangePassword(user, request.NewPw).ConfigureAwait(false); @@ -329,7 +329,7 @@ namespace Jellyfin.Api.Controllers { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) { - return Forbid("User is not allowed to update the easy password."); + return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the easy password."); } var user = _userManager.GetUserById(userId); @@ -376,7 +376,7 @@ namespace Jellyfin.Api.Controllers if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, false)) { - return Forbid("User update not allowed."); + return StatusCode(StatusCodes.Status403Forbidden, "User update not allowed."); } var user = _userManager.GetUserById(userId); @@ -421,14 +421,14 @@ namespace Jellyfin.Api.Controllers { if (_userManager.Users.Count(i => i.HasPermission(PermissionKind.IsAdministrator)) == 1) { - return Forbid("There must be at least one user in the system with administrative access."); + return StatusCode(StatusCodes.Status403Forbidden, "There must be at least one user in the system with administrative access."); } } // If disabling if (newPolicy.IsDisabled && user.HasPermission(PermissionKind.IsAdministrator)) { - return Forbid("Administrators cannot be disabled."); + return StatusCode(StatusCodes.Status403Forbidden, "Administrators cannot be disabled."); } // If disabling @@ -436,7 +436,7 @@ namespace Jellyfin.Api.Controllers { if (_userManager.Users.Count(i => !i.HasPermission(PermissionKind.IsDisabled)) == 1) { - return Forbid("There must be at least one enabled user in the system."); + return StatusCode(StatusCodes.Status403Forbidden, "There must be at least one enabled user in the system."); } var currentToken = _authContext.GetAuthorizationInfo(Request).Token; @@ -466,7 +466,7 @@ namespace Jellyfin.Api.Controllers { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, false)) { - return Forbid("User configuration update not allowed"); + return StatusCode(StatusCodes.Status403Forbidden, "User configuration update not allowed"); } await _userManager.UpdateConfigurationAsync(userId, userConfig).ConfigureAwait(false); -- cgit v1.2.3 From 76f61eb0d6941782c3d3187e16a22333c8b13b4c Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 11 Dec 2020 10:15:43 -0500 Subject: Convert DeleteUser to async --- Jellyfin.Api/Controllers/UserController.cs | 4 ++-- Jellyfin.Server.Implementations/Users/UserManager.cs | 8 ++++---- MediaBrowser.Controller/Library/IUserManager.cs | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 0f7c25d0e..d61417480 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -133,11 +133,11 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteUser([FromRoute, Required] Guid userId) + public async Task DeleteUser([FromRoute, Required] Guid userId) { var user = _userManager.GetUserById(userId); _sessionManager.RevokeUserTokens(user.Id, null); - _userManager.DeleteUser(userId); + await _userManager.DeleteUserAsync(userId).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index f684d151d..e37fcc908 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -220,7 +220,7 @@ namespace Jellyfin.Server.Implementations.Users } /// - public void DeleteUser(Guid userId) + public async Task DeleteUserAsync(Guid userId) { if (!_users.TryGetValue(userId, out var user)) { @@ -246,7 +246,7 @@ namespace Jellyfin.Server.Implementations.Users nameof(userId)); } - using var dbContext = _dbProvider.CreateContext(); + await using var dbContext = _dbProvider.CreateContext(); // Clear all entities related to the user from the database. if (user.ProfileImage != null) @@ -258,10 +258,10 @@ namespace Jellyfin.Server.Implementations.Users dbContext.RemoveRange(user.Preferences); dbContext.RemoveRange(user.AccessSchedules); dbContext.Users.Remove(user); - dbContext.SaveChanges(); + await dbContext.SaveChangesAsync().ConfigureAwait(false); _users.Remove(userId); - _eventManager.Publish(new UserDeletedEventArgs(user)); + await _eventManager.PublishAsync(new UserDeletedEventArgs(user)).ConfigureAwait(false); } /// diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 8fd3b8c34..6e267834b 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -93,7 +93,8 @@ namespace MediaBrowser.Controller.Library /// Deletes the specified user. /// /// The id of the user to be deleted. - void DeleteUser(Guid userId); + /// A task representing the deletion of the user. + Task DeleteUserAsync(Guid userId); /// /// Resets the password. -- cgit v1.2.3 From 77b417e41e76dadfacbff5605c81eeb5ce2d5d2d Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 24 Jan 2021 15:02:56 -0700 Subject: Mark non-nullable body as null --- Jellyfin.Api/Controllers/PackageController.cs | 2 +- Jellyfin.Api/Controllers/UserController.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/PackageController.cs b/Jellyfin.Api/Controllers/PackageController.cs index 9ab8e0bdc..c589f54ac 100644 --- a/Jellyfin.Api/Controllers/PackageController.cs +++ b/Jellyfin.Api/Controllers/PackageController.cs @@ -158,7 +158,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Repositories")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult SetRepositories([FromBody] List repositoryInfos) + public ActionResult SetRepositories([FromBody, Required] List repositoryInfos) { _serverConfigurationManager.Configuration.PluginRepositories = repositoryInfos; _serverConfigurationManager.SaveConfiguration(); diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 0f0bee4bc..87a4ffd92 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -514,7 +514,7 @@ namespace Jellyfin.Api.Controllers /// A containing a . [HttpPost("ForgotPassword/Pin")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> ForgotPasswordPin([FromBody] string? pin) + public async Task> ForgotPasswordPin([FromBody, Required] string pin) { var result = await _userManager.RedeemPasswordResetPin(pin).ConfigureAwait(false); return result; -- cgit v1.2.3 From eb0ff0c37077cc70def96713b257204179c15f80 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 6 Feb 2021 17:18:37 +0100 Subject: Fix forgot password pin request --- Jellyfin.Api/Controllers/UserController.cs | 6 +++--- Jellyfin.Api/Models/UserDtos/ForgotPasswordPinDto.cs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 Jellyfin.Api/Models/UserDtos/ForgotPasswordPinDto.cs (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 87a4ffd92..17c28779a 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -509,14 +509,14 @@ namespace Jellyfin.Api.Controllers /// /// Redeems a forgot password pin. /// - /// The pin. + /// The forgot password pin request containing the endered pin. /// Pin reset process started. /// A containing a . [HttpPost("ForgotPassword/Pin")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> ForgotPasswordPin([FromBody, Required] string pin) + public async Task> ForgotPasswordPin([FromBody, Required] ForgotPasswordPinDto forgotPasswordPinRequest) { - var result = await _userManager.RedeemPasswordResetPin(pin).ConfigureAwait(false); + var result = await _userManager.RedeemPasswordResetPin(forgotPasswordPinRequest.Pin).ConfigureAwait(false); return result; } diff --git a/Jellyfin.Api/Models/UserDtos/ForgotPasswordPinDto.cs b/Jellyfin.Api/Models/UserDtos/ForgotPasswordPinDto.cs new file mode 100644 index 000000000..62780e23c --- /dev/null +++ b/Jellyfin.Api/Models/UserDtos/ForgotPasswordPinDto.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; + +namespace Jellyfin.Api.Models.UserDtos +{ + /// + /// Forgot Password Pin enter request body DTO. + /// + public class ForgotPasswordPinDto + { + /// + /// Gets or sets the entered pin to have the password reset. + /// + [Required] + public string? Pin { get; set; } + } +} -- cgit v1.2.3 From 07f1a2c2dcad7890f19591ccd14d43cbc0e51e95 Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Sat, 6 Feb 2021 18:36:18 +0100 Subject: Update Jellyfin.Api/Controllers/UserController.cs Co-authored-by: Cody Robibero --- Jellyfin.Api/Controllers/UserController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 17c28779a..43ee309b7 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -509,7 +509,7 @@ namespace Jellyfin.Api.Controllers /// /// Redeems a forgot password pin. /// - /// The forgot password pin request containing the endered pin. + /// The forgot password pin request containing the entered pin. /// Pin reset process started. /// A containing a . [HttpPost("ForgotPassword/Pin")] -- cgit v1.2.3 From 19e4ef82dda1cbba9d533047950afb42c425749a Mon Sep 17 00:00:00 2001 From: David Date: Tue, 23 Mar 2021 17:16:10 +0100 Subject: Remove conversion from IPAddress to string to IPAddress --- .../HttpServer/Security/SessionContext.cs | 2 +- Jellyfin.Api/Controllers/UserController.cs | 25 ++++++++++++++++------ Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 2 +- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 5 +++-- Jellyfin.Api/Helpers/RequestHelpers.cs | 2 +- .../Extensions/HttpContextExtensions.cs | 4 ++-- .../LocalAccessPolicy/LocalAccessHandlerTests.cs | 3 ++- 7 files changed, 28 insertions(+), 15 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs index 040b6b9e4..dd77b45d8 100644 --- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs @@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.HttpServer.Security var authorization = _authContext.GetAuthorizationInfo(requestContext); var user = authorization.User; - return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.GetNormalizedRemoteIp(), user); + return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.GetNormalizedRemoteIp().ToString(), user); } public SessionInfo GetSession(object requestContext) diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 43ee309b7..3c0d2aca1 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -21,6 +21,7 @@ using MediaBrowser.Model.Users; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; namespace Jellyfin.Api.Controllers { @@ -36,6 +37,7 @@ namespace Jellyfin.Api.Controllers private readonly IDeviceManager _deviceManager; private readonly IAuthorizationContext _authContext; private readonly IServerConfigurationManager _config; + private readonly ILogger _logger; /// /// Initializes a new instance of the class. @@ -46,13 +48,15 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public UserController( IUserManager userManager, ISessionManager sessionManager, INetworkManager networkManager, IDeviceManager deviceManager, IAuthorizationContext authContext, - IServerConfigurationManager config) + IServerConfigurationManager config, + ILogger logger) { _userManager = userManager; _sessionManager = sessionManager; @@ -60,6 +64,7 @@ namespace Jellyfin.Api.Controllers _deviceManager = deviceManager; _authContext = authContext; _config = config; + _logger = logger; } /// @@ -118,7 +123,7 @@ namespace Jellyfin.Api.Controllers return NotFound("User not found"); } - var result = _userManager.GetUserDto(user, HttpContext.GetNormalizedRemoteIp()); + var result = _userManager.GetUserDto(user, HttpContext.GetNormalizedRemoteIp().ToString()); return result; } @@ -204,7 +209,7 @@ namespace Jellyfin.Api.Controllers DeviceName = auth.Device, Password = request.Pw, PasswordSha1 = request.Password, - RemoteEndPoint = HttpContext.GetNormalizedRemoteIp(), + RemoteEndPoint = HttpContext.GetNormalizedRemoteIp().ToString(), Username = request.Username }).ConfigureAwait(false); @@ -291,7 +296,7 @@ namespace Jellyfin.Api.Controllers user.Username, request.CurrentPw, request.CurrentPw, - HttpContext.GetNormalizedRemoteIp(), + HttpContext.GetNormalizedRemoteIp().ToString(), false).ConfigureAwait(false); if (success == null) @@ -483,7 +488,7 @@ namespace Jellyfin.Api.Controllers await _userManager.ChangePassword(newUser, request.Password).ConfigureAwait(false); } - var result = _userManager.GetUserDto(newUser, HttpContext.GetNormalizedRemoteIp()); + var result = _userManager.GetUserDto(newUser, HttpContext.GetNormalizedRemoteIp().ToString()); return result; } @@ -498,8 +503,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public async Task> ForgotPassword([FromBody, Required] ForgotPasswordDto forgotPasswordRequest) { + var ip = HttpContext.GetNormalizedRemoteIp(); var isLocal = HttpContext.IsLocal() - || _networkManager.IsInLocalNetwork(HttpContext.GetNormalizedRemoteIp()); + || _networkManager.IsInLocalNetwork(ip); + + if (isLocal) + { + _logger.LogWarning("Password reset proccess initiated from outside the local network with IP: {IP}", ip); + } var result = await _userManager.StartForgotPasswordProcess(forgotPasswordRequest.EnteredUsername, isLocal).ConfigureAwait(false); @@ -581,7 +592,7 @@ namespace Jellyfin.Api.Controllers var result = users .OrderBy(u => u.Username) - .Select(i => _userManager.GetUserDto(i, HttpContext.GetNormalizedRemoteIp())); + .Select(i => _userManager.GetUserDto(i, HttpContext.GetNormalizedRemoteIp().ToString())); return result; } diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index 16380f0bb..751b48682 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -434,7 +434,7 @@ namespace Jellyfin.Api.Helpers } } - private bool EnableAdaptiveBitrateStreaming(StreamState state, bool isLiveStream, bool enableAdaptiveBitrateStreaming, string ipAddress) + private bool EnableAdaptiveBitrateStreaming(StreamState state, bool isLiveStream, bool enableAdaptiveBitrateStreaming, IPAddress ipAddress) { // Within the local network this will likely do more harm than good. if (_networkManager.IsInLocalNetwork(ipAddress)) diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index ce6740fc9..a2d0e7030 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using System.Linq; +using System.Net; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -179,7 +180,7 @@ namespace Jellyfin.Api.Helpers bool enableTranscoding, bool allowVideoStreamCopy, bool allowAudioStreamCopy, - string ipAddress) + IPAddress ipAddress) { var streamBuilder = new StreamBuilder(_mediaEncoder, _logger); @@ -551,7 +552,7 @@ namespace Jellyfin.Api.Helpers } } - private int? GetMaxBitrate(int? clientMaxBitrate, User user, string ipAddress) + private int? GetMaxBitrate(int? clientMaxBitrate, User user, IPAddress ipAddress) { var maxBitrate = clientMaxBitrate; var remoteClientMaxBitrate = user.RemoteClientBitrateLimit ?? 0; diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs index 94856e03e..56585aeab 100644 --- a/Jellyfin.Api/Helpers/RequestHelpers.cs +++ b/Jellyfin.Api/Helpers/RequestHelpers.cs @@ -84,7 +84,7 @@ namespace Jellyfin.Api.Helpers authorization.Version, authorization.DeviceId, authorization.Device, - request.HttpContext.GetNormalizedRemoteIp(), + request.HttpContext.GetNormalizedRemoteIp().ToString(), user); if (session == null) diff --git a/MediaBrowser.Common/Extensions/HttpContextExtensions.cs b/MediaBrowser.Common/Extensions/HttpContextExtensions.cs index 19fa95480..e51ad42d1 100644 --- a/MediaBrowser.Common/Extensions/HttpContextExtensions.cs +++ b/MediaBrowser.Common/Extensions/HttpContextExtensions.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Common.Extensions /// /// The HTTP context. /// The remote caller IP address. - public static string GetNormalizedRemoteIp(this HttpContext context) + public static IPAddress GetNormalizedRemoteIp(this HttpContext context) { // Default to the loopback address if no RemoteIpAddress is specified (i.e. during integration tests) var ip = context.Connection.RemoteIpAddress ?? IPAddress.Loopback; @@ -35,7 +35,7 @@ namespace MediaBrowser.Common.Extensions ip = ip.MapToIPv4(); } - return ip.ToString(); + return ip; } } } diff --git a/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs index 09ffa8468..5b3d784ff 100644 --- a/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Net; using System.Threading.Tasks; using AutoFixture; using AutoFixture.AutoMoq; @@ -41,7 +42,7 @@ namespace Jellyfin.Api.Tests.Auth.LocalAccessPolicy public async Task LocalAccessOnly(bool isInLocalNetwork, bool shouldSucceed) { _networkManagerMock - .Setup(n => n.IsInLocalNetwork(It.IsAny())) + .Setup(n => n.IsInLocalNetwork(It.IsAny())) .Returns(isInLocalNetwork); TestHelpers.SetupConfigurationManager(_configurationManagerMock, true); -- cgit v1.2.3 From 856819e58f79bdfce3433a9080e673187a4a77c1 Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 22 Apr 2021 06:49:42 -0600 Subject: Don't use obsolete Password property --- Jellyfin.Api/Controllers/UserController.cs | 3 --- 1 file changed, 3 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserController.cs') diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 3c0d2aca1..b13db4baa 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -177,11 +177,9 @@ namespace Jellyfin.Api.Controllers return StatusCode(StatusCodes.Status403Forbidden, "Only sha1 password is not allowed."); } - // Password should always be null AuthenticateUserByName request = new AuthenticateUserByName { Username = user.Username, - Password = null, Pw = pw }; return await AuthenticateUserByName(request).ConfigureAwait(false); @@ -208,7 +206,6 @@ namespace Jellyfin.Api.Controllers DeviceId = auth.DeviceId, DeviceName = auth.Device, Password = request.Pw, - PasswordSha1 = request.Password, RemoteEndPoint = HttpContext.GetNormalizedRemoteIp().ToString(), Username = request.Username }).ConfigureAwait(false); -- cgit v1.2.3