aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCody Robibero <cody@robibe.ro>2025-08-12 20:20:54 -0600
committerGitHub <noreply@github.com>2025-08-12 20:20:54 -0600
commitbd94ca30711e551c834d10c5c3de6fb6a7795bc7 (patch)
tree52040cf9cb2b083cf923dd093b94d7c97908a327
parent869b4f8bbf3d27c95e3a012b1f9d7d1cb590fcfc (diff)
parentbeca405ad4c771f9cfdd5b410b4a4ba9f1c2d6c8 (diff)
Merge pull request #14634 from crobibero/itemname-counts
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs169
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs19
-rw-r--r--Jellyfin.Server.Implementations/Item/BaseItemRepository.cs60
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs2
-rw-r--r--MediaBrowser.Controller/Persistence/IItemRepository.cs2
-rw-r--r--MediaBrowser.Model/Dto/ItemCounts.cs9
6 files changed, 224 insertions, 37 deletions
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index cf886ae82..0db1606ea 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -37,6 +38,77 @@ namespace Emby.Server.Implementations.Dto
{
public class DtoService : IDtoService
{
+ private static readonly FrozenDictionary<BaseItemKind, BaseItemKind[]> _relatedItemKinds = new Dictionary<BaseItemKind, BaseItemKind[]>
+ {
+ {
+ BaseItemKind.Genre, [
+ BaseItemKind.Audio,
+ BaseItemKind.Episode,
+ BaseItemKind.Movie,
+ BaseItemKind.LiveTvProgram,
+ BaseItemKind.MusicAlbum,
+ BaseItemKind.MusicArtist,
+ BaseItemKind.MusicVideo,
+ BaseItemKind.Series,
+ BaseItemKind.Trailer
+ ]
+ },
+ {
+ BaseItemKind.MusicArtist, [
+ BaseItemKind.Audio,
+ BaseItemKind.MusicAlbum,
+ BaseItemKind.MusicVideo
+ ]
+ },
+ {
+ BaseItemKind.MusicGenre, [
+ BaseItemKind.Audio,
+ BaseItemKind.MusicAlbum,
+ BaseItemKind.MusicArtist,
+ BaseItemKind.MusicVideo
+ ]
+ },
+ {
+ BaseItemKind.Person, [
+ BaseItemKind.Audio,
+ BaseItemKind.Episode,
+ BaseItemKind.Movie,
+ BaseItemKind.LiveTvProgram,
+ BaseItemKind.MusicAlbum,
+ BaseItemKind.MusicArtist,
+ BaseItemKind.MusicVideo,
+ BaseItemKind.Series,
+ BaseItemKind.Trailer
+ ]
+ },
+ {
+ BaseItemKind.Studio, [
+ BaseItemKind.Audio,
+ BaseItemKind.Episode,
+ BaseItemKind.Movie,
+ BaseItemKind.LiveTvProgram,
+ BaseItemKind.MusicAlbum,
+ BaseItemKind.MusicArtist,
+ BaseItemKind.MusicVideo,
+ BaseItemKind.Series,
+ BaseItemKind.Trailer
+ ]
+ },
+ {
+ BaseItemKind.Year, [
+ BaseItemKind.Audio,
+ BaseItemKind.Episode,
+ BaseItemKind.Movie,
+ BaseItemKind.LiveTvProgram,
+ BaseItemKind.MusicAlbum,
+ BaseItemKind.MusicArtist,
+ BaseItemKind.MusicVideo,
+ BaseItemKind.Series,
+ BaseItemKind.Trailer
+ ]
+ }
+ }.ToFrozenDictionary();
+
private readonly ILogger<DtoService> _logger;
private readonly ILibraryManager _libraryManager;
private readonly IUserDataManager _userDataRepository;
@@ -102,21 +174,9 @@ namespace Emby.Server.Implementations.Dto
(programTuples ??= []).Add((item, dto));
}
- if (item is IItemByName byName)
+ if (options.ContainsField(ItemFields.ItemCounts))
{
- if (options.ContainsField(ItemFields.ItemCounts))
- {
- var libraryItems = byName.GetTaggedItems(new InternalItemsQuery(user)
- {
- Recursive = true,
- DtoOptions = new DtoOptions(false)
- {
- EnableImages = false
- }
- });
-
- SetItemByNameInfo(item, dto, libraryItems);
- }
+ SetItemByNameInfo(dto, user);
}
returnItems[index] = dto;
@@ -147,34 +207,14 @@ namespace Emby.Server.Implementations.Dto
LivetvManager.AddInfoToProgramDto(new[] { (item, dto) }, options.Fields, user).GetAwaiter().GetResult();
}
- if (item is IItemByName itemByName
- && options.ContainsField(ItemFields.ItemCounts))
+ if (options.ContainsField(ItemFields.ItemCounts))
{
- SetItemByNameInfo(
- item,
- dto,
- GetTaggedItems(
- itemByName,
- user,
- new DtoOptions(false)
- {
- EnableImages = false
- }));
+ SetItemByNameInfo(dto, user);
}
return dto;
}
- private static IReadOnlyList<BaseItem> GetTaggedItems(IItemByName byName, User? user, DtoOptions options)
- {
- return byName.GetTaggedItems(
- new InternalItemsQuery(user)
- {
- Recursive = true,
- DtoOptions = options
- });
- }
-
private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User? user = null, BaseItem? owner = null)
{
var dto = new BaseItemDto
@@ -315,11 +355,15 @@ namespace Emby.Server.Implementations.Dto
}
/// <inheritdoc />
+ /// TODO refactor this to use the new SetItemByNameInfo.
+ /// Some callers already have the counts extracted so no reason to retrieve them again.
public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem>? taggedItems, User? user = null)
{
var dto = GetBaseItemDtoInternal(item, options, user);
- if (taggedItems is not null && options.ContainsField(ItemFields.ItemCounts))
+ if (options.ContainsField(ItemFields.ItemCounts)
+ && taggedItems is not null
+ && taggedItems.Count != 0)
{
SetItemByNameInfo(item, dto, taggedItems);
}
@@ -327,6 +371,57 @@ namespace Emby.Server.Implementations.Dto
return dto;
}
+ private void SetItemByNameInfo(BaseItemDto dto, User? user)
+ {
+ if (!_relatedItemKinds.TryGetValue(dto.Type, out var relatedItemKinds))
+ {
+ return;
+ }
+
+ var query = new InternalItemsQuery(user)
+ {
+ Recursive = true,
+ DtoOptions = new DtoOptions(false) { EnableImages = false },
+ IncludeItemTypes = relatedItemKinds
+ };
+
+ switch (dto.Type)
+ {
+ case BaseItemKind.Genre:
+ case BaseItemKind.MusicGenre:
+ query.GenreIds = [dto.Id];
+ break;
+ case BaseItemKind.MusicArtist:
+ query.ArtistIds = [dto.Id];
+ break;
+ case BaseItemKind.Person:
+ query.PersonIds = [dto.Id];
+ break;
+ case BaseItemKind.Studio:
+ query.StudioIds = [dto.Id];
+ break;
+ case BaseItemKind.Year
+ when int.TryParse(dto.Name, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year):
+ query.Years = [year];
+ break;
+ default:
+ return;
+ }
+
+ var counts = _libraryManager.GetItemCounts(query);
+
+ dto.AlbumCount = counts.AlbumCount;
+ dto.ArtistCount = counts.ArtistCount;
+ dto.EpisodeCount = counts.EpisodeCount;
+ dto.MovieCount = counts.MovieCount;
+ dto.MusicVideoCount = counts.MusicVideoCount;
+ dto.ProgramCount = counts.ProgramCount;
+ dto.SeriesCount = counts.SeriesCount;
+ dto.SongCount = counts.SongCount;
+ dto.TrailerCount = counts.TrailerCount;
+ dto.ChildCount = counts.TotalItemCount();
+ }
+
private static void SetItemByNameInfo(BaseItem item, BaseItemDto dto, IReadOnlyList<BaseItem> taggedItems)
{
if (item is MusicArtist)
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 526092c62..58a971f62 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -1389,6 +1389,25 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetCount(query);
}
+ public ItemCounts GetItemCounts(InternalItemsQuery query)
+ {
+ if (query.Recursive && !query.ParentId.IsEmpty())
+ {
+ var parent = GetItemById(query.ParentId);
+ if (parent is not null)
+ {
+ SetTopParentIdsOrAncestors(query, [parent]);
+ }
+ }
+
+ if (query.User is not null)
+ {
+ AddUserToQuery(query, query.User);
+ }
+
+ return _itemRepository.GetItemCounts(query);
+ }
+
public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents)
{
SetTopParentIdsOrAncestors(query, parents);
diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs
index b2062e6c8..ecde48406 100644
--- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs
+++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs
@@ -457,6 +457,66 @@ public sealed class BaseItemRepository
return dbQuery.Count();
}
+ /// <inheritdoc />
+ public ItemCounts GetItemCounts(InternalItemsQuery filter)
+ {
+ ArgumentNullException.ThrowIfNull(filter);
+ // Hack for right now since we currently don't support filtering out these duplicates within a query
+ PrepareFilterQuery(filter);
+
+ using var context = _dbProvider.CreateDbContext();
+ var dbQuery = TranslateQuery(context.BaseItems.AsNoTracking(), context, filter);
+
+ var counts = dbQuery
+ .GroupBy(x => x.Type)
+ .Select(x => new { x.Key, Count = x.Count() })
+ .AsEnumerable();
+
+ var lookup = _itemTypeLookup.BaseItemKindNames;
+ var result = new ItemCounts();
+ foreach (var count in counts)
+ {
+ if (string.Equals(count.Key, lookup[BaseItemKind.MusicAlbum], StringComparison.Ordinal))
+ {
+ result.AlbumCount = count.Count;
+ }
+ else if (string.Equals(count.Key, lookup[BaseItemKind.MusicArtist], StringComparison.Ordinal))
+ {
+ result.ArtistCount = count.Count;
+ }
+ else if (string.Equals(count.Key, lookup[BaseItemKind.Episode], StringComparison.Ordinal))
+ {
+ result.EpisodeCount = count.Count;
+ }
+ else if (string.Equals(count.Key, lookup[BaseItemKind.Movie], StringComparison.Ordinal))
+ {
+ result.MovieCount = count.Count;
+ }
+ else if (string.Equals(count.Key, lookup[BaseItemKind.MusicVideo], StringComparison.Ordinal))
+ {
+ result.MusicVideoCount = count.Count;
+ }
+ else if (string.Equals(count.Key, lookup[BaseItemKind.LiveTvProgram], StringComparison.Ordinal))
+ {
+ result.ProgramCount = count.Count;
+ }
+ else if (string.Equals(count.Key, lookup[BaseItemKind.Series], StringComparison.Ordinal))
+ {
+ result.SeriesCount = count.Count;
+ }
+ else if (string.Equals(count.Key, lookup[BaseItemKind.Audio], StringComparison.Ordinal))
+ {
+ result.SongCount = count.Count;
+ }
+ else if (string.Equals(count.Key, lookup[BaseItemKind.Trailer], StringComparison.Ordinal))
+ {
+ result.TrailerCount = count.Count;
+ }
+ }
+
+ return result;
+ }
+
#pragma warning disable CA1307 // Specify StringComparison for clarity
/// <summary>
/// Gets the type.
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 98ed15eb6..b72d1d0b4 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -630,6 +630,8 @@ namespace MediaBrowser.Controller.Library
int GetCount(InternalItemsQuery query);
+ ItemCounts GetItemCounts(InternalItemsQuery query);
+
Task RunMetadataSavers(BaseItem item, ItemUpdateType updateReason);
BaseItem GetParentItem(Guid? parentId, Guid? userId);
diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs
index f4ac0ece4..a0dabbac6 100644
--- a/MediaBrowser.Controller/Persistence/IItemRepository.cs
+++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs
@@ -84,6 +84,8 @@ public interface IItemRepository
int GetCount(InternalItemsQuery filter);
+ ItemCounts GetItemCounts(InternalItemsQuery filter);
+
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery filter);
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery filter);
diff --git a/MediaBrowser.Model/Dto/ItemCounts.cs b/MediaBrowser.Model/Dto/ItemCounts.cs
index 95f4a3d77..a15a0c82a 100644
--- a/MediaBrowser.Model/Dto/ItemCounts.cs
+++ b/MediaBrowser.Model/Dto/ItemCounts.cs
@@ -76,5 +76,14 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The item count.</value>
public int ItemCount { get; set; }
+
+ /// <summary>
+ /// Adds all counts.
+ /// </summary>
+ /// <returns>The total of the counts.</returns>
+ public int TotalItemCount()
+ {
+ return MovieCount + SeriesCount + EpisodeCount + ArtistCount + ProgramCount + TrailerCount + SongCount + AlbumCount + MusicVideoCount + BoxSetCount + BookCount;
+ }
}
}