diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 569e12530..f1d596213 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -159,7 +159,7 @@ namespace MediaBrowser.Api return libraryManager.GetPerson(DeSlugPersonName(name, libraryManager)); } - protected IList GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId = null) + protected IEnumerable GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId = null) { if (!string.IsNullOrEmpty(parentId)) { @@ -169,7 +169,7 @@ namespace MediaBrowser.Api { var user = userManager.GetUserById(userId.Value); - return folder.GetRecursiveChildren(user).ToList(); + return folder.GetRecursiveChildren(user); } return folder.GetRecursiveChildren(); @@ -178,7 +178,7 @@ namespace MediaBrowser.Api { var user = userManager.GetUserById(userId.Value); - return userManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user, null); + return userManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user); } return libraryManager.RootFolder.GetRecursiveChildren(); @@ -239,7 +239,8 @@ namespace MediaBrowser.Api return name; } - return libraryManager.RootFolder.GetRecursiveChildren(i => i is Game) + return libraryManager.RootFolder.GetRecursiveChildren() + .OfType() .SelectMany(i => i.Genres) .Distinct(StringComparer.OrdinalIgnoreCase) .FirstOrDefault(i => diff --git a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs index 6acecd342..8bc867a2e 100644 --- a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs +++ b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs @@ -381,7 +381,7 @@ namespace MediaBrowser.Api.DefaultTheme var currentUser1 = user; var ownedEpisodes = series - .SelectMany(i => i.GetRecursiveChildren(currentUser1, j => j.LocationType != LocationType.Virtual)) + .SelectMany(i => i.GetRecursiveChildren(currentUser1).Where(j => j.LocationType != LocationType.Virtual)) .OfType() .ToList(); diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 57c2244c7..fa78fa020 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -59,10 +59,11 @@ namespace MediaBrowser.Api.Playback.Hls /// Processes the request. /// /// The request. + /// if set to true [is live]. /// System.Object. - protected object ProcessRequest(StreamRequest request) + protected object ProcessRequest(StreamRequest request, bool isLive) { - return ProcessRequestAsync(request).Result; + return ProcessRequestAsync(request, isLive).Result; } private static readonly SemaphoreSlim FfmpegStartLock = new SemaphoreSlim(1, 1); @@ -70,13 +71,12 @@ namespace MediaBrowser.Api.Playback.Hls /// Processes the request async. /// /// The request. + /// if set to true [is live]. /// Task{System.Object}. - /// - /// A video bitrate is required + /// A video bitrate is required /// or - /// An audio bitrate is required - /// - private async Task ProcessRequestAsync(StreamRequest request) + /// An audio bitrate is required + private async Task ProcessRequestAsync(StreamRequest request, bool isLive) { var cancellationTokenSource = new CancellationTokenSource(); @@ -110,7 +110,8 @@ namespace MediaBrowser.Api.Playback.Hls throw; } - await WaitForMinimumSegmentCount(playlist, GetSegmentWait(), cancellationTokenSource.Token).ConfigureAwait(false); + var waitCount = isLive ? 1 : GetSegmentWait(); + await WaitForMinimumSegmentCount(playlist, waitCount, cancellationTokenSource.Token).ConfigureAwait(false); } } finally @@ -119,6 +120,22 @@ namespace MediaBrowser.Api.Playback.Hls } } + if (isLive) + { + //var file = request.PlaylistId + Path.GetExtension(Request.PathInfo); + + //file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, file); + + try + { + return ResultFactory.GetStaticFileResult(Request, playlist, FileShare.ReadWrite); + } + finally + { + ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls); + } + } + var audioBitrate = state.OutputAudioBitrate ?? 0; var videoBitrate = state.OutputVideoBitrate ?? 0; @@ -188,16 +205,18 @@ namespace MediaBrowser.Api.Playback.Hls protected async Task WaitForMinimumSegmentCount(string playlist, int segmentCount, CancellationToken cancellationToken) { - var count = 0; + Logger.Debug("Waiting for {0} segments in {1}", segmentCount, playlist); - // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written - using (var fileStream = FileSystem.GetFileStream(playlist, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) + while (true) { - using (var reader = new StreamReader(fileStream)) + // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written + using (var fileStream = FileSystem.GetFileStream(playlist, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) { - while (true) + using (var reader = new StreamReader(fileStream)) { - if (!reader.EndOfStream) + var count = 0; + + while (!reader.EndOfStream) { var line = await reader.ReadLineAsync().ConfigureAwait(false); @@ -206,11 +225,12 @@ namespace MediaBrowser.Api.Playback.Hls count++; if (count >= segmentCount) { + Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist); return; } } } - await Task.Delay(25, cancellationToken).ConfigureAwait(false); + await Task.Delay(100, cancellationToken).ConfigureAwait(false); } } } @@ -229,7 +249,7 @@ namespace MediaBrowser.Api.Playback.Hls var itsOffsetMs = hlsVideoRequest == null ? 0 - : ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs; + : hlsVideoRequest.TimeStampOffsetMs; var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture)); @@ -240,7 +260,15 @@ namespace MediaBrowser.Api.Playback.Hls // If isEncoding is true we're actually starting ffmpeg var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; - var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"", + var baseUrlParam = string.Empty; + + if (state.Request is GetLiveHlsStream) + { + baseUrlParam = string.Format(" -hls_base_url \"{0}/\"", + "hls/" + Path.GetFileNameWithoutExtension(outputPath)); + } + + var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"", itsOffset, inputModifier, GetInputArgument(state), @@ -251,6 +279,7 @@ namespace MediaBrowser.Api.Playback.Hls state.SegmentLength.ToString(UsCulture), startNumberParam, state.HlsListSize.ToString(UsCulture), + baseUrlParam, outputPath ).Trim(); diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index bde2c5694..5bb610686 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -22,11 +22,6 @@ namespace MediaBrowser.Api.Playback.Hls [Api(Description = "Gets a video stream using HTTP live streaming.")] public class GetMasterHlsVideoStream : VideoStreamRequest { - [ApiMember(Name = "BaselineStreamAudioBitRate", Description = "Optional. Specify the audio bitrate for the baseline stream.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? BaselineStreamAudioBitRate { get; set; } - - [ApiMember(Name = "AppendBaselineStream", Description = "Optional. Whether or not to include a baseline audio-only stream in the master playlist.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool AppendBaselineStream { get; set; } } [Route("/Videos/{Id}/main.m3u8", "GET")] @@ -35,12 +30,6 @@ namespace MediaBrowser.Api.Playback.Hls { } - [Route("/Videos/{Id}/baseline.m3u8", "GET")] - [Api(Description = "Gets a video stream using HTTP live streaming.")] - public class GetBaselineHlsVideoStream : VideoStreamRequest - { - } - /// /// Class GetHlsVideoSegment /// @@ -73,16 +62,11 @@ namespace MediaBrowser.Api.Playback.Hls public object Get(GetDynamicHlsVideoSegment request) { - if (string.Equals("baseline", request.PlaylistId, StringComparison.OrdinalIgnoreCase)) - { - return GetDynamicSegment(request, false).Result; - } - - return GetDynamicSegment(request, true).Result; + return GetDynamicSegment(request).Result; } private static readonly SemaphoreSlim FfmpegStartLock = new SemaphoreSlim(1, 1); - private async Task GetDynamicSegment(GetDynamicHlsVideoSegment request, bool isMain) + private async Task GetDynamicSegment(GetDynamicHlsVideoSegment request) { if ((request.StartTimeTicks ?? 0) > 0) { @@ -322,7 +306,9 @@ namespace MediaBrowser.Api.Playback.Hls var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex); // Main stream - var playlistUrl = "main.m3u8" + queryString; + var playlistUrl = (state.RunTimeTicks ?? 0) > 0 ? "main.m3u8" : "live.m3u8"; + playlistUrl += queryString; + AppendPlaylist(builder, playlistUrl, totalBitrate); if (state.VideoRequest.VideoBitRate.HasValue) @@ -385,13 +371,6 @@ namespace MediaBrowser.Api.Playback.Hls return result; } - public object Get(GetBaselineHlsVideoStream request) - { - var result = GetPlaylistAsync(request, "baseline").Result; - - return result; - } - private async Task GetPlaylistAsync(VideoStreamRequest request, string name) { var state = await GetState(request, CancellationToken.None).ConfigureAwait(false); @@ -506,14 +485,6 @@ namespace MediaBrowser.Api.Playback.Hls /// System.String. protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding) { - var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream; - - var itsOffsetMs = hlsVideoRequest == null - ? 0 - : ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs; - - var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture)); - var threads = GetNumberOfThreads(state, false); var inputModifier = GetInputModifier(state); @@ -521,8 +492,7 @@ namespace MediaBrowser.Api.Playback.Hls // If isEncoding is true we're actually starting ffmpeg var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; - var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -flags -global_header {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"", - itsOffset, + var args = string.Format("{0} -i {1} -map_metadata -1 -threads {2} {3} {4} -flags -global_header {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", inputModifier, GetInputArgument(state), threads, diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 1a925378b..28c0219fc 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -32,6 +32,12 @@ namespace MediaBrowser.Api.Playback.Hls public int TimeStampOffsetMs { get; set; } } + [Route("/Videos/{Id}/live.m3u8", "GET")] + [Api(Description = "Gets a video stream using HTTP live streaming.")] + public class GetLiveHlsStream : VideoStreamRequest + { + } + /// /// Class GetHlsVideoSegment /// @@ -105,7 +111,12 @@ namespace MediaBrowser.Api.Playback.Hls /// System.Object. public object Get(GetHlsVideoStream request) { - return ProcessRequest(request); + return ProcessRequest(request, false); + } + + public object Get(GetLiveHlsStream request) + { + return ProcessRequest(request, true); } /// diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs index 9fcbe39eb..e72edcc98 100644 --- a/MediaBrowser.Api/SearchService.cs +++ b/MediaBrowser.Api/SearchService.cs @@ -192,14 +192,14 @@ namespace MediaBrowser.Api { result.Series = season.Series.Name; - result.EpisodeCount = season.GetRecursiveChildren(i => i is Episode).Count; + result.EpisodeCount = season.GetRecursiveChildren().Count(i => i is Episode); } var series = item as Series; if (series != null) { - result.EpisodeCount = series.GetRecursiveChildren(i => i is Episode).Count; + result.EpisodeCount = series.GetRecursiveChildren().Count(i => i is Episode); } var album = item as MusicAlbum; diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs index 660b55f30..2da29928b 100644 --- a/MediaBrowser.Api/SimilarItemsHelper.cs +++ b/MediaBrowser.Api/SimilarItemsHelper.cs @@ -78,8 +78,8 @@ namespace MediaBrowser.Api var fields = request.GetItemFields().ToList(); var inputItems = user == null - ? libraryManager.RootFolder.GetRecursiveChildren(i => i.Id != item.Id) - : user.RootFolder.GetRecursiveChildren(user, i => i.Id != item.Id); + ? libraryManager.RootFolder.GetRecursiveChildren().Where(i => i.Id != item.Id) + : user.RootFolder.GetRecursiveChildren(user).Where(i => i.Id != item.Id); var items = GetSimilaritems(item, inputItems.Where(includeInSearch), getSimilarityScore) .ToList(); diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index 96bbd6dff..a1625d052 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -541,7 +541,8 @@ namespace MediaBrowser.Api.UserLibrary if (series != null) { var dtos = series - .GetRecursiveChildren(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0) + .GetRecursiveChildren() + .Where(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0) .OrderBy(i => { if (i.PremiereDate.HasValue) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index c712d50af..52c414ae8 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -856,19 +856,6 @@ namespace MediaBrowser.Controller.Entities /// IEnumerable{BaseItem}. /// public IEnumerable GetRecursiveChildren(User user, bool includeLinkedChildren = true) - { - return GetRecursiveChildren(user, null, includeLinkedChildren); - } - - /// - /// Gets the recursive children. - /// - /// The user. - /// The filter. - /// if set to true [include linked children]. - /// IList{BaseItem}. - /// - public IList GetRecursiveChildren(User user, Func filter, bool includeLinkedChildren = true) { if (user == null) { @@ -877,7 +864,7 @@ namespace MediaBrowser.Controller.Entities var list = new List(); - var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, true, filter); + var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, true, null); return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list; } @@ -887,20 +874,10 @@ namespace MediaBrowser.Controller.Entities /// /// IList{BaseItem}. public IList GetRecursiveChildren() - { - return GetRecursiveChildren(i => true); - } - - /// - /// Gets the recursive children. - /// - /// The filter. - /// IEnumerable{BaseItem}. - public IList GetRecursiveChildren(Func filter) { var list = new List(); - AddChildrenToList(list, true, filter); + AddChildrenToList(list, true, null); return list; } diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index ce188554c..86099fdc0 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -18,11 +18,17 @@ namespace MediaBrowser.Controller.Entities switch (ViewType) { case CollectionType.Games: - return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)).OfType(); + return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)) + .OfType(); case CollectionType.BoxSets: - return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)).OfType(); + return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)) + .OfType(); case CollectionType.TvShows: - return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)).OfType(); + return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)) + .OfType(); + case CollectionType.Trailers: + return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)) + .OfType(); default: return mediaFolders.SelectMany(i => i.GetChildren(user, includeLinkedChildren)); } diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index 4f008571c..33c6f980c 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -217,6 +217,7 @@ namespace MediaBrowser.Model.ApiClient /// Gets the additional parts. /// /// The item identifier. + /// The user identifier. /// Task{BaseItemDto[]}. Task GetAdditionalParts(string itemId, string userId); @@ -241,6 +242,12 @@ namespace MediaBrowser.Model.ApiClient /// Task{SessionInfoDto[]}. Task GetClientSessionsAsync(SessionQuery query); + /// + /// Gets the client session asynchronous. + /// + /// Task{SessionInfoDto}. + Task GetCurrentSessionAsync(); + /// /// Gets the item counts async. /// diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index 24a41863e..0834452fb 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -93,8 +93,6 @@ namespace MediaBrowser.Model.Configuration BlockUnratedItems = new UnratedItem[] { }; ExcludeFoldersFromGrouping = new string[] { }; - - DisplayCollectionsView = true; } } } diff --git a/MediaBrowser.Model/Dto/StudioDto.cs b/MediaBrowser.Model/Dto/StudioDto.cs index 4f21784fd..a0027cc4e 100644 --- a/MediaBrowser.Model/Dto/StudioDto.cs +++ b/MediaBrowser.Model/Dto/StudioDto.cs @@ -1,5 +1,4 @@ -using System; -using System.ComponentModel; +using System.ComponentModel; using System.Diagnostics; using System.Runtime.Serialization; @@ -17,6 +16,12 @@ namespace MediaBrowser.Model.Dto /// The name. public string Name { get; set; } + /// + /// Gets or sets the identifier. + /// + /// The identifier. + public string Id { get; set; } + /// /// Gets or sets the primary image tag. /// diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 7f63dac33..73216ca33 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -328,7 +328,8 @@ namespace MediaBrowser.Server.Implementations.Dto if (!string.IsNullOrEmpty(item.Album)) { var parentAlbum = _libraryManager.RootFolder - .GetRecursiveChildren(i => i is MusicAlbum) + .GetRecursiveChildren() + .Where(i => i is MusicAlbum) .FirstOrDefault(i => string.Equals(i.Name, item.Album, StringComparison.OrdinalIgnoreCase)); if (parentAlbum != null) @@ -539,6 +540,7 @@ namespace MediaBrowser.Server.Implementations.Dto if (dictionary.TryGetValue(studio, out entity)) { + studioDto.Id = entity.Id.ToString("N"); studioDto.PrimaryImageTag = GetImageCacheTag(entity, ImageType.Primary); } @@ -1248,7 +1250,8 @@ namespace MediaBrowser.Server.Implementations.Dto } else { - children = folder.GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual); + children = folder.GetRecursiveChildren(user) + .Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual); } // Loop through each recursive child diff --git a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs index 4c65fad68..6faa72b81 100644 --- a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs +++ b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs @@ -41,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.Library { var user = _userManager.GetUserById(new Guid(query.UserId)); - inputItems = user.RootFolder.GetRecursiveChildren(user, null); + inputItems = user.RootFolder.GetRecursiveChildren(user, true); } diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs index 24e4f0ef4..dd2978b17 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Querying; namespace MediaBrowser.Server.Implementations.Library { @@ -84,7 +85,7 @@ namespace MediaBrowser.Server.Implementations.Library if (user.Configuration.DisplayCollectionsView || recursiveChildren.OfType().Any()) { - list.Add(await GetUserView(CollectionType.BoxSets, user, "zzz_" + CollectionType.BoxSets, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(CollectionType.BoxSets, user, CollectionType.BoxSets, cancellationToken).ConfigureAwait(false)); } if (query.IncludeExternalContent) @@ -114,7 +115,7 @@ namespace MediaBrowser.Server.Implementations.Library } } - return list.OrderBy(i => i.SortName); + return _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).Cast(); } private Task GetUserView(string type, User user, string sortName, CancellationToken cancellationToken)