From 0c0d8244d0d6db8c501848589fd812ca285547e2 Mon Sep 17 00:00:00 2001 From: niksedk Date: Thu, 8 Oct 2026 06:18:43 +0200 Subject: [PATCH] ArteCheck: round the tolerated minimum duration to whole frames 25 frames - 15% = 21.25 frames: the message showed "21 fr" but the check compared against 850 ms, so 21 frames was reported. The accepted minimum is now frame-rounded once, so the displayed limit, the duration check, dialog splits and gap fixes agree: 21 frames accepted, 20 reported (matches Triathlon-rally's web checker). Co-Authored-By: Claude Opus 5.5 --- se5/ArteCheck.Tests/DurationToleranceTests.cs | 31 +++++++++++++++++++ se5/ArteCheck/ArteChecker.cs | 7 ++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 se5/ArteCheck.Tests/DurationToleranceTests.cs diff --git a/se5/ArteCheck.Tests/DurationToleranceTests.cs b/se5/ArteCheck.Tests/DurationToleranceTests.cs new file mode 100644 index 00000000..b01523ec --- /dev/null +++ b/se5/ArteCheck.Tests/DurationToleranceTests.cs @@ -0,0 +1,31 @@ +using SubtitleEdit.Plugins.ArteCheck; +using SubtitleEdit.Plugins.Shared; + +namespace ArteCheck.Tests; + +/// 25 fps, 25-frame minimum, 15% tolerance: 21.25 frames is shown as 21, so 21 frames is accepted. +public class DurationToleranceTests +{ + private const double Hour10 = 10 * 3_600_000.0; + + private static List Analyze(int frames) => ArteChecker.Analyze( + new[] { new PluginParagraph { StartMs = Hour10, EndMs = Hour10 + frames * ArteChecker.FrameMs, Text = "Hi", MarginV = "22" } }, + null, + new ArteOptions { EnabledChecks = new() { ArteCheckType.DisplayDuration } }); + + [Theory] + [InlineData(20, true)] + [InlineData(21, false)] + [InlineData(22, false)] + public void ToleratedMinimum_IsRoundedToWholeFrames(int frames, bool reported) + { + Assert.Equal(reported, Analyze(frames).Any(f => f.Kind == ArteFixKind.DisplayDuration)); + } + + [Fact] + public void ToleratedMinimum_MessageShowsTheLimitTheCheckUses() + { + var fix = Assert.Single(Analyze(20), f => f.Kind == ArteFixKind.DisplayDuration); + Assert.Contains("tolerated minimum of 0 s 21 fr", fix.Reason); + } +} diff --git a/se5/ArteCheck/ArteChecker.cs b/se5/ArteCheck/ArteChecker.cs index d2a02caa..4cf4f73d 100644 --- a/se5/ArteCheck/ArteChecker.cs +++ b/se5/ArteCheck/ArteChecker.cs @@ -281,10 +281,15 @@ private double RequiredDurationMs(string text) return Math.Max(_options.MinimumDurationMs, reading); } + /// + /// The shortest duration accepted, on whole frames - the limit the message shows (FormatDuration + /// rounds to frames) must be the one the check uses: 25 frames - 15% = 21.25 shows as 21 frames, + /// so 21 frames passes and 20 is reported (same as Triathlon-rally's web checker). + /// private double AcceptedMinimumDurationMs(string text) => _options.AcceptShortDurations ? _options.ShortMinimumFrames * FrameMs - : RequiredDurationMs(text) * Math.Max(0, 1.0 - _options.ReadingDurationTolerancePercent / 100.0); + : RoundToFrame(RequiredDurationMs(text) * Math.Max(0, 1.0 - _options.ReadingDurationTolerancePercent / 100.0)); private void AnalyzeDisplayDurations(List subtitle) {