aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
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
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')
-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);
}
}