diff options
| author | Bond-009 <bond.009@outlook.com> | 2026-05-06 20:49:19 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-06 20:49:19 +0200 |
| commit | 33ed52b8ee25e1fae4763a26337b838dc9782b26 (patch) | |
| tree | ee68da202f604eef267254ea8c689965098b1c3e /Emby.Server.Implementations/TV | |
| parent | aa96ff42e616ecf5638a8f1e2e8459b94513c528 (diff) | |
| parent | d1ab428476f961426841a0561036c59c3b93878e (diff) | |
Merge branch 'master' into feature/season-provider-id-from-path
Diffstat (limited to 'Emby.Server.Implementations/TV')
| -rw-r--r-- | Emby.Server.Implementations/TV/TVSeriesManager.cs | 208 |
1 files changed, 100 insertions, 108 deletions
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index cd98dbe86e..535dc01a31 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.TV if (!string.IsNullOrEmpty(presentationUniqueKey)) { - return GetResult(GetNextUpEpisodes(query, user, new[] { presentationUniqueKey }, options), query); + return GetNextUpBatched(query, user, [presentationUniqueKey], options); } BaseItem[] parents; @@ -58,11 +58,11 @@ namespace Emby.Server.Implementations.TV if (parent is not null) { - parents = new[] { parent }; + parents = [parent]; } else { - parents = Array.Empty<BaseItem>(); + parents = []; } } else @@ -93,7 +93,7 @@ namespace Emby.Server.Implementations.TV if (!string.IsNullOrEmpty(presentationUniqueKey)) { - return GetResult(GetNextUpEpisodes(request, user, [presentationUniqueKey], options), request); + return GetNextUpBatched(request, user, [presentationUniqueKey], options); } if (limit.HasValue) @@ -103,151 +103,143 @@ namespace Emby.Server.Implementations.TV var nextUpSeriesKeys = _libraryManager.GetNextUpSeriesKeys(new InternalItemsQuery(user) { Limit = limit }, parentsFolders, request.NextUpDateCutoff); - var episodes = GetNextUpEpisodes(request, user, nextUpSeriesKeys, options); - - return GetResult(episodes, request); + return GetNextUpBatched(request, user, nextUpSeriesKeys, options); } - private IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IReadOnlyList<string> seriesKeys, DtoOptions dtoOptions) + private QueryResult<BaseItem> GetNextUpBatched(NextUpQuery request, User user, IReadOnlyList<string> seriesKeys, DtoOptions dtoOptions) { - var allNextUp = seriesKeys.Select(i => GetNextUp(i, user, dtoOptions, request.EnableResumable, false)); - - if (request.EnableRewatching) + if (seriesKeys.Count == 0) { - allNextUp = allNextUp - .Concat(seriesKeys.Select(i => GetNextUp(i, user, dtoOptions, false, true))) - .OrderByDescending(i => i.LastWatchedDate); + return new QueryResult<BaseItem>(); } - return allNextUp - .Select(i => i.GetEpisodeFunction()) - .Where(i => i is not null)!; - } - - private static string GetUniqueSeriesKey(Series series) - { - return series.GetPresentationUniqueKey(); - } + var includeSpecials = _configurationManager.Configuration.DisplaySpecialsWithinSeasons; + var includeRewatching = request.EnableRewatching; - /// <summary> - /// Gets the next up. - /// </summary> - /// <returns>Task{Episode}.</returns> - private (DateTime LastWatchedDate, Func<Episode?> GetEpisodeFunction) GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool includeResumable, bool includePlayed) - { - var lastQuery = new InternalItemsQuery(user) + var query = new InternalItemsQuery(user) { - AncestorWithPresentationUniqueKey = null, - SeriesPresentationUniqueKey = seriesKey, - IncludeItemTypes = [BaseItemKind.Episode], - IsPlayed = true, - Limit = 1, - ParentIndexNumberNotEquals = 0, - DtoOptions = new DtoOptions - { - Fields = [ItemFields.SortName], - EnableImages = false - } + DtoOptions = dtoOptions }; - // If including played results, sort first by date played and then by season and episode numbers - lastQuery.OrderBy = includePlayed - ? new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) } - : new[] { (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) }; + var batchResult = _libraryManager.GetNextUpEpisodesBatch(query, seriesKeys, includeSpecials, includeRewatching); - var lastWatchedEpisode = _libraryManager.GetItemList(lastQuery).Cast<Episode>().FirstOrDefault(); + var nextUpList = new List<(DateTime LastWatchedDate, Episode Episode)>(); - Episode? GetEpisode() + foreach (var seriesKey in seriesKeys) { - var nextQuery = new InternalItemsQuery(user) + if (!batchResult.TryGetValue(seriesKey, out var result)) { - AncestorWithPresentationUniqueKey = null, - SeriesPresentationUniqueKey = seriesKey, - IncludeItemTypes = [BaseItemKind.Episode], - OrderBy = [(ItemSortBy.ParentIndexNumber, SortOrder.Ascending), (ItemSortBy.IndexNumber, SortOrder.Ascending)], - Limit = 1, - IsPlayed = includePlayed, - IsVirtualItem = false, - ParentIndexNumberNotEquals = 0, - DtoOptions = dtoOptions - }; - - // Locate the next up episode based on the last watched episode's season and episode number - var lastWatchedParentIndexNumber = lastWatchedEpisode?.ParentIndexNumber; - var lastWatchedIndexNumber = lastWatchedEpisode?.IndexNumberEnd ?? lastWatchedEpisode?.IndexNumber; - if (lastWatchedParentIndexNumber.HasValue && lastWatchedIndexNumber.HasValue) - { - nextQuery.MinParentAndIndexNumber = (lastWatchedParentIndexNumber.Value, lastWatchedIndexNumber.Value + 1); + continue; } - var nextEpisode = _libraryManager.GetItemList(nextQuery).Cast<Episode>().FirstOrDefault(); + var nextEpisode = DetermineNextEpisode(result, user, includeSpecials, request.EnableResumable, false); + + if (nextEpisode is not null) + { + DateTime lastWatchedDate = DateTime.MinValue; + if (result.LastWatched is not null) + { + var userData = _userDataManager.GetUserData(user, result.LastWatched); + lastWatchedDate = userData?.LastPlayedDate ?? DateTime.MinValue.AddDays(1); + } + + nextUpList.Add((lastWatchedDate, nextEpisode)); + } - if (_configurationManager.Configuration.DisplaySpecialsWithinSeasons) + if (includeRewatching) { - var consideredEpisodes = _libraryManager.GetItemList(new InternalItemsQuery(user) + var nextPlayedEpisode = DetermineNextEpisodeForRewatching(result, user, includeSpecials); + + if (nextPlayedEpisode is not null) { - AncestorWithPresentationUniqueKey = null, - SeriesPresentationUniqueKey = seriesKey, - ParentIndexNumber = 0, - IncludeItemTypes = [BaseItemKind.Episode], - IsPlayed = includePlayed, - IsVirtualItem = false, - DtoOptions = dtoOptions - }) + DateTime rewatchLastWatchedDate = DateTime.MinValue; + if (result.LastWatchedForRewatching is not null) + { + var userData = _userDataManager.GetUserData(user, result.LastWatchedForRewatching); + rewatchLastWatchedDate = userData?.LastPlayedDate ?? DateTime.MinValue.AddDays(1); + } + + nextUpList.Add((rewatchLastWatchedDate, nextPlayedEpisode)); + } + } + } + + var sortedEpisodes = nextUpList + .OrderByDescending(x => x.LastWatchedDate) + .Select(x => (BaseItem)x.Episode); + + return GetResult(sortedEpisodes, request); + } + + private Episode? DetermineNextEpisode( + MediaBrowser.Controller.Persistence.NextUpEpisodeBatchResult result, + User user, + bool includeSpecials, + bool includeResumable, + bool includePlayed) + { + var nextEpisode = (includePlayed ? result.NextPlayedForRewatching : result.NextUp) as Episode; + var lastWatchedEpisode = (includePlayed ? result.LastWatchedForRewatching : result.LastWatched) as Episode; + + if (includeSpecials && result.Specials?.Count > 0) + { + var consideredEpisodes = result.Specials .Cast<Episode>() .Where(episode => episode.AirsBeforeSeasonNumber is not null || episode.AirsAfterSeasonNumber is not null) .ToList(); - if (lastWatchedEpisode is not null) - { - // Last watched episode is added, because there could be specials that aired before the last watched episode - consideredEpisodes.Add(lastWatchedEpisode); - } + if (lastWatchedEpisode is not null) + { + consideredEpisodes.Add(lastWatchedEpisode); + } - if (nextEpisode is not null) - { - consideredEpisodes.Add(nextEpisode); - } + if (nextEpisode is not null) + { + consideredEpisodes.Add(nextEpisode); + } - var sortedConsideredEpisodes = _libraryManager.Sort(consideredEpisodes, user, [(ItemSortBy.AiredEpisodeOrder, SortOrder.Ascending)]) + if (consideredEpisodes.Count > 0) + { + var sortedEpisodes = _libraryManager.Sort(consideredEpisodes, user, [(ItemSortBy.AiredEpisodeOrder, SortOrder.Ascending)]) .Cast<Episode>(); + if (lastWatchedEpisode is not null) { - sortedConsideredEpisodes = sortedConsideredEpisodes.SkipWhile(episode => !episode.Id.Equals(lastWatchedEpisode.Id)).Skip(1); + sortedEpisodes = sortedEpisodes.SkipWhile(episode => !episode.Id.Equals(lastWatchedEpisode.Id)).Skip(1); } - nextEpisode = sortedConsideredEpisodes.FirstOrDefault(); - } - - if (nextEpisode is not null && !includeResumable) - { - var userData = _userDataManager.GetUserData(user, nextEpisode); - - if (userData?.PlaybackPositionTicks > 0) + if (!includePlayed) { - return null; + sortedEpisodes = sortedEpisodes.Where(episode => _userDataManager.GetUserData(user, episode) is not { Played: true }); } - } - return nextEpisode; + nextEpisode = sortedEpisodes.FirstOrDefault(); + } } - if (lastWatchedEpisode is not null) + if (nextEpisode is not null && !includeResumable) { - var userData = _userDataManager.GetUserData(user, lastWatchedEpisode); - - if (userData is null) + var userData = _userDataManager.GetUserData(user, nextEpisode); + if (userData?.PlaybackPositionTicks > 0) { - return (DateTime.MinValue, GetEpisode); + return null; } + } - var lastWatchedDate = userData.LastPlayedDate ?? DateTime.MinValue.AddDays(1); + return nextEpisode; + } - return (lastWatchedDate, GetEpisode); - } + private Episode? DetermineNextEpisodeForRewatching( + MediaBrowser.Controller.Persistence.NextUpEpisodeBatchResult result, + User user, + bool includeSpecials) + { + return DetermineNextEpisode(result, user, includeSpecials, includeResumable: false, includePlayed: true); + } - // Return the first episode - return (DateTime.MinValue, GetEpisode); + private static string GetUniqueSeriesKey(Series series) + { + return series.GetPresentationUniqueKey(); } private static QueryResult<BaseItem> GetResult(IEnumerable<BaseItem> items, NextUpQuery query) |
