diff --git a/ErsatzTV.Core.Tests/FFmpeg/WatermarkSelectorTests.cs b/ErsatzTV.Core.Tests/FFmpeg/WatermarkSelectorTests.cs index d647152ab..685c9192e 100644 --- a/ErsatzTV.Core.Tests/FFmpeg/WatermarkSelectorTests.cs +++ b/ErsatzTV.Core.Tests/FFmpeg/WatermarkSelectorTests.cs @@ -8,6 +8,7 @@ using NSubstitute; using NUnit.Framework; using Serilog; using Shouldly; +using Testably.Abstractions.Testing; namespace ErsatzTV.Core.Tests.FFmpeg; @@ -65,8 +66,18 @@ public class WatermarkSelectorTests var loggerFactory = new LoggerFactory().AddSerilog(Log.Logger); + // watermarks should always exist; effectively ignoring filesystem checks for now + var mockFileSystem = new MockFileSystem(); + mockFileSystem.Initialize() + .WithFile("/tmp/watermark"); + + var fakeImageCache = Substitute.For(); + fakeImageCache.GetPathForImage(Arg.Any(), Arg.Is(ArtworkKind.Watermark), Arg.Any>()) + .Returns(_ => "/tmp/watermark"); + WatermarkSelector = new WatermarkSelector( - Substitute.For(), + mockFileSystem, + fakeImageCache, new DecoSelector(loggerFactory.CreateLogger()), loggerFactory.CreateLogger()); diff --git a/ErsatzTV.Core/FFmpeg/WatermarkSelector.cs b/ErsatzTV.Core/FFmpeg/WatermarkSelector.cs index 10342a9f4..6519860c6 100644 --- a/ErsatzTV.Core/FFmpeg/WatermarkSelector.cs +++ b/ErsatzTV.Core/FFmpeg/WatermarkSelector.cs @@ -1,3 +1,4 @@ +using System.IO.Abstractions; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain.Filler; using ErsatzTV.Core.Domain.Scheduling; @@ -8,7 +9,11 @@ using Microsoft.Extensions.Logging; namespace ErsatzTV.Core.FFmpeg; -public class WatermarkSelector(IImageCache imageCache, IDecoSelector decoSelector, ILogger logger) +public class WatermarkSelector( + IFileSystem fileSystem, + IImageCache imageCache, + IDecoSelector decoSelector, + ILogger logger) : IWatermarkSelector { public List SelectWatermarks( @@ -170,10 +175,18 @@ public class WatermarkSelector(IImageCache imageCache, IDecoSelector decoSelecto { // used for song progress overlay case ChannelWatermarkImageSource.Resource: - return new WatermarkOptions( - watermark, - Path.Combine(FileSystemLayout.ResourcesCacheFolder, watermark.Image), - Option.None); + string resourcePath = fileSystem.Path.Combine( + FileSystemLayout.ResourcesCacheFolder, + watermark.Image); + if (fileSystem.File.Exists(resourcePath)) + { + return new WatermarkOptions(watermark, resourcePath, Option.None); + } + + logger.LogWarning( + "Watermark resource no longer exists at {Path} and will be ignored", + resourcePath); + return None; case ChannelWatermarkImageSource.Custom: // bad form validation makes this possible if (string.IsNullOrWhiteSpace(watermark.Image)) @@ -190,10 +203,16 @@ public class WatermarkSelector(IImageCache imageCache, IDecoSelector decoSelecto watermark.Image, ArtworkKind.Watermark, Option.None); - return new WatermarkOptions( - watermark, - customPath, - None); + + if (fileSystem.File.Exists(customPath)) + { + return new WatermarkOptions(watermark, customPath, None); + } + + logger.LogWarning( + "Custom watermark no longer exists at {Path} and will be ignored", + customPath); + return None; case ChannelWatermarkImageSource.ChannelLogo: logger.LogDebug("Watermark will come from playout item (channel logo)"); @@ -207,7 +226,15 @@ public class WatermarkSelector(IImageCache imageCache, IDecoSelector decoSelecto : imageCache.GetPathForImage(logoArtwork.Path, ArtworkKind.Logo, Option.None); } - return new WatermarkOptions(watermark, channelPath, None); + if (fileSystem.File.Exists(channelPath)) + { + return new WatermarkOptions(watermark, channelPath, None); + } + + logger.LogWarning( + "Channel logo no longer exists at {Path} and will be ignored", + channelPath); + return None; default: throw new NotSupportedException("Unsupported watermark image source"); } @@ -225,10 +252,16 @@ public class WatermarkSelector(IImageCache imageCache, IDecoSelector decoSelecto channel.Watermark.Image, ArtworkKind.Watermark, Option.None); - return new WatermarkOptions( - channel.Watermark, - customPath, - None); + + if (fileSystem.File.Exists(customPath)) + { + return new WatermarkOptions(channel.Watermark, customPath, None); + } + + logger.LogWarning( + "Custom watermark no longer exists at {Path} and will be ignored", + customPath); + return None; case ChannelWatermarkImageSource.ChannelLogo: logger.LogDebug("Watermark will come from channel (channel logo)"); @@ -242,7 +275,15 @@ public class WatermarkSelector(IImageCache imageCache, IDecoSelector decoSelecto : imageCache.GetPathForImage(logoArtwork.Path, ArtworkKind.Logo, Option.None); } - return new WatermarkOptions(channel.Watermark, channelPath, None); + if (fileSystem.File.Exists(channelPath)) + { + return new WatermarkOptions(channel.Watermark, channelPath, None); + } + + logger.LogWarning( + "Channel logo no longer exists at {Path} and will be ignored", + channelPath); + return None; default: throw new NotSupportedException("Unsupported watermark image source"); } @@ -260,10 +301,16 @@ public class WatermarkSelector(IImageCache imageCache, IDecoSelector decoSelecto watermark.Image, ArtworkKind.Watermark, Option.None); - return new WatermarkOptions( - watermark, - customPath, - None); + + if (fileSystem.File.Exists(customPath)) + { + return new WatermarkOptions(watermark, customPath, None); + } + + logger.LogWarning( + "Custom watermark no longer exists at {Path} and will be ignored", + customPath); + return None; case ChannelWatermarkImageSource.ChannelLogo: logger.LogDebug("Watermark will come from global (channel logo)"); @@ -277,7 +324,15 @@ public class WatermarkSelector(IImageCache imageCache, IDecoSelector decoSelecto : imageCache.GetPathForImage(logoArtwork.Path, ArtworkKind.Logo, Option.None); } - return new WatermarkOptions(watermark, channelPath, None); + if (fileSystem.File.Exists(channelPath)) + { + return new WatermarkOptions(watermark, channelPath, None); + } + + logger.LogWarning( + "Channel logo no longer exists at {Path} and will be ignored", + channelPath); + return None; default: throw new NotSupportedException("Unsupported watermark image source"); } diff --git a/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs b/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs index 7e14380ba..ff8e988a4 100644 --- a/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs +++ b/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs @@ -34,6 +34,7 @@ using NSubstitute; using NUnit.Framework; using Serilog; using Shouldly; +using Testably.Abstractions; using Testably.Abstractions.Testing; using MediaStream = ErsatzTV.Core.Domain.MediaStream; @@ -236,7 +237,7 @@ public class TranscodingTests StreamingMode streamingMode) { var localFileSystem = new LocalFileSystem( - new MockFileSystem(), + new RealFileSystem(), Substitute.For(), LoggerFactory.CreateLogger()); var fileSystem = new MockFileSystem(); @@ -367,6 +368,7 @@ public class TranscodingTests DateTimeOffset now = DateTimeOffset.Now; WatermarkSelector watermarkSelector = new WatermarkSelector( + new MockFileSystem(), mockImageCache, new DecoSelector(LoggerFactory.CreateLogger()), LoggerFactory.CreateLogger()); @@ -696,6 +698,7 @@ public class TranscodingTests .Returns(Path.Combine(TestContext.CurrentContext.TestDirectory, "Resources", "ErsatzTV.png")); WatermarkSelector watermarkSelector = new WatermarkSelector( + new RealFileSystem(), mockImageCache, new DecoSelector(LoggerFactory.CreateLogger()), LoggerFactory.CreateLogger());