diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 9082186fd..14205e668 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -210,6 +210,7 @@ + diff --git a/MediaBrowser.Controller/Sorting/SortExtensions.cs b/MediaBrowser.Controller/Sorting/SortExtensions.cs new file mode 100644 index 000000000..ec8ee5a11 --- /dev/null +++ b/MediaBrowser.Controller/Sorting/SortExtensions.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MediaBrowser.Controller.Sorting +{ + public static class SortExtensions + { + public static IEnumerable OrderByString(this IEnumerable list, Func getName) + { + return list.OrderBy(getName, new AlphanumComparator()); + } + + public static IEnumerable OrderByStringDescending(this IEnumerable list, Func getName) + { + return list.OrderByDescending(getName, new AlphanumComparator()); + } + + public static IOrderedEnumerable ThenByString(this IOrderedEnumerable list, Func getName) + { + return list.ThenBy(getName, new AlphanumComparator()); + } + + public static IOrderedEnumerable ThenByStringDescending(this IOrderedEnumerable list, Func getName) + { + return list.ThenByDescending(getName, new AlphanumComparator()); + } + + private class AlphanumComparator : IComparer + { + private enum ChunkType { Alphanumeric, Numeric }; + + private static bool InChunk(char ch, char otherCh) + { + var type = ChunkType.Alphanumeric; + + if (char.IsDigit(otherCh)) + { + type = ChunkType.Numeric; + } + + if ((type == ChunkType.Alphanumeric && char.IsDigit(ch)) + || (type == ChunkType.Numeric && !char.IsDigit(ch))) + { + return false; + } + + return true; + } + + public static int CompareValues(string s1, string s2) + { + if (s1 == null || s2 == null) + { + return 0; + } + + int thisMarker = 0, thisNumericChunk = 0; + int thatMarker = 0, thatNumericChunk = 0; + + while ((thisMarker < s1.Length) || (thatMarker < s2.Length)) + { + if (thisMarker >= s1.Length) + { + return -1; + } + else if (thatMarker >= s2.Length) + { + return 1; + } + char thisCh = s1[thisMarker]; + char thatCh = s2[thatMarker]; + + StringBuilder thisChunk = new StringBuilder(); + StringBuilder thatChunk = new StringBuilder(); + + while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || InChunk(thisCh, thisChunk[0]))) + { + thisChunk.Append(thisCh); + thisMarker++; + + if (thisMarker < s1.Length) + { + thisCh = s1[thisMarker]; + } + } + + while ((thatMarker < s2.Length) && (thatChunk.Length == 0 || InChunk(thatCh, thatChunk[0]))) + { + thatChunk.Append(thatCh); + thatMarker++; + + if (thatMarker < s2.Length) + { + thatCh = s2[thatMarker]; + } + } + + int result = 0; + // If both chunks contain numeric characters, sort them numerically + if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0])) + { + if (!int.TryParse(thisChunk.ToString(), out thisNumericChunk)) + { + return 0; + } + if (!int.TryParse(thatChunk.ToString(), out thatNumericChunk)) + { + return 0; + } + + if (thisNumericChunk < thatNumericChunk) + { + result = -1; + } + + if (thisNumericChunk > thatNumericChunk) + { + result = 1; + } + } + else + { + result = thisChunk.ToString().CompareTo(thatChunk.ToString()); + } + + if (result != 0) + { + return result; + } + } + + return 0; + } + + public int Compare(string x, string y) + { + return CompareValues(x, y); + } + } + } +} diff --git a/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs b/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs index 8940f1d49..306956c95 100644 --- a/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs +++ b/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs @@ -172,6 +172,11 @@ namespace MediaBrowser.Providers.Movies item.CommunityRating = imdbRating; } + if (!string.IsNullOrEmpty(result.Website)) + { + item.HomePageUrl = result.Website; + } + ParseAdditionalMetadata(item, result); } @@ -251,6 +256,11 @@ namespace MediaBrowser.Providers.Movies public string Production { get; set; } public string Website { get; set; } public string Response { get; set; } + + public string Language { get; set; } + public string Country { get; set; } + public string Awards { get; set; } + public string Metascore { get; set; } } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index ef8f515f5..a8b62c5a7 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -9,6 +9,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaInfo; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Sorting; using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Logging; @@ -956,14 +957,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv if (string.Equals(query.SortBy, "Priority", StringComparison.OrdinalIgnoreCase)) { timers = query.SortOrder == SortOrder.Descending ? - timers.OrderBy(i => i.Priority).ThenByDescending(i => i.Name) : - timers.OrderByDescending(i => i.Priority).ThenBy(i => i.Name); + timers.OrderBy(i => i.Priority).ThenByStringDescending(i => i.Name) : + timers.OrderByDescending(i => i.Priority).ThenByString(i => i.Name); } else { timers = query.SortOrder == SortOrder.Descending ? - timers.OrderByDescending(i => i.Name) : - timers.OrderBy(i => i.Name); + timers.OrderByStringDescending(i => i.Name) : + timers.OrderByString(i => i.Name); } var returnArray = timers @@ -1154,7 +1155,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv .ToLookup(i => i.Name, StringComparer.OrdinalIgnoreCase) .ToList(); - groups.AddRange(series.OrderBy(i => i.Key).Select(i => new RecordingGroupDto + groups.AddRange(series.OrderByString(i => i.Key).Select(i => new RecordingGroupDto { Name = i.Key, RecordingCount = i.Count()