mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
Prune trickplay data on regenerate and scan (#14085)
This commit is contained in:
parent
44b5de1568
commit
e1a5c16404
@ -7,6 +7,7 @@ using System.Text;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AsyncKeyedLock;
|
using AsyncKeyedLock;
|
||||||
|
using J2N.Collections.Generic.Extensions;
|
||||||
using Jellyfin.Database.Implementations;
|
using Jellyfin.Database.Implementations;
|
||||||
using Jellyfin.Database.Implementations.Entities;
|
using Jellyfin.Database.Implementations.Entities;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
@ -80,7 +81,7 @@ public class TrickplayManager : ITrickplayManager
|
|||||||
public async Task MoveGeneratedTrickplayDataAsync(Video video, LibraryOptions libraryOptions, CancellationToken cancellationToken)
|
public async Task MoveGeneratedTrickplayDataAsync(Video video, LibraryOptions libraryOptions, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var options = _config.Configuration.TrickplayOptions;
|
var options = _config.Configuration.TrickplayOptions;
|
||||||
if (!CanGenerateTrickplay(video, options.Interval, libraryOptions))
|
if (libraryOptions is null || !libraryOptions.EnableTrickplayImageExtraction || !CanGenerateTrickplay(video, options.Interval))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -137,9 +138,45 @@ public class TrickplayManager : ITrickplayManager
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task RefreshTrickplayDataAsync(Video video, bool replace, LibraryOptions libraryOptions, CancellationToken cancellationToken)
|
public async Task RefreshTrickplayDataAsync(Video video, bool replace, LibraryOptions libraryOptions, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
var options = _config.Configuration.TrickplayOptions;
|
||||||
|
if (!CanGenerateTrickplay(video, options.Interval) || libraryOptions is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbContext = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
await using (dbContext.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);
|
_logger.LogDebug("Trickplay refresh for {ItemId} (replace existing: {Replace})", video.Id, replace);
|
||||||
|
|
||||||
var options = _config.Configuration.TrickplayOptions;
|
|
||||||
if (options.Interval < 1000)
|
if (options.Interval < 1000)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Trickplay image interval {Interval} is too small, reset to the minimum valid value of 1000", options.Interval);
|
_logger.LogWarning("Trickplay image interval {Interval} is too small, reset to the minimum valid value of 1000", options.Interval);
|
||||||
@ -154,9 +191,32 @@ public class TrickplayManager : ITrickplayManager
|
|||||||
replace,
|
replace,
|
||||||
width,
|
width,
|
||||||
options,
|
options,
|
||||||
libraryOptions,
|
saveWithMedia,
|
||||||
cancellationToken).ConfigureAwait(false);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RefreshTrickplayDataInternal(
|
private async Task RefreshTrickplayDataInternal(
|
||||||
@ -164,14 +224,9 @@ public class TrickplayManager : ITrickplayManager
|
|||||||
bool replace,
|
bool replace,
|
||||||
int width,
|
int width,
|
||||||
TrickplayOptions options,
|
TrickplayOptions options,
|
||||||
LibraryOptions libraryOptions,
|
bool saveWithMedia,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!CanGenerateTrickplay(video, options.Interval, libraryOptions))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var imgTempDir = string.Empty;
|
var imgTempDir = string.Empty;
|
||||||
|
|
||||||
using (await _resourcePool.LockAsync(cancellationToken).ConfigureAwait(false))
|
using (await _resourcePool.LockAsync(cancellationToken).ConfigureAwait(false))
|
||||||
@ -215,7 +270,6 @@ public class TrickplayManager : ITrickplayManager
|
|||||||
|
|
||||||
var tileWidth = options.TileWidth;
|
var tileWidth = options.TileWidth;
|
||||||
var tileHeight = options.TileHeight;
|
var tileHeight = options.TileHeight;
|
||||||
var saveWithMedia = libraryOptions is not null && libraryOptions.SaveTrickplayWithMedia;
|
|
||||||
var outputDir = new DirectoryInfo(GetTrickplayDirectory(video, tileWidth, tileHeight, actualWidth, saveWithMedia));
|
var outputDir = new DirectoryInfo(GetTrickplayDirectory(video, tileWidth, tileHeight, actualWidth, saveWithMedia));
|
||||||
|
|
||||||
// Import existing trickplay tiles
|
// Import existing trickplay tiles
|
||||||
@ -402,7 +456,7 @@ public class TrickplayManager : ITrickplayManager
|
|||||||
return trickplayInfo;
|
return trickplayInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CanGenerateTrickplay(Video video, int interval, LibraryOptions libraryOptions)
|
private bool CanGenerateTrickplay(Video video, int interval)
|
||||||
{
|
{
|
||||||
var videoType = video.VideoType;
|
var videoType = video.VideoType;
|
||||||
if (videoType == VideoType.Iso || videoType == VideoType.Dvd || videoType == VideoType.BluRay)
|
if (videoType == VideoType.Iso || videoType == VideoType.Dvd || videoType == VideoType.BluRay)
|
||||||
@ -430,11 +484,6 @@ public class TrickplayManager : ITrickplayManager
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (libraryOptions is null || !libraryOptions.EnableTrickplayImageExtraction)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can't extract images if there are no video streams
|
// Can't extract images if there are no video streams
|
||||||
return video.GetMediaStreams().Count > 0;
|
return video.GetMediaStreams().Count > 0;
|
||||||
}
|
}
|
||||||
|
@ -59,14 +59,14 @@ public class TrickplayImagesTask : IScheduledTask
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||||
{
|
{
|
||||||
return new[]
|
return
|
||||||
{
|
[
|
||||||
new TaskTriggerInfo
|
new TaskTriggerInfo
|
||||||
{
|
{
|
||||||
Type = TaskTriggerInfoType.DailyTrigger,
|
Type = TaskTriggerInfoType.DailyTrigger,
|
||||||
TimeOfDayTicks = TimeSpan.FromHours(3).Ticks
|
TimeOfDayTicks = TimeSpan.FromHours(3).Ticks
|
||||||
}
|
}
|
||||||
};
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -74,8 +74,8 @@ public class TrickplayImagesTask : IScheduledTask
|
|||||||
{
|
{
|
||||||
var query = new InternalItemsQuery
|
var query = new InternalItemsQuery
|
||||||
{
|
{
|
||||||
MediaTypes = new[] { MediaType.Video },
|
MediaTypes = [MediaType.Video],
|
||||||
SourceTypes = new[] { SourceType.Library },
|
SourceTypes = [SourceType.Library],
|
||||||
IsVirtualItem = false,
|
IsVirtualItem = false,
|
||||||
IsFolder = false,
|
IsFolder = false,
|
||||||
Recursive = true,
|
Recursive = true,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user