Tweak Trickplay migration for speed (#12643)

This commit is contained in:
Cody Robibero 2024-09-23 09:09:23 -06:00 committed by GitHub
parent 0539fdc5e3
commit 3c639c2e80
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 83 additions and 39 deletions

View file

@ -455,16 +455,18 @@ public class TrickplayManager : ITrickplayManager
}
/// <inheritdoc />
public async Task<IReadOnlyList<Guid>> GetTrickplayItemsAsync()
public async Task<IReadOnlyList<TrickplayInfo>> GetTrickplayItemsAsync(int limit, int offset)
{
List<Guid> trickplayItems;
IReadOnlyList<TrickplayInfo> trickplayItems;
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
await using (dbContext.ConfigureAwait(false))
{
trickplayItems = await dbContext.TrickplayInfos
.AsNoTracking()
.Select(i => i.ItemId)
.OrderBy(i => i.ItemId)
.Skip(offset)
.Take(limit)
.ToListAsync()
.ConfigureAwait(false);
}

View file

@ -1,10 +1,15 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using Jellyfin.Data.Enums;
using MediaBrowser.Common;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Trickplay;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Server.Migrations.Routines;
@ -16,6 +21,7 @@ public class MoveTrickplayFiles : IMigrationRoutine
private readonly ITrickplayManager _trickplayManager;
private readonly IFileSystem _fileSystem;
private readonly ILibraryManager _libraryManager;
private readonly ILogger<MoveTrickplayFiles> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="MoveTrickplayFiles"/> class.
@ -23,11 +29,13 @@ public class MoveTrickplayFiles : IMigrationRoutine
/// <param name="trickplayManager">Instance of the <see cref="ITrickplayManager"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
public MoveTrickplayFiles(ITrickplayManager trickplayManager, IFileSystem fileSystem, ILibraryManager libraryManager)
/// <param name="logger">The logger.</param>
public MoveTrickplayFiles(ITrickplayManager trickplayManager, IFileSystem fileSystem, ILibraryManager libraryManager, ILogger<MoveTrickplayFiles> logger)
{
_trickplayManager = trickplayManager;
_fileSystem = fileSystem;
_libraryManager = libraryManager;
_logger = logger;
}
/// <inheritdoc />
@ -42,26 +50,49 @@ public class MoveTrickplayFiles : IMigrationRoutine
/// <inheritdoc />
public void Perform()
{
var trickplayItems = _trickplayManager.GetTrickplayItemsAsync().GetAwaiter().GetResult();
foreach (var itemId in trickplayItems)
{
var resolutions = _trickplayManager.GetTrickplayResolutions(itemId).GetAwaiter().GetResult();
var item = _libraryManager.GetItemById(itemId);
if (item is null)
{
continue;
}
const int Limit = 100;
int itemCount = 0, offset = 0, previousCount;
foreach (var resolution in resolutions)
var sw = Stopwatch.StartNew();
var trickplayQuery = new InternalItemsQuery
{
MediaTypes = [MediaType.Video],
SourceTypes = [SourceType.Library],
IsVirtualItem = false,
IsFolder = false
};
do
{
var trickplayInfos = _trickplayManager.GetTrickplayItemsAsync(Limit, offset).GetAwaiter().GetResult();
previousCount = trickplayInfos.Count;
offset += Limit;
trickplayQuery.ItemIds = trickplayInfos.Select(i => i.ItemId).Distinct().ToArray();
var items = _libraryManager.GetItemList(trickplayQuery);
foreach (var trickplayInfo in trickplayInfos)
{
var oldPath = GetOldTrickplayDirectory(item, resolution.Key);
var newPath = _trickplayManager.GetTrickplayDirectory(item, resolution.Value.TileWidth, resolution.Value.TileHeight, resolution.Value.Width, false);
var item = items.OfType<Video>().FirstOrDefault(i => i.Id.Equals(trickplayInfo.ItemId));
if (item is null)
{
continue;
}
if (++itemCount % 1_000 == 0)
{
_logger.LogInformation("Moved {Count} items in {Time}", itemCount, sw.Elapsed);
}
var oldPath = GetOldTrickplayDirectory(item, trickplayInfo.Width);
var newPath = _trickplayManager.GetTrickplayDirectory(item, trickplayInfo.TileWidth, trickplayInfo.TileHeight, trickplayInfo.Width, false);
if (_fileSystem.DirectoryExists(oldPath))
{
_fileSystem.MoveDirectory(oldPath, newPath);
}
}
}
} while (previousCount == Limit);
_logger.LogInformation("Moved {Count} items in {Time}", itemCount, sw.Elapsed);
}
private string GetOldTrickplayDirectory(BaseItem item, int? width = null)

View file

@ -46,8 +46,10 @@ public interface ITrickplayManager
/// <summary>
/// Gets the item ids of all items with trickplay info.
/// </summary>
/// <param name="limit">The limit of items to return.</param>
/// <param name="offset">The offset to start the query at.</param>
/// <returns>The list of item ids that have trickplay info.</returns>
public Task<IReadOnlyList<Guid>> GetTrickplayItemsAsync();
Task<IReadOnlyList<TrickplayInfo>> GetTrickplayItemsAsync(int limit, int offset);
/// <summary>
/// Saves trickplay info.

View file

@ -18,8 +18,6 @@ namespace MediaBrowser.Providers.Trickplay;
/// </summary>
public class TrickplayMoveImagesTask : IScheduledTask
{
private const int QueryPageLimit = 100;
private readonly ILogger<TrickplayMoveImagesTask> _logger;
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
@ -62,32 +60,46 @@ public class TrickplayMoveImagesTask : IScheduledTask
/// <inheritdoc />
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var trickplayItems = await _trickplayManager.GetTrickplayItemsAsync().ConfigureAwait(false);
var query = new InternalItemsQuery
const int Limit = 100;
int itemCount = 0, offset = 0, previousCount;
// This count may not be accurate, but just get something to show progress on the dashboard.
var totalVideoCount = _libraryManager.GetCount(new InternalItemsQuery
{
MediaTypes = [MediaType.Video],
SourceTypes = [SourceType.Library],
IsVirtualItem = false,
IsFolder = false,
Recursive = true,
Limit = QueryPageLimit
Recursive = true
});
var trickplayQuery = new InternalItemsQuery
{
MediaTypes = [MediaType.Video],
SourceTypes = [SourceType.Library],
IsVirtualItem = false,
IsFolder = false
};
var numberOfVideos = _libraryManager.GetCount(query);
var startIndex = 0;
var numComplete = 0;
while (startIndex < numberOfVideos)
do
{
query.StartIndex = startIndex;
var videos = _libraryManager.GetItemList(query).OfType<Video>().ToList();
videos.RemoveAll(i => !trickplayItems.Contains(i.Id));
var trickplayInfos = await _trickplayManager.GetTrickplayItemsAsync(Limit, offset).ConfigureAwait(false);
previousCount = trickplayInfos.Count;
offset += Limit;
foreach (var video in videos)
trickplayQuery.ItemIds = trickplayInfos.Select(i => i.ItemId).Distinct().ToArray();
var items = _libraryManager.GetItemList(trickplayQuery);
foreach (var trickplayInfo in trickplayInfos)
{
cancellationToken.ThrowIfCancellationRequested();
var video = items.OfType<Video>().FirstOrDefault(i => i.Id.Equals(trickplayInfo.ItemId));
if (video is null)
{
continue;
}
itemCount++;
try
{
var libraryOptions = _libraryManager.GetLibraryOptions(video);
@ -97,13 +109,10 @@ public class TrickplayMoveImagesTask : IScheduledTask
{
_logger.LogError(ex, "Error moving trickplay files for {ItemName}", video.Name);
}
numComplete++;
progress.Report(100d * numComplete / numberOfVideos);
}
startIndex += QueryPageLimit;
}
progress.Report(100d * itemCount / totalVideoCount);
} while (previousCount == Limit);
progress.Report(100);
}