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 /> /// <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); var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
await using (dbContext.ConfigureAwait(false)) await using (dbContext.ConfigureAwait(false))
{ {
trickplayItems = await dbContext.TrickplayInfos trickplayItems = await dbContext.TrickplayInfos
.AsNoTracking() .AsNoTracking()
.Select(i => i.ItemId) .OrderBy(i => i.ItemId)
.Skip(offset)
.Take(limit)
.ToListAsync() .ToListAsync()
.ConfigureAwait(false); .ConfigureAwait(false);
} }

View File

@ -1,10 +1,15 @@
using System; using System;
using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using Jellyfin.Data.Enums;
using MediaBrowser.Common;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Trickplay; using MediaBrowser.Controller.Trickplay;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Server.Migrations.Routines; namespace Jellyfin.Server.Migrations.Routines;
@ -16,6 +21,7 @@ public class MoveTrickplayFiles : IMigrationRoutine
private readonly ITrickplayManager _trickplayManager; private readonly ITrickplayManager _trickplayManager;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILogger<MoveTrickplayFiles> _logger;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="MoveTrickplayFiles"/> class. /// 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="trickplayManager">Instance of the <see cref="ITrickplayManager"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> 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; _trickplayManager = trickplayManager;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_logger = logger;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -42,26 +50,49 @@ public class MoveTrickplayFiles : IMigrationRoutine
/// <inheritdoc /> /// <inheritdoc />
public void Perform() public void Perform()
{ {
var trickplayItems = _trickplayManager.GetTrickplayItemsAsync().GetAwaiter().GetResult(); const int Limit = 100;
foreach (var itemId in trickplayItems) int itemCount = 0, offset = 0, previousCount;
{
var resolutions = _trickplayManager.GetTrickplayResolutions(itemId).GetAwaiter().GetResult();
var item = _libraryManager.GetItemById(itemId);
if (item is null)
{
continue;
}
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 item = items.OfType<Video>().FirstOrDefault(i => i.Id.Equals(trickplayInfo.ItemId));
var newPath = _trickplayManager.GetTrickplayDirectory(item, resolution.Value.TileWidth, resolution.Value.TileHeight, resolution.Value.Width, false); 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)) if (_fileSystem.DirectoryExists(oldPath))
{ {
_fileSystem.MoveDirectory(oldPath, newPath); _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) private string GetOldTrickplayDirectory(BaseItem item, int? width = null)

View File

@ -46,8 +46,10 @@ public interface ITrickplayManager
/// <summary> /// <summary>
/// Gets the item ids of all items with trickplay info. /// Gets the item ids of all items with trickplay info.
/// </summary> /// </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> /// <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> /// <summary>
/// Saves trickplay info. /// Saves trickplay info.

View File

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