diff --git a/Jellyfin.Api/Attributes/AcceptsFileAttribute.cs b/Jellyfin.Api/Attributes/AcceptsFileAttribute.cs
new file mode 100644
index 0000000000..49b6689cde
--- /dev/null
+++ b/Jellyfin.Api/Attributes/AcceptsFileAttribute.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Jellyfin.Api.Attributes
+{
+ ///
+ /// Internal produces image attribute.
+ ///
+ [AttributeUsage(AttributeTargets.Method)]
+ public class AcceptsFileAttribute : Attribute
+ {
+ private readonly string[] _contentTypes;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Content types this endpoint produces.
+ public AcceptsFileAttribute(params string[] contentTypes)
+ {
+ _contentTypes = contentTypes;
+ }
+
+ ///
+ /// Gets the configured content types.
+ ///
+ /// the configured content types.
+ public string[] GetContentTypes() => _contentTypes;
+ }
+}
diff --git a/Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs b/Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs
new file mode 100644
index 0000000000..001f27409e
--- /dev/null
+++ b/Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs
@@ -0,0 +1,18 @@
+namespace Jellyfin.Api.Attributes
+{
+ ///
+ /// Produces file attribute of "image/*".
+ ///
+ public class AcceptsImageFileAttribute : AcceptsFileAttribute
+ {
+ private const string ContentType = "image/*";
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AcceptsImageFileAttribute()
+ : base(ContentType)
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs
index c606d327c5..dc36349704 100644
--- a/Jellyfin.Api/Controllers/ImageController.cs
+++ b/Jellyfin.Api/Controllers/ImageController.cs
@@ -87,6 +87,7 @@ namespace Jellyfin.Api.Controllers
/// A .
[HttpPost("Users/{userId}/Images/{imageType}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
+ [AcceptsImageFile]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")]
@@ -133,6 +134,7 @@ namespace Jellyfin.Api.Controllers
/// A .
[HttpPost("Users/{userId}/Images/{imageType}/{index}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
+ [AcceptsImageFile]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")]
@@ -312,6 +314,7 @@ namespace Jellyfin.Api.Controllers
/// A on success, or a if item not found.
[HttpPost("Items/{itemId}/Images/{imageType}")]
[Authorize(Policy = Policies.RequiresElevation)]
+ [AcceptsImageFile]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
@@ -346,6 +349,7 @@ namespace Jellyfin.Api.Controllers
/// A on success, or a if item not found.
[HttpPost("Items/{itemId}/Images/{imageType}/{imageIndex}")]
[Authorize(Policy = Policies.RequiresElevation)]
+ [AcceptsImageFile]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
index da4cc267b3..545937207c 100644
--- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
@@ -316,6 +316,7 @@ namespace Jellyfin.Server.Extensions
c.OperationFilter();
c.OperationFilter();
+ c.OperationFilter();
c.OperationFilter();
c.DocumentFilter();
});
diff --git a/Jellyfin.Server/Filters/FileRequestFilter.cs b/Jellyfin.Server/Filters/FileRequestFilter.cs
new file mode 100644
index 0000000000..69e10994f4
--- /dev/null
+++ b/Jellyfin.Server/Filters/FileRequestFilter.cs
@@ -0,0 +1,43 @@
+using System.Collections.Generic;
+using Jellyfin.Api.Attributes;
+using Microsoft.OpenApi.Models;
+using Swashbuckle.AspNetCore.SwaggerGen;
+
+namespace Jellyfin.Server.Filters
+{
+ ///
+ public class FileRequestFilter : IOperationFilter
+ {
+ ///
+ public void Apply(OpenApiOperation operation, OperationFilterContext context)
+ {
+ foreach (var attribute in context.ApiDescription.ActionDescriptor.EndpointMetadata)
+ {
+ if (attribute is AcceptsFileAttribute acceptsFileAttribute)
+ {
+ operation.RequestBody = GetRequestBody(acceptsFileAttribute.GetContentTypes());
+ break;
+ }
+ }
+ }
+
+ private static OpenApiRequestBody GetRequestBody(IEnumerable contentTypes)
+ {
+ var body = new OpenApiRequestBody();
+ var mediaType = new OpenApiMediaType
+ {
+ Schema = new OpenApiSchema
+ {
+ Type = "string",
+ Format = "binary"
+ }
+ };
+ foreach (var contentType in contentTypes)
+ {
+ body.Content.Add(contentType, mediaType);
+ }
+
+ return body;
+ }
+ }
+}