add content blocker (#5)
* refactored code added deluge support added transmission support added content blocker added blacklist and whitelist * increased level on some logs; updated test docker compose; updated dev appsettings * updated docker compose and readme * moved some logs * fixed env var typo; fixed sonarr and radarr default download client
This commit is contained in:
@@ -1,64 +0,0 @@
|
||||
using Common.Configuration;
|
||||
using Executable.Jobs;
|
||||
using Infrastructure.Verticals.Arr;
|
||||
using Infrastructure.Verticals.QueueCleaner;
|
||||
|
||||
namespace Executable;
|
||||
using Quartz;
|
||||
|
||||
public static class DependencyInjection
|
||||
{
|
||||
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration) =>
|
||||
services
|
||||
.AddLogging(builder => builder.ClearProviders().AddConsole())
|
||||
.AddHttpClient()
|
||||
.AddConfiguration(configuration)
|
||||
.AddServices()
|
||||
.AddQuartzServices(configuration);
|
||||
|
||||
private static IServiceCollection AddConfiguration(this IServiceCollection services, IConfiguration configuration) =>
|
||||
services
|
||||
.Configure<QBitConfig>(configuration.GetSection(QBitConfig.SectionName))
|
||||
.Configure<SonarrConfig>(configuration.GetSection(SonarrConfig.SectionName))
|
||||
.Configure<RadarrConfig>(configuration.GetSection(RadarrConfig.SectionName));
|
||||
|
||||
private static IServiceCollection AddServices(this IServiceCollection services) =>
|
||||
services
|
||||
.AddTransient<SonarrClient>()
|
||||
.AddTransient<RadarrClient>()
|
||||
.AddTransient<QueueCleanerJob>()
|
||||
.AddTransient<QueueCleanerHandler>();
|
||||
|
||||
private static IServiceCollection AddQuartzServices(this IServiceCollection services, IConfiguration configuration) =>
|
||||
services
|
||||
.AddQuartz(q =>
|
||||
{
|
||||
TriggersConfig? config = configuration.GetRequiredSection(TriggersConfig.SectionName).Get<TriggersConfig>();
|
||||
|
||||
if (config is null)
|
||||
{
|
||||
throw new NullReferenceException("Quartz configuration is null");
|
||||
}
|
||||
|
||||
q.AddQueueCleanerJob(config.QueueCleaner);
|
||||
})
|
||||
.AddQuartzHostedService(opt =>
|
||||
{
|
||||
opt.WaitForJobsToComplete = true;
|
||||
});
|
||||
|
||||
private static void AddQueueCleanerJob(this IServiceCollectionQuartzConfigurator q, string trigger)
|
||||
{
|
||||
q.AddJob<QueueCleanerJob>(opts =>
|
||||
{
|
||||
opts.WithIdentity(nameof(QueueCleanerJob));
|
||||
});
|
||||
|
||||
q.AddTrigger(opts =>
|
||||
{
|
||||
opts.ForJob(nameof(QueueCleanerJob))
|
||||
.WithIdentity($"{nameof(QueueCleanerJob)}-trigger")
|
||||
.WithCronSchedule(trigger);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Common.Configuration;
|
||||
using Common.Configuration.ContentBlocker;
|
||||
|
||||
namespace Executable.DependencyInjection;
|
||||
|
||||
public static class ConfigurationDI
|
||||
{
|
||||
public static IServiceCollection AddConfiguration(this IServiceCollection services, IConfiguration configuration) =>
|
||||
services
|
||||
.Configure<ContentBlockerConfig>(configuration.GetSection(ContentBlockerConfig.SectionName))
|
||||
.Configure<QBitConfig>(configuration.GetSection(QBitConfig.SectionName))
|
||||
.Configure<DelugeConfig>(configuration.GetSection(DelugeConfig.SectionName))
|
||||
.Configure<TransmissionConfig>(configuration.GetSection(TransmissionConfig.SectionName))
|
||||
.Configure<SonarrConfig>(configuration.GetSection(SonarrConfig.SectionName))
|
||||
.Configure<RadarrConfig>(configuration.GetSection(RadarrConfig.SectionName));
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Net;
|
||||
using Common.Configuration;
|
||||
using Common.Configuration.ContentBlocker;
|
||||
using Executable.Jobs;
|
||||
using Infrastructure.Verticals.Arr;
|
||||
using Infrastructure.Verticals.ContentBlocker;
|
||||
using Infrastructure.Verticals.DownloadClient;
|
||||
using Infrastructure.Verticals.DownloadClient.Deluge;
|
||||
using Infrastructure.Verticals.DownloadClient.QBittorrent;
|
||||
using Infrastructure.Verticals.DownloadClient.Transmission;
|
||||
using Infrastructure.Verticals.QueueCleaner;
|
||||
|
||||
namespace Executable.DependencyInjection;
|
||||
|
||||
public static class MainDI
|
||||
{
|
||||
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration) =>
|
||||
services
|
||||
.AddLogging(builder => builder.ClearProviders().AddConsole())
|
||||
.AddHttpClients()
|
||||
.AddConfiguration(configuration)
|
||||
.AddServices()
|
||||
.AddQuartzServices(configuration);
|
||||
|
||||
private static IServiceCollection AddHttpClients(this IServiceCollection services)
|
||||
{
|
||||
// add default HttpClient
|
||||
services.AddHttpClient();
|
||||
|
||||
// add Deluge HttpClient
|
||||
services
|
||||
.AddHttpClient(nameof(DelugeService), x =>
|
||||
{
|
||||
x.Timeout = TimeSpan.FromSeconds(5);
|
||||
})
|
||||
.ConfigurePrimaryHttpMessageHandler(_ =>
|
||||
{
|
||||
return new HttpClientHandler
|
||||
{
|
||||
AllowAutoRedirect = true,
|
||||
UseCookies = true,
|
||||
CookieContainer = new CookieContainer(),
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
|
||||
ServerCertificateCustomValidationCallback = (_, _, _, _) => true
|
||||
};
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using Common.Configuration;
|
||||
using Common.Configuration.ContentBlocker;
|
||||
using Common.Configuration.QueueCleaner;
|
||||
using Executable.Jobs;
|
||||
using Infrastructure.Verticals.ContentBlocker;
|
||||
using Infrastructure.Verticals.QueueCleaner;
|
||||
using Quartz;
|
||||
|
||||
namespace Executable.DependencyInjection;
|
||||
|
||||
public static class QuartzDI
|
||||
{
|
||||
public static IServiceCollection AddQuartzServices(this IServiceCollection services, IConfiguration configuration) =>
|
||||
services
|
||||
.AddQuartz(q =>
|
||||
{
|
||||
TriggersConfig? config = configuration
|
||||
.GetRequiredSection(TriggersConfig.SectionName)
|
||||
.Get<TriggersConfig>();
|
||||
|
||||
if (config is null)
|
||||
{
|
||||
throw new NullReferenceException("triggers configuration is null");
|
||||
}
|
||||
|
||||
q.AddQueueCleanerJob(configuration, config.QueueCleaner);
|
||||
q.AddContentBlockerJob(configuration, config.ContentBlocker);
|
||||
})
|
||||
.AddQuartzHostedService(opt =>
|
||||
{
|
||||
opt.WaitForJobsToComplete = true;
|
||||
});
|
||||
|
||||
private static void AddQueueCleanerJob(
|
||||
this IServiceCollectionQuartzConfigurator q,
|
||||
IConfiguration configuration,
|
||||
string trigger
|
||||
)
|
||||
{
|
||||
QueueCleanerConfig? config = configuration
|
||||
.GetRequiredSection(QueueCleanerConfig.SectionName)
|
||||
.Get<QueueCleanerConfig>();
|
||||
|
||||
if (config is null)
|
||||
{
|
||||
throw new NullReferenceException($"{nameof(QueueCleaner)} configuration is null");
|
||||
}
|
||||
|
||||
if (!config.Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
q.AddJob<QueueCleanerJob>(opts =>
|
||||
{
|
||||
opts.WithIdentity(nameof(QueueCleanerJob));
|
||||
});
|
||||
|
||||
q.AddTrigger(opts =>
|
||||
{
|
||||
opts.ForJob(nameof(QueueCleanerJob))
|
||||
.WithIdentity($"{nameof(QueueCleanerJob)}-trigger")
|
||||
.WithCronSchedule(trigger, x =>x.WithMisfireHandlingInstructionDoNothing());
|
||||
});
|
||||
}
|
||||
|
||||
private static void AddContentBlockerJob(
|
||||
this IServiceCollectionQuartzConfigurator q,
|
||||
IConfiguration configuration,
|
||||
string trigger
|
||||
)
|
||||
{
|
||||
ContentBlockerConfig? config = configuration
|
||||
.GetRequiredSection(ContentBlockerConfig.SectionName)
|
||||
.Get<ContentBlockerConfig>();
|
||||
|
||||
if (config is null)
|
||||
{
|
||||
throw new NullReferenceException($"{nameof(ContentBlocker)} configuration is null");
|
||||
}
|
||||
|
||||
if (!config.Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
q.AddJob<ContentBlockerJob>(opts =>
|
||||
{
|
||||
opts.WithIdentity(nameof(ContentBlockerJob));
|
||||
});
|
||||
|
||||
q.AddTrigger(opts =>
|
||||
{
|
||||
opts.ForJob(nameof(ContentBlockerJob))
|
||||
.WithIdentity($"{nameof(ContentBlockerJob)}-trigger")
|
||||
.WithCronSchedule(trigger, x =>x.WithMisfireHandlingInstructionDoNothing());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Executable.Jobs;
|
||||
using Infrastructure.Verticals.Arr;
|
||||
using Infrastructure.Verticals.ContentBlocker;
|
||||
using Infrastructure.Verticals.DownloadClient;
|
||||
using Infrastructure.Verticals.DownloadClient.Deluge;
|
||||
using Infrastructure.Verticals.DownloadClient.QBittorrent;
|
||||
using Infrastructure.Verticals.DownloadClient.Transmission;
|
||||
using Infrastructure.Verticals.QueueCleaner;
|
||||
|
||||
namespace Executable.DependencyInjection;
|
||||
|
||||
public static class ServicesDI
|
||||
{
|
||||
public static IServiceCollection AddServices(this IServiceCollection services) =>
|
||||
services
|
||||
.AddTransient<SonarrClient>()
|
||||
.AddTransient<RadarrClient>()
|
||||
.AddTransient<QueueCleanerJob>()
|
||||
.AddTransient<ContentBlockerJob>()
|
||||
.AddTransient<QueueCleaner>()
|
||||
.AddTransient<ContentBlocker>()
|
||||
.AddTransient<FilenameEvaluator>()
|
||||
.AddTransient<QBitService>()
|
||||
.AddTransient<DelugeService>()
|
||||
.AddTransient<TransmissionService>()
|
||||
.AddTransient<ArrQueueIterator>()
|
||||
.AddTransient<DownloadServiceFactory>()
|
||||
.AddSingleton<BlocklistProvider>();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Infrastructure.Verticals.ContentBlocker;
|
||||
using Quartz;
|
||||
|
||||
namespace Executable.Jobs;
|
||||
|
||||
[DisallowConcurrentExecution]
|
||||
public sealed class ContentBlockerJob : IJob
|
||||
{
|
||||
private readonly ILogger<QueueCleanerJob> _logger;
|
||||
private readonly ContentBlocker _contentBlocker;
|
||||
|
||||
public ContentBlockerJob(
|
||||
ILogger<QueueCleanerJob> logger,
|
||||
ContentBlocker contentBlocker
|
||||
)
|
||||
{
|
||||
_logger = logger;
|
||||
_contentBlocker = contentBlocker;
|
||||
}
|
||||
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _contentBlocker.ExecuteAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"{nameof(ContentBlockerJob)} failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,20 +6,23 @@ namespace Executable.Jobs;
|
||||
[DisallowConcurrentExecution]
|
||||
public sealed class QueueCleanerJob : IJob
|
||||
{
|
||||
private ILogger<QueueCleanerJob> _logger;
|
||||
private QueueCleanerHandler _handler;
|
||||
private readonly ILogger<QueueCleanerJob> _logger;
|
||||
private readonly QueueCleaner _queueCleaner;
|
||||
|
||||
public QueueCleanerJob(ILogger<QueueCleanerJob> logger, QueueCleanerHandler handler)
|
||||
public QueueCleanerJob(
|
||||
ILogger<QueueCleanerJob> logger,
|
||||
QueueCleaner queueCleaner
|
||||
)
|
||||
{
|
||||
_logger = logger;
|
||||
_handler = handler;
|
||||
_queueCleaner = queueCleaner;
|
||||
}
|
||||
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _handler.HandleAsync();
|
||||
await _queueCleaner.ExecuteAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Executable;
|
||||
using Executable.DependencyInjection;
|
||||
|
||||
var builder = Host.CreateApplicationBuilder(args);
|
||||
|
||||
|
||||
@@ -1,11 +1,63 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
"Default": "Debug",
|
||||
"Microsoft.Hosting.Lifetime": "Information",
|
||||
"Quartz": "Warning",
|
||||
"System.Net.Http.HttpClient": "Error"
|
||||
}
|
||||
},
|
||||
"Triggers": {
|
||||
"QueueCleaner": "0 0/1 * * * ?"
|
||||
"QueueCleaner": "0/10 * * * * ?",
|
||||
"ContentBlocker": "0/10 * * * * ?"
|
||||
},
|
||||
"ContentBlocker": {
|
||||
"Enabled": false,
|
||||
"Blacklist": {
|
||||
"Enabled": false,
|
||||
"Path": "https://raw.githubusercontent.com/flmorg/cleanuperr/refs/heads/main/blacklist"
|
||||
},
|
||||
"Whitelist": {
|
||||
"Enabled": false,
|
||||
"Path": "https://raw.githubusercontent.com/flmorg/cleanuperr/refs/heads/main/whitelist"
|
||||
}
|
||||
},
|
||||
"QueueCleaner": {
|
||||
"Enabled": true
|
||||
},
|
||||
"qBittorrent": {
|
||||
"Enabled": true,
|
||||
"Url": "http://localhost:8080",
|
||||
"Username": "test",
|
||||
"Password": "testing"
|
||||
},
|
||||
"Deluge": {
|
||||
"Enabled": false,
|
||||
"Url": "http://localhost:8112",
|
||||
"Password": "testing"
|
||||
},
|
||||
"Transmission": {
|
||||
"Enabled": false,
|
||||
"Url": "http://localhost:9091",
|
||||
"Username": "test",
|
||||
"Password": "testing"
|
||||
},
|
||||
"Sonarr": {
|
||||
"Enabled": true,
|
||||
"Instances": [
|
||||
{
|
||||
"Url": "http://localhost:8989",
|
||||
"ApiKey": "96736c3eb3144936b8f1d62d27be8cee"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Radarr": {
|
||||
"Enabled": true,
|
||||
"Instances": [
|
||||
{
|
||||
"Url": "http://localhost:7878",
|
||||
"ApiKey": "705b553732ab4167ab23909305d60600"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,40 @@
|
||||
}
|
||||
},
|
||||
"Triggers": {
|
||||
"QueueCleaner": "0 0/5 * * * ?"
|
||||
"QueueCleaner": "0 0/5 * * * ?",
|
||||
"ContentBlocker": "0 0/5 * * * ?"
|
||||
},
|
||||
"ContentBlocker": {
|
||||
"Enabled": false,
|
||||
"Blacklist": {
|
||||
"Enabled": false,
|
||||
"Path": ""
|
||||
},
|
||||
"Whitelist": {
|
||||
"Enabled": false,
|
||||
"Path": ""
|
||||
}
|
||||
},
|
||||
"QueueCleaner": {
|
||||
"Enabled": true
|
||||
},
|
||||
"qBittorrent": {
|
||||
"Enabled": true,
|
||||
"Url": "http://localhost:8080",
|
||||
"Username": "",
|
||||
"Password": ""
|
||||
},
|
||||
"Deluge": {
|
||||
"Enabled": false,
|
||||
"Url": "http://localhost:8112",
|
||||
"Password": "testing"
|
||||
},
|
||||
"Transmission": {
|
||||
"Enabled": false,
|
||||
"Url": "http://localhost:9091",
|
||||
"Username": "test",
|
||||
"Password": "testing"
|
||||
},
|
||||
"Sonarr": {
|
||||
"Enabled": true,
|
||||
"Instances": [
|
||||
|
||||
Reference in New Issue
Block a user