diff --git a/Jellyfin.Api/Controllers/SplashscreenController.cs b/Jellyfin.Api/Controllers/SplashscreenController.cs
new file mode 100644
index 0000000000..0fc9af60bb
--- /dev/null
+++ b/Jellyfin.Api/Controllers/SplashscreenController.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Jellyfin.Api.Attributes;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Querying;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Api.Controllers
+{
+ ///
+ /// Splashscreen controller.
+ ///
+ [Route("Splashscreen")]
+ public class SplashscreenController : BaseJellyfinApiController
+ {
+ private readonly IImageEncoder _imageEncoder;
+ private readonly IItemRepository _itemRepository;
+ private readonly IApplicationPaths _appPaths;
+ private readonly ILogger _logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ public SplashscreenController(
+ IImageEncoder imageEncoder,
+ IItemRepository itemRepository,
+ IApplicationPaths applicationPaths,
+ ILogger logger)
+ {
+ _imageEncoder = imageEncoder;
+ _itemRepository = itemRepository;
+ _appPaths = applicationPaths;
+ _logger = logger;
+ }
+
+ ///
+ /// Generates or gets the splashscreen.
+ ///
+ /// Darken the generated image.
+ /// The image width.
+ /// The image height.
+ /// Whether to regenerate the image, regardless if one already exists.
+ /// The splashscreen.
+ [HttpGet]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ [ProducesImageFile]
+ public ActionResult GetSplashscreen(
+ [FromQuery] bool? darken = false,
+ [FromQuery] int? width = 1920,
+ [FromQuery] int? height = 1080,
+ [FromQuery] bool? regenerate = false)
+ {
+ var outputPath = Path.Combine(_appPaths.DataPath, $"splashscreen-{width}x{height}-{darken}.jpg");
+
+ if (!System.IO.File.Exists(outputPath) || (regenerate ?? false))
+ {
+ var posters = GetItemsWithImageType(ImageType.Primary).Select(x => x.GetImages(ImageType.Primary).First().Path).ToList();
+ var landscape = GetItemsWithImageType(ImageType.Thumb).Select(x => x.GetImages(ImageType.Thumb).First().Path).ToList();
+ if (landscape.Count == 0)
+ {
+ _logger.LogDebug("No thumb images found. Using backdrops to generate splashscreen.");
+ landscape = GetItemsWithImageType(ImageType.Backdrop).Select(x => x.GetImages(ImageType.Backdrop).First().Path).ToList();
+ }
+
+ _imageEncoder.CreateSplashscreen(new SplashscreenOptions(posters, landscape, outputPath, width!.Value, height!.Value, darken!.Value));
+ }
+
+ return PhysicalFile(outputPath, MimeTypes.GetMimeType(outputPath));
+ }
+
+ private IReadOnlyList GetItemsWithImageType(ImageType imageType)
+ {
+ return _itemRepository.GetItemList(new InternalItemsQuery
+ {
+ CollapseBoxSetItems = false,
+ Recursive = true,
+ DtoOptions = new DtoOptions(false),
+ ImageTypes = new ImageType[] { imageType },
+ Limit = 8,
+ OrderBy = new ValueTuple[]
+ {
+ new ValueTuple(ItemSortBy.Random, SortOrder.Ascending)
+ },
+ IncludeItemTypes = new string[] { "Movie", "Series" }
+ });
+ }
+ }
+}