|
@@ -7,6 +7,7 @@ using System.Text;
|
|
|
using System.Threading;
|
|
|
using System.Threading.Tasks;
|
|
|
using AsyncKeyedLock;
|
|
|
+using J2N.Collections.Generic.Extensions;
|
|
|
using Jellyfin.Database.Implementations;
|
|
|
using Jellyfin.Database.Implementations.Entities;
|
|
|
using MediaBrowser.Common.Configuration;
|
|
@@ -80,7 +81,7 @@ public class TrickplayManager : ITrickplayManager
|
|
|
public async Task MoveGeneratedTrickplayDataAsync(Video video, LibraryOptions libraryOptions, CancellationToken cancellationToken)
|
|
|
{
|
|
|
var options = _config.Configuration.TrickplayOptions;
|
|
|
- if (!CanGenerateTrickplay(video, options.Interval, libraryOptions))
|
|
|
+ if (libraryOptions is null || !libraryOptions.EnableTrickplayImageExtraction || !CanGenerateTrickplay(video, options.Interval))
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
@@ -137,25 +138,84 @@ public class TrickplayManager : ITrickplayManager
|
|
|
/// <inheritdoc />
|
|
|
public async Task RefreshTrickplayDataAsync(Video video, bool replace, LibraryOptions libraryOptions, CancellationToken cancellationToken)
|
|
|
{
|
|
|
- _logger.LogDebug("Trickplay refresh for {ItemId} (replace existing: {Replace})", video.Id, replace);
|
|
|
-
|
|
|
var options = _config.Configuration.TrickplayOptions;
|
|
|
- if (options.Interval < 1000)
|
|
|
+ if (!CanGenerateTrickplay(video, options.Interval) || libraryOptions is null)
|
|
|
{
|
|
|
- _logger.LogWarning("Trickplay image interval {Interval} is too small, reset to the minimum valid value of 1000", options.Interval);
|
|
|
- options.Interval = 1000;
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- foreach (var width in options.WidthResolutions)
|
|
|
+ var dbContext = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
|
|
+ await using (dbContext.ConfigureAwait(false))
|
|
|
{
|
|
|
- cancellationToken.ThrowIfCancellationRequested();
|
|
|
- await RefreshTrickplayDataInternal(
|
|
|
- video,
|
|
|
- replace,
|
|
|
- width,
|
|
|
- options,
|
|
|
- libraryOptions,
|
|
|
- cancellationToken).ConfigureAwait(false);
|
|
|
+ var saveWithMedia = libraryOptions.SaveTrickplayWithMedia;
|
|
|
+ var trickplayDirectory = _pathManager.GetTrickplayDirectory(video, saveWithMedia);
|
|
|
+ if (!libraryOptions.EnableTrickplayImageExtraction || replace)
|
|
|
+ {
|
|
|
+ // Prune existing data
|
|
|
+ if (Directory.Exists(trickplayDirectory))
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ Directory.Delete(trickplayDirectory, true);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogWarning("Unable to clear trickplay directory: {Directory}: {Exception}", trickplayDirectory, ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ await dbContext.TrickplayInfos
|
|
|
+ .Where(i => i.ItemId.Equals(video.Id))
|
|
|
+ .ExecuteDeleteAsync(cancellationToken)
|
|
|
+ .ConfigureAwait(false);
|
|
|
+
|
|
|
+ if (!replace)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _logger.LogDebug("Trickplay refresh for {ItemId} (replace existing: {Replace})", video.Id, replace);
|
|
|
+
|
|
|
+ if (options.Interval < 1000)
|
|
|
+ {
|
|
|
+ _logger.LogWarning("Trickplay image interval {Interval} is too small, reset to the minimum valid value of 1000", options.Interval);
|
|
|
+ options.Interval = 1000;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (var width in options.WidthResolutions)
|
|
|
+ {
|
|
|
+ cancellationToken.ThrowIfCancellationRequested();
|
|
|
+ await RefreshTrickplayDataInternal(
|
|
|
+ video,
|
|
|
+ replace,
|
|
|
+ width,
|
|
|
+ options,
|
|
|
+ saveWithMedia,
|
|
|
+ cancellationToken).ConfigureAwait(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Cleanup old trickplay files
|
|
|
+ var existingFolders = Directory.GetDirectories(trickplayDirectory).ToList();
|
|
|
+ var trickplayInfos = await dbContext.TrickplayInfos
|
|
|
+ .AsNoTracking()
|
|
|
+ .Where(i => i.ItemId.Equals(video.Id))
|
|
|
+ .ToListAsync(cancellationToken)
|
|
|
+ .ConfigureAwait(false);
|
|
|
+ var expectedFolders = trickplayInfos.Select(i => GetTrickplayDirectory(video, i.TileWidth, i.TileHeight, i.Width, saveWithMedia)).ToList();
|
|
|
+ var foldersToRemove = existingFolders.Except(expectedFolders);
|
|
|
+ foreach (var folder in foldersToRemove)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ _logger.LogWarning("Pruning trickplay files for {Item}", video.Path);
|
|
|
+ Directory.Delete(folder, true);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogWarning("Unable to remove trickplay directory: {Directory}: {Exception}", folder, ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -164,14 +224,9 @@ public class TrickplayManager : ITrickplayManager
|
|
|
bool replace,
|
|
|
int width,
|
|
|
TrickplayOptions options,
|
|
|
- LibraryOptions libraryOptions,
|
|
|
+ bool saveWithMedia,
|
|
|
CancellationToken cancellationToken)
|
|
|
{
|
|
|
- if (!CanGenerateTrickplay(video, options.Interval, libraryOptions))
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
var imgTempDir = string.Empty;
|
|
|
|
|
|
using (await _resourcePool.LockAsync(cancellationToken).ConfigureAwait(false))
|
|
@@ -215,7 +270,6 @@ public class TrickplayManager : ITrickplayManager
|
|
|
|
|
|
var tileWidth = options.TileWidth;
|
|
|
var tileHeight = options.TileHeight;
|
|
|
- var saveWithMedia = libraryOptions is not null && libraryOptions.SaveTrickplayWithMedia;
|
|
|
var outputDir = new DirectoryInfo(GetTrickplayDirectory(video, tileWidth, tileHeight, actualWidth, saveWithMedia));
|
|
|
|
|
|
// Import existing trickplay tiles
|
|
@@ -402,7 +456,7 @@ public class TrickplayManager : ITrickplayManager
|
|
|
return trickplayInfo;
|
|
|
}
|
|
|
|
|
|
- private bool CanGenerateTrickplay(Video video, int interval, LibraryOptions libraryOptions)
|
|
|
+ private bool CanGenerateTrickplay(Video video, int interval)
|
|
|
{
|
|
|
var videoType = video.VideoType;
|
|
|
if (videoType == VideoType.Iso || videoType == VideoType.Dvd || videoType == VideoType.BluRay)
|
|
@@ -430,11 +484,6 @@ public class TrickplayManager : ITrickplayManager
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- if (libraryOptions is null || !libraryOptions.EnableTrickplayImageExtraction)
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
// Can't extract images if there are no video streams
|
|
|
return video.GetMediaStreams().Count > 0;
|
|
|
}
|