aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Server/Migrations/Routines/MigrateKeyframeData.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Server/Migrations/Routines/MigrateKeyframeData.cs')
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateKeyframeData.cs103
1 files changed, 48 insertions, 55 deletions
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateKeyframeData.cs b/Jellyfin.Server/Migrations/Routines/MigrateKeyframeData.cs
index b8e69db8e..aa5530926 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateKeyframeData.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateKeyframeData.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
@@ -10,10 +9,9 @@ using Jellyfin.Data.Enums;
using Jellyfin.Database.Implementations;
using Jellyfin.Database.Implementations.Entities;
using Jellyfin.Extensions.Json;
+using Jellyfin.Server.ServerSetupApp;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
@@ -22,10 +20,10 @@ namespace Jellyfin.Server.Migrations.Routines;
/// <summary>
/// Migration to move extracted files to the new directories.
/// </summary>
+[JellyfinMigration("2025-04-21T00:00:00", nameof(MigrateKeyframeData))]
public class MigrateKeyframeData : IDatabaseMigrationRoutine
{
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger<MoveTrickplayFiles> _logger;
+ private readonly IStartupLogger _logger;
private readonly IApplicationPaths _appPaths;
private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
private static readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
@@ -33,18 +31,15 @@ public class MigrateKeyframeData : IDatabaseMigrationRoutine
/// <summary>
/// Initializes a new instance of the <see cref="MigrateKeyframeData"/> class.
/// </summary>
- /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
- /// <param name="logger">The logger.</param>
+ /// <param name="startupLogger">The startup logger for Startup UI intigration.</param>
/// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
/// <param name="dbProvider">The EFCore db factory.</param>
public MigrateKeyframeData(
- ILibraryManager libraryManager,
- ILogger<MoveTrickplayFiles> logger,
+ IStartupLogger<MigrateKeyframeData> startupLogger,
IApplicationPaths appPaths,
IDbContextFactory<JellyfinDbContext> dbProvider)
{
- _libraryManager = libraryManager;
- _logger = logger;
+ _logger = startupLogger;
_appPaths = appPaths;
_dbProvider = dbProvider;
}
@@ -52,59 +47,41 @@ public class MigrateKeyframeData : IDatabaseMigrationRoutine
private string KeyframeCachePath => Path.Combine(_appPaths.DataPath, "keyframes");
/// <inheritdoc />
- public Guid Id => new("EA4bCAE1-09A4-428E-9B90-4B4FD2EA1B24");
-
- /// <inheritdoc />
- public string Name => "MigrateKeyframeData";
-
- /// <inheritdoc />
- public bool PerformOnNewInstall => false;
-
- /// <inheritdoc />
public void Perform()
{
- const int Limit = 100;
- int itemCount = 0, offset = 0, previousCount;
+ const int Limit = 5000;
+ int itemCount = 0, offset = 0;
var sw = Stopwatch.StartNew();
- var itemsQuery = new InternalItemsQuery
- {
- MediaTypes = [MediaType.Video],
- SourceTypes = [SourceType.Library],
- IsVirtualItem = false,
- IsFolder = false
- };
using var context = _dbProvider.CreateDbContext();
+ var baseQuery = context.BaseItems.Where(b => b.MediaType == MediaType.Video.ToString() && !b.IsVirtualItem && !b.IsFolder).OrderBy(e => e.Id);
+ var records = baseQuery.Count();
+ _logger.LogInformation("Checking {Count} items for importable keyframe data.", records);
+
context.KeyframeData.ExecuteDelete();
using var transaction = context.Database.BeginTransaction();
- List<KeyframeData> keyframes = [];
-
do
{
- var result = _libraryManager.GetItemsResult(itemsQuery);
- _logger.LogInformation("Importing keyframes for {Count} items", result.TotalRecordCount);
-
- var items = result.Items;
- previousCount = items.Count;
- offset += Limit;
- foreach (var item in items)
+ var results = baseQuery.Skip(offset).Take(Limit).Select(b => new Tuple<Guid, string?>(b.Id, b.Path)).ToList();
+ foreach (var result in results)
{
- if (TryGetKeyframeData(item, out var data))
+ if (TryGetKeyframeData(result.Item1, result.Item2, out var data))
{
- keyframes.Add(data);
+ itemCount++;
+ context.KeyframeData.Add(data);
}
+ }
- if (++itemCount % 10_000 == 0)
- {
- context.KeyframeData.AddRange(keyframes);
- keyframes.Clear();
- _logger.LogInformation("Imported keyframes for {Count} items in {Time}", itemCount, sw.Elapsed);
- }
+ offset += Limit;
+ if (offset > records)
+ {
+ offset = records;
}
- } while (previousCount == Limit);
- context.KeyframeData.AddRange(keyframes);
+ _logger.LogInformation("Checked: {Count} - Imported: {Items} - Time: {Time}", offset, itemCount, sw.Elapsed);
+ } while (offset < records);
+
context.SaveChanges();
transaction.Commit();
@@ -116,10 +93,9 @@ public class MigrateKeyframeData : IDatabaseMigrationRoutine
}
}
- private bool TryGetKeyframeData(BaseItem item, [NotNullWhen(true)] out KeyframeData? data)
+ private bool TryGetKeyframeData(Guid id, string? path, [NotNullWhen(true)] out KeyframeData? data)
{
data = null;
- var path = item.Path;
if (!string.IsNullOrEmpty(path))
{
var cachePath = GetCachePath(KeyframeCachePath, path);
@@ -127,7 +103,7 @@ public class MigrateKeyframeData : IDatabaseMigrationRoutine
{
data = new()
{
- ItemId = item.Id,
+ ItemId = id,
KeyframeTicks = keyframeData.KeyframeTicks.ToList(),
TotalDuration = keyframeData.TotalDuration
};
@@ -146,6 +122,16 @@ public class MigrateKeyframeData : IDatabaseMigrationRoutine
{
lastWriteTimeUtc = File.GetLastWriteTimeUtc(filePath);
}
+ catch (ArgumentOutOfRangeException e)
+ {
+ _logger.LogDebug("Skipping {Path}: {Exception}", filePath, e.Message);
+ return null;
+ }
+ catch (UnauthorizedAccessException e)
+ {
+ _logger.LogDebug("Skipping {Path}: {Exception}", filePath, e.Message);
+ return null;
+ }
catch (IOException e)
{
_logger.LogDebug("Skipping {Path}: {Exception}", filePath, e.Message);
@@ -159,14 +145,21 @@ public class MigrateKeyframeData : IDatabaseMigrationRoutine
return Path.Join(keyframeCachePath, prefix, filename);
}
- private static bool TryReadFromCache(string? cachePath, [NotNullWhen(true)] out MediaEncoding.Keyframes.KeyframeData? cachedResult)
+ private bool TryReadFromCache(string? cachePath, [NotNullWhen(true)] out MediaEncoding.Keyframes.KeyframeData? cachedResult)
{
if (File.Exists(cachePath))
{
- var bytes = File.ReadAllBytes(cachePath);
- cachedResult = JsonSerializer.Deserialize<MediaEncoding.Keyframes.KeyframeData>(bytes, _jsonOptions);
+ try
+ {
+ var bytes = File.ReadAllBytes(cachePath);
+ cachedResult = JsonSerializer.Deserialize<MediaEncoding.Keyframes.KeyframeData>(bytes, _jsonOptions);
- return cachedResult is not null;
+ return cachedResult is not null;
+ }
+ catch (JsonException jsonException)
+ {
+ _logger.LogWarning(jsonException, "Failed to read {Path}", cachePath);
+ }
}
cachedResult = null;