aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Server.Implementations/Item/BaseItemRepository.cs')
-rw-r--r--Jellyfin.Server.Implementations/Item/BaseItemRepository.cs148
1 files changed, 82 insertions, 66 deletions
diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs
index 3d04cf95f..e89c43c45 100644
--- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs
+++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs
@@ -117,37 +117,37 @@ public sealed class BaseItemRepository(
}
/// <inheritdoc />
- public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAllArtists(InternalItemsQuery filter)
+ public QueryResult<(BaseItemDto Item, ItemCounts ItemCounts)> GetAllArtists(InternalItemsQuery filter)
{
return GetItemValues(filter, [ItemValueType.Artist, ItemValueType.AlbumArtist], itemTypeLookup.BaseItemKindNames[BaseItemKind.MusicArtist]!);
}
/// <inheritdoc />
- public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetArtists(InternalItemsQuery filter)
+ public QueryResult<(BaseItemDto Item, ItemCounts ItemCounts)> GetArtists(InternalItemsQuery filter)
{
return GetItemValues(filter, [ItemValueType.Artist], itemTypeLookup.BaseItemKindNames[BaseItemKind.MusicArtist]!);
}
/// <inheritdoc />
- public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAlbumArtists(InternalItemsQuery filter)
+ public QueryResult<(BaseItemDto Item, ItemCounts ItemCounts)> GetAlbumArtists(InternalItemsQuery filter)
{
return GetItemValues(filter, [ItemValueType.AlbumArtist], itemTypeLookup.BaseItemKindNames[BaseItemKind.MusicArtist]!);
}
/// <inheritdoc />
- public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetStudios(InternalItemsQuery filter)
+ public QueryResult<(BaseItemDto Item, ItemCounts ItemCounts)> GetStudios(InternalItemsQuery filter)
{
return GetItemValues(filter, [ItemValueType.Studios], itemTypeLookup.BaseItemKindNames[BaseItemKind.Studio]!);
}
/// <inheritdoc />
- public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery filter)
+ public QueryResult<(BaseItemDto Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery filter)
{
return GetItemValues(filter, [ItemValueType.Genre], itemTypeLookup.BaseItemKindNames[BaseItemKind.Genre]!);
}
/// <inheritdoc />
- public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery filter)
+ public QueryResult<(BaseItemDto Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery filter)
{
return GetItemValues(filter, [ItemValueType.Genre], itemTypeLookup.BaseItemKindNames[BaseItemKind.MusicGenre]!);
}
@@ -200,7 +200,7 @@ public sealed class BaseItemRepository(
using var context = dbProvider.CreateDbContext();
- IQueryable<BaseItemEntity> dbQuery = context.BaseItems.AsNoTracking()
+ IQueryable<BaseItemEntity> dbQuery = context.BaseItems.AsNoTracking().AsSingleQuery()
.Include(e => e.TrailerTypes)
.Include(e => e.Provider)
.Include(e => e.LockedFields);
@@ -212,28 +212,13 @@ public sealed class BaseItemRepository(
dbQuery = TranslateQuery(dbQuery, context, filter);
dbQuery = dbQuery.Distinct();
- // .DistinctBy(e => e.Id);
if (filter.EnableTotalRecordCount)
{
result.TotalRecordCount = dbQuery.Count();
}
dbQuery = ApplyOrder(dbQuery, filter);
-
- if (filter.Limit.HasValue || filter.StartIndex.HasValue)
- {
- var offset = filter.StartIndex ?? 0;
-
- if (offset > 0)
- {
- dbQuery = dbQuery.Skip(offset);
- }
-
- if (filter.Limit.HasValue)
- {
- dbQuery = dbQuery.Take(filter.Limit.Value);
- }
- }
+ dbQuery = ApplyQueryPageing(dbQuery, filter);
result.Items = dbQuery.AsEnumerable().Select(w => DeserialiseBaseItem(w, filter.SkipDeserialization)).ToImmutableArray();
result.StartIndex = filter.StartIndex ?? 0;
@@ -247,31 +232,43 @@ public sealed class BaseItemRepository(
PrepareFilterQuery(filter);
using var context = dbProvider.CreateDbContext();
+ IQueryable<BaseItemEntity> dbQuery = context.BaseItems.AsNoTracking().AsSingleQuery()
+ .Include(e => e.TrailerTypes)
+ .Include(e => e.Provider)
+ .Include(e => e.LockedFields);
+
+ if (filter.DtoOptions.EnableImages)
+ {
+ dbQuery = dbQuery.Include(e => e.Images);
+ }
- return PrepareItemQuery(context, filter).AsEnumerable().Select(w => DeserialiseBaseItem(w, filter.SkipDeserialization)).ToImmutableArray();
+ dbQuery = TranslateQuery(dbQuery, context, filter);
+ dbQuery = dbQuery.Distinct();
+ dbQuery = ApplyOrder(dbQuery, filter);
+ dbQuery = ApplyGroupingFilter(dbQuery, filter);
+
+ return dbQuery.AsEnumerable().Select(w => DeserialiseBaseItem(w, filter.SkipDeserialization)).ToImmutableArray();
}
private IQueryable<BaseItemEntity> ApplyGroupingFilter(IQueryable<BaseItemEntity> dbQuery, InternalItemsQuery filter)
{
- dbQuery = dbQuery.Distinct();
-
- // var enableGroupByPresentationUniqueKey = EnableGroupByPresentationUniqueKey(filter);
- // if (enableGroupByPresentationUniqueKey && filter.GroupBySeriesPresentationUniqueKey)
- // {
- // dbQuery = dbQuery.GroupBy(e => new { e.PresentationUniqueKey, e.SeriesPresentationUniqueKey }).Select(e => e.First());
- // }
- // else if (enableGroupByPresentationUniqueKey)
- // {
- // dbQuery = dbQuery.GroupBy(e => e.PresentationUniqueKey).Select(e => e.First());
- // }
- // else if (filter.GroupBySeriesPresentationUniqueKey)
- // {
- // dbQuery = dbQuery.GroupBy(e => e.SeriesPresentationUniqueKey).Select(e => e.First());
- // }
- // else
- // {
- // dbQuery = dbQuery.Distinct();
- // }
+ var enableGroupByPresentationUniqueKey = EnableGroupByPresentationUniqueKey(filter);
+ if (enableGroupByPresentationUniqueKey && filter.GroupBySeriesPresentationUniqueKey)
+ {
+ dbQuery = dbQuery.GroupBy(e => new { e.PresentationUniqueKey, e.SeriesPresentationUniqueKey }).Select(e => e.First());
+ }
+ else if (enableGroupByPresentationUniqueKey)
+ {
+ dbQuery = dbQuery.GroupBy(e => e.PresentationUniqueKey).Select(e => e.First());
+ }
+ else if (filter.GroupBySeriesPresentationUniqueKey)
+ {
+ dbQuery = dbQuery.GroupBy(e => e.SeriesPresentationUniqueKey).Select(e => e.First());
+ }
+ else
+ {
+ dbQuery = dbQuery.Distinct();
+ }
return dbQuery;
}
@@ -307,7 +304,7 @@ public sealed class BaseItemRepository(
private IQueryable<BaseItemEntity> PrepareItemQuery(JellyfinDbContext context, InternalItemsQuery filter)
{
- IQueryable<BaseItemEntity> dbQuery = context.BaseItems.AsNoTracking()
+ IQueryable<BaseItemEntity> dbQuery = context.BaseItems.AsNoTracking().AsSingleQuery()
.Include(e => e.TrailerTypes)
.Include(e => e.Provider)
.Include(e => e.LockedFields);
@@ -1086,13 +1083,13 @@ public sealed class BaseItemRepository(
if (filter.AncestorIds.Length > 0)
{
- baseQuery = baseQuery.Where(e => e.AncestorIds!.Any(f => filter.AncestorIds.Contains(f.ParentItemId)));
+ baseQuery = baseQuery.Where(e => e.Children!.Any(f => filter.AncestorIds.Contains(f.ParentItemId)));
}
if (!string.IsNullOrWhiteSpace(filter.AncestorWithPresentationUniqueKey))
{
baseQuery = baseQuery
- .Where(e => context.BaseItems.Where(f => f.PresentationUniqueKey == filter.AncestorWithPresentationUniqueKey).Any(f => f.AncestorIds!.Any(w => w.ItemId == f.Id)));
+ .Where(e => context.BaseItems.Where(f => f.PresentationUniqueKey == filter.AncestorWithPresentationUniqueKey).Any(f => f.ParentAncestors!.Any(w => w.ItemId == f.Id)));
}
if (!string.IsNullOrWhiteSpace(filter.SeriesPresentationUniqueKey))
@@ -1127,7 +1124,7 @@ public sealed class BaseItemRepository(
{
baseQuery = baseQuery
.Where(e =>
- e.AncestorIds!
+ e.ParentAncestors!
.Any(f =>
f.ParentItem.ItemValues!.Any(w => w.ItemValue.Type == ItemValueType.Tags && filter.IncludeInheritedTags.Contains(w.ItemValue.CleanValue))
|| e.Data!.Contains($"OwnerUserId\":\"{filter.User!.Id:N}\"")));
@@ -1136,7 +1133,7 @@ public sealed class BaseItemRepository(
else
{
baseQuery = baseQuery
- .Where(e => e.AncestorIds!.Any(f => f.ParentItem.ItemValues!.Any(w => w.ItemValue.Type == ItemValueType.Tags && filter.IncludeInheritedTags.Contains(w.ItemValue.CleanValue))));
+ .Where(e => e.ParentAncestors!.Any(f => f.ParentItem.ItemValues!.Any(w => w.ItemValue.Type == ItemValueType.Tags && filter.IncludeInheritedTags.Contains(w.ItemValue.CleanValue))));
}
}
@@ -1236,7 +1233,7 @@ public sealed class BaseItemRepository(
}
/// <inheritdoc cref="IItemRepository" />
- public void SaveImages(BaseItem item)
+ public void SaveImages(BaseItemDto item)
{
ArgumentNullException.ThrowIfNull(item);
@@ -1295,10 +1292,9 @@ public sealed class BaseItemRepository(
context.AncestorIds.Where(e => e.ItemId == entity.Id).ExecuteDelete();
if (item.Item.SupportsAncestors && item.AncestorIds != null)
{
- entity.AncestorIds = new List<AncestorId>();
foreach (var ancestorId in item.AncestorIds)
{
- entity.AncestorIds.Add(new AncestorId()
+ context.AncestorIds.Add(new AncestorId()
{
ParentItemId = ancestorId,
ItemId = entity.Id,
@@ -1378,7 +1374,7 @@ public sealed class BaseItemRepository(
/// <param name="entity">The entity.</param>
/// <param name="dto">The dto base instance.</param>
/// <returns>The dto to map.</returns>
- public BaseItemDto Map(BaseItemEntity entity, BaseItemDto dto)
+ public static BaseItemDto Map(BaseItemEntity entity, BaseItemDto dto)
{
dto.Id = entity.Id;
dto.ParentId = entity.ParentId.GetValueOrDefault();
@@ -1416,10 +1412,10 @@ public sealed class BaseItemRepository(
dto.Genres = entity.Genres?.Split('|') ?? [];
dto.DateCreated = entity.DateCreated.GetValueOrDefault();
dto.DateModified = entity.DateModified.GetValueOrDefault();
- dto.ChannelId = string.IsNullOrWhiteSpace(entity.ChannelId) ? Guid.Empty : Guid.Parse(entity.ChannelId);
+ dto.ChannelId = string.IsNullOrWhiteSpace(entity.ChannelId) ? Guid.Empty : (Guid.TryParse(entity.ChannelId, out var channelId) ? channelId : Guid.Empty);
dto.DateLastRefreshed = entity.DateLastRefreshed.GetValueOrDefault();
dto.DateLastSaved = entity.DateLastSaved.GetValueOrDefault();
- dto.OwnerId = string.IsNullOrWhiteSpace(entity.OwnerId) ? Guid.Empty : Guid.Parse(entity.OwnerId);
+ dto.OwnerId = string.IsNullOrWhiteSpace(entity.OwnerId) ? Guid.Empty : (Guid.TryParse(entity.OwnerId, out var ownerId) ? ownerId : Guid.Empty);
dto.Width = entity.Width.GetValueOrDefault();
dto.Height = entity.Height.GetValueOrDefault();
if (entity.Provider is not null)
@@ -1720,22 +1716,30 @@ public sealed class BaseItemRepository(
return query.Select(e => e.ItemValue.CleanValue).ToImmutableArray();
}
- private bool TypeRequiresDeserialization(Type type)
+ private static bool TypeRequiresDeserialization(Type type)
{
- if (serverConfigurationManager.Configuration.SkipDeserializationForBasicTypes)
- {
- if (type == typeof(Channel)
- || type == typeof(UserRootFolder))
- {
- return false;
- }
- }
-
return type.GetCustomAttribute<RequiresSourceSerialisationAttribute>() == null;
}
private BaseItemDto DeserialiseBaseItem(BaseItemEntity baseItemEntity, bool skipDeserialization = false)
{
+ var typeToSerialise = GetType(baseItemEntity.Type);
+ return BaseItemRepository.DeserialiseBaseItem(
+ baseItemEntity,
+ logger,
+ skipDeserialization || (serverConfigurationManager.Configuration.SkipDeserializationForBasicTypes && (typeToSerialise == typeof(Channel) || typeToSerialise == typeof(UserRootFolder))));
+ }
+
+ /// <summary>
+ /// Deserialises a BaseItemEntity and sets all properties.
+ /// </summary>
+ /// <param name="baseItemEntity">The DB entity.</param>
+ /// <param name="logger">Logger.</param>
+ /// <param name="skipDeserialization">If only mapping should be processed.</param>
+ /// <returns>A mapped BaseItem.</returns>
+ /// <exception cref="InvalidOperationException">Will be thrown if an invalid serialisation is requested.</exception>
+ public static BaseItemDto DeserialiseBaseItem(BaseItemEntity baseItemEntity, ILogger logger, bool skipDeserialization = false)
+ {
var type = GetType(baseItemEntity.Type) ?? throw new InvalidOperationException("Cannot deserialise unkown type.");
BaseItemDto? dto = null;
if (TypeRequiresDeserialization(type) && baseItemEntity.Data is not null && !skipDeserialization)
@@ -1815,7 +1819,7 @@ public sealed class BaseItemRepository(
}
}
- var result = new QueryResult<(BaseItem, ItemCounts)>();
+ var result = new QueryResult<(BaseItemDto, ItemCounts)>();
if (filter.EnableTotalRecordCount)
{
result.TotalRecordCount = query.DistinctBy(e => e.PresentationUniqueKey).Count();
@@ -1877,7 +1881,7 @@ public sealed class BaseItemRepository(
return value.RemoveDiacritics().ToLowerInvariant();
}
- private List<(int MagicNumber, string Value)> GetItemValuesToSave(BaseItem item, List<string> inheritedTags)
+ private List<(int MagicNumber, string Value)> GetItemValuesToSave(BaseItemDto item, List<string> inheritedTags)
{
var list = new List<(int, string)>();
@@ -2144,6 +2148,18 @@ public sealed class BaseItemRepository(
{
orderedQuery = query.OrderByDescending(expression);
}
+
+ if (firstOrdering.OrderBy is ItemSortBy.Default or ItemSortBy.SortName)
+ {
+ if (firstOrdering.SortOrder is SortOrder.Ascending)
+ {
+ orderedQuery = orderedQuery.ThenBy(e => e.Name);
+ }
+ else
+ {
+ orderedQuery = orderedQuery.ThenByDescending(e => e.Name);
+ }
+ }
}
foreach (var item in orderBy.Skip(1))