#pragma warning disable CS1591 using System; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Sorting; using MediaBrowser.Model.Querying; namespace Emby.Server.Implementations.Sorting { public class AiredEpisodeOrderComparer : IBaseItemComparer { /// /// Gets the name. /// /// The name. public string Name => ItemSortBy.AiredEpisodeOrder; /// /// Compares the specified x. /// /// The x. /// The y. /// System.Int32. public int Compare(BaseItem? x, BaseItem? y) { ArgumentNullException.ThrowIfNull(x); ArgumentNullException.ThrowIfNull(y); var episode1 = x as Episode; var episode2 = y as Episode; if (episode1 is null) { if (episode2 is null) { return 0; } return 1; } if (episode2 is null) { return -1; } return Compare(episode1, episode2); } private int Compare(Episode x, Episode y) { var isXSpecial = (x.ParentIndexNumber ?? -1) == 0; var isYSpecial = (y.ParentIndexNumber ?? -1) == 0; if (isXSpecial && isYSpecial) { return CompareSpecials(x, y); } if (!isXSpecial && !isYSpecial) { return CompareEpisodes(x, y); } if (!isXSpecial) { return CompareEpisodeToSpecial(x, y); } return CompareEpisodeToSpecial(y, x) * -1; } private static int CompareEpisodeToSpecial(Episode x, Episode y) { // http://thetvdb.com/wiki/index.php?title=Special_Episodes var xSeason = x.ParentIndexNumber ?? -1; var ySeason = y.AirsAfterSeasonNumber ?? y.AirsBeforeSeasonNumber ?? -1; if (xSeason != ySeason) { return xSeason.CompareTo(ySeason); } // Special comes after episode if (y.AirsAfterSeasonNumber.HasValue) { return -1; } var yEpisode = y.AirsBeforeEpisodeNumber; // Special comes before the season if (!yEpisode.HasValue) { return 1; } // Compare episode number var xEpisode = x.IndexNumber; if (!xEpisode.HasValue) { // Can't really compare if this happens return 0; } // Special comes before episode if (xEpisode.Value == yEpisode.Value) { return 1; } return xEpisode.Value.CompareTo(yEpisode.Value); } private int CompareSpecials(Episode x, Episode y) { return GetSpecialCompareValue(x).CompareTo(GetSpecialCompareValue(y)); } private static long GetSpecialCompareValue(Episode item) { // First sort by season number // Since there are three sort orders, pad with 9 digits (3 for each, figure 1000 episode buffer should be enough) var val = (item.AirsAfterSeasonNumber ?? item.AirsBeforeSeasonNumber ?? 0) * 1000000000L; // Second sort order is if it airs after the season if (item.AirsAfterSeasonNumber.HasValue) { val += 1000000; } // Third level is the episode number val += (item.AirsBeforeEpisodeNumber ?? 0) * 1000; // Finally, if that's still the same, last resort is the special number itself val += item.IndexNumber ?? 0; return val; } private static int CompareEpisodes(Episode x, Episode y) { var xValue = ((x.ParentIndexNumber ?? -1) * 1000) + (x.IndexNumber ?? -1); var yValue = ((y.ParentIndexNumber ?? -1) * 1000) + (y.IndexNumber ?? -1); var comparisonResult = xValue.CompareTo(yValue); // If equal, compare premiere dates if (comparisonResult == 0 && x.PremiereDate.HasValue && y.PremiereDate.HasValue) { comparisonResult = DateTime.Compare(x.PremiereDate.Value, y.PremiereDate.Value); } return comparisonResult; } } }