From 4ba168c8a1be41d1c9db581bc79f170e24327d19 Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Sun, 15 Aug 2021 20:32:08 +0200 Subject: Add splashscreen builder --- MediaBrowser.Controller/Drawing/IImageEncoder.cs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'MediaBrowser.Controller/Drawing/IImageEncoder.cs') diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs index 4e67cfee4..57d73699f 100644 --- a/MediaBrowser.Controller/Drawing/IImageEncoder.cs +++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs @@ -74,5 +74,11 @@ namespace MediaBrowser.Controller.Drawing /// The options to use when creating the collage. /// Optional. void CreateImageCollage(ImageCollageOptions options, string? libraryName); + + /// + /// Creates a splashscreen image. + /// + /// The options to use when creating the splashscreen. + void CreateSplashscreen(SplashscreenOptions options); } } -- cgit v1.2.3 From 3fb3ee074a375d0afd4d7d81252ba535d885afb8 Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Tue, 17 Aug 2021 18:11:38 +0200 Subject: Remove splashscreen generation from IImageEncoder and add IImageGenerator --- Emby.Drawing/NullImageEncoder.cs | 6 -- Jellyfin.Api/Controllers/SplashscreenController.cs | 105 --------------------- Jellyfin.Drawing.Skia/DefaultImageGenerator.cs | 83 ++++++++++++++++ Jellyfin.Drawing.Skia/SkiaEncoder.cs | 7 -- Jellyfin.Drawing.Skia/SplashscreenBuilder.cs | 20 ++-- Jellyfin.Server/CoreAppHost.cs | 3 + MediaBrowser.Controller/Drawing/GeneratedImages.cs | 13 +++ MediaBrowser.Controller/Drawing/IImageEncoder.cs | 6 -- MediaBrowser.Controller/Drawing/IImageGenerator.cs | 17 ++++ .../Drawing/SplashscreenOptions.cs | 20 +--- 10 files changed, 128 insertions(+), 152 deletions(-) delete mode 100644 Jellyfin.Api/Controllers/SplashscreenController.cs create mode 100644 Jellyfin.Drawing.Skia/DefaultImageGenerator.cs create mode 100644 MediaBrowser.Controller/Drawing/GeneratedImages.cs create mode 100644 MediaBrowser.Controller/Drawing/IImageGenerator.cs (limited to 'MediaBrowser.Controller/Drawing/IImageEncoder.cs') 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 @@ -43,12 +43,6 @@ namespace Emby.Drawing throw new NotImplementedException(); } - /// - public void CreateSplashscreen(SplashscreenOptions options) - { - throw new NotImplementedException(); - } - /// public string GetImageBlurHash(int xComp, int yComp, string path) { 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 -{ - /// - /// 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. - /// 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] 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 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[] - { - new ValueTuple(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 +{ + /// + /// The default image generator. + /// + public class DefaultImageGenerator : IImageGenerator + { + private readonly IImageEncoder _imageEncoder; + private readonly IItemRepository _itemRepository; + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + public DefaultImageGenerator( + IImageEncoder imageEncoder, + IItemRepository itemRepository, + ILogger logger) + { + _imageEncoder = imageEncoder; + _itemRepository = itemRepository; + _logger = logger; + } + + /// + public GeneratedImages[] GetSupportedImages() + { + return new[] { GeneratedImages.Splashscreen }; + } + + /// + 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 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[] + { + new ValueTuple(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 } } - /// - 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 /// /// Generate a splashscreen. /// - /// The options to generate the splashscreen. - public void GenerateSplash(SplashscreenOptions options) + /// The poster paths. + /// The landscape paths. + /// The output path. + /// Whether to apply the darkening filter. + public void GenerateSplash(IReadOnlyList posters, IReadOnlyList 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); } /// /// Generates a collage of posters and landscape pictures. /// - /// The poster paths. + /// The poster paths. /// The landscape paths. /// Whether to apply the darkening filter. /// The created collage as a bitmap. - private SKBitmap GenerateCollage(IReadOnlyList poster, IReadOnlyList backdrop, bool applyFilter) + private SKBitmap GenerateCollage(IReadOnlyList posters, IReadOnlyList 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(); serviceCollection.AddSingleton(); + // TODO search plugins + ServiceCollection.AddSingleton(); + // TODO search the assemblies instead of adding them manually? serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); 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 +{ + /// + /// Which generated images an supports. + /// + public enum GeneratedImages + { + /// + /// The splashscreen. + /// + 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 /// The options to use when creating the collage. /// Optional. void CreateImageCollage(ImageCollageOptions options, string? libraryName); - - /// - /// Creates a splashscreen image. - /// - /// The options to use when creating the splashscreen. - 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 + { + /// + /// Gets the supported generated images of the image generator. + /// + /// The supported images. + GeneratedImages[] GetSupportedImages(); + + /// + /// Generates a splashscreen. + /// + /// The options used to generate the splashscreen. + 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 { /// @@ -10,30 +8,14 @@ namespace MediaBrowser.Controller.Drawing /// /// Initializes a new instance of the class. /// - /// The portrait input paths. - /// The landscape input paths. /// The output path. - /// Optional. The image width. - /// Optional. The image height. /// Optional. Apply a darkening filter. - public SplashscreenOptions(IReadOnlyList portraitInputPaths, IReadOnlyList landscapeInputPaths, string outputPath, bool applyFilter = false) + public SplashscreenOptions(string outputPath, bool applyFilter = false) { - PortraitInputPaths = portraitInputPaths; - LandscapeInputPaths = landscapeInputPaths; OutputPath = outputPath; ApplyFilter = applyFilter; } - /// - /// Gets or sets the poster input paths. - /// - public IReadOnlyList PortraitInputPaths { get; set; } - - /// - /// Gets or sets the landscape input paths. - /// - public IReadOnlyList LandscapeInputPaths { get; set; } - /// /// Gets or sets the output path. /// -- cgit v1.2.3 From ecb73168b34e3d58dff186b6d90fb4bdd192e24a Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Mon, 10 Jan 2022 08:25:46 -0700 Subject: Suggestions from review --- Emby.Drawing/NullImageEncoder.cs | 6 ++ .../Library/SplashscreenPostScanTask.cs | 79 +++++++++++++++++++++ .../Tasks/RefreshMediaLibraryTask.cs | 17 +---- Jellyfin.Api/Controllers/ImageController.cs | 21 +++--- Jellyfin.Drawing.Skia/DefaultImageGenerator.cs | 82 ---------------------- Jellyfin.Drawing.Skia/SkiaEncoder.cs | 8 +++ Jellyfin.Drawing.Skia/SplashscreenBuilder.cs | 14 ++-- Jellyfin.Server/CoreAppHost.cs | 3 - MediaBrowser.Controller/Drawing/IImageEncoder.cs | 7 ++ MediaBrowser.Controller/Drawing/IImageGenerator.cs | 22 ------ 10 files changed, 119 insertions(+), 140 deletions(-) create mode 100644 Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs delete mode 100644 Jellyfin.Drawing.Skia/DefaultImageGenerator.cs delete mode 100644 MediaBrowser.Controller/Drawing/IImageGenerator.cs (limited to 'MediaBrowser.Controller/Drawing/IImageEncoder.cs') diff --git a/Emby.Drawing/NullImageEncoder.cs b/Emby.Drawing/NullImageEncoder.cs index 1c05aa916..d0a26b713 100644 --- a/Emby.Drawing/NullImageEncoder.cs +++ b/Emby.Drawing/NullImageEncoder.cs @@ -43,6 +43,12 @@ namespace Emby.Drawing throw new NotImplementedException(); } + /// + public void CreateSplashscreen(IReadOnlyList posters, IReadOnlyList backdrops) + { + throw new NotImplementedException(); + } + /// public string GetImageBlurHash(int xComp, int yComp, string path) { diff --git a/Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs b/Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs new file mode 100644 index 000000000..65445d3af --- /dev/null +++ b/Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Data.Enums; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; +using Microsoft.Extensions.Logging; + +namespace Emby.Server.Implementations.Library; + +/// +/// The splashscreen post scan task. +/// +public class SplashscreenPostScanTask : ILibraryPostScanTask +{ + private readonly IItemRepository _itemRepository; + private readonly IImageEncoder _imageEncoder; + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + public SplashscreenPostScanTask( + IItemRepository itemRepository, + IImageEncoder imageEncoder, + ILogger logger) + { + _itemRepository = itemRepository; + _imageEncoder = imageEncoder; + _logger = logger; + } + + /// + public Task Run(IProgress progress, CancellationToken cancellationToken) + { + var posters = GetItemsWithImageType(ImageType.Primary).Select(x => x.GetImages(ImageType.Primary).First().Path).ToList(); + var backdrops = GetItemsWithImageType(ImageType.Thumb).Select(x => x.GetImages(ImageType.Thumb).First().Path).ToList(); + if (backdrops.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"); + backdrops = GetItemsWithImageType(ImageType.Backdrop).Select(x => x.GetImages(ImageType.Backdrop).First().Path).ToList(); + } + + _imageEncoder.CreateSplashscreen(posters, backdrops); + return Task.CompletedTask; + } + + private IReadOnlyList GetItemsWithImageType(ImageType imageType) + { + // TODO make included libraries configurable + return _itemRepository.GetItemList(new InternalItemsQuery + { + CollapseBoxSetItems = false, + Recursive = true, + DtoOptions = new DtoOptions(false), + ImageTypes = new[] { imageType }, + Limit = 30, + // TODO max parental rating configurable + MaxParentalRating = 10, + OrderBy = new ValueTuple[] + { + new(ItemSortBy.Random, SortOrder.Ascending) + }, + IncludeItemTypes = new[] { BaseItemKind.Movie, BaseItemKind.Series } + }); + } +} diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs index c12fc2113..7c27ae384 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs @@ -2,12 +2,9 @@ using System; using System.Collections.Generic; -using System.IO; using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.Library; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; @@ -24,26 +21,16 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks /// private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localization; - private readonly IImageGenerator _imageGenerator; - private readonly IApplicationPaths _applicationPaths; /// /// Initializes a new instance of the class. /// /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - public RefreshMediaLibraryTask( - ILibraryManager libraryManager, - ILocalizationManager localization, - IImageGenerator imageGenerator, - IApplicationPaths applicationPaths) + public RefreshMediaLibraryTask(ILibraryManager libraryManager, ILocalizationManager localization) { _libraryManager = libraryManager; _localization = localization; - _imageGenerator = imageGenerator; - _applicationPaths = applicationPaths; } /// @@ -83,8 +70,6 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks progress.Report(0); - _imageGenerator.Generate(GeneratedImageType.Splashscreen, Path.Combine(_applicationPaths.DataPath, "splashscreen.webp")); - return ((LibraryManager)_libraryManager).ValidateMediaLibraryInternal(progress, cancellationToken); } } diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 6d34ca770..b44a21d03 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -48,7 +48,7 @@ namespace Jellyfin.Api.Controllers private readonly ILogger _logger; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IApplicationPaths _appPaths; - private readonly IImageGenerator _imageGenerator; + private readonly IImageEncoder _imageEncoder; /// /// Initializes a new instance of the class. @@ -62,7 +62,7 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. + /// Instance of the interface. public ImageController( IUserManager userManager, ILibraryManager libraryManager, @@ -73,7 +73,7 @@ namespace Jellyfin.Api.Controllers ILogger logger, IServerConfigurationManager serverConfigurationManager, IApplicationPaths appPaths, - IImageGenerator imageGenerator) + IImageEncoder imageEncoder) { _userManager = userManager; _libraryManager = libraryManager; @@ -84,7 +84,7 @@ namespace Jellyfin.Api.Controllers _logger = logger; _serverConfigurationManager = serverConfigurationManager; _appPaths = appPaths; - _imageGenerator = imageGenerator; + _imageEncoder = imageEncoder; } /// @@ -1737,19 +1737,20 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? foregroundLayer, [FromQuery, Range(0, 100)] int quality = 90) { - string splashscreenPath; var brandingOptions = _serverConfigurationManager.GetConfiguration("branding"); - if (!string.IsNullOrWhiteSpace(brandingOptions.SplashscreenLocation)) + string splashscreenPath; + + if (!string.IsNullOrWhiteSpace(brandingOptions.SplashscreenLocation) + && System.IO.File.Exists(brandingOptions.SplashscreenLocation)) { - splashscreenPath = brandingOptions.SplashscreenLocation!; + splashscreenPath = brandingOptions.SplashscreenLocation; } else { splashscreenPath = Path.Combine(_appPaths.DataPath, "splashscreen.webp"); - - if (!System.IO.File.Exists(splashscreenPath) && _imageGenerator.GetSupportedImages().Contains(GeneratedImageType.Splashscreen)) + if (!System.IO.File.Exists(splashscreenPath)) { - _imageGenerator.Generate(GeneratedImageType.Splashscreen, splashscreenPath); + return NotFound(); } } diff --git a/Jellyfin.Drawing.Skia/DefaultImageGenerator.cs b/Jellyfin.Drawing.Skia/DefaultImageGenerator.cs deleted file mode 100644 index e102b8f49..000000000 --- a/Jellyfin.Drawing.Skia/DefaultImageGenerator.cs +++ /dev/null @@ -1,82 +0,0 @@ -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; - -/// -/// The default image generator. -/// -public class DefaultImageGenerator : IImageGenerator -{ - private readonly IImageEncoder _imageEncoder; - private readonly IItemRepository _itemRepository; - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - public DefaultImageGenerator( - IImageEncoder imageEncoder, - IItemRepository itemRepository, - ILogger logger) - { - _imageEncoder = imageEncoder; - _itemRepository = itemRepository; - _logger = logger; - } - - /// - public IReadOnlyList GetSupportedImages() - { - return new[] { GeneratedImageType.Splashscreen }; - } - - /// - public void Generate(GeneratedImageType imageTypeType, string outputPath) - { - 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, outputPath); - } - - private IReadOnlyList GetItemsWithImageType(ImageType imageType) - { - // todo make included libraries configurable - return _itemRepository.GetItemList(new InternalItemsQuery - { - CollapseBoxSetItems = false, - Recursive = true, - DtoOptions = new DtoOptions(false), - ImageTypes = new[] { imageType }, - Limit = 30, - // todo max parental rating configurable - MaxParentalRating = 10, - OrderBy = new ValueTuple[] - { - new(ItemSortBy.Random, SortOrder.Ascending) - }, - IncludeItemTypes = new[] { BaseItemKind.Movie, BaseItemKind.Series } - }); - } -} diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index 6d0a5ac2b..c40103f82 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -492,6 +492,14 @@ namespace Jellyfin.Drawing.Skia } } + /// + public void CreateSplashscreen(IReadOnlyList posters, IReadOnlyList backdrops) + { + var splashBuilder = new SplashscreenBuilder(this); + var outputPath = Path.Combine(_appPaths.DataPath, "splashscreen.webp"); + splashBuilder.GenerateSplash(posters, backdrops, outputPath); + } + 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 9f801c320..132c35e67 100644 --- a/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs +++ b/Jellyfin.Drawing.Skia/SplashscreenBuilder.cs @@ -32,12 +32,12 @@ namespace Jellyfin.Drawing.Skia /// Generate a splashscreen. /// /// The poster paths. - /// The landscape paths. + /// The landscape paths. /// The output path. - public void GenerateSplash(IReadOnlyList posters, IReadOnlyList backdrop, string outputPath) + public void GenerateSplash(IReadOnlyList posters, IReadOnlyList backdrops, string outputPath) { - var wall = GenerateCollage(posters, backdrop); - var transformed = Transform3D(wall); + using var wall = GenerateCollage(posters, backdrops); + using var transformed = Transform3D(wall); using var outputStream = new SKFileWStream(outputPath); using var pixmap = new SKPixmap(new SKImageInfo(FinalWidth, FinalHeight), transformed.GetPixels()); @@ -48,9 +48,9 @@ namespace Jellyfin.Drawing.Skia /// Generates a collage of posters and landscape pictures. /// /// The poster paths. - /// The landscape paths. + /// The landscape paths. /// The created collage as a bitmap. - private SKBitmap GenerateCollage(IReadOnlyList posters, IReadOnlyList backdrop) + private SKBitmap GenerateCollage(IReadOnlyList posters, IReadOnlyList backdrops) { var random = new Random(); @@ -82,7 +82,7 @@ namespace Jellyfin.Drawing.Skia posterIndex = newPosterIndex; break; default: - currentImage = SkiaHelper.GetNextValidImage(_skiaEncoder, backdrop, backdropIndex, out int newBackdropIndex); + currentImage = SkiaHelper.GetNextValidImage(_skiaEncoder, backdrops, backdropIndex, out int newBackdropIndex); backdropIndex = newBackdropIndex; break; } diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index b2f9a518a..67e50b92d 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -85,9 +85,6 @@ namespace Jellyfin.Server serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - // TODO search plugins - serviceCollection.AddSingleton(); - // TODO search the assemblies instead of adding them manually? serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs index 4e67cfee4..e5c8ebfaf 100644 --- a/MediaBrowser.Controller/Drawing/IImageEncoder.cs +++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs @@ -74,5 +74,12 @@ namespace MediaBrowser.Controller.Drawing /// The options to use when creating the collage. /// Optional. void CreateImageCollage(ImageCollageOptions options, string? libraryName); + + /// + /// Creates a new splashscreen image. + /// + /// The list of poster paths. + /// The list of backdrop paths. + void CreateSplashscreen(IReadOnlyList posters, IReadOnlyList backdrops); } } diff --git a/MediaBrowser.Controller/Drawing/IImageGenerator.cs b/MediaBrowser.Controller/Drawing/IImageGenerator.cs deleted file mode 100644 index 773db02cb..000000000 --- a/MediaBrowser.Controller/Drawing/IImageGenerator.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -namespace MediaBrowser.Controller.Drawing; - -/// -/// Interface for an image generator. -/// -public interface IImageGenerator -{ - /// - /// Gets the supported generated images of the image generator. - /// - /// The supported generated image types. - IReadOnlyList GetSupportedImages(); - - /// - /// Generates a splashscreen. - /// - /// The image to generate. - /// The path where the splashscreen should be saved. - void Generate(GeneratedImageType imageTypeType, string outputPath); -} -- cgit v1.2.3