Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b3b211d956 | |||
| 279bd6d82d | |||
| 5dced28228 | |||
| 51bdaf64e4 | |||
| 9c8e0ebedc | |||
| e1bea8a8c8 | |||
| a6d3820104 | |||
| 36c793a5fb |
@@ -8,12 +8,42 @@ cleanuperr is a tool for automating the cleanup of unwanted or blocked files in
|
|||||||
|
|
||||||
cleanuperr was created primarily to address malicious files, such as `*.lnk` or `*.zipx`, that were getting stuck in Sonarr/Radarr and required manual intervention. Some of the reddit posts that made cleanuperr come to life can be found [here](https://www.reddit.com/r/sonarr/comments/1gqnx16/psa_sonarr_downloaded_a_virus/), [here](https://www.reddit.com/r/sonarr/comments/1gqwklr/sonar_downloaded_a_mkv_file_which_looked_like_a/), [here](https://www.reddit.com/r/sonarr/comments/1gpw2wa/downloaded_waiting_to_import/) and [here](https://www.reddit.com/r/sonarr/comments/1gpi344/downloads_not_importing_no_files_found/).
|
cleanuperr was created primarily to address malicious files, such as `*.lnk` or `*.zipx`, that were getting stuck in Sonarr/Radarr and required manual intervention. Some of the reddit posts that made cleanuperr come to life can be found [here](https://www.reddit.com/r/sonarr/comments/1gqnx16/psa_sonarr_downloaded_a_virus/), [here](https://www.reddit.com/r/sonarr/comments/1gqwklr/sonar_downloaded_a_mkv_file_which_looked_like_a/), [here](https://www.reddit.com/r/sonarr/comments/1gpw2wa/downloaded_waiting_to_import/) and [here](https://www.reddit.com/r/sonarr/comments/1gpi344/downloads_not_importing_no_files_found/).
|
||||||
|
|
||||||
The tool supports both qBittorrent's built-in exclusion features and its own blocklist-based system. Binaries for all platforms are provided, along with Docker images for easy deployment.
|
> [!IMPORTANT]
|
||||||
|
> **Features:**
|
||||||
|
> - Strike system to mark stalled or downloads stuck in metadata downloading.
|
||||||
|
> - Remove and block downloads that reached a maximum number of strikes.
|
||||||
|
> - Remove downloads blocked by qBittorrent or by cleanuperr's **content blocker**.
|
||||||
|
> - Trigger a search for downloads removed from the *arrs.
|
||||||
|
> - Clean up downloads that have been seeding for a certain amount of time.
|
||||||
|
> - Notify on strike or download removal.
|
||||||
|
|
||||||
#
|
cleanuperr supports both qBittorrent's built-in exclusion features and its own blocklist-based system. Binaries for all platforms are provided, along with Docker images for easy deployment.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> This tool is actively developed and still a work in progress, so using the `latest` Docker tag may result in breaking changes. Join the Discord server if you want to reach out to me quickly (or just stay updated on new releases) so we can squash those pesky bugs together:
|
||||||
|
>
|
||||||
|
> https://discord.gg/sWggpnmGNY
|
||||||
|
|
||||||
|
## Table of contents:
|
||||||
|
- [Naming choice](README.md#naming-choice)
|
||||||
|
- [Quick Start](README.md#quick-start)
|
||||||
|
- [How it works](README.md#how-it-works)
|
||||||
|
- [Setup](README.md#setup)
|
||||||
|
- [Usage](README.md#usage)
|
||||||
|
- [Docker Compose](README.md#docker-compose-yaml)
|
||||||
|
- [Environment Variables](README.md#environment-variables)
|
||||||
|
- [Binaries](README.md#binaries-if-youre-not-using-docker)
|
||||||
|
- [Credits](README.md#credits)
|
||||||
|
|
||||||
|
## Naming choice
|
||||||
|
|
||||||
|
I've had people asking why it's `cleanuperr` and not `cleanuparr` and that I should change it. This name was intentional.
|
||||||
|
|
||||||
|
I've seen a few discussions on this type of naming and I've decided that I didn't deserve the `arr` moniker since `cleanuperr` is not a fork of `NZB.Drone` and it does not have any affiliation with the arrs. I still wanted to keep the naming style close enough though, to suggest a correlation between them.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> ### Quick Start
|
|
||||||
>
|
>
|
||||||
> 1. **Docker (Recommended)**
|
> 1. **Docker (Recommended)**
|
||||||
> Pull the Docker image from `ghcr.io/flmorg/cleanuperr:latest`.
|
> Pull the Docker image from `ghcr.io/flmorg/cleanuperr:latest`.
|
||||||
@@ -27,10 +57,6 @@ The tool supports both qBittorrent's built-in exclusion features and its own blo
|
|||||||
> [!TIP]
|
> [!TIP]
|
||||||
> Refer to the [Environment variables](#Environment-variables) section for detailed configuration instructions and the [Setup](#Setup) section for an in-depth explanation of the cleanup process.
|
> Refer to the [Environment variables](#Environment-variables) section for detailed configuration instructions and the [Setup](#Setup) section for an in-depth explanation of the cleanup process.
|
||||||
|
|
||||||
## Key features
|
|
||||||
- Marks unwanted files as skip/unwanted in the download client.
|
|
||||||
- Automatically strikes stalled or stuck downloads.
|
|
||||||
- Removes and blocks downloads that reached the maximum number of strikes or are marked as unwanted by the download client or by cleanuperr and triggers a search for removed downloads.
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> Only the **latest versions** of the following apps are supported, or earlier versions that have the same API as the latest version:
|
> Only the **latest versions** of the following apps are supported, or earlier versions that have the same API as the latest version:
|
||||||
@@ -41,10 +67,6 @@ The tool supports both qBittorrent's built-in exclusion features and its own blo
|
|||||||
> - Radarr
|
> - Radarr
|
||||||
> - Lidarr
|
> - Lidarr
|
||||||
|
|
||||||
This tool is actively developed and still a work in progress, so using the `latest` Docker tag may result in breaking changes. Join the Discord server if you want to reach out to me quickly (or just stay updated on new releases) so we can squash those pesky bugs together:
|
|
||||||
|
|
||||||
> https://discord.gg/sWggpnmGNY
|
|
||||||
|
|
||||||
# How it works
|
# How it works
|
||||||
|
|
||||||
1. **Content blocker** will:
|
1. **Content blocker** will:
|
||||||
@@ -102,7 +124,8 @@ This tool is actively developed and still a work in progress, so using the `late
|
|||||||
3. Optionally set failed import message patterns to ignore using `QUEUECLEANER__IMPORT_FAILED_IGNORE_PATTERNS__<NUMBER>`.
|
3. Optionally set failed import message patterns to ignore using `QUEUECLEANER__IMPORT_FAILED_IGNORE_PATTERNS__<NUMBER>`.
|
||||||
4. Set `DOWNLOAD_CLIENT` to `none`.
|
4. Set `DOWNLOAD_CLIENT` to `none`.
|
||||||
|
|
||||||
**No other action involving a download client would work (e.g. content blocking, removing stalled downloads, excluding private trackers).**
|
> [!WARNING]
|
||||||
|
> When `DOWNLOAD_CLIENT=none`, no other action involving a download client would work (e.g. content blocking, removing stalled downloads, excluding private trackers).
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -117,6 +140,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./cleanuperr/logs:/var/logs
|
- ./cleanuperr/logs:/var/logs
|
||||||
environment:
|
environment:
|
||||||
|
- TZ=America/New_York
|
||||||
- DRY_RUN=false
|
- DRY_RUN=false
|
||||||
|
|
||||||
- LOGGING__LOGLEVEL=Information
|
- LOGGING__LOGLEVEL=Information
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ deployment:
|
|||||||
repository: ghcr.io/flmorg/cleanuperr
|
repository: ghcr.io/flmorg/cleanuperr
|
||||||
tag: latest
|
tag: latest
|
||||||
env:
|
env:
|
||||||
|
- name: DRY_RUN
|
||||||
|
value: "false"
|
||||||
|
|
||||||
- name: LOGGING__LOGLEVEL
|
- name: LOGGING__LOGLEVEL
|
||||||
value: Debug
|
value: Debug
|
||||||
- name: LOGGING__FILE__ENABLED
|
- name: LOGGING__FILE__ENABLED
|
||||||
@@ -18,6 +21,7 @@ deployment:
|
|||||||
value: /var/logs
|
value: /var/logs
|
||||||
- name: LOGGING__ENHANCED
|
- name: LOGGING__ENHANCED
|
||||||
value: "true"
|
value: "true"
|
||||||
|
|
||||||
- name: TRIGGERS__QUEUECLEANER
|
- name: TRIGGERS__QUEUECLEANER
|
||||||
value: 0 0/5 * * * ?
|
value: 0 0/5 * * * ?
|
||||||
- name: TRIGGERS__CONTENTBLOCKER
|
- name: TRIGGERS__CONTENTBLOCKER
|
||||||
@@ -47,6 +51,9 @@ deployment:
|
|||||||
- name: CONTENTBLOCKER__DELETE_PRIVATE
|
- name: CONTENTBLOCKER__DELETE_PRIVATE
|
||||||
value: "false"
|
value: "false"
|
||||||
|
|
||||||
|
- name: DOWNLOADCLEANER__ENABLED
|
||||||
|
value: "false"
|
||||||
|
|
||||||
- name: DOWNLOAD_CLIENT
|
- name: DOWNLOAD_CLIENT
|
||||||
value: qbittorrent
|
value: qbittorrent
|
||||||
- name: QBITTORRENT__URL
|
- name: QBITTORRENT__URL
|
||||||
@@ -75,6 +82,17 @@ deployment:
|
|||||||
value: http://service.radarr-low-res.svc.cluster.local
|
value: http://service.radarr-low-res.svc.cluster.local
|
||||||
- name: RADARR__INSTANCES__1__URL
|
- name: RADARR__INSTANCES__1__URL
|
||||||
value: http://service.radarr-high-res.svc.cluster.local
|
value: http://service.radarr-high-res.svc.cluster.local
|
||||||
|
|
||||||
|
- name: NOTIFIARR__ON_IMPORT_FAILED_STRIKE
|
||||||
|
value: "true"
|
||||||
|
- name: NOTIFIARR__ON_STALLED_STRIKE
|
||||||
|
value: "true"
|
||||||
|
- name: NOTIFIARR__ON_QUEUE_ITEM_DELETED
|
||||||
|
value: "true"
|
||||||
|
- name: NOTIFIARR__ON_DOWNLOAD_CLEANED
|
||||||
|
value: "true"
|
||||||
|
- name: NOTIFIARR__CHANNEL_ID
|
||||||
|
value: "1340708411259748413"
|
||||||
envFromSecret:
|
envFromSecret:
|
||||||
- secretName: qbit-auth
|
- secretName: qbit-auth
|
||||||
envs:
|
envs:
|
||||||
@@ -94,6 +112,10 @@ deployment:
|
|||||||
key: RDRL_API_KEY
|
key: RDRL_API_KEY
|
||||||
- name: RADARR__INSTANCES__1__APIKEY
|
- name: RADARR__INSTANCES__1__APIKEY
|
||||||
key: RDRH_API_KEY
|
key: RDRH_API_KEY
|
||||||
|
- secretName: notifiarr-auth
|
||||||
|
envs:
|
||||||
|
- name: NOTIFIARR__API_KEY
|
||||||
|
key: API_KEY
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: 0m
|
cpu: 0m
|
||||||
@@ -134,3 +156,7 @@ vaultSecrets:
|
|||||||
templates:
|
templates:
|
||||||
SNRL_API_KEY: "{% .Secrets.low_api_key %}"
|
SNRL_API_KEY: "{% .Secrets.low_api_key %}"
|
||||||
SNRH_API_KEY: "{% .Secrets.high_api_key %}"
|
SNRH_API_KEY: "{% .Secrets.high_api_key %}"
|
||||||
|
- name: notifiarr-auth
|
||||||
|
path: secrets/notifiarr
|
||||||
|
templates:
|
||||||
|
API_KEY: "{% .Secrets.passthrough_api_key %}"
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using Castle.DynamicProxy;
|
|
||||||
using Common.Configuration.General;
|
using Common.Configuration.General;
|
||||||
using Common.Helpers;
|
using Common.Helpers;
|
||||||
using Infrastructure.Interceptors;
|
|
||||||
using Infrastructure.Verticals.DownloadClient.Deluge;
|
using Infrastructure.Verticals.DownloadClient.Deluge;
|
||||||
using Infrastructure.Verticals.Notifications.Consumers;
|
using Infrastructure.Verticals.Notifications.Consumers;
|
||||||
using Infrastructure.Verticals.Notifications.Models;
|
using Infrastructure.Verticals.Notifications.Models;
|
||||||
@@ -42,8 +40,7 @@ public static class MainDI
|
|||||||
e.PrefetchCount = 1;
|
e.PrefetchCount = 1;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
.AddDryRunInterceptor();
|
|
||||||
|
|
||||||
private static IServiceCollection AddHttpClients(this IServiceCollection services, IConfiguration configuration)
|
private static IServiceCollection AddHttpClients(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
@@ -65,7 +62,7 @@ public static class MainDI
|
|||||||
services
|
services
|
||||||
.AddHttpClient(nameof(DelugeService), x =>
|
.AddHttpClient(nameof(DelugeService), x =>
|
||||||
{
|
{
|
||||||
x.Timeout = TimeSpan.FromSeconds(5);
|
x.Timeout = TimeSpan.FromSeconds(config.Timeout);
|
||||||
})
|
})
|
||||||
.ConfigurePrimaryHttpMessageHandler(_ =>
|
.ConfigurePrimaryHttpMessageHandler(_ =>
|
||||||
{
|
{
|
||||||
@@ -91,31 +88,4 @@ public static class MainDI
|
|||||||
.OrResult(response => !response.IsSuccessStatusCode && response.StatusCode != HttpStatusCode.Unauthorized)
|
.OrResult(response => !response.IsSuccessStatusCode && response.StatusCode != HttpStatusCode.Unauthorized)
|
||||||
.WaitAndRetryAsync(config.MaxRetries, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))
|
.WaitAndRetryAsync(config.MaxRetries, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))
|
||||||
);
|
);
|
||||||
|
|
||||||
private static IServiceCollection AddDryRunInterceptor(this IServiceCollection services)
|
|
||||||
{
|
|
||||||
services
|
|
||||||
.Where(s => s.ServiceType != typeof(IDryRunService) && typeof(IDryRunService).IsAssignableFrom(s.ServiceType))
|
|
||||||
.ToList()
|
|
||||||
.ForEach(service =>
|
|
||||||
{
|
|
||||||
services.Decorate(service.ServiceType, (target, svc) =>
|
|
||||||
{
|
|
||||||
ProxyGenerator proxyGenerator = new();
|
|
||||||
DryRunAsyncInterceptor interceptor = svc.GetRequiredService<DryRunAsyncInterceptor>();
|
|
||||||
|
|
||||||
object implementation = proxyGenerator.CreateClassProxyWithTarget(
|
|
||||||
service.ServiceType,
|
|
||||||
target,
|
|
||||||
interceptor
|
|
||||||
);
|
|
||||||
|
|
||||||
((IInterceptedService)target).Proxy = implementation;
|
|
||||||
|
|
||||||
return implementation;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ public static class NotificationsDI
|
|||||||
.Configure<NotifiarrConfig>(configuration.GetSection(NotifiarrConfig.SectionName))
|
.Configure<NotifiarrConfig>(configuration.GetSection(NotifiarrConfig.SectionName))
|
||||||
.AddTransient<INotifiarrProxy, NotifiarrProxy>()
|
.AddTransient<INotifiarrProxy, NotifiarrProxy>()
|
||||||
.AddTransient<INotificationProvider, NotifiarrProvider>()
|
.AddTransient<INotificationProvider, NotifiarrProvider>()
|
||||||
.AddTransient<NotificationPublisher>()
|
.AddTransient<INotificationPublisher, NotificationPublisher>()
|
||||||
.AddTransient<INotificationFactory, NotificationFactory>()
|
.AddTransient<INotificationFactory, NotificationFactory>()
|
||||||
.AddTransient<NotificationService>();
|
.AddTransient<NotificationService>();
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ public static class ServicesDI
|
|||||||
{
|
{
|
||||||
public static IServiceCollection AddServices(this IServiceCollection services) =>
|
public static IServiceCollection AddServices(this IServiceCollection services) =>
|
||||||
services
|
services
|
||||||
.AddTransient<DryRunAsyncInterceptor>()
|
.AddTransient<IDryRunInterceptor, DryRunInterceptor>()
|
||||||
.AddTransient<SonarrClient>()
|
.AddTransient<SonarrClient>()
|
||||||
.AddTransient<RadarrClient>()
|
.AddTransient<RadarrClient>()
|
||||||
.AddTransient<LidarrClient>()
|
.AddTransient<LidarrClient>()
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Executable;
|
||||||
|
|
||||||
|
public static class HostExtensions
|
||||||
|
{
|
||||||
|
public static IHost Init(this IHost host)
|
||||||
|
{
|
||||||
|
ILogger<Program> logger = host.Services.GetRequiredService<ILogger<Program>>();
|
||||||
|
|
||||||
|
Version? version = Assembly.GetExecutingAssembly().GetName().Version;
|
||||||
|
|
||||||
|
logger.LogInformation(
|
||||||
|
version is null
|
||||||
|
? "cleanuperr version not detected"
|
||||||
|
: $"cleanuperr v{version.Major}.{version.Minor}.{version.Build}"
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.LogInformation("timezone: {tz}", TimeZoneInfo.Local.Id);
|
||||||
|
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Reflection;
|
using Executable;
|
||||||
using Executable.DependencyInjection;
|
using Executable.DependencyInjection;
|
||||||
|
|
||||||
var builder = Host.CreateApplicationBuilder(args);
|
var builder = Host.CreateApplicationBuilder(args);
|
||||||
@@ -7,15 +7,6 @@ builder.Services.AddInfrastructure(builder.Configuration);
|
|||||||
builder.Logging.AddLogging(builder.Configuration);
|
builder.Logging.AddLogging(builder.Configuration);
|
||||||
|
|
||||||
var host = builder.Build();
|
var host = builder.Build();
|
||||||
|
host.Init();
|
||||||
var logger = host.Services.GetRequiredService<ILogger<Program>>();
|
|
||||||
|
|
||||||
var version = Assembly.GetExecutingAssembly().GetName().Version;
|
|
||||||
|
|
||||||
logger.LogInformation(
|
|
||||||
version is null
|
|
||||||
? "cleanuperr version not detected"
|
|
||||||
: $"cleanuperr v{version.Major}.{version.Minor}.{version.Build}"
|
|
||||||
);
|
|
||||||
|
|
||||||
host.Run();
|
host.Run();
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using Common.Configuration.ContentBlocker;
|
using Common.Configuration.ContentBlocker;
|
||||||
using Common.Configuration.DownloadCleaner;
|
using Common.Configuration.DownloadCleaner;
|
||||||
using Common.Configuration.QueueCleaner;
|
using Common.Configuration.QueueCleaner;
|
||||||
|
using Infrastructure.Interceptors;
|
||||||
using Infrastructure.Verticals.ContentBlocker;
|
using Infrastructure.Verticals.ContentBlocker;
|
||||||
using Infrastructure.Verticals.DownloadClient;
|
using Infrastructure.Verticals.DownloadClient;
|
||||||
using Infrastructure.Verticals.ItemStriker;
|
using Infrastructure.Verticals.ItemStriker;
|
||||||
@@ -53,7 +54,8 @@ public class DownloadServiceFixture : IDisposable
|
|||||||
downloadCleanerOptions.Value.Returns(new DownloadCleanerConfig());
|
downloadCleanerOptions.Value.Returns(new DownloadCleanerConfig());
|
||||||
|
|
||||||
var filenameEvaluator = Substitute.For<IFilenameEvaluator>();
|
var filenameEvaluator = Substitute.For<IFilenameEvaluator>();
|
||||||
var notifier = Substitute.For<NotificationPublisher>();
|
var notifier = Substitute.For<INotificationPublisher>();
|
||||||
|
var dryRunInterceptor = Substitute.For<IDryRunInterceptor>();
|
||||||
|
|
||||||
return new TestDownloadService(
|
return new TestDownloadService(
|
||||||
Logger,
|
Logger,
|
||||||
@@ -63,7 +65,8 @@ public class DownloadServiceFixture : IDisposable
|
|||||||
Cache,
|
Cache,
|
||||||
filenameEvaluator,
|
filenameEvaluator,
|
||||||
Striker,
|
Striker,
|
||||||
notifier
|
notifier,
|
||||||
|
dryRunInterceptor
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Text.RegularExpressions;
|
|||||||
using Common.Configuration.ContentBlocker;
|
using Common.Configuration.ContentBlocker;
|
||||||
using Common.Configuration.DownloadCleaner;
|
using Common.Configuration.DownloadCleaner;
|
||||||
using Common.Configuration.QueueCleaner;
|
using Common.Configuration.QueueCleaner;
|
||||||
|
using Infrastructure.Interceptors;
|
||||||
using Infrastructure.Verticals.ContentBlocker;
|
using Infrastructure.Verticals.ContentBlocker;
|
||||||
using Infrastructure.Verticals.DownloadClient;
|
using Infrastructure.Verticals.DownloadClient;
|
||||||
using Infrastructure.Verticals.ItemStriker;
|
using Infrastructure.Verticals.ItemStriker;
|
||||||
@@ -23,9 +24,12 @@ public class TestDownloadService : DownloadService
|
|||||||
IMemoryCache cache,
|
IMemoryCache cache,
|
||||||
IFilenameEvaluator filenameEvaluator,
|
IFilenameEvaluator filenameEvaluator,
|
||||||
IStriker striker,
|
IStriker striker,
|
||||||
NotificationPublisher notifier)
|
INotificationPublisher notifier,
|
||||||
: base(logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig,
|
IDryRunInterceptor dryRunInterceptor
|
||||||
cache, filenameEvaluator, striker, notifier)
|
) : base(
|
||||||
|
logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig, cache,
|
||||||
|
filenameEvaluator, striker, notifier, dryRunInterceptor
|
||||||
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Castle.Core.AsyncInterceptor" Version="2.1.0" />
|
|
||||||
<PackageReference Include="FLM.QBittorrent" Version="1.0.0" />
|
<PackageReference Include="FLM.QBittorrent" Version="1.0.0" />
|
||||||
<PackageReference Include="FLM.Transmission" Version="1.0.2" />
|
<PackageReference Include="FLM.Transmission" Version="1.0.2" />
|
||||||
<PackageReference Include="Mapster" Version="7.4.0" />
|
<PackageReference Include="Mapster" Version="7.4.0" />
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Castle.DynamicProxy;
|
|
||||||
using Common.Attributes;
|
using Common.Attributes;
|
||||||
using Common.Configuration.General;
|
using Common.Configuration.General;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -7,39 +6,68 @@ using Microsoft.Extensions.Options;
|
|||||||
|
|
||||||
namespace Infrastructure.Interceptors;
|
namespace Infrastructure.Interceptors;
|
||||||
|
|
||||||
public class DryRunAsyncInterceptor : AsyncInterceptorBase
|
public class DryRunInterceptor : IDryRunInterceptor
|
||||||
{
|
{
|
||||||
private readonly ILogger<DryRunAsyncInterceptor> _logger;
|
private readonly ILogger<DryRunInterceptor> _logger;
|
||||||
private readonly DryRunConfig _config;
|
private readonly DryRunConfig _config;
|
||||||
|
|
||||||
public DryRunAsyncInterceptor(ILogger<DryRunAsyncInterceptor> logger, IOptions<DryRunConfig> config)
|
public DryRunInterceptor(ILogger<DryRunInterceptor> logger, IOptions<DryRunConfig> config)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_config = config.Value;
|
_config = config.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func<IInvocation, IInvocationProceedInfo, Task> proceed)
|
public void Intercept(Action action)
|
||||||
{
|
{
|
||||||
MethodInfo? method = invocation.MethodInvocationTarget ?? invocation.Method;
|
MethodInfo methodInfo = action.Method;
|
||||||
if (IsDryRun(method))
|
|
||||||
|
if (IsDryRun(methodInfo))
|
||||||
{
|
{
|
||||||
_logger.LogInformation("[DRY RUN] skipping method: {name}", method.Name);
|
_logger.LogInformation("[DRY RUN] skipping method: {name}", methodInfo.Name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await proceed(invocation, proceedInfo);
|
action();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task<TResult> InterceptAsync<TResult>(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func<IInvocation, IInvocationProceedInfo, Task<TResult>> proceed)
|
public Task InterceptAsync(Delegate action, params object[] parameters)
|
||||||
{
|
{
|
||||||
MethodInfo? method = invocation.MethodInvocationTarget ?? invocation.Method;
|
MethodInfo methodInfo = action.Method;
|
||||||
if (IsDryRun(method))
|
|
||||||
|
if (IsDryRun(methodInfo))
|
||||||
{
|
{
|
||||||
_logger.LogInformation("[DRY RUN] skipping method: {name}", method.Name);
|
_logger.LogInformation("[DRY RUN] skipping method: {name}", methodInfo.Name);
|
||||||
return default!;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await proceed(invocation, proceedInfo);
|
object? result = action.DynamicInvoke(parameters);
|
||||||
|
|
||||||
|
if (result is Task task)
|
||||||
|
{
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<T?> InterceptAsync<T>(Delegate action, params object[] parameters)
|
||||||
|
{
|
||||||
|
MethodInfo methodInfo = action.Method;
|
||||||
|
|
||||||
|
if (IsDryRun(methodInfo))
|
||||||
|
{
|
||||||
|
_logger.LogInformation("[DRY RUN] skipping method: {name}", methodInfo.Name);
|
||||||
|
return Task.FromResult(default(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
object? result = action.DynamicInvoke(parameters);
|
||||||
|
|
||||||
|
if (result is Task<T?> task)
|
||||||
|
{
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(default(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsDryRun(MethodInfo method)
|
private bool IsDryRun(MethodInfo method)
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Infrastructure.Interceptors;
|
||||||
|
|
||||||
|
public interface IDryRunInterceptor
|
||||||
|
{
|
||||||
|
void Intercept(Action action);
|
||||||
|
|
||||||
|
Task InterceptAsync(Delegate action, params object[] parameters);
|
||||||
|
|
||||||
|
Task<T?> InterceptAsync<T>(Delegate action, params object[] parameters);
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
namespace Infrastructure.Interceptors;
|
|
||||||
|
|
||||||
public interface IDryRunService : IInterceptedService
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Infrastructure.Interceptors;
|
|
||||||
|
|
||||||
public interface IInterceptedService
|
|
||||||
{
|
|
||||||
public object Proxy { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
namespace Infrastructure.Interceptors;
|
|
||||||
|
|
||||||
public class InterceptedService : IInterceptedService
|
|
||||||
{
|
|
||||||
private object? _proxy;
|
|
||||||
|
|
||||||
public object Proxy
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_proxy is null)
|
|
||||||
{
|
|
||||||
throw new Exception("Proxy is not set");
|
|
||||||
}
|
|
||||||
|
|
||||||
return _proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
set => _proxy = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,27 +15,22 @@ using Newtonsoft.Json;
|
|||||||
|
|
||||||
namespace Infrastructure.Verticals.Arr;
|
namespace Infrastructure.Verticals.Arr;
|
||||||
|
|
||||||
public abstract class ArrClient : InterceptedService, IArrClient, IDryRunService
|
public abstract class ArrClient : IArrClient
|
||||||
{
|
{
|
||||||
protected readonly ILogger<ArrClient> _logger;
|
protected readonly ILogger<ArrClient> _logger;
|
||||||
protected readonly HttpClient _httpClient;
|
protected readonly HttpClient _httpClient;
|
||||||
protected readonly LoggingConfig _loggingConfig;
|
protected readonly LoggingConfig _loggingConfig;
|
||||||
protected readonly QueueCleanerConfig _queueCleanerConfig;
|
protected readonly QueueCleanerConfig _queueCleanerConfig;
|
||||||
protected readonly IStriker _striker;
|
protected readonly IStriker _striker;
|
||||||
|
protected readonly IDryRunInterceptor _dryRunInterceptor;
|
||||||
/// <summary>
|
|
||||||
/// Constructor to be used by interceptors.
|
|
||||||
/// </summary>
|
|
||||||
protected ArrClient()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ArrClient(
|
protected ArrClient(
|
||||||
ILogger<ArrClient> logger,
|
ILogger<ArrClient> logger,
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
IOptions<LoggingConfig> loggingConfig,
|
IOptions<LoggingConfig> loggingConfig,
|
||||||
IOptions<QueueCleanerConfig> queueCleanerConfig,
|
IOptions<QueueCleanerConfig> queueCleanerConfig,
|
||||||
IStriker striker
|
IStriker striker,
|
||||||
|
IDryRunInterceptor dryRunInterceptor
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -43,6 +38,7 @@ public abstract class ArrClient : InterceptedService, IArrClient, IDryRunService
|
|||||||
_loggingConfig = loggingConfig.Value;
|
_loggingConfig = loggingConfig.Value;
|
||||||
_queueCleanerConfig = queueCleanerConfig.Value;
|
_queueCleanerConfig = queueCleanerConfig.Value;
|
||||||
_striker = striker;
|
_striker = striker;
|
||||||
|
_dryRunInterceptor = dryRunInterceptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task<QueueListResponse> GetQueueItemsAsync(ArrInstance arrInstance, int page)
|
public virtual async Task<QueueListResponse> GetQueueItemsAsync(ArrInstance arrInstance, int page)
|
||||||
@@ -125,7 +121,8 @@ public abstract class ArrClient : InterceptedService, IArrClient, IDryRunService
|
|||||||
using HttpRequestMessage request = new(HttpMethod.Delete, uri);
|
using HttpRequestMessage request = new(HttpMethod.Delete, uri);
|
||||||
SetApiKey(request, arrInstance.ApiKey);
|
SetApiKey(request, arrInstance.ApiKey);
|
||||||
|
|
||||||
using var _ = await ((ArrClient)Proxy).SendRequestAsync(request);
|
HttpResponseMessage? response = await _dryRunInterceptor.InterceptAsync<HttpResponseMessage>(SendRequestAsync, request);
|
||||||
|
response?.Dispose();
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
removeFromClient
|
removeFromClient
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Common.Configuration.QueueCleaner;
|
|||||||
using Domain.Models.Arr;
|
using Domain.Models.Arr;
|
||||||
using Domain.Models.Arr.Queue;
|
using Domain.Models.Arr.Queue;
|
||||||
using Domain.Models.Lidarr;
|
using Domain.Models.Lidarr;
|
||||||
|
using Infrastructure.Interceptors;
|
||||||
using Infrastructure.Verticals.Arr.Interfaces;
|
using Infrastructure.Verticals.Arr.Interfaces;
|
||||||
using Infrastructure.Verticals.ItemStriker;
|
using Infrastructure.Verticals.ItemStriker;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -15,18 +16,14 @@ namespace Infrastructure.Verticals.Arr;
|
|||||||
|
|
||||||
public class LidarrClient : ArrClient, ILidarrClient
|
public class LidarrClient : ArrClient, ILidarrClient
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
|
||||||
public LidarrClient()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public LidarrClient(
|
public LidarrClient(
|
||||||
ILogger<LidarrClient> logger,
|
ILogger<LidarrClient> logger,
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
IOptions<LoggingConfig> loggingConfig,
|
IOptions<LoggingConfig> loggingConfig,
|
||||||
IOptions<QueueCleanerConfig> queueCleanerConfig,
|
IOptions<QueueCleanerConfig> queueCleanerConfig,
|
||||||
IStriker striker
|
IStriker striker,
|
||||||
) : base(logger, httpClientFactory, loggingConfig, queueCleanerConfig, striker)
|
IDryRunInterceptor dryRunInterceptor
|
||||||
|
) : base(logger, httpClientFactory, loggingConfig, queueCleanerConfig, striker, dryRunInterceptor)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +61,8 @@ public class LidarrClient : ArrClient, ILidarrClient
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var _ = await ((LidarrClient)Proxy).SendRequestAsync(request);
|
HttpResponseMessage? response = await _dryRunInterceptor.InterceptAsync<HttpResponseMessage>(SendRequestAsync, request);
|
||||||
|
response?.Dispose();
|
||||||
|
|
||||||
_logger.LogInformation("{log}", GetSearchLog(arrInstance.Url, command, true, logContext));
|
_logger.LogInformation("{log}", GetSearchLog(arrInstance.Url, command, true, logContext));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Common.Configuration.QueueCleaner;
|
|||||||
using Domain.Models.Arr;
|
using Domain.Models.Arr;
|
||||||
using Domain.Models.Arr.Queue;
|
using Domain.Models.Arr.Queue;
|
||||||
using Domain.Models.Radarr;
|
using Domain.Models.Radarr;
|
||||||
|
using Infrastructure.Interceptors;
|
||||||
using Infrastructure.Verticals.Arr.Interfaces;
|
using Infrastructure.Verticals.Arr.Interfaces;
|
||||||
using Infrastructure.Verticals.ItemStriker;
|
using Infrastructure.Verticals.ItemStriker;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -15,18 +16,14 @@ namespace Infrastructure.Verticals.Arr;
|
|||||||
|
|
||||||
public class RadarrClient : ArrClient, IRadarrClient
|
public class RadarrClient : ArrClient, IRadarrClient
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
|
||||||
public RadarrClient()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public RadarrClient(
|
public RadarrClient(
|
||||||
ILogger<ArrClient> logger,
|
ILogger<ArrClient> logger,
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
IOptions<LoggingConfig> loggingConfig,
|
IOptions<LoggingConfig> loggingConfig,
|
||||||
IOptions<QueueCleanerConfig> queueCleanerConfig,
|
IOptions<QueueCleanerConfig> queueCleanerConfig,
|
||||||
IStriker striker
|
IStriker striker,
|
||||||
) : base(logger, httpClientFactory, loggingConfig, queueCleanerConfig, striker)
|
IDryRunInterceptor dryRunInterceptor
|
||||||
|
) : base(logger, httpClientFactory, loggingConfig, queueCleanerConfig, striker, dryRunInterceptor)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +69,8 @@ public class RadarrClient : ArrClient, IRadarrClient
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var _ = await ((RadarrClient)Proxy).SendRequestAsync(request);
|
HttpResponseMessage? response = await _dryRunInterceptor.InterceptAsync<HttpResponseMessage>(SendRequestAsync, request);
|
||||||
|
response?.Dispose();
|
||||||
|
|
||||||
_logger.LogInformation("{log}", GetSearchLog(arrInstance.Url, command, true, logContext));
|
_logger.LogInformation("{log}", GetSearchLog(arrInstance.Url, command, true, logContext));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Common.Configuration.QueueCleaner;
|
|||||||
using Domain.Models.Arr;
|
using Domain.Models.Arr;
|
||||||
using Domain.Models.Arr.Queue;
|
using Domain.Models.Arr.Queue;
|
||||||
using Domain.Models.Sonarr;
|
using Domain.Models.Sonarr;
|
||||||
|
using Infrastructure.Interceptors;
|
||||||
using Infrastructure.Verticals.Arr.Interfaces;
|
using Infrastructure.Verticals.Arr.Interfaces;
|
||||||
using Infrastructure.Verticals.ItemStriker;
|
using Infrastructure.Verticals.ItemStriker;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -16,18 +17,14 @@ namespace Infrastructure.Verticals.Arr;
|
|||||||
|
|
||||||
public class SonarrClient : ArrClient, ISonarrClient
|
public class SonarrClient : ArrClient, ISonarrClient
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
|
||||||
public SonarrClient()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public SonarrClient(
|
public SonarrClient(
|
||||||
ILogger<SonarrClient> logger,
|
ILogger<SonarrClient> logger,
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
IOptions<LoggingConfig> loggingConfig,
|
IOptions<LoggingConfig> loggingConfig,
|
||||||
IOptions<QueueCleanerConfig> queueCleanerConfig,
|
IOptions<QueueCleanerConfig> queueCleanerConfig,
|
||||||
IStriker striker
|
IStriker striker,
|
||||||
) : base(logger, httpClientFactory, loggingConfig, queueCleanerConfig, striker)
|
IDryRunInterceptor dryRunInterceptor
|
||||||
|
) : base(logger, httpClientFactory, loggingConfig, queueCleanerConfig, striker, dryRunInterceptor)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +65,8 @@ public class SonarrClient : ArrClient, ISonarrClient
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var _ = await ((SonarrClient)Proxy).SendRequestAsync(request);
|
HttpResponseMessage? response = await _dryRunInterceptor.InterceptAsync<HttpResponseMessage>(SendRequestAsync, request);
|
||||||
|
response?.Dispose();
|
||||||
|
|
||||||
_logger.LogInformation("{log}", GetSearchLog(command.SearchType, arrInstance.Url, command, true, logContext));
|
_logger.LogInformation("{log}", GetSearchLog(command.SearchType, arrInstance.Url, command, true, logContext));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public sealed class ContentBlocker : GenericHandler
|
|||||||
ArrQueueIterator arrArrQueueIterator,
|
ArrQueueIterator arrArrQueueIterator,
|
||||||
BlocklistProvider blocklistProvider,
|
BlocklistProvider blocklistProvider,
|
||||||
DownloadServiceFactory downloadServiceFactory,
|
DownloadServiceFactory downloadServiceFactory,
|
||||||
NotificationPublisher notifier
|
INotificationPublisher notifier
|
||||||
) : base(
|
) : base(
|
||||||
logger, downloadClientConfig,
|
logger, downloadClientConfig,
|
||||||
sonarrConfig, radarrConfig, lidarrConfig,
|
sonarrConfig, radarrConfig, lidarrConfig,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ public sealed class DownloadCleaner : GenericHandler
|
|||||||
LidarrClient lidarrClient,
|
LidarrClient lidarrClient,
|
||||||
ArrQueueIterator arrArrQueueIterator,
|
ArrQueueIterator arrArrQueueIterator,
|
||||||
DownloadServiceFactory downloadServiceFactory,
|
DownloadServiceFactory downloadServiceFactory,
|
||||||
NotificationPublisher notifier
|
INotificationPublisher notifier
|
||||||
) : base(
|
) : base(
|
||||||
logger, downloadClientConfig,
|
logger, downloadClientConfig,
|
||||||
sonarrConfig, radarrConfig, lidarrConfig,
|
sonarrConfig, radarrConfig, lidarrConfig,
|
||||||
@@ -46,6 +46,12 @@ public sealed class DownloadCleaner : GenericHandler
|
|||||||
|
|
||||||
public override async Task ExecuteAsync()
|
public override async Task ExecuteAsync()
|
||||||
{
|
{
|
||||||
|
if (_downloadClientConfig.DownloadClient is Common.Enums.DownloadClient.None)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("download client is set to none");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_config.Categories?.Count is null or 0)
|
if (_config.Categories?.Count is null or 0)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("no categories configured");
|
_logger.LogWarning("no categories configured");
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ using Common.Configuration.DownloadClient;
|
|||||||
using Common.Configuration.QueueCleaner;
|
using Common.Configuration.QueueCleaner;
|
||||||
using Domain.Enums;
|
using Domain.Enums;
|
||||||
using Domain.Models.Deluge.Response;
|
using Domain.Models.Deluge.Response;
|
||||||
|
using Infrastructure.Interceptors;
|
||||||
using Infrastructure.Verticals.ContentBlocker;
|
using Infrastructure.Verticals.ContentBlocker;
|
||||||
using Infrastructure.Verticals.Context;
|
using Infrastructure.Verticals.Context;
|
||||||
using Infrastructure.Verticals.ItemStriker;
|
using Infrastructure.Verticals.ItemStriker;
|
||||||
using Infrastructure.Verticals.Notifications;
|
using Infrastructure.Verticals.Notifications;
|
||||||
using MassTransit.Configuration;
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
@@ -22,11 +22,6 @@ public class DelugeService : DownloadService, IDelugeService
|
|||||||
{
|
{
|
||||||
private readonly DelugeClient _client;
|
private readonly DelugeClient _client;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public DelugeService()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public DelugeService(
|
public DelugeService(
|
||||||
ILogger<DelugeService> logger,
|
ILogger<DelugeService> logger,
|
||||||
IOptions<DelugeConfig> config,
|
IOptions<DelugeConfig> config,
|
||||||
@@ -37,8 +32,12 @@ public class DelugeService : DownloadService, IDelugeService
|
|||||||
IMemoryCache cache,
|
IMemoryCache cache,
|
||||||
IFilenameEvaluator filenameEvaluator,
|
IFilenameEvaluator filenameEvaluator,
|
||||||
IStriker striker,
|
IStriker striker,
|
||||||
NotificationPublisher notifier
|
INotificationPublisher notifier,
|
||||||
) : base(logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig, cache, filenameEvaluator, striker, notifier)
|
IDryRunInterceptor dryRunInterceptor
|
||||||
|
) : base(
|
||||||
|
logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig, cache,
|
||||||
|
filenameEvaluator, striker, notifier, dryRunInterceptor
|
||||||
|
)
|
||||||
{
|
{
|
||||||
config.Value.Validate();
|
config.Value.Validate();
|
||||||
_client = new (config, httpClientFactory);
|
_client = new (config, httpClientFactory);
|
||||||
@@ -190,7 +189,7 @@ public class DelugeService : DownloadService, IDelugeService
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
await ((DelugeService)Proxy).ChangeFilesPriority(hash, sortedPriorities);
|
await _dryRunInterceptor.InterceptAsync(ChangeFilesPriority, hash, sortedPriorities);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -246,7 +245,7 @@ public class DelugeService : DownloadService, IDelugeService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await ((DelugeService)Proxy).DeleteDownload(download.Hash);
|
await _dryRunInterceptor.InterceptAsync(DeleteDownload, download.Hash);
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"download cleaned | {reason} reached | {name}",
|
"download cleaned | {reason} reached | {name}",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ using Microsoft.Extensions.Options;
|
|||||||
|
|
||||||
namespace Infrastructure.Verticals.DownloadClient;
|
namespace Infrastructure.Verticals.DownloadClient;
|
||||||
|
|
||||||
public abstract class DownloadService : InterceptedService, IDownloadService
|
public abstract class DownloadService : IDownloadService
|
||||||
{
|
{
|
||||||
protected readonly ILogger<DownloadService> _logger;
|
protected readonly ILogger<DownloadService> _logger;
|
||||||
protected readonly QueueCleanerConfig _queueCleanerConfig;
|
protected readonly QueueCleanerConfig _queueCleanerConfig;
|
||||||
@@ -28,14 +28,8 @@ public abstract class DownloadService : InterceptedService, IDownloadService
|
|||||||
protected readonly IFilenameEvaluator _filenameEvaluator;
|
protected readonly IFilenameEvaluator _filenameEvaluator;
|
||||||
protected readonly IStriker _striker;
|
protected readonly IStriker _striker;
|
||||||
protected readonly MemoryCacheEntryOptions _cacheOptions;
|
protected readonly MemoryCacheEntryOptions _cacheOptions;
|
||||||
protected readonly NotificationPublisher _notifier;
|
protected readonly INotificationPublisher _notifier;
|
||||||
|
protected readonly IDryRunInterceptor _dryRunInterceptor;
|
||||||
/// <summary>
|
|
||||||
/// Constructor to be used by interceptors.
|
|
||||||
/// </summary>
|
|
||||||
protected DownloadService()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected DownloadService(
|
protected DownloadService(
|
||||||
ILogger<DownloadService> logger,
|
ILogger<DownloadService> logger,
|
||||||
@@ -45,7 +39,9 @@ public abstract class DownloadService : InterceptedService, IDownloadService
|
|||||||
IMemoryCache cache,
|
IMemoryCache cache,
|
||||||
IFilenameEvaluator filenameEvaluator,
|
IFilenameEvaluator filenameEvaluator,
|
||||||
IStriker striker,
|
IStriker striker,
|
||||||
NotificationPublisher notifier)
|
INotificationPublisher notifier,
|
||||||
|
IDryRunInterceptor dryRunInterceptor
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_queueCleanerConfig = queueCleanerConfig.Value;
|
_queueCleanerConfig = queueCleanerConfig.Value;
|
||||||
@@ -55,6 +51,7 @@ public abstract class DownloadService : InterceptedService, IDownloadService
|
|||||||
_filenameEvaluator = filenameEvaluator;
|
_filenameEvaluator = filenameEvaluator;
|
||||||
_striker = striker;
|
_striker = striker;
|
||||||
_notifier = notifier;
|
_notifier = notifier;
|
||||||
|
_dryRunInterceptor = dryRunInterceptor;
|
||||||
_cacheOptions = new MemoryCacheEntryOptions()
|
_cacheOptions = new MemoryCacheEntryOptions()
|
||||||
.SetSlidingExpiration(StaticConfiguration.TriggerValue + Constants.CacheLimitBuffer);
|
.SetSlidingExpiration(StaticConfiguration.TriggerValue + Constants.CacheLimitBuffer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Text.RegularExpressions;
|
|||||||
using Common.Configuration.ContentBlocker;
|
using Common.Configuration.ContentBlocker;
|
||||||
using Common.Configuration.DownloadCleaner;
|
using Common.Configuration.DownloadCleaner;
|
||||||
using Common.Configuration.QueueCleaner;
|
using Common.Configuration.QueueCleaner;
|
||||||
|
using Infrastructure.Interceptors;
|
||||||
using Infrastructure.Verticals.ContentBlocker;
|
using Infrastructure.Verticals.ContentBlocker;
|
||||||
using Infrastructure.Verticals.ItemStriker;
|
using Infrastructure.Verticals.ItemStriker;
|
||||||
using Infrastructure.Verticals.Notifications;
|
using Infrastructure.Verticals.Notifications;
|
||||||
@@ -14,12 +15,7 @@ namespace Infrastructure.Verticals.DownloadClient;
|
|||||||
|
|
||||||
public class DummyDownloadService : DownloadService
|
public class DummyDownloadService : DownloadService
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
public DummyDownloadService(ILogger<DownloadService> logger, IOptions<QueueCleanerConfig> queueCleanerConfig, IOptions<ContentBlockerConfig> contentBlockerConfig, IOptions<DownloadCleanerConfig> downloadCleanerConfig, IMemoryCache cache, IFilenameEvaluator filenameEvaluator, IStriker striker, INotificationPublisher notifier, IDryRunInterceptor dryRunInterceptor) : base(logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig, cache, filenameEvaluator, striker, notifier, dryRunInterceptor)
|
||||||
public DummyDownloadService()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public DummyDownloadService(ILogger<DownloadService> logger, IOptions<QueueCleanerConfig> queueCleanerConfig, IOptions<ContentBlockerConfig> contentBlockerConfig, IOptions<DownloadCleanerConfig> downloadCleanerConfig, IMemoryCache cache, IFilenameEvaluator filenameEvaluator, IStriker striker, NotificationPublisher notifier) : base(logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig, cache, filenameEvaluator, striker, notifier)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using Infrastructure.Interceptors;
|
|||||||
|
|
||||||
namespace Infrastructure.Verticals.DownloadClient;
|
namespace Infrastructure.Verticals.DownloadClient;
|
||||||
|
|
||||||
public interface IDownloadService : IDisposable, IDryRunService
|
public interface IDownloadService : IDisposable
|
||||||
{
|
{
|
||||||
public Task LoginAsync();
|
public Task LoginAsync();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace Infrastructure.Verticals.DownloadClient.QBittorrent;
|
namespace Infrastructure.Verticals.DownloadClient.QBittorrent;
|
||||||
|
|
||||||
public interface IQBitService : IDownloadService
|
public interface IQBitService : IDownloadService, IDisposable
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@ using Common.Configuration.DownloadClient;
|
|||||||
using Common.Configuration.QueueCleaner;
|
using Common.Configuration.QueueCleaner;
|
||||||
using Common.Helpers;
|
using Common.Helpers;
|
||||||
using Domain.Enums;
|
using Domain.Enums;
|
||||||
|
using Infrastructure.Interceptors;
|
||||||
using Infrastructure.Verticals.ContentBlocker;
|
using Infrastructure.Verticals.ContentBlocker;
|
||||||
using Infrastructure.Verticals.Context;
|
using Infrastructure.Verticals.Context;
|
||||||
using Infrastructure.Verticals.ItemStriker;
|
using Infrastructure.Verticals.ItemStriker;
|
||||||
@@ -24,11 +25,6 @@ public class QBitService : DownloadService, IQBitService
|
|||||||
private readonly QBitConfig _config;
|
private readonly QBitConfig _config;
|
||||||
private readonly QBittorrentClient _client;
|
private readonly QBittorrentClient _client;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public QBitService()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public QBitService(
|
public QBitService(
|
||||||
ILogger<QBitService> logger,
|
ILogger<QBitService> logger,
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
@@ -39,8 +35,12 @@ public class QBitService : DownloadService, IQBitService
|
|||||||
IMemoryCache cache,
|
IMemoryCache cache,
|
||||||
IFilenameEvaluator filenameEvaluator,
|
IFilenameEvaluator filenameEvaluator,
|
||||||
IStriker striker,
|
IStriker striker,
|
||||||
NotificationPublisher notifier
|
INotificationPublisher notifier,
|
||||||
) : base(logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig, cache, filenameEvaluator, striker, notifier)
|
IDryRunInterceptor dryRunInterceptor
|
||||||
|
) : base(
|
||||||
|
logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig, cache,
|
||||||
|
filenameEvaluator, striker, notifier, dryRunInterceptor
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_config = config.Value;
|
_config = config.Value;
|
||||||
_config.Validate();
|
_config.Validate();
|
||||||
@@ -200,7 +200,7 @@ public class QBitService : DownloadService, IQBitService
|
|||||||
|
|
||||||
foreach (int fileIndex in unwantedFiles)
|
foreach (int fileIndex in unwantedFiles)
|
||||||
{
|
{
|
||||||
await ((QBitService)Proxy).SkipFile(hash, fileIndex);
|
await _dryRunInterceptor.InterceptAsync(SkipFile, hash, fileIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -272,7 +272,7 @@ public class QBitService : DownloadService, IQBitService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await ((QBitService)Proxy).DeleteDownload(download.Hash);
|
await _dryRunInterceptor.InterceptAsync(DeleteDownload, download.Hash);
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"download cleaned | {reason} reached | {name}",
|
"download cleaned | {reason} reached | {name}",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Common.Configuration.DownloadClient;
|
|||||||
using Common.Configuration.QueueCleaner;
|
using Common.Configuration.QueueCleaner;
|
||||||
using Common.Helpers;
|
using Common.Helpers;
|
||||||
using Domain.Enums;
|
using Domain.Enums;
|
||||||
|
using Infrastructure.Interceptors;
|
||||||
using Infrastructure.Verticals.ContentBlocker;
|
using Infrastructure.Verticals.ContentBlocker;
|
||||||
using Infrastructure.Verticals.Context;
|
using Infrastructure.Verticals.Context;
|
||||||
using Infrastructure.Verticals.ItemStriker;
|
using Infrastructure.Verticals.ItemStriker;
|
||||||
@@ -26,11 +27,6 @@ public class TransmissionService : DownloadService, ITransmissionService
|
|||||||
private readonly Client _client;
|
private readonly Client _client;
|
||||||
private TorrentInfo[]? _torrentsCache;
|
private TorrentInfo[]? _torrentsCache;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public TransmissionService()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransmissionService(
|
public TransmissionService(
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
ILogger<TransmissionService> logger,
|
ILogger<TransmissionService> logger,
|
||||||
@@ -41,8 +37,12 @@ public class TransmissionService : DownloadService, ITransmissionService
|
|||||||
IMemoryCache cache,
|
IMemoryCache cache,
|
||||||
IFilenameEvaluator filenameEvaluator,
|
IFilenameEvaluator filenameEvaluator,
|
||||||
IStriker striker,
|
IStriker striker,
|
||||||
NotificationPublisher notifier
|
INotificationPublisher notifier,
|
||||||
) : base(logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig, cache, filenameEvaluator, striker, notifier)
|
IDryRunInterceptor dryRunInterceptor
|
||||||
|
) : base(
|
||||||
|
logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig, cache,
|
||||||
|
filenameEvaluator, striker, notifier, dryRunInterceptor
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_config = config.Value;
|
_config = config.Value;
|
||||||
_config.Validate();
|
_config.Validate();
|
||||||
@@ -175,7 +175,7 @@ public class TransmissionService : DownloadService, ITransmissionService
|
|||||||
|
|
||||||
_logger.LogDebug("changing priorities | torrent {hash}", hash);
|
_logger.LogDebug("changing priorities | torrent {hash}", hash);
|
||||||
|
|
||||||
await ((TransmissionService)Proxy).SetUnwantedFiles(torrent.Id, unwantedFiles.ToArray());
|
await _dryRunInterceptor.InterceptAsync(SetUnwantedFiles, torrent.Id, unwantedFiles.ToArray());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,16 @@ public class TransmissionService : DownloadService, ITransmissionService
|
|||||||
?.Where(x => !string.IsNullOrEmpty(x.HashString))
|
?.Where(x => !string.IsNullOrEmpty(x.HashString))
|
||||||
.Where(x => x.Status is 5 or 6)
|
.Where(x => x.Status is 5 or 6)
|
||||||
.Where(x => categories
|
.Where(x => categories
|
||||||
.Any(cat => x.DownloadDir?.EndsWith(cat.Name, StringComparison.InvariantCultureIgnoreCase) is true)
|
.Any(cat =>
|
||||||
|
{
|
||||||
|
if (x.DownloadDir is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Path.GetFileName(Path.TrimEndingDirectorySeparator(x.DownloadDir))
|
||||||
|
.Equals(cat.Name, StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
})
|
||||||
)
|
)
|
||||||
.Cast<object>()
|
.Cast<object>()
|
||||||
.ToList();
|
.ToList();
|
||||||
@@ -220,7 +229,16 @@ public class TransmissionService : DownloadService, ITransmissionService
|
|||||||
}
|
}
|
||||||
|
|
||||||
Category? category = categoriesToClean
|
Category? category = categoriesToClean
|
||||||
.FirstOrDefault(x => download.DownloadDir?.EndsWith(x.Name, StringComparison.InvariantCultureIgnoreCase) is true);
|
.FirstOrDefault(x =>
|
||||||
|
{
|
||||||
|
if (download.DownloadDir is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Path.GetFileName(Path.TrimEndingDirectorySeparator(download.DownloadDir))
|
||||||
|
.Equals(x.Name, StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
});
|
||||||
|
|
||||||
if (category is null)
|
if (category is null)
|
||||||
{
|
{
|
||||||
@@ -250,7 +268,7 @@ public class TransmissionService : DownloadService, ITransmissionService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await ((TransmissionService)Proxy).RemoveDownloadAsync(download.Id);
|
await _dryRunInterceptor.InterceptAsync(RemoveDownloadAsync, download.Id);
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"download cleaned | {reason} reached | {name}",
|
"download cleaned | {reason} reached | {name}",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Common.Helpers;
|
using Common.Helpers;
|
||||||
using Domain.Enums;
|
using Domain.Enums;
|
||||||
using Infrastructure.Helpers;
|
using Infrastructure.Helpers;
|
||||||
|
using Infrastructure.Interceptors;
|
||||||
using Infrastructure.Verticals.Context;
|
using Infrastructure.Verticals.Context;
|
||||||
using Infrastructure.Verticals.Notifications;
|
using Infrastructure.Verticals.Notifications;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
@@ -13,13 +14,15 @@ public sealed class Striker : IStriker
|
|||||||
private readonly ILogger<Striker> _logger;
|
private readonly ILogger<Striker> _logger;
|
||||||
private readonly IMemoryCache _cache;
|
private readonly IMemoryCache _cache;
|
||||||
private readonly MemoryCacheEntryOptions _cacheOptions;
|
private readonly MemoryCacheEntryOptions _cacheOptions;
|
||||||
private readonly NotificationPublisher _notifier;
|
private readonly INotificationPublisher _notifier;
|
||||||
|
private readonly IDryRunInterceptor _dryRunInterceptor;
|
||||||
|
|
||||||
public Striker(ILogger<Striker> logger, IMemoryCache cache, NotificationPublisher notifier)
|
public Striker(ILogger<Striker> logger, IMemoryCache cache, INotificationPublisher notifier, IDryRunInterceptor dryRunInterceptor)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_cache = cache;
|
_cache = cache;
|
||||||
_notifier = notifier;
|
_notifier = notifier;
|
||||||
|
_dryRunInterceptor = dryRunInterceptor;
|
||||||
_cacheOptions = new MemoryCacheEntryOptions()
|
_cacheOptions = new MemoryCacheEntryOptions()
|
||||||
.SetSlidingExpiration(StaticConfiguration.TriggerValue + Constants.CacheLimitBuffer);
|
.SetSlidingExpiration(StaticConfiguration.TriggerValue + Constants.CacheLimitBuffer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public abstract class GenericHandler : IHandler, IDisposable
|
|||||||
protected readonly ILidarrClient _lidarrClient;
|
protected readonly ILidarrClient _lidarrClient;
|
||||||
protected readonly ArrQueueIterator _arrArrQueueIterator;
|
protected readonly ArrQueueIterator _arrArrQueueIterator;
|
||||||
protected readonly IDownloadService _downloadService;
|
protected readonly IDownloadService _downloadService;
|
||||||
protected readonly NotificationPublisher _notifier;
|
protected readonly INotificationPublisher _notifier;
|
||||||
|
|
||||||
protected GenericHandler(
|
protected GenericHandler(
|
||||||
ILogger<GenericHandler> logger,
|
ILogger<GenericHandler> logger,
|
||||||
@@ -37,7 +37,7 @@ public abstract class GenericHandler : IHandler, IDisposable
|
|||||||
ILidarrClient lidarrClient,
|
ILidarrClient lidarrClient,
|
||||||
ArrQueueIterator arrArrQueueIterator,
|
ArrQueueIterator arrArrQueueIterator,
|
||||||
DownloadServiceFactory downloadServiceFactory,
|
DownloadServiceFactory downloadServiceFactory,
|
||||||
NotificationPublisher notifier
|
INotificationPublisher notifier
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using Domain.Enums;
|
||||||
|
|
||||||
|
namespace Infrastructure.Verticals.Notifications;
|
||||||
|
|
||||||
|
public interface INotificationPublisher
|
||||||
|
{
|
||||||
|
Task NotifyStrike(StrikeType strikeType, int strikeCount);
|
||||||
|
|
||||||
|
Task NotifyQueueItemDeleted(bool removeFromClient, DeleteReason reason);
|
||||||
|
|
||||||
|
Task NotifyDownloadCleaned(double ratio, TimeSpan seedingTime, string categoryName, CleanReason reason);
|
||||||
|
}
|
||||||
@@ -12,25 +12,19 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Infrastructure.Verticals.Notifications;
|
namespace Infrastructure.Verticals.Notifications;
|
||||||
|
|
||||||
public class NotificationPublisher : InterceptedService, IDryRunService
|
public class NotificationPublisher : INotificationPublisher
|
||||||
{
|
{
|
||||||
private readonly ILogger<NotificationPublisher> _logger;
|
private readonly ILogger<INotificationPublisher> _logger;
|
||||||
private readonly IBus _messageBus;
|
private readonly IBus _messageBus;
|
||||||
|
private readonly IDryRunInterceptor _dryRunInterceptor;
|
||||||
|
|
||||||
/// <summary>
|
public NotificationPublisher(ILogger<INotificationPublisher> logger, IBus messageBus, IDryRunInterceptor dryRunInterceptor)
|
||||||
/// Constructor to be used by interceptors.
|
|
||||||
/// </summary>
|
|
||||||
public NotificationPublisher()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public NotificationPublisher(ILogger<NotificationPublisher> logger, IBus messageBus)
|
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_messageBus = messageBus;
|
_messageBus = messageBus;
|
||||||
|
_dryRunInterceptor = dryRunInterceptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
[DryRunSafeguard]
|
|
||||||
public virtual async Task NotifyStrike(StrikeType strikeType, int strikeCount)
|
public virtual async Task NotifyStrike(StrikeType strikeType, int strikeCount)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -54,10 +48,10 @@ public class NotificationPublisher : InterceptedService, IDryRunService
|
|||||||
switch (strikeType)
|
switch (strikeType)
|
||||||
{
|
{
|
||||||
case StrikeType.Stalled:
|
case StrikeType.Stalled:
|
||||||
await _messageBus.Publish(notification.Adapt<StalledStrikeNotification>());
|
await _dryRunInterceptor.InterceptAsync(Notify<StalledStrikeNotification>, notification.Adapt<StalledStrikeNotification>());
|
||||||
break;
|
break;
|
||||||
case StrikeType.ImportFailed:
|
case StrikeType.ImportFailed:
|
||||||
await _messageBus.Publish(notification.Adapt<FailedImportStrikeNotification>());
|
await _dryRunInterceptor.InterceptAsync(Notify<FailedImportStrikeNotification>, notification.Adapt<FailedImportStrikeNotification>());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,7 +61,6 @@ public class NotificationPublisher : InterceptedService, IDryRunService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[DryRunSafeguard]
|
|
||||||
public virtual async Task NotifyQueueItemDeleted(bool removeFromClient, DeleteReason reason)
|
public virtual async Task NotifyQueueItemDeleted(bool removeFromClient, DeleteReason reason)
|
||||||
{
|
{
|
||||||
QueueRecord record = ContextProvider.Get<QueueRecord>(nameof(QueueRecord));
|
QueueRecord record = ContextProvider.Get<QueueRecord>(nameof(QueueRecord));
|
||||||
@@ -86,10 +79,9 @@ public class NotificationPublisher : InterceptedService, IDryRunService
|
|||||||
Fields = [new() { Title = "Removed from download client?", Text = removeFromClient ? "Yes" : "No" }]
|
Fields = [new() { Title = "Removed from download client?", Text = removeFromClient ? "Yes" : "No" }]
|
||||||
};
|
};
|
||||||
|
|
||||||
await _messageBus.Publish(notification);
|
await _dryRunInterceptor.InterceptAsync(Notify<QueueItemDeletedNotification>, notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DryRunSafeguard]
|
|
||||||
public virtual async Task NotifyDownloadCleaned(double ratio, TimeSpan seedingTime, string categoryName, CleanReason reason)
|
public virtual async Task NotifyDownloadCleaned(double ratio, TimeSpan seedingTime, string categoryName, CleanReason reason)
|
||||||
{
|
{
|
||||||
DownloadCleanedNotification notification = new()
|
DownloadCleanedNotification notification = new()
|
||||||
@@ -106,7 +98,13 @@ public class NotificationPublisher : InterceptedService, IDryRunService
|
|||||||
Level = NotificationLevel.Important
|
Level = NotificationLevel.Important
|
||||||
};
|
};
|
||||||
|
|
||||||
await _messageBus.Publish(notification);
|
await _dryRunInterceptor.InterceptAsync(Notify<DownloadCleanedNotification>, notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DryRunSafeguard]
|
||||||
|
private Task Notify<T>(T message) where T: notnull
|
||||||
|
{
|
||||||
|
return _messageBus.Publish(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Uri GetImageFromContext(QueueRecord record, InstanceType instanceType) =>
|
private static Uri GetImageFromContext(QueueRecord record, InstanceType instanceType) =>
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public sealed class QueueCleaner : GenericHandler
|
|||||||
LidarrClient lidarrClient,
|
LidarrClient lidarrClient,
|
||||||
ArrQueueIterator arrArrQueueIterator,
|
ArrQueueIterator arrArrQueueIterator,
|
||||||
DownloadServiceFactory downloadServiceFactory,
|
DownloadServiceFactory downloadServiceFactory,
|
||||||
NotificationPublisher notifier
|
INotificationPublisher notifier
|
||||||
) : base(
|
) : base(
|
||||||
logger, downloadClientConfig,
|
logger, downloadClientConfig,
|
||||||
sonarrConfig, radarrConfig, lidarrConfig,
|
sonarrConfig, radarrConfig, lidarrConfig,
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ services:
|
|||||||
image: ghcr.io/flmorg/cleanuperr:latest
|
image: ghcr.io/flmorg/cleanuperr:latest
|
||||||
container_name: cleanuperr
|
container_name: cleanuperr
|
||||||
environment:
|
environment:
|
||||||
|
- TZ=Europe/Bucharest
|
||||||
- DRY_RUN=false
|
- DRY_RUN=false
|
||||||
|
|
||||||
- LOGGING__LOGLEVEL=Debug
|
- LOGGING__LOGLEVEL=Debug
|
||||||
@@ -250,6 +251,7 @@ services:
|
|||||||
# - NOTIFIARR__ON_IMPORT_FAILED_STRIKE=true
|
# - NOTIFIARR__ON_IMPORT_FAILED_STRIKE=true
|
||||||
# - NOTIFIARR__ON_STALLED_STRIKE=true
|
# - NOTIFIARR__ON_STALLED_STRIKE=true
|
||||||
# - NOTIFIARR__ON_QUEUE_ITEM_DELETED=true
|
# - NOTIFIARR__ON_QUEUE_ITEM_DELETED=true
|
||||||
|
# - NOTIFIARR__ON_DOWNLOAD_CLEANED=true
|
||||||
# - NOTIFIARR__API_KEY=notifiarr_secret
|
# - NOTIFIARR__API_KEY=notifiarr_secret
|
||||||
# - NOTIFIARR__CHANNEL_ID=discord_channel_id
|
# - NOTIFIARR__CHANNEL_ID=discord_channel_id
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -12,6 +12,13 @@
|
|||||||
|
|
||||||
### General settings
|
### General settings
|
||||||
|
|
||||||
|
**`TZ`**
|
||||||
|
- The time zone to use.
|
||||||
|
- Type: String.
|
||||||
|
- Possible values: Any valid timezone.
|
||||||
|
- Default: `UTC`.
|
||||||
|
- Required: No.
|
||||||
|
|
||||||
**`DRY_RUN`**
|
**`DRY_RUN`**
|
||||||
- When enabled, simulates irreversible operations (like deletions and notifications) without making actual changes.
|
- When enabled, simulates irreversible operations (like deletions and notifications) without making actual changes.
|
||||||
- Type: Boolean.
|
- Type: Boolean.
|
||||||
|
|||||||
Reference in New Issue
Block a user