mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-04-19 11:44:47 -04:00
Fixed (most) tests
This commit is contained in:
parent
c2844bda3b
commit
01d834f21a
13 changed files with 185 additions and 1109 deletions
|
@ -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; }
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -4,20 +4,18 @@ 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>
|
||||
|
@ -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 />
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
});
|
||||
|
|
@ -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",
|
||||
|
@ -368,16 +361,6 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||
table: "BaseItems",
|
||||
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",
|
||||
|
@ -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",
|
||||
principalTable: "BaseItems",
|
||||
principalColumn: "Id");
|
||||
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",
|
||||
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");
|
||||
|
||||
|
@ -489,26 +491,11 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||
migrationBuilder.DropTable(
|
||||
name: "Peoples");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "UserData");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "BaseItems");
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue