From e25b669cc45358f64c900123ea6826c141aa70b8 Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Sun, 7 Sep 2025 09:20:11 -0500 Subject: [PATCH] fix scaling content in certain locales (#2389) --- CHANGELOG.md | 1 + ErsatzTV.FFmpeg.Tests/MediaStreamTests.cs | 110 ++++++++++++++++++++++ ErsatzTV.FFmpeg/MediaStream.cs | 8 +- 3 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 ErsatzTV.FFmpeg.Tests/MediaStreamTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aeb1d604..fddde1e5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - `local_infile=ON` is required when using MySQL (for bulk inserts when building playouts) - ETV will set this automatically when it has permission - When ETV does not have permission, startup will fail with logged instructions on how to configure MySql +- Fix scaling content in locales that don't use period as a decimal separator (e.g. `,`) ### Changed - **BREAKING CHANGE**: change how `Scripted Schedule` system works diff --git a/ErsatzTV.FFmpeg.Tests/MediaStreamTests.cs b/ErsatzTV.FFmpeg.Tests/MediaStreamTests.cs new file mode 100644 index 000000000..9e732e25d --- /dev/null +++ b/ErsatzTV.FFmpeg.Tests/MediaStreamTests.cs @@ -0,0 +1,110 @@ +using ErsatzTV.FFmpeg.Format; +using LanguageExt; +using NUnit.Framework; +using Shouldly; + +namespace ErsatzTV.FFmpeg.Tests; + +[TestFixture] +public class MediaStreamTests +{ + [Test] + [SetCulture("it-IT")] + public void SAR_0_0_DAR_4_3_Should_Not_Use_Comma_it_IT() + { + var mediaStream = new VideoStream( + 0, + "h264", + "main", + Option.None, + ColorParams.Default, + FrameSize.Unknown, + "0:0", + "4:3", + Option.None, + false, + ScanKind.Progressive); + + mediaStream.SampleAspectRatio.ShouldBe("1.333333333333:1"); + } + + [Test] + [SetCulture("en-US")] + public void SAR_0_0_DAR_4_3_Should_Not_Use_Comma_en_US() + { + var mediaStream = new VideoStream( + 0, + "h264", + "main", + Option.None, + ColorParams.Default, + FrameSize.Unknown, + "0:0", + "4:3", + Option.None, + false, + ScanKind.Progressive); + + mediaStream.SampleAspectRatio.ShouldBe("1.333333333333:1"); + } + + [Test] + [SetCulture("en-US")] + public void SAR_1_1_DAR_16_9_Should_Not_Use_Comma_en_US() + { + var mediaStream = new VideoStream( + 0, + "h264", + "main", + Option.None, + ColorParams.Default, + FrameSize.Unknown, + "1:1", + "16:9", + Option.None, + false, + ScanKind.Progressive); + + mediaStream.SampleAspectRatio.ShouldBe("1:1"); + } + + [Test] + [SetCulture("en-US")] + public void SAR_32_27_DAR_16_9_Should_Not_Use_Comma_en_US() + { + var mediaStream = new VideoStream( + 0, + "h264", + "main", + Option.None, + ColorParams.Default, + FrameSize.Unknown, + "32:27", + "16:9", + Option.None, + false, + ScanKind.Progressive); + + mediaStream.SampleAspectRatio.ShouldBe("32:27"); + } + + [Test] + [SetCulture("en-US")] + public void SAR_1point5_3point5_DAR_16_9_Should_Not_Use_Comma_en_US() + { + var mediaStream = new VideoStream( + 0, + "h264", + "main", + Option.None, + ColorParams.Default, + FrameSize.Unknown, + "1.5:3.5", + "16:9", + Option.None, + false, + ScanKind.Progressive); + + mediaStream.SampleAspectRatio.ShouldBe("1.5:3.5"); + } +} diff --git a/ErsatzTV.FFmpeg/MediaStream.cs b/ErsatzTV.FFmpeg/MediaStream.cs index efe498321..a64f2aa63 100644 --- a/ErsatzTV.FFmpeg/MediaStream.cs +++ b/ErsatzTV.FFmpeg/MediaStream.cs @@ -52,14 +52,18 @@ public record VideoStream( } double res = FrameSize.Width / (double)FrameSize.Height; - return $"{dar}:{res}"; + var formattedDar = string.Format(CultureInfo.InvariantCulture, dar % 1 == 0 ? "{0:F0}" : "{0:0.############}", dar); + var formattedRes = string.Format(CultureInfo.InvariantCulture, res % 1 == 0 ? "{0:F0}" : "{0:0.############}", res); + return $"{formattedDar}:{formattedRes}"; } { string[] split = MaybeSampleAspectRatio.Split(':'); var num = double.Parse(split[0], CultureInfo.InvariantCulture); var den = double.Parse(split[1], CultureInfo.InvariantCulture); - return $"{num}:{den}"; + var formattedNum = string.Format(CultureInfo.InvariantCulture, num % 1 == 0 ? "{0:F0}" : "{0:0.############}", num); + var formattedDen = string.Format(CultureInfo.InvariantCulture, den % 1 == 0 ? "{0:F0}" : "{0:0.############}", den); + return $"{formattedNum}:{formattedDen}"; } } }