diff options
| author | Cody Robibero <cody@robibe.ro> | 2026-07-05 16:21:02 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-05 16:21:02 -0400 |
| commit | 6e728b009f0c622415a11d392198c1d8d61ff91f (patch) | |
| tree | 2eff0e3a1c6dae179661fc4f87a29fd110f504f4 /Emby.Server.Implementations/Library/MediaSourceManager.cs | |
| parent | efd3814a7e5b233fa70da1024157ac4b7aed1d4c (diff) | |
| parent | 38f1d9749ee67f18264937807b2f5882e1421557 (diff) | |
Merge pull request #17044 from Shadowghost/version-model-and-handling
Fixes for multi version handling
Diffstat (limited to 'Emby.Server.Implementations/Library/MediaSourceManager.cs')
| -rw-r--r-- | Emby.Server.Implementations/Library/MediaSourceManager.cs | 69 |
1 files changed, 68 insertions, 1 deletions
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index c369fb0957..c64833ddaa 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -229,7 +229,11 @@ namespace Emby.Server.Implementations.Library list.Add(source); } - return SortMediaSources(list, item.Id).ToArray(); + var preferredId = mediaSources.Count > 0 && Guid.TryParse(mediaSources[0].Id, out var topSourceId) + ? topSourceId + : item.Id; + + return SortMediaSources(list, preferredId).ToArray(); } /// <inheritdoc />> @@ -406,6 +410,69 @@ namespace Emby.Server.Implementations.Library source.SupportsDirectStream = user.HasPermission(PermissionKind.EnablePlaybackRemuxing); } } + + sources = SetAlternateVersionResumeStates(item, sources, user); + } + + return sources; + } + + /// <summary> + /// Populates each source's own playback position for the user and, when the queried item is a + /// primary, moves the most recently played version to the front so that resuming without an + /// explicit source selection plays the version that was last watched. A directly queried + /// alternate version keeps its own source first. + /// </summary> + /// <param name="item">The queried item.</param> + /// <param name="sources">The item's media sources.</param> + /// <param name="user">The user.</param> + /// <returns>The media sources, reordered when a version drives resume.</returns> + private IReadOnlyList<MediaSourceInfo> SetAlternateVersionResumeStates(BaseItem item, IReadOnlyList<MediaSourceInfo> sources, User user) + { + // For a video, multiple sources means alternate versions. + if (item is not Video video || sources.Count < 2) + { + return sources; + } + + var versions = video.GetAllVersions(); + if (versions.Count < 2) + { + return sources; + } + + var userDataByVersion = _userDataManager.GetUserDataBatch(versions, user); + var dataBySourceId = new Dictionary<string, UserItemData>(versions.Count, StringComparer.OrdinalIgnoreCase); + foreach (var version in versions) + { + if (userDataByVersion.TryGetValue(version.Id, out var data)) + { + dataBySourceId[version.Id.ToString("N", CultureInfo.InvariantCulture)] = data; + } + } + + foreach (var source in sources) + { + if (source.Id is not null + && dataBySourceId.TryGetValue(source.Id, out var data) + && data.PlaybackPositionTicks > 0) + { + 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 }; + reordered.AddRange(sources.Where(s => !ReferenceEquals(s, resumeSource))); + return reordered; } return sources; |
