diff options
Diffstat (limited to 'Emby.Server.Implementations/ScheduledTasks/Tasks')
13 files changed, 819 insertions, 749 deletions
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs index 031d14776..e912e9f01 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs @@ -76,93 +76,111 @@ public partial class AudioNormalizationTask : IScheduledTask /// <inheritdoc /> public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) { - foreach (var library in _libraryManager.RootFolder.Children) + var numComplete = 0; + var libraries = _libraryManager.RootFolder.Children.Where(library => _libraryManager.GetLibraryOptions(library).EnableLUFSScan).ToArray(); + double percent = 0; + + foreach (var library in libraries) { - var libraryOptions = _libraryManager.GetLibraryOptions(library); - if (!libraryOptions.EnableLUFSScan) - { - continue; - } + var albums = _libraryManager.GetItemList(new InternalItemsQuery { IncludeItemTypes = [BaseItemKind.MusicAlbum], Parent = library, Recursive = true }); - // Album gain - var albums = _libraryManager.GetItemList(new InternalItemsQuery - { - IncludeItemTypes = [BaseItemKind.MusicAlbum], - Parent = library, - Recursive = true - }); + double nextPercent = numComplete + 1; + nextPercent /= libraries.Length; + nextPercent -= percent; + // Split the progress for this single library into two halves: album gain and track gain. + // The first half will be for album gain, the second half for track gain. + nextPercent /= 2; + var albumComplete = 0; foreach (var a in albums) { - if (a.NormalizationGain.HasValue || a.LUFS.HasValue) + if (!a.NormalizationGain.HasValue && !a.LUFS.HasValue) { - continue; + // Album gain + var albumTracks = ((MusicAlbum)a).Tracks.Where(x => x.IsFileProtocol).ToList(); + + // Skip albums that don't have multiple tracks, album gain is useless here + if (albumTracks.Count > 1) + { + _logger.LogInformation("Calculating LUFS for album: {Album} with id: {Id}", a.Name, a.Id); + var tempDir = _applicationPaths.TempDirectory; + Directory.CreateDirectory(tempDir); + var tempFile = Path.Join(tempDir, a.Id + ".concat"); + var inputLines = albumTracks.Select(x => string.Format(CultureInfo.InvariantCulture, "file '{0}'", x.Path.Replace("'", @"'\''", StringComparison.Ordinal))); + await File.WriteAllLinesAsync(tempFile, inputLines, cancellationToken).ConfigureAwait(false); + try + { + a.LUFS = await CalculateLUFSAsync( + string.Format(CultureInfo.InvariantCulture, "-f concat -safe 0 -i \"{0}\"", tempFile), + OperatingSystem.IsWindows(), // Wait for process to exit on Windows before we try deleting the concat file + cancellationToken).ConfigureAwait(false); + } + finally + { + File.Delete(tempFile); + } + } } - // Skip albums that don't have multiple tracks, album gain is useless here - var albumTracks = ((MusicAlbum)a).Tracks.Where(x => x.IsFileProtocol).ToList(); - if (albumTracks.Count <= 1) - { - continue; - } + // Update sub-progress for album gain + albumComplete++; + double albumPercent = albumComplete; + albumPercent /= albums.Count; - _logger.LogInformation("Calculating LUFS for album: {Album} with id: {Id}", a.Name, a.Id); - var tempDir = _applicationPaths.TempDirectory; - Directory.CreateDirectory(tempDir); - var tempFile = Path.Join(tempDir, a.Id + ".concat"); - var inputLines = albumTracks.Select(x => string.Format(CultureInfo.InvariantCulture, "file '{0}'", x.Path.Replace("'", @"'\''", StringComparison.Ordinal))); - await File.WriteAllLinesAsync(tempFile, inputLines, cancellationToken).ConfigureAwait(false); - try - { - a.LUFS = await CalculateLUFSAsync( - string.Format(CultureInfo.InvariantCulture, "-f concat -safe 0 -i \"{0}\"", tempFile), - cancellationToken).ConfigureAwait(false); - } - finally - { - File.Delete(tempFile); - } + progress.Report(100 * (percent + (albumPercent * nextPercent))); } + // Update progress to start at the track gain percent calculation + percent += nextPercent; + _itemRepository.SaveItems(albums, cancellationToken); // Track gain - var tracks = _libraryManager.GetItemList(new InternalItemsQuery - { - MediaTypes = [MediaType.Audio], - IncludeItemTypes = [BaseItemKind.Audio], - Parent = library, - Recursive = true - }); + var tracks = _libraryManager.GetItemList(new InternalItemsQuery { MediaTypes = [MediaType.Audio], IncludeItemTypes = [BaseItemKind.Audio], Parent = library, Recursive = true }); + var tracksComplete = 0; foreach (var t in tracks) { - if (t.NormalizationGain.HasValue || t.LUFS.HasValue || !t.IsFileProtocol) + if (!t.NormalizationGain.HasValue && !t.LUFS.HasValue && t.IsFileProtocol) { - continue; + t.LUFS = await CalculateLUFSAsync( + string.Format(CultureInfo.InvariantCulture, "-i \"{0}\"", t.Path.Replace("\"", "\\\"", StringComparison.Ordinal)), + false, + cancellationToken).ConfigureAwait(false); } - t.LUFS = await CalculateLUFSAsync(string.Format(CultureInfo.InvariantCulture, "-i \"{0}\"", t.Path.Replace("\"", "\\\"", StringComparison.Ordinal)), cancellationToken).ConfigureAwait(false); + // Update sub-progress for track gain + tracksComplete++; + double trackPercent = tracksComplete; + trackPercent /= tracks.Count; + + progress.Report(100 * (percent + (trackPercent * nextPercent))); } _itemRepository.SaveItems(tracks, cancellationToken); + + // Update progress + numComplete++; + percent = numComplete; + percent /= libraries.Length; + + progress.Report(100 * percent); } + + progress.Report(100.0); } /// <inheritdoc /> public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() { - return - [ - new TaskTriggerInfo - { - Type = TaskTriggerInfoType.IntervalTrigger, - IntervalTicks = TimeSpan.FromHours(24).Ticks - } - ]; + yield return new TaskTriggerInfo + { + Type = TaskTriggerInfoType.IntervalTrigger, + IntervalTicks = TimeSpan.FromHours(24).Ticks + }; } - private async Task<float?> CalculateLUFSAsync(string inputArgs, CancellationToken cancellationToken) + private async Task<float?> CalculateLUFSAsync(string inputArgs, bool waitForExit, CancellationToken cancellationToken) { var args = $"-hide_banner {inputArgs} -af ebur128=framelog=verbose -f null -"; @@ -189,18 +207,28 @@ public partial class AudioNormalizationTask : IScheduledTask } using var reader = process.StandardError; - await foreach (var line in reader.ReadAllLinesAsync(cancellationToken)) + float? lufs = null; + await foreach (var line in reader.ReadAllLinesAsync(cancellationToken).ConfigureAwait(false)) { Match match = LUFSRegex().Match(line); - if (match.Success) { - return float.Parse(match.Groups[1].ValueSpan, CultureInfo.InvariantCulture.NumberFormat); + lufs = float.Parse(match.Groups[1].ValueSpan, CultureInfo.InvariantCulture.NumberFormat); + break; } } - _logger.LogError("Failed to find LUFS value in output"); - return null; + if (lufs is null) + { + _logger.LogError("Failed to find LUFS value in output"); + } + + if (waitForExit) + { + await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false); + } + + return lufs; } } } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs index 563e90fbe..f81309560 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs @@ -11,171 +11,157 @@ using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.ScheduledTasks.Tasks +namespace Emby.Server.Implementations.ScheduledTasks.Tasks; + +/// <summary> +/// Class ChapterImagesTask. +/// </summary> +public class ChapterImagesTask : IScheduledTask { + private readonly ILogger<ChapterImagesTask> _logger; + private readonly ILibraryManager _libraryManager; + private readonly IApplicationPaths _appPaths; + private readonly IChapterManager _chapterManager; + private readonly IFileSystem _fileSystem; + private readonly ILocalizationManager _localization; + /// <summary> - /// Class ChapterImagesTask. + /// Initializes a new instance of the <see cref="ChapterImagesTask" /> class. /// </summary> - public class ChapterImagesTask : IScheduledTask + /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> + /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> + /// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param> + /// <param name="chapterManager">Instance of the <see cref="IChapterManager"/> interface.</param> + /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> + /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> + public ChapterImagesTask( + ILogger<ChapterImagesTask> logger, + ILibraryManager libraryManager, + IApplicationPaths appPaths, + IChapterManager chapterManager, + IFileSystem fileSystem, + ILocalizationManager localization) { - private readonly ILogger<ChapterImagesTask> _logger; - private readonly ILibraryManager _libraryManager; - private readonly IItemRepository _itemRepo; - private readonly IApplicationPaths _appPaths; - private readonly IEncodingManager _encodingManager; - private readonly IFileSystem _fileSystem; - private readonly ILocalizationManager _localization; - private readonly IChapterRepository _chapterRepository; - - /// <summary> - /// Initializes a new instance of the <see cref="ChapterImagesTask" /> class. - /// </summary> - /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> - /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> - /// <param name="itemRepo">Instance of the <see cref="IItemRepository"/> interface.</param> - /// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param> - /// <param name="encodingManager">Instance of the <see cref="IEncodingManager"/> interface.</param> - /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> - /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> - /// <param name="chapterRepository">Instance of the <see cref="IChapterRepository"/> interface.</param> - public ChapterImagesTask( - ILogger<ChapterImagesTask> logger, - ILibraryManager libraryManager, - IItemRepository itemRepo, - IApplicationPaths appPaths, - IEncodingManager encodingManager, - IFileSystem fileSystem, - ILocalizationManager localization, - IChapterRepository chapterRepository) - { - _logger = logger; - _libraryManager = libraryManager; - _itemRepo = itemRepo; - _appPaths = appPaths; - _encodingManager = encodingManager; - _fileSystem = fileSystem; - _localization = localization; - _chapterRepository = chapterRepository; - } + _logger = logger; + _libraryManager = libraryManager; + _appPaths = appPaths; + _chapterManager = chapterManager; + _fileSystem = fileSystem; + _localization = localization; + } - /// <inheritdoc /> - public string Name => _localization.GetLocalizedString("TaskRefreshChapterImages"); + /// <inheritdoc /> + public string Name => _localization.GetLocalizedString("TaskRefreshChapterImages"); - /// <inheritdoc /> - public string Description => _localization.GetLocalizedString("TaskRefreshChapterImagesDescription"); + /// <inheritdoc /> + public string Description => _localization.GetLocalizedString("TaskRefreshChapterImagesDescription"); - /// <inheritdoc /> - public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); + /// <inheritdoc /> + public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); - /// <inheritdoc /> - public string Key => "RefreshChapterImages"; + /// <inheritdoc /> + public string Key => "RefreshChapterImages"; - /// <inheritdoc /> - public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + /// <inheritdoc /> + public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + { + yield return new TaskTriggerInfo { - return - [ - new TaskTriggerInfo - { - Type = TaskTriggerInfoType.DailyTrigger, - TimeOfDayTicks = TimeSpan.FromHours(2).Ticks, - MaxRuntimeTicks = TimeSpan.FromHours(4).Ticks - } - ]; - } + Type = TaskTriggerInfoType.DailyTrigger, + TimeOfDayTicks = TimeSpan.FromHours(2).Ticks, + MaxRuntimeTicks = TimeSpan.FromHours(4).Ticks + }; + } - /// <inheritdoc /> - public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + /// <inheritdoc /> + public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + { + var videos = _libraryManager.GetItemList(new InternalItemsQuery { - var videos = _libraryManager.GetItemList(new InternalItemsQuery + MediaTypes = [MediaType.Video], + IsFolder = false, + Recursive = true, + DtoOptions = new DtoOptions(false) { - MediaTypes = [MediaType.Video], - IsFolder = false, - Recursive = true, - DtoOptions = new DtoOptions(false) - { - EnableImages = false - }, - SourceTypes = [SourceType.Library], - IsVirtualItem = false - }) - .OfType<Video>() - .ToList(); + EnableImages = false + }, + SourceTypes = [SourceType.Library], + IsVirtualItem = false + }) + .OfType<Video>() + .ToList(); - var numComplete = 0; + var numComplete = 0; - var failHistoryPath = Path.Combine(_appPaths.CachePath, "chapter-failures.txt"); + var failHistoryPath = Path.Combine(_appPaths.CachePath, "chapter-failures.txt"); - List<string> previouslyFailedImages; + List<string> previouslyFailedImages; - if (File.Exists(failHistoryPath)) + if (File.Exists(failHistoryPath)) + { + try { - try - { - previouslyFailedImages = (await File.ReadAllTextAsync(failHistoryPath, cancellationToken).ConfigureAwait(false)) - .Split('|', StringSplitOptions.RemoveEmptyEntries) - .ToList(); - } - catch (IOException) - { - previouslyFailedImages = new List<string>(); - } + previouslyFailedImages = (await File.ReadAllTextAsync(failHistoryPath, cancellationToken).ConfigureAwait(false)) + .Split('|', StringSplitOptions.RemoveEmptyEntries) + .ToList(); } - else + catch (IOException) { - previouslyFailedImages = new List<string>(); + previouslyFailedImages = []; } + } + else + { + previouslyFailedImages = []; + } - var directoryService = new DirectoryService(_fileSystem); - - foreach (var video in videos) - { - cancellationToken.ThrowIfCancellationRequested(); + var directoryService = new DirectoryService(_fileSystem); - var key = video.Path + video.DateModified.Ticks; + foreach (var video in videos) + { + cancellationToken.ThrowIfCancellationRequested(); - var extract = !previouslyFailedImages.Contains(key, StringComparison.OrdinalIgnoreCase); + var key = video.Path + video.DateModified.Ticks; - try - { - var chapters = _chapterRepository.GetChapters(video.Id); + var extract = !previouslyFailedImages.Contains(key, StringComparison.OrdinalIgnoreCase); - var success = await _encodingManager.RefreshChapterImages(video, directoryService, chapters, extract, true, cancellationToken).ConfigureAwait(false); + try + { + var chapters = _chapterManager.GetChapters(video.Id); - if (!success) - { - previouslyFailedImages.Add(key); + var success = await _chapterManager.RefreshChapterImages(video, directoryService, chapters, extract, true, cancellationToken).ConfigureAwait(false); - var parentPath = Path.GetDirectoryName(failHistoryPath); - if (parentPath is not null) - { - Directory.CreateDirectory(parentPath); - } + if (!success) + { + previouslyFailedImages.Add(key); - string text = string.Join('|', previouslyFailedImages); - await File.WriteAllTextAsync(failHistoryPath, text, cancellationToken).ConfigureAwait(false); + var parentPath = Path.GetDirectoryName(failHistoryPath); + if (parentPath is not null) + { + Directory.CreateDirectory(parentPath); } - numComplete++; - double percent = numComplete; - percent /= videos.Count; - - progress.Report(100 * percent); - } - catch (ObjectDisposedException ex) - { - // TODO Investigate and properly fix. - _logger.LogError(ex, "Object Disposed"); - break; + string text = string.Join('|', previouslyFailedImages); + await File.WriteAllTextAsync(failHistoryPath, text, cancellationToken).ConfigureAwait(false); } + + numComplete++; + double percent = numComplete; + percent /= videos.Count; + + progress.Report(100 * percent); + } + catch (ObjectDisposedException ex) + { + // TODO Investigate and properly fix. + _logger.LogError(ex, "Object Disposed"); + break; } } } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs index fe1832165..1621bbaa1 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs @@ -7,71 +7,70 @@ using MediaBrowser.Model.Activity; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; -namespace Emby.Server.Implementations.ScheduledTasks.Tasks +namespace Emby.Server.Implementations.ScheduledTasks.Tasks; + +/// <summary> +/// Deletes old activity log entries. +/// </summary> +public class CleanActivityLogTask : IScheduledTask, IConfigurableScheduledTask { + private readonly ILocalizationManager _localization; + private readonly IActivityManager _activityManager; + private readonly IServerConfigurationManager _serverConfigurationManager; + /// <summary> - /// Deletes old activity log entries. + /// Initializes a new instance of the <see cref="CleanActivityLogTask"/> class. /// </summary> - public class CleanActivityLogTask : IScheduledTask, IConfigurableScheduledTask + /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> + /// <param name="activityManager">Instance of the <see cref="IActivityManager"/> interface.</param> + /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param> + public CleanActivityLogTask( + ILocalizationManager localization, + IActivityManager activityManager, + IServerConfigurationManager serverConfigurationManager) { - private readonly ILocalizationManager _localization; - private readonly IActivityManager _activityManager; - private readonly IServerConfigurationManager _serverConfigurationManager; - - /// <summary> - /// Initializes a new instance of the <see cref="CleanActivityLogTask"/> class. - /// </summary> - /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> - /// <param name="activityManager">Instance of the <see cref="IActivityManager"/> interface.</param> - /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param> - public CleanActivityLogTask( - ILocalizationManager localization, - IActivityManager activityManager, - IServerConfigurationManager serverConfigurationManager) - { - _localization = localization; - _activityManager = activityManager; - _serverConfigurationManager = serverConfigurationManager; - } + _localization = localization; + _activityManager = activityManager; + _serverConfigurationManager = serverConfigurationManager; + } - /// <inheritdoc /> - public string Name => _localization.GetLocalizedString("TaskCleanActivityLog"); + /// <inheritdoc /> + public string Name => _localization.GetLocalizedString("TaskCleanActivityLog"); - /// <inheritdoc /> - public string Key => "CleanActivityLog"; + /// <inheritdoc /> + public string Key => "CleanActivityLog"; - /// <inheritdoc /> - public string Description => _localization.GetLocalizedString("TaskCleanActivityLogDescription"); + /// <inheritdoc /> + public string Description => _localization.GetLocalizedString("TaskCleanActivityLogDescription"); - /// <inheritdoc /> - public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); + /// <inheritdoc /> + public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); - /// <inheritdoc /> - public bool IsHidden => false; + /// <inheritdoc /> + public bool IsHidden => false; - /// <inheritdoc /> - public bool IsEnabled => true; + /// <inheritdoc /> + public bool IsEnabled => true; - /// <inheritdoc /> - public bool IsLogged => true; + /// <inheritdoc /> + public bool IsLogged => true; - /// <inheritdoc /> - public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + /// <inheritdoc /> + public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + { + var retentionDays = _serverConfigurationManager.Configuration.ActivityLogRetentionDays; + if (!retentionDays.HasValue || retentionDays < 0) { - var retentionDays = _serverConfigurationManager.Configuration.ActivityLogRetentionDays; - if (!retentionDays.HasValue || retentionDays < 0) - { - throw new InvalidOperationException($"Activity Log Retention days must be at least 0. Currently: {retentionDays}"); - } - - var startDate = DateTime.UtcNow.AddDays(-retentionDays.Value); - return _activityManager.CleanAsync(startDate); + throw new InvalidOperationException($"Activity Log Retention days must be at least 0. Currently: {retentionDays}"); } - /// <inheritdoc /> - public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() - { - return []; - } + var startDate = DateTime.UtcNow.AddDays(-retentionDays.Value); + return _activityManager.CleanAsync(startDate); + } + + /// <inheritdoc /> + public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + { + return []; } } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs index 316e4a8f0..7f68f7701 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs @@ -27,7 +27,6 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask private readonly IPlaylistManager _playlistManager; private readonly ILogger<CleanupCollectionAndPlaylistPathsTask> _logger; private readonly IProviderManager _providerManager; - private readonly IFileSystem _fileSystem; /// <summary> /// Initializes a new instance of the <see cref="CleanupCollectionAndPlaylistPathsTask"/> class. @@ -37,21 +36,18 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask /// <param name="playlistManager">Instance of the <see cref="IPlaylistManager"/> interface.</param> /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param> - /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> public CleanupCollectionAndPlaylistPathsTask( ILocalizationManager localization, ICollectionManager collectionManager, IPlaylistManager playlistManager, ILogger<CleanupCollectionAndPlaylistPathsTask> logger, - IProviderManager providerManager, - IFileSystem fileSystem) + IProviderManager providerManager) { _localization = localization; _collectionManager = collectionManager; _playlistManager = playlistManager; _logger = logger; _providerManager = providerManager; - _fileSystem = fileSystem; } /// <inheritdoc /> @@ -84,7 +80,7 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask var collection = collections[index]; _logger.LogDebug("Checking boxset {CollectionName}", collection.Name); - CleanupLinkedChildren(collection, cancellationToken); + await CleanupLinkedChildrenAsync(collection, cancellationToken).ConfigureAwait(false); progress.Report(50D / collections.Length * (index + 1)); } } @@ -104,12 +100,12 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask var playlist = playlists[index]; _logger.LogDebug("Checking playlist {PlaylistName}", playlist.Name); - CleanupLinkedChildren(playlist, cancellationToken); + await CleanupLinkedChildrenAsync(playlist, cancellationToken).ConfigureAwait(false); progress.Report(50D / playlists.Length * (index + 1)); } } - private void CleanupLinkedChildren<T>(T folder, CancellationToken cancellationToken) + private async Task CleanupLinkedChildrenAsync<T>(T folder, CancellationToken cancellationToken) where T : Folder { List<LinkedChild>? itemsToRemove = null; @@ -127,14 +123,17 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask { _logger.LogDebug("Updating {FolderName}", folder.Name); folder.LinkedChildren = folder.LinkedChildren.Except(itemsToRemove).ToArray(); - _providerManager.SaveMetadataAsync(folder, ItemUpdateType.MetadataEdit); - folder.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken); + await _providerManager.SaveMetadataAsync(folder, ItemUpdateType.MetadataEdit).ConfigureAwait(false); + await folder.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); } } /// <inheritdoc /> public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() { - return [new TaskTriggerInfo() { Type = TaskTriggerInfoType.StartupTrigger }]; + yield return new TaskTriggerInfo + { + Type = TaskTriggerInfoType.StartupTrigger, + }; } } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupUserDataTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupUserDataTask.cs new file mode 100644 index 000000000..4156050eb --- /dev/null +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupUserDataTask.cs @@ -0,0 +1,77 @@ +#pragma warning disable RS0030 // Do not use banned APIs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Database.Implementations; +using Jellyfin.Server.Implementations.Item; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace Emby.Server.Implementations.ScheduledTasks.Tasks; + +/// <summary> +/// Task to clean up any detached userdata from the database. +/// </summary> +public class CleanupUserDataTask : IScheduledTask +{ + private readonly ILocalizationManager _localization; + private readonly IDbContextFactory<JellyfinDbContext> _dbProvider; + private readonly ILogger<CleanupUserDataTask> _logger; + + /// <summary> + /// Initializes a new instance of the <see cref="CleanupUserDataTask"/> class. + /// </summary> + /// <param name="localization">The localisation Provider.</param> + /// <param name="dbProvider">The DB context factory.</param> + /// <param name="logger">A logger.</param> + public CleanupUserDataTask(ILocalizationManager localization, IDbContextFactory<JellyfinDbContext> dbProvider, ILogger<CleanupUserDataTask> logger) + { + _localization = localization; + _dbProvider = dbProvider; + _logger = logger; + } + + /// <inheritdoc /> + public string Name => _localization.GetLocalizedString("CleanupUserDataTask"); + + /// <inheritdoc /> + public string Description => _localization.GetLocalizedString("CleanupUserDataTaskDescription"); + + /// <inheritdoc /> + public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); + + /// <inheritdoc /> + public string Key => nameof(CleanupUserDataTask); + + /// <inheritdoc/> + public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + { + const int LimitDays = 90; + var userDataDate = DateTime.UtcNow.AddDays(LimitDays * -1); + var dbContext = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + var detachedUserData = dbContext.UserData.Where(e => e.ItemId == BaseItemRepository.PlaceholderId); + _logger.LogInformation("There are {NoDetached} detached UserData entries.", detachedUserData.Count()); + + detachedUserData = detachedUserData.Where(e => e.RetentionDate < userDataDate); + + _logger.LogInformation("{NoDetached} are older then {Limit} days.", detachedUserData.Count(), LimitDays); + + await detachedUserData.ExecuteDeleteAsync(cancellationToken).ConfigureAwait(false); + } + + progress.Report(100); + } + + /// <inheritdoc/> + public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + { + yield break; + } +} diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs index ff295d9b7..0e77f0102 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs @@ -11,134 +11,133 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.ScheduledTasks.Tasks +namespace Emby.Server.Implementations.ScheduledTasks.Tasks; + +/// <summary> +/// Deletes old cache files. +/// </summary> +public class DeleteCacheFileTask : IScheduledTask, IConfigurableScheduledTask { /// <summary> - /// Deletes old cache files. + /// Gets or sets the application paths. + /// </summary> + /// <value>The application paths.</value> + private readonly IApplicationPaths _applicationPaths; + private readonly ILogger<DeleteCacheFileTask> _logger; + private readonly IFileSystem _fileSystem; + private readonly ILocalizationManager _localization; + + /// <summary> + /// Initializes a new instance of the <see cref="DeleteCacheFileTask" /> class. /// </summary> - public class DeleteCacheFileTask : IScheduledTask, IConfigurableScheduledTask + /// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param> + /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> + /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> + /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> + public DeleteCacheFileTask( + IApplicationPaths appPaths, + ILogger<DeleteCacheFileTask> logger, + IFileSystem fileSystem, + ILocalizationManager localization) { - /// <summary> - /// Gets or sets the application paths. - /// </summary> - /// <value>The application paths.</value> - private readonly IApplicationPaths _applicationPaths; - private readonly ILogger<DeleteCacheFileTask> _logger; - private readonly IFileSystem _fileSystem; - private readonly ILocalizationManager _localization; - - /// <summary> - /// Initializes a new instance of the <see cref="DeleteCacheFileTask" /> class. - /// </summary> - /// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param> - /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> - /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> - /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> - public DeleteCacheFileTask( - IApplicationPaths appPaths, - ILogger<DeleteCacheFileTask> logger, - IFileSystem fileSystem, - ILocalizationManager localization) - { - _applicationPaths = appPaths; - _logger = logger; - _fileSystem = fileSystem; - _localization = localization; - } + _applicationPaths = appPaths; + _logger = logger; + _fileSystem = fileSystem; + _localization = localization; + } - /// <inheritdoc /> - public string Name => _localization.GetLocalizedString("TaskCleanCache"); + /// <inheritdoc /> + public string Name => _localization.GetLocalizedString("TaskCleanCache"); - /// <inheritdoc /> - public string Description => _localization.GetLocalizedString("TaskCleanCacheDescription"); + /// <inheritdoc /> + public string Description => _localization.GetLocalizedString("TaskCleanCacheDescription"); - /// <inheritdoc /> - public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); + /// <inheritdoc /> + public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); - /// <inheritdoc /> - public string Key => "DeleteCacheFiles"; + /// <inheritdoc /> + public string Key => "DeleteCacheFiles"; - /// <inheritdoc /> - public bool IsHidden => false; + /// <inheritdoc /> + public bool IsHidden => false; - /// <inheritdoc /> - public bool IsEnabled => true; + /// <inheritdoc /> + public bool IsEnabled => true; - /// <inheritdoc /> - public bool IsLogged => true; + /// <inheritdoc /> + public bool IsLogged => true; - /// <inheritdoc /> - public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + /// <inheritdoc /> + public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + { + yield return new TaskTriggerInfo { - return - [ - // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfoType.IntervalTrigger, IntervalTicks = TimeSpan.FromHours(24).Ticks } - ]; - } + Type = TaskTriggerInfoType.IntervalTrigger, + IntervalTicks = TimeSpan.FromHours(24).Ticks + }; + } - /// <inheritdoc /> - public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + /// <inheritdoc /> + public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + { + var minDateModified = DateTime.UtcNow.AddDays(-30); + + try + { + DeleteCacheFilesFromDirectory(_applicationPaths.CachePath, minDateModified, progress, cancellationToken); + } + catch (DirectoryNotFoundException) { - var minDateModified = DateTime.UtcNow.AddDays(-30); - - try - { - DeleteCacheFilesFromDirectory(_applicationPaths.CachePath, minDateModified, progress, cancellationToken); - } - catch (DirectoryNotFoundException) - { - // No biggie here. Nothing to delete - } - - progress.Report(90); - - minDateModified = DateTime.UtcNow.AddDays(-1); - - try - { - DeleteCacheFilesFromDirectory(_applicationPaths.TempDirectory, minDateModified, progress, cancellationToken); - } - catch (DirectoryNotFoundException) - { - // No biggie here. Nothing to delete - } - - return Task.CompletedTask; + // No biggie here. Nothing to delete } - /// <summary> - /// Deletes the cache files from directory with a last write time less than a given date. - /// </summary> - /// <param name="directory">The directory.</param> - /// <param name="minDateModified">The min date modified.</param> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The task cancellation token.</param> - private void DeleteCacheFilesFromDirectory(string directory, DateTime minDateModified, IProgress<double> progress, CancellationToken cancellationToken) + progress.Report(90); + + minDateModified = DateTime.UtcNow.AddDays(-1); + + try { - var filesToDelete = _fileSystem.GetFiles(directory, true) - .Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified) - .ToList(); + DeleteCacheFilesFromDirectory(_applicationPaths.TempDirectory, minDateModified, progress, cancellationToken); + } + catch (DirectoryNotFoundException) + { + // No biggie here. Nothing to delete + } - var index = 0; + return Task.CompletedTask; + } - foreach (var file in filesToDelete) - { - double percent = index; - percent /= filesToDelete.Count; + /// <summary> + /// Deletes the cache files from directory with a last write time less than a given date. + /// </summary> + /// <param name="directory">The directory.</param> + /// <param name="minDateModified">The min date modified.</param> + /// <param name="progress">The progress.</param> + /// <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) + .ToList(); - progress.Report(100 * percent); + var index = 0; - cancellationToken.ThrowIfCancellationRequested(); + foreach (var file in filesToDelete) + { + double percent = index; + percent /= filesToDelete.Count; - FileSystemHelper.DeleteFile(_fileSystem, file.FullName, _logger); + progress.Report(100 * percent); - index++; - } + cancellationToken.ThrowIfCancellationRequested(); - FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger); + FileSystemHelper.DeleteFile(_fileSystem, file.FullName, _logger); - progress.Report(100); + index++; } + + FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger); + + progress.Report(100); } } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs index a091c2bd9..699529527 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs @@ -9,93 +9,93 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; -namespace Emby.Server.Implementations.ScheduledTasks.Tasks +namespace Emby.Server.Implementations.ScheduledTasks.Tasks; + +/// <summary> +/// Deletes old log files. +/// </summary> +public class DeleteLogFileTask : IScheduledTask, IConfigurableScheduledTask { + private readonly IConfigurationManager _configurationManager; + private readonly IFileSystem _fileSystem; + private readonly ILocalizationManager _localization; + /// <summary> - /// Deletes old log files. + /// Initializes a new instance of the <see cref="DeleteLogFileTask" /> class. /// </summary> - public class DeleteLogFileTask : IScheduledTask, IConfigurableScheduledTask + /// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param> + /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> + /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> + public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization) { - private readonly IConfigurationManager _configurationManager; - private readonly IFileSystem _fileSystem; - private readonly ILocalizationManager _localization; - - /// <summary> - /// Initializes a new instance of the <see cref="DeleteLogFileTask" /> class. - /// </summary> - /// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param> - /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> - /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> - public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization) - { - _configurationManager = configurationManager; - _fileSystem = fileSystem; - _localization = localization; - } + _configurationManager = configurationManager; + _fileSystem = fileSystem; + _localization = localization; + } - /// <inheritdoc /> - public string Name => _localization.GetLocalizedString("TaskCleanLogs"); + /// <inheritdoc /> + public string Name => _localization.GetLocalizedString("TaskCleanLogs"); - /// <inheritdoc /> - public string Description => string.Format( - CultureInfo.InvariantCulture, - _localization.GetLocalizedString("TaskCleanLogsDescription"), - _configurationManager.CommonConfiguration.LogFileRetentionDays); + /// <inheritdoc /> + public string Description => string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("TaskCleanLogsDescription"), + _configurationManager.CommonConfiguration.LogFileRetentionDays); - /// <inheritdoc /> - public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); + /// <inheritdoc /> + public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); - /// <inheritdoc /> - public string Key => "CleanLogFiles"; + /// <inheritdoc /> + public string Key => "CleanLogFiles"; - /// <inheritdoc /> - public bool IsHidden => false; + /// <inheritdoc /> + public bool IsHidden => false; - /// <inheritdoc /> - public bool IsEnabled => true; + /// <inheritdoc /> + public bool IsEnabled => true; - /// <inheritdoc /> - public bool IsLogged => true; + /// <inheritdoc /> + public bool IsLogged => true; - /// <inheritdoc /> - public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + /// <inheritdoc /> + public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + { + yield return new TaskTriggerInfo { - return - [ - new TaskTriggerInfo { Type = TaskTriggerInfoType.IntervalTrigger, IntervalTicks = TimeSpan.FromHours(24).Ticks } - ]; - } + Type = TaskTriggerInfoType.IntervalTrigger, + IntervalTicks = TimeSpan.FromHours(24).Ticks + }; + } - /// <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); + /// <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); - // Only delete files that serilog doesn't manage (anything that doesn't start with 'log_' - var filesToDelete = _fileSystem.GetFiles(_configurationManager.CommonApplicationPaths.LogDirectoryPath, true) - .Where(f => !f.Name.StartsWith("log_", StringComparison.Ordinal) - && _fileSystem.GetLastWriteTimeUtc(f) < minDateModified) - .ToList(); + // Only delete files that serilog doesn't manage (anything that doesn't start with 'log_' + var filesToDelete = _fileSystem.GetFiles(_configurationManager.CommonApplicationPaths.LogDirectoryPath, true) + .Where(f => !f.Name.StartsWith("log_", StringComparison.Ordinal) + && _fileSystem.GetLastWriteTimeUtc(f) < minDateModified) + .ToList(); - var index = 0; + var index = 0; - foreach (var file in filesToDelete) - { - double percent = index / (double)filesToDelete.Count; + foreach (var file in filesToDelete) + { + double percent = index / (double)filesToDelete.Count; - progress.Report(100 * percent); + progress.Report(100 * percent); - cancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); - _fileSystem.DeleteFile(file.FullName); + _fileSystem.DeleteFile(file.FullName); - index++; - } + index++; + } - progress.Report(100); + progress.Report(100); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs index d0896cc81..9cc2cc512 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs @@ -10,118 +10,115 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.ScheduledTasks.Tasks +namespace Emby.Server.Implementations.ScheduledTasks.Tasks; + +/// <summary> +/// Deletes all transcoding temp files. +/// </summary> +public class DeleteTranscodeFileTask : IScheduledTask, IConfigurableScheduledTask { + private readonly ILogger<DeleteTranscodeFileTask> _logger; + private readonly IConfigurationManager _configurationManager; + private readonly IFileSystem _fileSystem; + private readonly ILocalizationManager _localization; + /// <summary> - /// Deletes all transcoding temp files. + /// Initializes a new instance of the <see cref="DeleteTranscodeFileTask"/> class. /// </summary> - public class DeleteTranscodeFileTask : IScheduledTask, IConfigurableScheduledTask + /// <param name="logger">Instance of the <see cref="ILogger{DeleteTranscodeFileTask}"/> interface.</param> + /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> + /// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param> + /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> + public DeleteTranscodeFileTask( + ILogger<DeleteTranscodeFileTask> logger, + IFileSystem fileSystem, + IConfigurationManager configurationManager, + ILocalizationManager localization) { - private readonly ILogger<DeleteTranscodeFileTask> _logger; - private readonly IConfigurationManager _configurationManager; - private readonly IFileSystem _fileSystem; - private readonly ILocalizationManager _localization; - - /// <summary> - /// Initializes a new instance of the <see cref="DeleteTranscodeFileTask"/> class. - /// </summary> - /// <param name="logger">Instance of the <see cref="ILogger{DeleteTranscodeFileTask}"/> interface.</param> - /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> - /// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param> - /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> - public DeleteTranscodeFileTask( - ILogger<DeleteTranscodeFileTask> logger, - IFileSystem fileSystem, - IConfigurationManager configurationManager, - ILocalizationManager localization) - { - _logger = logger; - _fileSystem = fileSystem; - _configurationManager = configurationManager; - _localization = localization; - } + _logger = logger; + _fileSystem = fileSystem; + _configurationManager = configurationManager; + _localization = localization; + } - /// <inheritdoc /> - public string Name => _localization.GetLocalizedString("TaskCleanTranscode"); + /// <inheritdoc /> + public string Name => _localization.GetLocalizedString("TaskCleanTranscode"); - /// <inheritdoc /> - public string Description => _localization.GetLocalizedString("TaskCleanTranscodeDescription"); + /// <inheritdoc /> + public string Description => _localization.GetLocalizedString("TaskCleanTranscodeDescription"); - /// <inheritdoc /> - public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); + /// <inheritdoc /> + public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); - /// <inheritdoc /> - public string Key => "DeleteTranscodeFiles"; + /// <inheritdoc /> + public string Key => "DeleteTranscodeFiles"; - /// <inheritdoc /> - public bool IsHidden => false; + /// <inheritdoc /> + public bool IsHidden => false; - /// <inheritdoc /> - public bool IsEnabled => true; + /// <inheritdoc /> + public bool IsEnabled => true; - /// <inheritdoc /> - public bool IsLogged => true; + /// <inheritdoc /> + public bool IsLogged => true; - /// <inheritdoc /> - public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + /// <inheritdoc /> + public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + { + yield return new TaskTriggerInfo { - return - [ - new TaskTriggerInfo - { - Type = TaskTriggerInfoType.StartupTrigger - }, - new TaskTriggerInfo - { - Type = TaskTriggerInfoType.IntervalTrigger, - IntervalTicks = TimeSpan.FromHours(24).Ticks - } - ]; - } + Type = TaskTriggerInfoType.StartupTrigger + }; - /// <inheritdoc /> - public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + yield return new TaskTriggerInfo { - var minDateModified = DateTime.UtcNow.AddDays(-1); - progress.Report(50); - - DeleteTempFilesFromDirectory(_configurationManager.GetTranscodePath(), minDateModified, progress, cancellationToken); + Type = TaskTriggerInfoType.IntervalTrigger, + IntervalTicks = TimeSpan.FromHours(24).Ticks + }; + } - return Task.CompletedTask; - } + /// <inheritdoc /> + public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + { + var minDateModified = DateTime.UtcNow.AddDays(-1); + progress.Report(50); - /// <summary> - /// Deletes the transcoded temp files from directory with a last write time less than a given date. - /// </summary> - /// <param name="directory">The directory.</param> - /// <param name="minDateModified">The min date modified.</param> - /// <param name="progress">The progress.</param> - /// <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) - .ToList(); + DeleteTempFilesFromDirectory(_configurationManager.GetTranscodePath(), minDateModified, progress, cancellationToken); - var index = 0; + return Task.CompletedTask; + } - foreach (var file in filesToDelete) - { - double percent = index; - percent /= filesToDelete.Count; + /// <summary> + /// Deletes the transcoded temp files from directory with a last write time less than a given date. + /// </summary> + /// <param name="directory">The directory.</param> + /// <param name="minDateModified">The min date modified.</param> + /// <param name="progress">The progress.</param> + /// <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) + .ToList(); - progress.Report(100 * percent); + var index = 0; - cancellationToken.ThrowIfCancellationRequested(); + foreach (var file in filesToDelete) + { + double percent = index; + percent /= filesToDelete.Count; - FileSystemHelper.DeleteFile(_fileSystem, file.FullName, _logger); + progress.Report(100 * percent); - index++; - } + cancellationToken.ThrowIfCancellationRequested(); - FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger); + FileSystemHelper.DeleteFile(_fileSystem, file.FullName, _logger); - progress.Report(100); + index++; } + + FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger); + + progress.Report(100); } } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/MediaSegmentExtractionTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/MediaSegmentExtractionTask.cs index de1e60d30..51920c5b1 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/MediaSegmentExtractionTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/MediaSegmentExtractionTask.cs @@ -4,10 +4,10 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Enums; -using MediaBrowser.Controller; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.MediaSegments; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; @@ -62,11 +62,11 @@ public class MediaSegmentExtractionTask : IScheduledTask var query = new InternalItemsQuery { - MediaTypes = new[] { MediaType.Video, MediaType.Audio }, + MediaTypes = [MediaType.Video, MediaType.Audio], IsVirtualItem = false, IncludeItemTypes = _itemTypes, DtoOptions = new DtoOptions(true), - SourceTypes = new[] { SourceType.Library }, + SourceTypes = [SourceType.Library], Recursive = true, Limit = pagesize }; @@ -91,7 +91,8 @@ public class MediaSegmentExtractionTask : IScheduledTask // Only local files supported if (item.IsFileProtocol && File.Exists(item.Path)) { - await _mediaSegmentManager.RunSegmentPluginProviders(item, false, cancellationToken).ConfigureAwait(false); + var libraryOptions = _libraryManager.GetLibraryOptions(item); + await _mediaSegmentManager.RunSegmentPluginProviders(item, libraryOptions, false, cancellationToken).ConfigureAwait(false); } // Update progress diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs index 7d4e2377d..bf8ffaf47 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs @@ -2,96 +2,81 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Server.Implementations; +using Jellyfin.Database.Implementations; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.ScheduledTasks.Tasks +namespace Emby.Server.Implementations.ScheduledTasks.Tasks; + +/// <summary> +/// Optimizes Jellyfin's database by issuing a VACUUM command. +/// </summary> +public class OptimizeDatabaseTask : IScheduledTask, IConfigurableScheduledTask { + private readonly ILogger<OptimizeDatabaseTask> _logger; + private readonly ILocalizationManager _localization; + private readonly IJellyfinDatabaseProvider _jellyfinDatabaseProvider; + /// <summary> - /// Optimizes Jellyfin's database by issuing a VACUUM command. + /// Initializes a new instance of the <see cref="OptimizeDatabaseTask" /> class. /// </summary> - public class OptimizeDatabaseTask : IScheduledTask, IConfigurableScheduledTask + /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> + /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> + /// <param name="jellyfinDatabaseProvider">Instance of the JellyfinDatabaseProvider that can be used for provider specific operations.</param> + public OptimizeDatabaseTask( + ILogger<OptimizeDatabaseTask> logger, + ILocalizationManager localization, + IJellyfinDatabaseProvider jellyfinDatabaseProvider) { - private readonly ILogger<OptimizeDatabaseTask> _logger; - private readonly ILocalizationManager _localization; - private readonly IDbContextFactory<JellyfinDbContext> _provider; + _logger = logger; + _localization = localization; + _jellyfinDatabaseProvider = jellyfinDatabaseProvider; + } - /// <summary> - /// Initializes a new instance of the <see cref="OptimizeDatabaseTask" /> class. - /// </summary> - /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> - /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> - /// <param name="provider">Instance of the <see cref="IDbContextFactory{JellyfinDbContext}"/> interface.</param> - public OptimizeDatabaseTask( - ILogger<OptimizeDatabaseTask> logger, - ILocalizationManager localization, - IDbContextFactory<JellyfinDbContext> provider) - { - _logger = logger; - _localization = localization; - _provider = provider; - } + /// <inheritdoc /> + public string Name => _localization.GetLocalizedString("TaskOptimizeDatabase"); + + /// <inheritdoc /> + public string Description => _localization.GetLocalizedString("TaskOptimizeDatabaseDescription"); - /// <inheritdoc /> - public string Name => _localization.GetLocalizedString("TaskOptimizeDatabase"); + /// <inheritdoc /> + public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); - /// <inheritdoc /> - public string Description => _localization.GetLocalizedString("TaskOptimizeDatabaseDescription"); + /// <inheritdoc /> + public string Key => "OptimizeDatabaseTask"; - /// <inheritdoc /> - public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); + /// <inheritdoc /> + public bool IsHidden => false; - /// <inheritdoc /> - public string Key => "OptimizeDatabaseTask"; + /// <inheritdoc /> + public bool IsEnabled => true; - /// <inheritdoc /> - public bool IsHidden => false; + /// <inheritdoc /> + public bool IsLogged => true; - /// <inheritdoc /> - public bool IsEnabled => true; + /// <inheritdoc /> + public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + { + yield return new TaskTriggerInfo + { + Type = TaskTriggerInfoType.IntervalTrigger, + IntervalTicks = TimeSpan.FromHours(24).Ticks + }; + } - /// <inheritdoc /> - public bool IsLogged => true; + /// <inheritdoc /> + public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + { + _logger.LogInformation("Optimizing and vacuuming jellyfin.db..."); - /// <inheritdoc /> - public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + try { - return - [ - // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfoType.IntervalTrigger, IntervalTicks = TimeSpan.FromHours(24).Ticks } - ]; + await _jellyfinDatabaseProvider.RunScheduledOptimisation(cancellationToken).ConfigureAwait(false); } - - /// <inheritdoc /> - public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + catch (Exception e) { - _logger.LogInformation("Optimizing and vacuuming jellyfin.db..."); - - try - { - var context = await _provider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false); - await using (context.ConfigureAwait(false)) - { - if (context.Database.IsSqlite()) - { - await context.Database.ExecuteSqlRawAsync("PRAGMA optimize", cancellationToken).ConfigureAwait(false); - await context.Database.ExecuteSqlRawAsync("VACUUM", cancellationToken).ConfigureAwait(false); - _logger.LogInformation("jellyfin.db optimized successfully!"); - } - else - { - _logger.LogInformation("This database doesn't support optimization"); - } - } - } - catch (Exception e) - { - _logger.LogError(e, "Error while optimizing jellyfin.db"); - } + _logger.LogError(e, "Error while optimizing jellyfin.db"); } } } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs index 2907f18b5..18162ad2f 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs @@ -6,68 +6,64 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; -namespace Emby.Server.Implementations.ScheduledTasks.Tasks +namespace Emby.Server.Implementations.ScheduledTasks.Tasks; + +/// <summary> +/// Class PeopleValidationTask. +/// </summary> +public class PeopleValidationTask : IScheduledTask, IConfigurableScheduledTask { + private readonly ILibraryManager _libraryManager; + private readonly ILocalizationManager _localization; + /// <summary> - /// Class PeopleValidationTask. + /// Initializes a new instance of the <see cref="PeopleValidationTask" /> class. /// </summary> - public class PeopleValidationTask : IScheduledTask, IConfigurableScheduledTask + /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> + /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> + public PeopleValidationTask(ILibraryManager libraryManager, ILocalizationManager localization) { - private readonly ILibraryManager _libraryManager; - private readonly ILocalizationManager _localization; - - /// <summary> - /// Initializes a new instance of the <see cref="PeopleValidationTask" /> class. - /// </summary> - /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> - /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> - public PeopleValidationTask(ILibraryManager libraryManager, ILocalizationManager localization) - { - _libraryManager = libraryManager; - _localization = localization; - } + _libraryManager = libraryManager; + _localization = localization; + } - /// <inheritdoc /> - public string Name => _localization.GetLocalizedString("TaskRefreshPeople"); + /// <inheritdoc /> + public string Name => _localization.GetLocalizedString("TaskRefreshPeople"); - /// <inheritdoc /> - public string Description => _localization.GetLocalizedString("TaskRefreshPeopleDescription"); + /// <inheritdoc /> + public string Description => _localization.GetLocalizedString("TaskRefreshPeopleDescription"); - /// <inheritdoc /> - public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); + /// <inheritdoc /> + public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); - /// <inheritdoc /> - public string Key => "RefreshPeople"; + /// <inheritdoc /> + public string Key => "RefreshPeople"; - /// <inheritdoc /> - public bool IsHidden => false; + /// <inheritdoc /> + public bool IsHidden => false; - /// <inheritdoc /> - public bool IsEnabled => true; + /// <inheritdoc /> + public bool IsEnabled => true; - /// <inheritdoc /> - public bool IsLogged => true; + /// <inheritdoc /> + public bool IsLogged => true; - /// <summary> - /// Creates the triggers that define when the task will run. - /// </summary> - /// <returns>An <see cref="IEnumerable{TaskTriggerInfo}"/> containing the default trigger infos for this task.</returns> - public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + /// <summary> + /// Creates the triggers that define when the task will run. + /// </summary> + /// <returns>An <see cref="IEnumerable{TaskTriggerInfo}"/> containing the default trigger infos for this task.</returns> + public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + { + yield return new TaskTriggerInfo { - return new[] - { - new TaskTriggerInfo - { - Type = TaskTriggerInfoType.IntervalTrigger, - IntervalTicks = TimeSpan.FromDays(7).Ticks - } - }; - } + Type = TaskTriggerInfoType.IntervalTrigger, + IntervalTicks = TimeSpan.FromDays(7).Ticks + }; + } - /// <inheritdoc /> - public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) - { - return _libraryManager.ValidatePeopleAsync(progress, cancellationToken); - } + /// <inheritdoc /> + public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + { + return _libraryManager.ValidatePeopleAsync(progress, cancellationToken); } } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs index b74f4d1b2..31153af20 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs @@ -10,111 +10,115 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.ScheduledTasks.Tasks +namespace Emby.Server.Implementations.ScheduledTasks.Tasks; + +/// <summary> +/// Plugin Update Task. +/// </summary> +public class PluginUpdateTask : IScheduledTask, IConfigurableScheduledTask { + private readonly ILogger<PluginUpdateTask> _logger; + + private readonly IInstallationManager _installationManager; + private readonly ILocalizationManager _localization; + /// <summary> - /// Plugin Update Task. + /// Initializes a new instance of the <see cref="PluginUpdateTask" /> class. /// </summary> - public class PluginUpdateTask : IScheduledTask, IConfigurableScheduledTask + /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> + /// <param name="installationManager">Instance of the <see cref="IInstallationManager"/> interface.</param> + /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> + public PluginUpdateTask(ILogger<PluginUpdateTask> logger, IInstallationManager installationManager, ILocalizationManager localization) { - private readonly ILogger<PluginUpdateTask> _logger; - - private readonly IInstallationManager _installationManager; - private readonly ILocalizationManager _localization; - - /// <summary> - /// Initializes a new instance of the <see cref="PluginUpdateTask" /> class. - /// </summary> - /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> - /// <param name="installationManager">Instance of the <see cref="IInstallationManager"/> interface.</param> - /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> - public PluginUpdateTask(ILogger<PluginUpdateTask> logger, IInstallationManager installationManager, ILocalizationManager localization) - { - _logger = logger; - _installationManager = installationManager; - _localization = localization; - } + _logger = logger; + _installationManager = installationManager; + _localization = localization; + } - /// <inheritdoc /> - public string Name => _localization.GetLocalizedString("TaskUpdatePlugins"); + /// <inheritdoc /> + public string Name => _localization.GetLocalizedString("TaskUpdatePlugins"); - /// <inheritdoc /> - public string Description => _localization.GetLocalizedString("TaskUpdatePluginsDescription"); + /// <inheritdoc /> + public string Description => _localization.GetLocalizedString("TaskUpdatePluginsDescription"); - /// <inheritdoc /> - public string Category => _localization.GetLocalizedString("TasksApplicationCategory"); + /// <inheritdoc /> + public string Category => _localization.GetLocalizedString("TasksApplicationCategory"); - /// <inheritdoc /> - public string Key => "PluginUpdates"; + /// <inheritdoc /> + public string Key => "PluginUpdates"; - /// <inheritdoc /> - public bool IsHidden => false; + /// <inheritdoc /> + public bool IsHidden => false; - /// <inheritdoc /> - public bool IsEnabled => true; + /// <inheritdoc /> + public bool IsEnabled => true; - /// <inheritdoc /> - public bool IsLogged => true; + /// <inheritdoc /> + public bool IsLogged => true; - /// <inheritdoc /> - public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + /// <inheritdoc /> + public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + { + yield return new TaskTriggerInfo { - // At startup - yield return new TaskTriggerInfo { Type = TaskTriggerInfoType.StartupTrigger }; - - // Every so often - yield return new TaskTriggerInfo { Type = TaskTriggerInfoType.IntervalTrigger, IntervalTicks = TimeSpan.FromHours(24).Ticks }; - } + Type = TaskTriggerInfoType.StartupTrigger + }; - /// <inheritdoc /> - public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + yield return new TaskTriggerInfo { - progress.Report(0); + Type = TaskTriggerInfoType.IntervalTrigger, + IntervalTicks = TimeSpan.FromHours(24).Ticks + }; + } - var packageFetchTask = _installationManager.GetAvailablePluginUpdates(cancellationToken); - var packagesToInstall = (await packageFetchTask.ConfigureAwait(false)).ToList(); + /// <inheritdoc /> + public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + { + progress.Report(0); - progress.Report(10); + var packageFetchTask = _installationManager.GetAvailablePluginUpdates(cancellationToken); + var packagesToInstall = (await packageFetchTask.ConfigureAwait(false)).ToList(); - var numComplete = 0; + progress.Report(10); - foreach (var package in packagesToInstall) - { - cancellationToken.ThrowIfCancellationRequested(); + var numComplete = 0; - try - { - await _installationManager.InstallPackage(package, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - // InstallPackage has its own inner cancellation token, so only throw this if it's ours - if (cancellationToken.IsCancellationRequested) - { - throw; - } - } - catch (HttpRequestException ex) - { - _logger.LogError(ex, "Error downloading {0}", package.Name); - } - catch (IOException ex) - { - _logger.LogError(ex, "Error updating {0}", package.Name); - } - catch (InvalidDataException ex) - { - _logger.LogError(ex, "Error updating {0}", package.Name); - } + foreach (var package in packagesToInstall) + { + cancellationToken.ThrowIfCancellationRequested(); - // Update progress - lock (progress) + try + { + await _installationManager.InstallPackage(package, cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + // InstallPackage has its own inner cancellation token, so only throw this if it's ours + if (cancellationToken.IsCancellationRequested) { - progress.Report((90.0 * ++numComplete / packagesToInstall.Count) + 10); + throw; } } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Error downloading {Name}", package.Name); + } + catch (IOException ex) + { + _logger.LogError(ex, "Error updating {Name}", package.Name); + } + catch (InvalidDataException ex) + { + _logger.LogError(ex, "Error updating {Name}", package.Name); + } - progress.Report(100); + // Update progress + lock (progress) + { + progress.Report((90.0 * ++numComplete / packagesToInstall.Count) + 10); + } } + + progress.Report(100); } } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs index 172448dde..1865189d0 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs @@ -7,60 +7,59 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; -namespace Emby.Server.Implementations.ScheduledTasks.Tasks +namespace Emby.Server.Implementations.ScheduledTasks.Tasks; + +/// <summary> +/// Class RefreshMediaLibraryTask. +/// </summary> +public class RefreshMediaLibraryTask : IScheduledTask { /// <summary> - /// Class RefreshMediaLibraryTask. + /// The _library manager. /// </summary> - public class RefreshMediaLibraryTask : IScheduledTask - { - /// <summary> - /// The _library manager. - /// </summary> - private readonly ILibraryManager _libraryManager; - private readonly ILocalizationManager _localization; + private readonly ILibraryManager _libraryManager; + private readonly ILocalizationManager _localization; - /// <summary> - /// Initializes a new instance of the <see cref="RefreshMediaLibraryTask" /> class. - /// </summary> - /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> - /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> - public RefreshMediaLibraryTask(ILibraryManager libraryManager, ILocalizationManager localization) - { - _libraryManager = libraryManager; - _localization = localization; - } + /// <summary> + /// Initializes a new instance of the <see cref="RefreshMediaLibraryTask" /> class. + /// </summary> + /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> + /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> + public RefreshMediaLibraryTask(ILibraryManager libraryManager, ILocalizationManager localization) + { + _libraryManager = libraryManager; + _localization = localization; + } - /// <inheritdoc /> - public string Name => _localization.GetLocalizedString("TaskRefreshLibrary"); + /// <inheritdoc /> + public string Name => _localization.GetLocalizedString("TaskRefreshLibrary"); - /// <inheritdoc /> - public string Description => _localization.GetLocalizedString("TaskRefreshLibraryDescription"); + /// <inheritdoc /> + public string Description => _localization.GetLocalizedString("TaskRefreshLibraryDescription"); - /// <inheritdoc /> - public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); + /// <inheritdoc /> + public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); - /// <inheritdoc /> - public string Key => "RefreshLibrary"; + /// <inheritdoc /> + public string Key => "RefreshLibrary"; - /// <inheritdoc /> - public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + /// <inheritdoc /> + public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + { + yield return new TaskTriggerInfo { - yield return new TaskTriggerInfo - { - Type = TaskTriggerInfoType.IntervalTrigger, - IntervalTicks = TimeSpan.FromHours(12).Ticks - }; - } + Type = TaskTriggerInfoType.IntervalTrigger, + IntervalTicks = TimeSpan.FromHours(12).Ticks + }; + } - /// <inheritdoc /> - public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + /// <inheritdoc /> + public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - progress.Report(0); + progress.Report(0); - return ((LibraryManager)_libraryManager).ValidateMediaLibraryInternal(progress, cancellationToken); - } + await ((LibraryManager)_libraryManager).ValidateMediaLibraryInternal(progress, cancellationToken).ConfigureAwait(false); } } |
