Files
Cleanuparr/code/Infrastructure/Verticals/Jobs/GenericHandler.cs
2025-05-27 15:25:13 +03:00

236 lines
7.8 KiB
C#

using Common.Configuration.Arr;
using Common.Configuration.DownloadClient;
using Data.Enums;
using Data.Models.Arr;
using Data.Models.Arr.Queue;
using Infrastructure.Configuration;
using Infrastructure.Verticals.Arr;
using Infrastructure.Verticals.DownloadClient;
using Infrastructure.Verticals.DownloadRemover.Models;
using Infrastructure.Verticals.Notifications;
using MassTransit;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
namespace Infrastructure.Verticals.Jobs;
public abstract class GenericHandler : IHandler, IDisposable
{
protected readonly ILogger<GenericHandler> _logger;
protected DownloadClientConfig _downloadClientConfig = new();
protected SonarrConfig _sonarrConfig = new();
protected RadarrConfig _radarrConfig = new();
protected LidarrConfig _lidarrConfig = new();
protected readonly IMemoryCache _cache;
protected readonly IBus _messageBus;
protected readonly ArrClientFactory _arrClientFactory;
protected readonly ArrQueueIterator _arrArrQueueIterator;
protected readonly DownloadServiceFactory _downloadServiceFactory;
// Collection of download services for use with multiple clients
protected readonly List<IDownloadService> _downloadServices = new();
protected GenericHandler(
ILogger<GenericHandler> logger,
IMemoryCache cache,
IBus messageBus,
ArrClientFactory arrClientFactory,
ArrQueueIterator arrArrQueueIterator,
DownloadServiceFactory downloadServiceFactory
)
{
_logger = logger;
_cache = cache;
_messageBus = messageBus;
_arrClientFactory = arrClientFactory;
_arrArrQueueIterator = arrArrQueueIterator;
_downloadServiceFactory = downloadServiceFactory;
}
/// <summary>
/// Initialize download services based on configuration
/// </summary>
protected virtual void InitializeDownloadServices()
{
// Clear any existing services
DisposeDownloadServices();
_downloadServices.Clear();
// Add all enabled clients
foreach (var client in _downloadClientConfig.GetEnabledClients())
{
try
{
var service = _downloadServiceFactory.GetDownloadService(client);
if (service != null)
{
_downloadServices.Add(service);
_logger.LogDebug("Initialized download client: {name}", client.Name);
}
else
{
_logger.LogWarning("Download client service not available for: {name}", client.Name);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to initialize download client: {name}", client.Name);
}
}
if (_downloadServices.Count == 0)
{
_logger.LogWarning("No enabled download clients found");
}
else
{
_logger.LogDebug("Initialized {count} download clients", _downloadServices.Count);
}
}
public virtual async Task ExecuteAsync()
{
// Initialize download services
InitializeDownloadServices();
if (_downloadServices.Count == 0)
{
_logger.LogWarning("No download clients available, skipping execution");
return;
}
// Login to all download services
foreach (var downloadService in _downloadServices)
{
await downloadService.LoginAsync();
}
await ProcessArrConfigAsync(_sonarrConfig, InstanceType.Sonarr);
await ProcessArrConfigAsync(_radarrConfig, InstanceType.Radarr);
await ProcessArrConfigAsync(_lidarrConfig, InstanceType.Lidarr);
}
public virtual void Dispose()
{
DisposeDownloadServices();
}
/// <summary>
/// Dispose all download services
/// </summary>
protected void DisposeDownloadServices()
{
foreach (var service in _downloadServices)
{
service.Dispose();
}
}
protected abstract Task ProcessInstanceAsync(ArrInstance instance, InstanceType instanceType, ArrConfig config);
protected async Task ProcessArrConfigAsync(ArrConfig config, InstanceType instanceType, bool throwOnFailure = false)
{
if (!config.Enabled)
{
return;
}
foreach (ArrInstance arrInstance in config.Instances)
{
try
{
await ProcessInstanceAsync(arrInstance, instanceType, config);
}
catch (Exception exception)
{
_logger.LogError(exception, "failed to process {type} instance | {url}", instanceType, arrInstance.Url);
if (throwOnFailure)
{
throw;
}
}
}
}
protected async Task PublishQueueItemRemoveRequest(
string downloadRemovalKey,
InstanceType instanceType,
ArrInstance instance,
QueueRecord record,
bool isPack,
bool removeFromClient,
DeleteReason deleteReason
)
{
if (instanceType is InstanceType.Sonarr)
{
QueueItemRemoveRequest<SonarrSearchItem> removeRequest = new()
{
InstanceType = instanceType,
Instance = instance,
Record = record,
SearchItem = (SonarrSearchItem)GetRecordSearchItem(instanceType, record, isPack),
RemoveFromClient = removeFromClient,
DeleteReason = deleteReason
};
await _messageBus.Publish(removeRequest);
}
else
{
QueueItemRemoveRequest<SearchItem> removeRequest = new()
{
InstanceType = instanceType,
Instance = instance,
Record = record,
SearchItem = GetRecordSearchItem(instanceType, record, isPack),
RemoveFromClient = removeFromClient,
DeleteReason = deleteReason
};
await _messageBus.Publish(removeRequest);
}
_cache.Set(downloadRemovalKey, true);
_logger.LogInformation("item marked for removal | {title} | {url}", record.Title, instance.Url);
}
protected SearchItem GetRecordSearchItem(InstanceType type, QueueRecord record, bool isPack = false)
{
return type switch
{
InstanceType.Sonarr when _sonarrConfig.SearchType is SonarrSearchType.Episode && !isPack => new SonarrSearchItem
{
Id = record.EpisodeId,
SeriesId = record.SeriesId,
SearchType = SonarrSearchType.Episode
},
InstanceType.Sonarr when _sonarrConfig.SearchType is SonarrSearchType.Episode && isPack => new SonarrSearchItem
{
Id = record.SeasonNumber,
SeriesId = record.SeriesId,
SearchType = SonarrSearchType.Season
},
InstanceType.Sonarr when _sonarrConfig.SearchType is SonarrSearchType.Season => new SonarrSearchItem
{
Id = record.SeasonNumber,
SeriesId = record.SeriesId,
SearchType = SonarrSearchType.Series
},
InstanceType.Sonarr when _sonarrConfig.SearchType is SonarrSearchType.Series => new SonarrSearchItem
{
Id = record.SeriesId
},
InstanceType.Radarr => new SearchItem
{
Id = record.MovieId
},
InstanceType.Lidarr => new SearchItem
{
Id = record.AlbumId
},
_ => throw new NotImplementedException($"instance type {type} is not yet supported")
};
}
}