aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJPVenson <6794763+JPVenson@users.noreply.github.com>2024-10-09 15:20:42 +0000
committerJPVenson <6794763+JPVenson@users.noreply.github.com>2024-10-09 15:20:42 +0000
commit01d834f21abcb65d246b18762b79001929fe845b (patch)
treebc9f61cdb7805a9968f46ab2d7aa9e28bfe2200e
parentc2844bda3b7605257d7b2f8d146077cea6dd0b08 (diff)
Fixed (most) tests
-rw-r--r--Jellyfin.Data/Entities/BaseItemEntity.cs26
-rw-r--r--Jellyfin.Server.Implementations/Item/BaseItemRepository.cs134
-rw-r--r--Jellyfin.Server.Implementations/JellyfinDbContext.cs26
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20240907123425_UserDataInJfLib.Designer.cs775
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20240907123425_UserDataInJfLib.cs79
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20241009132112_BaseItemRefactor.Designer.cs (renamed from Jellyfin.Server.Implementations/Migrations/20241009112234_BaseItemRefactor.Designer.cs)41
-rw-r--r--Jellyfin.Server.Implementations/Migrations/20241009132112_BaseItemRefactor.cs (renamed from Jellyfin.Server.Implementations/Migrations/20241009112234_BaseItemRefactor.cs)139
-rw-r--r--Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs3
-rw-r--r--Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs39
-rw-r--r--Jellyfin.Server.Implementations/ModelConfiguration/BaseItemConfiguration.cs9
-rw-r--r--MediaBrowser.Controller/Providers/MetadataResult.cs2
-rw-r--r--tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs2
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Controllers/LibraryStructureControllerTests.cs19
13 files changed, 185 insertions, 1109 deletions
diff --git a/Jellyfin.Data/Entities/BaseItemEntity.cs b/Jellyfin.Data/Entities/BaseItemEntity.cs
index 5348c8746..dbe5a5372 100644
--- a/Jellyfin.Data/Entities/BaseItemEntity.cs
+++ b/Jellyfin.Data/Entities/BaseItemEntity.cs
@@ -156,28 +156,12 @@ public class BaseItemEntity
public Guid? ParentId { get; set; }
- public BaseItemEntity? Parent { get; set; }
-
- public ICollection<BaseItemEntity>? DirectChildren { get; set; }
-
public Guid? TopParentId { get; set; }
- public BaseItemEntity? TopParent { get; set; }
-
- public ICollection<BaseItemEntity>? AllChildren { get; set; }
-
public Guid? SeasonId { get; set; }
- public BaseItemEntity? Season { get; set; }
-
- public ICollection<BaseItemEntity>? SeasonEpisodes { get; set; }
-
public Guid? SeriesId { get; set; }
- public ICollection<BaseItemEntity>? SeriesEpisodes { get; set; }
-
- public BaseItemEntity? Series { get; set; }
-
public ICollection<People>? Peoples { get; set; }
public ICollection<UserData>? UserData { get; set; }
@@ -191,4 +175,14 @@ public class BaseItemEntity
public ICollection<BaseItemProvider>? Provider { get; set; }
public ICollection<AncestorId>? AncestorIds { get; set; }
+
+ // those are references to __LOCAL__ ids not DB ids ... TODO: Bring the whole folder structure into the DB
+ // public ICollection<BaseItemEntity>? SeriesEpisodes { get; set; }
+ // public BaseItemEntity? Series { get; set; }
+ // public BaseItemEntity? Season { get; set; }
+ // public BaseItemEntity? Parent { get; set; }
+ // public ICollection<BaseItemEntity>? DirectChildren { get; set; }
+ // public BaseItemEntity? TopParent { get; set; }
+ // public ICollection<BaseItemEntity>? AllChildren { get; set; }
+ // public ICollection<BaseItemEntity>? SeasonEpisodes { get; set; }
}
diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs
index d5a1be679..480d83eb1 100644
--- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs
+++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs
@@ -5,20 +5,16 @@ using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
-using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
-using System.Threading.Channels;
using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
@@ -26,6 +22,7 @@ using MediaBrowser.Model.Querying;
using Microsoft.EntityFrameworkCore;
using BaseItemDto = MediaBrowser.Controller.Entities.BaseItem;
using BaseItemEntity = Jellyfin.Data.Entities.BaseItemEntity;
+#pragma warning disable RS0030 // Do not use banned APIs
namespace Jellyfin.Server.Implementations.Item;
@@ -66,12 +63,12 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
using var context = dbProvider.CreateDbContext();
using var transaction = context.Database.BeginTransaction();
- context.Peoples.Where(e => e.ItemId.Equals(id)).ExecuteDelete();
- context.Chapters.Where(e => e.ItemId.Equals(id)).ExecuteDelete();
- context.MediaStreamInfos.Where(e => e.ItemId.Equals(id)).ExecuteDelete();
- context.AncestorIds.Where(e => e.ItemId.Equals(id)).ExecuteDelete();
- context.ItemValues.Where(e => e.ItemId.Equals(id)).ExecuteDelete();
- context.BaseItems.Where(e => e.Id.Equals(id)).ExecuteDelete();
+ context.Peoples.Where(e => e.ItemId == id).ExecuteDelete();
+ context.Chapters.Where(e => e.ItemId == id).ExecuteDelete();
+ context.MediaStreamInfos.Where(e => e.ItemId == id).ExecuteDelete();
+ context.AncestorIds.Where(e => e.ItemId == id).ExecuteDelete();
+ context.ItemValues.Where(e => e.ItemId == id).ExecuteDelete();
+ context.BaseItems.Where(e => e.Id == id).ExecuteDelete();
context.SaveChanges();
transaction.Commit();
}
@@ -113,8 +110,8 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
PrepareFilterQuery(filter);
using var context = dbProvider.CreateDbContext();
- var dbQuery = TranslateQuery(context.BaseItems.AsNoTracking(), context, filter)
- .DistinctBy(e => e.Id);
+ var dbQuery = TranslateQuery(context.BaseItems.AsNoTracking(), context, filter);
+ // .DistinctBy(e => e.Id);
var enableGroupByPresentationUniqueKey = EnableGroupByPresentationUniqueKey(filter);
if (enableGroupByPresentationUniqueKey && filter.GroupBySeriesPresentationUniqueKey)
@@ -266,8 +263,7 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
PrepareFilterQuery(filter);
using var context = dbProvider.CreateDbContext();
- var dbQuery = TranslateQuery(context.BaseItems, context, filter)
- .DistinctBy(e => e.Id);
+ var dbQuery = TranslateQuery(context.BaseItems, context, filter);
if (filter.Limit.HasValue || filter.StartIndex.HasValue)
{
var offset = filter.StartIndex ?? 0;
@@ -299,6 +295,7 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
return dbQuery.Count();
}
+#pragma warning disable CA1307 // Specify StringComparison for clarity
private IQueryable<BaseItemEntity> TranslateQuery(
IQueryable<BaseItemEntity> baseQuery,
JellyfinDbContext context,
@@ -419,7 +416,7 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
if (!string.IsNullOrEmpty(filter.SearchTerm))
{
- baseQuery = baseQuery.Where(e => e.CleanName!.Contains(filter.SearchTerm, StringComparison.InvariantCultureIgnoreCase) || (e.OriginalTitle != null && e.OriginalTitle.Contains(filter.SearchTerm, StringComparison.InvariantCultureIgnoreCase)));
+ baseQuery = baseQuery.Where(e => e.CleanName!.Contains(filter.SearchTerm) || (e.OriginalTitle != null && e.OriginalTitle.Contains(filter.SearchTerm)));
}
if (filter.IsFolder.HasValue)
@@ -474,18 +471,15 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
baseQuery = baseQuery.Where(e => includeTypeName.Contains(e.Type));
}
- if (filter.ChannelIds.Count == 1)
+ if (filter.ChannelIds.Count > 0)
{
- baseQuery = baseQuery.Where(e => e.ChannelId == filter.ChannelIds[0].ToString("N", CultureInfo.InvariantCulture));
- }
- else if (filter.ChannelIds.Count > 1)
- {
- baseQuery = baseQuery.Where(e => filter.ChannelIds.Select(f => f.ToString("N", CultureInfo.InvariantCulture)).Contains(e.ChannelId));
+ var channelIds = filter.ChannelIds.Select(e => e.ToString("N", CultureInfo.InvariantCulture)).ToArray();
+ baseQuery = baseQuery.Where(e => channelIds.Contains(e.ChannelId));
}
if (!filter.ParentId.IsEmpty())
{
- baseQuery = baseQuery.Where(e => e.ParentId.Equals(filter.ParentId));
+ baseQuery = baseQuery.Where(e => e.ParentId!.Value == filter.ParentId);
}
if (!string.IsNullOrWhiteSpace(filter.Path))
@@ -591,7 +585,8 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
if (filter.TrailerTypes.Length > 0)
{
- baseQuery = baseQuery.Where(e => filter.TrailerTypes.Any(f => e.TrailerTypes!.Contains(f.ToString(), StringComparison.OrdinalIgnoreCase)));
+ var trailerTypes = filter.TrailerTypes.Select(e => e.ToString()).ToArray();
+ baseQuery = baseQuery.Where(e => trailerTypes.Any(f => e.TrailerTypes!.Contains(f)));
}
if (filter.IsAiring.HasValue)
@@ -611,7 +606,7 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
baseQuery = baseQuery
.Where(e =>
context.Peoples.Where(w => context.BaseItems.Where(w => filter.PersonIds.Contains(w.Id)).Any(f => f.Name == w.Name))
- .Any(f => f.ItemId.Equals(e.Id)));
+ .Any(f => f.ItemId == e.Id));
}
if (!string.IsNullOrWhiteSpace(filter.Person))
@@ -649,12 +644,12 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
{
baseQuery = baseQuery.Where(e =>
e.CleanName == filter.NameContains
- || e.OriginalTitle!.Contains(filter.NameContains!, StringComparison.Ordinal));
+ || e.OriginalTitle!.Contains(filter.NameContains!));
}
if (!string.IsNullOrWhiteSpace(filter.NameStartsWith))
{
- baseQuery = baseQuery.Where(e => e.SortName!.Contains(filter.NameStartsWith, StringComparison.OrdinalIgnoreCase));
+ baseQuery = baseQuery.Where(e => e.SortName!.Contains(filter.NameStartsWith));
}
if (!string.IsNullOrWhiteSpace(filter.NameStartsWithOrGreater))
@@ -671,31 +666,32 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
if (filter.ImageTypes.Length > 0)
{
- baseQuery = baseQuery.Where(e => filter.ImageTypes.Any(f => e.Images!.Contains(f.ToString(), StringComparison.InvariantCulture)));
+ var imgTypes = filter.ImageTypes.Select(e => e.ToString()).ToArray();
+ baseQuery = baseQuery.Where(e => imgTypes.Any(f => e.Images!.Contains(f)));
}
if (filter.IsLiked.HasValue)
{
baseQuery = baseQuery
- .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(filter.User!.Id) && f.Key == e.UserDataKey)!.Rating >= UserItemData.MinLikeValue);
+ .Where(e => e.UserData!.FirstOrDefault(f => f.UserId == filter.User!.Id && f.Key == e.UserDataKey)!.Rating >= UserItemData.MinLikeValue);
}
if (filter.IsFavoriteOrLiked.HasValue)
{
baseQuery = baseQuery
- .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(filter.User!.Id) && f.Key == e.UserDataKey)!.IsFavorite == filter.IsFavoriteOrLiked);
+ .Where(e => e.UserData!.FirstOrDefault(f => f.UserId == filter.User!.Id && f.Key == e.UserDataKey)!.IsFavorite == filter.IsFavoriteOrLiked);
}
if (filter.IsFavorite.HasValue)
{
baseQuery = baseQuery
- .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(filter.User!.Id) && f.Key == e.UserDataKey)!.IsFavorite == filter.IsFavorite);
+ .Where(e => e.UserData!.FirstOrDefault(f => f.UserId == filter.User!.Id && f.Key == e.UserDataKey)!.IsFavorite == filter.IsFavorite);
}
if (filter.IsPlayed.HasValue)
{
baseQuery = baseQuery
- .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(filter.User!.Id) && f.Key == e.UserDataKey)!.Played == filter.IsPlayed.Value);
+ .Where(e => e.UserData!.FirstOrDefault(f => f.UserId == filter.User!.Id && f.Key == e.UserDataKey)!.Played == filter.IsPlayed.Value);
}
if (filter.IsResumable.HasValue)
@@ -703,12 +699,12 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
if (filter.IsResumable.Value)
{
baseQuery = baseQuery
- .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(filter.User!.Id) && f.Key == e.UserDataKey)!.PlaybackPositionTicks > 0);
+ .Where(e => e.UserData!.FirstOrDefault(f => f.UserId == filter.User!.Id && f.Key == e.UserDataKey)!.PlaybackPositionTicks > 0);
}
else
{
baseQuery = baseQuery
- .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(filter.User!.Id) && f.Key == e.UserDataKey)!.PlaybackPositionTicks == 0);
+ .Where(e => e.UserData!.FirstOrDefault(f => f.UserId == filter.User!.Id && f.Key == e.UserDataKey)!.PlaybackPositionTicks == 0);
}
}
@@ -925,7 +921,7 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
if (filter.HasDeadParentId.HasValue && filter.HasDeadParentId.Value)
{
baseQuery = baseQuery
- .Where(e => e.ParentId.HasValue && context.BaseItems.Any(f => f.Id.Equals(e.ParentId.Value)));
+ .Where(e => e.ParentId.HasValue && context.BaseItems.Any(f => f.Id == e.ParentId.Value));
}
if (filter.IsDeadArtist.HasValue && filter.IsDeadArtist.Value)
@@ -1048,11 +1044,11 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
var enableItemsByName = (filter.IncludeItemsByName ?? false) && includedItemByNameTypes.Count > 0;
if (enableItemsByName && includedItemByNameTypes.Count > 0)
{
- baseQuery = baseQuery.Where(e => includedItemByNameTypes.Contains(e.Type) || queryTopParentIds.Any(w => w.Equals(e.TopParentId!.Value)));
+ baseQuery = baseQuery.Where(e => includedItemByNameTypes.Contains(e.Type) || queryTopParentIds.Any(w => w == e.TopParentId!.Value));
}
else
{
- baseQuery = baseQuery.Where(e => queryTopParentIds.Any(w => w.Equals(e.TopParentId!.Value)));
+ baseQuery = baseQuery.Where(e => queryTopParentIds.Any(w => w == e.TopParentId!.Value));
}
}
@@ -1064,7 +1060,7 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
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.Equals(f.Id))));
+ .Where(e => context.BaseItems.Where(f => f.PresentationUniqueKey == filter.AncestorWithPresentationUniqueKey).Any(f => f.AncestorIds!.Any(w => w.ItemId == f.Id)));
}
if (!string.IsNullOrWhiteSpace(filter.SeriesPresentationUniqueKey))
@@ -1090,7 +1086,7 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
.Where(e => e.ItemValues!.Where(e => e.Type == 6)
.Any(f => filter.IncludeInheritedTags.Contains(f.CleanValue))
||
- (e.ParentId.HasValue && context.ItemValues.Where(w => w.ItemId.Equals(e.ParentId.Value))!.Where(e => e.Type == 6)
+ (e.ParentId.HasValue && context.ItemValues.Where(w => w.ItemId == e.ParentId.Value)!.Where(e => e.Type == 6)
.Any(f => filter.IncludeInheritedTags.Contains(f.CleanValue))));
}
@@ -1112,21 +1108,23 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
if (filter.SeriesStatuses.Length > 0)
{
+ var seriesStatus = filter.SeriesStatuses.Select(e => e.ToString()).ToArray();
baseQuery = baseQuery
- .Where(e => filter.SeriesStatuses.Any(f => e.Data!.Contains(f.ToString(), StringComparison.InvariantCultureIgnoreCase)));
+ .Where(e => seriesStatus.Any(f => e.Data!.Contains(f)));
}
if (filter.BoxSetLibraryFolders.Length > 0)
{
+ var boxsetFolders = filter.BoxSetLibraryFolders.Select(e => e.ToString("N", CultureInfo.InvariantCulture)).ToArray();
baseQuery = baseQuery
- .Where(e => filter.BoxSetLibraryFolders.Any(f => e.Data!.Contains(f.ToString("N", CultureInfo.InvariantCulture), StringComparison.InvariantCultureIgnoreCase)));
+ .Where(e => boxsetFolders.Any(f => e.Data!.Contains(f)));
}
if (filter.VideoTypes.Length > 0)
{
var videoTypeBs = filter.VideoTypes.Select(e => $"\"VideoType\":\"" + e + "\"");
baseQuery = baseQuery
- .Where(e => videoTypeBs.Any(f => e.Data!.Contains(f, StringComparison.InvariantCultureIgnoreCase)));
+ .Where(e => videoTypeBs.Any(f => e.Data!.Contains(f)));
}
if (filter.Is3D.HasValue)
@@ -1134,12 +1132,12 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
if (filter.Is3D.Value)
{
baseQuery = baseQuery
- .Where(e => e.Data!.Contains("Video3DFormat", StringComparison.InvariantCultureIgnoreCase));
+ .Where(e => e.Data!.Contains("Video3DFormat"));
}
else
{
baseQuery = baseQuery
- .Where(e => !e.Data!.Contains("Video3DFormat", StringComparison.InvariantCultureIgnoreCase));
+ .Where(e => !e.Data!.Contains("Video3DFormat"));
}
}
@@ -1148,12 +1146,12 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
if (filter.IsPlaceHolder.Value)
{
baseQuery = baseQuery
- .Where(e => e.Data!.Contains("IsPlaceHolder\":true", StringComparison.InvariantCultureIgnoreCase));
+ .Where(e => e.Data!.Contains("IsPlaceHolder\":true"));
}
else
{
baseQuery = baseQuery
- .Where(e => !e.Data!.Contains("IsPlaceHolder\":true", StringComparison.InvariantCultureIgnoreCase));
+ .Where(e => !e.Data!.Contains("IsPlaceHolder\":true"));
}
}
@@ -1212,7 +1210,7 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
using var db = dbProvider.CreateDbContext();
db.BaseItems
- .Where(e => e.Id.Equals(item.Id))
+ .Where(e => e.Id == item.Id)
.ExecuteUpdate(e => e.SetProperty(f => f.Images, images));
}
@@ -1246,11 +1244,20 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
}
using var context = dbProvider.CreateDbContext();
+ using var transaction = context.Database.BeginTransaction();
foreach (var item in tuples)
{
var entity = Map(item.Item);
- context.BaseItems.Add(entity);
+ if (!context.BaseItems.Any(e => e.Id == entity.Id))
+ {
+ context.BaseItems.Add(entity);
+ }
+ else
+ {
+ context.BaseItems.Attach(entity).State = EntityState.Modified;
+ }
+ context.AncestorIds.Where(e => e.ItemId == entity.Id).ExecuteDelete();
if (item.Item.SupportsAncestors && item.AncestorIds != null)
{
foreach (var ancestorId in item.AncestorIds)
@@ -1260,13 +1267,13 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
Item = entity,
AncestorIdText = ancestorId.ToString(),
Id = ancestorId,
- ItemId = entity.Id
+ ItemId = Guid.Empty
});
}
}
var itemValues = GetItemValuesToSave(item.Item, item.InheritedTags);
- context.ItemValues.Where(e => e.ItemId.Equals(entity.Id)).ExecuteDelete();
+ context.ItemValues.Where(e => e.ItemId == entity.Id).ExecuteDelete();
foreach (var itemValue in itemValues)
{
context.ItemValues.Add(new()
@@ -1275,12 +1282,13 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
Type = itemValue.MagicNumber,
Value = itemValue.Value,
CleanValue = GetCleanValue(itemValue.Value),
- ItemId = entity.Id
+ ItemId = Guid.Empty
});
}
}
- context.SaveChanges(true);
+ context.SaveChanges();
+ transaction.Commit();
}
/// <inheritdoc cref="IItemRepository" />
@@ -1292,7 +1300,7 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
}
using var context = dbProvider.CreateDbContext();
- var item = context.BaseItems.FirstOrDefault(e => e.Id.Equals(id));
+ var item = context.BaseItems.FirstOrDefault(e => e.Id == id);
if (item is null)
{
return null;
@@ -1380,7 +1388,7 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
dto.Audio = Enum.Parse<ProgramAudio>(entity.Audio);
}
- dto.ExtraIds = entity.ExtraIds?.Split('|').Select(e => Guid.Parse(e)).ToArray();
+ dto.ExtraIds = string.IsNullOrWhiteSpace(entity.ExtraIds) ? null : entity.ExtraIds.Split('|').Select(e => Guid.Parse(e)).ToArray();
dto.ProductionLocations = entity.ProductionLocations?.Split('|');
dto.Studios = entity.Studios?.Split('|');
dto.Tags = entity.Tags?.Split('|');
@@ -1535,8 +1543,8 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
entity.Audio = dto.Audio?.ToString();
entity.ExtraType = dto.ExtraType?.ToString();
- entity.ExtraIds = string.Join('|', dto.ExtraIds);
- entity.ProductionLocations = string.Join('|', dto.ProductionLocations);
+ entity.ExtraIds = dto.ExtraIds is not null ? string.Join('|', dto.ExtraIds) : null;
+ entity.ProductionLocations = dto.ProductionLocations is not null ? string.Join('|', dto.ProductionLocations) : null;
entity.Studios = dto.Studios is not null ? string.Join('|', dto.Studios) : null;
entity.Tags = dto.Tags is not null ? string.Join('|', dto.Tags) : null;
entity.LockedFields = dto.LockedFields is not null ? string.Join('|', dto.LockedFields) : null;
@@ -1628,15 +1636,15 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
.Where(e => itemValueTypes.Contains(e.Type));
if (withItemTypes.Count > 0)
{
- query = query.Where(e => context.BaseItems.Where(e => withItemTypes.Contains(e.Type)).Any(f => f.ItemValues!.Any(w => w.ItemId.Equals(e.ItemId))));
+ query = query.Where(e => context.BaseItems.Where(e => withItemTypes.Contains(e.Type)).Any(f => f.ItemValues!.Any(w => w.ItemId == e.ItemId)));
}
if (excludeItemTypes.Count > 0)
{
- query = query.Where(e => !context.BaseItems.Where(e => withItemTypes.Contains(e.Type)).Any(f => f.ItemValues!.Any(w => w.ItemId.Equals(e.ItemId))));
+ query = query.Where(e => !context.BaseItems.Where(e => withItemTypes.Contains(e.Type)).Any(f => f.ItemValues!.Any(w => w.ItemId == e.ItemId)));
}
- query = query.DistinctBy(e => e.CleanValue);
+ // query = query.DistinctBy(e => e.CleanValue);
return query.Select(e => e.CleanValue).ToImmutableArray();
}
@@ -2131,12 +2139,12 @@ public sealed class BaseItemRepository(IDbContextFactory<JellyfinDbContext> dbPr
ItemSortBy.AirTime => e => e.SortName, // TODO
ItemSortBy.Runtime => e => e.RunTimeTicks,
ItemSortBy.Random => e => EF.Functions.Random(),
- ItemSortBy.DatePlayed => e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id) && f.Key == e.UserDataKey)!.LastPlayedDate,
- ItemSortBy.PlayCount => e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id) && f.Key == e.UserDataKey)!.PlayCount,
- ItemSortBy.IsFavoriteOrLiked => e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id) && f.Key == e.UserDataKey)!.IsFavorite,
+ ItemSortBy.DatePlayed => e => e.UserData!.FirstOrDefault(f => f.UserId == query.User!.Id && f.Key == e.UserDataKey)!.LastPlayedDate,
+ ItemSortBy.PlayCount => e => e.UserData!.FirstOrDefault(f => f.UserId == query.User!.Id && f.Key == e.UserDataKey)!.PlayCount,
+ ItemSortBy.IsFavoriteOrLiked => e => e.UserData!.FirstOrDefault(f => f.UserId == query.User!.Id && f.Key == e.UserDataKey)!.IsFavorite,
ItemSortBy.IsFolder => e => e.IsFolder,
- ItemSortBy.IsPlayed => e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id) && f.Key == e.UserDataKey)!.Played,
- ItemSortBy.IsUnplayed => e => !e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id) && f.Key == e.UserDataKey)!.Played,
+ ItemSortBy.IsPlayed => e => e.UserData!.FirstOrDefault(f => f.UserId == query.User!.Id && f.Key == e.UserDataKey)!.Played,
+ ItemSortBy.IsUnplayed => e => !e.UserData!.FirstOrDefault(f => f.UserId == query.User!.Id && f.Key == e.UserDataKey)!.Played,
ItemSortBy.DateLastContentAdded => e => e.DateLastMediaAdded,
ItemSortBy.Artist => e => e.ItemValues!.Where(f => f.Type == 0).Select(f => f.CleanValue),
ItemSortBy.AlbumArtist => e => e.ItemValues!.Where(f => f.Type == 1).Select(f => f.CleanValue),
diff --git a/Jellyfin.Server.Implementations/JellyfinDbContext.cs b/Jellyfin.Server.Implementations/JellyfinDbContext.cs
index c1d6d58cd..a9eda1b64 100644
--- a/Jellyfin.Server.Implementations/JellyfinDbContext.cs
+++ b/Jellyfin.Server.Implementations/JellyfinDbContext.cs
@@ -4,21 +4,19 @@ using Jellyfin.Data.Entities;
using Jellyfin.Data.Entities.Security;
using Jellyfin.Data.Interfaces;
using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
namespace Jellyfin.Server.Implementations;
/// <inheritdoc/>
-public class JellyfinDbContext : DbContext
+/// <summary>
+/// Initializes a new instance of the <see cref="JellyfinDbContext"/> class.
+/// </summary>
+/// <param name="options">The database context options.</param>
+/// <param name="logger">Logger.</param>
+public class JellyfinDbContext(DbContextOptions<JellyfinDbContext> options, ILogger<JellyfinDbContext> logger) : DbContext(options)
{
/// <summary>
- /// Initializes a new instance of the <see cref="JellyfinDbContext"/> class.
- /// </summary>
- /// <param name="options">The database context options.</param>
- public JellyfinDbContext(DbContextOptions<JellyfinDbContext> options) : base(options)
- {
- }
-
- /// <summary>
/// Gets the <see cref="DbSet{TEntity}"/> containing the access schedules.
/// </summary>
public DbSet<AccessSchedule> AccessSchedules => Set<AccessSchedule>();
@@ -228,7 +226,15 @@ public class JellyfinDbContext : DbContext
saveEntity.OnSavingChanges();
}
- return base.SaveChanges();
+ try
+ {
+ return base.SaveChanges();
+ }
+ catch (Exception e)
+ {
+ logger.LogError(e, "Error trying to save changes.");
+ throw;
+ }
}
/// <inheritdoc />
diff --git a/Jellyfin.Server.Implementations/Migrations/20240907123425_UserDataInJfLib.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20240907123425_UserDataInJfLib.Designer.cs
deleted file mode 100644
index 609faa1e6..000000000
--- a/Jellyfin.Server.Implementations/Migrations/20240907123425_UserDataInJfLib.Designer.cs
+++ /dev/null
@@ -1,775 +0,0 @@
-// <auto-generated />
-using System;
-using Jellyfin.Server.Implementations;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-
-#nullable disable
-
-namespace Jellyfin.Server.Implementations.Migrations
-{
- [DbContext(typeof(JellyfinDbContext))]
- [Migration("20240907123425_UserDataInJfLib")]
- partial class UserDataInJfLib
- {
- /// <inheritdoc />
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder.HasAnnotation("ProductVersion", "8.0.8");
-
- modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
- {
- b.Property<int>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property<int>("DayOfWeek")
- .HasColumnType("INTEGER");
-
- b.Property<double>("EndHour")
- .HasColumnType("REAL");
-
- b.Property<double>("StartHour")
- .HasColumnType("REAL");
-
- b.Property<Guid>("UserId")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId");
-
- b.ToTable("AccessSchedules");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b =>
- {
- b.Property<int>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("DateCreated")
- .HasColumnType("TEXT");
-
- b.Property<string>("ItemId")
- .HasMaxLength(256)
- .HasColumnType("TEXT");
-
- b.Property<int>("LogSeverity")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Name")
- .IsRequired()
- .HasMaxLength(512)
- .HasColumnType("TEXT");
-
- b.Property<string>("Overview")
- .HasMaxLength(512)
- .HasColumnType("TEXT");
-
- b.Property<uint>("RowVersion")
- .IsConcurrencyToken()
- .HasColumnType("INTEGER");
-
- b.Property<string>("ShortOverview")
- .HasMaxLength(512)
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("TEXT");
-
- b.Property<Guid>("UserId")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("DateCreated");
-
- b.ToTable("ActivityLogs");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemDisplayPreferences", b =>
- {
- b.Property<int>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property<string>("Client")
- .IsRequired()
- .HasMaxLength(32)
- .HasColumnType("TEXT");
-
- b.Property<Guid>("ItemId")
- .HasColumnType("TEXT");
-
- b.Property<string>("Key")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property<Guid>("UserId")
- .HasColumnType("TEXT");
-
- b.Property<string>("Value")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId", "ItemId", "Client", "Key")
- .IsUnique();
-
- b.ToTable("CustomItemDisplayPreferences");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
- {
- b.Property<int>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property<int>("ChromecastVersion")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Client")
- .IsRequired()
- .HasMaxLength(32)
- .HasColumnType("TEXT");
-
- b.Property<string>("DashboardTheme")
- .HasMaxLength(32)
- .HasColumnType("TEXT");
-
- b.Property<bool>("EnableNextVideoInfoOverlay")
- .HasColumnType("INTEGER");
-
- b.Property<int?>("IndexBy")
- .HasColumnType("INTEGER");
-
- b.Property<Guid>("ItemId")
- .HasColumnType("TEXT");
-
- b.Property<int>("ScrollDirection")
- .HasColumnType("INTEGER");
-
- b.Property<bool>("ShowBackdrop")
- .HasColumnType("INTEGER");
-
- b.Property<bool>("ShowSidebar")
- .HasColumnType("INTEGER");
-
- b.Property<int>("SkipBackwardLength")
- .HasColumnType("INTEGER");
-
- b.Property<int>("SkipForwardLength")
- .HasColumnType("INTEGER");
-
- b.Property<string>("TvHome")
- .HasMaxLength(32)
- .HasColumnType("TEXT");
-
- b.Property<Guid>("UserId")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId", "ItemId", "Client")
- .IsUnique();
-
- b.ToTable("DisplayPreferences");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b =>
- {
- b.Property<int>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property<int>("DisplayPreferencesId")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Order")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Type")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("DisplayPreferencesId");
-
- b.ToTable("HomeSection");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b =>
- {
- b.Property<int>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("LastModified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Path")
- .IsRequired()
- .HasMaxLength(512)
- .HasColumnType("TEXT");
-
- b.Property<Guid?>("UserId")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("ImageInfos");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b =>
- {
- b.Property<int>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property<string>("Client")
- .IsRequired()
- .HasMaxLength(32)
- .HasColumnType("TEXT");
-
- b.Property<int?>("IndexBy")
- .HasColumnType("INTEGER");
-
- b.Property<Guid>("ItemId")
- .HasColumnType("TEXT");
-
- b.Property<bool>("RememberIndexing")
- .HasColumnType("INTEGER");
-
- b.Property<bool>("RememberSorting")
- .HasColumnType("INTEGER");
-
- b.Property<string>("SortBy")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("TEXT");
-
- b.Property<int>("SortOrder")
- .HasColumnType("INTEGER");
-
- b.Property<Guid>("UserId")
- .HasColumnType("TEXT");
-
- b.Property<int>("ViewType")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId");
-
- b.ToTable("ItemDisplayPreferences");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.MediaSegment", b =>
- {
- b.Property<Guid>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("TEXT");
-
- b.Property<long>("EndTicks")
- .HasColumnType("INTEGER");
-
- b.Property<Guid>("ItemId")
- .HasColumnType("TEXT");
-
- b.Property<string>("SegmentProviderId")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property<long>("StartTicks")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Type")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.ToTable("MediaSegments");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
- {
- b.Property<int>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property<int>("Kind")
- .HasColumnType("INTEGER");
-
- b.Property<Guid?>("Permission_Permissions_Guid")
- .HasColumnType("TEXT");
-
- b.Property<uint>("RowVersion")
- .IsConcurrencyToken()
- .HasColumnType("INTEGER");
-
- b.Property<Guid?>("UserId")
- .HasColumnType("TEXT");
-
- b.Property<bool>("Value")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId", "Kind")
- .IsUnique()
- .HasFilter("[UserId] IS NOT NULL");
-
- b.ToTable("Permissions");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
- {
- b.Property<int>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property<int>("Kind")
- .HasColumnType("INTEGER");
-
- b.Property<Guid?>("Preference_Preferences_Guid")
- .HasColumnType("TEXT");
-
- b.Property<uint>("RowVersion")
- .IsConcurrencyToken()
- .HasColumnType("INTEGER");
-
- b.Property<Guid?>("UserId")
- .HasColumnType("TEXT");
-
- b.Property<string>("Value")
- .IsRequired()
- .HasMaxLength(65535)
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId", "Kind")
- .IsUnique()
- .HasFilter("[UserId] IS NOT NULL");
-
- b.ToTable("Preferences");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.Security.ApiKey", b =>
- {
- b.Property<int>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property<string>("AccessToken")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("DateCreated")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("DateLastActivity")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("AccessToken")
- .IsUnique();
-
- b.ToTable("ApiKeys");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.Security.Device", b =>
- {
- b.Property<int>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property<string>("AccessToken")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property<string>("AppName")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("TEXT");
-
- b.Property<string>("AppVersion")
- .IsRequired()
- .HasMaxLength(32)
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("DateCreated")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("DateLastActivity")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("DateModified")
- .HasColumnType("TEXT");
-
- b.Property<string>("DeviceId")
- .IsRequired()
- .HasMaxLength(256)
- .HasColumnType("TEXT");
-
- b.Property<string>("DeviceName")
- .IsRequired()
- .HasMaxLength(64)
- .HasColumnType("TEXT");
-
- b.Property<bool>("IsActive")
- .HasColumnType("INTEGER");
-
- b.Property<Guid>("UserId")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("DeviceId");
-
- b.HasIndex("AccessToken", "DateLastActivity");
-
- b.HasIndex("DeviceId", "DateLastActivity");
-
- b.HasIndex("UserId", "DeviceId");
-
- b.ToTable("Devices");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.Security.DeviceOptions", b =>
- {
- b.Property<int>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property<string>("CustomName")
- .HasColumnType("TEXT");
-
- b.Property<string>("DeviceId")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("DeviceId")
- .IsUnique();
-
- b.ToTable("DeviceOptions");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.TrickplayInfo", b =>
- {
- b.Property<Guid>("ItemId")
- .HasColumnType("TEXT");
-
- b.Property<int>("Width")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Bandwidth")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Height")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Interval")
- .HasColumnType("INTEGER");
-
- b.Property<int>("ThumbnailCount")
- .HasColumnType("INTEGER");
-
- b.Property<int>("TileHeight")
- .HasColumnType("INTEGER");
-
- b.Property<int>("TileWidth")
- .HasColumnType("INTEGER");
-
- b.HasKey("ItemId", "Width");
-
- b.ToTable("TrickplayInfos");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
- {
- b.Property<Guid>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("TEXT");
-
- b.Property<string>("AudioLanguagePreference")
- .HasMaxLength(255)
- .HasColumnType("TEXT");
-
- b.Property<string>("AuthenticationProviderId")
- .IsRequired()
- .HasMaxLength(255)
- .HasColumnType("TEXT");
-
- b.Property<string>("CastReceiverId")
- .HasMaxLength(32)
- .HasColumnType("TEXT");
-
- b.Property<bool>("DisplayCollectionsView")
- .HasColumnType("INTEGER");
-
- b.Property<bool>("DisplayMissingEpisodes")
- .HasColumnType("INTEGER");
-
- b.Property<bool>("EnableAutoLogin")
- .HasColumnType("INTEGER");
-
- b.Property<bool>("EnableLocalPassword")
- .HasColumnType("INTEGER");
-
- b.Property<bool>("EnableNextEpisodeAutoPlay")
- .HasColumnType("INTEGER");
-
- b.Property<bool>("EnableUserPreferenceAccess")
- .HasColumnType("INTEGER");
-
- b.Property<bool>("HidePlayedInLatest")
- .HasColumnType("INTEGER");
-
- b.Property<long>("InternalId")
- .HasColumnType("INTEGER");
-
- b.Property<int>("InvalidLoginAttemptCount")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime?>("LastActivityDate")
- .HasColumnType("TEXT");
-
- b.Property<DateTime?>("LastLoginDate")
- .HasColumnType("TEXT");
-
- b.Property<int?>("LoginAttemptsBeforeLockout")
- .HasColumnType("INTEGER");
-
- b.Property<int>("MaxActiveSessions")
- .HasColumnType("INTEGER");
-
- b.Property<int?>("MaxParentalAgeRating")
- .HasColumnType("INTEGER");
-
- b.Property<bool>("MustUpdatePassword")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Password")
- .HasMaxLength(65535)
- .HasColumnType("TEXT");
-
- b.Property<string>("PasswordResetProviderId")
- .IsRequired()
- .HasMaxLength(255)
- .HasColumnType("TEXT");
-
- b.Property<bool>("PlayDefaultAudioTrack")
- .HasColumnType("INTEGER");
-
- b.Property<bool>("RememberAudioSelections")
- .HasColumnType("INTEGER");
-
- b.Property<bool>("RememberSubtitleSelections")
- .HasColumnType("INTEGER");
-
- b.Property<int?>("RemoteClientBitrateLimit")
- .HasColumnType("INTEGER");
-
- b.Property<uint>("RowVersion")
- .IsConcurrencyToken()
- .HasColumnType("INTEGER");
-
- b.Property<string>("SubtitleLanguagePreference")
- .HasMaxLength(255)
- .HasColumnType("TEXT");
-
- b.Property<int>("SubtitleMode")
- .HasColumnType("INTEGER");
-
- b.Property<int>("SyncPlayAccess")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasMaxLength(255)
- .HasColumnType("TEXT")
- .UseCollation("NOCASE");
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("Users");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.UserData", b =>
- {
- b.Property<int?>("AudioStreamIndex")
- .HasColumnType("INTEGER");
-
- b.Property<bool>("IsFavorite")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Key")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property<DateTime?>("LastPlayedDate")
- .HasColumnType("TEXT");
-
- b.Property<bool?>("Likes")
- .HasColumnType("INTEGER");
-
- b.Property<int>("PlayCount")
- .HasColumnType("INTEGER");
-
- b.Property<long>("PlaybackPositionTicks")
- .HasColumnType("INTEGER");
-
- b.Property<bool>("Played")
- .HasColumnType("INTEGER");
-
- b.Property<double?>("Rating")
- .HasColumnType("REAL");
-
- b.Property<int?>("SubtitleStreamIndex")
- .HasColumnType("INTEGER");
-
- b.Property<Guid>("UserId")
- .HasColumnType("TEXT");
-
- b.HasIndex("UserId");
-
- b.HasIndex("Key", "UserId")
- .IsUnique();
-
- b.HasIndex("Key", "UserId", "IsFavorite");
-
- b.HasIndex("Key", "UserId", "LastPlayedDate");
-
- b.HasIndex("Key", "UserId", "PlaybackPositionTicks");
-
- b.HasIndex("Key", "UserId", "Played");
-
- b.ToTable("UserData");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
- {
- b.HasOne("Jellyfin.Data.Entities.User", null)
- .WithMany("AccessSchedules")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
- {
- b.HasOne("Jellyfin.Data.Entities.User", null)
- .WithMany("DisplayPreferences")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b =>
- {
- b.HasOne("Jellyfin.Data.Entities.DisplayPreferences", null)
- .WithMany("HomeSections")
- .HasForeignKey("DisplayPreferencesId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b =>
- {
- b.HasOne("Jellyfin.Data.Entities.User", null)
- .WithOne("ProfileImage")
- .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId")
- .OnDelete(DeleteBehavior.Cascade);
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b =>
- {
- b.HasOne("Jellyfin.Data.Entities.User", null)
- .WithMany("ItemDisplayPreferences")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
- {
- b.HasOne("Jellyfin.Data.Entities.User", null)
- .WithMany("Permissions")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade);
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
- {
- b.HasOne("Jellyfin.Data.Entities.User", null)
- .WithMany("Preferences")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade);
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.Security.Device", b =>
- {
- b.HasOne("Jellyfin.Data.Entities.User", "User")
- .WithMany()
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("User");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.UserData", b =>
- {
- b.HasOne("Jellyfin.Data.Entities.User", "User")
- .WithMany()
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("User");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
- {
- b.Navigation("HomeSections");
- });
-
- modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
- {
- b.Navigation("AccessSchedules");
-
- b.Navigation("DisplayPreferences");
-
- b.Navigation("ItemDisplayPreferences");
-
- b.Navigation("Permissions");
-
- b.Navigation("Preferences");
-
- b.Navigation("ProfileImage");
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Jellyfin.Server.Implementations/Migrations/20240907123425_UserDataInJfLib.cs b/Jellyfin.Server.Implementations/Migrations/20240907123425_UserDataInJfLib.cs
deleted file mode 100644
index cb9a01f5b..000000000
--- a/Jellyfin.Server.Implementations/Migrations/20240907123425_UserDataInJfLib.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-#nullable disable
-
-namespace Jellyfin.Server.Implementations.Migrations
-{
- /// <inheritdoc />
- public partial class UserDataInJfLib : Migration
- {
- /// <inheritdoc />
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "UserData",
- columns: table => new
- {
- Key = table.Column<string>(type: "TEXT", nullable: false),
- Rating = table.Column<double>(type: "REAL", nullable: true),
- PlaybackPositionTicks = table.Column<long>(type: "INTEGER", nullable: false),
- PlayCount = table.Column<int>(type: "INTEGER", nullable: false),
- IsFavorite = table.Column<bool>(type: "INTEGER", nullable: false),
- LastPlayedDate = table.Column<DateTime>(type: "TEXT", nullable: true),
- Played = table.Column<bool>(type: "INTEGER", nullable: false),
- AudioStreamIndex = table.Column<int>(type: "INTEGER", nullable: true),
- SubtitleStreamIndex = table.Column<int>(type: "INTEGER", nullable: true),
- Likes = table.Column<bool>(type: "INTEGER", nullable: true),
- UserId = table.Column<Guid>(type: "TEXT", nullable: false)
- },
- constraints: table =>
- {
- table.ForeignKey(
- name: "FK_UserData_Users_UserId",
- column: x => x.UserId,
- principalTable: "Users",
- principalColumn: "Id",
- onDelete: ReferentialAction.Cascade);
- });
-
- migrationBuilder.CreateIndex(
- name: "IX_UserData_Key_UserId",
- table: "UserData",
- columns: new[] { "Key", "UserId" },
- unique: true);
-
- migrationBuilder.CreateIndex(
- name: "IX_UserData_Key_UserId_IsFavorite",
- table: "UserData",
- columns: new[] { "Key", "UserId", "IsFavorite" });
-
- migrationBuilder.CreateIndex(
- name: "IX_UserData_Key_UserId_LastPlayedDate",
- table: "UserData",
- columns: new[] { "Key", "UserId", "LastPlayedDate" });
-
- migrationBuilder.CreateIndex(
- name: "IX_UserData_Key_UserId_PlaybackPositionTicks",
- table: "UserData",
- columns: new[] { "Key", "UserId", "PlaybackPositionTicks" });
-
- migrationBuilder.CreateIndex(
- name: "IX_UserData_Key_UserId_Played",
- table: "UserData",
- columns: new[] { "Key", "UserId", "Played" });
-
- migrationBuilder.CreateIndex(
- name: "IX_UserData_UserId",
- table: "UserData",
- column: "UserId");
- }
-
- /// <inheritdoc />
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "UserData");
- }
- }
-}
diff --git a/Jellyfin.Server.Implementations/Migrations/20241009112234_BaseItemRefactor.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20241009132112_BaseItemRefactor.Designer.cs
index b3e028298..8e8e6c125 100644
--- a/Jellyfin.Server.Implementations/Migrations/20241009112234_BaseItemRefactor.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20241009132112_BaseItemRefactor.Designer.cs
@@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Jellyfin.Server.Implementations.Migrations
{
[DbContext(typeof(JellyfinDbContext))]
- [Migration("20241009112234_BaseItemRefactor")]
+ [Migration("20241009132112_BaseItemRefactor")]
partial class BaseItemRefactor
{
/// <inheritdoc />
@@ -379,10 +379,6 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasIndex("PresentationUniqueKey");
- b.HasIndex("SeasonId");
-
- b.HasIndex("SeriesId");
-
b.HasIndex("TopParentId", "Id");
b.HasIndex("UserDataKey", "Type");
@@ -1275,33 +1271,6 @@ namespace Jellyfin.Server.Implementations.Migrations
b.Navigation("Item");
});
- modelBuilder.Entity("Jellyfin.Data.Entities.BaseItemEntity", b =>
- {
- b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "Parent")
- .WithMany("DirectChildren")
- .HasForeignKey("ParentId");
-
- b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "Season")
- .WithMany("SeasonEpisodes")
- .HasForeignKey("SeasonId");
-
- b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "Series")
- .WithMany("SeriesEpisodes")
- .HasForeignKey("SeriesId");
-
- b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "TopParent")
- .WithMany("AllChildren")
- .HasForeignKey("TopParentId");
-
- b.Navigation("Parent");
-
- b.Navigation("Season");
-
- b.Navigation("Series");
-
- b.Navigation("TopParent");
- });
-
modelBuilder.Entity("Jellyfin.Data.Entities.BaseItemProvider", b =>
{
b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "Item")
@@ -1436,14 +1405,10 @@ namespace Jellyfin.Server.Implementations.Migrations
modelBuilder.Entity("Jellyfin.Data.Entities.BaseItemEntity", b =>
{
- b.Navigation("AllChildren");
-
b.Navigation("AncestorIds");
b.Navigation("Chapters");
- b.Navigation("DirectChildren");
-
b.Navigation("ItemValues");
b.Navigation("MediaStreams");
@@ -1452,10 +1417,6 @@ namespace Jellyfin.Server.Implementations.Migrations
b.Navigation("Provider");
- b.Navigation("SeasonEpisodes");
-
- b.Navigation("SeriesEpisodes");
-
b.Navigation("UserData");
});
diff --git a/Jellyfin.Server.Implementations/Migrations/20241009112234_BaseItemRefactor.cs b/Jellyfin.Server.Implementations/Migrations/20241009132112_BaseItemRefactor.cs
index f51e385e0..caa731c15 100644
--- a/Jellyfin.Server.Implementations/Migrations/20241009112234_BaseItemRefactor.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20241009132112_BaseItemRefactor.cs
@@ -11,21 +11,6 @@ namespace Jellyfin.Server.Implementations.Migrations
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
- migrationBuilder.DropIndex(
- name: "IX_UserData_Key_UserId",
- table: "UserData");
-
- migrationBuilder.AddColumn<Guid>(
- name: "BaseItemEntityId",
- table: "UserData",
- type: "TEXT",
- nullable: true);
-
- migrationBuilder.AddPrimaryKey(
- name: "PK_UserData",
- table: "UserData",
- columns: new[] { "Key", "UserId" });
-
migrationBuilder.CreateTable(
name: "BaseItems",
columns: table => new
@@ -109,26 +94,6 @@ namespace Jellyfin.Server.Implementations.Migrations
constraints: table =>
{
table.PrimaryKey("PK_BaseItems", x => x.Id);
- table.ForeignKey(
- name: "FK_BaseItems_BaseItems_ParentId",
- column: x => x.ParentId,
- principalTable: "BaseItems",
- principalColumn: "Id");
- table.ForeignKey(
- name: "FK_BaseItems_BaseItems_SeasonId",
- column: x => x.SeasonId,
- principalTable: "BaseItems",
- principalColumn: "Id");
- table.ForeignKey(
- name: "FK_BaseItems_BaseItems_SeriesId",
- column: x => x.SeriesId,
- principalTable: "BaseItems",
- principalColumn: "Id");
- table.ForeignKey(
- name: "FK_BaseItems_BaseItems_TopParentId",
- column: x => x.TopParentId,
- principalTable: "BaseItems",
- principalColumn: "Id");
});
migrationBuilder.CreateTable(
@@ -318,10 +283,38 @@ namespace Jellyfin.Server.Implementations.Migrations
onDelete: ReferentialAction.Cascade);
});
- migrationBuilder.CreateIndex(
- name: "IX_UserData_BaseItemEntityId",
- table: "UserData",
- column: "BaseItemEntityId");
+ migrationBuilder.CreateTable(
+ name: "UserData",
+ columns: table => new
+ {
+ Key = table.Column<string>(type: "TEXT", nullable: false),
+ UserId = table.Column<Guid>(type: "TEXT", nullable: false),
+ Rating = table.Column<double>(type: "REAL", nullable: true),
+ PlaybackPositionTicks = table.Column<long>(type: "INTEGER", nullable: false),
+ PlayCount = table.Column<int>(type: "INTEGER", nullable: false),
+ IsFavorite = table.Column<bool>(type: "INTEGER", nullable: false),
+ LastPlayedDate = table.Column<DateTime>(type: "TEXT", nullable: true),
+ Played = table.Column<bool>(type: "INTEGER", nullable: false),
+ AudioStreamIndex = table.Column<int>(type: "INTEGER", nullable: true),
+ SubtitleStreamIndex = table.Column<int>(type: "INTEGER", nullable: true),
+ Likes = table.Column<bool>(type: "INTEGER", nullable: true),
+ BaseItemEntityId = table.Column<Guid>(type: "TEXT", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_UserData", x => new { x.Key, x.UserId });
+ table.ForeignKey(
+ name: "FK_UserData_BaseItems_BaseItemEntityId",
+ column: x => x.BaseItemEntityId,
+ principalTable: "BaseItems",
+ principalColumn: "Id");
+ table.ForeignKey(
+ name: "FK_UserData_Users_UserId",
+ column: x => x.UserId,
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
migrationBuilder.CreateIndex(
name: "IX_AncestorIds_Id",
@@ -369,16 +362,6 @@ namespace Jellyfin.Server.Implementations.Migrations
column: "PresentationUniqueKey");
migrationBuilder.CreateIndex(
- name: "IX_BaseItems_SeasonId",
- table: "BaseItems",
- column: "SeasonId");
-
- migrationBuilder.CreateIndex(
- name: "IX_BaseItems_SeriesId",
- table: "BaseItems",
- column: "SeriesId");
-
- migrationBuilder.CreateIndex(
name: "IX_BaseItems_TopParentId_Id",
table: "BaseItems",
columns: new[] { "TopParentId", "Id" });
@@ -453,21 +436,40 @@ namespace Jellyfin.Server.Implementations.Migrations
table: "Peoples",
column: "Name");
- migrationBuilder.AddForeignKey(
- name: "FK_UserData_BaseItems_BaseItemEntityId",
+ migrationBuilder.CreateIndex(
+ name: "IX_UserData_BaseItemEntityId",
+ table: "UserData",
+ column: "BaseItemEntityId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_UserData_Key_UserId_IsFavorite",
+ table: "UserData",
+ columns: new[] { "Key", "UserId", "IsFavorite" });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_UserData_Key_UserId_LastPlayedDate",
+ table: "UserData",
+ columns: new[] { "Key", "UserId", "LastPlayedDate" });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_UserData_Key_UserId_PlaybackPositionTicks",
table: "UserData",
- column: "BaseItemEntityId",
- principalTable: "BaseItems",
- principalColumn: "Id");
+ columns: new[] { "Key", "UserId", "PlaybackPositionTicks" });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_UserData_Key_UserId_Played",
+ table: "UserData",
+ columns: new[] { "Key", "UserId", "Played" });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_UserData_UserId",
+ table: "UserData",
+ column: "UserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
- migrationBuilder.DropForeignKey(
- name: "FK_UserData_BaseItems_BaseItemEntityId",
- table: "UserData");
-
migrationBuilder.DropTable(
name: "AncestorIds");
@@ -490,25 +492,10 @@ namespace Jellyfin.Server.Implementations.Migrations
name: "Peoples");
migrationBuilder.DropTable(
- name: "BaseItems");
+ name: "UserData");
- migrationBuilder.DropPrimaryKey(
- name: "PK_UserData",
- table: "UserData");
-
- migrationBuilder.DropIndex(
- name: "IX_UserData_BaseItemEntityId",
- table: "UserData");
-
- migrationBuilder.DropColumn(
- name: "BaseItemEntityId",
- table: "UserData");
-
- migrationBuilder.CreateIndex(
- name: "IX_UserData_Key_UserId",
- table: "UserData",
- columns: new[] { "Key", "UserId" },
- unique: true);
+ migrationBuilder.DropTable(
+ name: "BaseItems");
}
}
}
diff --git a/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs b/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs
index 940cf7c5d..500c4a1c7 100644
--- a/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs
+++ b/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs
@@ -1,5 +1,6 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
+using Microsoft.Extensions.Logging.Abstractions;
namespace Jellyfin.Server.Implementations.Migrations
{
@@ -14,7 +15,7 @@ namespace Jellyfin.Server.Implementations.Migrations
var optionsBuilder = new DbContextOptionsBuilder<JellyfinDbContext>();
optionsBuilder.UseSqlite("Data Source=jellyfin.db");
- return new JellyfinDbContext(optionsBuilder.Options);
+ return new JellyfinDbContext(optionsBuilder.Options, NullLogger<JellyfinDbContext>.Instance);
}
}
}
diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
index f74f7d791..dd280489b 100644
--- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
+++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
@@ -376,10 +376,6 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasIndex("PresentationUniqueKey");
- b.HasIndex("SeasonId");
-
- b.HasIndex("SeriesId");
-
b.HasIndex("TopParentId", "Id");
b.HasIndex("UserDataKey", "Type");
@@ -1272,33 +1268,6 @@ namespace Jellyfin.Server.Implementations.Migrations
b.Navigation("Item");
});
- modelBuilder.Entity("Jellyfin.Data.Entities.BaseItemEntity", b =>
- {
- b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "Parent")
- .WithMany("DirectChildren")
- .HasForeignKey("ParentId");
-
- b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "Season")
- .WithMany("SeasonEpisodes")
- .HasForeignKey("SeasonId");
-
- b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "Series")
- .WithMany("SeriesEpisodes")
- .HasForeignKey("SeriesId");
-
- b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "TopParent")
- .WithMany("AllChildren")
- .HasForeignKey("TopParentId");
-
- b.Navigation("Parent");
-
- b.Navigation("Season");
-
- b.Navigation("Series");
-
- b.Navigation("TopParent");
- });
-
modelBuilder.Entity("Jellyfin.Data.Entities.BaseItemProvider", b =>
{
b.HasOne("Jellyfin.Data.Entities.BaseItemEntity", "Item")
@@ -1433,14 +1402,10 @@ namespace Jellyfin.Server.Implementations.Migrations
modelBuilder.Entity("Jellyfin.Data.Entities.BaseItemEntity", b =>
{
- b.Navigation("AllChildren");
-
b.Navigation("AncestorIds");
b.Navigation("Chapters");
- b.Navigation("DirectChildren");
-
b.Navigation("ItemValues");
b.Navigation("MediaStreams");
@@ -1449,10 +1414,6 @@ namespace Jellyfin.Server.Implementations.Migrations
b.Navigation("Provider");
- b.Navigation("SeasonEpisodes");
-
- b.Navigation("SeriesEpisodes");
-
b.Navigation("UserData");
});
diff --git a/Jellyfin.Server.Implementations/ModelConfiguration/BaseItemConfiguration.cs b/Jellyfin.Server.Implementations/ModelConfiguration/BaseItemConfiguration.cs
index d74b94784..6c36a1591 100644
--- a/Jellyfin.Server.Implementations/ModelConfiguration/BaseItemConfiguration.cs
+++ b/Jellyfin.Server.Implementations/ModelConfiguration/BaseItemConfiguration.cs
@@ -15,10 +15,11 @@ public class BaseItemConfiguration : IEntityTypeConfiguration<BaseItemEntity>
public void Configure(EntityTypeBuilder<BaseItemEntity> builder)
{
builder.HasKey(e => e.Id);
- builder.HasOne(e => e.Parent).WithMany(e => e.DirectChildren).HasForeignKey(e => e.ParentId);
- builder.HasOne(e => e.TopParent).WithMany(e => e.AllChildren).HasForeignKey(e => e.TopParentId);
- builder.HasOne(e => e.Season).WithMany(e => e.SeasonEpisodes).HasForeignKey(e => e.SeasonId);
- builder.HasOne(e => e.Series).WithMany(e => e.SeriesEpisodes).HasForeignKey(e => e.SeriesId);
+ // TODO: See rant in entity file.
+ // builder.HasOne(e => e.Parent).WithMany(e => e.DirectChildren).HasForeignKey(e => e.ParentId);
+ // builder.HasOne(e => e.TopParent).WithMany(e => e.AllChildren).HasForeignKey(e => e.TopParentId);
+ // builder.HasOne(e => e.Season).WithMany(e => e.SeasonEpisodes).HasForeignKey(e => e.SeasonId);
+ // builder.HasOne(e => e.Series).WithMany(e => e.SeriesEpisodes).HasForeignKey(e => e.SeriesId);
builder.HasMany(e => e.Peoples);
builder.HasMany(e => e.UserData);
builder.HasMany(e => e.ItemValues);
diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs
index eabbe73cd..ef69885fc 100644
--- a/MediaBrowser.Controller/Providers/MetadataResult.cs
+++ b/MediaBrowser.Controller/Providers/MetadataResult.cs
@@ -36,7 +36,7 @@ namespace MediaBrowser.Controller.Providers
public IReadOnlyList<PersonInfo> People
{
get => _people;
- set => _people = value.ToList();
+ set => _people = value?.ToList();
}
public bool HasMetadata { get; set; }
diff --git a/tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs b/tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs
index cedcaf9c0..b32ecf6ec 100644
--- a/tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs
+++ b/tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs
@@ -330,7 +330,7 @@ namespace Jellyfin.Providers.Tests.Manager
MetadataService<Movie, MovieInfo>.MergeBaseItemData(source, target, lockedFields, replaceData, false);
actualValue = target.People;
- return newValue?.Equals(actualValue) ?? actualValue is null;
+ return newValue?.SequenceEqual((IEnumerable<PersonInfo>)actualValue!) ?? actualValue is null;
}
/// <summary>
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/LibraryStructureControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/LibraryStructureControllerTests.cs
index bf3bfdad4..02a77516f 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Controllers/LibraryStructureControllerTests.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/LibraryStructureControllerTests.cs
@@ -13,7 +13,7 @@ using Xunit.Priority;
namespace Jellyfin.Server.Integration.Tests.Controllers;
-[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
+// [TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
public sealed class LibraryStructureControllerTests : IClassFixture<JellyfinApplicationFactory>
{
private readonly JellyfinApplicationFactory _factory;
@@ -62,12 +62,23 @@ public sealed class LibraryStructureControllerTests : IClassFixture<JellyfinAppl
}
[Fact]
- [Priority(0)]
+ [Priority(-2)]
public async Task UpdateLibraryOptions_Valid_Success()
{
var client = _factory.CreateClient();
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client));
+ var createBody = new AddVirtualFolderDto()
+ {
+ LibraryOptions = new LibraryOptions()
+ {
+ Enabled = false
+ }
+ };
+
+ using var createResponse = await client.PostAsJsonAsync("Library/VirtualFolders?name=test&refreshLibrary=true", createBody, _jsonOptions);
+ Assert.Equal(HttpStatusCode.NoContent, createResponse.StatusCode);
+
using var response = await client.GetAsync("Library/VirtualFolders");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@@ -80,13 +91,13 @@ public sealed class LibraryStructureControllerTests : IClassFixture<JellyfinAppl
Assert.False(options.Enabled);
options.Enabled = true;
- var body = new UpdateLibraryOptionsDto()
+ var existBody = new UpdateLibraryOptionsDto()
{
Id = Guid.Parse(library.ItemId),
LibraryOptions = options
};
- using var response2 = await client.PostAsJsonAsync("Library/VirtualFolders/LibraryOptions", body, _jsonOptions);
+ using var response2 = await client.PostAsJsonAsync("Library/VirtualFolders/LibraryOptions", existBody, _jsonOptions);
Assert.Equal(HttpStatusCode.NoContent, response2.StatusCode);
}