Browse Source

fix: don't ignore channel logo watermarks that are urls (#2961)

pull/2962/head
Jason Dove 4 days ago committed by GitHub
parent
commit
15db3c4ca0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      CHANGELOG.md
  2. 130
      ErsatzTV.Core.Tests/FFmpeg/WatermarkSelectorChannelLogoTests.cs
  3. 9
      ErsatzTV.Core/FFmpeg/WatermarkSelector.cs

3
CHANGELOG.md

@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Fixed
- Fix regression from `v26.2.0` that caused channel logo watermarks to be ignored when the logo is a url
- This affected external logo urls and generated channel logos
## [26.7.0] - 2026-07-27 ## [26.7.0] - 2026-07-27
### Added ### Added

130
ErsatzTV.Core.Tests/FFmpeg/WatermarkSelectorChannelLogoTests.cs

@ -0,0 +1,130 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.Images;
using ErsatzTV.Core.Scheduling;
using Microsoft.Extensions.Logging.Abstractions;
using NSubstitute;
using NUnit.Framework;
using Shouldly;
using Testably.Abstractions.Testing;
namespace ErsatzTV.Core.Tests.FFmpeg;
[TestFixture]
public class WatermarkSelectorChannelLogoTests
{
private const string CachedLogoPath = "/tmp/logo";
private static WatermarkSelector Selector()
{
var mockFileSystem = new MockFileSystem();
mockFileSystem.Initialize().WithFile(CachedLogoPath);
var fakeImageCache = Substitute.For<IImageCache>();
fakeImageCache.GetPathForImage(Arg.Any<string>(), Arg.Is(ArtworkKind.Logo), Arg.Any<Option<int>>())
.Returns(_ => CachedLogoPath);
return new WatermarkSelector(
mockFileSystem,
fakeImageCache,
new DecoSelector(),
NullLogger<WatermarkSelector>.Instance);
}
private static Channel ChannelWithLogo(params Artwork[] artwork) =>
new(Guid.Empty)
{
Id = 0,
Name = "Test Channel",
StreamingMode = StreamingMode.TransportStream,
Artwork = [..artwork],
Watermark = new ChannelWatermark
{
Id = 1,
Name = "Channel",
ImageSource = ChannelWatermarkImageSource.ChannelLogo
}
};
[Test]
public void Should_Use_External_Url_Channel_Logo()
{
const string ExternalUrl = "https://example.com/images/logo.png";
Channel channel = ChannelWithLogo(new Artwork { ArtworkKind = ArtworkKind.Logo, Path = ExternalUrl });
Option<WatermarkOptions> result = Selector().GetWatermarkOptions(
channel,
Option<ChannelWatermark>.None,
Option<ChannelWatermark>.None,
true);
result.IsSome.ShouldBeTrue();
foreach (WatermarkOptions options in result)
{
options.ImagePath.ShouldBe(ExternalUrl);
}
}
[Test]
public void Should_Use_Generated_Channel_Logo_Url_When_No_Logo_Artwork()
{
Channel channel = ChannelWithLogo();
Option<WatermarkOptions> result = Selector().GetWatermarkOptions(
channel,
Option<ChannelWatermark>.None,
Option<ChannelWatermark>.None,
true);
result.IsSome.ShouldBeTrue();
foreach (WatermarkOptions options in result)
{
options.ImagePath.ShouldContain("/iptv/logos/gen");
}
}
[Test]
public void Should_Use_Cached_Channel_Logo()
{
Channel channel = ChannelWithLogo(new Artwork { ArtworkKind = ArtworkKind.Logo, Path = "logo.png" });
Option<WatermarkOptions> result = Selector().GetWatermarkOptions(
channel,
Option<ChannelWatermark>.None,
Option<ChannelWatermark>.None,
true);
result.IsSome.ShouldBeTrue();
foreach (WatermarkOptions options in result)
{
options.ImagePath.ShouldBe(CachedLogoPath);
}
}
[Test]
public void Should_Ignore_Missing_Cached_Channel_Logo()
{
var mockFileSystem = new MockFileSystem();
var fakeImageCache = Substitute.For<IImageCache>();
fakeImageCache.GetPathForImage(Arg.Any<string>(), Arg.Is(ArtworkKind.Logo), Arg.Any<Option<int>>())
.Returns(_ => "/tmp/missing-logo");
var selector = new WatermarkSelector(
mockFileSystem,
fakeImageCache,
new DecoSelector(),
NullLogger<WatermarkSelector>.Instance);
Channel channel = ChannelWithLogo(new Artwork { ArtworkKind = ArtworkKind.Logo, Path = "logo.png" });
Option<WatermarkOptions> result = selector.GetWatermarkOptions(
channel,
Option<ChannelWatermark>.None,
Option<ChannelWatermark>.None,
true);
result.IsNone.ShouldBeTrue();
}
}

9
ErsatzTV.Core/FFmpeg/WatermarkSelector.cs

@ -238,7 +238,8 @@ public class WatermarkSelector(
: imageCache.GetPathForImage(logoArtwork.Path, ArtworkKind.Logo, Option<int>.None); : imageCache.GetPathForImage(logoArtwork.Path, ArtworkKind.Logo, Option<int>.None);
} }
if (fileSystem.File.Exists(channelPath)) // urls (external logos, generated channel logos) can't be checked on the file system
if (Artwork.IsExternalUrl(channelPath) || fileSystem.File.Exists(channelPath))
{ {
return new WatermarkOptions(watermark, channelPath, None); return new WatermarkOptions(watermark, channelPath, None);
} }
@ -287,7 +288,8 @@ public class WatermarkSelector(
: imageCache.GetPathForImage(logoArtwork.Path, ArtworkKind.Logo, Option<int>.None); : imageCache.GetPathForImage(logoArtwork.Path, ArtworkKind.Logo, Option<int>.None);
} }
if (fileSystem.File.Exists(channelPath)) // urls (external logos, generated channel logos) can't be checked on the file system
if (Artwork.IsExternalUrl(channelPath) || fileSystem.File.Exists(channelPath))
{ {
return new WatermarkOptions(channel.Watermark, channelPath, None); return new WatermarkOptions(channel.Watermark, channelPath, None);
} }
@ -336,7 +338,8 @@ public class WatermarkSelector(
: imageCache.GetPathForImage(logoArtwork.Path, ArtworkKind.Logo, Option<int>.None); : imageCache.GetPathForImage(logoArtwork.Path, ArtworkKind.Logo, Option<int>.None);
} }
if (fileSystem.File.Exists(channelPath)) // urls (external logos, generated channel logos) can't be checked on the file system
if (Artwork.IsExternalUrl(channelPath) || fileSystem.File.Exists(channelPath))
{ {
return new WatermarkOptions(watermark, channelPath, None); return new WatermarkOptions(watermark, channelPath, None);
} }

Loading…
Cancel
Save