diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 1e9f2f7a2f..8bc06b6577 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -211,12 +211,7 @@ namespace MediaBrowser.Api private void UpdateItem(BaseItemDto request, BaseItem item) { item.Name = request.Name; - - // Only set the forced value if they changed it, or there's already one - if (!string.Equals(item.SortName, request.SortName) || !string.IsNullOrEmpty(item.ForcedSortName)) - { - item.ForcedSortName = request.SortName; - } + item.ForcedSortName = request.ForcedSortName; var hasBudget = item as IHasBudget; if (hasBudget != null) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index a6b3b72942..7dcb06f7b3 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1168,18 +1168,23 @@ namespace MediaBrowser.Api.Playback protected double? GetFramerateParam(StreamState state) { - if (state.VideoRequest != null && state.VideoRequest.Framerate.HasValue) + if (state.VideoRequest != null) { - return state.VideoRequest.Framerate.Value; - } - - if (state.VideoStream != null) - { - var contentRate = state.VideoStream.AverageFrameRate ?? state.VideoStream.RealFrameRate; - - if (contentRate.HasValue && contentRate.Value > 23.976) + if (state.VideoRequest.Framerate.HasValue) { - return 23.976; + return state.VideoRequest.Framerate.Value; + } + + var maxrate = state.VideoRequest.MaxFramerate ?? 23.976; + + if (state.VideoStream != null) + { + var contentRate = state.VideoStream.AverageFrameRate ?? state.VideoStream.RealFrameRate; + + if (contentRate.HasValue && contentRate.Value > maxrate) + { + return maxrate; + } } } @@ -1265,17 +1270,38 @@ namespace MediaBrowser.Api.Playback { if (videoRequest != null) { - request.StartTimeTicks = long.Parse(val, UsCulture); + videoRequest.MaxWidth = int.Parse(val, UsCulture); } } else if (i == 12) { if (videoRequest != null) { - videoRequest.Profile = val; + videoRequest.MaxHeight = int.Parse(val, UsCulture); } } else if (i == 13) + { + if (videoRequest != null) + { + videoRequest.Framerate = int.Parse(val, UsCulture); + } + } + else if (i == 14) + { + if (videoRequest != null) + { + request.StartTimeTicks = long.Parse(val, UsCulture); + } + } + else if (i == 15) + { + if (videoRequest != null) + { + videoRequest.Profile = val; + } + } + else if (i == 16) { if (videoRequest != null) { diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs index 3439621e95..8db5920f6b 100644 --- a/MediaBrowser.Api/Playback/StreamRequest.cs +++ b/MediaBrowser.Api/Playback/StreamRequest.cs @@ -145,6 +145,9 @@ namespace MediaBrowser.Api.Playback [ApiMember(Name = "Framerate", Description = "Optional. A specific video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.", IsRequired = false, DataType = "double", ParameterType = "query", Verb = "GET")] public double? Framerate { get; set; } + [ApiMember(Name = "MaxFramerate", Description = "Optional. A specific maximum video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.", IsRequired = false, DataType = "double", ParameterType = "query", Verb = "GET")] + public double? MaxFramerate { get; set; } + /// /// Gets or sets the profile. /// diff --git a/MediaBrowser.Controller/Dlna/CodecProfile.cs b/MediaBrowser.Controller/Dlna/CodecProfile.cs index 5621c7ef23..2b9a40ea06 100644 --- a/MediaBrowser.Controller/Dlna/CodecProfile.cs +++ b/MediaBrowser.Controller/Dlna/CodecProfile.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; namespace MediaBrowser.Controller.Dlna @@ -18,6 +19,13 @@ namespace MediaBrowser.Controller.Dlna { return (Codec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); } + + public bool ContainsCodec(string codec) + { + var codecs = GetCodecs(); + + return codecs.Count == 0 || codecs.Contains(codec, StringComparer.OrdinalIgnoreCase); + } } public enum CodecType diff --git a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs index 1ce2adb1ba..007cb632e4 100644 --- a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs +++ b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs @@ -32,9 +32,7 @@ namespace MediaBrowser.Controller.Dlna public enum TranscodingSettingType { - VideoLevel = 0, - VideoProfile = 1, - MaxAudioChannels = 2 + VideoProfile = 0 } public enum TranscodeSeekInfo diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs index 3992fbfbf5..20f31cf9da 100644 --- a/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs +++ b/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs @@ -34,7 +34,20 @@ namespace MediaBrowser.Dlna.PlayTo public int? SubtitleStreamIndex { get; set; } public string DeviceProfileName { get; set; } - + + public int? MaxAudioChannels { get; set; } + + public int? AudioBitrate { get; set; } + + public int? VideoBitrate { get; set; } + + public int? VideoLevel { get; set; } + + public int? MaxWidth { get; set; } + public int? MaxHeight { get; set; } + + public int? MaxFramerate { get; set; } + public PlaylistItem() { TranscodingSettings = new List(); diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs index e4c49a224c..1b2d791136 100644 --- a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs +++ b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs @@ -24,13 +24,16 @@ namespace MediaBrowser.Dlna.PlayTo var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio); - if (profile.CodecProfiles.Where(i => i.Type == CodecType.AudioCodec) - .All(i => IsCodecProfileSupported(i, item.Path, null, audioStream))) - { - var directPlay = profile.DirectPlayProfiles - .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, audioStream)); + var directPlay = profile.DirectPlayProfiles + .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, audioStream)); - if (directPlay != null) + if (directPlay != null) + { + var audioCodec = audioStream == null ? null : audioStream.Codec; + + // Make sure audio codec profiles are satisfied + if (!string.IsNullOrEmpty(audioCodec) && profile.CodecProfiles.Where(i => i.Type == CodecType.AudioCodec && i.ContainsCodec(audioCodec)) + .All(i => AreConditionsSatisfied(i.Conditions, item.Path, null, audioStream))) { playlistItem.Transcode = false; playlistItem.Container = Path.GetExtension(item.Path); @@ -48,8 +51,15 @@ namespace MediaBrowser.Dlna.PlayTo playlistItem.TranscodingSettings = transcodingProfile.Settings.ToList(); playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.'); playlistItem.AudioCodec = transcodingProfile.AudioCodec; + + var audioTranscodingConditions = profile.CodecProfiles + .Where(i => i.Type == CodecType.AudioCodec && i.ContainsCodec(transcodingProfile.AudioCodec)) + .Take(1) + .SelectMany(i => i.Conditions); + + ApplyTranscodingConditions(playlistItem, audioTranscodingConditions); } - + return playlistItem; } @@ -81,7 +91,7 @@ namespace MediaBrowser.Dlna.PlayTo playlistItem.TranscodingSettings = transcodingProfile.Settings.ToList(); playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.'); } - + return playlistItem; } @@ -96,18 +106,28 @@ namespace MediaBrowser.Dlna.PlayTo var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio); var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); - if (profile.CodecProfiles.Where(i => i.Type == CodecType.VideoCodec || i.Type == CodecType.VideoAudioCodec) - .All(i => IsCodecProfileSupported(i, item.Path, videoStream, audioStream))) + var directPlay = profile.DirectPlayProfiles + .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, videoStream, audioStream)); + + if (directPlay != null) { - var directPlay = profile.DirectPlayProfiles - .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, videoStream, audioStream)); + var videoCodec = videoStream == null ? null : videoStream.Codec; - if (directPlay != null) + // Make sure video codec profiles are satisfied + if (!string.IsNullOrEmpty(videoCodec) && profile.CodecProfiles.Where(i => i.Type == CodecType.VideoCodec && i.ContainsCodec(videoCodec)) + .All(i => AreConditionsSatisfied(i.Conditions, item.Path, videoStream, audioStream))) { - playlistItem.Transcode = false; - playlistItem.Container = Path.GetExtension(item.Path); + var audioCodec = audioStream == null ? null : audioStream.Codec; - return playlistItem; + // Make sure audio codec profiles are satisfied + if (string.IsNullOrEmpty(audioCodec) || profile.CodecProfiles.Where(i => i.Type == CodecType.VideoAudioCodec && i.ContainsCodec(audioCodec)) + .All(i => AreConditionsSatisfied(i.Conditions, item.Path, videoStream, audioStream))) + { + playlistItem.Transcode = false; + playlistItem.Container = Path.GetExtension(item.Path); + + return playlistItem; + } } } @@ -121,11 +141,113 @@ namespace MediaBrowser.Dlna.PlayTo playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.'); playlistItem.AudioCodec = transcodingProfile.AudioCodec.Split(',').FirstOrDefault(); playlistItem.VideoCodec = transcodingProfile.VideoCodec; + + var videoTranscodingConditions = profile.CodecProfiles + .Where(i => i.Type == CodecType.VideoCodec && i.ContainsCodec(transcodingProfile.VideoCodec)) + .Take(1) + .SelectMany(i => i.Conditions); + + ApplyTranscodingConditions(playlistItem, videoTranscodingConditions); + + var audioTranscodingConditions = profile.CodecProfiles + .Where(i => i.Type == CodecType.VideoAudioCodec && i.ContainsCodec(transcodingProfile.AudioCodec)) + .Take(1) + .SelectMany(i => i.Conditions); + + ApplyTranscodingConditions(playlistItem, audioTranscodingConditions); } return playlistItem; } + private void ApplyTranscodingConditions(PlaylistItem item, IEnumerable conditions) + { + foreach (var condition in conditions.Where(i => !string.IsNullOrEmpty(i.Value))) + { + var value = condition.Value; + + switch (condition.Property) + { + case ProfileConditionValue.AudioBitrate: + { + var num = 0; + if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) + { + item.AudioBitrate = num; + } + break; + } + case ProfileConditionValue.AudioChannels: + { + var num = 0; + if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) + { + item.MaxAudioChannels = num; + } + break; + } + case ProfileConditionValue.Filesize: + case ProfileConditionValue.AudioProfile: + case ProfileConditionValue.Has64BitOffsets: + case ProfileConditionValue.VideoBitDepth: + case ProfileConditionValue.VideoPacketLength: + case ProfileConditionValue.VideoProfile: + case ProfileConditionValue.VideoTimestamp: + { + // Not supported yet + break; + } + case ProfileConditionValue.Height: + { + var num = 0; + if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) + { + item.MaxHeight = num; + } + break; + } + case ProfileConditionValue.VideoBitrate: + { + var num = 0; + if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) + { + item.VideoBitrate = num; + } + break; + } + case ProfileConditionValue.VideoFramerate: + { + var num = 0; + if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) + { + item.MaxFramerate = num; + } + break; + } + case ProfileConditionValue.VideoLevel: + { + var num = 0; + if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) + { + item.VideoLevel = num; + } + break; + } + case ProfileConditionValue.Width: + { + var num = 0; + if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) + { + item.MaxWidth = num; + } + break; + } + default: + throw new ArgumentException("Unrecognized ProfileConditionValue"); + } + } + } + private bool IsSupported(DirectPlayProfile profile, Photo item) { var mediaPath = item.Path; @@ -142,7 +264,7 @@ namespace MediaBrowser.Dlna.PlayTo return true; } - + private bool IsSupported(DirectPlayProfile profile, Audio item, MediaStream audioStream) { var mediaPath = item.Path; @@ -222,22 +344,9 @@ namespace MediaBrowser.Dlna.PlayTo return true; } - private bool IsCodecProfileSupported(CodecProfile profile, string mediaPath, MediaStream videoStream, MediaStream audioStream) + private bool AreConditionsSatisfied(IEnumerable conditions, string mediaPath, MediaStream videoStream, MediaStream audioStream) { - var codecs = profile.GetCodecs(); - var stream = profile.Type == CodecType.VideoCodec ? videoStream : audioStream; - var existingCodec = (stream == null ? null : stream.Codec) ?? string.Empty; - - if (codecs.Count == 0 || codecs.Contains(existingCodec, StringComparer.OrdinalIgnoreCase)) - { - // Check additional conditions - if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, videoStream, audioStream))) - { - return false; - } - } - - return true; + return conditions.All(i => IsConditionSatisfied(i, mediaPath, videoStream, audioStream)); } /// diff --git a/MediaBrowser.Dlna/PlayTo/StreamHelper.cs b/MediaBrowser.Dlna/PlayTo/StreamHelper.cs index 6cd66c16ae..61c3bdd730 100644 --- a/MediaBrowser.Dlna/PlayTo/StreamHelper.cs +++ b/MediaBrowser.Dlna/PlayTo/StreamHelper.cs @@ -18,7 +18,7 @@ namespace MediaBrowser.Dlna.PlayTo /// System.String. internal static string GetAudioUrl(DeviceInfo deviceProperties, PlaylistItem item, List streams, string serverAddress) { - var dlnaCommand = BuildDlnaUrl(item.DeviceProfileName, item.MediaSourceId, deviceProperties.UUID, !item.Transcode, null, item.AudioCodec, item.AudioStreamIndex, item.SubtitleStreamIndex, null, 128000, item.StartPositionTicks, item.TranscodingSettings); + var dlnaCommand = BuildDlnaUrl(deviceProperties, item); return string.Format("{0}/audio/{1}/stream{2}?{3}", serverAddress, item.ItemId, "." + item.Container.TrimStart('.'), dlnaCommand); } @@ -33,7 +33,7 @@ namespace MediaBrowser.Dlna.PlayTo /// The url to send to the device internal static string GetVideoUrl(DeviceInfo deviceProperties, PlaylistItem item, List streams, string serverAddress) { - var dlnaCommand = BuildDlnaUrl(item.DeviceProfileName, item.MediaSourceId, deviceProperties.UUID, !item.Transcode, item.VideoCodec, item.AudioCodec, item.AudioStreamIndex, item.SubtitleStreamIndex, 1500000, 128000, item.StartPositionTicks, item.TranscodingSettings); + var dlnaCommand = BuildDlnaUrl(deviceProperties, item); return string.Format("{0}/Videos/{1}/stream{2}?{3}", serverAddress, item.ItemId, item.Container, dlnaCommand); } @@ -41,30 +41,33 @@ namespace MediaBrowser.Dlna.PlayTo /// /// Builds the dlna URL. /// - private static string BuildDlnaUrl(string deviceProfileName, string mediaSourceId, string deviceID, bool isStatic, string videoCodec, string audioCodec, int? audiostreamIndex, int? subtitleIndex, int? videoBitrate, int? audioBitrate, long? startPositionTicks, List settings) + private static string BuildDlnaUrl(DeviceInfo deviceProperties, PlaylistItem item) { - var profile = settings.Where(i => i.Name == TranscodingSettingType.VideoProfile).Select(i => i.Value).FirstOrDefault(); - var videoLevel = settings.Where(i => i.Name == TranscodingSettingType.VideoLevel).Select(i => i.Value).FirstOrDefault(); - var maxAudioChannels = settings.Where(i => i.Name == TranscodingSettingType.MaxAudioChannels).Select(i => i.Value).FirstOrDefault(); + var profile = item.TranscodingSettings.Where(i => i.Name == TranscodingSettingType.VideoProfile) + .Select(i => i.Value) + .FirstOrDefault(); var usCulture = new CultureInfo("en-US"); - + var list = new List { - deviceProfileName ?? string.Empty, - deviceID ?? string.Empty, - mediaSourceId ?? string.Empty, - isStatic.ToString().ToLower(), - videoCodec ?? string.Empty, - audioCodec ?? string.Empty, - audiostreamIndex.HasValue ? audiostreamIndex.Value.ToString(usCulture) : string.Empty, - subtitleIndex.HasValue ? subtitleIndex.Value.ToString(usCulture) : string.Empty, - videoBitrate.HasValue ? videoBitrate.Value.ToString(usCulture) : string.Empty, - audioBitrate.HasValue ? audioBitrate.Value.ToString(usCulture) : string.Empty, - maxAudioChannels ?? string.Empty, - startPositionTicks.HasValue ? startPositionTicks.Value.ToString(usCulture) : string.Empty, + item.DeviceProfileName ?? string.Empty, + deviceProperties.UUID ?? string.Empty, + item.MediaSourceId ?? string.Empty, + (!item.Transcode).ToString().ToLower(), + item.VideoCodec ?? string.Empty, + item.AudioCodec ?? string.Empty, + item.AudioStreamIndex.HasValue ? item.AudioStreamIndex.Value.ToString(usCulture) : string.Empty, + item.SubtitleStreamIndex.HasValue ? item.SubtitleStreamIndex.Value.ToString(usCulture) : string.Empty, + item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(usCulture) : string.Empty, + item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(usCulture) : string.Empty, + item.MaxAudioChannels.HasValue ? item.MaxAudioChannels.Value.ToString(usCulture) : string.Empty, + item.MaxFramerate.HasValue ? item.MaxFramerate.Value.ToString(usCulture) : string.Empty, + item.MaxWidth.HasValue ? item.MaxWidth.Value.ToString(usCulture) : string.Empty, + item.MaxHeight.HasValue ? item.MaxHeight.Value.ToString(usCulture) : string.Empty, + item.StartPositionTicks.ToString(usCulture), profile ?? string.Empty, - videoLevel ?? string.Empty + item.VideoLevel.HasValue ? item.VideoLevel.Value.ToString(usCulture) : string.Empty }; return string.Format("Params={0}", string.Join(";", list.ToArray())); diff --git a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs index a1c6612753..710f02df2e 100644 --- a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs +++ b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs @@ -34,7 +34,6 @@ namespace MediaBrowser.Dlna.Profiles Settings = new [] { - new TranscodingSetting {Name = TranscodingSettingType.VideoLevel, Value = "3"}, new TranscodingSetting {Name = TranscodingSettingType.VideoProfile, Value = "baseline"} } } @@ -54,6 +53,24 @@ namespace MediaBrowser.Dlna.Profiles Type = DlnaProfileType.Video } }; + + CodecProfiles = new[] + { + new CodecProfile + { + Type = CodecType.VideoCodec, + Conditions = new [] + { + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.VideoLevel, + Value = "3", + IsRequired = false + } + } + } + }; } } } diff --git a/MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs b/MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs index e5fc1ed073..39490f8062 100644 --- a/MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs @@ -6,10 +6,12 @@ namespace MediaBrowser.Dlna.Profiles { public SonyPs3Profile() { - Name = "Sony Bravia (2010)"; + Name = "Sony PlayStation 3"; Identification = new DeviceIdentification { + FriendlyName = "PLAYSTATION 3", + Headers = new[] { new HttpHeaderInfo diff --git a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs index 52d4a5f291..47c7b21cc8 100644 --- a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs +++ b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs @@ -44,7 +44,6 @@ namespace MediaBrowser.Dlna.Profiles Settings = new [] { - new TranscodingSetting {Name = TranscodingSettingType.VideoLevel, Value = "3"}, new TranscodingSetting {Name = TranscodingSettingType.VideoProfile, Value = "baseline"} } }, diff --git a/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs b/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs index 7216457a3f..660d821d34 100644 --- a/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs +++ b/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs @@ -49,8 +49,6 @@ namespace MediaBrowser.Dlna.Profiles Settings = new [] { - new TranscodingSetting {Name = TranscodingSettingType.MaxAudioChannels, Value = "6"}, - new TranscodingSetting {Name = TranscodingSettingType.VideoLevel, Value = "3"}, new TranscodingSetting {Name = TranscodingSettingType.VideoProfile, Value = "baseline"} } }, @@ -229,6 +227,13 @@ namespace MediaBrowser.Dlna.Profiles Property = ProfileConditionValue.VideoBitrate, Value = "10240000", IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.VideoLevel, + Value = "3", + IsRequired = false } } }, @@ -264,6 +269,13 @@ namespace MediaBrowser.Dlna.Profiles Property = ProfileConditionValue.VideoBitrate, Value = "15360000", IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.VideoLevel, + Value = "3", + IsRequired = false } } }, @@ -294,7 +306,7 @@ namespace MediaBrowser.Dlna.Profiles { Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.AudioChannels, - Value = "6", + Value = "2", IsRequired = false }, new ProfileCondition diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index 780f936be2..c13e1d2d08 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -69,6 +69,7 @@ namespace MediaBrowser.Model.Dto /// /// The name of the sort. public string SortName { get; set; } + public string ForcedSortName { get; set; } /// /// Gets or sets the video3 D format. diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index dfd13d8c32..dad716f6ad 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -107,7 +107,8 @@ namespace MediaBrowser.Providers.Manager // Next run metadata providers if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) { - var providers = GetProviders(item, refreshResult.DateLastMetadataRefresh.HasValue, refreshOptions).ToList(); + var providers = GetProviders(item, refreshResult.DateLastMetadataRefresh.HasValue, refreshOptions) + .ToList(); if (providers.Count > 0 || !refreshResult.DateLastMetadataRefresh.HasValue) { diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 66a9284d73..1833b708fc 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -741,6 +741,7 @@ namespace MediaBrowser.Server.Implementations.Dto { dto.LockedFields = item.LockedFields; dto.LockData = item.IsLocked; + dto.ForcedSortName = item.ForcedSortName; } var hasBudget = item as IHasBudget;