From da3031628fda013be20ae4f89906882cc5d72e14 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Tue, 15 Sep 2026 11:16:22 -0400 Subject: Backport pull request #18002 from jellyfin/release-12.z Fix slashed rating handling Original-merge: 4405fccda92426b01903e3fe4c5985f1d7c30957 Merged-by: crobibero Backported-by: Cody Robibero --- .../Localization/LocalizationManager.cs | 12 ++++++++++ .../Entities/AggregateFolderTests.cs | 1 + .../Entities/BaseItemTests.cs | 1 + .../Localization/LocalizationManagerTests.cs | 26 ++++++++++++++++++++++ 4 files changed, 40 insertions(+) diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index c77717c76e..3f89237ab2 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -382,9 +382,21 @@ namespace Emby.Server.Implementations.Localization return null; } + // Several rating systems contain a '/' inside a single rating (e.g. "M/12" in PT, + // "U/A 13+" in IN, "7/i/fig" in ES), so the value as a whole always wins over the split below. + var wholeValueScore = GetSingleRatingScore(rating, countryCode); + if (wholeValueScore is not null) + { + return wholeValueScore; + } + // Some providers may list multiple ratings separated by '/' (e.g. "SE:15 / SE:15+ / SE:Från 15 år"). // Try each one in order and use the first that resolves. var ratingValues = rating.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + if (ratingValues.Length == 1) + { + return null; + } foreach (var ratingValue in ratingValues) { diff --git a/tests/Jellyfin.Controller.Tests/Entities/AggregateFolderTests.cs b/tests/Jellyfin.Controller.Tests/Entities/AggregateFolderTests.cs index 6c297673d4..272c434fe9 100644 --- a/tests/Jellyfin.Controller.Tests/Entities/AggregateFolderTests.cs +++ b/tests/Jellyfin.Controller.Tests/Entities/AggregateFolderTests.cs @@ -9,6 +9,7 @@ using Xunit; namespace Jellyfin.Controller.Tests.Entities; +[Collection("LibraryManagerTests")] public class AggregateFolderTests { [Fact] diff --git a/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs b/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs index f363968909..588f9d5530 100644 --- a/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs +++ b/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs @@ -27,6 +27,7 @@ using Xunit; namespace Jellyfin.Controller.Tests.Entities; +[Collection("LibraryManagerTests")] public class BaseItemTests { [Fact] diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index 221f1d8ec8..93014e7244 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -296,6 +296,32 @@ namespace Jellyfin.Server.Implementations.Tests.Localization Assert.Equal(expectedSubScore, score.SubScore); } + [Theory] + // Ratings that contain a '/' themselves must not be split into a list of ratings + [InlineData("M/3", "pt", 3, null)] + [InlineData("M/12", "pt", 12, null)] + [InlineData("M/18", "pt", 18, null)] + [InlineData("PT-M/12", "pt", 12, null)] // TMDB style country prefix + [InlineData("M/12", "us", 12, null)] // Resolved through the all-systems fallback + [InlineData("U/A 13+", "in", 13, null)] + [InlineData("7/i", "es", 11, null)] + [InlineData("7/i/fig", "es", 11, null)] + [InlineData("18/fig", "es", 18, null)] + public async Task GetRatingScore_RatingContainingSlash_IsNotSplit(string value, string countryCode, int expectedScore, int? expectedSubScore) + { + var localizationManager = Setup(new ServerConfiguration + { + MetadataCountryCode = countryCode + }); + await localizationManager.LoadAll(); + + var score = localizationManager.GetRatingScore(value); + + Assert.NotNull(score); + Assert.Equal(expectedScore, score.Score); + Assert.Equal(expectedSubScore, score.SubScore); + } + [Theory] [InlineData("-NO RATING SHOWN-")] [InlineData(":NO RATING SHOWN:")] -- cgit v1.2.3