From e1e18e8da015e7311e62cdb62167d51e90331edd Mon Sep 17 00:00:00 2001 From: Erik W <22211983+Lampan-git@users.noreply.github.com> Date: Thu, 7 May 2026 20:07:23 +0200 Subject: Add OriginalLanguage as option to PreferredAudioLanguage (#12579) * Add OriginalLanguage as option to PreferredAudioLanguage * Support for multiple original languages * Add original audio stream indicator * Fetch OriginalLanguage from TMDB * Adapt to EFCore refactor * Fix PlayDefaultAudioTrack OriginalLanguage behavior * Fix better PlayDefaultAudioTrack OriginalLanguage behavior * Add comment to ItemFields * Improved PlayDefaultAudioTrack behavior * Add migration for original language * Use sting.Equals for string comparisons * Always set dto OriginalLanguage * Remove OriginalLanguage from ItemFields --------- Co-authored-by: Lampan-git --- MediaBrowser.Model/Dto/BaseItemDto.cs | 2 + MediaBrowser.Model/Entities/MediaStream.cs | 243 +++++++++++++++-------------- 2 files changed, 130 insertions(+), 115 deletions(-) (limited to 'MediaBrowser.Model') diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index e96bba0464..062034327e 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -800,5 +800,7 @@ namespace MediaBrowser.Model.Dto /// /// The current program. public BaseItemDto CurrentProgram { get; set; } + + public string OriginalLanguage { get; set; } } } diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 4491fb5ace..dad4a6e149 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -260,6 +260,8 @@ namespace MediaBrowser.Model.Entities public string LocalizedLanguage { get; set; } + public string LocalizedOriginal { get; set; } + public string DisplayTitle { get @@ -267,162 +269,167 @@ namespace MediaBrowser.Model.Entities switch (Type) { case MediaStreamType.Audio: - { - var attributes = new List(); - - // Do not display the language code in display titles if unset or set to a special code. Show it in all other cases (possibly expanded). - if (!string.IsNullOrEmpty(Language) && !_specialCodes.Contains(Language, StringComparison.OrdinalIgnoreCase)) { - // Use pre-resolved localized language name, falling back to raw language code. - attributes.Add(StringHelper.FirstToUpper(LocalizedLanguage ?? Language)); - } + var attributes = new List(); - if (!string.IsNullOrEmpty(Profile) && !string.Equals(Profile, "lc", StringComparison.OrdinalIgnoreCase)) - { - attributes.Add(Profile); - } - else if (!string.IsNullOrEmpty(Codec)) - { - attributes.Add(AudioCodec.GetFriendlyName(Codec)); - } + // Do not display the language code in display titles if unset or set to a special code. Show it in all other cases (possibly expanded). + if (!string.IsNullOrEmpty(Language) && !_specialCodes.Contains(Language, StringComparison.OrdinalIgnoreCase)) + { + // Use pre-resolved localized language name, falling back to raw language code. + attributes.Add(StringHelper.FirstToUpper(LocalizedLanguage ?? Language)); + } - if (!string.IsNullOrEmpty(ChannelLayout)) - { - attributes.Add(StringHelper.FirstToUpper(ChannelLayout)); - } - else if (Channels.HasValue) - { - attributes.Add(Channels.Value.ToString(CultureInfo.InvariantCulture) + " ch"); - } + if (!string.IsNullOrEmpty(Profile) && !string.Equals(Profile, "lc", StringComparison.OrdinalIgnoreCase)) + { + attributes.Add(Profile); + } + else if (!string.IsNullOrEmpty(Codec)) + { + attributes.Add(AudioCodec.GetFriendlyName(Codec)); + } - if (IsDefault) - { - attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault); - } + if (!string.IsNullOrEmpty(ChannelLayout)) + { + attributes.Add(StringHelper.FirstToUpper(ChannelLayout)); + } + else if (Channels.HasValue) + { + attributes.Add(Channels.Value.ToString(CultureInfo.InvariantCulture) + " ch"); + } - if (IsExternal) - { - attributes.Add(string.IsNullOrEmpty(LocalizedExternal) ? "External" : LocalizedExternal); - } + if (IsDefault) + { + attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault); + } - if (!string.IsNullOrEmpty(Title)) - { - var result = new StringBuilder(Title); - foreach (var tag in attributes) + if (IsExternal) + { + attributes.Add(string.IsNullOrEmpty(LocalizedExternal) ? "External" : LocalizedExternal); + } + + if (IsOriginal) + { + attributes.Add(string.IsNullOrEmpty(LocalizedOriginal) ? "Original" : LocalizedOriginal); + } + + if (!string.IsNullOrEmpty(Title)) { - // Keep Tags that are not already in Title. - if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase)) + var result = new StringBuilder(Title); + foreach (var tag in attributes) { - result.Append(" - ").Append(tag); + // Keep Tags that are not already in Title. + if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase)) + { + result.Append(" - ").Append(tag); + } } + + return result.ToString(); } - return result.ToString(); + return string.Join(" - ", attributes); } - return string.Join(" - ", attributes); - } - case MediaStreamType.Video: - { - var attributes = new List(); + { + var attributes = new List(); - var resolutionText = GetResolutionText(); + var resolutionText = GetResolutionText(); - if (!string.IsNullOrEmpty(resolutionText)) - { - attributes.Add(resolutionText); - } + if (!string.IsNullOrEmpty(resolutionText)) + { + attributes.Add(resolutionText); + } - if (!string.IsNullOrEmpty(Codec)) - { - attributes.Add(Codec.ToUpperInvariant()); - } + if (!string.IsNullOrEmpty(Codec)) + { + attributes.Add(Codec.ToUpperInvariant()); + } - if (VideoDoViTitle is not null) - { - attributes.Add(VideoDoViTitle); - } - else if (VideoRange != VideoRange.Unknown) - { - attributes.Add(VideoRange.ToString()); - } + if (VideoDoViTitle is not null) + { + attributes.Add(VideoDoViTitle); + } + else if (VideoRange != VideoRange.Unknown) + { + attributes.Add(VideoRange.ToString()); + } - if (!string.IsNullOrEmpty(Title)) - { - var result = new StringBuilder(Title); - foreach (var tag in attributes) + if (!string.IsNullOrEmpty(Title)) { - // Keep Tags that are not already in Title. - if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase)) + var result = new StringBuilder(Title); + foreach (var tag in attributes) { - result.Append(" - ").Append(tag); + // Keep Tags that are not already in Title. + if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase)) + { + result.Append(" - ").Append(tag); + } } + + return result.ToString(); } - return result.ToString(); + return string.Join(' ', attributes); } - return string.Join(' ', attributes); - } - case MediaStreamType.Subtitle: - { - var attributes = new List(); - - if (!string.IsNullOrEmpty(Language)) - { - // Use pre-resolved localized language name, falling back to raw language code. - attributes.Add(StringHelper.FirstToUpper(LocalizedLanguage ?? Language)); - } - else { - attributes.Add(string.IsNullOrEmpty(LocalizedUndefined) ? "Und" : LocalizedUndefined); - } + var attributes = new List(); - if (IsHearingImpaired == true) - { - attributes.Add(string.IsNullOrEmpty(LocalizedHearingImpaired) ? "Hearing Impaired" : LocalizedHearingImpaired); - } + if (!string.IsNullOrEmpty(Language)) + { + // Use pre-resolved localized language name, falling back to raw language code. + attributes.Add(StringHelper.FirstToUpper(LocalizedLanguage ?? Language)); + } + else + { + attributes.Add(string.IsNullOrEmpty(LocalizedUndefined) ? "Und" : LocalizedUndefined); + } - if (IsDefault) - { - attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault); - } + if (IsHearingImpaired == true) + { + attributes.Add(string.IsNullOrEmpty(LocalizedHearingImpaired) ? "Hearing Impaired" : LocalizedHearingImpaired); + } - if (IsForced) - { - attributes.Add(string.IsNullOrEmpty(LocalizedForced) ? "Forced" : LocalizedForced); - } + if (IsDefault) + { + attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault); + } - if (!string.IsNullOrEmpty(Codec)) - { - attributes.Add(Codec.ToUpperInvariant()); - } + if (IsForced) + { + attributes.Add(string.IsNullOrEmpty(LocalizedForced) ? "Forced" : LocalizedForced); + } - if (IsExternal) - { - attributes.Add(string.IsNullOrEmpty(LocalizedExternal) ? "External" : LocalizedExternal); - } + if (!string.IsNullOrEmpty(Codec)) + { + attributes.Add(Codec.ToUpperInvariant()); + } - if (!string.IsNullOrEmpty(Title)) - { - var result = new StringBuilder(Title); - foreach (var tag in attributes) + if (IsExternal) + { + attributes.Add(string.IsNullOrEmpty(LocalizedExternal) ? "External" : LocalizedExternal); + } + + if (!string.IsNullOrEmpty(Title)) { - // Keep Tags that are not already in Title. - if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase)) + var result = new StringBuilder(Title); + foreach (var tag in attributes) { - result.Append(" - ").Append(tag); + // Keep Tags that are not already in Title. + if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase)) + { + result.Append(" - ").Append(tag); + } } + + return result.ToString(); } - return result.ToString(); + return string.Join(" - ", attributes); } - return string.Join(" - ", attributes); - } - default: return null; } @@ -499,6 +506,12 @@ namespace MediaBrowser.Model.Entities /// true if this instance is for the hearing impaired; otherwise, false. public bool IsHearingImpaired { get; set; } + /// + /// Gets or sets a value indicating whether this instance is original. + /// + /// true if this instance is original; otherwise, false. + public bool IsOriginal { get; set; } + /// /// Gets or sets the height. /// -- cgit v1.2.3