Add People Dedup and multiple progress fixes (#14848)

This commit is contained in:
JPVenson
2025-09-25 00:20:30 +03:00
committed by GitHub
parent 897975fc57
commit 5a6d9180fe
11 changed files with 257 additions and 112 deletions

View File

@@ -327,6 +327,45 @@ namespace Emby.Server.Implementations.Library
DeleteItem(item, options, parent, notifyParentItem);
}
public void DeleteItemsUnsafeFast(IEnumerable<BaseItem> items)
{
var pathMaps = items.Select(e => (Item: e, InternalPath: GetInternalMetadataPaths(e), DeletePaths: e.GetDeletePaths())).ToArray();
foreach (var (item, internalPaths, pathsToDelete) in pathMaps)
{
foreach (var metadataPath in internalPaths)
{
if (!Directory.Exists(metadataPath))
{
continue;
}
_logger.LogDebug(
"Deleting metadata path, Type: {Type}, Name: {Name}, Path: {Path}, Id: {Id}",
item.GetType().Name,
item.Name ?? "Unknown name",
metadataPath,
item.Id);
try
{
Directory.Delete(metadataPath, true);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error deleting {MetadataPath}", metadataPath);
}
}
foreach (var fileSystemInfo in pathsToDelete)
{
DeleteItemPath(item, false, fileSystemInfo);
}
}
_itemRepository.DeleteItem([.. pathMaps.Select(f => f.Item.Id)]);
}
public void DeleteItem(BaseItem item, DeleteOptions options, BaseItem parent, bool notifyParentItem)
{
ArgumentNullException.ThrowIfNull(item);
@@ -403,59 +442,7 @@ namespace Emby.Server.Implementations.Library
foreach (var fileSystemInfo in item.GetDeletePaths())
{
if (Directory.Exists(fileSystemInfo.FullName) || File.Exists(fileSystemInfo.FullName))
{
try
{
_logger.LogInformation(
"Deleting item path, Type: {Type}, Name: {Name}, Path: {Path}, Id: {Id}",
item.GetType().Name,
item.Name ?? "Unknown name",
fileSystemInfo.FullName,
item.Id);
if (fileSystemInfo.IsDirectory)
{
Directory.Delete(fileSystemInfo.FullName, true);
}
else
{
File.Delete(fileSystemInfo.FullName);
}
}
catch (DirectoryNotFoundException)
{
_logger.LogInformation(
"Directory not found, only removing from database, Type: {Type}, Name: {Name}, Path: {Path}, Id: {Id}",
item.GetType().Name,
item.Name ?? "Unknown name",
fileSystemInfo.FullName,
item.Id);
}
catch (FileNotFoundException)
{
_logger.LogInformation(
"File not found, only removing from database, Type: {Type}, Name: {Name}, Path: {Path}, Id: {Id}",
item.GetType().Name,
item.Name ?? "Unknown name",
fileSystemInfo.FullName,
item.Id);
}
catch (IOException)
{
if (isRequiredForDelete)
{
throw;
}
}
catch (UnauthorizedAccessException)
{
if (isRequiredForDelete)
{
throw;
}
}
}
DeleteItemPath(item, isRequiredForDelete, fileSystemInfo);
isRequiredForDelete = false;
}
@@ -463,17 +450,73 @@ namespace Emby.Server.Implementations.Library
item.SetParent(null);
_itemRepository.DeleteItem(item.Id);
_itemRepository.DeleteItem([item.Id, .. children.Select(f => f.Id)]);
_cache.TryRemove(item.Id, out _);
foreach (var child in children)
{
_itemRepository.DeleteItem(child.Id);
_cache.TryRemove(child.Id, out _);
}
ReportItemRemoved(item, parent);
}
private void DeleteItemPath(BaseItem item, bool isRequiredForDelete, FileSystemMetadata fileSystemInfo)
{
if (Directory.Exists(fileSystemInfo.FullName) || File.Exists(fileSystemInfo.FullName))
{
try
{
_logger.LogInformation(
"Deleting item path, Type: {Type}, Name: {Name}, Path: {Path}, Id: {Id}",
item.GetType().Name,
item.Name ?? "Unknown name",
fileSystemInfo.FullName,
item.Id);
if (fileSystemInfo.IsDirectory)
{
Directory.Delete(fileSystemInfo.FullName, true);
}
else
{
File.Delete(fileSystemInfo.FullName);
}
}
catch (DirectoryNotFoundException)
{
_logger.LogInformation(
"Directory not found, only removing from database, Type: {Type}, Name: {Name}, Path: {Path}, Id: {Id}",
item.GetType().Name,
item.Name ?? "Unknown name",
fileSystemInfo.FullName,
item.Id);
}
catch (FileNotFoundException)
{
_logger.LogInformation(
"File not found, only removing from database, Type: {Type}, Name: {Name}, Path: {Path}, Id: {Id}",
item.GetType().Name,
item.Name ?? "Unknown name",
fileSystemInfo.FullName,
item.Id);
}
catch (IOException)
{
if (isRequiredForDelete)
{
throw;
}
}
catch (UnauthorizedAccessException)
{
if (isRequiredForDelete)
{
throw;
}
}
}
}
private bool IsInternalItem(BaseItem item)
{
if (!item.IsFileProtocol)
@@ -990,6 +1033,11 @@ namespace Emby.Server.Implementations.Library
return GetArtist(name, new DtoOptions(true));
}
public IReadOnlyDictionary<string, MusicArtist[]> GetArtists(IReadOnlyList<string> names)
{
return _itemRepository.FindArtists(names);
}
public MusicArtist GetArtist(string name, DtoOptions options)
{
return CreateItemByName<MusicArtist>(MusicArtist.GetPath, name, options);
@@ -1115,18 +1163,24 @@ namespace Emby.Server.Implementations.Library
cancellationToken: cancellationToken).ConfigureAwait(false);
// Quickly scan CollectionFolders for changes
var toDelete = new List<Guid>();
foreach (var child in rootFolder.Children!.OfType<Folder>())
{
// If the user has somehow deleted the collection directory, remove the metadata from the database.
if (child is CollectionFolder collectionFolder && !Directory.Exists(collectionFolder.Path))
{
_itemRepository.DeleteItem(collectionFolder.Id);
toDelete.Add(collectionFolder.Id);
}
else
{
await child.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
}
if (toDelete.Count > 0)
{
_itemRepository.DeleteItem(toDelete.ToArray());
}
}
private async Task PerformLibraryValidation(IProgress<double> progress, CancellationToken cancellationToken)

View File

@@ -1,5 +1,5 @@
using System;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
@@ -55,6 +55,8 @@ public class PeopleValidator
var numPeople = people.Count;
IProgress<double> subProgress = new Progress<double>((val) => progress.Report(val / 2));
_logger.LogDebug("Will refresh {Amount} people", numPeople);
foreach (var person in people)
@@ -92,7 +94,7 @@ public class PeopleValidator
double percent = numComplete;
percent /= numPeople;
progress.Report(100 * percent);
subProgress.Report(100 * percent);
}
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
@@ -102,17 +104,13 @@ public class PeopleValidator
IsLocked = false
});
foreach (var item in deadEntities)
{
_logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
subProgress = new Progress<double>((val) => progress.Report((val / 2) + 50));
_libraryManager.DeleteItem(
item,
new DeleteOptions
{
DeleteFileLocation = false
},
false);
var i = 0;
foreach (var item in deadEntities.Chunk(500))
{
_libraryManager.DeleteItemsUnsafeFast(item);
subProgress.Report(100f / deadEntities.Count * (i++ * 100));
}
progress.Report(100);