diff --git a/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj b/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj index 867f66761..bdc224593 100644 --- a/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj +++ b/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj @@ -32,4 +32,9 @@ + + + + + diff --git a/ErsatzTV.Core.Tests/Images/ChannelLogoGeneratorTests.cs b/ErsatzTV.Core.Tests/Images/ChannelLogoGeneratorTests.cs new file mode 100644 index 000000000..09b6238be --- /dev/null +++ b/ErsatzTV.Core.Tests/Images/ChannelLogoGeneratorTests.cs @@ -0,0 +1,41 @@ +using ErsatzTV.Core.Images; +using Microsoft.Extensions.Logging.Abstractions; +using NUnit.Framework; +using Shouldly; +using SkiaSharp; + +namespace ErsatzTV.Core.Tests.Images; + +[TestFixture] +public class ChannelLogoGeneratorTests +{ + [TestCase("News", 100, 200)] + [TestCase("A Very Long Channel Name That Should Force Font Shrinking", 100, 200)] + [TestCase("Tall Logo", 180, 120)] + public void GenerateChannelLogo_WithInjectedAssets_ReturnsValidPngWithRequestedDimensions( + string text, + int logoHeight, + int logoWidth) + { + var generator = new ChannelLogoGenerator( + NullLogger.Instance, + TestAssetPath("Resources", "Images", "ersatztv-500.png"), + TestAssetPath("Resources", "Fonts", "Sen.ttf")); + + byte[] bytes = generator.GenerateChannelLogo(text, logoHeight, logoWidth, CancellationToken.None) + .Match( + Left: error => throw new AssertionException(error.Value), + Right: identity); + + using var stream = new MemoryStream(bytes); + using var codec = SKCodec.Create(stream); + + codec.ShouldNotBeNull(); + codec.EncodedFormat.ShouldBe(SKEncodedImageFormat.Png); + codec.Info.Width.ShouldBe(logoWidth); + codec.Info.Height.ShouldBe(logoHeight); + } + + private static string TestAssetPath(params string[] segments) => + Path.Combine([TestContext.CurrentContext.TestDirectory, .. segments]); +} diff --git a/ErsatzTV.Core/Images/ChannelLogoGenerator.cs b/ErsatzTV.Core/Images/ChannelLogoGenerator.cs index 4f6504a90..fae401afa 100644 --- a/ErsatzTV.Core/Images/ChannelLogoGenerator.cs +++ b/ErsatzTV.Core/Images/ChannelLogoGenerator.cs @@ -10,11 +10,25 @@ public class ChannelLogoGenerator : IChannelLogoGenerator public const string GetRoute = "/iptv/logos/gen"; public const string GetRouteQueryParamName = "text"; + private readonly string _fontPath; private readonly ILogger _logger; + private readonly string _overlayImagePath; public ChannelLogoGenerator( - ILogger logger) => + ILogger logger) + : this(logger, DefaultOverlayImagePath(), DefaultFontPath()) + { + } + + public ChannelLogoGenerator( + ILogger logger, + string overlayImagePath, + string fontPath) + { _logger = logger; + _overlayImagePath = overlayImagePath; + _fontPath = fontPath; + } public Either GenerateChannelLogo( string text, @@ -29,13 +43,11 @@ public class ChannelLogoGenerator : IChannelLogoGenerator canvas.Clear(SKColors.Black); //etv logo - string overlayImagePath = Path.Combine("wwwroot", "images", "ersatztv-500.png"); - using var overlayImage = SKBitmap.Decode(overlayImagePath); + using var overlayImage = SKBitmap.Decode(_overlayImagePath); canvas.DrawBitmap(overlayImage, new SKRect(155, 60, 205, 110)); //Custom Font - string fontPath = Path.Combine(FileSystemLayout.ResourcesCacheFolder, "Sen.ttf"); - using var fontTypeface = SKTypeface.FromFile(fontPath); + using var fontTypeface = SKTypeface.FromFile(_fontPath); var fontSize = 30; var font = new SKFont { @@ -80,4 +92,10 @@ public class ChannelLogoGenerator : IChannelLogoGenerator public static string GenerateChannelLogoUrl(Channel channel) => $"http://localhost:{Settings.StreamingPort}{GetRoute}?{GetRouteQueryParamName}={channel.WebEncodedName}"; + + private static string DefaultFontPath() => + Path.Combine(FileSystemLayout.ResourcesCacheFolder, "Sen.ttf"); + + private static string DefaultOverlayImagePath() => + Path.Combine("wwwroot", "images", "ersatztv-500.png"); }