From ed2280a0605c7e971d151b9b2239d332ee204579 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sun, 9 Oct 2022 22:56:23 +0200 Subject: Overhaul content ratings --- .../Localization/LocalizationManagerTests.cs | 25 ++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs') diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index 16eb7a75c..256bde819 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -83,11 +83,11 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(23, ratings.Count); + Assert.Equal(50, ratings.Count); var tvma = ratings.FirstOrDefault(x => x.Name.Equals("TV-MA", StringComparison.Ordinal)); Assert.NotNull(tvma); - Assert.Equal(9, tvma!.Value); + Assert.Equal(17, tvma!.Value); } [Fact] @@ -100,21 +100,21 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(10, ratings.Count); + Assert.Equal(12, ratings.Count); var fsk = ratings.FirstOrDefault(x => x.Name.Equals("FSK-12", StringComparison.Ordinal)); Assert.NotNull(fsk); - Assert.Equal(7, fsk!.Value); + Assert.Equal(12, fsk!.Value); } [Theory] - [InlineData("CA-R", "CA", 10)] - [InlineData("FSK-16", "DE", 8)] - [InlineData("FSK-18", "DE", 9)] - [InlineData("FSK-18", "US", 9)] - [InlineData("TV-MA", "US", 9)] - [InlineData("XXX", "asdf", 100)] - [InlineData("Germany: FSK-18", "DE", 9)] + [InlineData("CA-R", "CA", 18)] + [InlineData("FSK-16", "DE", 16)] + [InlineData("FSK-18", "DE", 18)] + [InlineData("FSK-18", "US", 18)] + [InlineData("TV-MA", "US", 17)] + [InlineData("XXX", "asdf", 1000)] + [InlineData("Germany: FSK-18", "DE", 18)] public async Task GetRatingLevel_GivenValidString_Success(string value, string countryCode, int expectedLevel) { var localizationManager = Setup(new ServerConfiguration() @@ -135,6 +135,9 @@ namespace Jellyfin.Server.Implementations.Tests.Localization UICulture = "de-DE" }); await localizationManager.LoadAll(); + Assert.Null(localizationManager.GetRatingLevel("NR")); + Assert.Null(localizationManager.GetRatingLevel("unrated")); + Assert.Null(localizationManager.GetRatingLevel("Not Rated")); Assert.Null(localizationManager.GetRatingLevel("n/a")); } -- cgit v1.2.3 From 9d21f078c71373d956893d2d298cc4d7fcc0adf1 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Tue, 29 Nov 2022 22:04:28 +0100 Subject: Add default rating selections --- .../Localization/LocalizationManager.cs | 59 +++++++++++++++++++--- MediaBrowser.Model/Entities/ParentalRating.cs | 4 +- .../Localization/LocalizationManagerTests.cs | 4 +- 3 files changed, 57 insertions(+), 10 deletions(-) (limited to 'tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 0e19e80a8..ca7c2c21f 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -87,12 +87,10 @@ namespace Emby.Server.Implementations.Localization var name = parts[0]; dict.Add(name, new ParentalRating(name, value)); } -#if DEBUG else { _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); } -#endif } _allParentalRatings[countryCode] = dict; @@ -185,7 +183,56 @@ namespace Emby.Server.Implementations.Localization /// public IEnumerable GetParentalRatings() - => GetParentalRatingsDictionary().Values; + { + var ratings = GetParentalRatingsDictionary().Values.ToList(); + + // Add common ratings to ensure them being available for selection. + // Based on the US rating system due to it being the main source of rating in the metadata providers + // Minimum rating possible + if (!ratings.Any(x => x.Value == 0)) + { + ratings.Add(new ParentalRating("Approved", 0)); + } + + // Matches PG (this has differnet age restrictions depending on country) + if (!ratings.Any(x => x.Value == 10)) + { + ratings.Add(new ParentalRating("10", 10)); + } + + // Matches PG-13 + if (!ratings.Any(x => x.Value == 13)) + { + ratings.Add(new ParentalRating("13", 13)); + } + + // Matches TV-14 + if (!ratings.Any(x => x.Value == 14)) + { + ratings.Add(new ParentalRating("14", 14)); + } + + // Catchall if max rating of country is less than 21 + // Using 21 instead of 18 to be sure to allow access to all rated content except adult and banned + if (!ratings.Any(x => x.Value >= 21)) + { + ratings.Add(new ParentalRating("21", 21)); + } + + // A lot of countries don't excplicitly have a seperate rating for adult content + if (!ratings.Any(x => x.Value == 1000)) + { + ratings.Add(new ParentalRating("XXX", 1000)); + } + + // A lot of countries don't excplicitly have a seperate rating for banned content + if (!ratings.Any(x => x.Value == 1001)) + { + ratings.Add(new ParentalRating("Banned", 1001)); + } + + return ratings.OrderBy(r => r.Value); + } /// /// Gets the parental ratings dictionary. @@ -207,15 +254,15 @@ namespace Emby.Server.Implementations.Localization } /// - /// Gets the ratings. + /// Gets the ratings for a country. /// /// The country code. /// The ratings. private Dictionary? GetRatings(string countryCode) { - _allParentalRatings.TryGetValue(countryCode, out var value); + _allParentalRatings.TryGetValue(countryCode, out var countryValue); - return value; + return countryValue; } /// diff --git a/MediaBrowser.Model/Entities/ParentalRating.cs b/MediaBrowser.Model/Entities/ParentalRating.cs index 17b2868a3..c92640818 100644 --- a/MediaBrowser.Model/Entities/ParentalRating.cs +++ b/MediaBrowser.Model/Entities/ParentalRating.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Model.Entities { } - public ParentalRating(string name, int value) + public ParentalRating(string name, int? value) { Name = name; Value = value; @@ -28,6 +28,6 @@ namespace MediaBrowser.Model.Entities /// Gets or sets the value. /// /// The value. - public int Value { get; set; } + public int? Value { get; set; } } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index 256bde819..ab3682ccf 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -83,7 +83,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(50, ratings.Count); + Assert.Equal(53, ratings.Count); var tvma = ratings.FirstOrDefault(x => x.Name.Equals("TV-MA", StringComparison.Ordinal)); Assert.NotNull(tvma); @@ -100,7 +100,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(12, ratings.Count); + Assert.Equal(18, ratings.Count); var fsk = ratings.FirstOrDefault(x => x.Name.Equals("FSK-12", StringComparison.Ordinal)); Assert.NotNull(fsk); -- cgit v1.2.3 From daefdaf8b0867d8f46056c80ecd19a94c5d7561e Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Thu, 9 Mar 2023 14:13:57 +0100 Subject: Extend language code handling --- .../Localization/LocalizationManager.cs | 80 +++++++++++++--------- .../Globalization/ILocalizationManager.cs | 3 +- .../Localization/LocalizationManagerTests.cs | 4 +- 3 files changed, 53 insertions(+), 34 deletions(-) (limited to 'tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 6e2a33fd5..166b71b4a 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -184,10 +184,19 @@ namespace Emby.Server.Implementations.Localization /// public IEnumerable GetParentalRatings() { - var ratings = GetParentalRatingsDictionary().Values.ToList(); + // Use server default language for ratings + // Fall back to empty list if there are no parental ratings for that language + var ratings = GetParentalRatingsDictionary()?.Values.ToList() + ?? new List(); - // Add common ratings to ensure them being available for selection. + // Add common ratings to ensure them being available for selection // Based on the US rating system due to it being the main source of rating in the metadata providers + // Unrated + if (!ratings.Any(x => x.Value is null)) + { + ratings.Add(new ParentalRating("Unrated", null)); + } + // Minimum rating possible if (!ratings.Any(x => x.Value == 0)) { @@ -237,36 +246,26 @@ namespace Emby.Server.Implementations.Localization /// /// Gets the parental ratings dictionary. /// + /// The optional two letter ISO language string. /// . - private Dictionary GetParentalRatingsDictionary() + private Dictionary? GetParentalRatingsDictionary(string? countryCode = null) { - var countryCode = _configurationManager.Configuration.MetadataCountryCode; - - // Fall back to US ratings if no country code is specified or country code does not exist. + // Fallback to server default if no country code is specified. if (string.IsNullOrEmpty(countryCode)) { - countryCode = "us"; + countryCode = _configurationManager.Configuration.MetadataCountryCode; } - return GetRatings(countryCode) - ?? GetRatings("us") - ?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'"); - } - - /// - /// Gets the ratings for a country. - /// - /// The country code. - /// The ratings. - private Dictionary? GetRatings(string countryCode) - { - _allParentalRatings.TryGetValue(countryCode, out var countryValue); + if (_allParentalRatings.TryGetValue(countryCode, out var countryValue)) + { + return countryValue; + } - return countryValue; + return null; } /// - public int? GetRatingLevel(string rating) + public int? GetRatingLevel(string rating, string? countryCode = null) { ArgumentException.ThrowIfNullOrEmpty(rating); @@ -280,32 +279,51 @@ namespace Emby.Server.Implementations.Localization rating = rating.Replace("Rated :", string.Empty, StringComparison.OrdinalIgnoreCase); rating = rating.Replace("Rated ", string.Empty, StringComparison.OrdinalIgnoreCase); - var ratingsDictionary = GetParentalRatingsDictionary(); - - if (ratingsDictionary.TryGetValue(rating, out ParentalRating? value)) + // Use rating system matching the language + if (!string.IsNullOrEmpty(countryCode)) + { + var ratingsDictionary = GetParentalRatingsDictionary(countryCode); + if (ratingsDictionary is not null && ratingsDictionary.TryGetValue(rating, out ParentalRating? value)) + { + return value.Value; + } + } + else { - return value.Value; + // Fall back to server default language for ratings check + // If it has no ratings, use the US ratings + var ratingsDictionary = GetParentalRatingsDictionary() ?? GetParentalRatingsDictionary("us"); + if (ratingsDictionary is not null && ratingsDictionary.TryGetValue(rating, out ParentalRating? value)) + { + return value.Value; + } } - // If we don't find anything check all ratings systems + // If we don't find anything, check all ratings systems foreach (var dictionary in _allParentalRatings.Values) { - if (dictionary.TryGetValue(rating, out value)) + if (dictionary.TryGetValue(rating, out var value)) { return value.Value; } } - // Try splitting by : to handle "Germany: FSK 18" + // Try splitting by : to handle "Germany: FSK-18" if (rating.Contains(':', StringComparison.OrdinalIgnoreCase)) { return GetRatingLevel(rating.AsSpan().RightPart(':').ToString()); } - // Remove prefix country code to handle "DE-18" + // Handle prefix country code to handle "DE-18" if (rating.Contains('-', StringComparison.OrdinalIgnoreCase)) { - return GetRatingLevel(rating.AsSpan().RightPart('-').ToString()); + var ratingSpan = rating.AsSpan(); + + // Extract culture from country prefix + var culture = FindLanguageInfo(ratingSpan.LeftPart('-').ToString()); + + // Check rating system of culture + return GetRatingLevel(ratingSpan.RightPart('-').ToString(), culture?.TwoLetterISOLanguageName); } return null; diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs index e00157dce..02a29e7fa 100644 --- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs +++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs @@ -30,8 +30,9 @@ namespace MediaBrowser.Model.Globalization /// Gets the rating level. /// /// The rating. + /// The optional two letter ISO language string. /// or null. - int? GetRatingLevel(string rating); + int? GetRatingLevel(string rating, string? countryCode = null); /// /// Gets the localized string. diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index ab3682ccf..7fabe9904 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -83,7 +83,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(53, ratings.Count); + Assert.Equal(54, ratings.Count); var tvma = ratings.FirstOrDefault(x => x.Name.Equals("TV-MA", StringComparison.Ordinal)); Assert.NotNull(tvma); @@ -100,7 +100,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(18, ratings.Count); + Assert.Equal(19, ratings.Count); var fsk = ratings.FirstOrDefault(x => x.Name.Equals("FSK-12", StringComparison.Ordinal)); Assert.NotNull(fsk); -- cgit v1.2.3