From 792ce4a391c524f8e20f676be2d6cab07e5a59c4 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Tue, 1 Sep 2026 07:36:53 +0200 Subject: Fix people validator not creating missing people --- .../Library/Validators/PeopleValidator.cs | 79 ++++++++++------------ 1 file changed, 34 insertions(+), 45 deletions(-) (limited to 'Emby.Server.Implementations/Library/Validators/PeopleValidator.cs') diff --git a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs index 078a0b921d..3c8806d549 100644 --- a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs +++ b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs @@ -1,12 +1,11 @@ using System; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Library.Validators; @@ -17,94 +16,88 @@ namespace Emby.Server.Implementations.Library.Validators; public class PeopleValidator { /// - /// The _library manager. + /// The library manager. /// private readonly ILibraryManager _libraryManager; /// - /// The _logger. + /// The logger. /// - private readonly ILogger _logger; - - private readonly IFileSystem _fileSystem; + private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The library manager. /// The logger. - /// The file system. - public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem) + public PeopleValidator(ILibraryManager libraryManager, ILogger logger) { _libraryManager = libraryManager; _logger = logger; - _fileSystem = fileSystem; } /// /// Validates the people. /// - /// The cancellation token. /// The progress. + /// The cancellation token. /// Task. - public async Task ValidatePeople(CancellationToken cancellationToken, IProgress progress) + public async Task Run(IProgress progress, CancellationToken cancellationToken) { // Before the refresh below walks them: a credit no item maps to any more stands for nothing, // and while it is there the person it names cannot reach the dead-person sweep either. var numOrphaned = _libraryManager.DeleteOrphanedCredits(); if (numOrphaned > 0) { - _logger.LogDebug("Deleted {Amount} credits no item maps to", numOrphaned); + _logger.LogInformation("Deleted {Amount} credits no item maps to", numOrphaned); } - var people = _libraryManager.GetPeopleNames(new InternalPeopleQuery()); + var names = _libraryManager.GetPeopleNames(new InternalPeopleQuery()); + var existingPersonIds = _libraryManager.GetItemIds(new InternalItemsQuery + { + IncludeItemTypes = [BaseItemKind.Person] + }).ToHashSet(); var numComplete = 0; + var count = names.Count; + var refreshed = 0; - var numPeople = people.Count; - - IProgress subProgress = new Progress((val) => progress.Report(val / 2)); - - _logger.LogDebug("Will refresh {Amount} people", numPeople); - - foreach (var person in people) + foreach (var name in names) { cancellationToken.ThrowIfCancellationRequested(); try { - var item = _libraryManager.GetPerson(person); - if (item is null) - { - _logger.LogWarning("Failed to get person: {Name}", person); - continue; - } + var item = _libraryManager.GetOrCreatePerson(name); + var isNew = !existingPersonIds.Contains(item.Id); + var neverRefreshed = item.DateLastRefreshed == default; - var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem)) + if (isNew || neverRefreshed) { - ImageRefreshMode = MetadataRefreshMode.ValidationOnly, - MetadataRefreshMode = MetadataRefreshMode.ValidationOnly - }; - - await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false); + await item.RefreshMetadata(cancellationToken).ConfigureAwait(false); + refreshed++; + } } catch (OperationCanceledException) { + // Don't clutter the log throw; } catch (Exception ex) { - _logger.LogError(ex, "Error validating IBN entry {Person}", person); + _logger.LogError(ex, "Error refreshing {PersonName}", name); } - // Update progress numComplete++; double percent = numComplete; - percent /= numPeople; + percent /= count; + percent *= 100; - subProgress.Report(100 * percent); + progress.Report(percent); } + _logger.LogInformation("Refreshed metadata for {RefreshedCount} new people out of {TotalCount} total", refreshed, count); + var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery { IncludeItemTypes = [BaseItemKind.Person], @@ -112,17 +105,13 @@ public class PeopleValidator IsLocked = false }); - subProgress = new Progress((val) => progress.Report((val / 2) + 50)); - - var i = 0; - foreach (var item in deadEntities.Chunk(500)) + foreach (var item in deadEntities) { - _libraryManager.DeleteItemsUnsafeFast(item, true); - subProgress.Report(100f / deadEntities.Count * (i++ * 100)); + _logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name); } - progress.Report(100); + _libraryManager.DeleteItemsUnsafeFast(deadEntities, deleteSourceFiles: true); - _logger.LogInformation("People validation complete, deleted {Orphaned} orphaned credits", numOrphaned); + progress.Report(100); } } -- cgit v1.2.3