Browse Source

Fix some warnings

Bond_009 3 years ago
parent
commit
5732e6188c
27 changed files with 194 additions and 93 deletions
  1. 1 1
      Emby.Server.Implementations/ApplicationHost.cs
  2. 28 1
      Emby.Server.Implementations/Channels/ChannelManager.cs
  3. 1 1
      Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
  4. 5 0
      Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
  5. 1 1
      Emby.Server.Implementations/Images/BaseFolderImageProvider.cs
  6. 2 8
      Emby.Server.Implementations/Library/LibraryManager.cs
  7. 32 1
      Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
  8. 30 3
      Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
  9. 1 1
      Emby.Server.Implementations/LiveTv/RefreshGuideScheduledTask.cs
  10. 5 1
      Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
  11. 2 7
      Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
  12. 2 2
      Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
  13. 6 11
      Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
  14. 2 7
      Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
  15. 5 10
      Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
  16. 2 7
      Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
  17. 3 8
      Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
  18. 2 7
      Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
  19. 2 7
      Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
  20. 16 1
      Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
  21. 16 1
      Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs
  22. 16 1
      Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs
  23. 2 2
      MediaBrowser.Controller/Library/ILibraryManager.cs
  24. 2 2
      MediaBrowser.Model/Tasks/IScheduledTask.cs
  25. 3 1
      MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
  26. 6 0
      jellyfin.ruleset
  27. 1 1
      src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs

+ 1 - 1
Emby.Server.Implementations/ApplicationHost.cs

@@ -150,7 +150,7 @@ namespace Emby.Server.Implementations
         /// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
         /// <param name="options">Instance of the <see cref="IStartupOptions"/> interface.</param>
         /// <param name="startupConfig">The <see cref="IConfiguration" /> interface.</param>
-        public ApplicationHost(
+        protected ApplicationHost(
             IServerApplicationPaths applicationPaths,
             ILoggerFactory loggerFactory,
             IStartupOptions options,

+ 28 - 1
Emby.Server.Implementations/Channels/ChannelManager.cs

@@ -39,7 +39,7 @@ namespace Emby.Server.Implementations.Channels
     /// <summary>
     /// The LiveTV channel manager.
     /// </summary>
-    public class ChannelManager : IChannelManager
+    public class ChannelManager : IChannelManager, IDisposable
     {
         private readonly IUserManager _userManager;
         private readonly IUserDataManager _userDataManager;
@@ -52,6 +52,7 @@ namespace Emby.Server.Implementations.Channels
         private readonly IMemoryCache _memoryCache;
         private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
         private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
+        private bool _disposed = false;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ChannelManager"/> class.
@@ -1213,5 +1214,31 @@ namespace Emby.Server.Implementations.Channels
 
             return result;
         }
+
+        /// <inheritdoc />
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        /// Releases unmanaged and optionally managed resources.
+        /// </summary>
+        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+        protected virtual void Dispose(bool disposing)
+        {
+            if (_disposed)
+            {
+                return;
+            }
+
+            if (disposing)
+            {
+                _resourcePool?.Dispose();
+            }
+
+            _disposed = true;
+        }
     }
 }

+ 1 - 1
Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs

@@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.Channels
         public string Key => "RefreshInternetChannels";
 
         /// <inheritdoc />
-        public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+        public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
         {
             var manager = (ChannelManager)_channelManager;
 

+ 5 - 0
Emby.Server.Implementations/Data/SqliteUserDataRepository.cs

@@ -390,6 +390,7 @@ namespace Emby.Server.Implementations.Data
             return userData;
         }
 
+#pragma warning disable CA2215
         /// <inheritdoc/>
         /// <remarks>
         /// There is nothing to dispose here since <see cref="BaseSqliteRepository.WriteLock"/> and
@@ -398,6 +399,10 @@ namespace Emby.Server.Implementations.Data
         /// </remarks>
         protected override void Dispose(bool dispose)
         {
+            // The write lock and connection for the item repository are shared with the user data repository
+            // since they point to the same database. The item repo has responsibility for disposing these two objects,
+            // so the user data repo should not attempt to dispose them as well
         }
+#pragma warning restore CA2215
     }
 }

+ 1 - 1
Emby.Server.Implementations/Images/BaseFolderImageProvider.cs

@@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.Images
     {
         private readonly ILibraryManager _libraryManager;
 
-        public BaseFolderImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager)
+        protected BaseFolderImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager)
             : base(fileSystem, providerManager, applicationPaths, imageProcessor)
         {
             _libraryManager = libraryManager;

+ 2 - 8
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -1007,14 +1007,8 @@ namespace Emby.Server.Implementations.Library
             return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId);
         }
 
-        /// <summary>
-        /// Validate and refresh the People sub-set of the IBN.
-        /// The items are stored in the db but not loaded into memory until actually requested by an operation.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="progress">The progress.</param>
-        /// <returns>Task.</returns>
-        public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
+        /// <inheritdoc />
+        public Task ValidatePeopleAsync(IProgress<double> progress, CancellationToken cancellationToken)
         {
             // Ensure the location is available.
             Directory.CreateDirectory(_configurationManager.ApplicationPaths.PeoplePath);

+ 32 - 1
Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -24,7 +24,7 @@ using Microsoft.Extensions.Logging;
 
 namespace Emby.Server.Implementations.LiveTv.EmbyTV
 {
-    public class EncodedRecorder : IRecorder
+    public class EncodedRecorder : IRecorder, IDisposable
     {
         private readonly ILogger _logger;
         private readonly IMediaEncoder _mediaEncoder;
@@ -36,6 +36,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         private Stream _logFileStream;
         private string _targetPath;
         private Process _process;
+        private bool _disposed = false;
 
         public EncodedRecorder(
             ILogger logger,
@@ -323,5 +324,35 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 _logger.LogError(ex, "Error reading ffmpeg recording log");
             }
         }
+
+        /// <inheritdoc />
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        /// Releases unmanaged and optionally managed resources.
+        /// </summary>
+        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+        protected virtual void Dispose(bool disposing)
+        {
+            if (_disposed)
+            {
+                return;
+            }
+
+            if (disposing)
+            {
+                _logFileStream?.Dispose();
+                _process?.Dispose();
+            }
+
+            _logFileStream = null;
+            _process = null;
+
+            _disposed = true;
+        }
     }
 }

+ 30 - 3
Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs

@@ -28,7 +28,7 @@ using Microsoft.Extensions.Logging;
 
 namespace Emby.Server.Implementations.LiveTv.Listings
 {
-    public class SchedulesDirect : IListingsProvider
+    public class SchedulesDirect : IListingsProvider, IDisposable
     {
         private const string ApiUrl = "https://json.schedulesdirect.org/20141201";
 
@@ -39,6 +39,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
         private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new ConcurrentDictionary<string, NameValuePair>();
         private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
         private DateTime _lastErrorResponse;
+        private bool _disposed = false;
 
         public SchedulesDirect(
             ILogger<SchedulesDirect> logger,
@@ -58,8 +59,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
         {
             var dates = new List<string>();
 
-            var start = new List<DateTime> { startDateUtc, startDateUtc.ToLocalTime() }.Min().Date;
-            var end = new List<DateTime> { endDateUtc, endDateUtc.ToLocalTime() }.Max().Date;
+            var start = new[] { startDateUtc, startDateUtc.ToLocalTime() }.Min().Date;
+            var end = new[] { endDateUtc, endDateUtc.ToLocalTime() }.Max().Date;
 
             while (start <= end)
             {
@@ -822,5 +823,31 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
             return list;
         }
+
+        /// <inheritdoc />
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        /// Releases unmanaged and optionally managed resources.
+        /// </summary>
+        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+        protected virtual void Dispose(bool disposing)
+        {
+            if (_disposed)
+            {
+                return;
+            }
+
+            if (disposing)
+            {
+                _tokenSemaphore?.Dispose();
+            }
+
+            _disposed = true;
+        }
     }
 }

+ 1 - 1
Emby.Server.Implementations/LiveTv/RefreshGuideScheduledTask.cs

@@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.LiveTv
         public string Key => "RefreshGuide";
 
         /// <inheritdoc />
-        public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+        public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
         {
             var manager = (LiveTvManager)_liveTvManager;
 

+ 5 - 1
Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs

@@ -414,7 +414,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
                     CurrentCancellationTokenSource.CancelAfter(TimeSpan.FromTicks(options.MaxRuntimeTicks.Value));
                 }
 
-                await ScheduledTask.Execute(CurrentCancellationTokenSource.Token, progress).ConfigureAwait(false);
+                await ScheduledTask.ExecuteAsync(progress, CurrentCancellationTokenSource.Token).ConfigureAwait(false);
 
                 status = TaskCompletionStatus.Completed;
             }
@@ -757,6 +757,10 @@ namespace Emby.Server.Implementations.ScheduledTasks
                 var trigger = triggerInfo.Item2;
                 trigger.Triggered -= OnTriggerTriggered;
                 trigger.Stop();
+                if (trigger is IDisposable disposable)
+                {
+                    disposable.Dispose();
+                }
             }
         }
     }

+ 2 - 7
Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs

@@ -88,13 +88,8 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
             };
         }
 
-        /// <summary>
-        /// Returns the task to be executed.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="progress">The progress.</param>
-        /// <returns>Task.</returns>
-        public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+        /// <inheritdoc />
+        public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
         {
             var videos = _libraryManager.GetItemList(new InternalItemsQuery
             {

+ 2 - 2
Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs

@@ -57,12 +57,12 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
         public bool IsLogged => true;
 
         /// <inheritdoc />
-        public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+        public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
         {
             var retentionDays = _serverConfigurationManager.Configuration.ActivityLogRetentionDays;
             if (!retentionDays.HasValue || retentionDays < 0)
             {
-                throw new Exception($"Activity Log Retention days must be at least 0. Currently: {retentionDays}");
+                throw new InvalidOperationException($"Activity Log Retention days must be at least 0. Currently: {retentionDays}");
             }
 
             var startDate = DateTime.UtcNow.AddDays(-retentionDays.Value);

+ 6 - 11
Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs

@@ -79,19 +79,14 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
             };
         }
 
-        /// <summary>
-        /// Returns the task to be executed.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="progress">The progress.</param>
-        /// <returns>Task.</returns>
-        public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+        /// <inheritdoc />
+        public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
         {
             var minDateModified = DateTime.UtcNow.AddDays(-30);
 
             try
             {
-                DeleteCacheFilesFromDirectory(cancellationToken, _applicationPaths.CachePath, minDateModified, progress);
+                DeleteCacheFilesFromDirectory(_applicationPaths.CachePath, minDateModified, progress, cancellationToken);
             }
             catch (DirectoryNotFoundException)
             {
@@ -104,7 +99,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
 
             try
             {
-                DeleteCacheFilesFromDirectory(cancellationToken, _applicationPaths.TempDirectory, minDateModified, progress);
+                DeleteCacheFilesFromDirectory(_applicationPaths.TempDirectory, minDateModified, progress, cancellationToken);
             }
             catch (DirectoryNotFoundException)
             {
@@ -117,11 +112,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
         /// <summary>
         /// Deletes the cache files from directory with a last write time less than a given date.
         /// </summary>
-        /// <param name="cancellationToken">The task cancellation token.</param>
         /// <param name="directory">The directory.</param>
         /// <param name="minDateModified">The min date modified.</param>
         /// <param name="progress">The progress.</param>
-        private void DeleteCacheFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress<double> progress)
+        /// <param name="cancellationToken">The task cancellation token.</param>
+        private void DeleteCacheFilesFromDirectory(string directory, DateTime minDateModified, IProgress<double> progress, CancellationToken cancellationToken)
         {
             var filesToDelete = _fileSystem.GetFiles(directory, true)
                 .Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)

+ 2 - 7
Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs

@@ -69,13 +69,8 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
             };
         }
 
-        /// <summary>
-        /// Returns the task to be executed.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="progress">The progress.</param>
-        /// <returns>Task.</returns>
-        public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+        /// <inheritdoc />
+        public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
         {
             // Delete log files more than n days old
             var minDateModified = DateTime.UtcNow.AddDays(-_configurationManager.CommonConfiguration.LogFileRetentionDays);

+ 5 - 10
Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs

@@ -78,18 +78,13 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
             };
         }
 
-        /// <summary>
-        /// Returns the task to be executed.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="progress">The progress.</param>
-        /// <returns>Task.</returns>
-        public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+        /// <inheritdoc />
+        public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
         {
             var minDateModified = DateTime.UtcNow.AddDays(-1);
             progress.Report(50);
 
-            DeleteTempFilesFromDirectory(cancellationToken, _configurationManager.GetTranscodePath(), minDateModified, progress);
+            DeleteTempFilesFromDirectory(_configurationManager.GetTranscodePath(), minDateModified, progress, cancellationToken);
 
             return Task.CompletedTask;
         }
@@ -97,11 +92,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
         /// <summary>
         /// Deletes the transcoded temp files from directory with a last write time less than a given date.
         /// </summary>
-        /// <param name="cancellationToken">The task cancellation token.</param>
         /// <param name="directory">The directory.</param>
         /// <param name="minDateModified">The min date modified.</param>
         /// <param name="progress">The progress.</param>
-        private void DeleteTempFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress<double> progress)
+        /// <param name="cancellationToken">The task cancellation token.</param>
+        private void DeleteTempFilesFromDirectory(string directory, DateTime minDateModified, IProgress<double> progress, CancellationToken cancellationToken)
         {
             var filesToDelete = _fileSystem.GetFiles(directory, true)
                 .Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)

+ 2 - 7
Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs

@@ -69,13 +69,8 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
             };
         }
 
-        /// <summary>
-        /// Returns the task to be executed.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="progress">The progress.</param>
-        /// <returns>Task.</returns>
-        public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+        /// <inheritdoc />
+        public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
         {
             _logger.LogInformation("Optimizing and vacuuming jellyfin.db...");
 

+ 3 - 8
Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs

@@ -62,15 +62,10 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
             };
         }
 
-        /// <summary>
-        /// Returns the task to be executed.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="progress">The progress.</param>
-        /// <returns>Task.</returns>
-        public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+        /// <inheritdoc />
+        public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
         {
-            return _libraryManager.ValidatePeople(cancellationToken, progress);
+            return _libraryManager.ValidatePeopleAsync(progress, cancellationToken);
         }
     }
 }

+ 2 - 7
Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs

@@ -68,13 +68,8 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
             yield return new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks };
         }
 
-        /// <summary>
-        /// Update installed plugins.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="progress">The progress.</param>
-        /// <returns><see cref="Task" />.</returns>
-        public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+        /// <inheritdoc />
+        public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
         {
             progress.Report(0);
 

+ 2 - 7
Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs

@@ -58,13 +58,8 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
             };
         }
 
-        /// <summary>
-        /// Executes the internal.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="progress">The progress.</param>
-        /// <returns>Task.</returns>
-        public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+        /// <inheritdoc />
+        public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
 

+ 16 - 1
Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs

@@ -8,10 +8,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
     /// <summary>
     /// Represents a task trigger that fires everyday.
     /// </summary>
-    public sealed class DailyTrigger : ITaskTrigger
+    public sealed class DailyTrigger : ITaskTrigger, IDisposable
     {
         private readonly TimeSpan _timeOfDay;
         private Timer? _timer;
+        private bool _disposed = false;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="DailyTrigger"/> class.
@@ -71,6 +72,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
         private void DisposeTimer()
         {
             _timer?.Dispose();
+            _timer = null;
         }
 
         /// <summary>
@@ -80,5 +82,18 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
         {
             Triggered?.Invoke(this, EventArgs.Empty);
         }
+
+        /// <inheritdoc />
+        public void Dispose()
+        {
+            if (_disposed)
+            {
+                return;
+            }
+
+            DisposeTimer();
+
+            _disposed = true;
+        }
     }
 }

+ 16 - 1
Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs

@@ -9,11 +9,12 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
     /// <summary>
     /// Represents a task trigger that runs repeatedly on an interval.
     /// </summary>
-    public sealed class IntervalTrigger : ITaskTrigger
+    public sealed class IntervalTrigger : ITaskTrigger, IDisposable
     {
         private readonly TimeSpan _interval;
         private DateTime _lastStartDate;
         private Timer? _timer;
+        private bool _disposed = false;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="IntervalTrigger"/> class.
@@ -89,6 +90,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
         private void DisposeTimer()
         {
             _timer?.Dispose();
+            _timer = null;
         }
 
         /// <summary>
@@ -104,5 +106,18 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
                 Triggered(this, EventArgs.Empty);
             }
         }
+
+        /// <inheritdoc />
+        public void Dispose()
+        {
+            if (_disposed)
+            {
+                return;
+            }
+
+            DisposeTimer();
+
+            _disposed = true;
+        }
     }
 }

+ 16 - 1
Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs

@@ -8,11 +8,12 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
     /// <summary>
     /// Represents a task trigger that fires on a weekly basis.
     /// </summary>
-    public sealed class WeeklyTrigger : ITaskTrigger
+    public sealed class WeeklyTrigger : ITaskTrigger, IDisposable
     {
         private readonly TimeSpan _timeOfDay;
         private readonly DayOfWeek _dayOfWeek;
         private Timer? _timer;
+        private bool _disposed = false;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="WeeklyTrigger"/> class.
@@ -94,6 +95,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
         private void DisposeTimer()
         {
             _timer?.Dispose();
+            _timer = null;
         }
 
         /// <summary>
@@ -103,5 +105,18 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
         {
             Triggered?.Invoke(this, EventArgs.Empty);
         }
+
+        /// <inheritdoc />
+        public void Dispose()
+        {
+            if (_disposed)
+            {
+                return;
+            }
+
+            DisposeTimer();
+
+            _disposed = true;
+        }
     }
 }

+ 2 - 2
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -138,10 +138,10 @@ namespace MediaBrowser.Controller.Library
         /// Validate and refresh the People sub-set of the IBN.
         /// The items are stored in the db but not loaded into memory until actually requested by an operation.
         /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="progress">The progress.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress);
+        Task ValidatePeopleAsync(IProgress<double> progress, CancellationToken cancellationToken);
 
         /// <summary>
         /// Reloads the root media folder.

+ 2 - 2
MediaBrowser.Model/Tasks/IScheduledTask.cs

@@ -36,10 +36,10 @@ namespace MediaBrowser.Model.Tasks
         /// <summary>
         /// Executes the task.
         /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="progress">The progress.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task Execute(CancellationToken cancellationToken, IProgress<double> progress);
+        Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the default triggers that define when the task will run.

+ 3 - 1
MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs

@@ -63,7 +63,8 @@ namespace MediaBrowser.Providers.MediaInfo
             return _config.GetConfiguration<SubtitleOptions>("subtitles");
         }
 
-        public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+        /// <inheritdoc />
+        public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
         {
             var options = GetOptions();
 
@@ -210,6 +211,7 @@ namespace MediaBrowser.Providers.MediaInfo
             return true;
         }
 
+        /// <inheritdoc />
         public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
         {
             return new[]

+ 6 - 0
jellyfin.ruleset

@@ -57,6 +57,10 @@
   </Rules>
 
   <Rules AnalyzerId="Microsoft.CodeAnalysis.NetAnalyzers" RuleNamespace="Microsoft.Design">
+    <!-- error on CA1001: Types that own disposable fields should be disposable -->
+    <Rule Id="CA1001" Action="Error" />
+    <!-- error on CA1012: Abstract types should not have public constructors -->
+    <Rule Id="CA1012" Action="Error" />
     <!-- error on CA1063: Implement IDisposable correctly -->
     <Rule Id="CA1063" Action="Error" />
     <!-- error on CA1305: Specify IFormatProvider -->
@@ -80,6 +84,8 @@
     <!-- error on CA2016: Forward the CancellationToken parameter to methods that take one
         or pass in 'CancellationToken.None' explicitly to indicate intentionally not propagating the token -->
     <Rule Id="CA2016" Action="Error" />
+    <!-- error on CA2215: Dispose methods should call base class dispose -->
+    <Rule Id="CA2215" Action="Error" />
     <!-- error on CA2254: Template should be a static expression -->
     <Rule Id="CA2254" Action="Error" />
 

+ 1 - 1
src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs

@@ -51,7 +51,7 @@ public class KeyframeExtractionScheduledTask : IScheduledTask
     public string Category => _localizationManager.GetLocalizedString("TasksLibraryCategory");
 
     /// <inheritdoc />
-    public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+    public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
     {
         var query = new InternalItemsQuery
         {