diff --git a/Jellyfin.Api/Controllers/VideoAttachmentsController.cs b/Jellyfin.Api/Controllers/VideoAttachmentsController.cs
new file mode 100644
index 0000000000..86d9322fe4
--- /dev/null
+++ b/Jellyfin.Api/Controllers/VideoAttachmentsController.cs
@@ -0,0 +1,84 @@
+#nullable enable
+
+using System;
+using System.Net.Mime;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.MediaEncoding;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Jellyfin.Api.Controllers
+{
+ ///
+ /// Attachments controller.
+ ///
+ [Route("Videos")]
+ [Authorize]
+ public class VideoAttachmentsController : BaseJellyfinApiController
+ {
+ private readonly ILibraryManager _libraryManager;
+ private readonly IAttachmentExtractor _attachmentExtractor;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Instance of the interface.
+ /// Instance of the interface.
+ public VideoAttachmentsController(
+ ILibraryManager libraryManager,
+ IAttachmentExtractor attachmentExtractor)
+ {
+ _libraryManager = libraryManager;
+ _attachmentExtractor = attachmentExtractor;
+ }
+
+ ///
+ /// Get video attachment.
+ ///
+ /// Video ID.
+ /// Media Source ID.
+ /// Attachment Index.
+ /// Attachment retrieved.
+ /// Video or attachment not found.
+ /// An containing the attachment stream on success, or a if the attachment could not be found.
+ [HttpGet("{VideoID}/{MediaSourceID}/Attachments/{Index}")]
+ [Produces(MediaTypeNames.Application.Octet)]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public async Task> GetAttachment(
+ [FromRoute] Guid videoId,
+ [FromRoute] string mediaSourceId,
+ [FromRoute] int index)
+ {
+ try
+ {
+ var item = _libraryManager.GetItemById(videoId);
+ if (item == null)
+ {
+ return NotFound();
+ }
+
+ var (attachment, stream) = await _attachmentExtractor.GetAttachment(
+ item,
+ mediaSourceId,
+ index,
+ CancellationToken.None)
+ .ConfigureAwait(false);
+
+ var contentType = string.IsNullOrWhiteSpace(attachment.MimeType)
+ ? MediaTypeNames.Application.Octet
+ : attachment.MimeType;
+
+ return new FileStreamResult(stream, contentType);
+ }
+ catch (ResourceNotFoundException e)
+ {
+ return NotFound(e.Message);
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Api/Attachments/AttachmentService.cs b/MediaBrowser.Api/Attachments/AttachmentService.cs
deleted file mode 100644
index 1632ca1b06..0000000000
--- a/MediaBrowser.Api/Attachments/AttachmentService.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Services;
-using Microsoft.Extensions.Logging;
-
-namespace MediaBrowser.Api.Attachments
-{
- [Route("/Videos/{Id}/{MediaSourceId}/Attachments/{Index}", "GET", Summary = "Gets specified attachment.")]
- public class GetAttachment
- {
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public Guid Id { get; set; }
-
- [ApiMember(Name = "MediaSourceId", Description = "MediaSourceId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string MediaSourceId { get; set; }
-
- [ApiMember(Name = "Index", Description = "The attachment stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
- public int Index { get; set; }
- }
-
- public class AttachmentService : BaseApiService
- {
- private readonly ILibraryManager _libraryManager;
- private readonly IAttachmentExtractor _attachmentExtractor;
-
- public AttachmentService(
- ILogger logger,
- IServerConfigurationManager serverConfigurationManager,
- IHttpResultFactory httpResultFactory,
- ILibraryManager libraryManager,
- IAttachmentExtractor attachmentExtractor)
- : base(logger, serverConfigurationManager, httpResultFactory)
- {
- _libraryManager = libraryManager;
- _attachmentExtractor = attachmentExtractor;
- }
-
- public async Task