From 586a81af64a18fd443d85a80a52c4e894b186f26 Mon Sep 17 00:00:00 2001 From: martimarkov Date: Mon, 26 Oct 2026 18:12:19 -0400 Subject: Backport pull request #18267 from jellyfin/release-12.z Do not store ratings or gains that are not finite numbers Original-merge: 02c49f03382f35c70bb2f730e4ed7dc950239b93 Merged-by: crobibero Backported-by: Cody Robibero --- MediaBrowser.Controller/Entities/BaseItem.cs | 41 +++++++++++++++++++--- .../Entities/BaseItemTests.cs | 22 ++++++++++++ .../Parsers/MovieNfoParserTests.cs | 15 ++++++++ .../Test Data/Ratings_NotFinite.nfo | 6 ++++ 4 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 tests/Jellyfin.XbmcMetadata.Tests/Test Data/Ratings_NotFinite.nfo diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 022f8f0fbf..a1e955f33f 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -102,6 +102,14 @@ namespace MediaBrowser.Controller.Entities private string _originalLanguage; + private float? _lufs; + + private float? _normalizationGain; + + private float? _criticRating; + + private float? _communityRating; + public const char SlugChar = '-'; protected BaseItem() @@ -154,14 +162,22 @@ namespace MediaBrowser.Controller.Entities /// /// The LUFS Value. [JsonIgnore] - public float? LUFS { get; set; } + public float? LUFS + { + get => _lufs; + set => _lufs = FiniteOrNull(value); + } /// /// Gets or sets the gain required for audio normalization. /// /// The gain required for audio normalization. [JsonIgnore] - public float? NormalizationGain { get; set; } + public float? NormalizationGain + { + get => _normalizationGain; + set => _normalizationGain = FiniteOrNull(value); + } /// /// Gets or sets the channel identifier. @@ -610,7 +626,11 @@ namespace MediaBrowser.Controller.Entities /// /// The critic rating. [JsonIgnore] - public float? CriticRating { get; set; } + public float? CriticRating + { + get => _criticRating; + set => _criticRating = FiniteOrNull(value); + } /// /// Gets or sets the custom rating. @@ -662,7 +682,11 @@ namespace MediaBrowser.Controller.Entities /// /// The community rating. [JsonIgnore] - public float? CommunityRating { get; set; } + public float? CommunityRating + { + get => _communityRating; + set => _communityRating = FiniteOrNull(value); + } /// /// Gets or sets the run time ticks. @@ -1375,6 +1399,15 @@ namespace MediaBrowser.Controller.Entities return string.Join('/', terms); } + /// + /// Treats a rating or gain that is not a finite number as no value, since NaN and infinity + /// cannot be stored by every database or written as JSON. + /// + /// The value to check. + /// when it is finite or has no value, otherwise . + private static float? FiniteOrNull(float? value) + => value.HasValue && !float.IsFinite(value.Value) ? null : value; + /// /// Derives the prefix shared by the supplied media source items' file names, used to strip the /// common part and surface a short version label per source. Returns null when there are fewer diff --git a/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs b/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs index c67717e2fa..cb95c9d204 100644 --- a/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs +++ b/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs @@ -91,6 +91,28 @@ public class BaseItemTests Assert.Equal(video.Id.ToString("N", CultureInfo.InvariantCulture), video.PresentationUniqueKey); } + [Theory] + [InlineData(float.NaN, null)] + [InlineData(float.PositiveInfinity, null)] + [InlineData(float.NegativeInfinity, null)] + [InlineData(-3.5f, -3.5f)] + [InlineData(null, null)] + public void RatingsAndGains_KeepOnlyFiniteValues(float? value, float? expected) + { + var item = new Video + { + CommunityRating = value, + CriticRating = value, + LUFS = value, + NormalizationGain = value + }; + + Assert.Equal(expected, item.CommunityRating); + Assert.Equal(expected, item.CriticRating); + Assert.Equal(expected, item.LUFS); + Assert.Equal(expected, item.NormalizationGain); + } + [Fact] public void GetItemByNameFolderName_ShortName_IsKeptAsIs() { diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs index 4142831c31..d884493cea 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs @@ -337,5 +337,20 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers Assert.Equal(7.5f, item.CommunityRating); } + + [Fact] + public void Parse_Ratings_NotFinite_Ignored() + { + var result = new MetadataResult