Fix extras naming and version assignment
This commit is contained in:
@@ -88,7 +88,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
Model.Entities.ExtraType.Short
|
||||
};
|
||||
|
||||
private static readonly char[] VersionDelimiters = ['-', '_', '.'];
|
||||
private protected static readonly char[] VersionDelimiters = ['-', '_', '.'];
|
||||
|
||||
private string _sortName;
|
||||
|
||||
@@ -1543,19 +1543,33 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
private async Task<bool> RefreshExtras(BaseItem item, MetadataRefreshOptions options, IReadOnlyList<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
|
||||
{
|
||||
var extras = LibraryManager.FindExtras(item, fileSystemChildren, options.DirectoryService).ToArray();
|
||||
var newExtraIds = Array.ConvertAll(extras, x => x.Id);
|
||||
|
||||
// An extra is owned by the version it is named after, so all of them are maintained together.
|
||||
var currentExtras = LibraryManager.GetItemList(new InternalItemsQuery()
|
||||
{
|
||||
OwnerIds = [item.Id]
|
||||
});
|
||||
OwnerIds = item.GetOwnedVersionIds()
|
||||
}).Where(e => e.ExtraType.HasValue).ToList();
|
||||
|
||||
var currentExtraIds = currentExtras.Select(e => e.Id).ToArray();
|
||||
|
||||
// Snapshot the persisted names before resolving, as FindExtras corrects the name on the
|
||||
// items it hands back and may well hand back these very instances.
|
||||
var currentExtraNames = new Dictionary<Guid, string>();
|
||||
foreach (var extra in currentExtras)
|
||||
{
|
||||
currentExtraNames[extra.Id] = extra.Name;
|
||||
}
|
||||
|
||||
var extras = LibraryManager.FindExtras(item, fileSystemChildren, options.DirectoryService).ToArray();
|
||||
var newExtraIds = Array.ConvertAll(extras, x => x.Id);
|
||||
|
||||
var renamedExtraIds = extras
|
||||
.Where(e => currentExtraNames.TryGetValue(e.Id, out var oldName) && !string.Equals(oldName, e.Name, StringComparison.Ordinal))
|
||||
.Select(e => e.Id)
|
||||
.ToHashSet();
|
||||
|
||||
var extrasChanged = !currentExtraIds.OrderBy(x => x).SequenceEqual(newExtraIds.OrderBy(x => x));
|
||||
|
||||
if (!extrasChanged && !options.ReplaceAllMetadata && options.MetadataRefreshMode != MetadataRefreshMode.FullRefresh)
|
||||
if (!extrasChanged && renamedExtraIds.Count == 0 && !options.ReplaceAllMetadata && options.MetadataRefreshMode != MetadataRefreshMode.FullRefresh)
|
||||
{
|
||||
// The owner's dates may only have become known after its extras were created, so keep
|
||||
// them in sync even when there is nothing to refresh.
|
||||
@@ -1570,12 +1584,11 @@ namespace MediaBrowser.Controller.Entities
|
||||
return false;
|
||||
}
|
||||
|
||||
var ownerId = item.Id;
|
||||
|
||||
var tasks = extras.Select(i =>
|
||||
{
|
||||
var ownerId = item.GetOwnerIdForExtra(i);
|
||||
var subOptions = new MetadataRefreshOptions(options);
|
||||
if (!i.OwnerId.Equals(ownerId) || !i.ParentId.IsEmpty())
|
||||
if (!i.OwnerId.Equals(ownerId) || !i.ParentId.IsEmpty() || renamedExtraIds.Contains(i.Id))
|
||||
{
|
||||
subOptions.ForceSave = true;
|
||||
}
|
||||
@@ -2920,6 +2933,25 @@ namespace MediaBrowser.Controller.Entities
|
||||
return [Id];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ids of this item and the versions of it whose extras it maintains.
|
||||
/// </summary>
|
||||
/// <returns>An array containing the version ids.</returns>
|
||||
protected virtual Guid[] GetOwnedVersionIds()
|
||||
{
|
||||
return [Id];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the id of the version an extra belongs to.
|
||||
/// </summary>
|
||||
/// <param name="extra">The extra.</param>
|
||||
/// <returns>The id of the owning version.</returns>
|
||||
protected virtual Guid GetOwnerIdForExtra(BaseItem extra)
|
||||
{
|
||||
return Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all extras associated with this item, sorted by <see cref="SortName"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -751,6 +751,80 @@ namespace MediaBrowser.Controller.Entities
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Guid[] GetOwnedVersionIds()
|
||||
{
|
||||
// Only the versions that live beside this one in the folder this scan covers. Linked
|
||||
// versions are items of their own and maintain their extras themselves.
|
||||
return [Id, .. LibraryManager.GetLocalAlternateVersionIds(this)];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Guid GetOwnerIdForExtra(BaseItem extra)
|
||||
{
|
||||
if (string.IsNullOrEmpty(extra.Path))
|
||||
{
|
||||
return Id;
|
||||
}
|
||||
|
||||
var extraDirectory = System.IO.Path.GetDirectoryName(extra.Path.AsSpan());
|
||||
var extraFileName = System.IO.Path.GetFileNameWithoutExtension(extra.Path.AsSpan());
|
||||
|
||||
var ownerId = Id;
|
||||
var matchedLength = MatchedVersionNameLength(Path, extraDirectory, extraFileName);
|
||||
|
||||
foreach (var versionId in LibraryManager.GetLocalAlternateVersionIds(this))
|
||||
{
|
||||
var version = LibraryManager.GetItemById(versionId);
|
||||
if (version is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// "Movie - [2160p]-trailer.mkv" belongs to "Movie - [2160p].mkv" rather than to the
|
||||
// primary version, whose name it also starts with when the primary is plain "Movie.mkv"
|
||||
var length = MatchedVersionNameLength(version.Path, extraDirectory, extraFileName);
|
||||
if (length > matchedLength)
|
||||
{
|
||||
matchedLength = length;
|
||||
ownerId = versionId;
|
||||
}
|
||||
}
|
||||
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets how much of an extra's file name is the name of the given version file, or 0 when the
|
||||
/// extra is not named after it.
|
||||
/// </summary>
|
||||
/// <param name="versionPath">The path of the version.</param>
|
||||
/// <param name="extraDirectory">The directory the extra lives in.</param>
|
||||
/// <param name="extraFileName">The file name of the extra, without extension.</param>
|
||||
/// <returns>The length of the match.</returns>
|
||||
private static int MatchedVersionNameLength(string versionPath, ReadOnlySpan<char> extraDirectory, ReadOnlySpan<char> extraFileName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(versionPath)
|
||||
|| !System.IO.Path.GetDirectoryName(versionPath.AsSpan()).Equals(extraDirectory, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var versionFileName = System.IO.Path.GetFileNameWithoutExtension(versionPath.AsSpan());
|
||||
if (versionFileName.IsEmpty || !extraFileName.StartsWith(versionFileName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The version name has to end where the extra's own name begins, so that a version
|
||||
// named "Movie - 4K" does not claim the extras of "Movie - 4Kish"
|
||||
var remainder = extraFileName[versionFileName.Length..];
|
||||
|
||||
return !remainder.IsEmpty && (remainder[0] == ' ' || Array.IndexOf(VersionDelimiters, remainder[0]) >= 0)
|
||||
? versionFileName.Length
|
||||
: 0;
|
||||
}
|
||||
|
||||
protected override IEnumerable<(BaseItem Item, MediaSourceType MediaSourceType)> GetAllItemsForMediaSources()
|
||||
{
|
||||
var primary = PrimaryVersionId.HasValue
|
||||
|
||||
Reference in New Issue
Block a user