diff --git a/code/Common/Configuration/ContentBlocker/ContentBlockerConfig.cs b/code/Common/Configuration/ContentBlocker/ContentBlockerConfig.cs
deleted file mode 100644
index 15ea46ee..00000000
--- a/code/Common/Configuration/ContentBlocker/ContentBlockerConfig.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-namespace Common.Configuration.ContentBlocker;
-
-public sealed record ContentBlockerConfig : IJobConfig
-{
- public bool Enabled { get; init; }
-
- public string CronExpression { get; init; } = "0 0/5 * * * ?";
-
- public bool IgnorePrivate { get; init; }
-
- public bool DeletePrivate { get; init; }
-
- // TODO
- public string IgnoredDownloadsPath { get; init; } = string.Empty;
-
- public BlocklistSettings Sonarr { get; init; } = new();
-
- public BlocklistSettings Radarr { get; init; } = new();
-
- public BlocklistSettings Lidarr { get; init; } = new();
-
- public void Validate()
- {
- }
-}
-
-public record BlocklistSettings
-{
- public bool Enabled { get; init; }
-
- public BlocklistType Type { get; init; }
-
- public string? Path { get; init; }
-}
\ No newline at end of file
diff --git a/code/Common/Configuration/DTOs/ContentBlocker/ContentBlockerConfigDto.cs b/code/Common/Configuration/DTOs/ContentBlocker/ContentBlockerConfigDto.cs
index 8e285c5e..632a1d33 100644
--- a/code/Common/Configuration/DTOs/ContentBlocker/ContentBlockerConfigDto.cs
+++ b/code/Common/Configuration/DTOs/ContentBlocker/ContentBlockerConfigDto.cs
@@ -1,4 +1,4 @@
-using Common.Configuration.ContentBlocker;
+using Common.Configuration.QueueCleaner;
namespace Common.Configuration.DTOs.ContentBlocker;
diff --git a/code/Common/Configuration/QueueCleaner/BlocklistSettings.cs b/code/Common/Configuration/QueueCleaner/BlocklistSettings.cs
new file mode 100644
index 00000000..536da6b9
--- /dev/null
+++ b/code/Common/Configuration/QueueCleaner/BlocklistSettings.cs
@@ -0,0 +1,11 @@
+namespace Common.Configuration.QueueCleaner;
+
+///
+/// Settings for a blocklist
+///
+public sealed record BlocklistSettings
+{
+ public BlocklistType BlocklistType { get; init; }
+
+ public string? BlocklistPath { get; init; }
+}
\ No newline at end of file
diff --git a/code/Common/Configuration/ContentBlocker/BlocklistType.cs b/code/Common/Configuration/QueueCleaner/BlocklistType.cs
similarity index 53%
rename from code/Common/Configuration/ContentBlocker/BlocklistType.cs
rename to code/Common/Configuration/QueueCleaner/BlocklistType.cs
index fa0cee07..4d371089 100644
--- a/code/Common/Configuration/ContentBlocker/BlocklistType.cs
+++ b/code/Common/Configuration/QueueCleaner/BlocklistType.cs
@@ -1,4 +1,4 @@
-namespace Common.Configuration.ContentBlocker;
+namespace Common.Configuration.QueueCleaner;
public enum BlocklistType
{
diff --git a/code/Common/Configuration/QueueCleaner/ContentBlockerConfig.cs b/code/Common/Configuration/QueueCleaner/ContentBlockerConfig.cs
new file mode 100644
index 00000000..6571efb0
--- /dev/null
+++ b/code/Common/Configuration/QueueCleaner/ContentBlockerConfig.cs
@@ -0,0 +1,20 @@
+namespace Common.Configuration.QueueCleaner;
+
+public sealed record ContentBlockerConfig
+{
+ public bool Enabled { get; init; }
+
+ public bool IgnorePrivate { get; init; }
+
+ public bool DeletePrivate { get; init; }
+
+ public BlocklistSettings Sonarr { get; init; } = new();
+
+ public BlocklistSettings Radarr { get; init; } = new();
+
+ public BlocklistSettings Lidarr { get; init; } = new();
+
+ public void Validate()
+ {
+ }
+}
diff --git a/code/Common/Configuration/QueueCleaner/QueueCleanerConfig.cs b/code/Common/Configuration/QueueCleaner/QueueCleanerConfig.cs
index 7dbee54c..bd1b2ad5 100644
--- a/code/Common/Configuration/QueueCleaner/QueueCleanerConfig.cs
+++ b/code/Common/Configuration/QueueCleaner/QueueCleanerConfig.cs
@@ -12,94 +12,123 @@ public sealed record QueueCleanerConfig : IJobConfig
public string CronExpression { get; init; } = "0 0/5 * * * ?";
- public bool RunSequentially { get; init; }
-
public string IgnoredDownloadsPath { get; init; } = string.Empty;
-
- public ushort FailedImportMaxStrikes { get; init; }
-
- public bool FailedImportIgnorePrivate { get; init; }
-
- public bool FailedImportDeletePrivate { get; init; }
- public IReadOnlyList FailedImportIgnorePatterns { get; init; } = [];
+ public FailedImportConfig FailedImport { get; init; } = new();
- public ushort StalledMaxStrikes { get; init; }
+ public StalledConfig Stalled { get; init; } = new();
- public bool StalledResetStrikesOnProgress { get; init; }
+ public SlowConfig Slow { get; init; } = new();
- public bool StalledIgnorePrivate { get; init; }
-
- public bool StalledDeletePrivate { get; init; }
-
- public ushort DownloadingMetadataMaxStrikes { get; init; }
-
- public ushort SlowMaxStrikes { get; init; }
-
- public bool SlowResetStrikesOnProgress { get; init; }
-
- public bool SlowIgnorePrivate { get; init; }
-
- public bool SlowDeletePrivate { get; init; }
-
- public string SlowMinSpeed { get; init; } = string.Empty;
-
- [JsonIgnore]
- public ByteSize SlowMinSpeedByteSize => string.IsNullOrEmpty(SlowMinSpeed) ? new ByteSize(0) : ByteSize.Parse(SlowMinSpeed);
-
- public double SlowMaxTime { get; init; }
-
- public string SlowIgnoreAboveSize { get; init; } = string.Empty;
-
- [JsonIgnore]
- public ByteSize? SlowIgnoreAboveSizeByteSize => string.IsNullOrEmpty(SlowIgnoreAboveSize) ? null : ByteSize.Parse(SlowIgnoreAboveSize);
+ public ContentBlockerConfig ContentBlocker { get; init; } = new();
public void Validate()
{
- if (FailedImportMaxStrikes is > 0 and < 3)
- {
- throw new ValidationException($"the minimum value for {SectionName.ToUpperInvariant()}__IMPORT_FAILED_MAX_STRIKES must be 3");
- }
+ FailedImport.Validate(SectionName);
+ Stalled.Validate(SectionName);
+ Slow.Validate(SectionName);
+ ContentBlocker.Validate();
+ }
+}
- if (StalledMaxStrikes is > 0 and < 3)
+public sealed record FailedImportConfig
+{
+ public ushort MaxStrikes { get; init; }
+
+ public bool IgnorePrivate { get; init; }
+
+ public bool DeletePrivate { get; init; }
+
+ public IReadOnlyList IgnoredPatterns { get; init; } = [];
+
+ public void Validate(string sectionName)
+ {
+ if (MaxStrikes is > 0 and < 3)
{
- throw new ValidationException($"the minimum value for {SectionName.ToUpperInvariant()}__STALLED_MAX_STRIKES must be 3");
+ throw new ValidationException($"the minimum value for {sectionName.ToUpperInvariant()}__FAILED_IMPORT__MAX_STRIKES must be 3");
+ }
+ }
+}
+
+public sealed record StalledConfig
+{
+ public ushort MaxStrikes { get; init; }
+
+ public bool ResetStrikesOnProgress { get; init; }
+
+ public bool IgnorePrivate { get; init; }
+
+ public bool DeletePrivate { get; init; }
+
+ public ushort DownloadingMetadataMaxStrikes { get; init; }
+
+ public void Validate(string sectionName)
+ {
+ if (MaxStrikes is > 0 and < 3)
+ {
+ throw new ValidationException($"the minimum value for {sectionName.ToUpperInvariant()}__STALLED__MAX_STRIKES must be 3");
}
if (DownloadingMetadataMaxStrikes is > 0 and < 3)
{
- throw new ValidationException($"the minimum value for {SectionName.ToUpperInvariant()}__DOWNLOADING_METADATA_MAX_STRIKES must be 3");
+ throw new ValidationException($"the minimum value for {sectionName.ToUpperInvariant()}__STALLED__DOWNLOADING_METADATA_MAX_STRIKES must be 3");
}
-
- if (SlowMaxStrikes is > 0 and < 3)
+ }
+}
+
+public sealed record SlowConfig
+{
+ public ushort MaxStrikes { get; init; }
+
+ public bool ResetStrikesOnProgress { get; init; }
+
+ public bool IgnorePrivate { get; init; }
+
+ public bool DeletePrivate { get; init; }
+
+ public string MinSpeed { get; init; } = string.Empty;
+
+ [JsonIgnore]
+ public ByteSize MinSpeedByteSize => string.IsNullOrEmpty(MinSpeed) ? new ByteSize(0) : ByteSize.Parse(MinSpeed);
+
+ public double MaxTime { get; init; }
+
+ public string IgnoreAboveSize { get; init; } = string.Empty;
+
+ [JsonIgnore]
+ public ByteSize? IgnoreAboveSizeByteSize => string.IsNullOrEmpty(IgnoreAboveSize) ? null : ByteSize.Parse(IgnoreAboveSize);
+
+ public void Validate(string sectionName)
+ {
+ if (MaxStrikes is > 0 and < 3)
{
- throw new ValidationException($"the minimum value for {SectionName.ToUpperInvariant()}__SLOW_MAX_STRIKES must be 3");
+ throw new ValidationException($"the minimum value for {sectionName.ToUpperInvariant()}__SLOW__MAX_STRIKES must be 3");
}
- if (SlowMaxStrikes > 0)
+ if (MaxStrikes > 0)
{
- bool isSlowSpeedSet = !string.IsNullOrEmpty(SlowMinSpeed);
+ bool isSpeedSet = !string.IsNullOrEmpty(MinSpeed);
- if (isSlowSpeedSet && ByteSize.TryParse(SlowMinSpeed, out _) is false)
+ if (isSpeedSet && ByteSize.TryParse(MinSpeed, out _) is false)
{
- throw new ValidationException($"invalid value for {SectionName.ToUpperInvariant()}__SLOW_MIN_SPEED");
+ throw new ValidationException($"invalid value for {sectionName.ToUpperInvariant()}__SLOW__MIN_SPEED");
}
- if (SlowMaxTime < 0)
+ if (MaxTime < 0)
{
- throw new ValidationException($"invalid value for {SectionName.ToUpperInvariant()}__SLOW_MAX_TIME");
+ throw new ValidationException($"invalid value for {sectionName.ToUpperInvariant()}__SLOW__MAX_TIME");
}
- if (!isSlowSpeedSet && SlowMaxTime is 0)
+ if (!isSpeedSet && MaxTime is 0)
{
- throw new ValidationException($"either {SectionName.ToUpperInvariant()}__SLOW_MIN_SPEED or {SectionName.ToUpperInvariant()}__SLOW_MAX_STRIKES must be set");
+ throw new ValidationException($"either {sectionName.ToUpperInvariant()}__SLOW__MIN_SPEED or {sectionName.ToUpperInvariant()}__SLOW__MAX_TIME must be set");
}
- bool isSlowIgnoreAboveSizeSet = !string.IsNullOrEmpty(SlowIgnoreAboveSize);
+ bool isIgnoreAboveSizeSet = !string.IsNullOrEmpty(IgnoreAboveSize);
- if (isSlowIgnoreAboveSizeSet && ByteSize.TryParse(SlowIgnoreAboveSize, out _) is false)
+ if (isIgnoreAboveSizeSet && ByteSize.TryParse(IgnoreAboveSize, out _) is false)
{
- throw new ValidationException($"invalid value for {SectionName.ToUpperInvariant()}__SLOW_IGNORE_ABOVE_SIZE");
+ throw new ValidationException($"invalid value for {sectionName.ToUpperInvariant()}__SLOW__IGNORE_ABOVE_SIZE");
}
}
}
diff --git a/code/Executable/Controllers/ConfigurationController.cs b/code/Executable/Controllers/ConfigurationController.cs
index 79a6068e..46d42bb3 100644
--- a/code/Executable/Controllers/ConfigurationController.cs
+++ b/code/Executable/Controllers/ConfigurationController.cs
@@ -1,6 +1,5 @@
using Common.Configuration;
using Common.Configuration.Arr;
-using Common.Configuration.ContentBlocker;
using Common.Configuration.DownloadCleaner;
using Common.Configuration.DownloadClient;
using Common.Configuration.DTOs.Arr;
diff --git a/code/Executable/DependencyInjection/LoggingDI.cs b/code/Executable/DependencyInjection/LoggingDI.cs
index 470965d2..7de518af 100644
--- a/code/Executable/DependencyInjection/LoggingDI.cs
+++ b/code/Executable/DependencyInjection/LoggingDI.cs
@@ -29,12 +29,8 @@ public static class LoggingDI
const string consoleOutputTemplate = $"[{{@t:yyyy-MM-dd HH:mm:ss.fff}} {{@l:u3}}]{jobNameTemplate}{categoryTemplate} {{@m}}\n{{@x}}";
const string fileOutputTemplate = $"{{@t:yyyy-MM-dd HH:mm:ss.fff zzz}} [{{@l:u3}}]{jobNameTemplate}{categoryTemplate} {{@m:lj}}\n{{@x}}";
- // Determine categories and padding sizes
- List categories = ["SYSTEM", "API", "JOBS", "NOTIFICATIONS"];
- int catPadding = categories.Max(x => x.Length) + 2;
-
// Determine job name padding
- List jobNames = [nameof(ContentBlocker), nameof(QueueCleaner), nameof(DownloadCleaner)];
+ List jobNames = [nameof(QueueCleaner), nameof(DownloadCleaner)];
int jobPadding = jobNames.Max(x => x.Length) + 2;
// Determine instance name padding
@@ -46,7 +42,7 @@ public static class LoggingDI
InstanceType.Whisparr.ToString(),
"SYSTEM"
];
- int arrPadding = categoryNames.Max(x => x.Length) + 2;
+ int catPadding = categoryNames.Max(x => x.Length) + 2;
// Apply padding values to templates
string consoleTemplate = consoleOutputTemplate
diff --git a/code/Executable/DependencyInjection/ServicesDI.cs b/code/Executable/DependencyInjection/ServicesDI.cs
index 0d6f532c..12d703ef 100644
--- a/code/Executable/DependencyInjection/ServicesDI.cs
+++ b/code/Executable/DependencyInjection/ServicesDI.cs
@@ -38,7 +38,6 @@ public static class ServicesDI
.AddTransient()
.AddTransient()
.AddTransient()
- .AddTransient()
.AddTransient()
.AddTransient()
.AddTransient()
diff --git a/code/Executable/Jobs/BackgroundJobManager.cs b/code/Executable/Jobs/BackgroundJobManager.cs
index ad00e751..93e5c437 100644
--- a/code/Executable/Jobs/BackgroundJobManager.cs
+++ b/code/Executable/Jobs/BackgroundJobManager.cs
@@ -1,15 +1,11 @@
-using Common.Configuration.ContentBlocker;
using Common.Configuration.DownloadCleaner;
using Common.Configuration.QueueCleaner;
using Common.Helpers;
using Infrastructure.Configuration;
-using Infrastructure.Verticals.ContentBlocker;
using Infrastructure.Verticals.DownloadCleaner;
using Infrastructure.Verticals.Jobs;
using Infrastructure.Verticals.QueueCleaner;
-using Microsoft.Extensions.Hosting;
using Quartz;
-using Quartz.Impl.Matchers;
using Quartz.Spi;
namespace Executable.Jobs;
@@ -28,8 +24,8 @@ public class BackgroundJobManager : IHostedService
public BackgroundJobManager(
ISchedulerFactory schedulerFactory,
IConfigManager configManager,
- IServiceProvider serviceProvider,
- ILogger logger)
+ ILogger logger
+ )
{
_schedulerFactory = schedulerFactory;
_configManager = configManager;
@@ -78,48 +74,11 @@ public class BackgroundJobManager : IHostedService
}
// Get configurations from JSON files
- ContentBlockerConfig? contentBlockerConfig = await _configManager.GetConfigurationAsync();
- QueueCleanerConfig? queueCleanerConfig = await _configManager.GetConfigurationAsync();
- DownloadCleanerConfig? downloadCleanerConfig = await _configManager.GetConfigurationAsync();
+ QueueCleanerConfig queueCleanerConfig = await _configManager.GetConfigurationAsync();
+ DownloadCleanerConfig downloadCleanerConfig = await _configManager.GetConfigurationAsync();
- // Add ContentBlocker job if enabled
- if (contentBlockerConfig?.Enabled == true)
- {
- await AddContentBlockerJob(contentBlockerConfig, cancellationToken);
- }
-
- // Add QueueCleaner job if enabled
- if (queueCleanerConfig?.Enabled == true)
- {
- // Check if we need to chain it after ContentBlocker
- bool shouldChainAfterContentBlocker =
- contentBlockerConfig?.Enabled == true &&
- queueCleanerConfig.RunSequentially;
-
- await AddQueueCleanerJob(queueCleanerConfig, shouldChainAfterContentBlocker, cancellationToken);
- }
-
- // Add DownloadCleaner job if enabled
- if (downloadCleanerConfig?.Enabled == true)
- {
- await AddDownloadCleanerJob(downloadCleanerConfig, cancellationToken);
- }
- }
-
- ///
- /// Adds the ContentBlocker job to the scheduler.
- ///
- public async Task AddContentBlockerJob(ContentBlockerConfig config, CancellationToken cancellationToken = default)
- {
- if (!config.Enabled)
- {
- return;
- }
-
- await AddJobWithTrigger(
- config,
- config.CronExpression,
- cancellationToken);
+ await AddQueueCleanerJob(queueCleanerConfig, cancellationToken);
+ await AddDownloadCleanerJob(downloadCleanerConfig, cancellationToken);
}
///
@@ -127,7 +86,6 @@ public class BackgroundJobManager : IHostedService
///
public async Task AddQueueCleanerJob(
QueueCleanerConfig config,
- bool chainAfterContentBlocker = false,
CancellationToken cancellationToken = default)
{
if (!config.Enabled)
@@ -135,28 +93,10 @@ public class BackgroundJobManager : IHostedService
return;
}
- var jobKey = new JobKey(nameof(QueueCleaner));
-
- // If the job should be chained after ContentBlocker, add it without a cron trigger
- if (chainAfterContentBlocker)
- {
- await AddJobWithoutTrigger(cancellationToken);
-
- // Add job listener to chain QueueCleaner after ContentBlocker
- if (_scheduler != null)
- {
- var chainListener = new JobChainingListener(nameof(ContentBlocker), nameof(QueueCleaner));
- _scheduler.ListenerManager.AddJobListener(chainListener, KeyMatcher.KeyEquals(new JobKey(nameof(ContentBlocker))));
- }
- }
- else
- {
- // Add job with normal cron trigger
- await AddJobWithTrigger(
- config,
- config.CronExpression,
- cancellationToken);
- }
+ await AddJobWithTrigger(
+ config,
+ config.CronExpression,
+ cancellationToken);
}
///
diff --git a/code/Infrastructure.Tests/Verticals/ContentBlocker/FilenameEvaluatorTests.cs b/code/Infrastructure.Tests/Verticals/ContentBlocker/FilenameEvaluatorTests.cs
index f295eb5c..ba434556 100644
--- a/code/Infrastructure.Tests/Verticals/ContentBlocker/FilenameEvaluatorTests.cs
+++ b/code/Infrastructure.Tests/Verticals/ContentBlocker/FilenameEvaluatorTests.cs
@@ -1,6 +1,6 @@
using System.Collections.Concurrent;
using System.Text.RegularExpressions;
-using Common.Configuration.ContentBlocker;
+using Common.Configuration.QueueCleaner;
using Shouldly;
namespace Infrastructure.Tests.Verticals.ContentBlocker;
diff --git a/code/Infrastructure/Configuration/ConfigInitializer.cs b/code/Infrastructure/Configuration/ConfigInitializer.cs
index c089798f..98b7da63 100644
--- a/code/Infrastructure/Configuration/ConfigInitializer.cs
+++ b/code/Infrastructure/Configuration/ConfigInitializer.cs
@@ -1,4 +1,3 @@
-using Common.Configuration.ContentBlocker;
using Microsoft.Extensions.Logging;
namespace Infrastructure.Configuration;
diff --git a/code/Infrastructure/Configuration/ConfigManager.cs b/code/Infrastructure/Configuration/ConfigManager.cs
index fa250673..a317c3e9 100644
--- a/code/Infrastructure/Configuration/ConfigManager.cs
+++ b/code/Infrastructure/Configuration/ConfigManager.cs
@@ -1,7 +1,6 @@
using System.Reflection;
using Common.Configuration;
using Common.Configuration.Arr;
-using Common.Configuration.ContentBlocker;
using Common.Configuration.DownloadCleaner;
using Common.Configuration.DownloadClient;
using Common.Configuration.General;
diff --git a/code/Infrastructure/Configuration/IConfigManager.cs b/code/Infrastructure/Configuration/IConfigManager.cs
index dccbce95..8c1b313b 100644
--- a/code/Infrastructure/Configuration/IConfigManager.cs
+++ b/code/Infrastructure/Configuration/IConfigManager.cs
@@ -1,5 +1,4 @@
using Common.Configuration.Arr;
-using Common.Configuration.ContentBlocker;
using Common.Configuration.DownloadCleaner;
using Common.Configuration.DownloadClient;
using Common.Configuration.General;
diff --git a/code/Infrastructure/Verticals/Arr/ArrClient.cs b/code/Infrastructure/Verticals/Arr/ArrClient.cs
index 4dbcc359..c842c678 100644
--- a/code/Infrastructure/Verticals/Arr/ArrClient.cs
+++ b/code/Infrastructure/Verticals/Arr/ArrClient.cs
@@ -72,9 +72,9 @@ public abstract class ArrClient : IArrClient
return queueResponse;
}
- public virtual async Task ShouldRemoveFromQueue(InstanceType instanceType, QueueRecord record, bool isPrivateDownload, short arrMaxStrikes)
+ public virtual async Task ShouldRemoveFromQueue(InstanceType instanceType, QueueRecord record, bool isPrivateDownload, ushort arrMaxStrikes)
{
- if (_queueCleanerConfig.FailedImportIgnorePrivate && isPrivateDownload)
+ if (_queueCleanerConfig.FailedImport.IgnorePrivate && isPrivateDownload)
{
// ignore private trackers
_logger.LogDebug("skip failed import check | download is private | {name}", record.Title);
@@ -108,7 +108,7 @@ public abstract class ArrClient : IArrClient
return false;
}
- ushort maxStrikes = arrMaxStrikes > 0 ? (ushort)arrMaxStrikes : _queueCleanerConfig.FailedImportMaxStrikes;
+ ushort maxStrikes = arrMaxStrikes > 0 ? (ushort)arrMaxStrikes : _queueCleanerConfig.FailedImport.MaxStrikes;
return await _striker.StrikeAndCheckLimit(
record.DownloadId,
@@ -214,7 +214,7 @@ public abstract class ArrClient : IArrClient
private bool HasIgnoredPatterns(QueueRecord record)
{
- if (_queueCleanerConfig.FailedImportIgnorePatterns.Count is 0)
+ if (_queueCleanerConfig.FailedImport.IgnoredPatterns.Count is 0)
{
// no patterns are configured
return false;
@@ -234,7 +234,7 @@ public abstract class ArrClient : IArrClient
.ForEach(x => messages.Add(x));
return messages.Any(
- m => _queueCleanerConfig.FailedImportIgnorePatterns.Any(
+ m => _queueCleanerConfig.FailedImport.IgnoredPatterns.Any(
p => !string.IsNullOrWhiteSpace(p.Trim()) && m.Contains(p, StringComparison.InvariantCultureIgnoreCase)
)
);
diff --git a/code/Infrastructure/Verticals/Arr/Interfaces/IArrClient.cs b/code/Infrastructure/Verticals/Arr/Interfaces/IArrClient.cs
index d9966d94..07ff5e40 100644
--- a/code/Infrastructure/Verticals/Arr/Interfaces/IArrClient.cs
+++ b/code/Infrastructure/Verticals/Arr/Interfaces/IArrClient.cs
@@ -9,7 +9,7 @@ public interface IArrClient
{
Task GetQueueItemsAsync(ArrInstance arrInstance, int page);
- Task ShouldRemoveFromQueue(InstanceType instanceType, QueueRecord record, bool isPrivateDownload, short arrMaxStrikes);
+ Task ShouldRemoveFromQueue(InstanceType instanceType, QueueRecord record, bool isPrivateDownload, ushort arrMaxStrikes);
Task DeleteQueueItemAsync(ArrInstance arrInstance, QueueRecord record, bool removeFromClient, DeleteReason deleteReason);
diff --git a/code/Infrastructure/Verticals/ContentBlocker/BlocklistProvider.cs b/code/Infrastructure/Verticals/ContentBlocker/BlocklistProvider.cs
index 74c453cc..23f1bad3 100644
--- a/code/Infrastructure/Verticals/ContentBlocker/BlocklistProvider.cs
+++ b/code/Infrastructure/Verticals/ContentBlocker/BlocklistProvider.cs
@@ -1,7 +1,7 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Text.RegularExpressions;
-using Common.Configuration.ContentBlocker;
+using Common.Configuration.QueueCleaner;
using Common.Helpers;
using Data.Enums;
using Infrastructure.Configuration;
@@ -14,8 +14,7 @@ namespace Infrastructure.Verticals.ContentBlocker;
public sealed class BlocklistProvider
{
private readonly ILogger _logger;
- private readonly IConfigManager _configManager;
- private ContentBlockerConfig _contentBlockerConfig;
+ private readonly QueueCleanerConfig _queueCleanerConfig;
private readonly HttpClient _httpClient;
private readonly IMemoryCache _cache;
private bool _initialized;
@@ -28,11 +27,10 @@ public sealed class BlocklistProvider
)
{
_logger = logger;
- _configManager = configManager;
_cache = cache;
_httpClient = httpClientFactory.CreateClient(Constants.HttpClientWithRetryName);
- _contentBlockerConfig = _configManager.GetConfiguration();
+ _queueCleanerConfig = configManager.GetConfiguration();
}
public async Task LoadBlocklistsAsync()
@@ -45,9 +43,9 @@ public sealed class BlocklistProvider
try
{
- await LoadPatternsAndRegexesAsync(_contentBlockerConfig.Sonarr, InstanceType.Sonarr);
- await LoadPatternsAndRegexesAsync(_contentBlockerConfig.Radarr, InstanceType.Radarr);
- await LoadPatternsAndRegexesAsync(_contentBlockerConfig.Lidarr, InstanceType.Lidarr);
+ await LoadPatternsAndRegexesAsync(_queueCleanerConfig.ContentBlocker.Sonarr, InstanceType.Sonarr);
+ await LoadPatternsAndRegexesAsync(_queueCleanerConfig.ContentBlocker.Radarr, InstanceType.Radarr);
+ await LoadPatternsAndRegexesAsync(_queueCleanerConfig.ContentBlocker.Lidarr, InstanceType.Lidarr);
_initialized = true;
}
@@ -81,17 +79,12 @@ public sealed class BlocklistProvider
private async Task LoadPatternsAndRegexesAsync(BlocklistSettings blocklistSettings, InstanceType instanceType)
{
- if (!blocklistSettings.Enabled)
+ if (string.IsNullOrEmpty(blocklistSettings.BlocklistPath))
{
return;
}
- if (string.IsNullOrEmpty(blocklistSettings.Path))
- {
- return;
- }
-
- string[] filePatterns = await ReadContentAsync(blocklistSettings.Path);
+ string[] filePatterns = await ReadContentAsync(blocklistSettings.BlocklistPath);
long startTime = Stopwatch.GetTimestamp();
ParallelOptions options = new() { MaxDegreeOfParallelism = 5 };
@@ -122,13 +115,13 @@ public sealed class BlocklistProvider
TimeSpan elapsed = Stopwatch.GetElapsedTime(startTime);
- _cache.Set(CacheKeys.BlocklistType(instanceType), blocklistSettings.Type);
+ _cache.Set(CacheKeys.BlocklistType(instanceType), blocklistSettings.BlocklistType);
_cache.Set(CacheKeys.BlocklistPatterns(instanceType), patterns);
_cache.Set(CacheKeys.BlocklistRegexes(instanceType), regexes);
_logger.LogDebug("loaded {count} patterns", patterns.Count);
_logger.LogDebug("loaded {count} regexes", regexes.Count);
- _logger.LogDebug("blocklist loaded in {elapsed} ms | {path}", elapsed.TotalMilliseconds, blocklistSettings.Path);
+ _logger.LogDebug("blocklist loaded in {elapsed} ms | {path}", elapsed.TotalMilliseconds, blocklistSettings.BlocklistPath);
}
private async Task ReadContentAsync(string path)
diff --git a/code/Infrastructure/Verticals/ContentBlocker/ContentBlocker.cs b/code/Infrastructure/Verticals/ContentBlocker/ContentBlocker.cs
index 722597e3..8f0d35f7 100644
--- a/code/Infrastructure/Verticals/ContentBlocker/ContentBlocker.cs
+++ b/code/Infrastructure/Verticals/ContentBlocker/ContentBlocker.cs
@@ -1,187 +1,187 @@
-using System.Collections.Concurrent;
-using System.Text.RegularExpressions;
-using Common.Configuration.Arr;
-using Common.Configuration.ContentBlocker;
-using Common.Configuration.DownloadClient;
-using Data.Enums;
-using Data.Models.Arr.Queue;
-using Infrastructure.Configuration;
-using Infrastructure.Helpers;
-using Infrastructure.Services;
-using Infrastructure.Verticals.Arr;
-using Infrastructure.Verticals.Arr.Interfaces;
-using Infrastructure.Verticals.DownloadClient;
-using Infrastructure.Verticals.Jobs;
-using MassTransit;
-using Microsoft.Extensions.Caching.Memory;
-using Microsoft.Extensions.Logging;
-using LogContext = Serilog.Context.LogContext;
-
-namespace Infrastructure.Verticals.ContentBlocker;
-
-public sealed class ContentBlocker : GenericHandler
-{
- private readonly ContentBlockerConfig _config;
- private readonly BlocklistProvider _blocklistProvider;
- private readonly IIgnoredDownloadsService _ignoredDownloadsService;
-
- public ContentBlocker(
- ILogger logger,
- IConfigManager configManager,
- IMemoryCache cache,
- IBus messageBus,
- ArrClientFactory arrClientFactory,
- ArrQueueIterator arrArrQueueIterator,
- BlocklistProvider blocklistProvider,
- IIgnoredDownloadsService ignoredDownloadsService,
- DownloadServiceFactory downloadServiceFactory
- ) : base(
- logger, cache, messageBus,
- arrClientFactory, arrArrQueueIterator, downloadServiceFactory
- )
- {
- _blocklistProvider = blocklistProvider;
- _ignoredDownloadsService = ignoredDownloadsService;
-
- _config = configManager.GetConfiguration();
- _downloadClientConfig = configManager.GetConfiguration();
- _sonarrConfig = configManager.GetConfiguration();
- _radarrConfig = configManager.GetConfiguration();
- _lidarrConfig = configManager.GetConfiguration();
- }
-
- public override async Task ExecuteAsync()
- {
- if (_downloadClientConfig.Clients.Count is 0)
- {
- _logger.LogWarning("No download clients configured");
- return;
- }
-
- bool blocklistIsConfigured = _config.Sonarr.Enabled && !string.IsNullOrEmpty(_config.Sonarr.Path) ||
- _config.Radarr.Enabled && !string.IsNullOrEmpty(_config.Radarr.Path) ||
- _config.Lidarr.Enabled && !string.IsNullOrEmpty(_config.Lidarr.Path);
-
- if (!blocklistIsConfigured)
- {
- _logger.LogWarning("no blocklist is configured");
- return;
- }
-
- await _blocklistProvider.LoadBlocklistsAsync();
- await base.ExecuteAsync();
- }
-
- protected override async Task ProcessInstanceAsync(ArrInstance instance, InstanceType instanceType, ArrConfig config)
- {
- IReadOnlyList ignoredDownloads = await _ignoredDownloadsService.GetIgnoredDownloadsAsync();
-
- using var _ = LogContext.PushProperty(LogProperties.Category, instanceType.ToString());
-
- IArrClient arrClient = _arrClientFactory.GetClient(instanceType);
- BlocklistType blocklistType = _blocklistProvider.GetBlocklistType(instanceType);
- ConcurrentBag patterns = _blocklistProvider.GetPatterns(instanceType);
- ConcurrentBag regexes = _blocklistProvider.GetRegexes(instanceType);
-
- await _arrArrQueueIterator.Iterate(arrClient, instance, async items =>
- {
- var groups = items
- .GroupBy(x => x.DownloadId)
- .ToList();
-
- foreach (var group in groups)
- {
- if (group.Any(x => !arrClient.IsRecordValid(x)))
- {
- continue;
- }
-
- QueueRecord record = group.First();
-
- if (record.Protocol is not "torrent")
- {
- continue;
- }
-
- if (string.IsNullOrEmpty(record.DownloadId))
- {
- _logger.LogDebug("skip | download id is null for {title}", record.Title);
- continue;
- }
-
- if (ignoredDownloads.Contains(record.DownloadId, StringComparer.InvariantCultureIgnoreCase))
- {
- _logger.LogInformation("skip | {title} | ignored", record.Title);
- continue;
- }
-
- _logger.LogTrace("processing | {name}", record.Title);
-
- string downloadRemovalKey = CacheKeys.DownloadMarkedForRemoval(record.DownloadId, instance.Url);
-
- if (_cache.TryGetValue(downloadRemovalKey, out bool _))
- {
- _logger.LogDebug("skip | already marked for removal | {title}", record.Title);
- continue;
- }
-
- _logger.LogDebug("searching unwanted files for {title}", record.Title);
- bool found = false;
-
- foreach (var downloadService in _downloadServices)
- {
- try
- {
- BlockFilesResult result = await downloadService
- .BlockUnwantedFilesAsync(record.DownloadId, blocklistType, patterns, regexes, ignoredDownloads);
-
- if (!result.Found)
- {
- continue;
- }
-
- found = true;
-
- if (!result.ShouldRemove)
- {
- break;
- }
-
- _logger.LogDebug("all files are marked as unwanted | {hash}", record.Title);
-
- bool removeFromClient = true;
-
- if (result.IsPrivate && !_config.DeletePrivate)
- {
- removeFromClient = false;
- }
-
- await PublishQueueItemRemoveRequest(
- downloadRemovalKey,
- instanceType,
- instance,
- record,
- group.Count() > 1,
- removeFromClient,
- DeleteReason.AllFilesBlocked
- );
-
- break;
- }
- catch (Exception ex)
- {
- _logger.LogError(
- ex,
- "Error blocking unwanted files for {hash} with download client",
- record.DownloadId);
- }
- }
-
- if (!found)
- {
- _logger.LogWarning("skip | download not found {title}", record.Title);
- }
- }
- });
- }
-}
\ No newline at end of file
+// using System.Collections.Concurrent;
+// using System.Text.RegularExpressions;
+// using Common.Configuration.Arr;
+// using Common.Configuration.DownloadClient;
+// using Common.Configuration.QueueCleaner;
+// using Data.Enums;
+// using Data.Models.Arr.Queue;
+// using Infrastructure.Configuration;
+// using Infrastructure.Helpers;
+// using Infrastructure.Services;
+// using Infrastructure.Verticals.Arr;
+// using Infrastructure.Verticals.Arr.Interfaces;
+// using Infrastructure.Verticals.DownloadClient;
+// using Infrastructure.Verticals.Jobs;
+// using MassTransit;
+// using Microsoft.Extensions.Caching.Memory;
+// using Microsoft.Extensions.Logging;
+// using LogContext = Serilog.Context.LogContext;
+//
+// namespace Infrastructure.Verticals.ContentBlocker;
+//
+// public sealed class ContentBlocker : GenericHandler
+// {
+// private readonly ContentBlockerConfig _config;
+// private readonly BlocklistProvider _blocklistProvider;
+// private readonly IIgnoredDownloadsService _ignoredDownloadsService;
+//
+// public ContentBlocker(
+// ILogger logger,
+// IConfigManager configManager,
+// IMemoryCache cache,
+// IBus messageBus,
+// ArrClientFactory arrClientFactory,
+// ArrQueueIterator arrArrQueueIterator,
+// BlocklistProvider blocklistProvider,
+// IIgnoredDownloadsService ignoredDownloadsService,
+// DownloadServiceFactory downloadServiceFactory
+// ) : base(
+// logger, cache, messageBus,
+// arrClientFactory, arrArrQueueIterator, downloadServiceFactory
+// )
+// {
+// _blocklistProvider = blocklistProvider;
+// _ignoredDownloadsService = ignoredDownloadsService;
+//
+// _config = configManager.GetConfiguration();
+// _downloadClientConfig = configManager.GetConfiguration();
+// _sonarrConfig = configManager.GetConfiguration();
+// _radarrConfig = configManager.GetConfiguration();
+// _lidarrConfig = configManager.GetConfiguration();
+// }
+//
+// public override async Task ExecuteAsync()
+// {
+// if (_downloadClientConfig.Clients.Count is 0)
+// {
+// _logger.LogWarning("No download clients configured");
+// return;
+// }
+//
+// bool blocklistIsConfigured = _config.Sonarr.Enabled && !string.IsNullOrEmpty(_config.Sonarr.Path) ||
+// _config.Radarr.Enabled && !string.IsNullOrEmpty(_config.Radarr.Path) ||
+// _config.Lidarr.Enabled && !string.IsNullOrEmpty(_config.Lidarr.Path);
+//
+// if (!blocklistIsConfigured)
+// {
+// _logger.LogWarning("no blocklist is configured");
+// return;
+// }
+//
+// await _blocklistProvider.LoadBlocklistsAsync();
+// await base.ExecuteAsync();
+// }
+//
+// protected override async Task ProcessInstanceAsync(ArrInstance instance, InstanceType instanceType, ArrConfig config)
+// {
+// IReadOnlyList ignoredDownloads = await _ignoredDownloadsService.GetIgnoredDownloadsAsync();
+//
+// using var _ = LogContext.PushProperty(LogProperties.Category, instanceType.ToString());
+//
+// IArrClient arrClient = _arrClientFactory.GetClient(instanceType);
+// BlocklistType blocklistType = _blocklistProvider.GetBlocklistType(instanceType);
+// ConcurrentBag patterns = _blocklistProvider.GetPatterns(instanceType);
+// ConcurrentBag regexes = _blocklistProvider.GetRegexes(instanceType);
+//
+// await _arrArrQueueIterator.Iterate(arrClient, instance, async items =>
+// {
+// var groups = items
+// .GroupBy(x => x.DownloadId)
+// .ToList();
+//
+// foreach (var group in groups)
+// {
+// if (group.Any(x => !arrClient.IsRecordValid(x)))
+// {
+// continue;
+// }
+//
+// QueueRecord record = group.First();
+//
+// if (record.Protocol is not "torrent")
+// {
+// continue;
+// }
+//
+// if (string.IsNullOrEmpty(record.DownloadId))
+// {
+// _logger.LogDebug("skip | download id is null for {title}", record.Title);
+// continue;
+// }
+//
+// if (ignoredDownloads.Contains(record.DownloadId, StringComparer.InvariantCultureIgnoreCase))
+// {
+// _logger.LogInformation("skip | {title} | ignored", record.Title);
+// continue;
+// }
+//
+// _logger.LogTrace("processing | {name}", record.Title);
+//
+// string downloadRemovalKey = CacheKeys.DownloadMarkedForRemoval(record.DownloadId, instance.Url);
+//
+// if (_cache.TryGetValue(downloadRemovalKey, out bool _))
+// {
+// _logger.LogDebug("skip | already marked for removal | {title}", record.Title);
+// continue;
+// }
+//
+// _logger.LogDebug("searching unwanted files for {title}", record.Title);
+// bool found = false;
+//
+// foreach (var downloadService in _downloadServices)
+// {
+// try
+// {
+// BlockFilesResult result = await downloadService
+// .BlockUnwantedFilesAsync(record.DownloadId, blocklistType, patterns, regexes, ignoredDownloads);
+//
+// if (!result.Found)
+// {
+// continue;
+// }
+//
+// found = true;
+//
+// if (!result.ShouldRemove)
+// {
+// break;
+// }
+//
+// _logger.LogDebug("all files are marked as unwanted | {hash}", record.Title);
+//
+// bool removeFromClient = true;
+//
+// if (result.IsPrivate && !_config.DeletePrivate)
+// {
+// removeFromClient = false;
+// }
+//
+// await PublishQueueItemRemoveRequest(
+// downloadRemovalKey,
+// instanceType,
+// instance,
+// record,
+// group.Count() > 1,
+// removeFromClient,
+// DeleteReason.AllFilesBlocked
+// );
+//
+// break;
+// }
+// catch (Exception ex)
+// {
+// _logger.LogError(
+// ex,
+// "Error blocking unwanted files for {hash} with download client",
+// record.DownloadId);
+// }
+// }
+//
+// if (!found)
+// {
+// _logger.LogWarning("skip | download not found {title}", record.Title);
+// }
+// }
+// });
+// }
+// }
\ No newline at end of file
diff --git a/code/Infrastructure/Verticals/ContentBlocker/FilenameEvaluator.cs b/code/Infrastructure/Verticals/ContentBlocker/FilenameEvaluator.cs
index be7738e1..c8cefc87 100644
--- a/code/Infrastructure/Verticals/ContentBlocker/FilenameEvaluator.cs
+++ b/code/Infrastructure/Verticals/ContentBlocker/FilenameEvaluator.cs
@@ -1,6 +1,6 @@
using System.Collections.Concurrent;
using System.Text.RegularExpressions;
-using Common.Configuration.ContentBlocker;
+using Common.Configuration.QueueCleaner;
using Microsoft.Extensions.Logging;
namespace Infrastructure.Verticals.ContentBlocker;
diff --git a/code/Infrastructure/Verticals/ContentBlocker/IFilenameEvaluator.cs b/code/Infrastructure/Verticals/ContentBlocker/IFilenameEvaluator.cs
index 91e69c00..6119f5c6 100644
--- a/code/Infrastructure/Verticals/ContentBlocker/IFilenameEvaluator.cs
+++ b/code/Infrastructure/Verticals/ContentBlocker/IFilenameEvaluator.cs
@@ -1,6 +1,6 @@
using System.Collections.Concurrent;
using System.Text.RegularExpressions;
-using Common.Configuration.ContentBlocker;
+using Common.Configuration.QueueCleaner;
namespace Infrastructure.Verticals.ContentBlocker;
diff --git a/code/Infrastructure/Verticals/DownloadClient/Deluge/DelugeService.cs b/code/Infrastructure/Verticals/DownloadClient/Deluge/DelugeService.cs
index ee2d3ed3..63f175f1 100644
--- a/code/Infrastructure/Verticals/DownloadClient/Deluge/DelugeService.cs
+++ b/code/Infrastructure/Verticals/DownloadClient/Deluge/DelugeService.cs
@@ -1,21 +1,11 @@
-using System.Collections.Concurrent;
-using System.Text.RegularExpressions;
-using Common.Attributes;
-using Common.Configuration.ContentBlocker;
-using Common.Configuration.DownloadCleaner;
using Common.Configuration.DownloadClient;
-using Common.CustomDataTypes;
using Common.Exceptions;
-using Data.Enums;
using Data.Models.Deluge.Response;
using Infrastructure.Events;
-using Infrastructure.Extensions;
using Infrastructure.Interceptors;
using Infrastructure.Verticals.ContentBlocker;
-using Infrastructure.Verticals.Context;
using Infrastructure.Verticals.Files;
using Infrastructure.Verticals.ItemStriker;
-using Infrastructure.Verticals.Notifications;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Infrastructure.Configuration;
@@ -23,7 +13,7 @@ using Infrastructure.Http;
namespace Infrastructure.Verticals.DownloadClient.Deluge;
-public class DelugeService : DownloadService, IDelugeService
+public partial class DelugeService : DownloadService, IDelugeService
{
private DelugeClient? _client;
@@ -36,11 +26,12 @@ public class DelugeService : DownloadService, IDelugeService
IDryRunInterceptor dryRunInterceptor,
IHardLinkFileService hardLinkFileService,
IDynamicHttpClientProvider httpClientProvider,
- EventPublisher eventPublisher
+ EventPublisher eventPublisher,
+ BlocklistProvider blocklistProvider
) : base(
logger, configManager, cache,
filenameEvaluator, striker, dryRunInterceptor, hardLinkFileService,
- httpClientProvider, eventPublisher
+ httpClientProvider, eventPublisher, blocklistProvider
)
{
// Client will be initialized when Initialize() is called with a specific client configuration
@@ -95,476 +86,6 @@ public class DelugeService : DownloadService, IDelugeService
throw;
}
}
-
- ///
- public override async Task ShouldRemoveFromArrQueueAsync(string hash, IReadOnlyList ignoredDownloads)
- {
- if (_client == null)
- {
- throw new InvalidOperationException("Deluge client is not initialized");
- }
-
- hash = hash.ToLowerInvariant();
-
- DelugeContents? contents = null;
- DownloadCheckResult result = new();
-
- DownloadStatus? download = await _client.GetTorrentStatus(hash);
-
- if (download?.Hash is null)
- {
- _logger.LogDebug("failed to find torrent {hash} in the download client", hash);
- return result;
- }
-
- result.Found = true;
- result.IsPrivate = download.Private;
-
- if (ignoredDownloads.Count > 0 && download.ShouldIgnore(ignoredDownloads))
- {
- _logger.LogInformation("skip | download is ignored | {name}", download.Name);
- return result;
- }
-
- try
- {
- contents = await _client.GetTorrentFiles(hash);
- }
- catch (Exception exception)
- {
- _logger.LogDebug(exception, "failed to find torrent {hash} in the download client", hash);
- }
-
-
- bool shouldRemove = contents?.Contents?.Count > 0;
-
- ProcessFiles(contents.Contents, (_, file) =>
- {
- if (file.Priority > 0)
- {
- shouldRemove = false;
- }
- });
-
- if (shouldRemove)
- {
- // remove if all files are unwanted
- result.ShouldRemove = true;
- result.DeleteReason = DeleteReason.AllFilesSkipped;
- return result;
- }
-
- // remove if download is stuck
- (result.ShouldRemove, result.DeleteReason) = await EvaluateDownloadRemoval(download);
-
- return result;
- }
-
- ///
- public override async Task BlockUnwantedFilesAsync(string hash,
- BlocklistType blocklistType,
- ConcurrentBag patterns,
- ConcurrentBag regexes, IReadOnlyList ignoredDownloads)
- {
- hash = hash.ToLowerInvariant();
-
- DownloadStatus? download = await _client.GetTorrentStatus(hash);
- BlockFilesResult result = new();
-
- if (download?.Hash is null)
- {
- _logger.LogDebug("failed to find torrent {hash} in the download client", hash);
- return result;
- }
-
- // Mark as processed since we found the download
- result.Found = true;
-
- if (ignoredDownloads.Count > 0 && download.ShouldIgnore(ignoredDownloads))
- {
- _logger.LogInformation("skip | download is ignored | {name}", download.Name);
- return result;
- }
-
- result.IsPrivate = download.Private;
-
- if (_contentBlockerConfig.IgnorePrivate && download.Private)
- {
- // ignore private trackers
- _logger.LogDebug("skip files check | download is private | {name}", download.Name);
- return result;
- }
-
- DelugeContents? contents = null;
-
- try
- {
- contents = await _client.GetTorrentFiles(hash);
- }
- catch (Exception exception)
- {
- _logger.LogDebug(exception, "failed to find torrent {hash} in the download client", hash);
- }
-
- if (contents is null)
- {
- return result;
- }
-
- Dictionary priorities = [];
- bool hasPriorityUpdates = false;
- long totalFiles = 0;
- long totalUnwantedFiles = 0;
-
- ProcessFiles(contents.Contents, (name, file) =>
- {
- totalFiles++;
- int priority = file.Priority;
-
- if (file.Priority is 0)
- {
- totalUnwantedFiles++;
- }
-
- if (file.Priority is not 0 && !_filenameEvaluator.IsValid(name, blocklistType, patterns, regexes))
- {
- totalUnwantedFiles++;
- priority = 0;
- hasPriorityUpdates = true;
- _logger.LogInformation("unwanted file found | {file}", file.Path);
- }
-
- priorities.Add(file.Index, priority);
- });
-
- if (!hasPriorityUpdates)
- {
- return result;
- }
-
- _logger.LogDebug("changing priorities | torrent {hash}", hash);
-
- List sortedPriorities = priorities
- .OrderBy(x => x.Key)
- .Select(x => x.Value)
- .ToList();
-
- if (totalUnwantedFiles == totalFiles)
- {
- // Skip marking files as unwanted. The download will be removed completely.
- result.ShouldRemove = true;
-
- return result;
- }
-
- await _dryRunInterceptor.InterceptAsync(ChangeFilesPriority, hash, sortedPriorities);
-
- return result;
- }
-
- public override async Task?> GetSeedingDownloads()
- {
- return (await _client.GetStatusForAllTorrents())
- ?.Where(x => !string.IsNullOrEmpty(x.Hash))
- .Where(x => x.State?.Equals("seeding", StringComparison.InvariantCultureIgnoreCase) is true)
- .Cast