using Common.Configuration.Arr; using Common.Configuration.DownloadCleaner; using Common.Configuration.DownloadClient; using Domain.Enums; using Domain.Models.Arr.Queue; using Infrastructure.Verticals.Arr; using Infrastructure.Verticals.Arr.Interfaces; using Infrastructure.Verticals.DownloadClient; using Infrastructure.Verticals.Jobs; using Infrastructure.Verticals.Notifications; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Serilog.Context; namespace Infrastructure.Verticals.DownloadCleaner; public sealed class DownloadCleaner : GenericHandler { private readonly DownloadCleanerConfig _config; private readonly HashSet _excludedHashes = []; private static bool _hardLinkCategoryCreated = false; public DownloadCleaner( ILogger logger, IOptions config, IOptions downloadClientConfig, IOptions sonarrConfig, IOptions radarrConfig, IOptions lidarrConfig, SonarrClient sonarrClient, RadarrClient radarrClient, LidarrClient lidarrClient, ArrQueueIterator arrArrQueueIterator, DownloadServiceFactory downloadServiceFactory, INotificationPublisher notifier ) : base( logger, downloadClientConfig, sonarrConfig, radarrConfig, lidarrConfig, sonarrClient, radarrClient, lidarrClient, arrArrQueueIterator, downloadServiceFactory, notifier ) { _config = config.Value; _config.Validate(); } 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) { _logger.LogWarning("no categories configured"); return; } await _downloadService.LoginAsync(); List? downloadsToBeCleaned = await _downloadService.GetDownloadsToBeCleanedAsync(_config.Categories); List? downloadsToChangeCategory = null; if (!string.IsNullOrEmpty(_config.NoHardlinksCategory) && _config.HardlinkCategories?.Count > 0) { if (!_hardLinkCategoryCreated) { await _downloadService.CreateCategoryAsync(_config.NoHardlinksCategory); _hardLinkCategoryCreated = true; } downloadsToChangeCategory = await _downloadService.GetDownloadsToChangeCategoryAsync(_config.HardlinkCategories); } bool hasDownloadsToClean = downloadsToBeCleaned?.Count > 0; bool hasDownloadsToChange = downloadsToChangeCategory?.Count > 0; if (!hasDownloadsToClean && !hasDownloadsToChange) { _logger.LogDebug("no downloads to process"); return; } // wait for the downloads to appear in the arr queue await Task.Delay(10 * 1000); await ProcessArrConfigAsync(_sonarrConfig, InstanceType.Sonarr, true); await ProcessArrConfigAsync(_radarrConfig, InstanceType.Radarr, true); await ProcessArrConfigAsync(_lidarrConfig, InstanceType.Lidarr, true); if (hasDownloadsToChange) { await _downloadService.ChangeCategoryForNoHardLinksAsync(downloadsToChangeCategory, _excludedHashes); } else { _logger.LogDebug("no downloads found to change category"); } if (hasDownloadsToClean) { await _downloadService.CleanDownloadsAsync(downloadsToBeCleaned, _config.Categories, _excludedHashes); } else { _logger.LogDebug("no downloads found to be cleaned"); } } protected override async Task ProcessInstanceAsync(ArrInstance instance, InstanceType instanceType) { using var _ = LogContext.PushProperty("InstanceName", instanceType.ToString()); IArrClient arrClient = GetClient(instanceType); await _arrArrQueueIterator.Iterate(arrClient, instance, async items => { var groups = items .Where(x => !string.IsNullOrEmpty(x.DownloadId)) .GroupBy(x => x.DownloadId) .ToList(); foreach (QueueRecord record in groups.Select(group => group.First())) { _excludedHashes.Add(record.DownloadId.ToLowerInvariant()); } }); } public override void Dispose() { _downloadService.Dispose(); } }