aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
diff options
context:
space:
mode:
authorJohn Corser <johnpc@umich.edu>2026-05-09 14:11:16 -0400
committerJohn Corser <xss@amazon.com>2026-06-28 21:44:46 -0400
commitd55f80842391e1b69db927ae8938c0455ddb5192 (patch)
treebd390e05bb9df1c8ec33e137b7e8363b3b9115ab /Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
parentdc92e3b0e489b7d6efe9ba950cbf124b84a3a4ce (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/ScheduledTasks/Tasks/PeopleValidationTask.cs')
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs84
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);
}
}