aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/TV
diff options
context:
space:
mode:
authorBond-009 <bond.009@outlook.com>2026-05-06 20:49:19 +0200
committerGitHub <noreply@github.com>2026-05-06 20:49:19 +0200
commit33ed52b8ee25e1fae4763a26337b838dc9782b26 (patch)
treeee68da202f604eef267254ea8c689965098b1c3e /Emby.Server.Implementations/TV
parentaa96ff42e616ecf5638a8f1e2e8459b94513c528 (diff)
parentd1ab428476f961426841a0561036c59c3b93878e (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.cs208
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)