diff options
| author | David Ullmer <davidullmer@outlook.de> | 2021-08-17 18:11:38 +0200 |
|---|---|---|
| committer | Cody Robibero <cody@robibe.ro> | 2022-01-04 08:20:16 -0700 |
| commit | 3fb3ee074a375d0afd4d7d81252ba535d885afb8 (patch) | |
| tree | 327112e24e12a389cca09484849778feb4d932f9 | |
| parent | 0fd4ff44513b195bb43368c7002d08991fed6c98 (diff) | |
Remove splashscreen generation from IImageEncoder and add IImageGenerator
| -rw-r--r-- | Emby.Drawing/NullImageEncoder.cs | 6 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/SplashscreenController.cs | 105 | ||||
| -rw-r--r-- | Jellyfin.Drawing.Skia/DefaultImageGenerator.cs | 83 | ||||
| -rw-r--r-- | Jellyfin.Drawing.Skia/SkiaEncoder.cs | 7 | ||||
| -rw-r--r-- | Jellyfin.Drawing.Skia/SplashscreenBuilder.cs | 20 | ||||
| -rw-r--r-- | Jellyfin.Server/CoreAppHost.cs | 3 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Drawing/GeneratedImages.cs | 13 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Drawing/IImageEncoder.cs | 6 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Drawing/IImageGenerator.cs | 17 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Drawing/SplashscreenOptions.cs | 20 |
10 files changed, 128 insertions, 152 deletions
diff --git a/Emby.Drawing/NullImageEncoder.cs b/Emby.Drawing/NullImageEncoder.cs index ed12f6acb..1c05aa916 100644 --- a/Emby.Drawing/NullImageEncoder.cs +++ b/Emby.Drawing/NullImageEncoder.cs @@ -44,12 +44,6 @@ namespace Emby.Drawing } /// <inheritdoc /> - public void CreateSplashscreen(SplashscreenOptions options) - { - throw new NotImplementedException(); - } - - /// <inheritdoc /> public string GetImageBlurHash(int xComp, int yComp, string path) { throw new NotImplementedException(); diff --git a/Jellyfin.Api/Controllers/SplashscreenController.cs b/Jellyfin.Api/Controllers/SplashscreenController.cs deleted file mode 100644 index 48a559b28..000000000 --- a/Jellyfin.Api/Controllers/SplashscreenController.cs +++ /dev/null @@ -1,105 +0,0 @@ -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 -{ - /// <summary> - /// Splashscreen controller. - /// </summary> - [Route("Splashscreen")] - public class SplashscreenController : BaseJellyfinApiController - { - private readonly IImageEncoder _imageEncoder; - private readonly IItemRepository _itemRepository; - private readonly IApplicationPaths _appPaths; - private readonly ILogger _logger; - - /// <summary> - /// Initializes a new instance of the <see cref="SplashscreenController"/> class. - /// </summary> - /// <param name="imageEncoder">Instance of the <see cref="IImageEncoder"/> interface.</param> - /// <param name="itemRepository">Instance of the <see cref="IItemRepository"/> interface.</param> - /// <param name="applicationPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param> - /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> - public SplashscreenController( - IImageEncoder imageEncoder, - IItemRepository itemRepository, - IApplicationPaths applicationPaths, - ILogger<SplashscreenController> logger) - { - _imageEncoder = imageEncoder; - _itemRepository = itemRepository; - _appPaths = applicationPaths; - _logger = logger; - } - - /// <summary> - /// Generates or gets the splashscreen. - /// </summary> - /// <param name="darken">Darken the generated image.</param> - /// <param name="regenerate">Whether to regenerate the image, regardless if one already exists.</param> - /// <returns>The splashscreen.</returns> - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - [ProducesImageFile] - public ActionResult GetSplashscreen( - [FromQuery] bool? darken = false, - [FromQuery] bool? regenerate = false) - { - var outputPath = Path.Combine(_appPaths.DataPath, $"splashscreen-{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) - { - // Thumb images fit better because they include the title in the image but are not provided with TMDb. - // Using backdrops as a fallback to generate an image at all - _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, darken!.Value)); - } - - return PhysicalFile(outputPath, MimeTypes.GetMimeType(outputPath)); - } - - private IReadOnlyList<BaseItem> GetItemsWithImageType(ImageType imageType) - { - // todo make included libraries configurable - return _itemRepository.GetItemList(new InternalItemsQuery - { - CollapseBoxSetItems = false, - Recursive = true, - DtoOptions = new DtoOptions(false), - ImageTypes = new ImageType[] { imageType }, - Limit = 30, - // todo max parental rating configurable - MaxParentalRating = 10, - OrderBy = new ValueTuple<string, SortOrder>[] - { - new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) - }, - IncludeItemTypes = new string[] { "Movie", "Series" } - }); - } - } -} diff --git a/Jellyfin.Drawing.Skia/DefaultImageGenerator.cs b/Jellyfin.Drawing.Skia/DefaultImageGenerator.cs new file mode 100644 index 000000000..780d0b060 --- /dev/null +++ b/Jellyfin.Drawing.Skia/DefaultImageGenerator.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Jellyfin.Data.Enums; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Drawing.Skia +{ + /// <summary> + /// The default image generator. + /// </summary> + public class DefaultImageGenerator : IImageGenerator + { + private readonly IImageEncoder _imageEncoder; + private readonly IItemRepository _itemRepository; + private readonly ILogger _logger; + + /// <summary> + /// Initializes a new instance of the <see cref="DefaultImageGenerator"/> class. + /// </summary> + /// <param name="imageEncoder">Instance of the <see cref="IImageEncoder"/> interface.</param> + /// <param name="itemRepository">Instance of the <see cref="IItemRepository"/> interface.</param> + /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> + public DefaultImageGenerator( + IImageEncoder imageEncoder, + IItemRepository itemRepository, + ILogger<DefaultImageGenerator> logger) + { + _imageEncoder = imageEncoder; + _itemRepository = itemRepository; + _logger = logger; + } + + /// <inheritdoc/> + public GeneratedImages[] GetSupportedImages() + { + return new[] { GeneratedImages.Splashscreen }; + } + + /// <inheritdoc/> + public void GenerateSplashscreen(SplashscreenOptions generationOptions) + { + 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) + { + // Thumb images fit better because they include the title in the image but are not provided with TMDb. + // Using backdrops as a fallback to generate an image at all + _logger.LogDebug("No thumb images found. Using backdrops to generate splashscreen."); + landscape = GetItemsWithImageType(ImageType.Backdrop).Select(x => x.GetImages(ImageType.Backdrop).First().Path).ToList(); + } + + var splashBuilder = new SplashscreenBuilder((SkiaEncoder)_imageEncoder); + splashBuilder.GenerateSplash(posters, landscape, generationOptions.OutputPath, generationOptions.ApplyFilter); + } + + private IReadOnlyList<BaseItem> GetItemsWithImageType(ImageType imageType) + { + // todo make included libraries configurable + return _itemRepository.GetItemList(new InternalItemsQuery + { + CollapseBoxSetItems = false, + Recursive = true, + DtoOptions = new DtoOptions(false), + ImageTypes = new ImageType[] { imageType }, + Limit = 30, + // todo max parental rating configurable + MaxParentalRating = 10, + OrderBy = new ValueTuple<string, SortOrder>[] + { + new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) + }, + IncludeItemTypes = new string[] { "Movie", "Series" } + }); + } + } +} diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index 16de5d7fd..6d0a5ac2b 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -492,13 +492,6 @@ namespace Jellyfin.Drawing.Skia } } - /// <inheritdoc/> - public void CreateSplashscreen(SplashscreenOptions options) - { - var splashBuilder = new SplashscreenBuilder(this); - splashBuilder.GenerateSplash(options); - } - private void DrawIndicator(SKCanvas canvas, int imageWidth, int imageHeight, ImageProcessingOptions options) { try diff --git a/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs b/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs index 4773464b4..7cb10bfee 100644 --- a/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs +++ b/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using MediaBrowser.Controller.Drawing; using SkiaSharp; namespace Jellyfin.Drawing.Skia @@ -34,25 +33,28 @@ namespace Jellyfin.Drawing.Skia /// <summary> /// Generate a splashscreen. /// </summary> - /// <param name="options">The options to generate the splashscreen.</param> - public void GenerateSplash(SplashscreenOptions options) + /// <param name="posters">The poster paths.</param> + /// <param name="backdrop">The landscape paths.</param> + /// <param name="outputPath">The output path.</param> + /// <param name="applyFilter">Whether to apply the darkening filter.</param> + public void GenerateSplash(IReadOnlyList<string> posters, IReadOnlyList<string> backdrop, string outputPath, bool applyFilter) { - var wall = GenerateCollage(options.PortraitInputPaths, options.LandscapeInputPaths, options.ApplyFilter); + var wall = GenerateCollage(posters, backdrop, applyFilter); var transformed = Transform3D(wall); - using var outputStream = new SKFileWStream(options.OutputPath); + using var outputStream = new SKFileWStream(outputPath); using var pixmap = new SKPixmap(new SKImageInfo(FinalWidth, FinalHeight), transformed.GetPixels()); - pixmap.Encode(outputStream, StripCollageBuilder.GetEncodedFormat(options.OutputPath), 90); + pixmap.Encode(outputStream, StripCollageBuilder.GetEncodedFormat(outputPath), 90); } /// <summary> /// Generates a collage of posters and landscape pictures. /// </summary> - /// <param name="poster">The poster paths.</param> + /// <param name="posters">The poster paths.</param> /// <param name="backdrop">The landscape paths.</param> /// <param name="applyFilter">Whether to apply the darkening filter.</param> /// <returns>The created collage as a bitmap.</returns> - private SKBitmap GenerateCollage(IReadOnlyList<string> poster, IReadOnlyList<string> backdrop, bool applyFilter) + private SKBitmap GenerateCollage(IReadOnlyList<string> posters, IReadOnlyList<string> backdrop, bool applyFilter) { _random = new Random(); @@ -80,7 +82,7 @@ namespace Jellyfin.Drawing.Skia case 0: case 2: case 3: - currentImage = SkiaHelper.GetNextValidImage(_skiaEncoder, poster, posterIndex, out int newPosterIndex); + currentImage = SkiaHelper.GetNextValidImage(_skiaEncoder, posters, posterIndex, out int newPosterIndex); posterIndex = newPosterIndex; break; default: diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 67e50b92d..ac7ab2dee 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -85,6 +85,9 @@ namespace Jellyfin.Server serviceCollection.AddSingleton<IDisplayPreferencesManager, DisplayPreferencesManager>(); serviceCollection.AddSingleton<IDeviceManager, DeviceManager>(); + // TODO search plugins + ServiceCollection.AddSingleton<IImageGenerator, DefaultImageGenerator>(); + // TODO search the assemblies instead of adding them manually? serviceCollection.AddSingleton<IWebSocketListener, SessionWebSocketListener>(); serviceCollection.AddSingleton<IWebSocketListener, ActivityLogWebSocketListener>(); diff --git a/MediaBrowser.Controller/Drawing/GeneratedImages.cs b/MediaBrowser.Controller/Drawing/GeneratedImages.cs new file mode 100644 index 000000000..47b60979b --- /dev/null +++ b/MediaBrowser.Controller/Drawing/GeneratedImages.cs @@ -0,0 +1,13 @@ +namespace MediaBrowser.Controller.Drawing +{ + /// <summary> + /// Which generated images an <see cref="IImageGenerator"/> supports. + /// </summary> + public enum GeneratedImages + { + /// <summary> + /// The splashscreen. + /// </summary> + Splashscreen + } +} diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs index 57d73699f..4e67cfee4 100644 --- a/MediaBrowser.Controller/Drawing/IImageEncoder.cs +++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs @@ -74,11 +74,5 @@ namespace MediaBrowser.Controller.Drawing /// <param name="options">The options to use when creating the collage.</param> /// <param name="libraryName">Optional. </param> void CreateImageCollage(ImageCollageOptions options, string? libraryName); - - /// <summary> - /// Creates a splashscreen image. - /// </summary> - /// <param name="options">The options to use when creating the splashscreen.</param> - void CreateSplashscreen(SplashscreenOptions options); } } diff --git a/MediaBrowser.Controller/Drawing/IImageGenerator.cs b/MediaBrowser.Controller/Drawing/IImageGenerator.cs new file mode 100644 index 000000000..21699c3f0 --- /dev/null +++ b/MediaBrowser.Controller/Drawing/IImageGenerator.cs @@ -0,0 +1,17 @@ +namespace MediaBrowser.Controller.Drawing +{ + public interface IImageGenerator + { + /// <summary> + /// Gets the supported generated images of the image generator. + /// </summary> + /// <returns>The supported images.</returns> + GeneratedImages[] GetSupportedImages(); + + /// <summary> + /// Generates a splashscreen. + /// </summary> + /// <param name="generationOptions">The options used to generate the splashscreen.</param> + void GenerateSplashscreen(SplashscreenOptions generationOptions); + } +} diff --git a/MediaBrowser.Controller/Drawing/SplashscreenOptions.cs b/MediaBrowser.Controller/Drawing/SplashscreenOptions.cs index 0534d60b6..ba268b8eb 100644 --- a/MediaBrowser.Controller/Drawing/SplashscreenOptions.cs +++ b/MediaBrowser.Controller/Drawing/SplashscreenOptions.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace MediaBrowser.Controller.Drawing { /// <summary> @@ -10,31 +8,15 @@ namespace MediaBrowser.Controller.Drawing /// <summary> /// Initializes a new instance of the <see cref="SplashscreenOptions"/> class. /// </summary> - /// <param name="portraitInputPaths">The portrait input paths.</param> - /// <param name="landscapeInputPaths">The landscape input paths.</param> /// <param name="outputPath">The output path.</param> - /// <param name="width">Optional. The image width.</param> - /// <param name="height">Optional. The image height.</param> /// <param name="applyFilter">Optional. Apply a darkening filter.</param> - public SplashscreenOptions(IReadOnlyList<string> portraitInputPaths, IReadOnlyList<string> landscapeInputPaths, string outputPath, bool applyFilter = false) + public SplashscreenOptions(string outputPath, bool applyFilter = false) { - PortraitInputPaths = portraitInputPaths; - LandscapeInputPaths = landscapeInputPaths; OutputPath = outputPath; ApplyFilter = applyFilter; } /// <summary> - /// Gets or sets the poster input paths. - /// </summary> - public IReadOnlyList<string> PortraitInputPaths { get; set; } - - /// <summary> - /// Gets or sets the landscape input paths. - /// </summary> - public IReadOnlyList<string> LandscapeInputPaths { get; set; } - - /// <summary> /// Gets or sets the output path. /// </summary> public string OutputPath { get; set; } |
