diff --git a/Jellyfin.Api/Controllers/AttachmentsController.cs b/Jellyfin.Api/Controllers/AttachmentsController.cs
new file mode 100644
index 0000000000..5d48a79b9b
--- /dev/null
+++ b/Jellyfin.Api/Controllers/AttachmentsController.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Net;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Jellyfin.Api.Controllers
+{
+ ///
+ /// Attachments controller.
+ ///
+ [Route("Videos")]
+ [Authenticated]
+ public class AttachmentsController : Controller
+ {
+ private readonly ILibraryManager _libraryManager;
+ private readonly IAttachmentExtractor _attachmentExtractor;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Instance of the interface.
+ /// Instance of the interface.
+ public AttachmentsController(
+ ILibraryManager libraryManager,
+ IAttachmentExtractor attachmentExtractor)
+ {
+ _libraryManager = libraryManager;
+ _attachmentExtractor = attachmentExtractor;
+ }
+
+ ///
+ /// Get video attachment.
+ ///
+ /// Video ID.
+ /// Media Source ID.
+ /// Attachment Index.
+ /// Attachment.
+ [HttpGet("{VideoID}/{MediaSourceID}/Attachments/{Index}")]
+ [Produces("application/octet-stream")]
+ [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)]
+ 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 = "application/octet-stream";
+ if (string.IsNullOrWhiteSpace(attachment.MimeType))
+ {
+ contentType = attachment.MimeType;
+ }
+
+ return new FileStreamResult(stream, contentType);
+ }
+ catch (ResourceNotFoundException e)
+ {
+ return StatusCode(StatusCodes.Status404NotFound, e.Message);
+ }
+ catch (Exception e)
+ {
+ return StatusCode(StatusCodes.Status500InternalServerError, e.Message);
+ }
+ }
+ }
+}