diff --git a/CHANGELOG.md b/CHANGELOG.md index f6c297d57..079f27c2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Use NVIDIA NvEnc API to detect encoder capability instead of heuristic based on GPU model/architecture - Use NVIDIA Cuvid API to detect decoder capability instead of heuristic based on GPU model/architecture - Fix filler expression not being respected when using a playlist as filler +- Use "repeat count" metadata from animated GIFs in graphics engine (i.e. watermarks) + - GIFs flagged to loop forever will loop forever + - GIFs with a specific loop count will loop the specified number of times and then hold the final frame + - Note that looping is relative to the start of the content, so this works best with permanent watermarks ### Changed - Filler presets: use separate text fields for `hours`, `minutes` and `seconds` duration diff --git a/ErsatzTV.Application/Configuration/Commands/UpdateLibraryRefreshIntervalHandler.cs b/ErsatzTV.Application/Configuration/Commands/UpdateLibraryRefreshIntervalHandler.cs index 5027ddeb5..90db6d47b 100644 --- a/ErsatzTV.Application/Configuration/Commands/UpdateLibraryRefreshIntervalHandler.cs +++ b/ErsatzTV.Application/Configuration/Commands/UpdateLibraryRefreshIntervalHandler.cs @@ -26,6 +26,6 @@ public class UpdateLibraryRefreshIntervalHandler : Optional(request.LibraryRefreshInterval) .Where(lri => lri is >= 0 and < 1_000_000) .Map(_ => Unit.Default) - .ToValidation("Library refresh interval must be zero or greated") + .ToValidation("Library refresh interval must be zero or greater") .AsTask(); } diff --git a/ErsatzTV.Infrastructure/Streaming/Graphics/Image/ImageElementBase.cs b/ErsatzTV.Infrastructure/Streaming/Graphics/Image/ImageElementBase.cs index 304bd2e26..f8dacfc35 100644 --- a/ErsatzTV.Infrastructure/Streaming/Graphics/Image/ImageElementBase.cs +++ b/ErsatzTV.Infrastructure/Streaming/Graphics/Image/ImageElementBase.cs @@ -19,6 +19,7 @@ public abstract class ImageElementBase : GraphicsElement, IDisposable private readonly List _frameDelays = []; private readonly List _scaledFrames = []; private double _animatedDurationSeconds; + private ushort _repeatCount; private Image _sourceImage; @@ -83,6 +84,11 @@ public abstract class ImageElementBase : GraphicsElement, IDisposable horizontalMargin, verticalMargin); + if (_sourceImage.Metadata.DecodedImageFormat == GifFormat.Instance) + { + _repeatCount = _sourceImage.Metadata.GetFormatMetadata(GifFormat.Instance).RepeatCount; + } + _animatedDurationSeconds = 0; for (var i = 0; i < _sourceImage.Frames.Count; i++) @@ -161,6 +167,11 @@ public abstract class ImageElementBase : GraphicsElement, IDisposable return _scaledFrames[0]; } + if (_repeatCount > 0 && timestamp.TotalSeconds >= _animatedDurationSeconds * _repeatCount) + { + return _scaledFrames.Last(); + } + double currentTime = timestamp.TotalSeconds % _animatedDurationSeconds; double frameTime = 0;