mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-03 19:17:24 -05:00 
			
		
		
		
	Suggestions from review
This commit is contained in:
		
							parent
							
								
									360fd70fc7
								
							
						
					
					
						commit
						ecb73168b3
					
				@ -43,6 +43,12 @@ namespace Emby.Drawing
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        public void CreateSplashscreen(IReadOnlyList<string> posters, IReadOnlyList<string> backdrops)
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        public string GetImageBlurHash(int xComp, int yComp, string path)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
@ -1,68 +1,65 @@
 | 
			
		||||
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 Jellyfin.Drawing.Skia;
 | 
			
		||||
namespace Emby.Server.Implementations.Library;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// The default image generator.
 | 
			
		||||
/// The splashscreen post scan task.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class DefaultImageGenerator : IImageGenerator
 | 
			
		||||
public class SplashscreenPostScanTask : ILibraryPostScanTask
 | 
			
		||||
{
 | 
			
		||||
    private readonly IImageEncoder _imageEncoder;
 | 
			
		||||
    private readonly IItemRepository _itemRepository;
 | 
			
		||||
    private readonly ILogger _logger;
 | 
			
		||||
    private readonly IImageEncoder _imageEncoder;
 | 
			
		||||
    private readonly ILogger<SplashscreenPostScanTask> _logger;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Initializes a new instance of the <see cref="DefaultImageGenerator"/> class.
 | 
			
		||||
    /// Initializes a new instance of the <see cref="SplashscreenPostScanTask"/> 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,
 | 
			
		||||
    /// <param name="imageEncoder">Instance of the <see cref="IImageEncoder"/> interface.</param>
 | 
			
		||||
    /// <param name="logger">Instance of the <see cref="ILogger{SplashscreenPostScanTask"/> interface.</param>
 | 
			
		||||
    public SplashscreenPostScanTask(
 | 
			
		||||
        IItemRepository itemRepository,
 | 
			
		||||
        ILogger<DefaultImageGenerator> logger)
 | 
			
		||||
        IImageEncoder imageEncoder,
 | 
			
		||||
        ILogger<SplashscreenPostScanTask> logger)
 | 
			
		||||
    {
 | 
			
		||||
        _imageEncoder = imageEncoder;
 | 
			
		||||
        _itemRepository = itemRepository;
 | 
			
		||||
        _imageEncoder = imageEncoder;
 | 
			
		||||
        _logger = logger;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public IReadOnlyList<GeneratedImageType> GetSupportedImages()
 | 
			
		||||
    {
 | 
			
		||||
        return new[] { GeneratedImageType.Splashscreen };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public void Generate(GeneratedImageType imageTypeType, string outputPath)
 | 
			
		||||
    public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        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)
 | 
			
		||||
        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");
 | 
			
		||||
            landscape = GetItemsWithImageType(ImageType.Backdrop).Select(x => x.GetImages(ImageType.Backdrop).First().Path).ToList();
 | 
			
		||||
            backdrops = GetItemsWithImageType(ImageType.Backdrop).Select(x => x.GetImages(ImageType.Backdrop).First().Path).ToList();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var splashBuilder = new SplashscreenBuilder((SkiaEncoder)_imageEncoder);
 | 
			
		||||
        splashBuilder.GenerateSplash(posters, landscape, outputPath);
 | 
			
		||||
        _imageEncoder.CreateSplashscreen(posters, backdrops);
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private IReadOnlyList<BaseItem> GetItemsWithImageType(ImageType imageType)
 | 
			
		||||
    {
 | 
			
		||||
        // todo make included libraries configurable
 | 
			
		||||
        // TODO make included libraries configurable
 | 
			
		||||
        return _itemRepository.GetItemList(new InternalItemsQuery
 | 
			
		||||
        {
 | 
			
		||||
            CollapseBoxSetItems = false,
 | 
			
		||||
@ -70,7 +67,7 @@ public class DefaultImageGenerator : IImageGenerator
 | 
			
		||||
            DtoOptions = new DtoOptions(false),
 | 
			
		||||
            ImageTypes = new[] { imageType },
 | 
			
		||||
            Limit = 30,
 | 
			
		||||
            // todo max parental rating configurable
 | 
			
		||||
            // TODO max parental rating configurable
 | 
			
		||||
            MaxParentalRating = 10,
 | 
			
		||||
            OrderBy = new ValueTuple<string, SortOrder>[]
 | 
			
		||||
            {
 | 
			
		||||
@ -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
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        private readonly ILibraryManager _libraryManager;
 | 
			
		||||
        private readonly ILocalizationManager _localization;
 | 
			
		||||
        private readonly IImageGenerator _imageGenerator;
 | 
			
		||||
        private readonly IApplicationPaths _applicationPaths;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="RefreshMediaLibraryTask" /> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 | 
			
		||||
        /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
 | 
			
		||||
        /// <param name="imageGenerator">Instance of the <see cref="IImageGenerator"/> interface.</param>
 | 
			
		||||
        /// <param name="applicationPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
 | 
			
		||||
        public RefreshMediaLibraryTask(
 | 
			
		||||
            ILibraryManager libraryManager,
 | 
			
		||||
            ILocalizationManager localization,
 | 
			
		||||
            IImageGenerator imageGenerator,
 | 
			
		||||
            IApplicationPaths applicationPaths)
 | 
			
		||||
        public RefreshMediaLibraryTask(ILibraryManager libraryManager, ILocalizationManager localization)
 | 
			
		||||
        {
 | 
			
		||||
            _libraryManager = libraryManager;
 | 
			
		||||
            _localization = localization;
 | 
			
		||||
            _imageGenerator = imageGenerator;
 | 
			
		||||
            _applicationPaths = applicationPaths;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -48,7 +48,7 @@ namespace Jellyfin.Api.Controllers
 | 
			
		||||
        private readonly ILogger<ImageController> _logger;
 | 
			
		||||
        private readonly IServerConfigurationManager _serverConfigurationManager;
 | 
			
		||||
        private readonly IApplicationPaths _appPaths;
 | 
			
		||||
        private readonly IImageGenerator _imageGenerator;
 | 
			
		||||
        private readonly IImageEncoder _imageEncoder;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="ImageController"/> class.
 | 
			
		||||
@ -62,7 +62,7 @@ namespace Jellyfin.Api.Controllers
 | 
			
		||||
        /// <param name="logger">Instance of the <see cref="ILogger{ImageController}"/> interface.</param>
 | 
			
		||||
        /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
 | 
			
		||||
        /// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
 | 
			
		||||
        /// <param name="imageGenerator">Instance of the <see cref="IImageGenerator"/> interface.</param>
 | 
			
		||||
        /// <param name="imageEncoder">Instance of the <see cref="IImageEncoder"/> interface.</param>
 | 
			
		||||
        public ImageController(
 | 
			
		||||
            IUserManager userManager,
 | 
			
		||||
            ILibraryManager libraryManager,
 | 
			
		||||
@ -73,7 +73,7 @@ namespace Jellyfin.Api.Controllers
 | 
			
		||||
            ILogger<ImageController> 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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
@ -1737,19 +1737,20 @@ namespace Jellyfin.Api.Controllers
 | 
			
		||||
            [FromQuery] string? foregroundLayer,
 | 
			
		||||
            [FromQuery, Range(0, 100)] int quality = 90)
 | 
			
		||||
        {
 | 
			
		||||
            string splashscreenPath;
 | 
			
		||||
            var brandingOptions = _serverConfigurationManager.GetConfiguration<BrandingOptions>("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();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -492,6 +492,14 @@ namespace Jellyfin.Drawing.Skia
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        public void CreateSplashscreen(IReadOnlyList<string> posters, IReadOnlyList<string> 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
 | 
			
		||||
 | 
			
		||||
@ -32,12 +32,12 @@ namespace Jellyfin.Drawing.Skia
 | 
			
		||||
        /// Generate a splashscreen.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="posters">The poster paths.</param>
 | 
			
		||||
        /// <param name="backdrop">The landscape paths.</param>
 | 
			
		||||
        /// <param name="backdrops">The landscape paths.</param>
 | 
			
		||||
        /// <param name="outputPath">The output path.</param>
 | 
			
		||||
        public void GenerateSplash(IReadOnlyList<string> posters, IReadOnlyList<string> backdrop, string outputPath)
 | 
			
		||||
        public void GenerateSplash(IReadOnlyList<string> posters, IReadOnlyList<string> 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.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="posters">The poster paths.</param>
 | 
			
		||||
        /// <param name="backdrop">The landscape paths.</param>
 | 
			
		||||
        /// <param name="backdrops">The landscape paths.</param>
 | 
			
		||||
        /// <returns>The created collage as a bitmap.</returns>
 | 
			
		||||
        private SKBitmap GenerateCollage(IReadOnlyList<string> posters, IReadOnlyList<string> backdrop)
 | 
			
		||||
        private SKBitmap GenerateCollage(IReadOnlyList<string> posters, IReadOnlyList<string> 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;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
@ -85,9 +85,6 @@ 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>();
 | 
			
		||||
 | 
			
		||||
@ -74,5 +74,12 @@ 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 new splashscreen image.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="posters">The list of poster paths.</param>
 | 
			
		||||
        /// <param name="backdrops">The list of backdrop paths.</param>
 | 
			
		||||
        void CreateSplashscreen(IReadOnlyList<string> posters, IReadOnlyList<string> backdrops);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,22 +0,0 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Controller.Drawing;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Interface for an image generator.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IImageGenerator
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets the supported generated images of the image generator.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>The supported generated image types.</returns>
 | 
			
		||||
    IReadOnlyList<GeneratedImageType> GetSupportedImages();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a splashscreen.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="imageTypeType">The image to generate.</param>
 | 
			
		||||
    /// <param name="outputPath">The path where the splashscreen should be saved.</param>
 | 
			
		||||
    void Generate(GeneratedImageType imageTypeType, string outputPath);
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user