From 3c423871d364ab84a5a1be5205ca827c4a450276 Mon Sep 17 00:00:00 2001 From: Qstick Date: Fri, 13 Sep 2019 21:47:18 -0400 Subject: [PATCH] Fixed: ImageResizer Tweaks Co-Authored-By: taloth Co-Authored-By: ta264 --- .../MediaCoverTests/ImageResizerFixture.cs | 23 ++++++++++-------- src/NzbDrone.Core/MediaCover/ImageResizer.cs | 24 ++++++++++++++++--- .../MediaCover/MediaCoverService.cs | 24 ++++++++++++++++++- src/NzbDrone.Test.Common/NzbDroneRunner.cs | 2 +- 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/NzbDrone.Core.Test/MediaCoverTests/ImageResizerFixture.cs b/src/NzbDrone.Core.Test/MediaCoverTests/ImageResizerFixture.cs index d1674d8b6..ea81b7d98 100644 --- a/src/NzbDrone.Core.Test/MediaCoverTests/ImageResizerFixture.cs +++ b/src/NzbDrone.Core.Test/MediaCoverTests/ImageResizerFixture.cs @@ -4,7 +4,9 @@ using FluentAssertions; using Moq; using NUnit.Framework; using NzbDrone.Common.Disk; +using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Test.Framework; +using SixLabors.ImageSharp; namespace NzbDrone.Core.Test.MediaCoverTests { @@ -14,13 +16,10 @@ namespace NzbDrone.Core.Test.MediaCoverTests [SetUp] public void SetUp() { - Mocker.GetMock() - .Setup(v => v.OpenReadStream(It.IsAny())) - .Returns(s => new FileStream(s, FileMode.Open)); - - Mocker.GetMock() - .Setup(v => v.OpenWriteStream(It.IsAny())) - .Returns(s => new FileStream(s, FileMode.Create)); + if (PlatformInfo.IsMono && PlatformInfo.GetVersion() < new Version(5, 8)) + { + Assert.Inconclusive("Not supported on Mono < 5.8"); + } Mocker.GetMock() .Setup(v => v.FileExists(It.IsAny())) @@ -29,6 +28,8 @@ namespace NzbDrone.Core.Test.MediaCoverTests Mocker.GetMock() .Setup(v => v.DeleteFile(It.IsAny())) .Callback(s => File.Delete(s)); + + Mocker.SetConstant(Mocker.Resolve()); } [Test] @@ -45,9 +46,11 @@ namespace NzbDrone.Core.Test.MediaCoverTests fileInfo.Exists.Should().BeTrue(); fileInfo.Length.Should().BeInRange(1000, 30000); - var image = System.Drawing.Image.FromFile(resizedFile); - image.Height.Should().Be(170); - image.Width.Should().Be(170); + using (var image = Image.Load(resizedFile)) + { + image.Height.Should().Be(170); + image.Width.Should().Be(170); + } } [Test] diff --git a/src/NzbDrone.Core/MediaCover/ImageResizer.cs b/src/NzbDrone.Core/MediaCover/ImageResizer.cs index 0b684d28e..0429c8d8a 100644 --- a/src/NzbDrone.Core/MediaCover/ImageResizer.cs +++ b/src/NzbDrone.Core/MediaCover/ImageResizer.cs @@ -1,6 +1,8 @@ using System; using NzbDrone.Common.Disk; +using NzbDrone.Common.EnvironmentInfo; using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Processing; using SixLabors.Memory; @@ -14,23 +16,39 @@ namespace NzbDrone.Core.MediaCover public class ImageResizer : IImageResizer { private readonly IDiskProvider _diskProvider; + private readonly bool _enabled; - public ImageResizer(IDiskProvider diskProvider) + public ImageResizer(IDiskProvider diskProvider, IPlatformInfo platformInfo) { _diskProvider = diskProvider; + // Random segfaults on mono 5.0 and 5.4 + if (PlatformInfo.IsMono && platformInfo.Version < new System.Version(5, 8)) + { + return; + } + + _enabled = true; + // More conservative memory allocation SixLabors.ImageSharp.Configuration.Default.MemoryAllocator = new SimpleGcMemoryAllocator(); + + // Thumbnails don't need super high quality + SixLabors.ImageSharp.Configuration.Default.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder + { + Quality = 92 + }); } public void Resize(string source, string destination, int height) { + if (!_enabled) return; + try { using (var image = Image.Load(source)) { - var width = (int)Math.Floor((double)image.Width * (double)height / (double)image.Height); - image.Mutate(x => x.Resize(width, height)); + image.Mutate(x => x.Resize(0, height)); image.Save(destination); } } diff --git a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs index db03637c5..99cc1090c 100644 --- a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs +++ b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; +using System.Threading; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; @@ -38,6 +39,11 @@ namespace NzbDrone.Core.MediaCover private readonly string _coverRootFolder; + // ImageSharp is slow on ARM (no hardware acceleration on mono yet) + // So limit the number of concurrent resizing tasks + private static SemaphoreSlim _semaphore = new SemaphoreSlim((int)Math.Ceiling(Environment.ProcessorCount / 2.0)); + + public MediaCoverService(IImageResizer resizer, IAlbumService albumService, IHttpClient httpClient, @@ -109,6 +115,8 @@ namespace NzbDrone.Core.MediaCover private void EnsureArtistCovers(Artist artist) { + var toResize = new List>(); + foreach (var cover in artist.Metadata.Value.Images) { var fileName = GetCoverPath(artist.Id, MediaCoverEntity.Artist, cover.CoverType, cover.Extension); @@ -134,7 +142,21 @@ namespace NzbDrone.Core.MediaCover _logger.Error(e, "Couldn't download media cover for {0}", artist); } - EnsureResizedCovers(artist, cover, !alreadyExists); + toResize.Add(Tuple.Create(cover, alreadyExists)); + } + + try + { + _semaphore.Wait(); + + foreach (var tuple in toResize) + { + EnsureResizedCovers(artist, tuple.Item1, !tuple.Item2); + } + } + finally + { + _semaphore.Release(); } } diff --git a/src/NzbDrone.Test.Common/NzbDroneRunner.cs b/src/NzbDrone.Test.Common/NzbDroneRunner.cs index fb78a522e..f61d5a2aa 100644 --- a/src/NzbDrone.Test.Common/NzbDroneRunner.cs +++ b/src/NzbDrone.Test.Common/NzbDroneRunner.cs @@ -76,7 +76,7 @@ namespace NzbDrone.Test.Common return; } - Console.WriteLine("Waiting for Lidarr to start. Response Status : {0} [{1}] {2}", statusCall.ResponseStatus, statusCall.StatusDescription, statusCall.ErrorException); + Console.WriteLine("Waiting for Lidarr to start. Response Status : {0} [{1}] {2}", statusCall.ResponseStatus, statusCall.StatusDescription, statusCall.ErrorException.Message); Thread.Sleep(500); }