mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-10-26 16:45:09 +00:00
Backport pull request #17842 from jellyfin/release-12.z
Fix versions of a video still listing separately from their group and preserve manual merges
Original-merge: cabec7ec28
Merged-by: crobibero <cody@robibe.ro>
Backported-by: Cody Robibero <cody@robibe.ro>
This commit is contained in:
1 parent
7baca5f2b8
commit
8150ee2484
16 files changed
+1226
-28
No files matched your search
@@ -316,7 +316,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
var dictionary = new Dictionary<Guid, BaseItem>();
|
||||
|
||||
Children = null; // invalidate cached children.
|
||||
var childrenList = Children.ToList();
|
||||
var childrenList = GetChildrenForValidation();
|
||||
|
||||
foreach (var child in childrenList)
|
||||
{
|
||||
@@ -551,7 +551,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
&& primaryVideo.OwnerId.IsEmpty()
|
||||
&& (primaryVideo.LocalAlternateVersions ?? []).Any(p => alternateVersionPaths.Contains(p)))
|
||||
{
|
||||
var newPrimary = newItems
|
||||
var newPrimary = validChildren
|
||||
.OfType<Video>()
|
||||
.FirstOrDefault(v => (v.LocalAlternateVersions ?? [])
|
||||
.Any(p => (primaryVideo.LocalAlternateVersions ?? [])
|
||||
@@ -593,6 +593,8 @@ namespace MediaBrowser.Controller.Entities
|
||||
newPrimary.Name,
|
||||
newPrimary.Id);
|
||||
|
||||
await PromoteToPrimaryVersionAsync(newPrimary, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Reroute collection/playlist references from old primary to new primary
|
||||
await LibraryManager.RerouteLinkedChildReferencesAsync(oldPrimary.Id, newPrimary.Id).ConfigureAwait(false);
|
||||
|
||||
@@ -621,9 +623,12 @@ namespace MediaBrowser.Controller.Entities
|
||||
LibraryManager.DeleteItem(oldPrimary, new DeleteOptions { DeleteFileLocation = false }, this, false);
|
||||
}
|
||||
|
||||
// Demote old primaries that are now alternate versions of newly created primaries.
|
||||
// Demote old primaries that are now alternate versions of another primary.
|
||||
// This handles the case where a new file is added that becomes the new primary
|
||||
// (e.g. movie-2 added, movie-3 was primary → movie-3 needs demotion).
|
||||
// (e.g. movie-2 added, movie-3 was primary → movie-3 needs demotion), and the case
|
||||
// where the file that takes over was already in the library and merely traded
|
||||
// places with this one — so the new primary is looked up among all valid children
|
||||
// rather than only the newly created ones.
|
||||
// Items in replacedPrimaries are excluded (already in actuallyRemoved).
|
||||
var oldPrimariesToDemote = new List<(Video OldPrimary, Video NewPrimary)>();
|
||||
foreach (var item in itemsRemoved.Except(actuallyRemoved))
|
||||
@@ -633,7 +638,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
&& !string.IsNullOrEmpty(item.Path)
|
||||
&& alternateVersionPaths.Contains(item.Path))
|
||||
{
|
||||
var newPrimary = newItems
|
||||
var newPrimary = validChildren
|
||||
.OfType<Video>()
|
||||
.FirstOrDefault(v => (v.LocalAlternateVersions ?? [])
|
||||
.Any(p => string.Equals(p, item.Path, StringComparison.OrdinalIgnoreCase)));
|
||||
@@ -653,10 +658,13 @@ namespace MediaBrowser.Controller.Entities
|
||||
newPrimary.Name,
|
||||
newPrimary.Id);
|
||||
|
||||
await PromoteToPrimaryVersionAsync(newPrimary, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// First: update old primary's alternate items to point to new primary.
|
||||
// Order matters — update alternates FIRST so they don't get orphan-deleted
|
||||
// when old primary's arrays are cleared.
|
||||
var oldAlternateIds = LibraryManager.GetLocalAlternateVersionIds(oldPrimary)
|
||||
var oldLocalAlternateIds = LibraryManager.GetLocalAlternateVersionIds(oldPrimary).ToHashSet();
|
||||
var oldAlternateIds = oldLocalAlternateIds
|
||||
.Concat(LibraryManager.GetLinkedAlternateVersions(oldPrimary).Select(v => v.Id))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
@@ -666,7 +674,10 @@ namespace MediaBrowser.Controller.Entities
|
||||
if (LibraryManager.GetItemById(altId) is Video altVideo && !altVideo.Id.Equals(newPrimary.Id))
|
||||
{
|
||||
altVideo.SetPrimaryVersionId(newPrimary.Id);
|
||||
altVideo.OwnerId = newPrimary.Id;
|
||||
|
||||
// Only a version stored next to the new primary is owned by it; one that
|
||||
// was merged in by hand keeps its own row and must stay unowned.
|
||||
altVideo.OwnerId = oldLocalAlternateIds.Contains(altVideo.Id) ? newPrimary.Id : Guid.Empty;
|
||||
await altVideo.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -772,6 +783,23 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PromoteToPrimaryVersionAsync(Video newPrimary, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!newPrimary.PrimaryVersionId.HasValue && newPrimary.OwnerId.IsEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogInformation(
|
||||
"Promoting {Name} ({Id}) to the primary version of its group",
|
||||
newPrimary.Name,
|
||||
newPrimary.Id);
|
||||
|
||||
newPrimary.SetPrimaryVersionId(null);
|
||||
newPrimary.OwnerId = Guid.Empty;
|
||||
await newPrimary.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task RefreshMetadataRecursive(IList<BaseItem> children, MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
await RunTasks(
|
||||
@@ -876,6 +904,17 @@ namespace MediaBrowser.Controller.Entities
|
||||
});
|
||||
}
|
||||
|
||||
private IReadOnlyList<BaseItem> GetChildrenForValidation()
|
||||
{
|
||||
return ItemRepository.GetItemList(new InternalItemsQuery
|
||||
{
|
||||
Parent = this,
|
||||
GroupByPresentationUniqueKey = false,
|
||||
IncludeAlternateVersions = true,
|
||||
DtoOptions = new DtoOptions(true)
|
||||
});
|
||||
}
|
||||
|
||||
public virtual int GetChildCount(User user)
|
||||
{
|
||||
if (LinkedChildren.Length > 0)
|
||||
|
||||
@@ -488,6 +488,14 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// </summary>
|
||||
public bool IncludeOwnedItems { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to include alternate versions, which carry a
|
||||
/// <see cref="Video.PrimaryVersionId"/> and are normally hidden behind the version they
|
||||
/// belong to. Unlike <see cref="IncludeOwnedItems"/> this keeps the versions a user merged
|
||||
/// by hand without also returning the parts and extras owned by another item.
|
||||
/// </summary>
|
||||
public bool IncludeAlternateVersions { get; set; }
|
||||
|
||||
public bool? Is4K { get; set; }
|
||||
|
||||
public int? MaxHeight { get; set; }
|
||||
|
||||
Reference in new issue
Block a user