瀏覽代碼

Refactor to pull item counts in a single query

Cody Robibero 1 周之前
父節點
當前提交
beca405ad4

+ 115 - 17
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,9 +174,9 @@ namespace Emby.Server.Implementations.Dto
                     (programTuples ??= []).Add((item, dto));
                 }
 
-                if (item is IItemByName itemByName && options.ContainsField(ItemFields.ItemCounts))
+                if (options.ContainsField(ItemFields.ItemCounts))
                 {
-                    SetItemByNameInfo(itemByName, dto, user);
+                    SetItemByNameInfo(dto, user);
                 }
 
                 returnItems[index] = dto;
@@ -135,9 +207,9 @@ 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(itemByName, dto, user);
+                SetItemByNameInfo(dto, user);
             }
 
             return dto;
@@ -283,34 +355,60 @@ 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 (options.ContainsField(ItemFields.ItemCounts))
+            if (options.ContainsField(ItemFields.ItemCounts)
+                && taggedItems is not null
+                && taggedItems.Count != 0)
             {
-                if (taggedItems is not null)
-                {
-                    SetItemByNameInfo(item, dto, taggedItems!);
-                }
-                else if (item is IItemByName itemByName)
-                {
-                    SetItemByNameInfo(itemByName, dto, user);
-                }
+                SetItemByNameInfo(item, dto, taggedItems);
             }
 
             return dto;
         }
 
-        private static void SetItemByNameInfo(IItemByName item, BaseItemDto dto, User? user)
+        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 }
+                DtoOptions = new DtoOptions(false) { EnableImages = false },
+                IncludeItemTypes = relatedItemKinds
             };
 
-            var counts = item.GetTaggedItemCounts(query);
+            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;
@@ -321,7 +419,7 @@ namespace Emby.Server.Implementations.Dto
             dto.SeriesCount = counts.SeriesCount;
             dto.SongCount = counts.SongCount;
             dto.TrailerCount = counts.TrailerCount;
-            dto.ChildCount = counts.ChildCount;
+            dto.ChildCount = counts.TotalItemCount();
         }
 
         private static void SetItemByNameInfo(BaseItem item, BaseItemDto dto, IReadOnlyList<BaseItem> taggedItems)

+ 19 - 0
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);

+ 60 - 0
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.

+ 0 - 18
MediaBrowser.Controller/Entities/Audio/MusicArtist.cs

@@ -98,24 +98,6 @@ namespace MediaBrowser.Controller.Entities.Audio
             return LibraryManager.GetItemList(query);
         }
 
-        public TaggedItemCounts GetTaggedItemCounts(InternalItemsQuery query)
-        {
-            query.ArtistIds = [Id];
-
-            var counts = new TaggedItemCounts();
-
-            query.IncludeItemTypes = [BaseItemKind.MusicAlbum];
-            counts.AlbumCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.MusicVideo];
-            counts.MusicVideoCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Audio];
-            counts.SongCount = LibraryManager.GetCount(query);
-
-            return counts;
-        }
-
         public override int GetChildCount(User user)
         {
             return IsAccessedByName ? 0 : base.GetChildCount(user);

+ 0 - 21
MediaBrowser.Controller/Entities/Audio/MusicGenre.cs

@@ -73,27 +73,6 @@ namespace MediaBrowser.Controller.Entities.Audio
             return LibraryManager.GetItemList(query);
         }
 
-        public TaggedItemCounts GetTaggedItemCounts(InternalItemsQuery query)
-        {
-            query.GenreIds = [Id];
-
-            var counts = new TaggedItemCounts();
-
-            query.IncludeItemTypes = [BaseItemKind.MusicAlbum];
-            counts.AlbumCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.MusicArtist];
-            counts.ArtistCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.MusicVideo];
-            counts.MusicVideoCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Audio];
-            counts.SongCount = LibraryManager.GetCount(query);
-
-            return counts;
-        }
-
         public static string GetPath(string name)
         {
             return GetPath(name, true);

+ 0 - 31
MediaBrowser.Controller/Entities/Genre.cs

@@ -76,37 +76,6 @@ namespace MediaBrowser.Controller.Entities
             return LibraryManager.GetItemList(query);
         }
 
-        public TaggedItemCounts GetTaggedItemCounts(InternalItemsQuery query)
-        {
-            query.GenreIds = [Id];
-            query.ExcludeItemTypes =
-            [
-                BaseItemKind.MusicVideo,
-                BaseItemKind.Audio,
-                BaseItemKind.MusicAlbum,
-                BaseItemKind.MusicArtist
-            ];
-
-            var counts = new TaggedItemCounts();
-
-            query.IncludeItemTypes = [BaseItemKind.Episode];
-            counts.EpisodeCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Movie];
-            counts.MovieCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.LiveTvProgram];
-            counts.ProgramCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Series];
-            counts.SeriesCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Trailer];
-            counts.TrailerCount = LibraryManager.GetCount(query);
-
-            return counts;
-        }
-
         public static string GetPath(string name)
         {
             return GetPath(name, true);

+ 0 - 2
MediaBrowser.Controller/Entities/IItemByName.cs

@@ -10,8 +10,6 @@ namespace MediaBrowser.Controller.Entities
     public interface IItemByName
     {
         IReadOnlyList<BaseItem> GetTaggedItems(InternalItemsQuery query);
-
-        TaggedItemCounts GetTaggedItemCounts(InternalItemsQuery query);
     }
 
     public interface IHasDualAccess : IItemByName

+ 0 - 38
MediaBrowser.Controller/Entities/Person.cs

@@ -5,7 +5,6 @@
 using System;
 using System.Collections.Generic;
 using System.Text.Json.Serialization;
-using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
 using MediaBrowser.Controller.Providers;
 using Microsoft.Extensions.Logging;
@@ -71,43 +70,6 @@ namespace MediaBrowser.Controller.Entities
             return LibraryManager.GetItemList(query);
         }
 
-        public TaggedItemCounts GetTaggedItemCounts(InternalItemsQuery query)
-        {
-            query.PersonIds = [Id];
-
-            var counts = new TaggedItemCounts();
-
-            // TODO: Remove MusicAlbum and MusicArtist when the relationship between Persons and Music is removed
-            query.IncludeItemTypes = [BaseItemKind.MusicAlbum];
-            counts.AlbumCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.MusicArtist];
-            counts.ArtistCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Episode];
-            counts.EpisodeCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Movie];
-            counts.MovieCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.MusicVideo];
-            counts.MusicVideoCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.LiveTvProgram];
-            counts.ProgramCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Series];
-            counts.SeriesCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Audio];
-            counts.SongCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Trailer];
-            counts.TrailerCount = LibraryManager.GetCount(query);
-
-            return counts;
-        }
-
         public override bool CanDelete()
         {
             return false;

+ 0 - 37
MediaBrowser.Controller/Entities/Studio.cs

@@ -5,7 +5,6 @@
 using System;
 using System.Collections.Generic;
 using System.Text.Json.Serialization;
-using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
 using Microsoft.Extensions.Logging;
 
@@ -72,42 +71,6 @@ namespace MediaBrowser.Controller.Entities
             return LibraryManager.GetItemList(query);
         }
 
-        public TaggedItemCounts GetTaggedItemCounts(InternalItemsQuery query)
-        {
-            query.StudioIds = [Id];
-
-            var counts = new TaggedItemCounts();
-
-            query.IncludeItemTypes = [BaseItemKind.MusicAlbum];
-            counts.AlbumCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.MusicArtist];
-            counts.ArtistCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Episode];
-            counts.EpisodeCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Movie];
-            counts.MovieCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.MusicVideo];
-            counts.MusicVideoCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.LiveTvProgram];
-            counts.ProgramCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Series];
-            counts.SeriesCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Audio];
-            counts.SongCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Trailer];
-            counts.TrailerCount = LibraryManager.GetCount(query);
-
-            return counts;
-        }
-
         public static string GetPath(string name)
         {
             return GetPath(name, true);

+ 0 - 27
MediaBrowser.Controller/Entities/TaggedItemCounts.cs

@@ -1,27 +0,0 @@
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Controller.Entities
-{
-    public class TaggedItemCounts
-    {
-        public int? AlbumCount { get; set; }
-
-        public int? ArtistCount { get; set; }
-
-        public int? EpisodeCount { get; set; }
-
-        public int? MovieCount { get; set; }
-
-        public int? MusicVideoCount { get; set; }
-
-        public int? ProgramCount { get; set; }
-
-        public int? SeriesCount { get; set; }
-
-        public int? SongCount { get; set; }
-
-        public int? TrailerCount { get; set; }
-
-        public int ChildCount => (AlbumCount ?? 0) + (ArtistCount ?? 0) + (EpisodeCount ?? 0) + (MovieCount ?? 0) + (MusicVideoCount ?? 0) + (ProgramCount ?? 0) + (SeriesCount ?? 0) + (SongCount ?? 0) + (TrailerCount ?? 0);
-    }
-}

+ 0 - 42
MediaBrowser.Controller/Entities/Year.cs

@@ -6,7 +6,6 @@ using System;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Text.Json.Serialization;
-using Jellyfin.Data.Enums;
 using Microsoft.Extensions.Logging;
 
 namespace MediaBrowser.Controller.Entities
@@ -69,47 +68,6 @@ namespace MediaBrowser.Controller.Entities
             return LibraryManager.GetItemList(query);
         }
 
-        public TaggedItemCounts GetTaggedItemCounts(InternalItemsQuery query)
-        {
-            if (!int.TryParse(Name, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
-            {
-                return new TaggedItemCounts();
-            }
-
-            query.Years = [year];
-
-            var counts = new TaggedItemCounts();
-
-            query.IncludeItemTypes = [BaseItemKind.MusicAlbum];
-            counts.AlbumCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.MusicArtist];
-            counts.ArtistCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Episode];
-            counts.EpisodeCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Movie];
-            counts.MovieCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.MusicVideo];
-            counts.MusicVideoCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.LiveTvProgram];
-            counts.ProgramCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Series];
-            counts.SeriesCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Audio];
-            counts.SongCount = LibraryManager.GetCount(query);
-
-            query.IncludeItemTypes = [BaseItemKind.Trailer];
-            counts.TrailerCount = LibraryManager.GetCount(query);
-
-            return counts;
-        }
-
         public int? GetYearValue()
         {
             if (int.TryParse(Name, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))

+ 2 - 0
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);

+ 2 - 0
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);

+ 9 - 0
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;
+        }
     }
 }