aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Ullmer <davidullmer@outlook.de>2021-08-17 18:11:38 +0200
committerCody Robibero <cody@robibe.ro>2022-01-04 08:20:16 -0700
commit3fb3ee074a375d0afd4d7d81252ba535d885afb8 (patch)
tree327112e24e12a389cca09484849778feb4d932f9
parent0fd4ff44513b195bb43368c7002d08991fed6c98 (diff)
Remove splashscreen generation from IImageEncoder and add IImageGenerator
-rw-r--r--Emby.Drawing/NullImageEncoder.cs6
-rw-r--r--Jellyfin.Api/Controllers/SplashscreenController.cs105
-rw-r--r--Jellyfin.Drawing.Skia/DefaultImageGenerator.cs83
-rw-r--r--Jellyfin.Drawing.Skia/SkiaEncoder.cs7
-rw-r--r--Jellyfin.Drawing.Skia/SplashscreenBuilder.cs20
-rw-r--r--Jellyfin.Server/CoreAppHost.cs3
-rw-r--r--MediaBrowser.Controller/Drawing/GeneratedImages.cs13
-rw-r--r--MediaBrowser.Controller/Drawing/IImageEncoder.cs6
-rw-r--r--MediaBrowser.Controller/Drawing/IImageGenerator.cs17
-rw-r--r--MediaBrowser.Controller/Drawing/SplashscreenOptions.cs20
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; }