mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
Fixed AncestorIds
Fixed Sorting, NextUp and Continue Watching
This commit is contained in:
parent
a7a2257ccb
commit
85b8b2573b
@ -164,7 +164,9 @@ public class BaseItemEntity
|
|||||||
|
|
||||||
public ICollection<BaseItemProvider>? Provider { get; set; }
|
public ICollection<BaseItemProvider>? Provider { get; set; }
|
||||||
|
|
||||||
public ICollection<AncestorId>? AncestorIds { get; set; }
|
public ICollection<AncestorId>? ParentAncestors { get; set; }
|
||||||
|
|
||||||
|
public ICollection<AncestorId>? Children { get; set; }
|
||||||
|
|
||||||
public ICollection<BaseItemMetadataField>? LockedFields { get; set; }
|
public ICollection<BaseItemMetadataField>? LockedFields { get; set; }
|
||||||
|
|
||||||
|
@ -117,37 +117,37 @@ public sealed class BaseItemRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <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]!);
|
return GetItemValues(filter, [ItemValueType.Artist, ItemValueType.AlbumArtist], itemTypeLookup.BaseItemKindNames[BaseItemKind.MusicArtist]!);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <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]!);
|
return GetItemValues(filter, [ItemValueType.Artist], itemTypeLookup.BaseItemKindNames[BaseItemKind.MusicArtist]!);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <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]!);
|
return GetItemValues(filter, [ItemValueType.AlbumArtist], itemTypeLookup.BaseItemKindNames[BaseItemKind.MusicArtist]!);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <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]!);
|
return GetItemValues(filter, [ItemValueType.Studios], itemTypeLookup.BaseItemKindNames[BaseItemKind.Studio]!);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <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]!);
|
return GetItemValues(filter, [ItemValueType.Genre], itemTypeLookup.BaseItemKindNames[BaseItemKind.Genre]!);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <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]!);
|
return GetItemValues(filter, [ItemValueType.Genre], itemTypeLookup.BaseItemKindNames[BaseItemKind.MusicGenre]!);
|
||||||
}
|
}
|
||||||
@ -200,7 +200,7 @@ public sealed class BaseItemRepository(
|
|||||||
|
|
||||||
using var context = dbProvider.CreateDbContext();
|
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.TrailerTypes)
|
||||||
.Include(e => e.Provider)
|
.Include(e => e.Provider)
|
||||||
.Include(e => e.LockedFields);
|
.Include(e => e.LockedFields);
|
||||||
@ -212,28 +212,13 @@ public sealed class BaseItemRepository(
|
|||||||
|
|
||||||
dbQuery = TranslateQuery(dbQuery, context, filter);
|
dbQuery = TranslateQuery(dbQuery, context, filter);
|
||||||
dbQuery = dbQuery.Distinct();
|
dbQuery = dbQuery.Distinct();
|
||||||
// .DistinctBy(e => e.Id);
|
|
||||||
if (filter.EnableTotalRecordCount)
|
if (filter.EnableTotalRecordCount)
|
||||||
{
|
{
|
||||||
result.TotalRecordCount = dbQuery.Count();
|
result.TotalRecordCount = dbQuery.Count();
|
||||||
}
|
}
|
||||||
|
|
||||||
dbQuery = ApplyOrder(dbQuery, filter);
|
dbQuery = ApplyOrder(dbQuery, filter);
|
||||||
|
dbQuery = ApplyQueryPageing(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Items = dbQuery.AsEnumerable().Select(w => DeserialiseBaseItem(w, filter.SkipDeserialization)).ToImmutableArray();
|
result.Items = dbQuery.AsEnumerable().Select(w => DeserialiseBaseItem(w, filter.SkipDeserialization)).ToImmutableArray();
|
||||||
result.StartIndex = filter.StartIndex ?? 0;
|
result.StartIndex = filter.StartIndex ?? 0;
|
||||||
@ -247,31 +232,43 @@ public sealed class BaseItemRepository(
|
|||||||
PrepareFilterQuery(filter);
|
PrepareFilterQuery(filter);
|
||||||
|
|
||||||
using var context = dbProvider.CreateDbContext();
|
using var context = dbProvider.CreateDbContext();
|
||||||
|
IQueryable<BaseItemEntity> dbQuery = context.BaseItems.AsNoTracking().AsSingleQuery()
|
||||||
|
.Include(e => e.TrailerTypes)
|
||||||
|
.Include(e => e.Provider)
|
||||||
|
.Include(e => e.LockedFields);
|
||||||
|
|
||||||
return PrepareItemQuery(context, filter).AsEnumerable().Select(w => DeserialiseBaseItem(w, filter.SkipDeserialization)).ToImmutableArray();
|
if (filter.DtoOptions.EnableImages)
|
||||||
|
{
|
||||||
|
dbQuery = dbQuery.Include(e => e.Images);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
private IQueryable<BaseItemEntity> ApplyGroupingFilter(IQueryable<BaseItemEntity> dbQuery, InternalItemsQuery filter)
|
||||||
{
|
{
|
||||||
dbQuery = dbQuery.Distinct();
|
var enableGroupByPresentationUniqueKey = EnableGroupByPresentationUniqueKey(filter);
|
||||||
|
if (enableGroupByPresentationUniqueKey && filter.GroupBySeriesPresentationUniqueKey)
|
||||||
// var enableGroupByPresentationUniqueKey = EnableGroupByPresentationUniqueKey(filter);
|
{
|
||||||
// if (enableGroupByPresentationUniqueKey && filter.GroupBySeriesPresentationUniqueKey)
|
dbQuery = dbQuery.GroupBy(e => new { e.PresentationUniqueKey, e.SeriesPresentationUniqueKey }).Select(e => e.First());
|
||||||
// {
|
}
|
||||||
// dbQuery = dbQuery.GroupBy(e => new { e.PresentationUniqueKey, e.SeriesPresentationUniqueKey }).Select(e => e.First());
|
else if (enableGroupByPresentationUniqueKey)
|
||||||
// }
|
{
|
||||||
// else if (enableGroupByPresentationUniqueKey)
|
dbQuery = dbQuery.GroupBy(e => e.PresentationUniqueKey).Select(e => e.First());
|
||||||
// {
|
}
|
||||||
// dbQuery = dbQuery.GroupBy(e => e.PresentationUniqueKey).Select(e => e.First());
|
else if (filter.GroupBySeriesPresentationUniqueKey)
|
||||||
// }
|
{
|
||||||
// else if (filter.GroupBySeriesPresentationUniqueKey)
|
dbQuery = dbQuery.GroupBy(e => e.SeriesPresentationUniqueKey).Select(e => e.First());
|
||||||
// {
|
}
|
||||||
// dbQuery = dbQuery.GroupBy(e => e.SeriesPresentationUniqueKey).Select(e => e.First());
|
else
|
||||||
// }
|
{
|
||||||
// else
|
dbQuery = dbQuery.Distinct();
|
||||||
// {
|
}
|
||||||
// dbQuery = dbQuery.Distinct();
|
|
||||||
// }
|
|
||||||
|
|
||||||
return dbQuery;
|
return dbQuery;
|
||||||
}
|
}
|
||||||
@ -307,7 +304,7 @@ public sealed class BaseItemRepository(
|
|||||||
|
|
||||||
private IQueryable<BaseItemEntity> PrepareItemQuery(JellyfinDbContext context, InternalItemsQuery filter)
|
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.TrailerTypes)
|
||||||
.Include(e => e.Provider)
|
.Include(e => e.Provider)
|
||||||
.Include(e => e.LockedFields);
|
.Include(e => e.LockedFields);
|
||||||
@ -1086,13 +1083,13 @@ public sealed class BaseItemRepository(
|
|||||||
|
|
||||||
if (filter.AncestorIds.Length > 0)
|
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))
|
if (!string.IsNullOrWhiteSpace(filter.AncestorWithPresentationUniqueKey))
|
||||||
{
|
{
|
||||||
baseQuery = baseQuery
|
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))
|
if (!string.IsNullOrWhiteSpace(filter.SeriesPresentationUniqueKey))
|
||||||
@ -1127,7 +1124,7 @@ public sealed class BaseItemRepository(
|
|||||||
{
|
{
|
||||||
baseQuery = baseQuery
|
baseQuery = baseQuery
|
||||||
.Where(e =>
|
.Where(e =>
|
||||||
e.AncestorIds!
|
e.ParentAncestors!
|
||||||
.Any(f =>
|
.Any(f =>
|
||||||
f.ParentItem.ItemValues!.Any(w => w.ItemValue.Type == ItemValueType.Tags && filter.IncludeInheritedTags.Contains(w.ItemValue.CleanValue))
|
f.ParentItem.ItemValues!.Any(w => w.ItemValue.Type == ItemValueType.Tags && filter.IncludeInheritedTags.Contains(w.ItemValue.CleanValue))
|
||||||
|| e.Data!.Contains($"OwnerUserId\":\"{filter.User!.Id:N}\"")));
|
|| e.Data!.Contains($"OwnerUserId\":\"{filter.User!.Id:N}\"")));
|
||||||
@ -1136,7 +1133,7 @@ public sealed class BaseItemRepository(
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
baseQuery = baseQuery
|
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" />
|
/// <inheritdoc cref="IItemRepository" />
|
||||||
public void SaveImages(BaseItem item)
|
public void SaveImages(BaseItemDto item)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(item);
|
ArgumentNullException.ThrowIfNull(item);
|
||||||
|
|
||||||
@ -1295,10 +1292,9 @@ public sealed class BaseItemRepository(
|
|||||||
context.AncestorIds.Where(e => e.ItemId == entity.Id).ExecuteDelete();
|
context.AncestorIds.Where(e => e.ItemId == entity.Id).ExecuteDelete();
|
||||||
if (item.Item.SupportsAncestors && item.AncestorIds != null)
|
if (item.Item.SupportsAncestors && item.AncestorIds != null)
|
||||||
{
|
{
|
||||||
entity.AncestorIds = new List<AncestorId>();
|
|
||||||
foreach (var ancestorId in item.AncestorIds)
|
foreach (var ancestorId in item.AncestorIds)
|
||||||
{
|
{
|
||||||
entity.AncestorIds.Add(new AncestorId()
|
context.AncestorIds.Add(new AncestorId()
|
||||||
{
|
{
|
||||||
ParentItemId = ancestorId,
|
ParentItemId = ancestorId,
|
||||||
ItemId = entity.Id,
|
ItemId = entity.Id,
|
||||||
@ -1378,7 +1374,7 @@ public sealed class BaseItemRepository(
|
|||||||
/// <param name="entity">The entity.</param>
|
/// <param name="entity">The entity.</param>
|
||||||
/// <param name="dto">The dto base instance.</param>
|
/// <param name="dto">The dto base instance.</param>
|
||||||
/// <returns>The dto to map.</returns>
|
/// <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.Id = entity.Id;
|
||||||
dto.ParentId = entity.ParentId.GetValueOrDefault();
|
dto.ParentId = entity.ParentId.GetValueOrDefault();
|
||||||
@ -1416,10 +1412,10 @@ public sealed class BaseItemRepository(
|
|||||||
dto.Genres = entity.Genres?.Split('|') ?? [];
|
dto.Genres = entity.Genres?.Split('|') ?? [];
|
||||||
dto.DateCreated = entity.DateCreated.GetValueOrDefault();
|
dto.DateCreated = entity.DateCreated.GetValueOrDefault();
|
||||||
dto.DateModified = entity.DateModified.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.DateLastRefreshed = entity.DateLastRefreshed.GetValueOrDefault();
|
||||||
dto.DateLastSaved = entity.DateLastSaved.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.Width = entity.Width.GetValueOrDefault();
|
||||||
dto.Height = entity.Height.GetValueOrDefault();
|
dto.Height = entity.Height.GetValueOrDefault();
|
||||||
if (entity.Provider is not null)
|
if (entity.Provider is not null)
|
||||||
@ -1720,21 +1716,29 @@ public sealed class BaseItemRepository(
|
|||||||
return query.Select(e => e.ItemValue.CleanValue).ToImmutableArray();
|
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;
|
return type.GetCustomAttribute<RequiresSourceSerialisationAttribute>() == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BaseItemDto DeserialiseBaseItem(BaseItemEntity baseItemEntity, bool skipDeserialization = false)
|
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.");
|
var type = GetType(baseItemEntity.Type) ?? throw new InvalidOperationException("Cannot deserialise unkown type.");
|
||||||
BaseItemDto? dto = null;
|
BaseItemDto? dto = null;
|
||||||
@ -1815,7 +1819,7 @@ public sealed class BaseItemRepository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = new QueryResult<(BaseItem, ItemCounts)>();
|
var result = new QueryResult<(BaseItemDto, ItemCounts)>();
|
||||||
if (filter.EnableTotalRecordCount)
|
if (filter.EnableTotalRecordCount)
|
||||||
{
|
{
|
||||||
result.TotalRecordCount = query.DistinctBy(e => e.PresentationUniqueKey).Count();
|
result.TotalRecordCount = query.DistinctBy(e => e.PresentationUniqueKey).Count();
|
||||||
@ -1877,7 +1881,7 @@ public sealed class BaseItemRepository(
|
|||||||
return value.RemoveDiacritics().ToLowerInvariant();
|
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)>();
|
var list = new List<(int, string)>();
|
||||||
|
|
||||||
@ -2144,6 +2148,18 @@ public sealed class BaseItemRepository(
|
|||||||
{
|
{
|
||||||
orderedQuery = query.OrderByDescending(expression);
|
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))
|
foreach (var item in orderBy.Skip(1))
|
||||||
|
1603
Jellyfin.Server.Implementations/Migrations/20241112152323_FixAncestorIdConfig.Designer.cs
generated
Normal file
1603
Jellyfin.Server.Implementations/Migrations/20241112152323_FixAncestorIdConfig.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Jellyfin.Server.Implementations.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class FixAncestorIdConfig : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_AncestorIds_BaseItems_BaseItemEntityId",
|
||||||
|
table: "AncestorIds");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_AncestorIds_BaseItemEntityId",
|
||||||
|
table: "AncestorIds");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "BaseItemEntityId",
|
||||||
|
table: "AncestorIds");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<Guid>(
|
||||||
|
name: "BaseItemEntityId",
|
||||||
|
table: "AncestorIds",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AncestorIds_BaseItemEntityId",
|
||||||
|
table: "AncestorIds",
|
||||||
|
column: "BaseItemEntityId");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_AncestorIds_BaseItems_BaseItemEntityId",
|
||||||
|
table: "AncestorIds",
|
||||||
|
column: "BaseItemEntityId",
|
||||||
|
principalTable: "BaseItems",
|
||||||
|
principalColumn: "Id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -98,13 +98,8 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
b.Property<Guid>("ParentItemId")
|
b.Property<Guid>("ParentItemId")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<Guid?>("BaseItemEntityId")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.HasKey("ItemId", "ParentItemId");
|
b.HasKey("ItemId", "ParentItemId");
|
||||||
|
|
||||||
b.HasIndex("BaseItemEntityId");
|
|
||||||
|
|
||||||
b.HasIndex("ParentItemId");
|
b.HasIndex("ParentItemId");
|
||||||
|
|
||||||
b.ToTable("AncestorIds");
|
b.ToTable("AncestorIds");
|
||||||
@ -1332,18 +1327,14 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.AncestorId", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.AncestorId", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", null)
|
|
||||||
.WithMany("AncestorIds")
|
|
||||||
.HasForeignKey("BaseItemEntityId");
|
|
||||||
|
|
||||||
b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "Item")
|
b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "Item")
|
||||||
.WithMany()
|
.WithMany("Children")
|
||||||
.HasForeignKey("ItemId")
|
.HasForeignKey("ItemId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "ParentItem")
|
b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "ParentItem")
|
||||||
.WithMany()
|
.WithMany("ParentAncestors")
|
||||||
.HasForeignKey("ParentItemId")
|
.HasForeignKey("ParentItemId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
@ -1551,10 +1542,10 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.BaseItemEntity", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.BaseItemEntity", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("AncestorIds");
|
|
||||||
|
|
||||||
b.Navigation("Chapters");
|
b.Navigation("Chapters");
|
||||||
|
|
||||||
|
b.Navigation("Children");
|
||||||
|
|
||||||
b.Navigation("Images");
|
b.Navigation("Images");
|
||||||
|
|
||||||
b.Navigation("ItemValues");
|
b.Navigation("ItemValues");
|
||||||
@ -1563,6 +1554,8 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
b.Navigation("MediaStreams");
|
b.Navigation("MediaStreams");
|
||||||
|
|
||||||
|
b.Navigation("ParentAncestors");
|
||||||
|
|
||||||
b.Navigation("Peoples");
|
b.Navigation("Peoples");
|
||||||
|
|
||||||
b.Navigation("Provider");
|
b.Navigation("Provider");
|
||||||
|
@ -15,7 +15,7 @@ public class AncestorIdConfiguration : IEntityTypeConfiguration<AncestorId>
|
|||||||
{
|
{
|
||||||
builder.HasKey(e => new { e.ItemId, e.ParentItemId });
|
builder.HasKey(e => new { e.ItemId, e.ParentItemId });
|
||||||
builder.HasIndex(e => e.ParentItemId);
|
builder.HasIndex(e => e.ParentItemId);
|
||||||
builder.HasOne(e => e.ParentItem);
|
builder.HasOne(e => e.ParentItem).WithMany(e => e.ParentAncestors).HasForeignKey(f => f.ParentItemId);
|
||||||
builder.HasOne(e => e.Item);
|
builder.HasOne(e => e.Item).WithMany(e => e.Children).HasForeignKey(f => f.ItemId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,8 @@ public class BaseItemConfiguration : IEntityTypeConfiguration<BaseItemEntity>
|
|||||||
builder.HasMany(e => e.MediaStreams);
|
builder.HasMany(e => e.MediaStreams);
|
||||||
builder.HasMany(e => e.Chapters);
|
builder.HasMany(e => e.Chapters);
|
||||||
builder.HasMany(e => e.Provider);
|
builder.HasMany(e => e.Provider);
|
||||||
builder.HasMany(e => e.AncestorIds);
|
builder.HasMany(e => e.ParentAncestors);
|
||||||
|
builder.HasMany(e => e.Children);
|
||||||
builder.HasMany(e => e.LockedFields);
|
builder.HasMany(e => e.LockedFields);
|
||||||
builder.HasMany(e => e.TrailerTypes);
|
builder.HasMany(e => e.TrailerTypes);
|
||||||
builder.HasMany(e => e.Images);
|
builder.HasMany(e => e.Images);
|
||||||
|
@ -11,6 +11,7 @@ using Emby.Server.Implementations.Data;
|
|||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
using Jellyfin.Extensions;
|
using Jellyfin.Extensions;
|
||||||
using Jellyfin.Server.Implementations;
|
using Jellyfin.Server.Implementations;
|
||||||
|
using Jellyfin.Server.Implementations.Item;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
@ -79,7 +80,14 @@ public class MigrateLibraryDb : IMigrationRoutine
|
|||||||
stopwatch.Restart();
|
stopwatch.Restart();
|
||||||
|
|
||||||
_logger.LogInformation("Start moving TypedBaseItem.");
|
_logger.LogInformation("Start moving TypedBaseItem.");
|
||||||
var typedBaseItemsQuery = "SELECT guid, type, data, StartDate, EndDate, ChannelId, IsMovie, IsSeries, EpisodeTitle, IsRepeat, CommunityRating, CustomRating, IndexNumber, IsLocked, PreferredMetadataLanguage, PreferredMetadataCountryCode, Width, Height, DateLastRefreshed, Name, Path, PremiereDate, Overview, ParentIndexNumber, ProductionYear, OfficialRating, ForcedSortName, RunTimeTicks, Size, DateCreated, DateModified, Genres, ParentId, TopParentId, Audio, ExternalServiceId, IsInMixedFolder, DateLastSaved, LockedFields, Studios, Tags, TrailerTypes, OriginalTitle, PrimaryVersionId, DateLastMediaAdded, Album, LUFS, NormalizationGain, CriticRating, IsVirtualItem, SeriesName, UserDataKey, SeasonName, SeasonId, SeriesId, PresentationUniqueKey, InheritedParentalRatingValue, ExternalSeriesId, Tagline, ProviderIds, Images, ProductionLocations, ExtraIds, TotalBitrate, ExtraType, Artists, AlbumArtists, ExternalId, SeriesPresentationUniqueKey, ShowId, OwnerId FROM TypedBaseItems";
|
var typedBaseItemsQuery = "SELECT guid, type, data, StartDate, EndDate, ChannelId, IsMovie, " +
|
||||||
|
"IsSeries, EpisodeTitle, IsRepeat, CommunityRating, CustomRating, IndexNumber, IsLocked, PreferredMetadataLanguage, " +
|
||||||
|
"PreferredMetadataCountryCode, Width, Height, DateLastRefreshed, Name, Path, PremiereDate, Overview, ParentIndexNumber, " +
|
||||||
|
"ProductionYear, OfficialRating, ForcedSortName, RunTimeTicks, Size, DateCreated, DateModified, Genres, ParentId, TopParentId, " +
|
||||||
|
"Audio, ExternalServiceId, IsInMixedFolder, DateLastSaved, LockedFields, Studios, Tags, TrailerTypes, OriginalTitle, PrimaryVersionId, " +
|
||||||
|
"DateLastMediaAdded, Album, LUFS, NormalizationGain, CriticRating, IsVirtualItem, SeriesName, UserDataKey, SeasonName, SeasonId, SeriesId, " +
|
||||||
|
"PresentationUniqueKey, InheritedParentalRatingValue, ExternalSeriesId, Tagline, ProviderIds, Images, ProductionLocations, ExtraIds, TotalBitrate, " +
|
||||||
|
"ExtraType, Artists, AlbumArtists, ExternalId, SeriesPresentationUniqueKey, ShowId, OwnerId, MediaType FROM TypedBaseItems";
|
||||||
dbContext.BaseItems.ExecuteDelete();
|
dbContext.BaseItems.ExecuteDelete();
|
||||||
|
|
||||||
var legacyBaseItemWithUserKeys = new Dictionary<string, BaseItemEntity>();
|
var legacyBaseItemWithUserKeys = new Dictionary<string, BaseItemEntity>();
|
||||||
@ -87,7 +95,10 @@ public class MigrateLibraryDb : IMigrationRoutine
|
|||||||
{
|
{
|
||||||
var baseItem = GetItem(dto);
|
var baseItem = GetItem(dto);
|
||||||
dbContext.BaseItems.Add(baseItem.BaseItem);
|
dbContext.BaseItems.Add(baseItem.BaseItem);
|
||||||
legacyBaseItemWithUserKeys[baseItem.LegacyUserDataKey] = baseItem.BaseItem;
|
foreach (var dataKey in baseItem.LegacyUserDataKey)
|
||||||
|
{
|
||||||
|
legacyBaseItemWithUserKeys[dataKey] = baseItem.BaseItem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Try saving {0} BaseItem entries.", dbContext.BaseItems.Local.Count);
|
_logger.LogInformation("Try saving {0} BaseItem entries.", dbContext.BaseItems.Local.Count);
|
||||||
@ -636,7 +647,7 @@ public class MigrateLibraryDb : IMigrationRoutine
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
private (BaseItemEntity BaseItem, string LegacyUserDataKey) GetItem(SqliteDataReader reader)
|
private (BaseItemEntity BaseItem, string[] LegacyUserDataKey) GetItem(SqliteDataReader reader)
|
||||||
{
|
{
|
||||||
var entity = new BaseItemEntity()
|
var entity = new BaseItemEntity()
|
||||||
{
|
{
|
||||||
@ -905,8 +916,10 @@ public class MigrateLibraryDb : IMigrationRoutine
|
|||||||
entity.SeriesName = seriesName;
|
entity.SeriesName = seriesName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reader.TryGetString(index++, out var userDataKey))
|
var userDataKeys = new List<string>();
|
||||||
|
if (reader.TryGetString(index++, out var directUserDataKey))
|
||||||
{
|
{
|
||||||
|
userDataKeys.Add(directUserDataKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reader.TryGetString(index++, out var seasonName))
|
if (reader.TryGetString(index++, out var seasonName))
|
||||||
@ -1010,7 +1023,16 @@ public class MigrateLibraryDb : IMigrationRoutine
|
|||||||
entity.OwnerId = ownerId;
|
entity.OwnerId = ownerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (entity, userDataKey);
|
if (reader.TryGetString(index++, out var mediaType))
|
||||||
|
{
|
||||||
|
entity.MediaType = mediaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseItem = BaseItemRepository.DeserialiseBaseItem(entity, _logger, false);
|
||||||
|
var dataKeys = baseItem.GetUserDataKeys();
|
||||||
|
userDataKeys.AddRange(dataKeys);
|
||||||
|
|
||||||
|
return (entity, userDataKeys.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BaseItemImageInfo Map(Guid baseItemId, ItemImageInfo e)
|
private static BaseItemImageInfo Map(Guid baseItemId, ItemImageInfo e)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user