diff --git a/code/Infrastructure.Tests/Verticals/DownloadClient/TestDownloadService.cs b/code/Infrastructure.Tests/Verticals/DownloadClient/TestDownloadService.cs index 190f836..de90ddd 100644 --- a/code/Infrastructure.Tests/Verticals/DownloadClient/TestDownloadService.cs +++ b/code/Infrastructure.Tests/Verticals/DownloadClient/TestDownloadService.cs @@ -40,12 +40,12 @@ public class TestDownloadService : DownloadService public override Task ShouldRemoveFromArrQueueAsync(string hash) => Task.FromResult(new StalledResult()); public override Task BlockUnwantedFilesAsync(string hash, BlocklistType blocklistType, ConcurrentBag patterns, ConcurrentBag regexes) => Task.FromResult(new BlockFilesResult()); - public override Task DeleteDownloadAsync(string hash) => Task.CompletedTask; + public override Task DeleteDownload(string hash) => Task.CompletedTask; public override Task CreateCategoryAsync(string name) => Task.CompletedTask; - public override Task?> GetDownloadsToBeCleanedAsync(List categories) => Task.FromResult?>(null); - public override Task?> GetDownloadsToChangeCategoryAsync(List categories) => Task.FromResult?>(null); - public override Task CleanDownloadsAsync(List downloads, List categoriesToClean, HashSet excludedHashes) => Task.CompletedTask; - public override Task ChangeCategoryForNoHardLinksAsync(List downloads, HashSet excludedHashes) => Task.CompletedTask; + public override List? FilterDownloadsToBeCleanedAsync(List? downloads, List categories) => Task.FromResult?>(null); + public override List? FilterDownloadsToChangeCategoryAsync(List? downloads, List categories) => Task.FromResult?>(null); + public override Task CleanDownloadsAsync(List? downloads, List categoriesToClean, HashSet excludedHashes) => Task.CompletedTask; + public override Task ChangeCategoryForNoHardLinksAsync(List? downloads, HashSet excludedHashes) => Task.CompletedTask; // Expose protected methods for testing public new void ResetStrikesOnProgress(string hash, long downloaded) => base.ResetStrikesOnProgress(hash, downloaded); diff --git a/code/Infrastructure/Verticals/DownloadCleaner/DownloadCleaner.cs b/code/Infrastructure/Verticals/DownloadCleaner/DownloadCleaner.cs index e1f85e8..44232bb 100644 --- a/code/Infrastructure/Verticals/DownloadCleaner/DownloadCleaner.cs +++ b/code/Infrastructure/Verticals/DownloadCleaner/DownloadCleaner.cs @@ -61,31 +61,20 @@ public sealed class DownloadCleaner : GenericHandler } await _downloadService.LoginAsync(); - - List? downloadsToBeCleaned = await _downloadService.GetDownloadsToBeCleanedAsync(_config.Categories); + List? downloads = await _downloadService.GetSeedingDownloads(); List? downloadsToChangeCategory = null; - + if (!string.IsNullOrEmpty(_config.NoHardLinksCategory) && _config.NoHardLinksCategories?.Count > 0) { if (!_hardLinkCategoryCreated) { - _logger.LogTrace("creating category {cat}", _config.NoHardLinksCategory); - + _logger.LogDebug("creating category {cat}", _config.NoHardLinksCategory); + await _downloadService.CreateCategoryAsync(_config.NoHardLinksCategory); _hardLinkCategoryCreated = true; } - _logger.LogTrace("getting downloads to change category"); - downloadsToChangeCategory = await _downloadService.GetDownloadsToChangeCategoryAsync(_config.NoHardLinksCategories); - } - - bool hasDownloadsToClean = downloadsToBeCleaned?.Count > 0; - bool hasDownloadsToChange = downloadsToChangeCategory?.Count > 0; - - if (!hasDownloadsToClean && !hasDownloadsToChange) - { - _logger.LogDebug("no downloads to process"); - return; + _downloadService.FilterDownloadsToChangeCategoryAsync(downloads, _config.NoHardLinksCategories); } // wait for the downloads to appear in the arr queue @@ -95,25 +84,13 @@ public sealed class DownloadCleaner : GenericHandler await ProcessArrConfigAsync(_radarrConfig, InstanceType.Radarr, true); await ProcessArrConfigAsync(_lidarrConfig, InstanceType.Lidarr, true); - if (hasDownloadsToChange) - { - _logger.LogTrace("processing downloads to change category"); - await _downloadService.ChangeCategoryForNoHardLinksAsync(downloadsToChangeCategory, _excludedHashes); - } - else - { - _logger.LogTrace("no downloads found to change category"); - } + _logger.LogTrace("looking for downloads to change category"); + await _downloadService.ChangeCategoryForNoHardLinksAsync(downloadsToChangeCategory, _excludedHashes); - if (hasDownloadsToClean) - { - _logger.LogTrace("processing downloads to be cleaned"); - await _downloadService.CleanDownloadsAsync(downloadsToBeCleaned, _config.Categories, _excludedHashes); - } - else - { - _logger.LogTrace("no downloads found to be cleaned"); - } + List? downloadsToClean = _downloadService.FilterDownloadsToBeCleanedAsync(downloads, _config.Categories); + + _logger.LogTrace("looking for downloads clean"); + await _downloadService.CleanDownloadsAsync(downloadsToClean, _config.Categories, _excludedHashes); } protected override async Task ProcessInstanceAsync(ArrInstance instance, InstanceType instanceType) diff --git a/code/Infrastructure/Verticals/DownloadClient/Deluge/DelugeService.cs b/code/Infrastructure/Verticals/DownloadClient/Deluge/DelugeService.cs index b099814..e7cf2a7 100644 --- a/code/Infrastructure/Verticals/DownloadClient/Deluge/DelugeService.cs +++ b/code/Infrastructure/Verticals/DownloadClient/Deluge/DelugeService.cs @@ -196,7 +196,7 @@ public class DelugeService : DownloadService, IDelugeService return result; } - public override async Task?> GetDownloadsToBeCleanedAsync(List categories) + public override async List? FilterDownloadsToBeCleanedAsync(List? downloads, List categories) { return (await _client.GetStatusForAllTorrents()) ?.Where(x => !string.IsNullOrEmpty(x.Hash)) @@ -206,13 +206,14 @@ public class DelugeService : DownloadService, IDelugeService .ToList(); } - public override Task?> GetDownloadsToChangeCategoryAsync(List categories) + public override List? FilterDownloadsToChangeCategoryAsync(List? downloads, List categories) { throw new NotImplementedException(); } /// - public override async Task CleanDownloadsAsync(List downloads, List categoriesToClean, HashSet excludedHashes) + public override async Task CleanDownloadsAsync(List? downloads, List categoriesToClean, + HashSet excludedHashes) { foreach (TorrentStatus download in downloads) { @@ -252,7 +253,7 @@ public class DelugeService : DownloadService, IDelugeService continue; } - await _dryRunInterceptor.InterceptAsync(DeleteDownloadAsync, download.Hash); + await _dryRunInterceptor.InterceptAsync(DeleteDownload, download.Hash); _logger.LogInformation( "download cleaned | {reason} reached | {name}", @@ -271,14 +272,14 @@ public class DelugeService : DownloadService, IDelugeService throw new NotImplementedException(); } - public override Task ChangeCategoryForNoHardLinksAsync(List downloads, HashSet excludedHashes) + public override Task ChangeCategoryForNoHardLinksAsync(List? downloads, HashSet excludedHashes) { throw new NotImplementedException(); } /// [DryRunSafeguard] - public override async Task DeleteDownloadAsync(string hash) + public override async Task DeleteDownload(string hash) { hash = hash.ToLowerInvariant(); diff --git a/code/Infrastructure/Verticals/DownloadClient/DownloadService.cs b/code/Infrastructure/Verticals/DownloadClient/DownloadService.cs index e3a7111..259576a 100644 --- a/code/Infrastructure/Verticals/DownloadClient/DownloadService.cs +++ b/code/Infrastructure/Verticals/DownloadClient/DownloadService.cs @@ -1,5 +1,4 @@ using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; using Common.Configuration.ContentBlocker; using Common.Configuration.DownloadCleaner; @@ -76,19 +75,22 @@ public abstract class DownloadService : IDownloadService ); /// - public abstract Task DeleteDownloadAsync(string hash); + public abstract Task DeleteDownload(string hash); /// - public abstract Task?> GetDownloadsToBeCleanedAsync(List categories); + public abstract Task?> GetSeedingDownloads(); + + /// + public abstract List? FilterDownloadsToBeCleanedAsync(List? downloads, List categories); /// - public abstract Task?> GetDownloadsToChangeCategoryAsync(List categories); + public abstract List? FilterDownloadsToChangeCategoryAsync(List? downloads, List categories); /// - public abstract Task CleanDownloadsAsync(List downloads, List categoriesToClean, HashSet excludedHashes); + public abstract Task CleanDownloadsAsync(List? downloads, List categoriesToClean, HashSet excludedHashes); /// - public abstract Task ChangeCategoryForNoHardLinksAsync(List downloads, HashSet excludedHashes); + public abstract Task ChangeCategoryForNoHardLinksAsync(List? downloads, HashSet excludedHashes); /// public abstract Task CreateCategoryAsync(string name); diff --git a/code/Infrastructure/Verticals/DownloadClient/DummyDownloadService.cs b/code/Infrastructure/Verticals/DownloadClient/DummyDownloadService.cs index 3d25f4c..5d872c3 100644 --- a/code/Infrastructure/Verticals/DownloadClient/DummyDownloadService.cs +++ b/code/Infrastructure/Verticals/DownloadClient/DummyDownloadService.cs @@ -53,22 +53,22 @@ public class DummyDownloadService : DownloadService throw new NotImplementedException(); } - public override Task?> GetDownloadsToBeCleanedAsync(List categories) + public override List? FilterDownloadsToBeCleanedAsync(List? downloads, List categories) { throw new NotImplementedException(); } - public override Task?> GetDownloadsToChangeCategoryAsync(List categories) + public override List? FilterDownloadsToChangeCategoryAsync(List? downloads, List categories) { throw new NotImplementedException(); } - public override Task CleanDownloadsAsync(List downloads, List categoriesToClean, HashSet excludedHashes) + public override Task CleanDownloadsAsync(List? downloads, List categoriesToClean, HashSet excludedHashes) { throw new NotImplementedException(); } - public override Task ChangeCategoryForNoHardLinksAsync(List downloads, HashSet excludedHashes) + public override Task ChangeCategoryForNoHardLinksAsync(List? downloads, HashSet excludedHashes) { throw new NotImplementedException(); } @@ -78,7 +78,7 @@ public class DummyDownloadService : DownloadService throw new NotImplementedException(); } - public override Task DeleteDownloadAsync(string hash) + public override Task DeleteDownload(string hash) { throw new NotImplementedException(); } diff --git a/code/Infrastructure/Verticals/DownloadClient/IDownloadService.cs b/code/Infrastructure/Verticals/DownloadClient/IDownloadService.cs index c83c8e4..c9dfd72 100644 --- a/code/Infrastructure/Verticals/DownloadClient/IDownloadService.cs +++ b/code/Infrastructure/Verticals/DownloadClient/IDownloadService.cs @@ -32,38 +32,46 @@ public interface IDownloadService : IDisposable ); /// - /// Fetches all downloads that should be cleaned. + /// Fetches all seeding downloads. /// - /// The categories by which to filter the downloads. - /// A list of downloads for the provided categories. - Task?> GetDownloadsToBeCleanedAsync(List categories); + /// A list of downloads that are seeding. + Task?> GetSeedingDownloads(); /// - /// Fetches all downloads that should have their category changed. + /// Filters downloads that should be cleaned. /// + /// The downloads to filter. /// The categories by which to filter the downloads. /// A list of downloads for the provided categories. - Task?> GetDownloadsToChangeCategoryAsync(List categories); - + List? FilterDownloadsToBeCleanedAsync(List? downloads, List categories); + + /// + /// Filters downloads that should have their category changed. + /// + /// The downloads to filter. + /// The categories by which to filter the downloads. + /// A list of downloads for the provided categories. + List? FilterDownloadsToChangeCategoryAsync(List? downloads, List categories); + /// /// Cleans the downloads. /// /// The downloads to clean. /// The categories that should be cleaned. /// The hashes that should not be cleaned. - Task CleanDownloadsAsync(List downloads, List categoriesToClean, HashSet excludedHashes); + Task CleanDownloadsAsync(List? downloads, List categoriesToClean, HashSet excludedHashes); /// /// Changes the category for downloads that have no hardlinks. /// /// The downloads to change. /// - Task ChangeCategoryForNoHardLinksAsync(List downloads, HashSet excludedHashes); + Task ChangeCategoryForNoHardLinksAsync(List? downloads, HashSet excludedHashes); /// /// Deletes a download item. /// - public Task DeleteDownloadAsync(string hash); + public Task DeleteDownload(string hash); /// /// Creates a category. diff --git a/code/Infrastructure/Verticals/DownloadClient/QBittorrent/QBitService.cs b/code/Infrastructure/Verticals/DownloadClient/QBittorrent/QBitService.cs index 44edc12..605650d 100644 --- a/code/Infrastructure/Verticals/DownloadClient/QBittorrent/QBitService.cs +++ b/code/Infrastructure/Verticals/DownloadClient/QBittorrent/QBitService.cs @@ -209,31 +209,42 @@ public class QBitService : DownloadService, IQBitService } /// - public override async Task?> GetDownloadsToBeCleanedAsync(List categories) => + public override async Task?> GetSeedingDownloads() => (await _client.GetTorrentListAsync(new() { Filter = TorrentListFilter.Seeding })) ?.Where(x => !string.IsNullOrEmpty(x.Hash)) - .Where(x => categories.Any(cat => cat.Name.Equals(x.Category, StringComparison.InvariantCultureIgnoreCase))) .Cast() .ToList(); - public override async Task?> GetDownloadsToChangeCategoryAsync(List categories) - { - return (await _client.GetTorrentListAsync(new() - { - Filter = TorrentListFilter.Seeding - })) - ?.Where(x => !string.IsNullOrEmpty(x.Hash)) + /// + public override List? FilterDownloadsToBeCleanedAsync(List? downloads, List categories) => + downloads + ?.Cast() + .Where(x => !string.IsNullOrEmpty(x.Hash)) + .Where(x => categories.Any(cat => cat.Name.Equals(x.Category, StringComparison.InvariantCultureIgnoreCase))) + .Cast() + .ToList(); + + /// + public override List? FilterDownloadsToChangeCategoryAsync(List? downloads, List categories) => + downloads + ?.Cast() + .Where(x => !string.IsNullOrEmpty(x.Hash)) .Where(x => categories.Any(cat => cat.Equals(x.Category, StringComparison.InvariantCultureIgnoreCase))) .Cast() .ToList(); - } /// - public override async Task CleanDownloadsAsync(List downloads, List categoriesToClean, HashSet excludedHashes) + public override async Task CleanDownloadsAsync(List? downloads, List categoriesToClean, + HashSet excludedHashes) { + if (downloads?.Count is null or 0) + { + return; + } + foreach (TorrentInfo download in downloads) { if (string.IsNullOrEmpty(download.Hash)) @@ -286,7 +297,7 @@ public class QBitService : DownloadService, IQBitService continue; } - await _dryRunInterceptor.InterceptAsync(DeleteDownloadAsync, download.Hash); + await _dryRunInterceptor.InterceptAsync(DeleteDownload, download.Hash); _logger.LogInformation( "download cleaned | {reason} reached | {name}", @@ -312,8 +323,13 @@ public class QBitService : DownloadService, IQBitService await _client.AddCategoryAsync(name); } - public override async Task ChangeCategoryForNoHardLinksAsync(List downloads, HashSet excludedHashes) + public override async Task ChangeCategoryForNoHardLinksAsync(List? downloads, HashSet excludedHashes) { + if (downloads?.Count is null or 0) + { + return; + } + if (_downloadCleanerConfig.NoHardLinksIgnoreRootDir) { downloads @@ -405,6 +421,7 @@ public class QBitService : DownloadService, IQBitService } await _dryRunInterceptor.InterceptAsync(ChangeCategory, download.Hash, _downloadCleanerConfig.NoHardLinksCategory); + download.Category = _downloadCleanerConfig.NoHardLinksCategory; _logger.LogInformation("category changed for {name}", download.Name); @@ -414,7 +431,7 @@ public class QBitService : DownloadService, IQBitService /// [DryRunSafeguard] - public override async Task DeleteDownloadAsync(string hash) + public override async Task DeleteDownload(string hash) { await _client.DeleteAsync(hash, deleteDownloadedData: true); } diff --git a/code/Infrastructure/Verticals/DownloadClient/Transmission/TransmissionService.cs b/code/Infrastructure/Verticals/DownloadClient/Transmission/TransmissionService.cs index c4c14e2..59d0854 100644 --- a/code/Infrastructure/Verticals/DownloadClient/Transmission/TransmissionService.cs +++ b/code/Infrastructure/Verticals/DownloadClient/Transmission/TransmissionService.cs @@ -183,7 +183,7 @@ public class TransmissionService : DownloadService, ITransmissionService } /// - public override async Task?> GetDownloadsToBeCleanedAsync(List categories) + public override async List? FilterDownloadsToBeCleanedAsync(List? downloads, List categories) { string[] fields = [ TorrentFields.FILES, @@ -220,13 +220,14 @@ public class TransmissionService : DownloadService, ITransmissionService .ToList(); } - public override Task?> GetDownloadsToChangeCategoryAsync(List categories) + public override List? FilterDownloadsToChangeCategoryAsync(List? downloads, List categories) { throw new NotImplementedException(); } /// - public override async Task CleanDownloadsAsync(List downloads, List categoriesToClean, HashSet excludedHashes) + public override async Task CleanDownloadsAsync(List? downloads, List categoriesToClean, + HashSet excludedHashes) { foreach (TorrentInfo download in downloads) { @@ -294,12 +295,12 @@ public class TransmissionService : DownloadService, ITransmissionService throw new NotImplementedException(); } - public override Task ChangeCategoryForNoHardLinksAsync(List downloads, HashSet excludedHashes) + public override Task ChangeCategoryForNoHardLinksAsync(List? downloads, HashSet excludedHashes) { throw new NotImplementedException(); } - public override async Task DeleteDownloadAsync(string hash) + public override async Task DeleteDownload(string hash) { TorrentInfo? torrent = await GetTorrentAsync(hash);