mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-05-13 12:16:49 +01:00
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 <lampan-git@users.noreply.github.com>
This commit is contained in:
@@ -230,6 +230,7 @@
|
||||
- [LiHRaM](https://github.com/LiHRaM)
|
||||
- [MSalman5230](https://github.com/MSalman5230)
|
||||
- [dwandw](https://github.com/dwandw)
|
||||
- [Lampan-git](https://github.com/Lampan-git)
|
||||
|
||||
# Emby Contributors
|
||||
|
||||
|
||||
@@ -1067,6 +1067,8 @@ namespace Emby.Server.Implementations.Dto
|
||||
dto.OriginalTitle = item.OriginalTitle;
|
||||
}
|
||||
|
||||
dto.OriginalLanguage = item.OriginalLanguage;
|
||||
|
||||
if (options.ContainsField(ItemFields.ParentId))
|
||||
{
|
||||
dto.ParentId = item.DisplayParentId;
|
||||
|
||||
@@ -23,6 +23,7 @@ using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
@@ -423,7 +424,7 @@ namespace Emby.Server.Implementations.Library
|
||||
MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs, user.SubtitleMode, audioLanguage);
|
||||
}
|
||||
|
||||
private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
|
||||
private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection, string originalLanguage)
|
||||
{
|
||||
if (userData is not null && userData.AudioStreamIndex.HasValue && user.RememberAudioSelections && allowRememberingSelection)
|
||||
{
|
||||
@@ -437,7 +438,42 @@ namespace Emby.Server.Implementations.Library
|
||||
}
|
||||
}
|
||||
|
||||
var preferredAudio = NormalizeLanguage(user.AudioLanguagePreference);
|
||||
if (string.Equals(user.AudioLanguagePreference, "OriginalLanguage", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
originalLanguage = !string.IsNullOrWhiteSpace(originalLanguage)
|
||||
? originalLanguage.Split(',').FirstOrDefault()
|
||||
: null;
|
||||
|
||||
if (user.PlayDefaultAudioTrack)
|
||||
{
|
||||
source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(
|
||||
source.MediaStreams,
|
||||
NormalizeLanguage(originalLanguage),
|
||||
user.PlayDefaultAudioTrack);
|
||||
return;
|
||||
}
|
||||
|
||||
var originalIndex = source.MediaStreams.FindIndex(i => i.Type == MediaStreamType.Audio && i.IsOriginal);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(originalLanguage) && originalIndex != -1)
|
||||
{
|
||||
var mediaLanguageOriginal = source.MediaStreams[originalIndex].Language;
|
||||
if (NormalizeLanguage(mediaLanguageOriginal).Contains(NormalizeLanguage(originalLanguage).FirstOrDefault()))
|
||||
{
|
||||
source.DefaultAudioStreamIndex = originalIndex;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (originalIndex != -1)
|
||||
{
|
||||
source.DefaultAudioStreamIndex = originalIndex;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var preferredAudio = string.Equals(user.AudioLanguagePreference, "OriginalLanguage", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(originalLanguage)
|
||||
? NormalizeLanguage(originalLanguage)
|
||||
: NormalizeLanguage(user.AudioLanguagePreference);
|
||||
|
||||
source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.PlayDefaultAudioTrack);
|
||||
if (user.PlayDefaultAudioTrack)
|
||||
@@ -462,7 +498,19 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
var allowRememberingSelection = item is null || item.EnableRememberingTrackSelections;
|
||||
|
||||
SetDefaultAudioStreamIndex(source, userData, user, allowRememberingSelection);
|
||||
var originalLanguage = item?.OriginalLanguage ?? item switch
|
||||
{
|
||||
Episode episode => episode.Series.OriginalLanguage,
|
||||
Video video => video.GetOwner() switch
|
||||
{
|
||||
Episode ownerEpisode => ownerEpisode.OriginalLanguage ?? ownerEpisode.Series.OriginalLanguage,
|
||||
BaseItem owner => owner.OriginalLanguage,
|
||||
null => null
|
||||
},
|
||||
_ => null
|
||||
};
|
||||
|
||||
SetDefaultAudioStreamIndex(source, userData, user, allowRememberingSelection, originalLanguage);
|
||||
SetDefaultSubtitleStreamIndex(source, userData, user, allowRememberingSelection);
|
||||
}
|
||||
else if (mediaType == MediaType.Audio)
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"NotificationOptionUserLockedOut": "User locked out",
|
||||
"NotificationOptionVideoPlayback": "Video playback started",
|
||||
"NotificationOptionVideoPlaybackStopped": "Video playback stopped",
|
||||
"Original": "Original",
|
||||
"Photos": "Photos",
|
||||
"Playlists": "Playlists",
|
||||
"Plugin": "Plugin",
|
||||
|
||||
@@ -242,6 +242,7 @@ public class ItemUpdateController : BaseJellyfinApiController
|
||||
item.ForcedSortName = request.ForcedSortName;
|
||||
|
||||
item.OriginalTitle = string.IsNullOrWhiteSpace(request.OriginalTitle) ? null : request.OriginalTitle;
|
||||
item.OriginalLanguage = string.IsNullOrWhiteSpace(request.OriginalLanguage) ? null : request.OriginalLanguage;
|
||||
|
||||
item.CriticRating = request.CriticRating;
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ internal static class BaseItemMapper
|
||||
dto.CriticRating = entity.CriticRating;
|
||||
dto.PresentationUniqueKey = entity.PresentationUniqueKey;
|
||||
dto.OriginalTitle = entity.OriginalTitle;
|
||||
dto.OriginalLanguage = entity.OriginalLanguage;
|
||||
dto.Album = entity.Album;
|
||||
dto.LUFS = entity.LUFS;
|
||||
dto.NormalizationGain = entity.NormalizationGain;
|
||||
@@ -243,6 +244,7 @@ internal static class BaseItemMapper
|
||||
entity.CriticRating = dto.CriticRating;
|
||||
entity.PresentationUniqueKey = dto.PresentationUniqueKey;
|
||||
entity.OriginalTitle = dto.OriginalTitle;
|
||||
entity.OriginalLanguage = dto.OriginalLanguage;
|
||||
entity.Album = dto.Album;
|
||||
entity.LUFS = dto.LUFS;
|
||||
entity.NormalizationGain = dto.NormalizationGain;
|
||||
|
||||
@@ -123,6 +123,7 @@ public class MediaStreamRepository : IMediaStreamRepository
|
||||
dto.IsDefault = entity.IsDefault;
|
||||
dto.IsForced = entity.IsForced;
|
||||
dto.IsExternal = entity.IsExternal;
|
||||
dto.IsOriginal = entity.IsOriginal;
|
||||
dto.Height = entity.Height;
|
||||
dto.Width = entity.Width;
|
||||
dto.AverageFrameRate = entity.AverageFrameRate;
|
||||
@@ -164,6 +165,11 @@ public class MediaStreamRepository : IMediaStreamRepository
|
||||
dto.LocalizedLanguage = culture?.DisplayName;
|
||||
}
|
||||
|
||||
if (dto.Type is MediaStreamType.Audio)
|
||||
{
|
||||
dto.LocalizedOriginal = _localization.GetLocalizedString("Original");
|
||||
}
|
||||
|
||||
if (dto.Type is MediaStreamType.Subtitle)
|
||||
{
|
||||
dto.LocalizedUndefined = _localization.GetLocalizedString("Undefined");
|
||||
@@ -198,6 +204,7 @@ public class MediaStreamRepository : IMediaStreamRepository
|
||||
IsDefault = dto.IsDefault,
|
||||
IsForced = dto.IsForced,
|
||||
IsExternal = dto.IsExternal,
|
||||
IsOriginal = dto.IsOriginal,
|
||||
Height = dto.Height,
|
||||
Width = dto.Width,
|
||||
AverageFrameRate = dto.AverageFrameRate,
|
||||
|
||||
@@ -216,6 +216,9 @@ namespace MediaBrowser.Controller.Entities
|
||||
[JsonIgnore]
|
||||
public string OriginalTitle { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string OriginalLanguage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
|
||||
@@ -729,6 +729,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||
stream.Type = MediaStreamType.Audio;
|
||||
stream.LocalizedDefault = _localization.GetLocalizedString("Default");
|
||||
stream.LocalizedExternal = _localization.GetLocalizedString("External");
|
||||
stream.LocalizedOriginal = _localization.GetLocalizedString("Original");
|
||||
stream.LocalizedLanguage = string.IsNullOrEmpty(stream.Language)
|
||||
? null
|
||||
: _localization.FindLanguageInfo(stream.Language)?.DisplayName;
|
||||
@@ -1031,6 +1032,11 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||
{
|
||||
stream.IsHearingImpaired = true;
|
||||
}
|
||||
|
||||
if (disposition.GetValueOrDefault("original") == 1)
|
||||
{
|
||||
stream.IsOriginal = true;
|
||||
}
|
||||
}
|
||||
|
||||
NormalizeStreamTitle(stream);
|
||||
|
||||
@@ -800,5 +800,7 @@ namespace MediaBrowser.Model.Dto
|
||||
/// </summary>
|
||||
/// <value>The current program.</value>
|
||||
public BaseItemDto CurrentProgram { get; set; }
|
||||
|
||||
public string OriginalLanguage { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,6 +260,8 @@ namespace MediaBrowser.Model.Entities
|
||||
|
||||
public string LocalizedLanguage { get; set; }
|
||||
|
||||
public string LocalizedOriginal { get; set; }
|
||||
|
||||
public string DisplayTitle
|
||||
{
|
||||
get
|
||||
@@ -267,161 +269,166 @@ namespace MediaBrowser.Model.Entities
|
||||
switch (Type)
|
||||
{
|
||||
case MediaStreamType.Audio:
|
||||
{
|
||||
var attributes = new List<string>();
|
||||
{
|
||||
var attributes = new List<string>();
|
||||
|
||||
// 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(Profile) && !string.Equals(Profile, "lc", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
attributes.Add(Profile);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(Codec))
|
||||
{
|
||||
attributes.Add(AudioCodec.GetFriendlyName(Codec));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ChannelLayout))
|
||||
{
|
||||
attributes.Add(StringHelper.FirstToUpper(ChannelLayout));
|
||||
}
|
||||
else if (Channels.HasValue)
|
||||
{
|
||||
attributes.Add(Channels.Value.ToString(CultureInfo.InvariantCulture) + " ch");
|
||||
}
|
||||
|
||||
if (IsDefault)
|
||||
{
|
||||
attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault);
|
||||
}
|
||||
|
||||
if (IsExternal)
|
||||
{
|
||||
attributes.Add(string.IsNullOrEmpty(LocalizedExternal) ? "External" : LocalizedExternal);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Title))
|
||||
{
|
||||
var result = new StringBuilder(Title);
|
||||
foreach (var tag in attributes)
|
||||
// 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))
|
||||
{
|
||||
// Keep Tags that are not already in Title.
|
||||
if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result.Append(" - ").Append(tag);
|
||||
}
|
||||
// Use pre-resolved localized language name, falling back to raw language code.
|
||||
attributes.Add(StringHelper.FirstToUpper(LocalizedLanguage ?? Language));
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
if (!string.IsNullOrEmpty(Profile) && !string.Equals(Profile, "lc", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
attributes.Add(Profile);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(Codec))
|
||||
{
|
||||
attributes.Add(AudioCodec.GetFriendlyName(Codec));
|
||||
}
|
||||
|
||||
return string.Join(" - ", attributes);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(ChannelLayout))
|
||||
{
|
||||
attributes.Add(StringHelper.FirstToUpper(ChannelLayout));
|
||||
}
|
||||
else if (Channels.HasValue)
|
||||
{
|
||||
attributes.Add(Channels.Value.ToString(CultureInfo.InvariantCulture) + " ch");
|
||||
}
|
||||
|
||||
if (IsDefault)
|
||||
{
|
||||
attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault);
|
||||
}
|
||||
|
||||
if (IsExternal)
|
||||
{
|
||||
attributes.Add(string.IsNullOrEmpty(LocalizedExternal) ? "External" : LocalizedExternal);
|
||||
}
|
||||
|
||||
if (IsOriginal)
|
||||
{
|
||||
attributes.Add(string.IsNullOrEmpty(LocalizedOriginal) ? "Original" : LocalizedOriginal);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Title))
|
||||
{
|
||||
var result = new StringBuilder(Title);
|
||||
foreach (var tag in attributes)
|
||||
{
|
||||
// Keep Tags that are not already in Title.
|
||||
if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result.Append(" - ").Append(tag);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
return string.Join(" - ", attributes);
|
||||
}
|
||||
|
||||
case MediaStreamType.Video:
|
||||
{
|
||||
var attributes = new List<string>();
|
||||
|
||||
var resolutionText = GetResolutionText();
|
||||
|
||||
if (!string.IsNullOrEmpty(resolutionText))
|
||||
{
|
||||
attributes.Add(resolutionText);
|
||||
}
|
||||
var attributes = new List<string>();
|
||||
|
||||
if (!string.IsNullOrEmpty(Codec))
|
||||
{
|
||||
attributes.Add(Codec.ToUpperInvariant());
|
||||
}
|
||||
var resolutionText = GetResolutionText();
|
||||
|
||||
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(resolutionText))
|
||||
{
|
||||
// Keep Tags that are not already in Title.
|
||||
if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result.Append(" - ").Append(tag);
|
||||
}
|
||||
attributes.Add(resolutionText);
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
if (!string.IsNullOrEmpty(Codec))
|
||||
{
|
||||
attributes.Add(Codec.ToUpperInvariant());
|
||||
}
|
||||
|
||||
return string.Join(' ', attributes);
|
||||
}
|
||||
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)
|
||||
{
|
||||
// Keep Tags that are not already in Title.
|
||||
if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result.Append(" - ").Append(tag);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
return string.Join(' ', attributes);
|
||||
}
|
||||
|
||||
case MediaStreamType.Subtitle:
|
||||
{
|
||||
var attributes = new List<string>();
|
||||
{
|
||||
var attributes = new List<string>();
|
||||
|
||||
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 (IsHearingImpaired == true)
|
||||
{
|
||||
attributes.Add(string.IsNullOrEmpty(LocalizedHearingImpaired) ? "Hearing Impaired" : LocalizedHearingImpaired);
|
||||
}
|
||||
|
||||
if (IsDefault)
|
||||
{
|
||||
attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault);
|
||||
}
|
||||
|
||||
if (IsForced)
|
||||
{
|
||||
attributes.Add(string.IsNullOrEmpty(LocalizedForced) ? "Forced" : LocalizedForced);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Codec))
|
||||
{
|
||||
attributes.Add(Codec.ToUpperInvariant());
|
||||
}
|
||||
|
||||
if (IsExternal)
|
||||
{
|
||||
attributes.Add(string.IsNullOrEmpty(LocalizedExternal) ? "External" : LocalizedExternal);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Title))
|
||||
{
|
||||
var result = new StringBuilder(Title);
|
||||
foreach (var tag in attributes)
|
||||
if (!string.IsNullOrEmpty(Language))
|
||||
{
|
||||
// Keep Tags that are not already in Title.
|
||||
if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result.Append(" - ").Append(tag);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
if (IsHearingImpaired == true)
|
||||
{
|
||||
attributes.Add(string.IsNullOrEmpty(LocalizedHearingImpaired) ? "Hearing Impaired" : LocalizedHearingImpaired);
|
||||
}
|
||||
|
||||
return string.Join(" - ", attributes);
|
||||
}
|
||||
if (IsDefault)
|
||||
{
|
||||
attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault);
|
||||
}
|
||||
|
||||
if (IsForced)
|
||||
{
|
||||
attributes.Add(string.IsNullOrEmpty(LocalizedForced) ? "Forced" : LocalizedForced);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Codec))
|
||||
{
|
||||
attributes.Add(Codec.ToUpperInvariant());
|
||||
}
|
||||
|
||||
if (IsExternal)
|
||||
{
|
||||
attributes.Add(string.IsNullOrEmpty(LocalizedExternal) ? "External" : LocalizedExternal);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Title))
|
||||
{
|
||||
var result = new StringBuilder(Title);
|
||||
foreach (var tag in attributes)
|
||||
{
|
||||
// Keep Tags that are not already in Title.
|
||||
if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result.Append(" - ").Append(tag);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
return string.Join(" - ", attributes);
|
||||
}
|
||||
|
||||
default:
|
||||
return null;
|
||||
@@ -499,6 +506,12 @@ namespace MediaBrowser.Model.Entities
|
||||
/// <value><c>true</c> if this instance is for the hearing impaired; otherwise, <c>false</c>.</value>
|
||||
public bool IsHearingImpaired { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is original.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is original; otherwise, <c>false</c>.</value>
|
||||
public bool IsOriginal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the height.
|
||||
/// </summary>
|
||||
|
||||
@@ -1023,6 +1023,11 @@ namespace MediaBrowser.Providers.Manager
|
||||
target.OriginalTitle = source.OriginalTitle;
|
||||
}
|
||||
|
||||
if (replaceData || string.IsNullOrEmpty(target.OriginalLanguage))
|
||||
{
|
||||
target.OriginalLanguage = source.OriginalLanguage;
|
||||
}
|
||||
|
||||
if (replaceData || !target.CommunityRating.HasValue)
|
||||
{
|
||||
target.CommunityRating = source.CommunityRating;
|
||||
|
||||
@@ -413,6 +413,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
|
||||
}
|
||||
|
||||
item.Overview = result.Plot;
|
||||
item.OriginalLanguage = result.Language;
|
||||
|
||||
if (!Plugin.Instance.Configuration.CastAndCrew)
|
||||
{
|
||||
|
||||
@@ -379,6 +379,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
|
||||
movie.RemoteTrailers = trailers;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(movieResult.OriginalLanguage))
|
||||
{
|
||||
movie.OriginalLanguage = movieResult.OriginalLanguage;
|
||||
}
|
||||
|
||||
return metadataResult;
|
||||
}
|
||||
|
||||
|
||||
@@ -329,6 +329,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(seriesResult.OriginalLanguage))
|
||||
{
|
||||
series.OriginalLanguage = seriesResult.OriginalLanguage;
|
||||
}
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
|
||||
"id",
|
||||
"credits",
|
||||
"originaltitle",
|
||||
"originallanguage",
|
||||
"watched",
|
||||
"playcount",
|
||||
"lastplayed",
|
||||
@@ -376,6 +377,11 @@ namespace MediaBrowser.XbmcMetadata.Savers
|
||||
writer.WriteElementString("default", stream.IsDefault.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteElementString("forced", stream.IsForced.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
if (stream.IsOriginal)
|
||||
{
|
||||
writer.WriteElementString("original", stream.IsOriginal.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
if (stream.Type == MediaStreamType.Video)
|
||||
{
|
||||
var runtimeTicks = item.RunTimeTicks;
|
||||
@@ -484,6 +490,11 @@ namespace MediaBrowser.XbmcMetadata.Savers
|
||||
writer.WriteElementString("originaltitle", item.OriginalTitle);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(item.OriginalLanguage))
|
||||
{
|
||||
writer.WriteElementString("originallanguage", item.OriginalLanguage);
|
||||
}
|
||||
|
||||
var people = libraryManager.GetPeople(item);
|
||||
|
||||
var directors = people
|
||||
|
||||
@@ -96,6 +96,8 @@ public class BaseItemEntity
|
||||
|
||||
public string? OriginalTitle { get; set; }
|
||||
|
||||
public string? OriginalLanguage { get; set; }
|
||||
|
||||
public Guid? PrimaryVersionId { get; set; }
|
||||
|
||||
public DateTime? DateLastMediaAdded { get; set; }
|
||||
|
||||
@@ -62,6 +62,16 @@ namespace Jellyfin.Database.Implementations.Entities.Libraries
|
||||
[StringLength(1024)]
|
||||
public string? OriginalTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the original language.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Max length = 1024.
|
||||
/// </remarks>
|
||||
[MaxLength(1024)]
|
||||
[StringLength(1024)]
|
||||
public string? OriginalLanguage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sort title.
|
||||
/// </summary>
|
||||
|
||||
@@ -40,6 +40,8 @@ public class MediaStreamInfo
|
||||
|
||||
public bool IsExternal { get; set; }
|
||||
|
||||
public bool IsOriginal { get; set; }
|
||||
|
||||
public int? Height { get; set; }
|
||||
|
||||
public int? Width { get; set; }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddOriginalLanguage : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsOriginal",
|
||||
table: "MediaStreamInfos",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "OriginalLanguage",
|
||||
table: "BaseItems",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "BaseItems",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("00000000-0000-0000-0000-000000000001"),
|
||||
column: "OriginalLanguage",
|
||||
value: null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsOriginal",
|
||||
table: "MediaStreamInfos");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "OriginalLanguage",
|
||||
table: "BaseItems");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -264,6 +264,9 @@ namespace Jellyfin.Server.Implementations.Migrations
|
||||
b.Property<string>("OfficialRating")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalLanguage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalTitle")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -955,6 +958,9 @@ namespace Jellyfin.Server.Implementations.Migrations
|
||||
b.Property<bool?>("IsInterlaced")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsOriginal")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("KeyFrames")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
||||
@@ -105,10 +105,12 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
|
||||
|
||||
var audio1 = res.MediaStreams[1];
|
||||
Assert.Equal("eac3", audio1.Codec);
|
||||
Assert.True(audio1.IsOriginal);
|
||||
Assert.Equal(AudioSpatialFormat.DolbyAtmos, audio1.AudioSpatialFormat);
|
||||
|
||||
var audio2 = res.MediaStreams[2];
|
||||
Assert.Equal("dts", audio2.Codec);
|
||||
Assert.False(audio2.IsOriginal);
|
||||
Assert.Equal(AudioSpatialFormat.DTSX, audio2.AudioSpatialFormat);
|
||||
|
||||
Assert.Empty(res.Chapters);
|
||||
@@ -156,6 +158,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
|
||||
Assert.Equal("aac", res.MediaStreams[1].Codec);
|
||||
Assert.Equal(7, res.MediaStreams[1].Channels);
|
||||
Assert.True(res.MediaStreams[1].IsDefault);
|
||||
Assert.False(res.MediaStreams[1].IsOriginal);
|
||||
Assert.Equal("eng", res.MediaStreams[1].Language);
|
||||
Assert.Equal("Surround 6.1", res.MediaStreams[1].Title);
|
||||
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
using System;
|
||||
using AutoFixture;
|
||||
using AutoFixture.AutoMoq;
|
||||
using Castle.Components.DictionaryAdapter;
|
||||
using Emby.Server.Implementations.IO;
|
||||
using Emby.Server.Implementations.Library;
|
||||
using Jellyfin.Database.Implementations.Entities;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
@@ -11,12 +20,28 @@ namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
public class MediaSourceManagerTests
|
||||
{
|
||||
private readonly MediaSourceManager _mediaSourceManager;
|
||||
private readonly Mock<IUserDataManager> _mockUserDataManager;
|
||||
private readonly Mock<ILocalizationManager> _mockLocalizationManager;
|
||||
private Video _item;
|
||||
private User _user;
|
||||
|
||||
public MediaSourceManagerTests()
|
||||
{
|
||||
IFixture fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
|
||||
fixture.Inject<IFileSystem>(fixture.Create<ManagedFileSystem>());
|
||||
|
||||
_mockUserDataManager = fixture.Freeze<Mock<IUserDataManager>>();
|
||||
_mockUserDataManager.Setup(m => m.GetUserData(It.IsAny<User>(), It.IsAny<BaseItem>())).Returns(new UserItemData() { Key = "key" });
|
||||
|
||||
_mockLocalizationManager = fixture.Create<Mock<ILocalizationManager>>();
|
||||
_mockLocalizationManager.Setup(m => m.FindLanguageInfo(It.IsAny<string>())).Returns((string s) => string.IsNullOrEmpty(s) ? null : new CultureDto(s, s, s, new EditableList<string> { s }));
|
||||
fixture.Inject(_mockLocalizationManager.Object);
|
||||
|
||||
_mediaSourceManager = fixture.Create<MediaSourceManager>();
|
||||
|
||||
_item = new Video { Id = Guid.NewGuid(), OwnerId = Guid.Empty, ParentId = Guid.Empty };
|
||||
|
||||
_user = fixture.Create<User>();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -28,5 +53,96 @@ namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
[InlineData("rtsp://media.example.com:554/twister/audiotrack", MediaProtocol.Rtsp)]
|
||||
public void GetPathProtocol_ValidArg_Correct(string path, MediaProtocol expected)
|
||||
=> Assert.Equal(expected, _mediaSourceManager.GetPathProtocol(path));
|
||||
|
||||
[Theory]
|
||||
[InlineData(5, "eng", "eng", false, true)]
|
||||
[InlineData(5, "eng", "eng", true, true)]
|
||||
[InlineData(2, "ger", "eng", false, true)]
|
||||
[InlineData(2, "ger", "eng", true, true)]
|
||||
[InlineData(1, "fre", "eng", false, true)]
|
||||
[InlineData(2, "fre", "eng", true, true)]
|
||||
[InlineData(5, "OriginalLanguage", "eng", false, false)]
|
||||
[InlineData(4, "OriginalLanguage", "eng", false, true)]
|
||||
[InlineData(5, "OriginalLanguage", "eng", true, false)]
|
||||
[InlineData(5, "OriginalLanguage", "eng", true, true)]
|
||||
[InlineData(2, "OriginalLanguage", "jpn", true, true)]
|
||||
[InlineData(2, "OriginalLanguage", "jpn", false, true)]
|
||||
[InlineData(2, "OriginalLanguage", "jpn,eng", false, true)]
|
||||
[InlineData(4, "OriginalLanguage", null, false, true)]
|
||||
[InlineData(2, "OriginalLanguage", null, true, true)]
|
||||
[InlineData(4, "OriginalLanguage", "", false, true)]
|
||||
[InlineData(2, "OriginalLanguage", "", false, false)]
|
||||
[InlineData(2, "OriginalLanguage", "ger", false, true)]
|
||||
[InlineData(2, "OriginalLanguage", "ger", false, false)]
|
||||
[InlineData(1, "OriginalLanguage", "fre", false, false)]
|
||||
[InlineData(2, "OriginalLanguage", "fre", true, true)]
|
||||
[InlineData(2, "OriginalLanguage", "fre", true, false)]
|
||||
public void SetDefaultAudioStreamIndex_Index_Correct(
|
||||
int expectedIndex,
|
||||
string prefferedLanguage,
|
||||
string? originalLanguage,
|
||||
bool playDefault,
|
||||
bool originalExist)
|
||||
{
|
||||
var streams = new MediaStream[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
Index = 0,
|
||||
Type = MediaStreamType.Video,
|
||||
IsDefault = true
|
||||
},
|
||||
new()
|
||||
{
|
||||
Index = 1,
|
||||
Type = MediaStreamType.Audio,
|
||||
Language = "fre",
|
||||
IsDefault = false,
|
||||
IsOriginal = false
|
||||
},
|
||||
new()
|
||||
{
|
||||
Index = 2,
|
||||
Type = MediaStreamType.Audio,
|
||||
Language = "jpn",
|
||||
IsDefault = true,
|
||||
IsOriginal = false
|
||||
},
|
||||
new()
|
||||
{
|
||||
Index = 3,
|
||||
Type = MediaStreamType.Audio,
|
||||
Language = "eng",
|
||||
IsDefault = false,
|
||||
IsOriginal = false
|
||||
},
|
||||
new()
|
||||
{
|
||||
Index = 4,
|
||||
Type = MediaStreamType.Audio,
|
||||
Language = "eng",
|
||||
IsDefault = false,
|
||||
IsOriginal = originalExist,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Index = 5,
|
||||
Type = MediaStreamType.Audio,
|
||||
Language = "eng",
|
||||
IsDefault = true,
|
||||
IsOriginal = false,
|
||||
}
|
||||
};
|
||||
var mediaInfo = new MediaSourceInfo
|
||||
{
|
||||
MediaStreams = streams
|
||||
};
|
||||
_user.AudioLanguagePreference = prefferedLanguage;
|
||||
_user.PlayDefaultAudioTrack = playDefault;
|
||||
_item.OriginalLanguage = originalLanguage;
|
||||
|
||||
_mediaSourceManager.SetDefaultAudioAndSubtitleStreamIndices(_item, mediaInfo, _user);
|
||||
Assert.Equal(expectedIndex, mediaInfo.DefaultAudioStreamIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user