JPVenson 4 месяцев назад
Родитель
Сommit
b33810534b

+ 10 - 4
Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs

@@ -67,10 +67,16 @@ namespace Emby.Server.Implementations.Data
                 progress.Report(percent * 100);
             }
 
-            using var context = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
-            using var transaction = await context.Database.BeginTransactionAsync(cancellationToken).ConfigureAwait(false);
-            await context.ItemValues.Where(e => e.BaseItemsMap!.Count == 0).ExecuteDeleteAsync(cancellationToken).ConfigureAwait(false);
-            await transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
+            var context = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
+            await using (context.ConfigureAwait(false))
+            {
+                var transaction = await context.Database.BeginTransactionAsync(cancellationToken).ConfigureAwait(false);
+                await using (transaction.ConfigureAwait(false))
+                {
+                    await context.ItemValues.Where(e => e.BaseItemsMap!.Count == 0).ExecuteDeleteAsync(cancellationToken).ConfigureAwait(false);
+                    await transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
+                }
+            }
 
             progress.Report(100);
         }

+ 9 - 3
Emby.Server.Implementations/Library/UserDataManager.cs

@@ -146,8 +146,8 @@ namespace Emby.Server.Implementations.Library
             {
                 ItemId = itemId,
                 CustomDataKey = dto.Key,
-                Item = null!,
-                User = null!,
+                Item = null,
+                User = null,
                 AudioStreamIndex = dto.AudioStreamIndex,
                 IsFavorite = dto.IsFavorite,
                 LastPlayedDate = dto.LastPlayedDate,
@@ -181,7 +181,13 @@ namespace Emby.Server.Implementations.Library
         private UserItemData? GetUserData(User user, Guid itemId, List<string> keys)
         {
             var cacheKey = GetCacheKey(user.InternalId, itemId);
-            var data = GetUserDataInternal(user.Id, itemId, keys);
+
+            if (_userData.TryGetValue(cacheKey, out var data))
+            {
+                return data;
+            }
+
+            data = GetUserDataInternal(user.Id, itemId, keys);
 
             if (data is null)
             {

+ 10 - 9
Jellyfin.Server.Implementations/Item/BaseItemRepository.cs

@@ -125,7 +125,7 @@ public sealed class BaseItemRepository
         transaction.Commit();
     }
 
-    /// <inheritdoc cref="IItemRepository"/>
+    /// <inheritdoc />
     public IReadOnlyList<Guid> GetItemIdsList(InternalItemsQuery filter)
     {
         ArgumentNullException.ThrowIfNull(filter);
@@ -201,7 +201,7 @@ public sealed class BaseItemRepository
             _itemTypeLookup.MusicGenreTypes);
     }
 
-    /// <inheritdoc cref="IItemRepository"/>
+    /// <inheritdoc />
     public QueryResult<BaseItemDto> GetItems(InternalItemsQuery filter)
     {
         ArgumentNullException.ThrowIfNull(filter);
@@ -235,7 +235,7 @@ public sealed class BaseItemRepository
         return result;
     }
 
-    /// <inheritdoc cref="IItemRepository"/>
+    /// <inheritdoc />
     public IReadOnlyList<BaseItemDto> GetItemList(InternalItemsQuery filter)
     {
         ArgumentNullException.ThrowIfNull(filter);
@@ -354,12 +354,14 @@ public sealed class BaseItemRepository
     {
         ArgumentException.ThrowIfNullOrEmpty(typeName);
 
+        // TODO: this isn't great. Refactor later to be both globally handled by a dedicated service not just an static variable and be loaded eagar.
+        // currently this is done so that plugins may introduce their own type of baseitems as we dont know when we are first called, before or after plugins are loaded
         return _typeMap.GetOrAdd(typeName, k => AppDomain.CurrentDomain.GetAssemblies()
             .Select(a => a.GetType(k))
             .FirstOrDefault(t => t is not null));
     }
 
-    /// <inheritdoc cref="IItemRepository" />
+    /// <inheritdoc  />
     public void SaveImages(BaseItemDto item)
     {
         ArgumentNullException.ThrowIfNull(item);
@@ -373,13 +375,13 @@ public sealed class BaseItemRepository
         transaction.Commit();
     }
 
-    /// <inheritdoc cref="IItemRepository" />
+    /// <inheritdoc  />
     public void SaveItems(IReadOnlyList<BaseItemDto> items, CancellationToken cancellationToken)
     {
         UpdateOrInsertItems(items, cancellationToken);
     }
 
-    /// <inheritdoc cref="IItemRepository" />
+    /// <inheritdoc />
     public void UpdateOrInsertItems(IReadOnlyList<BaseItemDto> items, CancellationToken cancellationToken)
     {
         ArgumentNullException.ThrowIfNull(items);
@@ -479,7 +481,7 @@ public sealed class BaseItemRepository
         transaction.Commit();
     }
 
-    /// <inheritdoc cref="IItemRepository" />
+    /// <inheritdoc  />
     public BaseItemDto? RetrieveItem(Guid id)
     {
         if (id.IsEmpty())
@@ -890,8 +892,7 @@ public sealed class BaseItemRepository
         {
             try
             {
-                using var dataAsStream = new MemoryStream(Encoding.UTF8.GetBytes(baseItemEntity.Data!));
-                dto = JsonSerializer.Deserialize(dataAsStream, type, JsonDefaults.Options) as BaseItemDto;
+                dto = JsonSerializer.Deserialize(baseItemEntity.Data, type, JsonDefaults.Options) as BaseItemDto;
             }
             catch (JsonException ex)
             {

+ 1 - 1
Jellyfin.Server.Implementations/Item/ChapterRepository.cs

@@ -71,7 +71,7 @@ public class ChapterRepository : IChapterRepository
                 chapter = e,
                 baseItemPath = e.Item.Path
             })
-            .ToList()
+            .AsEnumerable()
             .Select(e => Map(e.chapter, e.baseItemPath!))
             .ToArray();
     }

+ 8 - 7
Jellyfin.Server/Migrations/Routines/MigrateLibraryDb.cs

@@ -1,3 +1,5 @@
+#pragma warning disable RS0030 // Do not use banned APIs
+
 using System;
 using System.Collections.Generic;
 using System.Collections.Immutable;
@@ -21,7 +23,6 @@ using Microsoft.Extensions.Logging;
 using Chapter = Jellyfin.Data.Entities.Chapter;
 
 namespace Jellyfin.Server.Migrations.Routines;
-#pragma warning disable RS0030 // Do not use banned APIs
 
 /// <summary>
 /// The migration routine for migrating the userdata database to EF Core.
@@ -80,7 +81,7 @@ public class MigrateLibraryDb : IMigrationRoutine
         stopwatch.Restart();
 
         _logger.LogInformation("Start moving TypedBaseItem.");
-        var typedBaseItemsQuery = """
+        const string typedBaseItemsQuery = """
          SELECT guid, type, data, StartDate, EndDate, ChannelId, IsMovie,
          IsSeries, EpisodeTitle, IsRepeat, CommunityRating, CustomRating, IndexNumber, IsLocked, PreferredMetadataLanguage,
          PreferredMetadataCountryCode, Width, Height, DateLastRefreshed, Name, Path, PremiereDate, Overview, ParentIndexNumber,
@@ -111,7 +112,7 @@ public class MigrateLibraryDb : IMigrationRoutine
 
         _logger.LogInformation("Start moving ItemValues.");
         // do not migrate inherited types as they are now properly mapped in search and lookup.
-        var itemValueQuery =
+        const string itemValueQuery =
         """
         SELECT ItemId, Type, Value, CleanValue FROM ItemValues
                     WHERE Type <> 6 AND EXISTS(SELECT 1 FROM TypedBaseItems WHERE TypedBaseItems.guid = ItemValues.ItemId)
@@ -187,7 +188,7 @@ public class MigrateLibraryDb : IMigrationRoutine
         dbContext.SaveChanges();
 
         _logger.LogInformation("Start moving MediaStreamInfos.");
-        var mediaStreamQuery = """
+        const string mediaStreamQuery = """
         SELECT ItemId, StreamIndex, StreamType, Codec, Language, ChannelLayout, Profile, AspectRatio, Path,
         IsInterlaced, BitRate, Channels, SampleRate, IsDefault, IsForced, IsExternal, Height, Width,
         AverageFrameRate, RealFrameRate, Level, PixelFormat, BitDepth, IsAnamorphic, RefFrames, CodecTag,
@@ -211,7 +212,7 @@ public class MigrateLibraryDb : IMigrationRoutine
         stopwatch.Restart();
 
         _logger.LogInformation("Start moving People.");
-        var personsQuery = """
+        const string personsQuery = """
         SELECT ItemId, Name, Role, PersonType, SortOrder FROM People
         WHERE EXISTS(SELECT 1 FROM TypedBaseItems WHERE TypedBaseItems.guid = People.ItemId)
         """;
@@ -268,7 +269,7 @@ public class MigrateLibraryDb : IMigrationRoutine
         stopwatch.Restart();
 
         _logger.LogInformation("Start moving Chapters.");
-        var chapterQuery = """
+        const string chapterQuery = """
         SELECT ItemId,StartPositionTicks,Name,ImagePath,ImageDateModified,ChapterIndex from Chapters2
         WHERE EXISTS(SELECT 1 FROM TypedBaseItems WHERE TypedBaseItems.guid = Chapters2.ItemId)
         """;
@@ -287,7 +288,7 @@ public class MigrateLibraryDb : IMigrationRoutine
         stopwatch.Restart();
 
         _logger.LogInformation("Start moving AncestorIds.");
-        var ancestorIdsQuery = """
+        const string ancestorIdsQuery = """
         SELECT ItemId, AncestorId, AncestorIdText FROM AncestorIds
         WHERE
         EXISTS(SELECT 1 FROM TypedBaseItems WHERE TypedBaseItems.guid = AncestorIds.ItemId)

+ 5 - 5
src/Jellyfin.Drawing/ImageProcessor.cs

@@ -4,6 +4,7 @@ using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Net.Mime;
+using System.Reflection.Metadata.Ecma335;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
@@ -410,11 +411,11 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
 
     /// <inheritdoc />
     public string GetImageCacheTag(BaseItem item, ItemImageInfo image)
-        => (item.Path + image.DateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture);
+        => GetImageCacheTag(item.Path, image.DateModified);
 
     /// <inheritdoc />
     public string GetImageCacheTag(BaseItemDto item, ItemImageInfo image)
-        => (item.Path + image.DateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture);
+        => GetImageCacheTag(item.Path, image.DateModified);
 
     /// <inheritdoc />
     public string? GetImageCacheTag(BaseItemDto item, ChapterInfo chapter)
@@ -424,7 +425,7 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
             return null;
         }
 
-        return (item.Path + chapter.ImageDateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture);
+        return GetImageCacheTag(item.Path, chapter.ImageDateModified);
     }
 
     /// <inheritdoc />
@@ -451,8 +452,7 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
             return null;
         }
 
-        return (user.ProfileImage.Path + user.ProfileImage.LastModified.Ticks).GetMD5()
-            .ToString("N", CultureInfo.InvariantCulture);
+        return GetImageCacheTag(user.ProfileImage.Path, user.ProfileImage.LastModified);
     }
 
     private Task<(string Path, DateTime DateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)

+ 1 - 1
tests/Jellyfin.Server.Integration.Tests/Controllers/LibraryStructureControllerTests.cs

@@ -45,7 +45,7 @@ public sealed class LibraryStructureControllerTests : IClassFixture<JellyfinAppl
     }
 
     [Fact]
-    [Priority(0)]
+    [Priority(-2)]
     public async Task UpdateLibraryOptions_Invalid_NotFound()
     {
         var client = _factory.CreateClient();