Răsfoiți Sursa

Merge remote-tracking branch 'upstream/master'

Tim Hobbs 11 ani în urmă
părinte
comite
968339e4d5

+ 1 - 6
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)

+ 36 - 10
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.VideoRequest.Framerate.HasValue)
+                {
+                    return state.VideoRequest.Framerate.Value;
+                }
 
-            if (state.VideoStream != null)
-            {
-                var contentRate = state.VideoStream.AverageFrameRate ?? state.VideoStream.RealFrameRate;
+                var maxrate = state.VideoRequest.MaxFramerate ?? 23.976;
 
-                if (contentRate.HasValue && contentRate.Value > 23.976)
+                if (state.VideoStream != null)
                 {
-                    return 23.976;
+                    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)
                     {

+ 3 - 0
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; }
+        
         /// <summary>
         /// Gets or sets the profile.
         /// </summary>

+ 9 - 1
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

+ 1 - 3
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

+ 14 - 1
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<TranscodingSetting>();

+ 140 - 31
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));
+
+            if (directPlay != null)
             {
-                var directPlay = profile.DirectPlayProfiles
-                    .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, audioStream));
+                var audioCodec = audioStream == null ? null : audioStream.Codec;
 
-                if (directPlay != null)
+                // 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<ProfileCondition> 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<ProfileCondition> 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));
         }
 
         /// <summary>

+ 23 - 20
MediaBrowser.Dlna/PlayTo/StreamHelper.cs

@@ -18,7 +18,7 @@ namespace MediaBrowser.Dlna.PlayTo
         /// <returns>System.String.</returns>
         internal static string GetAudioUrl(DeviceInfo deviceProperties, PlaylistItem item, List<MediaStream> 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
         /// <returns>The url to send to the device</returns>
         internal static string GetVideoUrl(DeviceInfo deviceProperties, PlaylistItem item, List<MediaStream> 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
         /// <summary>
         /// Builds the dlna URL.
         /// </summary>
-        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<TranscodingSetting> 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<string>
             {
-                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()));

+ 18 - 1
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
+                        }
+                    }
+                }
+            };
         }
     }
 }

+ 3 - 1
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

+ 0 - 1
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"}
                     }
                 },

+ 15 - 3
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

+ 1 - 0
MediaBrowser.Model/Dto/BaseItemDto.cs

@@ -69,6 +69,7 @@ namespace MediaBrowser.Model.Dto
         /// </summary>
         /// <value>The name of the sort.</value>
         public string SortName { get; set; }
+        public string ForcedSortName { get; set; }
 
         /// <summary>
         /// Gets or sets the video3 D format.

+ 2 - 1
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)
                 {

+ 1 - 0
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;