aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Server.Implementations/Item/NextUpService.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Server.Implementations/Item/NextUpService.cs')
-rw-r--r--Jellyfin.Server.Implementations/Item/NextUpService.cs23
1 files changed, 16 insertions, 7 deletions
diff --git a/Jellyfin.Server.Implementations/Item/NextUpService.cs b/Jellyfin.Server.Implementations/Item/NextUpService.cs
index f478daef23..897fb98cbb 100644
--- a/Jellyfin.Server.Implementations/Item/NextUpService.cs
+++ b/Jellyfin.Server.Implementations/Item/NextUpService.cs
@@ -95,7 +95,7 @@ public class NextUpService : INextUpService
.Where(e => e.Type == episodeTypeName)
.Where(e => e.SeriesPresentationUniqueKey != null && seriesKeys.Contains(e.SeriesPresentationUniqueKey))
.Where(e => e.ParentIndexNumber != 0)
- .Where(e => e.UserData!.Any(ud => ud.UserId == userId && ud.Played));
+ .Where(DescendantQueryHelper.IsPlayedBy(userId));
lastWatchedBase = _queryHelpers.ApplyAccessFiltering(context, lastWatchedBase, filter);
// Use lightweight projection + client-side dedup to avoid the correlated scalar subquery
@@ -129,12 +129,21 @@ public class NextUpService : INextUpService
// Use an explicit Join (INNER JOIN) instead of SelectMany on a collection navigation.
// SelectMany on UserData with a correlated Where would translate to APPLY,
// which SQLite does not support.
+ // Access filtering leaves only primaries in the base query, but a play can be recorded
+ // against any version, so each row is attributed to its group's primary before the join.
+ var playedByGroupPrimary = context.UserData
+ .AsNoTracking()
+ .Where(ud => ud.ItemId != EF.Constant(BaseItemRepository.PlaceholderId))
+ .Where(ud => ud.Played)
+ .Join(
+ context.BaseItems.AsNoTracking(),
+ ud => ud.ItemId,
+ bi => bi.Id,
+ (ud, bi) => new { ud.UserId, ItemId = bi.PrimaryVersionId ?? bi.Id, ud.LastPlayedDate });
+
var playedWithDates = lastWatchedByDateBase
.Join(
- context.UserData
- .AsNoTracking()
- .Where(ud => ud.ItemId != EF.Constant(BaseItemRepository.PlaceholderId))
- .Where(ud => ud.Played),
+ playedByGroupPrimary,
e => new { UserId = userId, ItemId = e.Id },
ud => new { ud.UserId, ud.ItemId },
(e, ud) => new { EpisodeId = e.Id, e.SeriesPresentationUniqueKey, ud.LastPlayedDate })
@@ -198,7 +207,7 @@ public class NextUpService : INextUpService
.Where(e => e.SeriesPresentationUniqueKey != null && seriesKeys.Contains(e.SeriesPresentationUniqueKey))
.Where(e => e.ParentIndexNumber != 0)
.Where(e => !e.IsVirtualItem)
- .Where(e => !e.UserData!.Any(ud => ud.UserId == userId && ud.Played));
+ .Where(DescendantQueryHelper.IsUnplayedBy(userId));
allUnplayedBase = _queryHelpers.ApplyAccessFiltering(context, allUnplayedBase, filter);
var allUnplayedCandidates = allUnplayedBase
.Select(e => new
@@ -246,7 +255,7 @@ public class NextUpService : INextUpService
.Where(e => e.SeriesPresentationUniqueKey != null && seriesKeys.Contains(e.SeriesPresentationUniqueKey))
.Where(e => e.ParentIndexNumber != 0)
.Where(e => !e.IsVirtualItem)
- .Where(e => e.UserData!.Any(ud => ud.UserId == userId && ud.Played));
+ .Where(DescendantQueryHelper.IsPlayedBy(userId));
allPlayedBase = _queryHelpers.ApplyAccessFiltering(context, allPlayedBase, filter);
var allPlayedCandidates = allPlayedBase
.Select(e => new