diff options
| author | John Corser <johnpc@umich.edu> | 2026-05-09 14:11:16 -0400 |
|---|---|---|
| committer | John Corser <xss@amazon.com> | 2026-06-28 21:44:46 -0400 |
| commit | d55f80842391e1b69db927ae8938c0455ddb5192 (patch) | |
| tree | bd390e05bb9df1c8ec33e137b7e8363b3b9115ab /Emby.Server.Implementations | |
| parent | dc92e3b0e489b7d6efe9ba950cbf124b84a3a4ce (diff) | |
Move people filtering to database query
Instead of loading all people names and checking each one in memory,
query the database directly for Person items that need refresh:
- Missing primary image OR missing overview
- Not refreshed within the last 30 days
This reduces the operation from N+1 queries (1 for all names + 1 per
person to load) to a single filtered query returning only the IDs that
need work.
Diffstat (limited to 'Emby.Server.Implementations')
| -rw-r--r-- | Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs | 84 |
1 files changed, 48 insertions, 36 deletions
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs index 0c1d004aed..7d42693b39 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Database.Implementations; +using Jellyfin.Database.Implementations.Entities; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -168,55 +169,66 @@ public class PeopleValidationTask : IScheduledTask, IConfigurableScheduledTask private async Task RefreshPeopleImagesAsync(IProgress<double> progress, CancellationToken cancellationToken) { - var people = _libraryManager.GetPeopleNames(new InternalPeopleQuery()); - var numPeople = people.Count; - var numComplete = 0; - var numRefreshed = 0; + var thirtyDaysAgo = DateTime.UtcNow.AddDays(-30); + var personTypeName = typeof(Person).FullName!; - _logger.LogDebug("Checking {Count} people for missing images", numPeople); - - foreach (var person in people) + var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false); + await using (context.ConfigureAwait(false)) { - cancellationToken.ThrowIfCancellationRequested(); + var peopleIds = await context.BaseItems + .AsNoTracking() + .Where(b => b.Type == personTypeName) + .Where(b => b.DateLastRefreshed == null || b.DateLastRefreshed < thirtyDaysAgo) + .Where(b => + !b.Images!.Any(i => i.ImageType == ImageInfoImageType.Primary) || + string.IsNullOrEmpty(b.Overview)) + .Select(b => b.Id) + .ToListAsync(cancellationToken) + .ConfigureAwait(false); - try + var numPeople = peopleIds.Count; + var numComplete = 0; + var numRefreshed = 0; + + _logger.LogDebug("Found {Count} people needing image/overview refresh", numPeople); + + foreach (var personId in peopleIds) { - var item = _libraryManager.GetPerson(person); - if (item is null) + cancellationToken.ThrowIfCancellationRequested(); + + try { - continue; - } + if (_libraryManager.GetItemById(personId) is not Person item) + { + continue; + } - var hasImage = item.HasImage(ImageType.Primary, 0); - var hasOverview = !string.IsNullOrWhiteSpace(item.Overview); + var hasImage = item.HasImage(ImageType.Primary, 0); + var hasOverview = !string.IsNullOrWhiteSpace(item.Overview); - if ((hasImage && hasOverview) || (DateTime.UtcNow - item.DateLastRefreshed).TotalDays < 30) + var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem)) + { + ImageRefreshMode = hasImage ? MetadataRefreshMode.ValidationOnly : MetadataRefreshMode.Default, + MetadataRefreshMode = hasOverview ? MetadataRefreshMode.ValidationOnly : MetadataRefreshMode.Default + }; + + await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false); + numRefreshed++; + } + catch (OperationCanceledException) { - continue; + throw; } - - var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem)) + catch (Exception ex) { - ImageRefreshMode = hasImage ? MetadataRefreshMode.ValidationOnly : MetadataRefreshMode.Default, - MetadataRefreshMode = hasOverview ? MetadataRefreshMode.ValidationOnly : MetadataRefreshMode.Default - }; + _logger.LogError(ex, "Error refreshing images for person {PersonId}", personId); + } - await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false); - numRefreshed++; - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception ex) - { - _logger.LogError(ex, "Error refreshing images for {Person}", person); + numComplete++; + progress.Report(100.0 * numComplete / numPeople); } - numComplete++; - progress.Report(100.0 * numComplete / numPeople); + _logger.LogInformation("Refreshed metadata for {Count} people missing images or overview", numRefreshed); } - - _logger.LogInformation("Refreshed metadata for {Count} people missing images or overview", numRefreshed); } } |
