aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShadowghost <Ghost_of_Stone@web.de>2026-05-24 18:26:21 +0200
committerShadowghost <Ghost_of_Stone@web.de>2026-05-24 18:26:21 +0200
commitcb9d6e9884d3b952321736392801743198b0ccd9 (patch)
tree20d5c3e533f1447ceb0d1c6413d831f075702828
parent11758461200c7b369dd23db5ac7c9f232a87cf21 (diff)
Add batch method for people names
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs6
-rw-r--r--Jellyfin.Server.Implementations/Item/PeopleRepository.cs58
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs8
-rw-r--r--MediaBrowser.Controller/Persistence/IPeopleRepository.cs10
4 files changed, 82 insertions, 0 deletions
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 30ff1bd333..662e28ec1d 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -3394,6 +3394,12 @@ namespace Emby.Server.Implementations.Library
return _peopleRepository.GetPeopleNames(query);
}
+ /// <inheritdoc/>
+ public IReadOnlyDictionary<Guid, IReadOnlyList<string>> GetPeopleNamesByItem(IReadOnlyList<Guid> itemIds, IReadOnlyList<string> personTypes)
+ {
+ return _peopleRepository.GetPeopleNamesByItem(itemIds, personTypes);
+ }
+
public void UpdatePeople(BaseItem item, List<PersonInfo> people)
{
UpdatePeopleAsync(item, people, CancellationToken.None).GetAwaiter().GetResult();
diff --git a/Jellyfin.Server.Implementations/Item/PeopleRepository.cs b/Jellyfin.Server.Implementations/Item/PeopleRepository.cs
index b612112d49..d84a59850d 100644
--- a/Jellyfin.Server.Implementations/Item/PeopleRepository.cs
+++ b/Jellyfin.Server.Implementations/Item/PeopleRepository.cs
@@ -165,6 +165,64 @@ public class PeopleRepository(IDbContextFactory<JellyfinDbContext> dbProvider, I
transaction.Commit();
}
+ /// <inheritdoc/>
+ public IReadOnlyDictionary<Guid, IReadOnlyList<string>> GetPeopleNamesByItem(IReadOnlyList<Guid> itemIds, IReadOnlyList<string> personTypes)
+ {
+ if (itemIds.Count == 0)
+ {
+ return new Dictionary<Guid, IReadOnlyList<string>>();
+ }
+
+ using var context = _dbProvider.CreateDbContext();
+ var query = context.PeopleBaseItemMap
+ .AsNoTracking()
+ .Where(m => itemIds.Contains(m.ItemId));
+
+ if (personTypes.Count > 0)
+ {
+ query = query.Where(m => personTypes.Contains(m.People.PersonType));
+ }
+
+ // One round-trip: pull (ItemId, ListOrder, Name) sorted by ItemId+ListOrder, group in memory.
+ var rows = query
+ .OrderBy(m => m.ItemId)
+ .ThenBy(m => m.ListOrder)
+ .Select(m => new { m.ItemId, m.People.Name })
+ .ToArray();
+
+ var result = new Dictionary<Guid, IReadOnlyList<string>>();
+ List<string>? current = null;
+ var currentId = Guid.Empty;
+ var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var row in rows)
+ {
+ if (row.ItemId != currentId)
+ {
+ if (current is { Count: > 0 })
+ {
+ result[currentId] = current;
+ }
+
+ currentId = row.ItemId;
+ current = new List<string>();
+ seen.Clear();
+ }
+
+ if (!string.IsNullOrWhiteSpace(row.Name) && seen.Add(row.Name))
+ {
+ current!.Add(row.Name);
+ }
+ }
+
+ if (current is { Count: > 0 })
+ {
+ result[currentId] = current;
+ }
+
+ return result;
+ }
+
private PersonInfo Map(People people)
{
var mapping = people.BaseItems?.FirstOrDefault();
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index f4c2196400..d794205f00 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -598,6 +598,14 @@ namespace MediaBrowser.Controller.Library
IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery query);
/// <summary>
+ /// Gets the people names per item for a batch of item IDs in a single DB round-trip.
+ /// </summary>
+ /// <param name="itemIds">The item IDs to look up.</param>
+ /// <param name="personTypes">Optional person types to include. Empty for all.</param>
+ /// <returns>Dictionary keyed by item id; values are the per-item people names. Items with no people are absent.</returns>
+ IReadOnlyDictionary<Guid, IReadOnlyList<string>> GetPeopleNamesByItem(IReadOnlyList<Guid> itemIds, IReadOnlyList<string> personTypes);
+
+ /// <summary>
/// Queries the items.
/// </summary>
/// <param name="query">The query.</param>
diff --git a/MediaBrowser.Controller/Persistence/IPeopleRepository.cs b/MediaBrowser.Controller/Persistence/IPeopleRepository.cs
index a89f3ef9ee..3a3b2bfb1f 100644
--- a/MediaBrowser.Controller/Persistence/IPeopleRepository.cs
+++ b/MediaBrowser.Controller/Persistence/IPeopleRepository.cs
@@ -32,4 +32,14 @@ public interface IPeopleRepository
/// <param name="filter">The query.</param>
/// <returns>The list of people names matching the filter.</returns>
IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery filter);
+
+ /// <summary>
+ /// Gets the people names per item for a batch of item IDs, preserving per-item list order.
+ /// One database round-trip for the whole batch; grouped by item id in memory.
+ /// Items with no people are omitted from the returned dictionary.
+ /// </summary>
+ /// <param name="itemIds">The item IDs to get people for.</param>
+ /// <param name="personTypes">Optional person types to include (e.g. "Actor", "Director"). Empty for all.</param>
+ /// <returns>Dictionary keyed by item id; values are the per-item people names.</returns>
+ IReadOnlyDictionary<Guid, IReadOnlyList<string>> GetPeopleNamesByItem(IReadOnlyList<Guid> itemIds, IReadOnlyList<string> personTypes);
}