diff --git a/zero/Media/ImageSharp/Processors/LoopWebProcessor.cs b/zero/Media/ImageSharp/Processors/LoopWebProcessor.cs
new file mode 100644
index 00000000..a08ea0b6
--- /dev/null
+++ b/zero/Media/ImageSharp/Processors/LoopWebProcessor.cs
@@ -0,0 +1,64 @@
+using Microsoft.Extensions.Logging;
+using SixLabors.ImageSharp.Processing;
+using SixLabors.ImageSharp.Web;
+using SixLabors.ImageSharp.Web.Commands;
+using SixLabors.ImageSharp.Web.Processors;
+using System.Globalization;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Formats.Gif;
+using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.Formats.Webp;
+
+namespace zero.Media.ImageSharp.Processors;
+
+public class LoopWebProcessor : IImageWebProcessor
+{
+ //
+ /// The command constant for the loop prop.
+ ///
+ public const string Loop = "loop";
+
+ ///
+ /// The reusable collection of commands.
+ ///
+ private static readonly IEnumerable LoopCommands = [Loop];
+
+ ///
+ public IEnumerable Commands { get; } = LoopCommands;
+
+ ///
+ public FormattedImage Process(
+ FormattedImage image,
+ ILogger logger,
+ CommandCollection commands,
+ CommandParser parser,
+ CultureInfo culture)
+ {
+ if (commands.Contains(Loop))
+ {
+ SixLabors.ImageSharp.Metadata.ImageMetadata metadata = image.Image.Metadata;
+ bool loop = parser.ParseValue(commands.GetValueOrDefault(Loop), culture);
+
+ if (!loop)
+ {
+ if (metadata.TryGetWebpMetadata(out WebpMetadata webpMetadata))
+ {
+ webpMetadata.RepeatCount = 1;
+ }
+ if (metadata.TryGetGifMetadata(out GifMetadata gifMetadata))
+ {
+ gifMetadata.RepeatCount = 1;
+ }
+ if (metadata.TryGetPngMetadata(out PngMetadata pngMetadata))
+ {
+ pngMetadata.RepeatCount = 1;
+ }
+ }
+ }
+
+ return image;
+ }
+
+ ///
+ public bool RequiresTrueColorPixelFormat(CommandCollection commands, CommandParser parser, CultureInfo culture) => true;
+}
diff --git a/zero/Media/ImageSharp/Processors/StripMetadataWebProcessor.cs b/zero/Media/ImageSharp/Processors/StripMetadataWebProcessor.cs
index f2d77384..be2413fc 100644
--- a/zero/Media/ImageSharp/Processors/StripMetadataWebProcessor.cs
+++ b/zero/Media/ImageSharp/Processors/StripMetadataWebProcessor.cs
@@ -16,8 +16,7 @@ public class StripMetadataWebProcessor : IImageWebProcessor
///
/// The reusable collection of commands.
///
- private static readonly IEnumerable StripCommands
- = new[] { Strip };
+ private static readonly IEnumerable StripCommands = [Strip];
///
public IEnumerable Commands { get; } = StripCommands;
diff --git a/zero/Media/ZeroMediaModule.cs b/zero/Media/ZeroMediaModule.cs
index 60c258d8..4e4efd51 100644
--- a/zero/Media/ZeroMediaModule.cs
+++ b/zero/Media/ZeroMediaModule.cs
@@ -22,7 +22,8 @@ internal class ZeroMediaModule : ZeroModule
.AddProvider()
.AddProcessor()
.AddProcessor()
- .AddProcessor();
+ .AddProcessor()
+ .AddProcessor();
services.AddOptions().Configure(opts =>
{