Centralize Multilang field constants

This commit is contained in:
ajp_anton
2026-07-15 19:37:50 +00:00
parent 2968412374
commit 09a1b6f589
4 changed files with 161 additions and 188 deletions
@@ -3,6 +3,7 @@ using System.Diagnostics;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Nodes;
using static Jellyfin.Plugin.Multilang.MultilangConstants;
using Jellyfin.Plugin.Multilang.Configuration;
using Jellyfin.Plugin.Multilang.Data;
using Jellyfin.Plugin.Multilang.Rules;
@@ -450,11 +451,11 @@ public sealed class MultilangController : ControllerBase
var classificationFacts = GetClassificationFacts(rules, facts);
var category = PickCategory(rules, classificationFacts);
var title = ResolveField("title", GetActionList(rules, category, "title"), facts, byLang);
var title = ResolveField(TitleField, GetActionList(rules, category, TitleField), facts, byLang);
if (title.Change)
item["Name"] = title.Value ?? string.Empty;
var overview = ResolveField("overview", GetActionList(rules, category, "overview"), facts, byLang);
var overview = ResolveField(OverviewField, GetActionList(rules, category, OverviewField), facts, byLang);
if (overview.Change)
{
if (string.IsNullOrWhiteSpace(overview.Value))
@@ -463,16 +464,16 @@ public sealed class MultilangController : ControllerBase
item["Overview"] = overview.Value;
}
var tagline = ResolveField("tagline", GetActionList(rules, category, "tagline"), facts, byLang);
var tagline = ResolveField(TaglineField, GetActionList(rules, category, TaglineField), facts, byLang);
if (tagline.Change)
item["Taglines"] = string.IsNullOrWhiteSpace(tagline.Value) ? new JsonArray() : new JsonArray(tagline.Value);
assets.TryGetValue(itemId, out var assetsByKind);
ApplyImageAsset(item, "Primary", ResolveAsset("poster", GetActionList(rules, category, "poster"), assetsByKind));
ApplyImageAsset(item, "Logo", ResolveAsset("logo", GetActionList(rules, category, "logo"), assetsByKind));
ApplyImageAsset(item, "Banner", ResolveAsset("banner", GetActionList(rules, category, "banner"), assetsByKind));
ApplyImageAsset(item, "Thumb", ResolveAsset("thumb", GetActionList(rules, category, "thumb"), assetsByKind));
ApplyBackdropAsset(item, ResolveAsset("backdrop", GetActionList(rules, category, "backdrop"), assetsByKind));
ApplyImageAsset(item, "Primary", ResolveAsset(PosterKind, GetActionList(rules, category, PosterKind), assetsByKind));
ApplyImageAsset(item, "Logo", ResolveAsset(LogoKind, GetActionList(rules, category, LogoKind), assetsByKind));
ApplyImageAsset(item, "Banner", ResolveAsset(BannerKind, GetActionList(rules, category, BannerKind), assetsByKind));
ApplyImageAsset(item, "Thumb", ResolveAsset(ThumbKind, GetActionList(rules, category, ThumbKind), assetsByKind));
ApplyBackdropAsset(item, ResolveAsset(BackdropKind, GetActionList(rules, category, BackdropKind), assetsByKind));
ApplyGenres(item, facts, movieGenreNames, tvGenreNames);
}
}
@@ -503,15 +504,14 @@ public sealed class MultilangController : ControllerBase
private static void AddLanguageAction(List<string> langs, string action)
{
if (action.Equals("Original", StringComparison.OrdinalIgnoreCase))
if (action.Equals(OriginalAction, StringComparison.OrdinalIgnoreCase))
{
langs.Add("Original");
langs.Add(OriginalAction);
return;
}
const string prefix = "Language:";
if (action.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
langs.Add(action[prefix.Length..]);
if (action.StartsWith(LanguagePrefix, StringComparison.OrdinalIgnoreCase))
langs.Add(action[LanguagePrefix.Length..]);
}
private static UserCategoryRule? PickCategory(UserRulesDocument rules, FactsData facts)
@@ -637,30 +637,29 @@ public sealed class MultilangController : ControllerBase
{
foreach (var action in actions)
{
if (action.Equals("Jellyfin", StringComparison.OrdinalIgnoreCase))
if (action.Equals(JellyfinAction, StringComparison.OrdinalIgnoreCase))
return new ResolvedText(false, null);
if (field.Equals("title", StringComparison.OrdinalIgnoreCase) &&
action.Equals("Original", StringComparison.OrdinalIgnoreCase) &&
if (field.Equals(TitleField, StringComparison.OrdinalIgnoreCase) &&
action.Equals(OriginalAction, StringComparison.OrdinalIgnoreCase) &&
!string.IsNullOrWhiteSpace(facts.OriginalTitle))
return new ResolvedText(true, facts.OriginalTitle);
if (action.Equals("Original", StringComparison.OrdinalIgnoreCase))
if (action.Equals(OriginalAction, StringComparison.OrdinalIgnoreCase))
{
var originalValue = GetTranslatedField(byLang, "Original", field);
var originalValue = GetTranslatedField(byLang, OriginalAction, field);
if (!string.IsNullOrWhiteSpace(originalValue))
return new ResolvedText(true, originalValue);
continue;
}
const string prefix = "Language:";
if (!action.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
if (!action.StartsWith(LanguagePrefix, StringComparison.OrdinalIgnoreCase))
continue;
var value = GetTranslatedField(byLang, action[prefix.Length..], field);
var value = GetTranslatedField(byLang, action[LanguagePrefix.Length..], field);
if (!string.IsNullOrWhiteSpace(value))
return new ResolvedText(true, value);
}
return field.Equals("title", StringComparison.OrdinalIgnoreCase)
return field.Equals(TitleField, StringComparison.OrdinalIgnoreCase)
? new ResolvedText(false, null)
: new ResolvedText(true, string.Empty);
}
@@ -682,26 +681,25 @@ public sealed class MultilangController : ControllerBase
{
foreach (var action in actions)
{
if (action.Equals("Jellyfin", StringComparison.OrdinalIgnoreCase))
if (action.Equals(JellyfinAction, StringComparison.OrdinalIgnoreCase))
return new ResolvedAsset(false, null);
if (action.Equals("Original", StringComparison.OrdinalIgnoreCase))
if (action.Equals(OriginalAction, StringComparison.OrdinalIgnoreCase))
{
var originalValue = GetAsset(byKind, kind, "Original");
var originalValue = GetAsset(byKind, kind, OriginalAction);
if (!string.IsNullOrWhiteSpace(originalValue))
return new ResolvedAsset(true, originalValue);
continue;
}
const string prefix = "Language:";
if (!action.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
if (!action.StartsWith(LanguagePrefix, StringComparison.OrdinalIgnoreCase))
continue;
var value = GetAsset(byKind, kind, action[prefix.Length..]);
var value = GetAsset(byKind, kind, action[LanguagePrefix.Length..]);
if (!string.IsNullOrWhiteSpace(value))
return new ResolvedAsset(true, value);
}
return kind.Equals("poster", StringComparison.OrdinalIgnoreCase)
return kind.Equals(PosterKind, StringComparison.OrdinalIgnoreCase)
? new ResolvedAsset(false, null)
: new ResolvedAsset(true, string.Empty);
}
@@ -1193,7 +1191,7 @@ public sealed class MultilangController : ControllerBase
var ruleLanguages = GetNeededLanguages(rules);
var debugLanguages = configuredLanguages
.Concat(ruleLanguages)
.Append("Original")
.Append(OriginalAction)
.Where(lang => !string.IsNullOrWhiteSpace(lang))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
@@ -1211,20 +1209,19 @@ public sealed class MultilangController : ControllerBase
var effectiveSortLocale = ResolveSortLocale(rules.SortLocale, clientLocale);
var genreLocale = string.IsNullOrWhiteSpace(clientLocale) ? effectiveSortLocale : clientLocale;
string[] fields = ["title", "overview", "tagline", "poster", "logo", "banner", "thumb", "backdrop"];
var actionLists = fields.ToDictionary(
var actionLists = ActionFields.ToDictionary(
field => field,
field => GetActionList(rules, category, field).ToArray(),
StringComparer.OrdinalIgnoreCase);
var title = TraceResolveField("title", actionLists["title"], facts, byLang);
var overview = TraceResolveField("overview", actionLists["overview"], facts, byLang);
var tagline = TraceResolveField("tagline", actionLists["tagline"], facts, byLang);
var poster = TraceResolveAsset("poster", actionLists["poster"], byKind);
var logo = TraceResolveAsset("logo", actionLists["logo"], byKind);
var banner = TraceResolveAsset("banner", actionLists["banner"], byKind);
var thumb = TraceResolveAsset("thumb", actionLists["thumb"], byKind);
var backdrop = TraceResolveAsset("backdrop", actionLists["backdrop"], byKind);
var title = TraceResolveField(TitleField, actionLists[TitleField], facts, byLang);
var overview = TraceResolveField(OverviewField, actionLists[OverviewField], facts, byLang);
var tagline = TraceResolveField(TaglineField, actionLists[TaglineField], facts, byLang);
var poster = TraceResolveAsset(PosterKind, actionLists[PosterKind], byKind);
var logo = TraceResolveAsset(LogoKind, actionLists[LogoKind], byKind);
var banner = TraceResolveAsset(BannerKind, actionLists[BannerKind], byKind);
var thumb = TraceResolveAsset(ThumbKind, actionLists[ThumbKind], byKind);
var backdrop = TraceResolveAsset(BackdropKind, actionLists[BackdropKind], byKind);
var genres = ResolveGenresForDebug(facts, genreLocale);
return Ok(new
@@ -1239,8 +1236,8 @@ public sealed class MultilangController : ControllerBase
ConfiguredLanguages = configuredLanguages,
RuleLanguages = ruleLanguages,
DebugLanguages = debugLanguages,
MatchedCategory = categoryMatch.Matched ?? new { Id = "fallback", Label = "Fallback" } as object,
EffectiveCategory = category ?? new { Id = "fallback", Label = "Fallback" } as object,
MatchedCategory = categoryMatch.Matched ?? new { Id = "fallback", Label = FallbackAction } as object,
EffectiveCategory = category ?? new { Id = "fallback", Label = FallbackAction } as object,
DuplicateCategoryLabelResolved = categoryMatch.DuplicateLabelResolved,
CategoryEvaluation = new
{
@@ -1311,15 +1308,15 @@ public sealed class MultilangController : ControllerBase
var attempts = new List<DebugResolutionAttempt>();
foreach (var action in actions)
{
if (action.Equals("Jellyfin", StringComparison.OrdinalIgnoreCase))
if (action.Equals(JellyfinAction, StringComparison.OrdinalIgnoreCase))
{
var result = new ResolvedText(false, null);
attempts.Add(new DebugResolutionAttempt(action, "Jellyfin", string.Empty, true, true, null, "Use Jellyfin value"));
return new DebugTextResolution(field, actions.ToArray(), attempts.ToArray(), result, "Jellyfin", "Jellyfin action reached");
attempts.Add(new DebugResolutionAttempt(action, JellyfinAction, string.Empty, true, true, null, "Use Jellyfin value"));
return new DebugTextResolution(field, actions.ToArray(), attempts.ToArray(), result, JellyfinAction, "Jellyfin action reached");
}
if (field.Equals("title", StringComparison.OrdinalIgnoreCase) &&
action.Equals("Original", StringComparison.OrdinalIgnoreCase))
if (field.Equals(TitleField, StringComparison.OrdinalIgnoreCase) &&
action.Equals(OriginalAction, StringComparison.OrdinalIgnoreCase))
{
var hasOriginalTitle = !string.IsNullOrWhiteSpace(facts.OriginalTitle);
attempts.Add(new DebugResolutionAttempt(action, "OriginalTitle", "facts.original_title", hasOriginalTitle, hasOriginalTitle, facts.OriginalTitle, hasOriginalTitle ? "Original title found" : "Original title missing"));
@@ -1332,9 +1329,9 @@ public sealed class MultilangController : ControllerBase
continue;
}
if (action.Equals("Original", StringComparison.OrdinalIgnoreCase))
if (action.Equals(OriginalAction, StringComparison.OrdinalIgnoreCase))
{
var originalValue = GetTranslatedField(byLang, "Original", field);
var originalValue = GetTranslatedField(byLang, OriginalAction, field);
var hasOriginalValue = !string.IsNullOrWhiteSpace(originalValue);
attempts.Add(new DebugResolutionAttempt(action, "Translation", "Original/" + field, hasOriginalValue, hasOriginalValue, originalValue, hasOriginalValue ? "Original-language translation found" : "Original-language translation missing"));
if (hasOriginalValue)
@@ -1346,14 +1343,13 @@ public sealed class MultilangController : ControllerBase
continue;
}
const string prefix = "Language:";
if (!action.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
if (!action.StartsWith(LanguagePrefix, StringComparison.OrdinalIgnoreCase))
{
attempts.Add(new DebugResolutionAttempt(action, "Unknown", string.Empty, false, false, null, "Unsupported action token"));
continue;
}
var lang = action[prefix.Length..];
var lang = action[LanguagePrefix.Length..];
var value = GetTranslatedField(byLang, lang, field);
var hasValue = !string.IsNullOrWhiteSpace(value);
attempts.Add(new DebugResolutionAttempt(action, "Translation", lang + "/" + field, hasValue, hasValue, value, hasValue ? "Translation found" : "Translation missing or empty"));
@@ -1364,13 +1360,13 @@ public sealed class MultilangController : ControllerBase
}
}
var fallback = field.Equals("title", StringComparison.OrdinalIgnoreCase)
var fallback = field.Equals(TitleField, StringComparison.OrdinalIgnoreCase)
? new ResolvedText(false, null)
: new ResolvedText(true, string.Empty);
var reason = field.Equals("title", StringComparison.OrdinalIgnoreCase)
var reason = field.Equals(TitleField, StringComparison.OrdinalIgnoreCase)
? "No action had data; title falls back to Jellyfin"
: "No action had data; field is cleared";
return new DebugTextResolution(field, actions.ToArray(), attempts.ToArray(), fallback, field.Equals("title", StringComparison.OrdinalIgnoreCase) ? "Jellyfin" : "Clear", reason);
return new DebugTextResolution(field, actions.ToArray(), attempts.ToArray(), fallback, field.Equals(TitleField, StringComparison.OrdinalIgnoreCase) ? JellyfinAction : ClearAction, reason);
}
private static DebugAssetResolution TraceResolveAsset(
@@ -1381,16 +1377,16 @@ public sealed class MultilangController : ControllerBase
var attempts = new List<DebugResolutionAttempt>();
foreach (var action in actions)
{
if (action.Equals("Jellyfin", StringComparison.OrdinalIgnoreCase))
if (action.Equals(JellyfinAction, StringComparison.OrdinalIgnoreCase))
{
var result = new ResolvedAsset(false, null);
attempts.Add(new DebugResolutionAttempt(action, "Jellyfin", string.Empty, true, true, null, "Use Jellyfin image"));
return new DebugAssetResolution(kind, actions.ToArray(), attempts.ToArray(), result, "Jellyfin", "Jellyfin action reached");
attempts.Add(new DebugResolutionAttempt(action, JellyfinAction, string.Empty, true, true, null, "Use Jellyfin image"));
return new DebugAssetResolution(kind, actions.ToArray(), attempts.ToArray(), result, JellyfinAction, "Jellyfin action reached");
}
if (action.Equals("Original", StringComparison.OrdinalIgnoreCase))
if (action.Equals(OriginalAction, StringComparison.OrdinalIgnoreCase))
{
var originalValue = GetAsset(byKind, kind, "Original");
var originalValue = GetAsset(byKind, kind, OriginalAction);
var hasOriginalValue = !string.IsNullOrWhiteSpace(originalValue);
attempts.Add(new DebugResolutionAttempt(action, "Asset", kind + "/Original", hasOriginalValue, hasOriginalValue, originalValue, hasOriginalValue ? "Original-language asset found" : "Original-language asset missing"));
if (hasOriginalValue)
@@ -1402,14 +1398,13 @@ public sealed class MultilangController : ControllerBase
continue;
}
const string prefix = "Language:";
if (!action.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
if (!action.StartsWith(LanguagePrefix, StringComparison.OrdinalIgnoreCase))
{
attempts.Add(new DebugResolutionAttempt(action, "Unknown", string.Empty, false, false, null, "Unsupported action token"));
continue;
}
var lang = action[prefix.Length..];
var lang = action[LanguagePrefix.Length..];
var value = GetAsset(byKind, kind, lang);
var hasValue = !string.IsNullOrWhiteSpace(value);
attempts.Add(new DebugResolutionAttempt(action, "Asset", kind + "/" + lang, hasValue, hasValue, value, hasValue ? "Asset found" : "Asset missing"));
@@ -1420,13 +1415,13 @@ public sealed class MultilangController : ControllerBase
}
}
var fallback = kind.Equals("poster", StringComparison.OrdinalIgnoreCase)
var fallback = kind.Equals(PosterKind, StringComparison.OrdinalIgnoreCase)
? new ResolvedAsset(false, null)
: new ResolvedAsset(true, string.Empty);
var reason = kind.Equals("poster", StringComparison.OrdinalIgnoreCase)
var reason = kind.Equals(PosterKind, StringComparison.OrdinalIgnoreCase)
? "No action had data; poster falls back to Jellyfin"
: "No action had data; image is cleared";
return new DebugAssetResolution(kind, actions.ToArray(), attempts.ToArray(), fallback, kind.Equals("poster", StringComparison.OrdinalIgnoreCase) ? "Jellyfin" : "Clear", reason);
return new DebugAssetResolution(kind, actions.ToArray(), attempts.ToArray(), fallback, kind.Equals(PosterKind, StringComparison.OrdinalIgnoreCase) ? JellyfinAction : ClearAction, reason);
}
private static object[] BuildMissingDataReport(
@@ -1434,18 +1429,16 @@ public sealed class MultilangController : ControllerBase
Dictionary<string, Dictionary<string, string>>? byLang,
Dictionary<string, Dictionary<string, string>>? byKind)
{
string[] textFields = ["title", "overview", "tagline"];
string[] assetKinds = ["poster", "logo", "banner", "thumb", "backdrop"];
return configuredLanguages
.Where(lang => !string.IsNullOrWhiteSpace(lang))
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(lang => lang, StringComparer.OrdinalIgnoreCase)
.Select(lang =>
{
var missingText = textFields
var missingText = TextFields
.Where(field => string.IsNullOrWhiteSpace(GetTranslatedField(byLang, lang, field)))
.ToArray();
var missingAssets = assetKinds
var missingAssets = AssetKinds
.Where(kind => string.IsNullOrWhiteSpace(GetAsset(byKind, kind, lang)))
.ToArray();
return new
@@ -1462,7 +1455,7 @@ public sealed class MultilangController : ControllerBase
private static object DebugTextOutput(ResolvedText text)
=> new
{
Source = text.Change ? "Multilang" : "Jellyfin",
Source = text.Change ? "Multilang" : JellyfinAction,
Value = text.Value,
ClearsValue = text.Change && string.IsNullOrWhiteSpace(text.Value)
};
@@ -1470,7 +1463,7 @@ public sealed class MultilangController : ControllerBase
private static object DebugAssetOutput(ResolvedAsset asset)
=> new
{
Source = asset.Change ? "Multilang" : "Jellyfin",
Source = asset.Change ? "Multilang" : JellyfinAction,
Value = asset.Change && !string.IsNullOrWhiteSpace(asset.Value) ? ToImageTag(asset.Value) : asset.Value,
ClearsValue = asset.Change && string.IsNullOrWhiteSpace(asset.Value)
};
@@ -1675,20 +1668,20 @@ public sealed class MultilangController : ControllerBase
DefaultActionLists().Keys.All(field =>
actions.TryGetValue(field, out var list) &&
list.Length == 1 &&
list[0].Equals("Jellyfin", StringComparison.OrdinalIgnoreCase));
list[0].Equals(JellyfinAction, StringComparison.OrdinalIgnoreCase));
private static bool HasMeaningfulLegacyActions(IReadOnlyDictionary<string, string> legacyActions)
=> legacyActions.Values.Any(action =>
!string.IsNullOrWhiteSpace(action) &&
!action.Equals("Fallback", StringComparison.OrdinalIgnoreCase) &&
!action.Equals("Jellyfin", StringComparison.OrdinalIgnoreCase));
!action.Equals(FallbackAction, StringComparison.OrdinalIgnoreCase) &&
!action.Equals(JellyfinAction, StringComparison.OrdinalIgnoreCase));
private static string[] LegacyActionToList(string field, IReadOnlyDictionary<string, string>? legacyActions)
{
if (legacyActions is null || !legacyActions.TryGetValue(field, out var action))
return ["Jellyfin"];
if (string.IsNullOrWhiteSpace(action) || action.Equals("Fallback", StringComparison.OrdinalIgnoreCase))
return ["Jellyfin"];
return [JellyfinAction];
if (string.IsNullOrWhiteSpace(action) || action.Equals(FallbackAction, StringComparison.OrdinalIgnoreCase))
return [JellyfinAction];
return [action];
}
@@ -1714,24 +1707,23 @@ public sealed class MultilangController : ControllerBase
private static string NormalizeAction(string field, string? raw, IReadOnlySet<string> allowedLanguages)
{
var action = (raw ?? string.Empty).Trim();
if (action.Equals("Jellyfin", StringComparison.OrdinalIgnoreCase) ||
action.Equals("Fallback", StringComparison.OrdinalIgnoreCase))
if (action.Equals(JellyfinAction, StringComparison.OrdinalIgnoreCase) ||
action.Equals(FallbackAction, StringComparison.OrdinalIgnoreCase))
return action;
if (action.Equals("Original", StringComparison.OrdinalIgnoreCase))
return "Original";
if ((field is "overview" or "tagline" or "logo") &&
action.Equals("Clear", StringComparison.OrdinalIgnoreCase))
return "Clear";
const string prefix = "Language:";
if (action.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
if (action.Equals(OriginalAction, StringComparison.OrdinalIgnoreCase))
return OriginalAction;
if ((field is OverviewField or TaglineField or LogoKind) &&
action.Equals(ClearAction, StringComparison.OrdinalIgnoreCase))
return ClearAction;
if (action.StartsWith(LanguagePrefix, StringComparison.OrdinalIgnoreCase))
{
var lang = action[prefix.Length..].Trim();
var lang = action[LanguagePrefix.Length..].Trim();
var canonical = allowedLanguages.FirstOrDefault(l => l.Equals(lang, StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrWhiteSpace(canonical))
return prefix + canonical;
return LanguagePrefix + canonical;
}
return "Fallback";
return FallbackAction;
}
private static string[] NormalizeActionList(string field, IEnumerable<string>? raw, IReadOnlySet<string> allowedLanguages)
@@ -1747,11 +1739,10 @@ public sealed class MultilangController : ControllerBase
result.Add(normalized);
}
if (field.Equals("title", StringComparison.OrdinalIgnoreCase) ||
field.Equals("poster", StringComparison.OrdinalIgnoreCase))
if (RequiresJellyfinFallback(field))
{
if (!result.Contains("Jellyfin", StringComparer.OrdinalIgnoreCase))
result.Add("Jellyfin");
if (!result.Contains(JellyfinAction, StringComparer.OrdinalIgnoreCase))
result.Add(JellyfinAction);
}
return result.ToArray();
@@ -1761,48 +1752,21 @@ public sealed class MultilangController : ControllerBase
{
var action = (raw ?? string.Empty).Trim();
if (action.Equals("J", StringComparison.OrdinalIgnoreCase))
return "Jellyfin";
return JellyfinAction;
if (action.Equals("O", StringComparison.OrdinalIgnoreCase))
return "Original";
if (action.Equals("Jellyfin", StringComparison.OrdinalIgnoreCase))
return "Jellyfin";
if (action.Equals("Original", StringComparison.OrdinalIgnoreCase))
return "Original";
return OriginalAction;
if (action.Equals(JellyfinAction, StringComparison.OrdinalIgnoreCase))
return JellyfinAction;
if (action.Equals(OriginalAction, StringComparison.OrdinalIgnoreCase))
return OriginalAction;
const string prefix = "Language:";
var lang = action.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)
? action[prefix.Length..].Trim()
var lang = action.StartsWith(LanguagePrefix, StringComparison.OrdinalIgnoreCase)
? action[LanguagePrefix.Length..].Trim()
: action;
var canonical = allowedLanguages.FirstOrDefault(l => l.Equals(lang, StringComparison.OrdinalIgnoreCase));
return string.IsNullOrWhiteSpace(canonical) ? string.Empty : prefix + canonical;
return string.IsNullOrWhiteSpace(canonical) ? string.Empty : LanguagePrefix + canonical;
}
private static Dictionary<string, string> DefaultFieldActions()
=> new(StringComparer.OrdinalIgnoreCase)
{
["title"] = "Fallback",
["overview"] = "Fallback",
["tagline"] = "Fallback",
["poster"] = "Fallback",
["logo"] = "Fallback",
["banner"] = "Fallback",
["thumb"] = "Fallback",
["backdrop"] = "Fallback"
};
private static Dictionary<string, string[]> DefaultActionLists()
=> new(StringComparer.OrdinalIgnoreCase)
{
["title"] = ["Jellyfin"],
["overview"] = ["Jellyfin"],
["tagline"] = ["Jellyfin"],
["poster"] = ["Jellyfin"],
["logo"] = ["Jellyfin"],
["banner"] = ["Jellyfin"],
["thumb"] = ["Jellyfin"],
["backdrop"] = ["Jellyfin"]
};
private static IReadOnlyList<string> GetActionList(UserRulesDocument rules, UserCategoryRule? category, string field)
{
var actions = category is null ? rules.FallbackFieldActions : category.FieldActionLists;
@@ -1812,7 +1776,7 @@ public sealed class MultilangController : ControllerBase
if (category is not null && category.FieldActions is not null)
return LegacyActionToList(field, category.FieldActions);
return ["Jellyfin"];
return [JellyfinAction];
}
private sealed record ProxyRequest(