using System.Globalization; using System.Text.RegularExpressions; namespace AmiReel.Services; public sealed record FfmpegOutputLine(string Line, TimeSpan? Time, double? FramesPerSecond, double? Speed, int? Frame); /// /// Parses a single line of FFmpeg stdout/stderr for the `time=`/`frame=`/`fps=`/`speed=` /// progress fields FFmpeg prints while encoding. /// public static partial class FfmpegProgressParser { public static FfmpegOutputLine Parse(string line) { TimeSpan? time = null; double? fps = null; double? speed = null; int? frame = null; Match timeMatch = TimeRegex().Match(line); if (timeMatch.Success && TimeSpan.TryParse(timeMatch.Groups[1].Value, CultureInfo.InvariantCulture, out TimeSpan parsedTime)) time = parsedTime; Match frameMatch = FrameRegex().Match(line); if (frameMatch.Success) { if (int.TryParse(frameMatch.Groups["frame"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int parsedFrame)) frame = parsedFrame; if (double.TryParse(frameMatch.Groups["fps"].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double parsedFps)) fps = parsedFps; if (double.TryParse(frameMatch.Groups["speed"].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double parsedSpeed)) speed = parsedSpeed; } return new FfmpegOutputLine(line, time, fps, speed, frame); } [GeneratedRegex(@"time=(\d{2}:\d{2}:\d{2}(?:\.\d+)?)", RegexOptions.CultureInvariant)] private static partial Regex TimeRegex(); [GeneratedRegex(@"frame=\s*(?\d+).*?fps=\s*(?\d+(?:\.\d+)?).*?speed=\s*(?\d+(?:\.\d+)?)x", RegexOptions.CultureInvariant)] private static partial Regex FrameRegex(); }