Explorar el Código

crobibero styling, format, code suggestions

Nick hace 2 años
padre
commit
3377032228

+ 11 - 16
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -90,6 +90,13 @@ namespace MediaBrowser.Controller.MediaEncoding
             { "truehd", 6 },
         };
 
+        private static readonly string _defaultMjpegEncoder = "mjpeg";
+        private static readonly Dictionary<string, string> _mjpegCodecMap = new(StringComparer.OrdinalIgnoreCase)
+        {
+            { "vaapi", _defaultMjpegEncoder + "_vaapi" },
+            { "qsv", _defaultMjpegEncoder + "_qsv" }
+        };
+
         public static readonly string[] LosslessAudioCodecs = new string[]
         {
             "alac",
@@ -151,32 +158,20 @@ namespace MediaBrowser.Controller.MediaEncoding
 
         private string GetMjpegEncoder(EncodingJobInfo state, EncodingOptions encodingOptions)
         {
-            var defaultEncoder = "mjpeg";
-
             if (state.VideoType == VideoType.VideoFile)
             {
                 var hwType = encodingOptions.HardwareAccelerationType;
 
-                var codecMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
-                {
-                    { "vaapi",                  defaultEncoder + "_vaapi" },
-                    { "qsv",                defaultEncoder + "_qsv" }
-                };
-
                 if (!string.IsNullOrEmpty(hwType)
                     && encodingOptions.EnableHardwareEncoding
-                    && codecMap.ContainsKey(hwType))
+                    && _mjpegCodecMap.TryGetValue(hwType, out var preferredEncoder)
+                    && _mediaEncoder.SupportsEncoder(preferredEncoder))
                 {
-                    var preferredEncoder = codecMap[hwType];
-
-                    if (_mediaEncoder.SupportsEncoder(preferredEncoder))
-                    {
-                        return preferredEncoder;
-                    }
+                    return preferredEncoder;
                 }
             }
 
-            return defaultEncoder;
+            return _defaultMjpegEncoder;
         }
 
         private bool IsVaapiSupported(EncodingJobInfo state)

+ 38 - 39
MediaBrowser.Controller/Trickplay/ITrickplayManager.cs

@@ -5,50 +5,49 @@ using System.Threading.Tasks;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Entities;
 
-namespace MediaBrowser.Controller.Trickplay
+namespace MediaBrowser.Controller.Trickplay;
+
+/// <summary>
+/// Interface ITrickplayManager.
+/// </summary>
+public interface ITrickplayManager
 {
     /// <summary>
-    /// Interface ITrickplayManager.
+    /// Generate or replace trickplay data.
     /// </summary>
-    public interface ITrickplayManager
-    {
-        /// <summary>
-        /// Generate or replace trickplay data.
-        /// </summary>
-        /// <param name="video">The video.</param>
-        /// <param name="replace">Whether or not existing data should be replaced.</param>
-        /// <param name="cancellationToken">CancellationToken to use for operation.</param>
-        /// <returns>Task.</returns>
-        Task RefreshTrickplayData(Video video, bool replace, CancellationToken cancellationToken);
+    /// <param name="video">The video.</param>
+    /// <param name="replace">Whether or not existing data should be replaced.</param>
+    /// <param name="cancellationToken">CancellationToken to use for operation.</param>
+    /// <returns>Task.</returns>
+    Task RefreshTrickplayDataAsync(Video video, bool replace, CancellationToken cancellationToken);
 
-        /// <summary>
-        /// Get available trickplay resolutions and corresponding info.
-        /// </summary>
-        /// <param name="itemId">The item.</param>
-        /// <returns>Map of width resolutions to trickplay tiles info.</returns>
-        Dictionary<int, TrickplayTilesInfo> GetTilesResolutions(Guid itemId);
+    /// <summary>
+    /// Get available trickplay resolutions and corresponding info.
+    /// </summary>
+    /// <param name="itemId">The item.</param>
+    /// <returns>Map of width resolutions to trickplay tiles info.</returns>
+    Dictionary<int, TrickplayTilesInfo> GetTilesResolutions(Guid itemId);
 
-        /// <summary>
-        /// Saves trickplay tiles info.
-        /// </summary>
-        /// <param name="itemId">The item.</param>
-        /// <param name="tilesInfo">The trickplay tiles info.</param>
-        void SaveTilesInfo(Guid itemId, TrickplayTilesInfo tilesInfo);
+    /// <summary>
+    /// Saves trickplay tiles info.
+    /// </summary>
+    /// <param name="itemId">The item.</param>
+    /// <param name="tilesInfo">The trickplay tiles info.</param>
+    void SaveTilesInfo(Guid itemId, TrickplayTilesInfo tilesInfo);
 
-        /// <summary>
-        /// Gets the trickplay manifest.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>A map of media source id to a map of tile width to tile info.</returns>
-        Dictionary<Guid, Dictionary<int, TrickplayTilesInfo>> GetTrickplayManifest(BaseItem item);
+    /// <summary>
+    /// Gets the trickplay manifest.
+    /// </summary>
+    /// <param name="item">The item.</param>
+    /// <returns>A map of media source id to a map of tile width to tile info.</returns>
+    Dictionary<Guid, Dictionary<int, TrickplayTilesInfo>> GetTrickplayManifest(BaseItem item);
 
-        /// <summary>
-        /// Gets the path to a trickplay tiles image.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="width">The width of a single tile.</param>
-        /// <param name="index">The tile grid's index.</param>
-        /// <returns>The absolute path.</returns>
-        string GetTrickplayTilePath(BaseItem item, int width, int index);
-    }
+    /// <summary>
+    /// Gets the path to a trickplay tiles image.
+    /// </summary>
+    /// <param name="item">The item.</param>
+    /// <param name="width">The width of a single tile.</param>
+    /// <param name="index">The tile grid's index.</param>
+    /// <returns>The absolute path.</returns>
+    string GetTrickplayTilePath(BaseItem item, int width, int index);
 }

+ 1 - 1
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -793,7 +793,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             CancellationToken cancellationToken)
         {
             var options = allowHwAccel ? _configurationManager.GetEncodingOptions() : new EncodingOptions();
-            threads = threads ?? _threads;
+            threads ??= _threads;
 
             // A new EncodingOptions instance must be used as to not disable HW acceleration for all of Jellyfin.
             // Additionally, we must set a few fields without defaults to prevent null pointer exceptions.

+ 54 - 55
MediaBrowser.Model/Configuration/TrickplayOptions.cs

@@ -1,61 +1,60 @@
 using System.Collections.Generic;
 using System.Diagnostics;
 
-namespace MediaBrowser.Model.Configuration
+namespace MediaBrowser.Model.Configuration;
+
+/// <summary>
+/// Class TrickplayOptions.
+/// </summary>
+public class TrickplayOptions
 {
     /// <summary>
-    /// Class TrickplayOptions.
-    /// </summary>
-    public class TrickplayOptions
-    {
-        /// <summary>
-        /// Gets or sets a value indicating whether or not to use HW acceleration.
-        /// </summary>
-        public bool EnableHwAcceleration { get; set; } = false;
-
-        /// <summary>
-        /// Gets or sets the behavior used by trickplay provider on library scan/update.
-        /// </summary>
-        public TrickplayScanBehavior ScanBehavior { get; set; } = TrickplayScanBehavior.NonBlocking;
-
-        /// <summary>
-        /// Gets or sets the process priority for the ffmpeg process.
-        /// </summary>
-        public ProcessPriorityClass ProcessPriority { get; set; } = ProcessPriorityClass.BelowNormal;
-
-        /// <summary>
-        /// Gets or sets the interval, in ms, between each new trickplay image.
-        /// </summary>
-        public int Interval { get; set; } = 10000;
-
-        /// <summary>
-        /// Gets or sets the target width resolutions, in px, to generates preview images for.
-        /// </summary>
-        public int[] WidthResolutions { get; set; } = new[] { 320 };
-
-        /// <summary>
-        /// Gets or sets number of tile images to allow in X dimension.
-        /// </summary>
-        public int TileWidth { get; set; } = 10;
-
-        /// <summary>
-        /// Gets or sets number of tile images to allow in Y dimension.
-        /// </summary>
-        public int TileHeight { get; set; } = 10;
-
-        /// <summary>
-        /// Gets or sets the ffmpeg output quality level.
-        /// </summary>
-        public int Qscale { get; set; } = 4;
-
-        /// <summary>
-        /// Gets or sets the jpeg quality to use for image tiles.
-        /// </summary>
-        public int JpegQuality { get; set; } = 90;
-
-        /// <summary>
-        /// Gets or sets the number of threads to be used by ffmpeg.
-        /// </summary>
-        public int ProcessThreads { get; set; } = 0;
-    }
+    /// Gets or sets a value indicating whether or not to use HW acceleration.
+    /// </summary>
+    public bool EnableHwAcceleration { get; set; } = false;
+
+    /// <summary>
+    /// Gets or sets the behavior used by trickplay provider on library scan/update.
+    /// </summary>
+    public TrickplayScanBehavior ScanBehavior { get; set; } = TrickplayScanBehavior.NonBlocking;
+
+    /// <summary>
+    /// Gets or sets the process priority for the ffmpeg process.
+    /// </summary>
+    public ProcessPriorityClass ProcessPriority { get; set; } = ProcessPriorityClass.BelowNormal;
+
+    /// <summary>
+    /// Gets or sets the interval, in ms, between each new trickplay image.
+    /// </summary>
+    public int Interval { get; set; } = 10000;
+
+    /// <summary>
+    /// Gets or sets the target width resolutions, in px, to generates preview images for.
+    /// </summary>
+    public int[] WidthResolutions { get; set; } = new[] { 320 };
+
+    /// <summary>
+    /// Gets or sets number of tile images to allow in X dimension.
+    /// </summary>
+    public int TileWidth { get; set; } = 10;
+
+    /// <summary>
+    /// Gets or sets number of tile images to allow in Y dimension.
+    /// </summary>
+    public int TileHeight { get; set; } = 10;
+
+    /// <summary>
+    /// Gets or sets the ffmpeg output quality level.
+    /// </summary>
+    public int Qscale { get; set; } = 4;
+
+    /// <summary>
+    /// Gets or sets the jpeg quality to use for image tiles.
+    /// </summary>
+    public int JpegQuality { get; set; } = 90;
+
+    /// <summary>
+    /// Gets or sets the number of threads to be used by ffmpeg.
+    /// </summary>
+    public int ProcessThreads { get; set; } = 0;
 }

+ 12 - 13
MediaBrowser.Model/Configuration/TrickplayScanBehavior.cs

@@ -1,18 +1,17 @@
-namespace MediaBrowser.Model.Configuration
+namespace MediaBrowser.Model.Configuration;
+
+/// <summary>
+/// Enum TrickplayScanBehavior.
+/// </summary>
+public enum TrickplayScanBehavior
 {
     /// <summary>
-    /// Enum TrickplayScanBehavior.
+    /// Starts generation, only return once complete.
     /// </summary>
-    public enum TrickplayScanBehavior
-    {
-        /// <summary>
-        /// Starts generation, only return once complete.
-        /// </summary>
-        Blocking,
+    Blocking,
 
-        /// <summary>
-        /// Start generation, return immediately.
-        /// </summary>
-        NonBlocking
-    }
+    /// <summary>
+    /// Start generation, return immediately.
+    /// </summary>
+    NonBlocking
 }

+ 39 - 40
MediaBrowser.Model/Entities/TrickplayTilesInfo.cs

@@ -1,50 +1,49 @@
-namespace MediaBrowser.Model.Entities
+namespace MediaBrowser.Model.Entities;
+
+/// <summary>
+/// Class TrickplayTilesInfo.
+/// </summary>
+public class TrickplayTilesInfo
 {
     /// <summary>
-    /// Class TrickplayTilesInfo.
+    /// Gets or sets width of an individual tile.
     /// </summary>
-    public class TrickplayTilesInfo
-    {
-        /// <summary>
-        /// Gets or sets width of an individual tile.
-        /// </summary>
-        /// <value>The width.</value>
-        public int Width { get; set; }
+    /// <value>The width.</value>
+    public int Width { get; set; }
 
-        /// <summary>
-        /// Gets or sets height of an individual tile.
-        /// </summary>
-        /// <value>The height.</value>
-        public int Height { get; set; }
+    /// <summary>
+    /// Gets or sets height of an individual tile.
+    /// </summary>
+    /// <value>The height.</value>
+    public int Height { get; set; }
 
-        /// <summary>
-        /// Gets or sets amount of tiles per row.
-        /// </summary>
-        /// <value>The tile grid's width.</value>
-        public int TileWidth { get; set; }
+    /// <summary>
+    /// Gets or sets amount of tiles per row.
+    /// </summary>
+    /// <value>The tile grid's width.</value>
+    public int TileWidth { get; set; }
 
-        /// <summary>
-        /// Gets or sets amount of tiles per column.
-        /// </summary>
-        /// <value>The tile grid's height.</value>
-        public int TileHeight { get; set; }
+    /// <summary>
+    /// Gets or sets amount of tiles per column.
+    /// </summary>
+    /// <value>The tile grid's height.</value>
+    public int TileHeight { get; set; }
 
-        /// <summary>
-        /// Gets or sets total amount of non-black tiles.
-        /// </summary>
-        /// <value>The tile count.</value>
-        public int TileCount { get; set; }
+    /// <summary>
+    /// Gets or sets total amount of non-black tiles.
+    /// </summary>
+    /// <value>The tile count.</value>
+    public int TileCount { get; set; }
 
-        /// <summary>
-        /// Gets or sets interval in milliseconds between each trickplay tile.
-        /// </summary>
-        /// <value>The interval.</value>
-        public int Interval { get; set; }
+    /// <summary>
+    /// Gets or sets interval in milliseconds between each trickplay tile.
+    /// </summary>
+    /// <value>The interval.</value>
+    public int Interval { get; set; }
 
-        /// <summary>
-        /// Gets or sets peak bandwith usage in bits per second.
-        /// </summary>
-        /// <value>The bandwidth.</value>
-        public int Bandwidth { get; set; }
-    }
+    /// <summary>
+    /// Gets or sets peak bandwith usage in bits per second.
+    /// </summary>
+    /// <value>The bandwidth.</value>
+    public int Bandwidth { get; set; }
 }

+ 73 - 74
MediaBrowser.Providers/Trickplay/TrickplayImagesTask.cs

@@ -12,98 +12,97 @@ using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Tasks;
 using Microsoft.Extensions.Logging;
 
-namespace MediaBrowser.Providers.Trickplay
+namespace MediaBrowser.Providers.Trickplay;
+
+/// <summary>
+/// Class TrickplayImagesTask.
+/// </summary>
+public class TrickplayImagesTask : IScheduledTask
 {
+    private readonly ILogger<TrickplayImagesTask> _logger;
+    private readonly ILibraryManager _libraryManager;
+    private readonly ILocalizationManager _localization;
+    private readonly ITrickplayManager _trickplayManager;
+
     /// <summary>
-    /// Class TrickplayImagesTask.
+    /// Initializes a new instance of the <see cref="TrickplayImagesTask"/> class.
     /// </summary>
-    public class TrickplayImagesTask : IScheduledTask
+    /// <param name="logger">The logger.</param>
+    /// <param name="libraryManager">The library manager.</param>
+    /// <param name="localization">The localization manager.</param>
+    /// <param name="trickplayManager">The trickplay manager.</param>
+    public TrickplayImagesTask(
+        ILogger<TrickplayImagesTask> logger,
+        ILibraryManager libraryManager,
+        ILocalizationManager localization,
+        ITrickplayManager trickplayManager)
     {
-        private readonly ILogger<TrickplayImagesTask> _logger;
-        private readonly ILibraryManager _libraryManager;
-        private readonly ILocalizationManager _localization;
-        private readonly ITrickplayManager _trickplayManager;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="TrickplayImagesTask"/> class.
-        /// </summary>
-        /// <param name="logger">The logger.</param>
-        /// <param name="libraryManager">The library manager.</param>
-        /// <param name="localization">The localization manager.</param>
-        /// <param name="trickplayManager">The trickplay manager.</param>
-        public TrickplayImagesTask(
-            ILogger<TrickplayImagesTask> logger,
-            ILibraryManager libraryManager,
-            ILocalizationManager localization,
-            ITrickplayManager trickplayManager)
-        {
-            _libraryManager = libraryManager;
-            _logger = logger;
-            _localization = localization;
-            _trickplayManager = trickplayManager;
-        }
+        _libraryManager = libraryManager;
+        _logger = logger;
+        _localization = localization;
+        _trickplayManager = trickplayManager;
+    }
 
-        /// <inheritdoc />
-        public string Name => _localization.GetLocalizedString("TaskRefreshTrickplayImages");
+    /// <inheritdoc />
+    public string Name => _localization.GetLocalizedString("TaskRefreshTrickplayImages");
 
-        /// <inheritdoc />
-        public string Description => _localization.GetLocalizedString("TaskRefreshTrickplayImagesDescription");
+    /// <inheritdoc />
+    public string Description => _localization.GetLocalizedString("TaskRefreshTrickplayImagesDescription");
 
-        /// <inheritdoc />
-        public string Key => "RefreshTrickplayImages";
+    /// <inheritdoc />
+    public string Key => "RefreshTrickplayImages";
 
-        /// <inheritdoc />
-        public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
+    /// <inheritdoc />
+    public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
 
-        /// <inheritdoc />
-        public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
+    /// <inheritdoc />
+    public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
+    {
+        return new[]
         {
-            return new[]
+            new TaskTriggerInfo
             {
-                new TaskTriggerInfo
-                {
-                    Type = TaskTriggerInfo.TriggerDaily,
-                    TimeOfDayTicks = TimeSpan.FromHours(3).Ticks
-                }
-            };
-        }
+                Type = TaskTriggerInfo.TriggerDaily,
+                TimeOfDayTicks = TimeSpan.FromHours(3).Ticks
+            }
+        };
+    }
 
-        /// <inheritdoc />
-        public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
+    /// <inheritdoc />
+    public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
+    {
+        var items = _libraryManager.GetItemList(new InternalItemsQuery
         {
-            var items = _libraryManager.GetItemList(new InternalItemsQuery
-            {
-                MediaTypes = new[] { MediaType.Video },
-                IsVirtualItem = false,
-                IsFolder = false,
-                Recursive = true
-            }).OfType<Video>().ToList();
+            MediaTypes = new[] { MediaType.Video },
+            IsVirtualItem = false,
+            IsFolder = false,
+            Recursive = true
+        }).OfType<Video>().ToList();
 
-            var numComplete = 0;
+        var numComplete = 0;
 
-            foreach (var item in items)
+        foreach (var item in items)
+        {
+            try
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+                await _trickplayManager.RefreshTrickplayDataAsync(item, false, cancellationToken).ConfigureAwait(false);
+            }
+            catch (OperationCanceledException)
             {
-                try
-                {
-                    cancellationToken.ThrowIfCancellationRequested();
-                    await _trickplayManager.RefreshTrickplayData(item, false, cancellationToken).ConfigureAwait(false);
-                }
-                catch (OperationCanceledException)
-                {
-                    break;
-                }
-                catch (Exception ex)
-                {
-                    _logger.LogError("Error creating trickplay files for {ItemName}: {Msg}", item.Name, ex);
-                }
+                break;
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError("Error creating trickplay files for {ItemName}: {Msg}", item.Name, ex);
+            }
 
-                numComplete++;
-                double percent = numComplete;
-                percent /= items.Count;
-                percent *= 100;
+            numComplete++;
+            double percent = numComplete;
+            percent /= items.Count;
+            percent *= 100;
 
-                progress.Report(percent);
-            }
+            progress.Report(percent);
         }
     }
 }

+ 284 - 285
MediaBrowser.Providers/Trickplay/TrickplayManager.cs

@@ -17,370 +17,369 @@ using MediaBrowser.Model.IO;
 using Microsoft.Extensions.Logging;
 using SkiaSharp;
 
-namespace MediaBrowser.Providers.Trickplay
+namespace MediaBrowser.Providers.Trickplay;
+
+/// <summary>
+/// ITrickplayManager implementation.
+/// </summary>
+public class TrickplayManager : ITrickplayManager
 {
+    private readonly ILogger<TrickplayManager> _logger;
+    private readonly IItemRepository _itemRepo;
+    private readonly IMediaEncoder _mediaEncoder;
+    private readonly IFileSystem _fileSystem;
+    private readonly EncodingHelper _encodingHelper;
+    private readonly ILibraryManager _libraryManager;
+    private readonly IServerConfigurationManager _config;
+
+    private static readonly SemaphoreSlim _resourcePool = new(1, 1);
+
     /// <summary>
-    /// ITrickplayManager implementation.
+    /// Initializes a new instance of the <see cref="TrickplayManager"/> class.
     /// </summary>
-    public class TrickplayManager : ITrickplayManager
+    /// <param name="logger">The logger.</param>
+    /// <param name="itemRepo">The item repository.</param>
+    /// <param name="mediaEncoder">The media encoder.</param>
+    /// <param name="fileSystem">The file systen.</param>
+    /// <param name="encodingHelper">The encoding helper.</param>
+    /// <param name="libraryManager">The library manager.</param>
+    /// <param name="config">The server configuration manager.</param>
+    public TrickplayManager(
+        ILogger<TrickplayManager> logger,
+        IItemRepository itemRepo,
+        IMediaEncoder mediaEncoder,
+        IFileSystem fileSystem,
+        EncodingHelper encodingHelper,
+        ILibraryManager libraryManager,
+        IServerConfigurationManager config)
     {
-        private readonly ILogger<TrickplayManager> _logger;
-        private readonly IItemRepository _itemRepo;
-        private readonly IMediaEncoder _mediaEncoder;
-        private readonly IFileSystem _fileSystem;
-        private readonly EncodingHelper _encodingHelper;
-        private readonly ILibraryManager _libraryManager;
-        private readonly IServerConfigurationManager _config;
-
-        private static readonly SemaphoreSlim _resourcePool = new(1, 1);
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="TrickplayManager"/> class.
-        /// </summary>
-        /// <param name="logger">The logger.</param>
-        /// <param name="itemRepo">The item repository.</param>
-        /// <param name="mediaEncoder">The media encoder.</param>
-        /// <param name="fileSystem">The file systen.</param>
-        /// <param name="encodingHelper">The encoding helper.</param>
-        /// <param name="libraryManager">The library manager.</param>
-        /// <param name="config">The server configuration manager.</param>
-        public TrickplayManager(
-            ILogger<TrickplayManager> logger,
-            IItemRepository itemRepo,
-            IMediaEncoder mediaEncoder,
-            IFileSystem fileSystem,
-            EncodingHelper encodingHelper,
-            ILibraryManager libraryManager,
-            IServerConfigurationManager config)
+        _logger = logger;
+        _itemRepo = itemRepo;
+        _mediaEncoder = mediaEncoder;
+        _fileSystem = fileSystem;
+        _encodingHelper = encodingHelper;
+        _libraryManager = libraryManager;
+        _config = config;
+    }
+
+    /// <inheritdoc />
+    public async Task RefreshTrickplayDataAsync(Video video, bool replace, CancellationToken cancellationToken)
+    {
+        _logger.LogDebug("Trickplay refresh for {ItemId} (replace existing: {Replace})", video.Id, replace);
+
+        var options = _config.Configuration.TrickplayOptions;
+        foreach (var width in options.WidthResolutions)
         {
-            _logger = logger;
-            _itemRepo = itemRepo;
-            _mediaEncoder = mediaEncoder;
-            _fileSystem = fileSystem;
-            _encodingHelper = encodingHelper;
-            _libraryManager = libraryManager;
-            _config = config;
+            cancellationToken.ThrowIfCancellationRequested();
+            await RefreshTrickplayDataInternal(
+                video,
+                replace,
+                width,
+                options,
+                cancellationToken).ConfigureAwait(false);
         }
+    }
 
-        /// <inheritdoc />
-        public async Task RefreshTrickplayData(Video video, bool replace, CancellationToken cancellationToken)
+    private async Task RefreshTrickplayDataInternal(
+        Video video,
+        bool replace,
+        int width,
+        TrickplayOptions options,
+        CancellationToken cancellationToken)
+    {
+        if (!CanGenerateTrickplay(video, options.Interval))
         {
-            _logger.LogDebug("Trickplay refresh for {ItemId} (replace existing: {Replace})", video.Id, replace);
-
-            var options = _config.Configuration.TrickplayOptions;
-            foreach (var width in options.WidthResolutions)
-            {
-                cancellationToken.ThrowIfCancellationRequested();
-                await RefreshTrickplayDataInternal(
-                    video,
-                    replace,
-                    width,
-                    options,
-                    cancellationToken).ConfigureAwait(false);
-            }
+            return;
         }
 
-        private async Task RefreshTrickplayDataInternal(
-            Video video,
-            bool replace,
-            int width,
-            TrickplayOptions options,
-            CancellationToken cancellationToken)
+        var imgTempDir = string.Empty;
+        var outputDir = GetTrickplayDirectory(video, width);
+
+        try
         {
-            if (!CanGenerateTrickplay(video, options.Interval))
+            await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+            if (!replace && Directory.Exists(outputDir) && GetTilesResolutions(video.Id).ContainsKey(width))
             {
+                _logger.LogDebug("Found existing trickplay files for {ItemId}. Exiting.", video.Id);
                 return;
             }
 
-            var imgTempDir = string.Empty;
-            var outputDir = GetTrickplayDirectory(video, width);
+            // Extract images
+            // Note: Media sources under parent items exist as their own video/item as well. Only use this video stream for trickplay.
+            var mediaSource = video.GetMediaSources(false).Find(source => Guid.Parse(source.Id).Equals(video.Id));
 
-            try
+            if (mediaSource is null)
             {
-                await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
-                if (!replace && Directory.Exists(outputDir) && GetTilesResolutions(video.Id).ContainsKey(width))
-                {
-                    _logger.LogDebug("Found existing trickplay files for {ItemId}. Exiting.", video.Id);
-                    return;
-                }
-
-                // Extract images
-                // Note: Media sources under parent items exist as their own video/item as well. Only use this video stream for trickplay.
-                var mediaSource = video.GetMediaSources(false).Find(source => Guid.Parse(source.Id).Equals(video.Id));
-
-                if (mediaSource is null)
-                {
-                    _logger.LogDebug("Found no matching media source for item {ItemId}", video.Id);
-                    return;
-                }
+                _logger.LogDebug("Found no matching media source for item {ItemId}", video.Id);
+                return;
+            }
 
-                var mediaPath = mediaSource.Path;
-                var mediaStream = mediaSource.VideoStream;
-                var container = mediaSource.Container;
-
-                _logger.LogInformation("Creating trickplay files at {Width} width, for {Path} [ID: {ItemId}]", width, mediaPath, video.Id);
-                imgTempDir = await _mediaEncoder.ExtractVideoImagesOnIntervalAccelerated(
-                    mediaPath,
-                    container,
-                    mediaSource,
-                    mediaStream,
-                    width,
-                    TimeSpan.FromMilliseconds(options.Interval),
-                    options.EnableHwAcceleration,
-                    options.ProcessThreads,
-                    options.Qscale,
-                    options.ProcessPriority,
-                    _encodingHelper,
-                    cancellationToken).ConfigureAwait(false);
-
-                if (string.IsNullOrEmpty(imgTempDir) || !Directory.Exists(imgTempDir))
-                {
-                    throw new InvalidOperationException("Null or invalid directory from media encoder.");
-                }
+            var mediaPath = mediaSource.Path;
+            var mediaStream = mediaSource.VideoStream;
+            var container = mediaSource.Container;
+
+            _logger.LogInformation("Creating trickplay files at {Width} width, for {Path} [ID: {ItemId}]", width, mediaPath, video.Id);
+            imgTempDir = await _mediaEncoder.ExtractVideoImagesOnIntervalAccelerated(
+                mediaPath,
+                container,
+                mediaSource,
+                mediaStream,
+                width,
+                TimeSpan.FromMilliseconds(options.Interval),
+                options.EnableHwAcceleration,
+                options.ProcessThreads,
+                options.Qscale,
+                options.ProcessPriority,
+                _encodingHelper,
+                cancellationToken).ConfigureAwait(false);
+
+            if (string.IsNullOrEmpty(imgTempDir) || !Directory.Exists(imgTempDir))
+            {
+                throw new InvalidOperationException("Null or invalid directory from media encoder.");
+            }
 
-                var images = _fileSystem.GetFiles(imgTempDir, new string[] { ".jpg" }, false, false)
-                    .Where(img => string.Equals(img.Extension, ".jpg", StringComparison.Ordinal))
-                    .OrderBy(i => i.FullName)
-                    .ToList();
+            var images = _fileSystem.GetFiles(imgTempDir, new string[] { ".jpg" }, false, false)
+                .Where(img => string.Equals(img.Extension, ".jpg", StringComparison.Ordinal))
+                .OrderBy(i => i.FullName)
+                .ToList();
 
-                // Create tiles
-                var tilesTempDir = Path.Combine(imgTempDir, Guid.NewGuid().ToString("N"));
-                var tilesInfo = CreateTiles(images, width, options, tilesTempDir, outputDir);
+            // Create tiles
+            var tilesTempDir = Path.Combine(imgTempDir, Guid.NewGuid().ToString("N"));
+            var tilesInfo = CreateTiles(images, width, options, tilesTempDir, outputDir);
 
-                // Save tiles info
-                try
+            // Save tiles info
+            try
+            {
+                if (tilesInfo is not null)
                 {
-                    if (tilesInfo is not null)
-                    {
-                        SaveTilesInfo(video.Id, tilesInfo);
-                        _logger.LogInformation("Finished creation of trickplay files for {0}", mediaPath);
-                    }
-                    else
-                    {
-                        throw new InvalidOperationException("Null trickplay tiles info from CreateTiles.");
-                    }
+                    SaveTilesInfo(video.Id, tilesInfo);
+                    _logger.LogInformation("Finished creation of trickplay files for {0}", mediaPath);
                 }
-                catch (Exception ex)
+                else
                 {
-                    _logger.LogError(ex, "Error while saving trickplay tiles info.");
-
-                    // Make sure no files stay in metadata folders on failure
-                    // if tiles info wasn't saved.
-                    Directory.Delete(outputDir, true);
+                    throw new InvalidOperationException("Null trickplay tiles info from CreateTiles.");
                 }
             }
             catch (Exception ex)
             {
-                _logger.LogError(ex, "Error creating trickplay images.");
-            }
-            finally
-            {
-                _resourcePool.Release();
+                _logger.LogError(ex, "Error while saving trickplay tiles info.");
 
-                if (!string.IsNullOrEmpty(imgTempDir))
-                {
-                    Directory.Delete(imgTempDir, true);
-                }
+                // Make sure no files stay in metadata folders on failure
+                // if tiles info wasn't saved.
+                Directory.Delete(outputDir, true);
             }
         }
-
-        private TrickplayTilesInfo CreateTiles(List<FileSystemMetadata> images, int width, TrickplayOptions options, string workDir, string outputDir)
+        catch (Exception ex)
+        {
+            _logger.LogError(ex, "Error creating trickplay images.");
+        }
+        finally
         {
-            if (images.Count == 0)
+            _resourcePool.Release();
+
+            if (!string.IsNullOrEmpty(imgTempDir))
             {
-                throw new InvalidOperationException("Can't create trickplay from 0 images.");
+                Directory.Delete(imgTempDir, true);
             }
+        }
+    }
 
-            Directory.CreateDirectory(workDir);
+    private TrickplayTilesInfo CreateTiles(List<FileSystemMetadata> images, int width, TrickplayOptions options, string workDir, string outputDir)
+    {
+        if (images.Count == 0)
+        {
+            throw new InvalidOperationException("Can't create trickplay from 0 images.");
+        }
 
-            var tilesInfo = new TrickplayTilesInfo
-            {
-                Width = width,
-                Interval = options.Interval,
-                TileWidth = options.TileWidth,
-                TileHeight = options.TileHeight,
-                TileCount = 0,
-                Bandwidth = 0
-            };
-
-            var firstImg = SKBitmap.Decode(images[0].FullName);
-            if (firstImg == null)
-            {
-                throw new InvalidDataException("Could not decode image data.");
-            }
+        Directory.CreateDirectory(workDir);
 
-            tilesInfo.Height = firstImg.Height;
-            if (tilesInfo.Width != firstImg.Width)
-            {
-                throw new InvalidOperationException("Image width does not match config width.");
-            }
+        var tilesInfo = new TrickplayTilesInfo
+        {
+            Width = width,
+            Interval = options.Interval,
+            TileWidth = options.TileWidth,
+            TileHeight = options.TileHeight,
+            TileCount = 0,
+            Bandwidth = 0
+        };
+
+        var firstImg = SKBitmap.Decode(images[0].FullName);
+        if (firstImg == null)
+        {
+            throw new InvalidDataException("Could not decode image data.");
+        }
 
-            /*
-             * Generate grids of trickplay image tiles
-             */
-            var imgNo = 0;
-            var i = 0;
-            while (i < images.Count)
-            {
-                var tileGrid = new SKBitmap(tilesInfo.Width * tilesInfo.TileWidth, tilesInfo.Height * tilesInfo.TileHeight);
+        tilesInfo.Height = firstImg.Height;
+        if (tilesInfo.Width != firstImg.Width)
+        {
+            throw new InvalidOperationException("Image width does not match config width.");
+        }
+
+        /*
+         * Generate grids of trickplay image tiles
+         */
+        var imgNo = 0;
+        var i = 0;
+        while (i < images.Count)
+        {
+            var tileGrid = new SKBitmap(tilesInfo.Width * tilesInfo.TileWidth, tilesInfo.Height * tilesInfo.TileHeight);
 
-                using (var canvas = new SKCanvas(tileGrid))
+            using (var canvas = new SKCanvas(tileGrid))
+            {
+                for (var y = 0; y < tilesInfo.TileHeight; y++)
                 {
-                    for (var y = 0; y < tilesInfo.TileHeight; y++)
+                    for (var x = 0; x < tilesInfo.TileWidth; x++)
                     {
-                        for (var x = 0; x < tilesInfo.TileWidth; x++)
+                        if (i >= images.Count)
                         {
-                            if (i >= images.Count)
-                            {
-                                break;
-                            }
-
-                            var img = SKBitmap.Decode(images[i].FullName);
-                            if (img == null)
-                            {
-                                throw new InvalidDataException("Could not decode image data.");
-                            }
-
-                            if (tilesInfo.Width != img.Width)
-                            {
-                                throw new InvalidOperationException("Image width does not match config width.");
-                            }
-
-                            if (tilesInfo.Height != img.Height)
-                            {
-                                throw new InvalidOperationException("Image height does not match first image height.");
-                            }
-
-                            canvas.DrawBitmap(img, x * tilesInfo.Width, y * tilesInfo.Height);
-                            tilesInfo.TileCount++;
-                            i++;
+                            break;
                         }
-                    }
-                }
 
-                // Output each tile grid to singular file
-                var tileGridPath = Path.Combine(workDir, $"{imgNo}.jpg");
-                using (var stream = File.OpenWrite(tileGridPath))
-                {
-                    tileGrid.Encode(stream, SKEncodedImageFormat.Jpeg, options.JpegQuality);
-                }
+                        var img = SKBitmap.Decode(images[i].FullName);
+                        if (img == null)
+                        {
+                            throw new InvalidDataException("Could not decode image data.");
+                        }
+
+                        if (tilesInfo.Width != img.Width)
+                        {
+                            throw new InvalidOperationException("Image width does not match config width.");
+                        }
 
-                var bitrate = (int)Math.Ceiling((decimal)new FileInfo(tileGridPath).Length * 8 / tilesInfo.TileWidth / tilesInfo.TileHeight / (tilesInfo.Interval / 1000));
-                tilesInfo.Bandwidth = Math.Max(tilesInfo.Bandwidth, bitrate);
+                        if (tilesInfo.Height != img.Height)
+                        {
+                            throw new InvalidOperationException("Image height does not match first image height.");
+                        }
 
-                imgNo++;
+                        canvas.DrawBitmap(img, x * tilesInfo.Width, y * tilesInfo.Height);
+                        tilesInfo.TileCount++;
+                        i++;
+                    }
+                }
             }
 
-            /*
-             * Move trickplay tiles to output directory
-             */
-            Directory.CreateDirectory(outputDir);
-
-            // Replace existing tile grids if they already exist
-            if (Directory.Exists(outputDir))
+            // Output each tile grid to singular file
+            var tileGridPath = Path.Combine(workDir, $"{imgNo}.jpg");
+            using (var stream = File.OpenWrite(tileGridPath))
             {
-                Directory.Delete(outputDir, true);
+                tileGrid.Encode(stream, SKEncodedImageFormat.Jpeg, options.JpegQuality);
             }
 
-            MoveDirectory(workDir, outputDir);
+            var bitrate = (int)Math.Ceiling((decimal)new FileInfo(tileGridPath).Length * 8 / tilesInfo.TileWidth / tilesInfo.TileHeight / (tilesInfo.Interval / 1000));
+            tilesInfo.Bandwidth = Math.Max(tilesInfo.Bandwidth, bitrate);
 
-            return tilesInfo;
+            imgNo++;
         }
 
-        private bool CanGenerateTrickplay(Video video, int interval)
-        {
-            var videoType = video.VideoType;
-            if (videoType == VideoType.Iso || videoType == VideoType.Dvd || videoType == VideoType.BluRay)
-            {
-                return false;
-            }
-
-            if (video.IsPlaceHolder)
-            {
-                return false;
-            }
+        /*
+         * Move trickplay tiles to output directory
+         */
+        Directory.CreateDirectory(outputDir);
 
-            if (video.IsShortcut)
-            {
-                return false;
-            }
+        // Replace existing tile grids if they already exist
+        if (Directory.Exists(outputDir))
+        {
+            Directory.Delete(outputDir, true);
+        }
 
-            if (!video.IsCompleteMedia)
-            {
-                return false;
-            }
+        MoveDirectory(workDir, outputDir);
 
-            if (!video.RunTimeTicks.HasValue || video.RunTimeTicks.Value < TimeSpan.FromMilliseconds(interval).Ticks)
-            {
-                return false;
-            }
-
-            var libraryOptions = _libraryManager.GetLibraryOptions(video);
-            if (libraryOptions is not null)
-            {
-                if (!libraryOptions.EnableTrickplayImageExtraction)
-                {
-                    return false;
-                }
-            }
-            else
-            {
-                return false;
-            }
+        return tilesInfo;
+    }
 
-            // Can't extract images if there are no video streams
-            return video.GetMediaStreams().Count > 0;
+    private bool CanGenerateTrickplay(Video video, int interval)
+    {
+        var videoType = video.VideoType;
+        if (videoType == VideoType.Iso || videoType == VideoType.Dvd || videoType == VideoType.BluRay)
+        {
+            return false;
         }
 
-        /// <inheritdoc />
-        public Dictionary<int, TrickplayTilesInfo> GetTilesResolutions(Guid itemId)
+        if (video.IsPlaceHolder)
         {
-            return _itemRepo.GetTilesResolutions(itemId);
+            return false;
         }
 
-        /// <inheritdoc />
-        public void SaveTilesInfo(Guid itemId, TrickplayTilesInfo tilesInfo)
+        if (video.IsShortcut)
         {
-            _itemRepo.SaveTilesInfo(itemId, tilesInfo);
+            return false;
         }
 
-        /// <inheritdoc />
-        public Dictionary<Guid, Dictionary<int, TrickplayTilesInfo>> GetTrickplayManifest(BaseItem item)
+        if (!video.IsCompleteMedia)
         {
-            return _itemRepo.GetTrickplayManifest(item);
+            return false;
         }
 
-        /// <inheritdoc />
-        public string GetTrickplayTilePath(BaseItem item, int width, int index)
+        if (!video.RunTimeTicks.HasValue || video.RunTimeTicks.Value < TimeSpan.FromMilliseconds(interval).Ticks)
         {
-            return Path.Combine(GetTrickplayDirectory(item, width), index + ".jpg");
+            return false;
         }
 
-        private string GetTrickplayDirectory(BaseItem item, int? width = null)
+        var libraryOptions = _libraryManager.GetLibraryOptions(video);
+        if (libraryOptions is not null)
         {
-            var path = Path.Combine(item.GetInternalMetadataPath(), "trickplay");
-
-            return width.HasValue ? Path.Combine(path, width.Value.ToString(CultureInfo.InvariantCulture)) : path;
+            if (!libraryOptions.EnableTrickplayImageExtraction)
+            {
+                return false;
+            }
+        }
+        else
+        {
+            return false;
         }
 
-        private void MoveDirectory(string source, string destination)
+        // Can't extract images if there are no video streams
+        return video.GetMediaStreams().Count > 0;
+    }
+
+    /// <inheritdoc />
+    public Dictionary<int, TrickplayTilesInfo> GetTilesResolutions(Guid itemId)
+    {
+        return _itemRepo.GetTilesResolutions(itemId);
+    }
+
+    /// <inheritdoc />
+    public void SaveTilesInfo(Guid itemId, TrickplayTilesInfo tilesInfo)
+    {
+        _itemRepo.SaveTilesInfo(itemId, tilesInfo);
+    }
+
+    /// <inheritdoc />
+    public Dictionary<Guid, Dictionary<int, TrickplayTilesInfo>> GetTrickplayManifest(BaseItem item)
+    {
+        return _itemRepo.GetTrickplayManifest(item);
+    }
+
+    /// <inheritdoc />
+    public string GetTrickplayTilePath(BaseItem item, int width, int index)
+    {
+        return Path.Combine(GetTrickplayDirectory(item, width), index + ".jpg");
+    }
+
+    private string GetTrickplayDirectory(BaseItem item, int? width = null)
+    {
+        var path = Path.Combine(item.GetInternalMetadataPath(), "trickplay");
+
+        return width.HasValue ? Path.Combine(path, width.Value.ToString(CultureInfo.InvariantCulture)) : path;
+    }
+
+    private void MoveDirectory(string source, string destination)
+    {
+        try
         {
-            try
+            Directory.Move(source, destination);
+        }
+        catch (IOException)
+        {
+            // Cross device move requires a copy
+            Directory.CreateDirectory(destination);
+            foreach (string file in Directory.GetFiles(source))
             {
-                Directory.Move(source, destination);
+                File.Copy(file, Path.Join(destination, Path.GetFileName(file)), true);
             }
-            catch (IOException)
-            {
-                // Cross device move requires a copy
-                Directory.CreateDirectory(destination);
-                foreach (string file in Directory.GetFiles(source))
-                {
-                    File.Copy(file, Path.Join(destination, Path.GetFileName(file)), true);
-                }
 
-                Directory.Delete(source, true);
-            }
+            Directory.Delete(source, true);
         }
     }
 }

+ 90 - 91
MediaBrowser.Providers/Trickplay/TrickplayProvider.cs

@@ -10,118 +10,117 @@ using MediaBrowser.Controller.Trickplay;
 using MediaBrowser.Model.Configuration;
 using Microsoft.Extensions.Logging;
 
-namespace MediaBrowser.Providers.Trickplay
+namespace MediaBrowser.Providers.Trickplay;
+
+/// <summary>
+/// Class TrickplayProvider. Provides images and metadata for trickplay
+/// scrubbing previews.
+/// </summary>
+public class TrickplayProvider : ICustomMetadataProvider<Episode>,
+    ICustomMetadataProvider<MusicVideo>,
+    ICustomMetadataProvider<Movie>,
+    ICustomMetadataProvider<Trailer>,
+    ICustomMetadataProvider<Video>,
+    IHasItemChangeMonitor,
+    IHasOrder,
+    IForcedProvider
 {
+    private readonly ILogger<TrickplayProvider> _logger;
+    private readonly IServerConfigurationManager _config;
+    private readonly ITrickplayManager _trickplayManager;
+    private readonly ILibraryManager _libraryManager;
+
     /// <summary>
-    /// Class TrickplayProvider. Provides images and metadata for trickplay
-    /// scrubbing previews.
+    /// Initializes a new instance of the <see cref="TrickplayProvider"/> class.
     /// </summary>
-    public class TrickplayProvider : ICustomMetadataProvider<Episode>,
-        ICustomMetadataProvider<MusicVideo>,
-        ICustomMetadataProvider<Movie>,
-        ICustomMetadataProvider<Trailer>,
-        ICustomMetadataProvider<Video>,
-        IHasItemChangeMonitor,
-        IHasOrder,
-        IForcedProvider
+    /// <param name="logger">The logger.</param>
+    /// <param name="config">The configuration manager.</param>
+    /// <param name="trickplayManager">The trickplay manager.</param>
+    /// <param name="libraryManager">The library manager.</param>
+    public TrickplayProvider(
+        ILogger<TrickplayProvider> logger,
+        IServerConfigurationManager config,
+        ITrickplayManager trickplayManager,
+        ILibraryManager libraryManager)
     {
-        private readonly ILogger<TrickplayProvider> _logger;
-        private readonly IServerConfigurationManager _config;
-        private readonly ITrickplayManager _trickplayManager;
-        private readonly ILibraryManager _libraryManager;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="TrickplayProvider"/> class.
-        /// </summary>
-        /// <param name="logger">The logger.</param>
-        /// <param name="config">The configuration manager.</param>
-        /// <param name="trickplayManager">The trickplay manager.</param>
-        /// <param name="libraryManager">The library manager.</param>
-        public TrickplayProvider(
-            ILogger<TrickplayProvider> logger,
-            IServerConfigurationManager config,
-            ITrickplayManager trickplayManager,
-            ILibraryManager libraryManager)
-        {
-            _logger = logger;
-            _config = config;
-            _trickplayManager = trickplayManager;
-            _libraryManager = libraryManager;
-        }
+        _logger = logger;
+        _config = config;
+        _trickplayManager = trickplayManager;
+        _libraryManager = libraryManager;
+    }
 
-        /// <inheritdoc />
-        public string Name => "Trickplay Provider";
+    /// <inheritdoc />
+    public string Name => "Trickplay Provider";
 
-        /// <inheritdoc />
-        public int Order => 100;
+    /// <inheritdoc />
+    public int Order => 100;
 
-        /// <inheritdoc />
-        public bool HasChanged(BaseItem item, IDirectoryService directoryService)
+    /// <inheritdoc />
+    public bool HasChanged(BaseItem item, IDirectoryService directoryService)
+    {
+        if (item.IsFileProtocol)
         {
-            if (item.IsFileProtocol)
+            var file = directoryService.GetFile(item.Path);
+            if (file is not null && item.DateModified != file.LastWriteTimeUtc)
             {
-                var file = directoryService.GetFile(item.Path);
-                if (file is not null && item.DateModified != file.LastWriteTimeUtc)
-                {
-                    return true;
-                }
+                return true;
             }
-
-            return false;
         }
 
-        /// <inheritdoc />
-        public Task<ItemUpdateType> FetchAsync(Episode item, MetadataRefreshOptions options, CancellationToken cancellationToken)
-        {
-            return FetchInternal(item, options, cancellationToken);
-        }
+        return false;
+    }
 
-        /// <inheritdoc />
-        public Task<ItemUpdateType> FetchAsync(MusicVideo item, MetadataRefreshOptions options, CancellationToken cancellationToken)
-        {
-            return FetchInternal(item, options, cancellationToken);
-        }
+    /// <inheritdoc />
+    public Task<ItemUpdateType> FetchAsync(Episode item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+    {
+        return FetchInternal(item, options, cancellationToken);
+    }
 
-        /// <inheritdoc />
-        public Task<ItemUpdateType> FetchAsync(Movie item, MetadataRefreshOptions options, CancellationToken cancellationToken)
-        {
-            return FetchInternal(item, options, cancellationToken);
-        }
+    /// <inheritdoc />
+    public Task<ItemUpdateType> FetchAsync(MusicVideo item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+    {
+        return FetchInternal(item, options, cancellationToken);
+    }
+
+    /// <inheritdoc />
+    public Task<ItemUpdateType> FetchAsync(Movie item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+    {
+        return FetchInternal(item, options, cancellationToken);
+    }
 
-        /// <inheritdoc />
-        public Task<ItemUpdateType> FetchAsync(Trailer item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+    /// <inheritdoc />
+    public Task<ItemUpdateType> FetchAsync(Trailer item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+    {
+        return FetchInternal(item, options, cancellationToken);
+    }
+
+    /// <inheritdoc />
+    public Task<ItemUpdateType> FetchAsync(Video item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+    {
+        return FetchInternal(item, options, cancellationToken);
+    }
+
+    private async Task<ItemUpdateType> FetchInternal(Video video, MetadataRefreshOptions options, CancellationToken cancellationToken)
+    {
+        var libraryOptions = _libraryManager.GetLibraryOptions(video);
+        bool? enableDuringScan = libraryOptions?.ExtractTrickplayImagesDuringLibraryScan;
+        bool replace = options.ReplaceAllImages;
+
+        if (options.IsAutomated && !enableDuringScan.GetValueOrDefault(false))
         {
-            return FetchInternal(item, options, cancellationToken);
+            return ItemUpdateType.None;
         }
 
-        /// <inheritdoc />
-        public Task<ItemUpdateType> FetchAsync(Video item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+        if (_config.Configuration.TrickplayOptions.ScanBehavior == TrickplayScanBehavior.Blocking)
         {
-            return FetchInternal(item, options, cancellationToken);
+            await _trickplayManager.RefreshTrickplayDataAsync(video, replace, cancellationToken).ConfigureAwait(false);
         }
-
-        private async Task<ItemUpdateType> FetchInternal(Video video, MetadataRefreshOptions options, CancellationToken cancellationToken)
+        else
         {
-            var libraryOptions = _libraryManager.GetLibraryOptions(video);
-            bool? enableDuringScan = libraryOptions?.ExtractTrickplayImagesDuringLibraryScan;
-            bool replace = options.ReplaceAllImages;
-
-            if (options.IsAutomated && !enableDuringScan.GetValueOrDefault(false))
-            {
-                return ItemUpdateType.None;
-            }
-
-            if (_config.Configuration.TrickplayOptions.ScanBehavior == TrickplayScanBehavior.Blocking)
-            {
-                await _trickplayManager.RefreshTrickplayData(video, replace, cancellationToken).ConfigureAwait(false);
-            }
-            else
-            {
-                _ = _trickplayManager.RefreshTrickplayData(video, replace, cancellationToken).ConfigureAwait(false);
-            }
-
-            // The core doesn't need to trigger any save operations over this
-            return ItemUpdateType.None;
+            _ = _trickplayManager.RefreshTrickplayDataAsync(video, replace, cancellationToken).ConfigureAwait(false);
         }
+
+        // The core doesn't need to trigger any save operations over this
+        return ItemUpdateType.None;
     }
 }