diff options
| author | Evan <evan@MacBook-Pro.local> | 2025-08-08 23:21:40 +0800 |
|---|---|---|
| committer | Evan <evan@MacBook-Pro.local> | 2025-08-10 18:02:17 +0800 |
| commit | 0a4ff3f3c0592cb32b7fb98bfc9f423386ecb84c (patch) | |
| tree | 5daba1e704d045ca9ddce93a6ec7e43a66c16fef /MediaBrowser.Controller | |
| parent | 2b94b3b5f637fda7bff76b4044ba976975efce4f (diff) | |
Fix GetBaseItemDto to return related item counts via SQL count
For API call /Items/{item id} GetBaseItemDto will return the counts of related items e.g. artists, albums, songs. GetBaseItemDto currently does this by calling GetTaggedItems which retrieves the objects into memory to count them. Replace with SQL count.
Fixes:
This should be an improvement for any large libraries, but especially large music libraries. Example:
Request Library -> Genres -> any very popular genre in your large library, e.g. Classical
Number of albums = 1552, songs = 23515, ...
- Before change: Try to retrieve 1552 albums, 23515 songs, ... in memory, API never returns, database on fire
- After change: API returns in 367ms and Genre view opens with 200 albums in 2 seconds
I verified the numbers returned are correct but note that there is a bug somewhere else in Jellyfin that is setting TopParentId to NULL for a large portion of my MusicArtists, which causes them to not be counted by the existing GetCount(). This is not related to this change, also happens with the existing code, and does not seem to affect the Web UI.
Includes Cory's changes in:
- https://github.com/jellyfin/jellyfin/pull/14610#issuecomment-3172211468
- https://github.com/jellyfin/jellyfin/pull/14610#issuecomment-3172239154
Diffstat (limited to 'MediaBrowser.Controller')
| -rw-r--r-- | MediaBrowser.Controller/Entities/Audio/MusicArtist.cs | 18 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Audio/MusicGenre.cs | 21 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Genre.cs | 31 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/IItemByName.cs | 25 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Person.cs | 38 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Studio.cs | 37 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Year.cs | 42 |
7 files changed, 212 insertions, 0 deletions
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 58841e5b7..cde4cd6b3 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -98,6 +98,24 @@ 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); diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs index 65669e680..9538e0cfa 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -73,6 +73,27 @@ 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); diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index 6ec78a270..20fab93b2 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -76,6 +76,37 @@ 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); diff --git a/MediaBrowser.Controller/Entities/IItemByName.cs b/MediaBrowser.Controller/Entities/IItemByName.cs index 4928bda7a..3e79e3f00 100644 --- a/MediaBrowser.Controller/Entities/IItemByName.cs +++ b/MediaBrowser.Controller/Entities/IItemByName.cs @@ -10,10 +10,35 @@ namespace MediaBrowser.Controller.Entities public interface IItemByName { IReadOnlyList<BaseItem> GetTaggedItems(InternalItemsQuery query); + + TaggedItemCounts GetTaggedItemCounts(InternalItemsQuery query); } public interface IHasDualAccess : IItemByName { bool IsAccessedByName { get; } } + + 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); + } } diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index 5cc4d322f..d4158655b 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -5,6 +5,7 @@ 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; @@ -70,6 +71,43 @@ 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; diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index 9103b09a9..a44b32d85 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using Jellyfin.Data.Enums; using Jellyfin.Extensions; using Microsoft.Extensions.Logging; @@ -71,6 +72,42 @@ 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); diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs index 37820296c..a1ff9f4d4 100644 --- a/MediaBrowser.Controller/Entities/Year.cs +++ b/MediaBrowser.Controller/Entities/Year.cs @@ -6,6 +6,7 @@ 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 @@ -68,6 +69,47 @@ 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)) |
