aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Library/UserDataManager.cs
diff options
context:
space:
mode:
authorShadowghost <Ghost_of_Stone@web.de>2026-06-09 23:23:03 +0200
committerShadowghost <Ghost_of_Stone@web.de>2026-06-09 23:23:03 +0200
commit0874a26131a5a1e9d62fb7231acbaf9eb921b5c8 (patch)
tree95dd2ed86e5afa7429afb4ceffe0e59bea349726 /Emby.Server.Implementations/Library/UserDataManager.cs
parentfe1d8d88401a5536b03bb99bd0f4f126665bf3a0 (diff)
Coalesce alternate-version progress onto primary in resume filter
Diffstat (limited to 'Emby.Server.Implementations/Library/UserDataManager.cs')
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs70
1 files changed, 70 insertions, 0 deletions
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index 962dd6fda8..4fda853bb1 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -247,6 +247,72 @@ namespace Emby.Server.Implementations.Library
return result;
}
+ /// <inheritdoc />
+ public VersionResumeData? GetResumeUserData(User user, BaseItem item)
+ {
+ return GetResumeUserDataBatch([item], user).GetValueOrDefault(item.Id);
+ }
+
+ /// <inheritdoc />
+ public IReadOnlyDictionary<Guid, VersionResumeData> GetResumeUserDataBatch(IReadOnlyList<BaseItem> items, User user)
+ {
+ ArgumentNullException.ThrowIfNull(user);
+
+ var result = new Dictionary<Guid, VersionResumeData>();
+ List<(Guid PrimaryId, IReadOnlyList<Video> Versions)>? versionGroups = null;
+ List<BaseItem>? allVersions = null;
+
+ foreach (var item in items)
+ {
+ // Only primary items aggregate over their versions; a directly queried version keeps its own data.
+ if (item is not Video video
+ || video.PrimaryVersionId.HasValue
+ || (video.LinkedAlternateVersions.Length == 0 && !video.HasLocalAlternateVersions))
+ {
+ continue;
+ }
+
+ var versions = video.GetAllVersions();
+ if (versions.Count < 2)
+ {
+ continue;
+ }
+
+ (versionGroups ??= []).Add((item.Id, versions));
+ (allVersions ??= []).AddRange(versions);
+ }
+
+ if (versionGroups is null)
+ {
+ return result;
+ }
+
+ var userDataByVersion = GetUserDataBatch(allVersions!.DistinctBy(i => i.Id).ToList(), user);
+
+ foreach (var (primaryId, versions) in versionGroups)
+ {
+ Video? resumeVersion = null;
+ UserItemData? resumeData = null;
+ foreach (var version in versions)
+ {
+ if (userDataByVersion.TryGetValue(version.Id, out var data)
+ && data.PlaybackPositionTicks > 0
+ && (resumeData is null || (data.LastPlayedDate ?? DateTime.MinValue) > (resumeData.LastPlayedDate ?? DateTime.MinValue)))
+ {
+ resumeVersion = version;
+ resumeData = data;
+ }
+ }
+
+ if (resumeData is not null)
+ {
+ result[primaryId] = new VersionResumeData(resumeData, resumeVersion!.RunTimeTicks);
+ }
+ }
+
+ return result;
+ }
+
/// <summary>
/// Gets the internal key.
/// </summary>
@@ -281,6 +347,10 @@ namespace Emby.Server.Implementations.Library
var dto = GetUserItemDataDto(userData, item.Id);
item.FillUserDataDtoValues(dto, userData, itemDto, user, options);
+
+ // For an item with alternate versions, surface the most recently played version's resume point.
+ GetResumeUserData(user, item)?.ApplyTo(dto);
+
return dto;
}