aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
authorShadowghost <Ghost_of_Stone@web.de>2026-07-02 08:49:11 +0200
committerShadowghost <Ghost_of_Stone@web.de>2026-07-02 08:49:11 +0200
commit38f1d9749ee67f18264937807b2f5882e1421557 (patch)
tree0860e37b596ad2ce8dd60e135647df3659b6c5c6 /Emby.Server.Implementations
parent0fb042b7403ebd7578b696aba35ba0c582ccf6ba (diff)
Fix review comments
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs5
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs25
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs20
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs11
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs20
5 files changed, 37 insertions, 44 deletions
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 2bf478953e..9881565cd2 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -1272,8 +1272,9 @@ namespace Emby.Server.Implementations.Dto
// Match the per-user filtering of the media sources: versions the user cannot
// access are not selectable, so they must not count towards the badge either.
var mediaSourceCount = user is null
- ? video.MediaSourceCount
- : video.GetAllVersions().Count(v => v.Id.Equals(video.Id) || v.IsVisibleStandalone(user));
+ || (!video.PrimaryVersionId.HasValue && video.LinkedAlternateVersions.Length == 0 && !video.HasLocalAlternateVersions)
+ ? video.MediaSourceCount
+ : video.GetAllVersions().Count(v => v.Id.Equals(video.Id) || v.IsVisibleStandalone(user));
if (mediaSourceCount != 1)
{
dto.MediaSourceCount = mediaSourceCount;
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index c0e45ab6c7..c64833ddaa 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -451,26 +451,23 @@ namespace Emby.Server.Implementations.Library
}
}
- MediaSourceInfo resumeSource = null;
- UserItemData resumeData = null;
foreach (var source in sources)
{
- if (source.Id is null
- || !dataBySourceId.TryGetValue(source.Id, out var data)
- || data.PlaybackPositionTicks <= 0)
+ if (source.Id is not null
+ && dataBySourceId.TryGetValue(source.Id, out var data)
+ && data.PlaybackPositionTicks > 0)
{
- continue;
- }
-
- source.PlaybackPositionTicks = data.PlaybackPositionTicks;
-
- if (resumeData is null || (data.LastPlayedDate ?? DateTime.MinValue) > (resumeData.LastPlayedDate ?? DateTime.MinValue))
- {
- resumeSource = source;
- resumeData = data;
+ source.PlaybackPositionTicks = data.PlaybackPositionTicks;
}
}
+ // Reorder only for a resumable (in-progress) version;
+ // a completed version has no position to resume, so it must not be pulled to the front here.
+ var resumeSource = VersionPlaybackSelector.SelectMostRecentlyPlayed(
+ sources,
+ source => source.Id is not null ? dataBySourceId.GetValueOrDefault(source.Id) : null,
+ data => data.PlaybackPositionTicks > 0);
+
if (resumeSource is not null && !video.PrimaryVersionId.HasValue && !ReferenceEquals(sources[0], resumeSource))
{
var reordered = new List<MediaSourceInfo>(sources.Count) { resumeSource };
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index 61372f8b56..40cd2bb69c 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -329,21 +329,15 @@ namespace Emby.Server.Implementations.Library
foreach (var (primaryId, versions) in versionGroups)
{
- UserItemData? resumeData = null;
- foreach (var version in versions)
- {
- // Consider both in-progress and completed versions so a finished alternate still marks the primary as played.
- if (userDataByVersion.TryGetValue(version.Id, out var data)
- && (data.PlaybackPositionTicks > 0 || data.Played)
- && (resumeData is null || (data.LastPlayedDate ?? DateTime.MinValue) > (resumeData.LastPlayedDate ?? DateTime.MinValue)))
- {
- resumeData = data;
- }
- }
+ // Consider both in-progress and completed versions so a finished alternate still marks the primary as played.
+ var resumeVersion = VersionPlaybackSelector.SelectMostRecentlyPlayed(
+ versions,
+ version => userDataByVersion.GetValueOrDefault(version.Id),
+ data => data.PlaybackPositionTicks > 0 || data.Played);
- if (resumeData is not null)
+ if (resumeVersion is not null)
{
- result[primaryId] = new VersionResumeData(resumeData);
+ result[primaryId] = new VersionResumeData(resumeVersion.Id, userDataByVersion[resumeVersion.Id]);
}
}
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 6017b7cbf6..f652634c69 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -980,14 +980,17 @@ namespace Emby.Server.Implementations.Session
{
_userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None);
+ // A completed version marks every alternate version played and clears their resume points, so the
+ // whole movie leaves Continue Watching and reads as watched everywhere. (Per-version resume positions
+ // only persist while nothing has been completed yet.)
if (data.Played == true && item is Video playedVideo)
{
playedVideo.PropagatePlayedState(user, true);
}
}
- if ((!user.RememberAudioSelections && data.AudioStreamIndex.HasValue)
- || (!user.RememberSubtitleSelections && data.SubtitleStreamIndex.HasValue))
+ if ((!user.RememberAudioSelections && info.AudioStreamIndex.HasValue)
+ || (!user.RememberSubtitleSelections && info.SubtitleStreamIndex.HasValue))
{
_userDataManager.ResetPlaybackStreamSelections(user, item);
}
@@ -1177,7 +1180,9 @@ namespace Emby.Server.Implementations.Session
_userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackFinished, CancellationToken.None);
- // A completed version marks all of its alternate versions played; positions stay per-version.
+ // A completed version marks every alternate version played and clears their resume points, so the
+ // whole movie leaves Continue Watching and reads as watched everywhere. (Per-version resume positions
+ // only persist while nothing has been completed yet.)
if (data.Played == true && item is Video playedVideo)
{
playedVideo.PropagatePlayedState(user, true);
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index 9a402a5738..459ad1a17e 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -262,19 +262,15 @@ namespace Emby.Server.Implementations.TV
return (null, null);
}
- Video? playedVersion = null;
- DateTime? lastPlayedDate = null;
- foreach (var version in lastWatchedVideo.GetAllVersions())
- {
- var data = _userDataManager.GetUserData(user, version);
- if (data?.LastPlayedDate is { } date && (lastPlayedDate is null || date > lastPlayedDate))
- {
- lastPlayedDate = date;
- playedVersion = version;
- }
- }
+ var versions = lastWatchedVideo.GetAllVersions();
+ var userDataByVersion = _userDataManager.GetUserDataBatch(versions, user);
+
+ var playedVersion = VersionPlaybackSelector.SelectMostRecentlyPlayed(
+ versions,
+ version => userDataByVersion.GetValueOrDefault(version.Id),
+ data => data.LastPlayedDate.HasValue);
- return (playedVersion, lastPlayedDate);
+ return (playedVersion, playedVersion is null ? null : userDataByVersion[playedVersion.Id].LastPlayedDate);
}
/// <summary>