2
0
Luke Pulverenti 7 жил өмнө
parent
commit
39c4542cf6
34 өөрчлөгдсөн 229 нэмэгдсэн , 147 устгасан
  1. 8 10
      Emby.Dlna/ContentDirectory/ControlHandler.cs
  2. 7 6
      Emby.Server.Implementations/Channels/ChannelManager.cs
  3. 3 15
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  4. 3 0
      Emby.Server.Implementations/HttpServer/FileWriter.cs
  5. 32 2
      Emby.Server.Implementations/Library/LibraryManager.cs
  6. 2 1
      Emby.Server.Implementations/Library/MusicManager.cs
  7. 2 1
      Emby.Server.Implementations/Library/SearchEngine.cs
  8. 1 2
      Emby.Server.Implementations/Library/UserViewManager.cs
  9. 1 2
      Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  10. 17 17
      Emby.Server.Implementations/LiveTv/LiveTvManager.cs
  11. 4 3
      Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
  12. 4 8
      Emby.Server.Implementations/TV/TVSeriesManager.cs
  13. 1 1
      Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
  14. 12 3
      MediaBrowser.Api/ChannelService.cs
  15. 3 3
      MediaBrowser.Api/LiveTv/LiveTvService.cs
  16. 2 4
      MediaBrowser.Api/Movies/MoviesService.cs
  17. 1 2
      MediaBrowser.Api/Reports/ReportsService.cs
  18. 3 1
      MediaBrowser.Api/SuggestionsService.cs
  19. 1 2
      MediaBrowser.Api/TvShowsService.cs
  20. 1 1
      MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
  21. 30 5
      MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
  22. 1 2
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  23. 1 2
      MediaBrowser.Controller/Channels/Channel.cs
  24. 3 4
      MediaBrowser.Controller/Entities/Folder.cs
  25. 2 7
      MediaBrowser.Controller/Entities/InternalItemsQuery.cs
  26. 6 6
      MediaBrowser.Controller/Entities/TV/Series.cs
  27. 7 11
      MediaBrowser.Controller/Entities/UserViewBuilder.cs
  28. 2 2
      MediaBrowser.Controller/Library/ILibraryManager.cs
  29. 3 5
      MediaBrowser.Controller/Playlists/Playlist.cs
  30. 5 4
      MediaBrowser.Model/Channels/ChannelItemQuery.cs
  31. 2 12
      MediaBrowser.Model/LiveTv/ProgramQuery.cs
  32. 1 1
      Nuget/MediaBrowser.Common.nuspec
  33. 2 2
      Nuget/MediaBrowser.Server.Core.nuspec
  34. 56 0
      SocketHttpListener/Net/HttpResponseStream.Managed.cs

+ 8 - 10
Emby.Dlna/ContentDirectory/ControlHandler.cs

@@ -458,8 +458,7 @@ namespace Emby.Dlna.ContentDirectory
             {
             {
                 Limit = limit,
                 Limit = limit,
                 StartIndex = startIndex,
                 StartIndex = startIndex,
-                SortBy = sortOrders.ToArray(sortOrders.Count),
-                SortOrder = sort.SortOrder,
+                OrderBy = sortOrders.Select(i => new Tuple<string, SortOrder>(i, sort.SortOrder)).ToArray(),
                 User = user,
                 User = user,
                 Recursive = true,
                 Recursive = true,
                 IsMissing = false,
                 IsMissing = false,
@@ -828,7 +827,7 @@ namespace Emby.Dlna.ContentDirectory
             query.Parent = parent;
             query.Parent = parent;
             query.SetUser(user);
             query.SetUser(user);
 
 
-            query.OrderBy = new List<Tuple<string, SortOrder>>
+            query.OrderBy = new Tuple<string, SortOrder>[]
             {
             {
                 new Tuple<string, SortOrder> (ItemSortBy.DatePlayed, SortOrder.Descending),
                 new Tuple<string, SortOrder> (ItemSortBy.DatePlayed, SortOrder.Descending),
                 new Tuple<string, SortOrder> (ItemSortBy.SortName, SortOrder.Ascending)
                 new Tuple<string, SortOrder> (ItemSortBy.SortName, SortOrder.Ascending)
@@ -1077,7 +1076,7 @@ namespace Emby.Dlna.ContentDirectory
 
 
         private QueryResult<ServerItem> GetMusicLatest(BaseItem parent, User user, InternalItemsQuery query)
         private QueryResult<ServerItem> GetMusicLatest(BaseItem parent, User user, InternalItemsQuery query)
         {
         {
-            query.SortBy = new string[] { };
+            query.OrderBy = new Tuple<string, SortOrder>[] { };
 
 
             var items = _userViewManager.GetLatestItems(new LatestItemsQuery
             var items = _userViewManager.GetLatestItems(new LatestItemsQuery
             {
             {
@@ -1094,7 +1093,7 @@ namespace Emby.Dlna.ContentDirectory
 
 
         private QueryResult<ServerItem> GetNextUp(BaseItem parent, User user, InternalItemsQuery query)
         private QueryResult<ServerItem> GetNextUp(BaseItem parent, User user, InternalItemsQuery query)
         {
         {
-            query.SortBy = new string[] { };
+            query.OrderBy = new Tuple<string, SortOrder>[] { };
 
 
             var result = _tvSeriesManager.GetNextUp(new NextUpQuery
             var result = _tvSeriesManager.GetNextUp(new NextUpQuery
             {
             {
@@ -1109,7 +1108,7 @@ namespace Emby.Dlna.ContentDirectory
 
 
         private QueryResult<ServerItem> GetTvLatest(BaseItem parent, User user, InternalItemsQuery query)
         private QueryResult<ServerItem> GetTvLatest(BaseItem parent, User user, InternalItemsQuery query)
         {
         {
-            query.SortBy = new string[] { };
+            query.OrderBy = new Tuple<string, SortOrder>[] { };
 
 
             var items = _userViewManager.GetLatestItems(new LatestItemsQuery
             var items = _userViewManager.GetLatestItems(new LatestItemsQuery
             {
             {
@@ -1126,7 +1125,7 @@ namespace Emby.Dlna.ContentDirectory
 
 
         private QueryResult<ServerItem> GetMovieLatest(BaseItem parent, User user, InternalItemsQuery query)
         private QueryResult<ServerItem> GetMovieLatest(BaseItem parent, User user, InternalItemsQuery query)
         {
         {
-            query.SortBy = new string[] { };
+            query.OrderBy = new Tuple<string, SortOrder>[] { };
 
 
             var items = _userViewManager.GetLatestItems(new LatestItemsQuery
             var items = _userViewManager.GetLatestItems(new LatestItemsQuery
             {
             {
@@ -1236,8 +1235,7 @@ namespace Emby.Dlna.ContentDirectory
                 sortOrders.Add(ItemSortBy.SortName);
                 sortOrders.Add(ItemSortBy.SortName);
             }
             }
 
 
-            query.SortBy = sortOrders.ToArray(sortOrders.Count);
-            query.SortOrder = sort.SortOrder;
+            query.OrderBy = sortOrders.Select(i => new Tuple<string, SortOrder>(i, sort.SortOrder)).ToArray();
         }
         }
 
 
         private QueryResult<ServerItem> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
         private QueryResult<ServerItem> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
@@ -1246,7 +1244,7 @@ namespace Emby.Dlna.ContentDirectory
             {
             {
                 PersonIds = new[] { person.Id.ToString("N") },
                 PersonIds = new[] { person.Id.ToString("N") },
                 IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name, typeof(Trailer).Name },
                 IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name, typeof(Trailer).Name },
-                SortBy = new[] { ItemSortBy.SortName },
+                OrderBy = new[] { ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
                 Limit = limit,
                 Limit = limit,
                 StartIndex = startIndex,
                 StartIndex = startIndex,
                 DtoOptions = GetDtoOptions()
                 DtoOptions = GetDtoOptions()

+ 7 - 6
Emby.Server.Implementations/Channels/ChannelManager.cs

@@ -466,7 +466,7 @@ namespace Emby.Server.Implementations.Channels
             return _libraryManager.GetItemIds(new InternalItemsQuery
             return _libraryManager.GetItemIds(new InternalItemsQuery
             {
             {
                 IncludeItemTypes = new[] { typeof(Channel).Name },
                 IncludeItemTypes = new[] { typeof(Channel).Name },
-                SortBy = new[] { ItemSortBy.SortName }
+                OrderBy = new Tuple<string, SortOrder>[] { new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) }
 
 
             }).Select(i => GetChannelFeatures(i.ToString("N"))).ToArray();
             }).Select(i => GetChannelFeatures(i.ToString("N"))).ToArray();
         }
         }
@@ -932,14 +932,15 @@ namespace Emby.Server.Implementations.Channels
 
 
             ChannelItemSortField? sortField = null;
             ChannelItemSortField? sortField = null;
             ChannelItemSortField parsedField;
             ChannelItemSortField parsedField;
-            if (query.SortBy.Length == 1 &&
-                Enum.TryParse(query.SortBy[0], true, out parsedField))
+            var sortDescending = false;
+
+            if (query.OrderBy.Length == 1 &&
+                Enum.TryParse(query.OrderBy[0].Item1, true, out parsedField))
             {
             {
                 sortField = parsedField;
                 sortField = parsedField;
+                sortDescending = query.OrderBy[0].Item2 == SortOrder.Descending;
             }
             }
 
 
-            var sortDescending = query.SortOrder.HasValue && query.SortOrder.Value == SortOrder.Descending;
-
             var itemsResult = await GetChannelItems(channelProvider,
             var itemsResult = await GetChannelItems(channelProvider,
                 user,
                 user,
                 query.FolderId,
                 query.FolderId,
@@ -1166,7 +1167,7 @@ namespace Emby.Server.Implementations.Channels
         {
         {
             items = ApplyFilters(items, query.Filters, user);
             items = ApplyFilters(items, query.Filters, user);
 
 
-            items = _libraryManager.Sort(items, user, query.SortBy, query.SortOrder ?? SortOrder.Ascending);
+            items = _libraryManager.Sort(items, user, query.OrderBy);
 
 
             var all = items.ToList();
             var all = items.ToList();
             var totalCount = totalCountFromProvider ?? all.Count;
             var totalCount = totalCountFromProvider ?? all.Count;

+ 3 - 15
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -2135,8 +2135,7 @@ namespace Emby.Server.Implementations.Data
                 //return true;
                 //return true;
             }
             }
 
 
-            var sortingFields = query.SortBy.ToList();
-            sortingFields.AddRange(query.OrderBy.Select(i => i.Item1));
+            var sortingFields = query.OrderBy.Select(i => i.Item1).ToList();
 
 
             if (sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase))
             if (sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase))
             {
             {
@@ -2975,16 +2974,7 @@ namespace Emby.Server.Implementations.Data
         private string GetOrderByText(InternalItemsQuery query)
         private string GetOrderByText(InternalItemsQuery query)
         {
         {
             var orderBy = query.OrderBy.ToList();
             var orderBy = query.OrderBy.ToList();
-            var enableOrderInversion = true;
-
-            if (orderBy.Count == 0)
-            {
-                orderBy.AddRange(query.SortBy.Select(i => new Tuple<string, SortOrder>(i, query.SortOrder)));
-            }
-            else
-            {
-                enableOrderInversion = false;
-            }
+            var enableOrderInversion = false;
 
 
             if (query.SimilarTo != null)
             if (query.SimilarTo != null)
             {
             {
@@ -2993,12 +2983,10 @@ namespace Emby.Server.Implementations.Data
                     orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
                     orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
                     orderBy.Add(new Tuple<string, SortOrder>("SimilarityScore", SortOrder.Descending));
                     orderBy.Add(new Tuple<string, SortOrder>("SimilarityScore", SortOrder.Descending));
                     //orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
                     //orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
-                    query.SortOrder = SortOrder.Descending;
-                    enableOrderInversion = false;
                 }
                 }
             }
             }
 
 
-            query.OrderBy = orderBy;
+            query.OrderBy = orderBy.ToArray();
 
 
             if (orderBy.Count == 0)
             if (orderBy.Count == 0)
             {
             {

+ 3 - 0
Emby.Server.Implementations/HttpServer/FileWriter.cs

@@ -160,6 +160,9 @@ namespace Emby.Server.Implementations.HttpServer
                 if (string.IsNullOrWhiteSpace(RangeHeader) || (RangeStart <= 0 && RangeEnd >= TotalContentLength - 1))
                 if (string.IsNullOrWhiteSpace(RangeHeader) || (RangeStart <= 0 && RangeEnd >= TotalContentLength - 1))
                 {
                 {
                     Logger.Info("Transmit file {0}", Path);
                     Logger.Info("Transmit file {0}", Path);
+
+                    //var count = FileShare == FileShareMode.ReadWrite ? TotalContentLength : 0;
+
                     await response.TransmitFile(Path, 0, 0, FileShare, cancellationToken).ConfigureAwait(false);
                     await response.TransmitFile(Path, 0, 0, FileShare, cancellationToken).ConfigureAwait(false);
                     return;
                     return;
                 }
                 }

+ 32 - 2
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -846,8 +846,7 @@ namespace Emby.Server.Implementations.Library
             {
             {
                 Path = path,
                 Path = path,
                 IsFolder = isFolder,
                 IsFolder = isFolder,
-                SortBy = new[] { ItemSortBy.DateCreated },
-                SortOrder = SortOrder.Descending,
+                OrderBy = new[] { ItemSortBy.DateCreated }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
                 Limit = 1,
                 Limit = 1,
                 DtoOptions = new DtoOptions(true)
                 DtoOptions = new DtoOptions(true)
             };
             };
@@ -1777,6 +1776,37 @@ namespace Emby.Server.Implementations.Library
             return orderedItems ?? items;
             return orderedItems ?? items;
         }
         }
 
 
+        public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<Tuple<string, SortOrder>> orderByList)
+        {
+            var isFirst = true;
+
+            IOrderedEnumerable<BaseItem> orderedItems = null;
+
+            foreach (var orderBy in orderByList)
+            {
+                var comparer = GetComparer(orderBy.Item1, user);
+                if (comparer == null)
+                {
+                    continue;
+                }
+
+                var sortOrder = orderBy.Item2;
+
+                if (isFirst)
+                {
+                    orderedItems = sortOrder == SortOrder.Descending ? items.OrderByDescending(i => i, comparer) : items.OrderBy(i => i, comparer);
+                }
+                else
+                {
+                    orderedItems = sortOrder == SortOrder.Descending ? orderedItems.ThenByDescending(i => i, comparer) : orderedItems.ThenBy(i => i, comparer);
+                }
+
+                isFirst = false;
+            }
+
+            return orderedItems ?? items;
+        }
+
         /// <summary>
         /// <summary>
         /// Gets the comparer.
         /// Gets the comparer.
         /// </summary>
         /// </summary>

+ 2 - 1
Emby.Server.Implementations/Library/MusicManager.cs

@@ -6,6 +6,7 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Dto;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
 
 
 namespace Emby.Server.Implementations.Library
 namespace Emby.Server.Implementations.Library
@@ -88,7 +89,7 @@ namespace Emby.Server.Implementations.Library
 
 
                 Limit = 200,
                 Limit = 200,
 
 
-                SortBy = new[] { ItemSortBy.Random },
+                OrderBy = new [] { new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) },
 
 
                 DtoOptions = dtoOptions
                 DtoOptions = dtoOptions
 
 

+ 2 - 1
Emby.Server.Implementations/Library/SearchEngine.cs

@@ -10,6 +10,7 @@ using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Extensions;
 using MediaBrowser.Controller.Extensions;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Extensions;
 
 
 namespace Emby.Server.Implementations.Library
 namespace Emby.Server.Implementations.Library
@@ -169,7 +170,7 @@ namespace Emby.Server.Implementations.Library
                 Limit = query.Limit,
                 Limit = query.Limit,
                 IncludeItemsByName = string.IsNullOrWhiteSpace(query.ParentId),
                 IncludeItemsByName = string.IsNullOrWhiteSpace(query.ParentId),
                 ParentId = string.IsNullOrWhiteSpace(query.ParentId) ? (Guid?)null : new Guid(query.ParentId),
                 ParentId = string.IsNullOrWhiteSpace(query.ParentId) ? (Guid?)null : new Guid(query.ParentId),
-                SortBy = new[] { ItemSortBy.SortName },
+                OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) },
                 Recursive = true,
                 Recursive = true,
 
 
                 IsKids = query.IsKids,
                 IsKids = query.IsKids,

+ 1 - 2
Emby.Server.Implementations/Library/UserViewManager.cs

@@ -319,8 +319,7 @@ namespace Emby.Server.Implementations.Library
             var query = new InternalItemsQuery(user)
             var query = new InternalItemsQuery(user)
             {
             {
                 IncludeItemTypes = includeItemTypes,
                 IncludeItemTypes = includeItemTypes,
-                SortOrder = SortOrder.Descending,
-                SortBy = new[] { ItemSortBy.DateCreated },
+                OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) },
                 IsFolder = includeItemTypes.Length == 0 ? false : (bool?)null,
                 IsFolder = includeItemTypes.Length == 0 ? false : (bool?)null,
                 ExcludeItemTypes = excludeItemTypes,
                 ExcludeItemTypes = excludeItemTypes,
                 IsVirtualItem = false,
                 IsVirtualItem = false,

+ 1 - 2
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -1687,8 +1687,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
 
                 var episodesToDelete = (librarySeries.GetItemList(new InternalItemsQuery
                 var episodesToDelete = (librarySeries.GetItemList(new InternalItemsQuery
                 {
                 {
-                    SortBy = new[] { ItemSortBy.DateCreated },
-                    SortOrder = SortOrder.Descending,
+                    OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) },
                     IsVirtualItem = false,
                     IsVirtualItem = false,
                     IsFolder = false,
                     IsFolder = false,
                     Recursive = true,
                     Recursive = true,

+ 17 - 17
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -187,7 +187,6 @@ namespace Emby.Server.Implementations.LiveTv
                 IsSports = query.IsSports,
                 IsSports = query.IsSports,
                 IsSeries = query.IsSeries,
                 IsSeries = query.IsSeries,
                 IncludeItemTypes = new[] { typeof(LiveTvChannel).Name },
                 IncludeItemTypes = new[] { typeof(LiveTvChannel).Name },
-                SortOrder = query.SortOrder ?? SortOrder.Ascending,
                 TopParentIds = new[] { topFolder.Id.ToString("N") },
                 TopParentIds = new[] { topFolder.Id.ToString("N") },
                 IsFavorite = query.IsFavorite,
                 IsFavorite = query.IsFavorite,
                 IsLiked = query.IsLiked,
                 IsLiked = query.IsLiked,
@@ -196,18 +195,22 @@ namespace Emby.Server.Implementations.LiveTv
                 DtoOptions = dtoOptions
                 DtoOptions = dtoOptions
             };
             };
 
 
-            internalQuery.OrderBy.AddRange(query.SortBy.Select(i => new Tuple<string, SortOrder>(i, query.SortOrder ?? SortOrder.Ascending)));
+            var orderBy = internalQuery.OrderBy.ToList();
+
+            orderBy.AddRange(query.SortBy.Select(i => new Tuple<string, SortOrder>(i, query.SortOrder ?? SortOrder.Ascending)));
 
 
             if (query.EnableFavoriteSorting)
             if (query.EnableFavoriteSorting)
             {
             {
-                internalQuery.OrderBy.Insert(0, new Tuple<string, SortOrder>(ItemSortBy.IsFavoriteOrLiked, SortOrder.Descending));
+                orderBy.Insert(0, new Tuple<string, SortOrder>(ItemSortBy.IsFavoriteOrLiked, SortOrder.Descending));
             }
             }
 
 
             if (!internalQuery.OrderBy.Any(i => string.Equals(i.Item1, ItemSortBy.SortName, StringComparison.OrdinalIgnoreCase)))
             if (!internalQuery.OrderBy.Any(i => string.Equals(i.Item1, ItemSortBy.SortName, StringComparison.OrdinalIgnoreCase)))
             {
             {
-                internalQuery.OrderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending));
+                orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending));
             }
             }
 
 
+            internalQuery.OrderBy = orderBy.ToArray();
+
             return _libraryManager.GetItemsResult(internalQuery);
             return _libraryManager.GetItemsResult(internalQuery);
         }
         }
 
 
@@ -918,10 +921,10 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             var topFolder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
             var topFolder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
 
 
-            if (query.SortBy.Length == 0)
+            if (query.OrderBy.Length == 0)
             {
             {
                 // Unless something else was specified, order by start date to take advantage of a specialized index
                 // Unless something else was specified, order by start date to take advantage of a specialized index
-                query.SortBy = new[] { ItemSortBy.StartDate };
+                query.OrderBy = new Tuple<string, SortOrder>[] { new Tuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending) };
             }
             }
 
 
             RemoveFields(options);
             RemoveFields(options);
@@ -942,8 +945,7 @@ namespace Emby.Server.Implementations.LiveTv
                 Genres = query.Genres,
                 Genres = query.Genres,
                 StartIndex = query.StartIndex,
                 StartIndex = query.StartIndex,
                 Limit = query.Limit,
                 Limit = query.Limit,
-                SortBy = query.SortBy,
-                SortOrder = query.SortOrder ?? SortOrder.Ascending,
+                OrderBy = query.OrderBy,
                 EnableTotalRecordCount = query.EnableTotalRecordCount,
                 EnableTotalRecordCount = query.EnableTotalRecordCount,
                 TopParentIds = new[] { topFolder.Id.ToString("N") },
                 TopParentIds = new[] { topFolder.Id.ToString("N") },
                 Name = query.Name,
                 Name = query.Name,
@@ -1012,7 +1014,7 @@ namespace Emby.Server.Implementations.LiveTv
                 IsSports = query.IsSports,
                 IsSports = query.IsSports,
                 IsKids = query.IsKids,
                 IsKids = query.IsKids,
                 EnableTotalRecordCount = query.EnableTotalRecordCount,
                 EnableTotalRecordCount = query.EnableTotalRecordCount,
-                SortBy = new[] { ItemSortBy.StartDate },
+                OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending) },
                 TopParentIds = new[] { topFolder.Id.ToString("N") },
                 TopParentIds = new[] { topFolder.Id.ToString("N") },
                 DtoOptions = options
                 DtoOptions = options
             };
             };
@@ -1644,8 +1646,7 @@ namespace Emby.Server.Implementations.LiveTv
                 IsVirtualItem = false,
                 IsVirtualItem = false,
                 Limit = query.Limit,
                 Limit = query.Limit,
                 StartIndex = query.StartIndex,
                 StartIndex = query.StartIndex,
-                SortBy = new[] { ItemSortBy.DateCreated },
-                SortOrder = SortOrder.Descending,
+                OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) },
                 EnableTotalRecordCount = query.EnableTotalRecordCount,
                 EnableTotalRecordCount = query.EnableTotalRecordCount,
                 IncludeItemTypes = includeItemTypes.ToArray(includeItemTypes.Count),
                 IncludeItemTypes = includeItemTypes.ToArray(includeItemTypes.Count),
                 ExcludeItemTypes = excludeItemTypes.ToArray(excludeItemTypes.Count),
                 ExcludeItemTypes = excludeItemTypes.ToArray(excludeItemTypes.Count),
@@ -1692,8 +1693,7 @@ namespace Emby.Server.Implementations.LiveTv
                 Recursive = true,
                 Recursive = true,
                 AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(folders.Count),
                 AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(folders.Count),
                 Limit = query.Limit,
                 Limit = query.Limit,
-                SortBy = new[] { ItemSortBy.DateCreated },
-                SortOrder = SortOrder.Descending,
+                OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) },
                 EnableTotalRecordCount = query.EnableTotalRecordCount,
                 EnableTotalRecordCount = query.EnableTotalRecordCount,
                 IncludeItemTypes = includeItemTypes.ToArray(includeItemTypes.Count),
                 IncludeItemTypes = includeItemTypes.ToArray(includeItemTypes.Count),
                 ExcludeItemTypes = excludeItemTypes.ToArray(excludeItemTypes.Count),
                 ExcludeItemTypes = excludeItemTypes.ToArray(excludeItemTypes.Count),
@@ -1927,11 +1927,11 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             var info = recording;
             var info = recording;
 
 
-            dto.SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId)
+            dto.SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) || service == null
                 ? null
                 ? null
                 : _tvDtoService.GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N");
                 : _tvDtoService.GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N");
 
 
-            dto.TimerId = string.IsNullOrEmpty(info.TimerId)
+            dto.TimerId = string.IsNullOrEmpty(info.TimerId) || service == null
                 ? null
                 ? null
                 : _tvDtoService.GetInternalTimerId(service.Name, info.TimerId).ToString("N");
                 : _tvDtoService.GetInternalTimerId(service.Name, info.TimerId).ToString("N");
 
 
@@ -2037,7 +2037,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             var internalResult = await GetInternalRecordings(query, options, cancellationToken).ConfigureAwait(false);
             var internalResult = await GetInternalRecordings(query, options, cancellationToken).ConfigureAwait(false);
 
 
-            var returnArray =  _dtoService.GetBaseItemDtos(internalResult.Items, options, user);
+            var returnArray = _dtoService.GetBaseItemDtos(internalResult.Items, options, user);
 
 
             return new QueryResult<BaseItemDto>
             return new QueryResult<BaseItemDto>
             {
             {
@@ -2377,7 +2377,7 @@ namespace Emby.Server.Implementations.LiveTv
                 MaxStartDate = now,
                 MaxStartDate = now,
                 MinEndDate = now,
                 MinEndDate = now,
                 Limit = channelIds.Length,
                 Limit = channelIds.Length,
-                SortBy = new[] { "StartDate" },
+                OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending) },
                 TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Result.Id.ToString("N") },
                 TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Result.Id.ToString("N") },
                 DtoOptions = options
                 DtoOptions = options
 
 

+ 4 - 3
Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Configuration;
+using System;
+using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Audio;
@@ -86,7 +87,7 @@ namespace Emby.Server.Implementations.Playlists
             {
             {
                 Genres = new[] { item.Name },
                 Genres = new[] { item.Name },
                 IncludeItemTypes = new[] { typeof(MusicAlbum).Name, typeof(MusicVideo).Name, typeof(Audio).Name },
                 IncludeItemTypes = new[] { typeof(MusicAlbum).Name, typeof(MusicVideo).Name, typeof(Audio).Name },
-                SortBy = new[] { ItemSortBy.Random },
+                OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) },
                 Limit = 4,
                 Limit = 4,
                 Recursive = true,
                 Recursive = true,
                 ImageTypes = new[] { ImageType.Primary },
                 ImageTypes = new[] { ImageType.Primary },
@@ -118,7 +119,7 @@ namespace Emby.Server.Implementations.Playlists
             {
             {
                 Genres = new[] { item.Name },
                 Genres = new[] { item.Name },
                 IncludeItemTypes = new[] { typeof(Series).Name, typeof(Movie).Name },
                 IncludeItemTypes = new[] { typeof(Series).Name, typeof(Movie).Name },
-                SortBy = new[] { ItemSortBy.Random },
+                OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) },
                 Limit = 4,
                 Limit = 4,
                 Recursive = true,
                 Recursive = true,
                 ImageTypes = new[] { ImageType.Primary },
                 ImageTypes = new[] { ImageType.Primary },

+ 4 - 8
Emby.Server.Implementations/TV/TVSeriesManager.cs

@@ -64,8 +64,7 @@ namespace Emby.Server.Implementations.TV
             var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
             var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
             {
             {
                 IncludeItemTypes = new[] { typeof(Episode).Name },
                 IncludeItemTypes = new[] { typeof(Episode).Name },
-                SortBy = new[] { ItemSortBy.DatePlayed },
-                SortOrder = SortOrder.Descending,
+                OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.DatePlayed, SortOrder.Descending) },
                 SeriesPresentationUniqueKey = presentationUniqueKey,
                 SeriesPresentationUniqueKey = presentationUniqueKey,
                 Limit = limit,
                 Limit = limit,
                 ParentId = parentIdGuid,
                 ParentId = parentIdGuid,
@@ -122,8 +121,7 @@ namespace Emby.Server.Implementations.TV
             var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
             var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
             {
             {
                 IncludeItemTypes = new[] { typeof(Episode).Name },
                 IncludeItemTypes = new[] { typeof(Episode).Name },
-                SortBy = new[] { ItemSortBy.DatePlayed },
-                SortOrder = SortOrder.Descending,
+                OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.DatePlayed, SortOrder.Descending) },
                 SeriesPresentationUniqueKey = presentationUniqueKey,
                 SeriesPresentationUniqueKey = presentationUniqueKey,
                 Limit = limit,
                 Limit = limit,
                 DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions
                 DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions
@@ -200,8 +198,7 @@ namespace Emby.Server.Implementations.TV
                 AncestorWithPresentationUniqueKey = null,
                 AncestorWithPresentationUniqueKey = null,
                 SeriesPresentationUniqueKey = seriesKey,
                 SeriesPresentationUniqueKey = seriesKey,
                 IncludeItemTypes = new[] { typeof(Episode).Name },
                 IncludeItemTypes = new[] { typeof(Episode).Name },
-                SortBy = new[] { ItemSortBy.SortName },
-                SortOrder = SortOrder.Descending,
+                OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Descending) },
                 IsPlayed = true,
                 IsPlayed = true,
                 Limit = 1,
                 Limit = 1,
                 ParentIndexNumberNotEquals = 0,
                 ParentIndexNumberNotEquals = 0,
@@ -223,8 +220,7 @@ namespace Emby.Server.Implementations.TV
                     AncestorWithPresentationUniqueKey = null,
                     AncestorWithPresentationUniqueKey = null,
                     SeriesPresentationUniqueKey = seriesKey,
                     SeriesPresentationUniqueKey = seriesKey,
                     IncludeItemTypes = new[] { typeof(Episode).Name },
                     IncludeItemTypes = new[] { typeof(Episode).Name },
-                    SortBy = new[] { ItemSortBy.SortName },
-                    SortOrder = SortOrder.Ascending,
+                    OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) },
                     Limit = 1,
                     Limit = 1,
                     IsPlayed = false,
                     IsPlayed = false,
                     IsVirtualItem = false,
                     IsVirtualItem = false,

+ 1 - 1
Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs

@@ -145,7 +145,7 @@ namespace Emby.Server.Implementations.UserViews
                 Recursive = recursive,
                 Recursive = recursive,
                 IncludeItemTypes = new[] { typeof(BoxSet).Name },
                 IncludeItemTypes = new[] { typeof(BoxSet).Name },
                 Limit = 20,
                 Limit = 20,
-                SortBy = new[] { ItemSortBy.Random },
+                OrderBy = new [] { new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending) },
                 DtoOptions = new DtoOptions(false)
                 DtoOptions = new DtoOptions(false)
             });
             });
 
 

+ 12 - 3
MediaBrowser.Api/ChannelService.cs

@@ -9,6 +9,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Api.UserLibrary;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
 
 
 namespace MediaBrowser.Api
 namespace MediaBrowser.Api
@@ -90,7 +91,7 @@ namespace MediaBrowser.Api
         public int? Limit { get; set; }
         public int? Limit { get; set; }
 
 
         [ApiMember(Name = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         [ApiMember(Name = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public SortOrder? SortOrder { get; set; }
+        public string SortOrder { get; set; }
 
 
         [ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
         [ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
         public string Filters { get; set; }
         public string Filters { get; set; }
@@ -116,6 +117,15 @@ namespace MediaBrowser.Api
 
 
             return val.Split(',').Select(v => (ItemFilter)Enum.Parse(typeof(ItemFilter), v, true));
             return val.Split(',').Select(v => (ItemFilter)Enum.Parse(typeof(ItemFilter), v, true));
         }
         }
+
+        /// <summary>
+        /// Gets the order by.
+        /// </summary>
+        /// <returns>IEnumerable{ItemSortBy}.</returns>
+        public Tuple<string, SortOrder>[] GetOrderBy()
+        {
+            return BaseItemsRequest.GetOrderBy(SortBy, SortOrder);
+        }
     }
     }
 
 
     [Route("/Channels/Items/Latest", "GET", Summary = "Gets channel items")]
     [Route("/Channels/Items/Latest", "GET", Summary = "Gets channel items")]
@@ -228,8 +238,7 @@ namespace MediaBrowser.Api
                 UserId = request.UserId,
                 UserId = request.UserId,
                 ChannelId = request.Id,
                 ChannelId = request.Id,
                 FolderId = request.FolderId,
                 FolderId = request.FolderId,
-                SortOrder = request.SortOrder,
-                SortBy = (request.SortBy ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(),
+                OrderBy = request.GetOrderBy(),
                 Filters = request.GetFilters().ToArray(),
                 Filters = request.GetFilters().ToArray(),
                 Fields = request.GetItemFields()
                 Fields = request.GetItemFields()
 
 

+ 3 - 3
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -15,6 +15,7 @@ using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Api.UserLibrary;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 
 
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
@@ -373,7 +374,7 @@ namespace MediaBrowser.Api.LiveTv
         public string SortBy { get; set; }
         public string SortBy { get; set; }
 
 
         [ApiMember(Name = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         [ApiMember(Name = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public SortOrder? SortOrder { get; set; }
+        public string SortOrder { get; set; }
 
 
         [ApiMember(Name = "Genres", Description = "The genres to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
         [ApiMember(Name = "Genres", Description = "The genres to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
         public string Genres { get; set; }
         public string Genres { get; set; }
@@ -994,8 +995,7 @@ namespace MediaBrowser.Api.LiveTv
 
 
             query.StartIndex = request.StartIndex;
             query.StartIndex = request.StartIndex;
             query.Limit = request.Limit;
             query.Limit = request.Limit;
-            query.SortBy = (request.SortBy ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
-            query.SortOrder = request.SortOrder;
+            query.OrderBy = BaseItemsRequest.GetOrderBy(request.SortBy, request.SortOrder);
             query.IsNews = request.IsNews;
             query.IsNews = request.IsNews;
             query.IsMovie = request.IsMovie;
             query.IsMovie = request.IsMovie;
             query.IsSeries = request.IsSeries;
             query.IsSeries = request.IsSeries;

+ 2 - 4
MediaBrowser.Api/Movies/MoviesService.cs

@@ -193,8 +193,7 @@ namespace MediaBrowser.Api.Movies
                     //typeof(LiveTvProgram).Name
                     //typeof(LiveTvProgram).Name
                 },
                 },
                 // IsMovie = true
                 // IsMovie = true
-                SortBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.Random },
-                SortOrder = SortOrder.Descending,
+                OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.Random }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
                 Limit = 7,
                 Limit = 7,
                 ParentId = parentIdGuid,
                 ParentId = parentIdGuid,
                 Recursive = true,
                 Recursive = true,
@@ -215,8 +214,7 @@ namespace MediaBrowser.Api.Movies
             {
             {
                 IncludeItemTypes = itemTypes.ToArray(itemTypes.Count),
                 IncludeItemTypes = itemTypes.ToArray(itemTypes.Count),
                 IsMovie = true,
                 IsMovie = true,
-                SortBy = new[] { ItemSortBy.Random },
-                SortOrder = SortOrder.Descending,
+                OrderBy = new[] { ItemSortBy.Random }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
                 Limit = 10,
                 Limit = 10,
                 IsFavoriteOrLiked = true,
                 IsFavoriteOrLiked = true,
                 ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id.ToString("N")).ToArray(recentlyPlayedMovies.Count),
                 ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id.ToString("N")).ToArray(recentlyPlayedMovies.Count),

+ 1 - 2
MediaBrowser.Api/Reports/ReportsService.cs

@@ -176,8 +176,7 @@ namespace MediaBrowser.Api.Reports
                 IncludeItemTypes = request.GetIncludeItemTypes(),
                 IncludeItemTypes = request.GetIncludeItemTypes(),
                 ExcludeItemTypes = request.GetExcludeItemTypes(),
                 ExcludeItemTypes = request.GetExcludeItemTypes(),
                 Recursive = request.Recursive,
                 Recursive = request.Recursive,
-                SortBy = request.GetOrderBy(),
-                SortOrder = request.SortOrder ?? SortOrder.Ascending,
+                OrderBy = request.GetOrderBy(),
 
 
                 IsFavorite = request.IsFavorite,
                 IsFavorite = request.IsFavorite,
                 Limit = request.Limit,
                 Limit = request.Limit,

+ 3 - 1
MediaBrowser.Api/SuggestionsService.cs

@@ -5,8 +5,10 @@ using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
 using System;
 using System;
+using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Extensions;
 
 
 namespace MediaBrowser.Api
 namespace MediaBrowser.Api
@@ -79,7 +81,7 @@ namespace MediaBrowser.Api
         {
         {
             return _libraryManager.GetItemsResult(new InternalItemsQuery(user)
             return _libraryManager.GetItemsResult(new InternalItemsQuery(user)
             {
             {
-                SortBy = new string[] { ItemSortBy.Random },
+                OrderBy = new[] { ItemSortBy.Random }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
                 MediaTypes = request.GetMediaTypes(),
                 MediaTypes = request.GetMediaTypes(),
                 IncludeItemTypes = request.GetIncludeItemTypes(),
                 IncludeItemTypes = request.GetIncludeItemTypes(),
                 IsVirtualItem = false,
                 IsVirtualItem = false,

+ 1 - 2
MediaBrowser.Api/TvShowsService.cs

@@ -347,8 +347,7 @@ namespace MediaBrowser.Api
             var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
             var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
             {
             {
                 IncludeItemTypes = new[] { typeof(Episode).Name },
                 IncludeItemTypes = new[] { typeof(Episode).Name },
-                SortBy = new[] { "PremiereDate", "AirTime", "SortName" },
-                SortOrder = SortOrder.Ascending,
+                OrderBy = new[] { ItemSortBy.PremiereDate, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
                 MinPremiereDate = minPremiereDate,
                 MinPremiereDate = minPremiereDate,
                 StartIndex = request.StartIndex,
                 StartIndex = request.StartIndex,
                 Limit = request.Limit,
                 Limit = request.Limit,

+ 1 - 1
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs

@@ -299,7 +299,7 @@ namespace MediaBrowser.Api.UserLibrary
 
 
             var filteredItems = FilterItems(request, extractedItems, user);
             var filteredItems = FilterItems(request, extractedItems, user);
 
 
-            filteredItems = LibraryManager.Sort(filteredItems, user, request.GetOrderBy(), request.SortOrder ?? SortOrder.Ascending);
+            filteredItems = LibraryManager.Sort(filteredItems, user, request.GetOrderBy());
 
 
             var ibnItemsArray = filteredItems.ToList();
             var ibnItemsArray = filteredItems.ToList();
 
 

+ 30 - 5
MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs

@@ -136,7 +136,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// </summary>
         /// <value>The sort order.</value>
         /// <value>The sort order.</value>
         [ApiMember(Name = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         [ApiMember(Name = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public SortOrder? SortOrder { get; set; }
+        public string SortOrder { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Specify this to localize the search to a specific item or folder. Omit to use the root.
         /// Specify this to localize the search to a specific item or folder. Omit to use the root.
@@ -467,16 +467,41 @@ namespace MediaBrowser.Api.UserLibrary
         /// Gets the order by.
         /// Gets the order by.
         /// </summary>
         /// </summary>
         /// <returns>IEnumerable{ItemSortBy}.</returns>
         /// <returns>IEnumerable{ItemSortBy}.</returns>
-        public string[] GetOrderBy()
+        public Tuple<string, SortOrder>[] GetOrderBy()
         {
         {
-            var val = SortBy;
+            return GetOrderBy(SortBy, SortOrder);
+        }
+
+        public static Tuple<string, SortOrder>[] GetOrderBy(string sortBy, string requestedSortOrder)
+        {
+            var val = sortBy;
 
 
             if (string.IsNullOrEmpty(val))
             if (string.IsNullOrEmpty(val))
             {
             {
-                return new string[] { };
+                return new Tuple<string, Model.Entities.SortOrder>[] { };
+            }
+
+            var vals = val.Split(',');
+            if (string.IsNullOrWhiteSpace(requestedSortOrder))
+            {
+                requestedSortOrder = "Ascending";
+            }
+
+            var sortOrders = requestedSortOrder.Split(',');
+
+            var result = new Tuple<string, Model.Entities.SortOrder>[vals.Length];
+
+            for (var i = 0; i < vals.Length; i++)
+            {
+                var sortOrderIndex = sortOrders.Length > i ? i : 0;
+
+                var sortOrderValue = sortOrders.Length > sortOrderIndex ? sortOrders[sortOrderIndex] : null;
+                var sortOrder = string.Equals(sortOrderValue, "Descending", StringComparison.OrdinalIgnoreCase) ? MediaBrowser.Model.Entities.SortOrder.Descending : MediaBrowser.Model.Entities.SortOrder.Ascending;
+
+                result[i] = new Tuple<string, Model.Entities.SortOrder>(vals[i], sortOrder);
             }
             }
 
 
-            return val.Split(',');
+            return result;
         }
         }
     }
     }
 }
 }

+ 1 - 2
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -198,8 +198,7 @@ namespace MediaBrowser.Api.UserLibrary
                 IncludeItemTypes = request.GetIncludeItemTypes(),
                 IncludeItemTypes = request.GetIncludeItemTypes(),
                 ExcludeItemTypes = request.GetExcludeItemTypes(),
                 ExcludeItemTypes = request.GetExcludeItemTypes(),
                 Recursive = request.Recursive,
                 Recursive = request.Recursive,
-                SortBy = request.GetOrderBy(),
-                SortOrder = request.SortOrder ?? SortOrder.Ascending,
+                OrderBy = request.GetOrderBy(),
 
 
                 IsFavorite = request.IsFavorite,
                 IsFavorite = request.IsFavorite,
                 Limit = request.Limit,
                 Limit = request.Limit,

+ 1 - 2
MediaBrowser.Controller/Channels/Channel.cs

@@ -58,8 +58,7 @@ namespace MediaBrowser.Controller.Channels
                     Limit = query.Limit,
                     Limit = query.Limit,
                     StartIndex = query.StartIndex,
                     StartIndex = query.StartIndex,
                     UserId = query.User.Id.ToString("N"),
                     UserId = query.User.Id.ToString("N"),
-                    SortBy = query.SortBy,
-                    SortOrder = query.SortOrder
+                    OrderBy = query.OrderBy
 
 
                 }, new SimpleProgress<double>(), CancellationToken.None).Result;
                 }, new SimpleProgress<double>(), CancellationToken.None).Result;
             }
             }

+ 3 - 4
MediaBrowser.Controller/Entities/Folder.cs

@@ -952,7 +952,7 @@ namespace MediaBrowser.Controller.Entities
             {
             {
                 var result = LibraryManager.GetItemsResult(query);
                 var result = LibraryManager.GetItemsResult(query);
 
 
-                if (query.SortBy.Length == 0)
+                if (query.OrderBy.Length == 0)
                 {
                 {
                     var ids = query.ItemIds.ToList();
                     var ids = query.ItemIds.ToList();
 
 
@@ -973,7 +973,7 @@ namespace MediaBrowser.Controller.Entities
             {
             {
                 var result = LibraryManager.GetItemList(query);
                 var result = LibraryManager.GetItemList(query);
 
 
-                if (query.SortBy.Length == 0)
+                if (query.OrderBy.Length == 0)
                 {
                 {
                     var ids = query.ItemIds.ToList();
                     var ids = query.ItemIds.ToList();
 
 
@@ -1000,8 +1000,7 @@ namespace MediaBrowser.Controller.Entities
                         Limit = query.Limit,
                         Limit = query.Limit,
                         StartIndex = query.StartIndex,
                         StartIndex = query.StartIndex,
                         UserId = query.User.Id.ToString("N"),
                         UserId = query.User.Id.ToString("N"),
-                        SortBy = query.SortBy,
-                        SortOrder = query.SortOrder
+                        OrderBy = query.OrderBy
 
 
                     }, new SimpleProgress<double>(), CancellationToken.None).Result;
                     }, new SimpleProgress<double>(), CancellationToken.None).Result;
                 }
                 }

+ 2 - 7
MediaBrowser.Controller/Entities/InternalItemsQuery.cs

@@ -16,10 +16,6 @@ namespace MediaBrowser.Controller.Entities
 
 
         public int? Limit { get; set; }
         public int? Limit { get; set; }
 
 
-        public string[] SortBy { get; set; }
-
-        public SortOrder SortOrder { get; set; }
-
         public User User { get; set; }
         public User User { get; set; }
 
 
         public BaseItem SimilarTo { get; set; }
         public BaseItem SimilarTo { get; set; }
@@ -165,7 +161,7 @@ namespace MediaBrowser.Controller.Entities
         public Dictionary<string, string> ExcludeProviderIds { get; set; }
         public Dictionary<string, string> ExcludeProviderIds { get; set; }
         public bool EnableGroupByMetadataKey { get; set; }
         public bool EnableGroupByMetadataKey { get; set; }
 
 
-        public List<Tuple<string, SortOrder>> OrderBy { get; set; }
+        public Tuple<string, SortOrder>[] OrderBy { get; set; }
 
 
         public DateTime? MinDateCreated { get; set; }
         public DateTime? MinDateCreated { get; set; }
         public DateTime? MinDateLastSaved { get; set; }
         public DateTime? MinDateLastSaved { get; set; }
@@ -190,7 +186,6 @@ namespace MediaBrowser.Controller.Entities
             BlockUnratedItems = new UnratedItem[] { };
             BlockUnratedItems = new UnratedItem[] { };
             Tags = new string[] { };
             Tags = new string[] { };
             OfficialRatings = new string[] { };
             OfficialRatings = new string[] { };
-            SortBy = new string[] { };
             MediaTypes = new string[] { };
             MediaTypes = new string[] { };
             IncludeItemTypes = new string[] { };
             IncludeItemTypes = new string[] { };
             ExcludeItemTypes = new string[] { };
             ExcludeItemTypes = new string[] { };
@@ -213,7 +208,7 @@ namespace MediaBrowser.Controller.Entities
             TrailerTypes = new TrailerType[] { };
             TrailerTypes = new TrailerType[] { };
             SourceTypes = new SourceType[] { };
             SourceTypes = new SourceType[] { };
             SeriesStatuses = new SeriesStatus[] { };
             SeriesStatuses = new SeriesStatus[] { };
-            OrderBy = new List<Tuple<string, SortOrder>>();
+            OrderBy = new Tuple<string, SortOrder>[] { };
         }
         }
 
 
         public InternalItemsQuery(User user)
         public InternalItemsQuery(User user)

+ 6 - 6
MediaBrowser.Controller/Entities/TV/Series.cs

@@ -252,7 +252,7 @@ namespace MediaBrowser.Controller.Entities.TV
             query.AncestorWithPresentationUniqueKey = null;
             query.AncestorWithPresentationUniqueKey = null;
             query.SeriesPresentationUniqueKey = seriesKey;
             query.SeriesPresentationUniqueKey = seriesKey;
             query.IncludeItemTypes = new[] { typeof(Season).Name };
             query.IncludeItemTypes = new[] { typeof(Season).Name };
-            query.SortBy = new[] {ItemSortBy.SortName};
+            query.OrderBy = new[] { ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray();
 
 
             if (!config.DisplayMissingEpisodes)
             if (!config.DisplayMissingEpisodes)
             {
             {
@@ -275,9 +275,9 @@ namespace MediaBrowser.Controller.Entities.TV
 
 
                 query.AncestorWithPresentationUniqueKey = null;
                 query.AncestorWithPresentationUniqueKey = null;
                 query.SeriesPresentationUniqueKey = seriesKey;
                 query.SeriesPresentationUniqueKey = seriesKey;
-                if (query.SortBy.Length == 0)
+                if (query.OrderBy.Length == 0)
                 {
                 {
-                    query.SortBy = new[] { ItemSortBy.SortName };
+                    query.OrderBy = new[] { ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray();
                 }
                 }
                 if (query.IncludeItemTypes.Length == 0)
                 if (query.IncludeItemTypes.Length == 0)
                 {
                 {
@@ -301,7 +301,7 @@ namespace MediaBrowser.Controller.Entities.TV
                 AncestorWithPresentationUniqueKey = null,
                 AncestorWithPresentationUniqueKey = null,
                 SeriesPresentationUniqueKey = seriesKey,
                 SeriesPresentationUniqueKey = seriesKey,
                 IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name },
                 IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name },
-                SortBy = new[] { ItemSortBy.SortName },
+                OrderBy = new[] { ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
                 DtoOptions = options
                 DtoOptions = options
             };
             };
             var config = user.Configuration;
             var config = user.Configuration;
@@ -410,7 +410,7 @@ namespace MediaBrowser.Controller.Entities.TV
                 AncestorWithPresentationUniqueKey = queryFromSeries ? null : seriesKey,
                 AncestorWithPresentationUniqueKey = queryFromSeries ? null : seriesKey,
                 SeriesPresentationUniqueKey = queryFromSeries ? seriesKey : null,
                 SeriesPresentationUniqueKey = queryFromSeries ? seriesKey : null,
                 IncludeItemTypes = new[] { typeof(Episode).Name },
                 IncludeItemTypes = new[] { typeof(Episode).Name },
-                SortBy = new[] { ItemSortBy.SortName },
+                OrderBy = new[] { ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
                 DtoOptions = options
                 DtoOptions = options
             };
             };
             if (user != null)
             if (user != null)
@@ -453,7 +453,7 @@ namespace MediaBrowser.Controller.Entities.TV
 
 
             return episodes.Where(episode =>
             return episodes.Where(episode =>
             {
             {
-                var episodeItem = (Episode) episode;
+                var episodeItem = (Episode)episode;
 
 
                 var currentSeasonNumber = supportSpecialsInSeason ? episodeItem.AiredSeasonNumber : episode.ParentIndexNumber;
                 var currentSeasonNumber = supportSpecialsInSeason ? episodeItem.AiredSeasonNumber : episode.ParentIndexNumber;
                 if (currentSeasonNumber.HasValue && seasonNumber.HasValue && currentSeasonNumber.Value == seasonNumber.Value)
                 if (currentSeasonNumber.HasValue && seasonNumber.HasValue && currentSeasonNumber.Value == seasonNumber.Value)

+ 7 - 11
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -397,7 +397,7 @@ namespace MediaBrowser.Controller.Entities
 
 
             }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null);
             }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null);
 
 
-            query.SortBy = new string[] { };
+            query.OrderBy = new Tuple<string, SortOrder>[] { };
 
 
             return PostFilterAndSort(items, parent, null, query, false, true);
             return PostFilterAndSort(items, parent, null, query, false, true);
         }
         }
@@ -507,8 +507,7 @@ namespace MediaBrowser.Controller.Entities
 
 
         private QueryResult<BaseItem> GetMovieLatest(Folder parent, User user, InternalItemsQuery query)
         private QueryResult<BaseItem> GetMovieLatest(Folder parent, User user, InternalItemsQuery query)
         {
         {
-            query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName };
-            query.SortOrder = SortOrder.Descending;
+            query.OrderBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
 
 
             query.Recursive = true;
             query.Recursive = true;
             query.Parent = parent;
             query.Parent = parent;
@@ -521,8 +520,7 @@ namespace MediaBrowser.Controller.Entities
 
 
         private QueryResult<BaseItem> GetMovieResume(Folder parent, User user, InternalItemsQuery query)
         private QueryResult<BaseItem> GetMovieResume(Folder parent, User user, InternalItemsQuery query)
         {
         {
-            query.SortBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName };
-            query.SortOrder = SortOrder.Descending;
+            query.OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
             query.IsResumable = true;
             query.IsResumable = true;
             query.Recursive = true;
             query.Recursive = true;
             query.Parent = parent;
             query.Parent = parent;
@@ -633,8 +631,7 @@ namespace MediaBrowser.Controller.Entities
 
 
         private QueryResult<BaseItem> GetTvLatest(Folder parent, User user, InternalItemsQuery query)
         private QueryResult<BaseItem> GetTvLatest(Folder parent, User user, InternalItemsQuery query)
         {
         {
-            query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName };
-            query.SortOrder = SortOrder.Descending;
+            query.OrderBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
 
 
             query.Recursive = true;
             query.Recursive = true;
             query.Parent = parent;
             query.Parent = parent;
@@ -663,8 +660,7 @@ namespace MediaBrowser.Controller.Entities
 
 
         private QueryResult<BaseItem> GetTvResume(Folder parent, User user, InternalItemsQuery query)
         private QueryResult<BaseItem> GetTvResume(Folder parent, User user, InternalItemsQuery query)
         {
         {
-            query.SortBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName };
-            query.SortOrder = SortOrder.Descending;
+            query.OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray();
             query.IsResumable = true;
             query.IsResumable = true;
             query.Recursive = true;
             query.Recursive = true;
             query.Parent = parent;
             query.Parent = parent;
@@ -1104,9 +1100,9 @@ namespace MediaBrowser.Controller.Entities
         {
         {
             items = items.DistinctBy(i => i.GetPresentationUniqueKey(), StringComparer.OrdinalIgnoreCase);
             items = items.DistinctBy(i => i.GetPresentationUniqueKey(), StringComparer.OrdinalIgnoreCase);
 
 
-            if (query.SortBy.Length > 0)
+            if (query.OrderBy.Length > 0)
             {
             {
-                items = libraryManager.Sort(items, query.User, query.SortBy, query.SortOrder);
+                items = libraryManager.Sort(items, query.User, query.OrderBy);
             }
             }
 
 
             var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray();
             var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray();

+ 2 - 2
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -181,8 +181,8 @@ namespace MediaBrowser.Controller.Library
         /// <param name="sortBy">The sort by.</param>
         /// <param name="sortBy">The sort by.</param>
         /// <param name="sortOrder">The sort order.</param>
         /// <param name="sortOrder">The sort order.</param>
         /// <returns>IEnumerable{BaseItem}.</returns>
         /// <returns>IEnumerable{BaseItem}.</returns>
-        IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<string> sortBy,
-                                   SortOrder sortOrder);
+        IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<string> sortBy, SortOrder sortOrder);
+        IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<Tuple<string, SortOrder>> orderBy);
 
 
         /// <summary>
         /// <summary>
         /// Gets the user root folder.
         /// Gets the user root folder.

+ 3 - 5
MediaBrowser.Controller/Playlists/Playlist.cs

@@ -149,8 +149,7 @@ namespace MediaBrowser.Controller.Playlists
                     Recursive = true,
                     Recursive = true,
                     IncludeItemTypes = new[] { typeof(Audio).Name },
                     IncludeItemTypes = new[] { typeof(Audio).Name },
                     GenreIds = new[] { musicGenre.Id.ToString("N") },
                     GenreIds = new[] { musicGenre.Id.ToString("N") },
-                    SortBy = new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName },
-                    SortOrder = SortOrder.Ascending,
+                    OrderBy = new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
                     DtoOptions = options
                     DtoOptions = options
                 });
                 });
             }
             }
@@ -163,8 +162,7 @@ namespace MediaBrowser.Controller.Playlists
                     Recursive = true,
                     Recursive = true,
                     IncludeItemTypes = new[] { typeof(Audio).Name },
                     IncludeItemTypes = new[] { typeof(Audio).Name },
                     ArtistIds = new[] { musicArtist.Id.ToString("N") },
                     ArtistIds = new[] { musicArtist.Id.ToString("N") },
-                    SortBy = new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName },
-                    SortOrder = SortOrder.Ascending,
+                    OrderBy = new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
                     DtoOptions = options
                     DtoOptions = options
                 });
                 });
             }
             }
@@ -176,7 +174,7 @@ namespace MediaBrowser.Controller.Playlists
                 {
                 {
                     Recursive = true,
                     Recursive = true,
                     IsFolder = false,
                     IsFolder = false,
-                    SortBy = new[] { ItemSortBy.SortName },
+                    OrderBy = new[] { ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
                     MediaTypes = new[] { mediaType },
                     MediaTypes = new[] { mediaType },
                     EnableTotalRecordCount = false,
                     EnableTotalRecordCount = false,
                     DtoOptions = options
                     DtoOptions = options

+ 5 - 4
MediaBrowser.Model/Channels/ChannelItemQuery.cs

@@ -1,4 +1,6 @@
-using MediaBrowser.Model.Entities;
+using System;
+using System.Collections.Generic;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
 
 
 namespace MediaBrowser.Model.Channels
 namespace MediaBrowser.Model.Channels
@@ -35,16 +37,15 @@ namespace MediaBrowser.Model.Channels
         /// <value>The limit.</value>
         /// <value>The limit.</value>
         public int? Limit { get; set; }
         public int? Limit { get; set; }
 
 
-        public SortOrder? SortOrder { get; set; }
-        public string[] SortBy { get; set; }
         public ItemFilter[] Filters { get; set; }
         public ItemFilter[] Filters { get; set; }
         public ItemFields[] Fields { get; set; }
         public ItemFields[] Fields { get; set; }
+        public Tuple<string, SortOrder>[] OrderBy { get; set; }
 
 
         public ChannelItemQuery()
         public ChannelItemQuery()
         {
         {
             Filters = new ItemFilter[] { };
             Filters = new ItemFilter[] { };
-            SortBy = new string[] { };
             Fields = new ItemFields[] { };
             Fields = new ItemFields[] { };
+            OrderBy = new Tuple<string, SortOrder>[] { };
         }
         }
     }
     }
 
 

+ 2 - 12
MediaBrowser.Model/LiveTv/ProgramQuery.cs

@@ -12,7 +12,7 @@ namespace MediaBrowser.Model.LiveTv
         public ProgramQuery()
         public ProgramQuery()
         {
         {
             ChannelIds = new string[] { };
             ChannelIds = new string[] { };
-            SortBy = new string[] { };
+            OrderBy = new Tuple<string, SortOrder>[] { };
             Genres = new string[] { };
             Genres = new string[] { };
             EnableTotalRecordCount = true;
             EnableTotalRecordCount = true;
             EnableUserData = true;
             EnableUserData = true;
@@ -104,17 +104,7 @@ namespace MediaBrowser.Model.LiveTv
         /// </summary>
         /// </summary>
         public int? Limit { get; set; }
         public int? Limit { get; set; }
 
 
-        /// <summary>
-        /// What to sort the results by
-        /// </summary>
-        /// <value>The sort by.</value>
-        public string[] SortBy { get; set; }
-
-        /// <summary>
-        /// The sort order to return results with
-        /// </summary>
-        /// <value>The sort order.</value>
-        public SortOrder? SortOrder { get; set; }
+        public Tuple<string, SortOrder>[] OrderBy { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Limit results to items containing specific genres
         /// Limit results to items containing specific genres

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
     <metadata>
         <id>MediaBrowser.Common</id>
         <id>MediaBrowser.Common</id>
-        <version>3.0.747</version>
+        <version>3.0.748</version>
         <title>Emby.Common</title>
         <title>Emby.Common</title>
         <authors>Emby Team</authors>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
         <owners>ebr,Luke,scottisafool</owners>

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
     <metadata>
         <id>MediaBrowser.Server.Core</id>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.747</version>
+        <version>3.0.748</version>
         <title>Emby.Server.Core</title>
         <title>Emby.Server.Core</title>
         <authors>Emby Team</authors>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Emby Server.</description>
         <description>Contains core components required to build plugins for Emby Server.</description>
         <copyright>Copyright © Emby 2013</copyright>
         <copyright>Copyright © Emby 2013</copyright>
         <dependencies>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.747" />
+            <dependency id="MediaBrowser.Common" version="3.0.748" />
         </dependencies>
         </dependencies>
     </metadata>
     </metadata>
     <files>
     <files>

+ 56 - 0
SocketHttpListener/Net/HttpResponseStream.Managed.cs

@@ -289,9 +289,65 @@ namespace SocketHttpListener.Net
 
 
         public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
         public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
         {
         {
+            //if (_supportsDirectSocketAccess && offset == 0 && count == 0 && !_response.SendChunked && _response.ContentLength64 > 8192)
+            //{
+            //    if (EnableSendFileWithSocket)
+            //    {
+            //        return TransmitFileOverSocket(path, offset, count, fileShareMode, cancellationToken);
+            //    }
+            //}
+
             return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken);
             return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken);
         }
         }
 
 
+        private readonly byte[] _emptyBuffer = new byte[] { };
+        private Task TransmitFileOverSocket(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
+        {
+            var ms = GetHeaders(false);
+
+            byte[] preBuffer;
+            if (ms != null)
+            {
+                using (var msCopy = new MemoryStream())
+                {
+                    ms.CopyTo(msCopy);
+                    preBuffer = msCopy.ToArray();
+                }
+            }
+            else
+            {
+                return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken);
+            }
+
+            //_logger.Info("Socket sending file {0} {1}", path, response.ContentLength64);
+
+            var taskCompletion = new TaskCompletionSource<bool>();
+
+            Action<IAsyncResult> callback = callbackResult =>
+            {
+                try
+                {
+                    _socket.EndSendFile(callbackResult);
+                    taskCompletion.TrySetResult(true);
+                }
+                catch (Exception ex)
+                {
+                    taskCompletion.TrySetException(ex);
+                }
+            };
+
+            var result = _socket.BeginSendFile(path, preBuffer, _emptyBuffer, TransmitFileOptions.UseDefaultWorkerThread, new AsyncCallback(callback), null);
+
+            if (result.CompletedSynchronously)
+            {
+                callback(result);
+            }
+
+            cancellationToken.Register(() => taskCompletion.TrySetCanceled());
+
+            return taskCompletion.Task;
+        }
+
         const int StreamCopyToBufferSize = 81920;
         const int StreamCopyToBufferSize = 81920;
         private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
         private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
         {
         {