aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Localization/LocalizationManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/Localization/LocalizationManager.cs')
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs66
1 files changed, 59 insertions, 7 deletions
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index e7dd984ec4..3f89237ab2 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -139,7 +139,8 @@ namespace Emby.Server.Implementations.Localization
var ratingSystem = await JsonSerializer.DeserializeAsync<ParentalRatingSystem>(stream, _jsonOptions).ConfigureAwait(false)
?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'");
- var dict = new Dictionary<string, ParentalRatingScore?>();
+ // Rating strings are compared case insensitively, providers are not consistent about casing (e.g. "VM18" vs "vm18")
+ var dict = new Dictionary<string, ParentalRatingScore?>(StringComparer.OrdinalIgnoreCase);
if (ratingSystem.Ratings is not null)
{
foreach (var ratingEntry in ratingSystem.Ratings)
@@ -262,6 +263,24 @@ namespace Emby.Server.Implementations.Localization
}
/// <inheritdoc />
+ public string? GetLanguageDisplayName(string language)
+ {
+ if (string.IsNullOrEmpty(language))
+ {
+ return null;
+ }
+
+ var displayName = FindLanguageInfo(language)?.DisplayName;
+ if (displayName is null)
+ {
+ return null;
+ }
+
+ // Truncate at the first delimiter to avoid cluttered display names
+ return displayName.Split([';', ','], StringSplitOptions.None)[0].Trim();
+ }
+
+ /// <inheritdoc />
public IReadOnlyList<CountryInfo> GetCountries()
{
using var stream = _assembly.GetManifestResourceStream(CountriesPath) ?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'");
@@ -356,12 +375,37 @@ namespace Emby.Server.Implementations.Localization
{
ArgumentException.ThrowIfNullOrEmpty(rating);
+ // Handle unrated content. This has to happen before the split below,
+ // because some of the unrated values contain a '/' themselves (e.g. "n/a").
+ if (IsUnrated(rating))
+ {
+ 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)
{
+ // A single entry of such a list may be unrated while a later one still resolves
+ if (IsUnrated(ratingValue))
+ {
+ continue;
+ }
+
var score = GetSingleRatingScore(ratingValue, countryCode);
if (score is not null)
{
@@ -373,16 +417,18 @@ namespace Emby.Server.Implementations.Localization
}
/// <summary>
+ /// Checks whether a rating value marks the content as unrated.
+ /// </summary>
+ /// <param name="rating">Rating value to check.</param>
+ /// <returns>Returns true if the value is an unrated marker.</returns>
+ private static bool IsUnrated(ReadOnlySpan<char> rating)
+ => _unratedValues.Contains(rating.Trim(), StringComparison.OrdinalIgnoreCase);
+
+ /// <summary>
/// Resolves a single rating value to a score.
/// </summary>
private ParentalRatingScore? GetSingleRatingScore(string rating, string? countryCode)
{
- // Handle unrated content
- if (_unratedValues.Contains(rating.AsSpan(), StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
-
// Convert ints directly
// This may override some of the locale specific age ratings (but those always map to the same age)
if (TryParseRatingAsScore(rating, out var ratingAge))
@@ -494,6 +540,12 @@ namespace Emby.Server.Implementations.Localization
return true;
}
+ // Explicitly unrated content (e.g. "IT-NR") is unrated by definition, not a lookup failure
+ if (IsUnrated(ratingPart))
+ {
+ return true;
+ }
+
_logger.LogWarning(
"Rating '{Rating}' not found in the '{CountryCode}' rating system, treating as unrated",
rating,