diff --git a/code/Common/Configuration/IIgnoredDownloadsConfig.cs b/code/Common/Configuration/IIgnoredDownloadsConfig.cs
deleted file mode 100644
index f08e445a..00000000
--- a/code/Common/Configuration/IIgnoredDownloadsConfig.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Common.Configuration;
-
-public interface IIgnoredDownloadsConfig
-{
- string? IgnoredDownloadsPath { get; }
-}
\ No newline at end of file
diff --git a/code/Common/Configuration/IgnoredDownloads/IgnoredDownloadsConfig.cs b/code/Common/Configuration/IgnoredDownloads/IgnoredDownloadsConfig.cs
new file mode 100644
index 00000000..f66b9020
--- /dev/null
+++ b/code/Common/Configuration/IgnoredDownloads/IgnoredDownloadsConfig.cs
@@ -0,0 +1,15 @@
+using Microsoft.Extensions.Configuration;
+
+namespace Common.Configuration.IgnoredDownloads;
+
+///
+/// Configuration for ignored downloads
+///
+public class IgnoredDownloadsConfig
+{
+ ///
+ /// List of download IDs to be ignored by all jobs
+ ///
+ [ConfigurationKeyName("IGNORED_DOWNLOADS")]
+ public List IgnoredDownloads { get; init; } = new();
+}
diff --git a/code/Executable/Controllers/ConfigurationController.cs b/code/Executable/Controllers/ConfigurationController.cs
index 8232a091..03aef25d 100644
--- a/code/Executable/Controllers/ConfigurationController.cs
+++ b/code/Executable/Controllers/ConfigurationController.cs
@@ -1,10 +1,10 @@
using Common.Configuration.ContentBlocker;
using Common.Configuration.DownloadCleaner;
+using Common.Configuration.DownloadClient;
+using Common.Configuration.IgnoredDownloads;
using Common.Configuration.QueueCleaner;
using Infrastructure.Services;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Options;
-using System.Text.Json;
namespace Executable.Controllers;
@@ -13,34 +13,23 @@ namespace Executable.Controllers;
public class ConfigurationController : ControllerBase
{
private readonly ILogger _logger;
- private readonly IConfiguration _configuration;
- private readonly IOptionsMonitor _queueCleanerConfig;
- private readonly IOptionsMonitor _contentBlockerConfig;
- private readonly IOptionsMonitor _downloadCleanerConfig;
private readonly IConfigurationService _configService;
public ConfigurationController(
ILogger logger,
- IConfiguration configuration,
- IOptionsMonitor queueCleanerConfig,
- IOptionsMonitor contentBlockerConfig,
- IOptionsMonitor downloadCleanerConfig,
IConfigurationService configService)
{
_logger = logger;
- _configuration = configuration;
- _queueCleanerConfig = queueCleanerConfig;
- _contentBlockerConfig = contentBlockerConfig;
- _downloadCleanerConfig = downloadCleanerConfig;
_configService = configService;
}
[HttpGet("queuecleaner")]
- public IActionResult GetQueueCleanerConfig()
+ public async Task GetQueueCleanerConfig()
{
try
{
- return Ok(_queueCleanerConfig.CurrentValue);
+ var config = await _configService.GetQueueCleanerConfigAsync();
+ return Ok(config);
}
catch (Exception ex)
{
@@ -50,11 +39,12 @@ public class ConfigurationController : ControllerBase
}
[HttpGet("contentblocker")]
- public IActionResult GetContentBlockerConfig()
+ public async Task GetContentBlockerConfig()
{
try
{
- return Ok(_contentBlockerConfig.CurrentValue);
+ var config = await _configService.GetContentBlockerConfigAsync();
+ return Ok(config);
}
catch (Exception ex)
{
@@ -64,11 +54,12 @@ public class ConfigurationController : ControllerBase
}
[HttpGet("downloadcleaner")]
- public IActionResult GetDownloadCleanerConfig()
+ public async Task GetDownloadCleanerConfig()
{
try
{
- return Ok(_downloadCleanerConfig.CurrentValue);
+ var config = await _configService.GetDownloadCleanerConfigAsync();
+ return Ok(config);
}
catch (Exception ex)
{
@@ -76,6 +67,36 @@ public class ConfigurationController : ControllerBase
return StatusCode(500, "An error occurred while retrieving DownloadCleaner configuration");
}
}
+
+ [HttpGet("downloadclient")]
+ public async Task GetDownloadClientConfig()
+ {
+ try
+ {
+ var config = await _configService.GetDownloadClientConfigAsync();
+ return Ok(config);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error retrieving DownloadClient configuration");
+ return StatusCode(500, "An error occurred while retrieving DownloadClient configuration");
+ }
+ }
+
+ [HttpGet("ignoreddownloads")]
+ public async Task GetIgnoredDownloadsConfig()
+ {
+ try
+ {
+ var config = await _configService.GetIgnoredDownloadsConfigAsync();
+ return Ok(config);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error retrieving IgnoredDownloads configuration");
+ return StatusCode(500, "An error occurred while retrieving IgnoredDownloads configuration");
+ }
+ }
[HttpPut("queuecleaner")]
public async Task UpdateQueueCleanerConfig([FromBody] QueueCleanerConfig config)
@@ -86,7 +107,7 @@ public class ConfigurationController : ControllerBase
config.Validate();
// Persist the configuration
- var result = await _configService.UpdateConfigurationAsync(QueueCleanerConfig.SectionName, config);
+ var result = await _configService.UpdateQueueCleanerConfigAsync(config);
if (!result)
{
return StatusCode(500, "Failed to save QueueCleaner configuration");
@@ -111,7 +132,7 @@ public class ConfigurationController : ControllerBase
config.Validate();
// Persist the configuration
- var result = await _configService.UpdateConfigurationAsync(ContentBlockerConfig.SectionName, config);
+ var result = await _configService.UpdateContentBlockerConfigAsync(config);
if (!result)
{
return StatusCode(500, "Failed to save ContentBlocker configuration");
@@ -136,7 +157,7 @@ public class ConfigurationController : ControllerBase
config.Validate();
// Persist the configuration
- var result = await _configService.UpdateConfigurationAsync(DownloadCleanerConfig.SectionName, config);
+ var result = await _configService.UpdateDownloadCleanerConfigAsync(config);
if (!result)
{
return StatusCode(500, "Failed to save DownloadCleaner configuration");
@@ -151,4 +172,48 @@ public class ConfigurationController : ControllerBase
return StatusCode(500, "An error occurred while updating DownloadCleaner configuration");
}
}
+
+ [HttpPut("downloadclient")]
+ public async Task UpdateDownloadClientConfig([FromBody] DownloadClientConfig config)
+ {
+ try
+ {
+ // Persist the configuration
+ var result = await _configService.UpdateDownloadClientConfigAsync(config);
+ if (!result)
+ {
+ return StatusCode(500, "Failed to save DownloadClient configuration");
+ }
+
+ _logger.LogInformation("DownloadClient configuration updated successfully");
+ return Ok(new { Message = "DownloadClient configuration updated successfully" });
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error updating DownloadClient configuration");
+ return StatusCode(500, "An error occurred while updating DownloadClient configuration");
+ }
+ }
+
+ [HttpPut("ignoreddownloads")]
+ public async Task UpdateIgnoredDownloadsConfig([FromBody] IgnoredDownloadsConfig config)
+ {
+ try
+ {
+ // Persist the configuration
+ var result = await _configService.UpdateIgnoredDownloadsConfigAsync(config);
+ if (!result)
+ {
+ return StatusCode(500, "Failed to save IgnoredDownloads configuration");
+ }
+
+ _logger.LogInformation("IgnoredDownloads configuration updated successfully");
+ return Ok(new { Message = "IgnoredDownloads configuration updated successfully" });
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error updating IgnoredDownloads configuration");
+ return StatusCode(500, "An error occurred while updating IgnoredDownloads configuration");
+ }
+ }
}
diff --git a/code/Executable/Controllers/IgnoredDownloadsController.cs b/code/Executable/Controllers/IgnoredDownloadsController.cs
new file mode 100644
index 00000000..1e478ee3
--- /dev/null
+++ b/code/Executable/Controllers/IgnoredDownloadsController.cs
@@ -0,0 +1,121 @@
+using Common.Configuration.IgnoredDownloads;
+using Infrastructure.Services;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Executable.Controllers;
+
+[ApiController]
+[Route("api/[controller]")]
+public class IgnoredDownloadsController : ControllerBase
+{
+ private readonly ILogger _logger;
+ private readonly IIgnoredDownloadsService _ignoredDownloadsService;
+
+ public IgnoredDownloadsController(
+ ILogger logger,
+ IIgnoredDownloadsService ignoredDownloadsService)
+ {
+ _logger = logger;
+ _ignoredDownloadsService = ignoredDownloadsService;
+ }
+
+ ///
+ /// Get all ignored downloads
+ ///
+ [HttpGet]
+ public async Task GetIgnoredDownloads()
+ {
+ try
+ {
+ var ignoredDownloads = await _ignoredDownloadsService.GetIgnoredDownloadsAsync();
+ return Ok(ignoredDownloads);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error retrieving ignored downloads");
+ return StatusCode(500, "An error occurred while retrieving ignored downloads");
+ }
+ }
+
+ ///
+ /// Add a new download ID to be ignored
+ ///
+ [HttpPost]
+ public async Task AddIgnoredDownload([FromBody] string downloadId)
+ {
+ try
+ {
+ if (string.IsNullOrWhiteSpace(downloadId))
+ {
+ return BadRequest("Download ID cannot be empty");
+ }
+
+ var result = await _ignoredDownloadsService.AddIgnoredDownloadAsync(downloadId);
+ if (!result)
+ {
+ return StatusCode(500, "Failed to add download ID to ignored list");
+ }
+
+ _logger.LogInformation("Added download ID to ignored list: {DownloadId}", downloadId);
+ return Ok(new { Message = $"Download ID '{downloadId}' added to ignored list" });
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error adding download ID to ignored list");
+ return StatusCode(500, "An error occurred while adding download ID to ignored list");
+ }
+ }
+
+ ///
+ /// Remove a download ID from the ignored list
+ ///
+ [HttpDelete("{downloadId}")]
+ public async Task RemoveIgnoredDownload(string downloadId)
+ {
+ try
+ {
+ if (string.IsNullOrWhiteSpace(downloadId))
+ {
+ return BadRequest("Download ID cannot be empty");
+ }
+
+ var result = await _ignoredDownloadsService.RemoveIgnoredDownloadAsync(downloadId);
+ if (!result)
+ {
+ return StatusCode(500, "Failed to remove download ID from ignored list");
+ }
+
+ _logger.LogInformation("Removed download ID from ignored list: {DownloadId}", downloadId);
+ return Ok(new { Message = $"Download ID '{downloadId}' removed from ignored list" });
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error removing download ID from ignored list");
+ return StatusCode(500, "An error occurred while removing download ID from ignored list");
+ }
+ }
+
+ ///
+ /// Clear all ignored downloads
+ ///
+ [HttpDelete]
+ public async Task ClearIgnoredDownloads()
+ {
+ try
+ {
+ var result = await _ignoredDownloadsService.ClearIgnoredDownloadsAsync();
+ if (!result)
+ {
+ return StatusCode(500, "Failed to clear ignored downloads list");
+ }
+
+ _logger.LogInformation("Cleared all ignored downloads");
+ return Ok(new { Message = "All ignored downloads have been cleared" });
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error clearing ignored downloads list");
+ return StatusCode(500, "An error occurred while clearing ignored downloads list");
+ }
+ }
+}
diff --git a/code/Executable/DependencyInjection/ServicesDI.cs b/code/Executable/DependencyInjection/ServicesDI.cs
index 232ab1c2..4ba70d1c 100644
--- a/code/Executable/DependencyInjection/ServicesDI.cs
+++ b/code/Executable/DependencyInjection/ServicesDI.cs
@@ -50,7 +50,5 @@ public static class ServicesDI
.AddTransient()
.AddTransient()
.AddSingleton()
- .AddSingleton>()
- .AddSingleton>()
- .AddSingleton>();
+ .AddSingleton();
}
\ No newline at end of file
diff --git a/code/Infrastructure/Configuration/ConfigInitializer.cs b/code/Infrastructure/Configuration/ConfigInitializer.cs
index 6417d542..4da05a0d 100644
--- a/code/Infrastructure/Configuration/ConfigInitializer.cs
+++ b/code/Infrastructure/Configuration/ConfigInitializer.cs
@@ -2,6 +2,7 @@ using Common.Configuration.Arr;
using Common.Configuration.ContentBlocker;
using Common.Configuration.DownloadCleaner;
using Common.Configuration.DownloadClient;
+using Common.Configuration.IgnoredDownloads;
using Common.Configuration.QueueCleaner;
using Microsoft.Extensions.Logging;
@@ -35,6 +36,7 @@ public class ConfigInitializer
await EnsureSonarrConfigAsync();
await EnsureRadarrConfigAsync();
await EnsureLidarrConfigAsync();
+ await EnsureIgnoredDownloadsConfigAsync();
_logger.LogInformation("Configuration files initialized");
}
@@ -173,4 +175,18 @@ public class ConfigInitializer
await _configManager.SaveLidarrConfigAsync(defaultConfig);
}
}
+
+ private async Task EnsureIgnoredDownloadsConfigAsync()
+ {
+ var config = await _configManager.GetIgnoredDownloadsConfigAsync();
+ if (config == null)
+ {
+ _logger.LogInformation("Creating default IgnoredDownloads configuration");
+ var defaultConfig = new IgnoredDownloadsConfig
+ {
+ IgnoredDownloads = new List()
+ };
+ await _configManager.SaveIgnoredDownloadsConfigAsync(defaultConfig);
+ }
+ }
}
diff --git a/code/Infrastructure/Configuration/ConfigurationManager.cs b/code/Infrastructure/Configuration/ConfigurationManager.cs
index c9f263d2..15bd956e 100644
--- a/code/Infrastructure/Configuration/ConfigurationManager.cs
+++ b/code/Infrastructure/Configuration/ConfigurationManager.cs
@@ -3,6 +3,7 @@ using Common.Configuration.Arr;
using Common.Configuration.ContentBlocker;
using Common.Configuration.DownloadCleaner;
using Common.Configuration.DownloadClient;
+using Common.Configuration.IgnoredDownloads;
using Common.Configuration.QueueCleaner;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -30,6 +31,7 @@ public interface IConfigurationManager
Task GetQueueCleanerConfigAsync();
Task GetDownloadCleanerConfigAsync();
Task GetDownloadClientConfigAsync();
+ Task GetIgnoredDownloadsConfigAsync();
Task SaveSonarrConfigAsync(SonarrConfig config);
Task SaveRadarrConfigAsync(RadarrConfig config);
@@ -38,6 +40,7 @@ public interface IConfigurationManager
Task SaveQueueCleanerConfigAsync(QueueCleanerConfig config);
Task SaveDownloadCleanerConfigAsync(DownloadCleanerConfig config);
Task SaveDownloadClientConfigAsync(DownloadClientConfig config);
+ Task SaveIgnoredDownloadsConfigAsync(IgnoredDownloadsConfig config);
}
public class ConfigurationManager : IConfigurationManager
@@ -51,6 +54,7 @@ public class ConfigurationManager : IConfigurationManager
private readonly IOptionsMonitor _queueCleanerConfig;
private readonly IOptionsMonitor _downloadCleanerConfig;
private readonly IOptionsMonitor _downloadClientConfig;
+ private readonly IOptionsMonitor _ignoredDownloadsConfig;
// Define standard config file names
private const string SonarrConfigFile = "sonarr.json";
@@ -60,6 +64,7 @@ public class ConfigurationManager : IConfigurationManager
private const string QueueCleanerConfigFile = "queuecleaner.json";
private const string DownloadCleanerConfigFile = "downloadcleaner.json";
private const string DownloadClientConfigFile = "downloadclient.json";
+ private const string IgnoredDownloadsConfigFile = "ignoreddownloads.json";
public ConfigurationManager(
ILogger logger,
@@ -70,7 +75,8 @@ public class ConfigurationManager : IConfigurationManager
IOptionsMonitor contentBlockerConfig,
IOptionsMonitor queueCleanerConfig,
IOptionsMonitor downloadCleanerConfig,
- IOptionsMonitor downloadClientConfig)
+ IOptionsMonitor downloadClientConfig,
+ IOptionsMonitor ignoredDownloadsConfig)
{
_logger = logger;
_configProvider = configProvider;
@@ -81,6 +87,7 @@ public class ConfigurationManager : IConfigurationManager
_queueCleanerConfig = queueCleanerConfig;
_downloadCleanerConfig = downloadCleanerConfig;
_downloadClientConfig = downloadClientConfig;
+ _ignoredDownloadsConfig = ignoredDownloadsConfig;
}
// Generic configuration methods
@@ -268,4 +275,23 @@ public class ConfigurationManager : IConfigurationManager
return Task.FromResult(false);
}
}
+
+ public async Task GetIgnoredDownloadsConfigAsync()
+ {
+ var config = await _configProvider.ReadConfigurationAsync(IgnoredDownloadsConfigFile);
+ return config ?? _ignoredDownloadsConfig.CurrentValue;
+ }
+
+ public Task SaveIgnoredDownloadsConfigAsync(IgnoredDownloadsConfig config)
+ {
+ try
+ {
+ return _configProvider.WriteConfigurationAsync(IgnoredDownloadsConfigFile, config);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "IgnoredDownloads configuration save failed");
+ return Task.FromResult(false);
+ }
+ }
}
diff --git a/code/Infrastructure/Providers/IgnoredDownloadsProvider.cs b/code/Infrastructure/Providers/IgnoredDownloadsProvider.cs
deleted file mode 100644
index e31dcd41..00000000
--- a/code/Infrastructure/Providers/IgnoredDownloadsProvider.cs
+++ /dev/null
@@ -1,114 +0,0 @@
-using Common.Configuration;
-using Infrastructure.Configuration;
-using Infrastructure.Helpers;
-using Microsoft.Extensions.Caching.Memory;
-using Microsoft.Extensions.Logging;
-
-namespace Infrastructure.Providers;
-
-public sealed class IgnoredDownloadsProvider
- where T : IIgnoredDownloadsConfig
-{
- private readonly ILogger> _logger;
- private IIgnoredDownloadsConfig _config;
- private readonly IConfigurationManager _configManager;
- private readonly IMemoryCache _cache;
- private DateTime _lastModified = DateTime.MinValue;
- private readonly string _configType;
-
- public IgnoredDownloadsProvider(ILogger> logger, IConfigurationManager configManager, IMemoryCache cache)
- {
- _logger = logger;
- _configManager = configManager;
- _cache = cache;
- _configType = typeof(T).Name;
-
- // Initialize configuration
- InitializeConfig().Wait();
- }
-
- private async Task InitializeConfig()
- {
- // Get the configuration based on the type
- if (typeof(T).Name.Contains("ContentBlocker"))
- {
- var config = await _configManager.GetContentBlockerConfigAsync();
- _config = config ?? new T();
- }
- else if (typeof(T).Name.Contains("QueueCleaner"))
- {
- var config = await _configManager.GetQueueCleanerConfigAsync();
- _config = config ?? new T();
- }
- else if (typeof(T).Name.Contains("DownloadCleaner"))
- {
- var config = await _configManager.GetDownloadCleanerConfigAsync();
- _config = config ?? new T();
- }
- else
- {
- _config = new T();
- }
-
- if (string.IsNullOrEmpty(_config.IgnoredDownloadsPath))
- {
- return;
- }
-
- if (!File.Exists(_config.IgnoredDownloadsPath))
- {
- _logger.LogWarning("Ignored downloads file not found: {path}", _config.IgnoredDownloadsPath);
- }
- }
-
- public async Task> GetIgnoredDownloads()
- {
- // Refresh the configuration before checking ignored downloads
- await InitializeConfig();
-
- if (string.IsNullOrEmpty(_config.IgnoredDownloadsPath))
- {
- return Array.Empty();
- }
-
- FileInfo fileInfo = new(_config.IgnoredDownloadsPath);
-
- if (fileInfo.LastWriteTime > _lastModified ||
- !_cache.TryGetValue(CacheKeys.IgnoredDownloads(_configType), out IReadOnlyList? ignoredDownloads) ||
- ignoredDownloads is null)
- {
- _lastModified = fileInfo.LastWriteTime;
-
- return await LoadFile();
- }
-
- return ignoredDownloads;
- }
-
- private async Task> LoadFile()
- {
- try
- {
- if (string.IsNullOrEmpty(_config.IgnoredDownloadsPath))
- {
- return Array.Empty();
- }
-
- string[] ignoredDownloads = (await File.ReadAllLinesAsync(_config.IgnoredDownloadsPath))
- .Where(x => !string.IsNullOrWhiteSpace(x))
- .ToArray();
-
- _cache.Set(CacheKeys.IgnoredDownloads(typeof(T).Name), ignoredDownloads);
-
- _logger.LogInformation("ignored downloads reloaded");
-
- return ignoredDownloads;
- }
- catch (Exception exception)
- {
- _logger.LogError(exception, "error while reading ignored downloads file | {file}", _config.IgnoredDownloadsPath);
- }
-
- return Array.Empty();
- }
-}
\ No newline at end of file
diff --git a/code/Infrastructure/Services/ConfigurationService.cs b/code/Infrastructure/Services/ConfigurationService.cs
index 02facf90..93d274f9 100644
--- a/code/Infrastructure/Services/ConfigurationService.cs
+++ b/code/Infrastructure/Services/ConfigurationService.cs
@@ -1,85 +1,57 @@
using Common.Configuration;
-using Microsoft.Extensions.Configuration;
+using Common.Configuration.ContentBlocker;
+using Common.Configuration.DownloadCleaner;
+using Common.Configuration.DownloadClient;
+using Common.Configuration.IgnoredDownloads;
+using Common.Configuration.QueueCleaner;
+using Infrastructure.Configuration;
using Microsoft.Extensions.Logging;
-using System.Text.Json;
-using System.Text.Json.Nodes;
-using Microsoft.Extensions.Hosting;
namespace Infrastructure.Services;
public interface IConfigurationService
{
- Task UpdateConfigurationAsync(string sectionName, T configSection) where T : class, IConfig;
- Task GetConfigurationAsync(string sectionName) where T : class, IConfig;
- Task RefreshConfigurationAsync();
+ Task UpdateConfigurationAsync(string sectionName, T configSection);
+ Task GetConfigurationAsync(string sectionName);
+ Task GetSonarrConfigAsync();
+ Task GetRadarrConfigAsync();
+ Task GetLidarrConfigAsync();
+ Task GetContentBlockerConfigAsync();
+ Task GetQueueCleanerConfigAsync();
+ Task GetDownloadCleanerConfigAsync();
+ Task GetDownloadClientConfigAsync();
+ Task GetIgnoredDownloadsConfigAsync();
+ Task UpdateSonarrConfigAsync(SonarrConfig config);
+ Task UpdateRadarrConfigAsync(RadarrConfig config);
+ Task UpdateLidarrConfigAsync(LidarrConfig config);
+ Task UpdateContentBlockerConfigAsync(ContentBlockerConfig config);
+ Task UpdateQueueCleanerConfigAsync(QueueCleanerConfig config);
+ Task UpdateDownloadCleanerConfigAsync(DownloadCleanerConfig config);
+ Task UpdateDownloadClientConfigAsync(DownloadClientConfig config);
+ Task UpdateIgnoredDownloadsConfigAsync(IgnoredDownloadsConfig config);
}
public class ConfigurationService : IConfigurationService
{
private readonly ILogger _logger;
- private readonly IConfiguration _configuration;
- private readonly string _configFilePath;
+ private readonly IConfigurationManager _configManager;
public ConfigurationService(
ILogger logger,
- IConfiguration configuration,
- IHostEnvironment environment)
+ IConfigurationManager configManager)
{
_logger = logger;
- _configuration = configuration;
-
- // Find primary configuration file
- var currentDirectory = environment.ContentRootPath;
- _configFilePath = Path.Combine(currentDirectory, "appsettings.json");
-
- if (!File.Exists(_configFilePath))
- {
- _logger.LogWarning("Configuration file not found at: {path}", _configFilePath);
- _configFilePath = Path.Combine(currentDirectory, "appsettings.Development.json");
-
- if (!File.Exists(_configFilePath))
- {
- _logger.LogError("No configuration file found");
- throw new FileNotFoundException("Configuration file not found");
- }
- }
-
- _logger.LogInformation("Using configuration file: {path}", _configFilePath);
+ _configManager = configManager;
}
- public async Task UpdateConfigurationAsync(string sectionName, T configSection) where T : class, IConfig
+ public async Task UpdateConfigurationAsync(string sectionName, T configSection)
{
try
{
- // Read existing configuration
- var json = await File.ReadAllTextAsync(_configFilePath);
- var jsonObject = JsonNode.Parse(json)?.AsObject()
- ?? throw new InvalidOperationException("Failed to parse configuration file");
-
- // Create JsonObject from config section
- var configJson = JsonSerializer.Serialize(configSection);
- var configObject = JsonNode.Parse(configJson)?.AsObject()
- ?? throw new InvalidOperationException("Failed to serialize configuration");
-
- // Update or add the section
- jsonObject[sectionName] = configObject;
-
- // Save back to file
- var options = new JsonSerializerOptions { WriteIndented = true };
- var updatedJson = jsonObject.ToJsonString(options);
-
- // Create backup
- var backupPath = $"{_configFilePath}.bak";
- await File.WriteAllTextAsync(backupPath, json);
-
- // Write updated configuration
- await File.WriteAllTextAsync(_configFilePath, updatedJson);
-
- // Refresh configuration
- await RefreshConfigurationAsync();
-
- _logger.LogInformation("Configuration section {section} updated successfully", sectionName);
- return true;
+ // This is just a placeholder method for backward compatibility
+ // The actual implementation depends on the specific config type
+ _logger.LogWarning("Using deprecated UpdateConfigurationAsync method with section name '{section}'.", sectionName);
+ return false;
}
catch (Exception ex)
{
@@ -88,52 +60,39 @@ public class ConfigurationService : IConfigurationService
}
}
- public async Task GetConfigurationAsync(string sectionName) where T : class, IConfig
+ public async Task GetConfigurationAsync(string sectionName)
{
try
{
- var json = await File.ReadAllTextAsync(_configFilePath);
- var jsonObject = JsonNode.Parse(json)?.AsObject();
-
- if (jsonObject == null || !jsonObject.ContainsKey(sectionName))
- {
- _logger.LogWarning("Section {section} not found in configuration", sectionName);
- return null;
- }
-
- var sectionObject = jsonObject[sectionName]?.ToJsonString();
- if (sectionObject == null)
- {
- return null;
- }
-
- return JsonSerializer.Deserialize(sectionObject);
+ // This is just a placeholder method for backward compatibility
+ // The actual implementation depends on the specific config type
+ _logger.LogWarning("Using deprecated GetConfigurationAsync method with section name '{section}'.", sectionName);
+ return default;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving configuration section {section}", sectionName);
- return null;
+ return default;
}
}
- public Task RefreshConfigurationAsync()
- {
- try
- {
- if (_configuration is IConfigurationRoot configRoot)
- {
- configRoot.Reload();
- _logger.LogInformation("Configuration reloaded");
- return Task.FromResult(true);
- }
-
- _logger.LogWarning("Unable to reload configuration: IConfigurationRoot not available");
- return Task.FromResult(false);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error reloading configuration");
- return Task.FromResult(false);
- }
- }
+ // Specific configuration getters
+ public async Task GetSonarrConfigAsync() => await _configManager.GetSonarrConfigAsync();
+ public async Task GetRadarrConfigAsync() => await _configManager.GetRadarrConfigAsync();
+ public async Task GetLidarrConfigAsync() => await _configManager.GetLidarrConfigAsync();
+ public async Task GetContentBlockerConfigAsync() => await _configManager.GetContentBlockerConfigAsync();
+ public async Task GetQueueCleanerConfigAsync() => await _configManager.GetQueueCleanerConfigAsync();
+ public async Task GetDownloadCleanerConfigAsync() => await _configManager.GetDownloadCleanerConfigAsync();
+ public async Task GetDownloadClientConfigAsync() => await _configManager.GetDownloadClientConfigAsync();
+ public async Task GetIgnoredDownloadsConfigAsync() => await _configManager.GetIgnoredDownloadsConfigAsync();
+
+ // Specific configuration setters
+ public async Task UpdateSonarrConfigAsync(SonarrConfig config) => await _configManager.SaveSonarrConfigAsync(config);
+ public async Task UpdateRadarrConfigAsync(RadarrConfig config) => await _configManager.SaveRadarrConfigAsync(config);
+ public async Task UpdateLidarrConfigAsync(LidarrConfig config) => await _configManager.SaveLidarrConfigAsync(config);
+ public async Task UpdateContentBlockerConfigAsync(ContentBlockerConfig config) => await _configManager.SaveContentBlockerConfigAsync(config);
+ public async Task UpdateQueueCleanerConfigAsync(QueueCleanerConfig config) => await _configManager.SaveQueueCleanerConfigAsync(config);
+ public async Task UpdateDownloadCleanerConfigAsync(DownloadCleanerConfig config) => await _configManager.SaveDownloadCleanerConfigAsync(config);
+ public async Task UpdateDownloadClientConfigAsync(DownloadClientConfig config) => await _configManager.SaveDownloadClientConfigAsync(config);
+ public async Task UpdateIgnoredDownloadsConfigAsync(IgnoredDownloadsConfig config) => await _configManager.SaveIgnoredDownloadsConfigAsync(config);
}
diff --git a/code/Infrastructure/Services/IgnoredDownloadsService.cs b/code/Infrastructure/Services/IgnoredDownloadsService.cs
new file mode 100644
index 00000000..6f086d2a
--- /dev/null
+++ b/code/Infrastructure/Services/IgnoredDownloadsService.cs
@@ -0,0 +1,171 @@
+using Common.Configuration.IgnoredDownloads;
+using Infrastructure.Configuration;
+using Infrastructure.Helpers;
+using Microsoft.Extensions.Caching.Memory;
+using Microsoft.Extensions.Logging;
+
+namespace Infrastructure.Services;
+
+///
+/// Service for managing ignored downloads
+///
+public interface IIgnoredDownloadsService
+{
+ ///
+ /// Gets the list of ignored download IDs
+ ///
+ Task> GetIgnoredDownloadsAsync();
+
+ ///
+ /// Adds a download ID to the ignored list
+ ///
+ Task AddIgnoredDownloadAsync(string downloadId);
+
+ ///
+ /// Removes a download ID from the ignored list
+ ///
+ Task RemoveIgnoredDownloadAsync(string downloadId);
+
+ ///
+ /// Clears all ignored downloads
+ ///
+ Task ClearIgnoredDownloadsAsync();
+}
+
+public class IgnoredDownloadsService : IIgnoredDownloadsService
+{
+ private readonly ILogger _logger;
+ private readonly IConfigurationManager _configManager;
+ private readonly IMemoryCache _cache;
+ private const string IgnoredDownloadsCacheKey = "IgnoredDownloads";
+
+ public IgnoredDownloadsService(
+ ILogger logger,
+ IConfigurationManager configManager,
+ IMemoryCache cache)
+ {
+ _logger = logger;
+ _configManager = configManager;
+ _cache = cache;
+ }
+
+ public async Task> GetIgnoredDownloadsAsync()
+ {
+ // Try to get from cache first
+ if (_cache.TryGetValue(IgnoredDownloadsCacheKey, out IReadOnlyList? cachedList) &&
+ cachedList != null)
+ {
+ return cachedList;
+ }
+
+ // Not in cache, load from config
+ var config = await _configManager.GetIgnoredDownloadsConfigAsync();
+ if (config == null)
+ {
+ return Array.Empty();
+ }
+
+ // Store in cache for quick access (5 minute expiration)
+ var ignoredDownloads = config.IgnoredDownloads.ToList();
+ _cache.Set(IgnoredDownloadsCacheKey, ignoredDownloads, TimeSpan.FromMinutes(5));
+
+ return ignoredDownloads;
+ }
+
+ public async Task AddIgnoredDownloadAsync(string downloadId)
+ {
+ if (string.IsNullOrWhiteSpace(downloadId))
+ {
+ return false;
+ }
+
+ var config = await _configManager.GetIgnoredDownloadsConfigAsync();
+ if (config == null)
+ {
+ config = new IgnoredDownloadsConfig
+ {
+ IgnoredDownloads = new List { downloadId }
+ };
+ }
+ else if (!config.IgnoredDownloads.Contains(downloadId, StringComparer.OrdinalIgnoreCase))
+ {
+ var updatedList = config.IgnoredDownloads.ToList();
+ updatedList.Add(downloadId);
+ config = new IgnoredDownloadsConfig
+ {
+ IgnoredDownloads = updatedList
+ };
+ }
+ else
+ {
+ // Already in the list
+ return true;
+ }
+
+ var result = await _configManager.SaveIgnoredDownloadsConfigAsync(config);
+ if (result)
+ {
+ // Update cache
+ _cache.Remove(IgnoredDownloadsCacheKey);
+ _logger.LogInformation("Added download ID to ignored list: {downloadId}", downloadId);
+ }
+
+ return result;
+ }
+
+ public async Task RemoveIgnoredDownloadAsync(string downloadId)
+ {
+ if (string.IsNullOrWhiteSpace(downloadId))
+ {
+ return false;
+ }
+
+ var config = await _configManager.GetIgnoredDownloadsConfigAsync();
+ if (config == null || config.IgnoredDownloads.Count == 0)
+ {
+ return true; // Nothing to remove
+ }
+
+ var updatedList = config.IgnoredDownloads
+ .Where(id => !string.Equals(id, downloadId, StringComparison.OrdinalIgnoreCase))
+ .ToList();
+
+ if (updatedList.Count == config.IgnoredDownloads.Count)
+ {
+ return true; // Item wasn't in the list
+ }
+
+ var newConfig = new IgnoredDownloadsConfig
+ {
+ IgnoredDownloads = updatedList
+ };
+
+ var result = await _configManager.SaveIgnoredDownloadsConfigAsync(newConfig);
+ if (result)
+ {
+ // Update cache
+ _cache.Remove(IgnoredDownloadsCacheKey);
+ _logger.LogInformation("Removed download ID from ignored list: {downloadId}", downloadId);
+ }
+
+ return result;
+ }
+
+ public async Task ClearIgnoredDownloadsAsync()
+ {
+ var config = new IgnoredDownloadsConfig
+ {
+ IgnoredDownloads = new List()
+ };
+
+ var result = await _configManager.SaveIgnoredDownloadsConfigAsync(config);
+ if (result)
+ {
+ // Update cache
+ _cache.Remove(IgnoredDownloadsCacheKey);
+ _logger.LogInformation("Cleared all ignored downloads");
+ }
+
+ return result;
+ }
+}
diff --git a/code/Infrastructure/Verticals/ContentBlocker/ContentBlocker.cs b/code/Infrastructure/Verticals/ContentBlocker/ContentBlocker.cs
index 00678fbd..65f67bc7 100644
--- a/code/Infrastructure/Verticals/ContentBlocker/ContentBlocker.cs
+++ b/code/Infrastructure/Verticals/ContentBlocker/ContentBlocker.cs
@@ -8,7 +8,7 @@ using Domain.Models.Arr;
using Domain.Models.Arr.Queue;
using Infrastructure.Configuration;
using Infrastructure.Helpers;
-using Infrastructure.Providers;
+using Infrastructure.Services;
using Infrastructure.Verticals.Arr;
using Infrastructure.Verticals.Arr.Interfaces;
using Infrastructure.Verticals.Context;
@@ -27,7 +27,7 @@ public sealed class ContentBlocker : GenericHandler
{
private readonly ContentBlockerConfig _config;
private readonly BlocklistProvider _blocklistProvider;
- private readonly IgnoredDownloadsProvider _ignoredDownloadsProvider;
+ private readonly IIgnoredDownloadsService _ignoredDownloadsService;
private readonly IConfigurationManager _configManager;
public ContentBlocker(
@@ -40,7 +40,7 @@ public sealed class ContentBlocker : GenericHandler
BlocklistProvider blocklistProvider,
DownloadServiceFactory downloadServiceFactory,
INotificationPublisher notifier,
- IgnoredDownloadsProvider ignoredDownloadsProvider
+ IIgnoredDownloadsService ignoredDownloadsService
) : base(
logger, cache, messageBus,
arrClientFactory, arrArrQueueIterator,
@@ -49,7 +49,7 @@ public sealed class ContentBlocker : GenericHandler
{
_configManager = configManager;
_blocklistProvider = blocklistProvider;
- _ignoredDownloadsProvider = ignoredDownloadsProvider;
+ _ignoredDownloadsService = ignoredDownloadsService;
// Initialize the configuration
var configTask = _configManager.GetContentBlockerConfigAsync();
@@ -101,7 +101,7 @@ public sealed class ContentBlocker : GenericHandler
protected override async Task ProcessInstanceAsync(ArrInstance instance, InstanceType instanceType, ArrConfig config)
{
- IReadOnlyList ignoredDownloads = await _ignoredDownloadsProvider.GetIgnoredDownloads();
+ IReadOnlyList ignoredDownloads = await _ignoredDownloadsService.GetIgnoredDownloadsAsync();
using var _ = LogContext.PushProperty("InstanceName", instanceType.ToString());
diff --git a/code/Infrastructure/Verticals/DownloadCleaner/DownloadCleaner.cs b/code/Infrastructure/Verticals/DownloadCleaner/DownloadCleaner.cs
index cadf641c..78aae559 100644
--- a/code/Infrastructure/Verticals/DownloadCleaner/DownloadCleaner.cs
+++ b/code/Infrastructure/Verticals/DownloadCleaner/DownloadCleaner.cs
@@ -4,7 +4,7 @@ using Common.Configuration.DownloadClient;
using Domain.Enums;
using Domain.Models.Arr.Queue;
using Infrastructure.Configuration;
-using Infrastructure.Providers;
+using Infrastructure.Services;
using Infrastructure.Verticals.Arr;
using Infrastructure.Verticals.Arr.Interfaces;
using Infrastructure.Verticals.DownloadClient;
@@ -20,7 +20,7 @@ namespace Infrastructure.Verticals.DownloadCleaner;
public sealed class DownloadCleaner : GenericHandler
{
private readonly DownloadCleanerConfig _config;
- private readonly IgnoredDownloadsProvider _ignoredDownloadsProvider;
+ private readonly IIgnoredDownloadsService _ignoredDownloadsService;
private readonly HashSet _excludedHashes = [];
private readonly IConfigurationManager _configManager;
@@ -35,7 +35,7 @@ public sealed class DownloadCleaner : GenericHandler
ArrQueueIterator arrArrQueueIterator,
DownloadServiceFactory downloadServiceFactory,
INotificationPublisher notifier,
- IgnoredDownloadsProvider ignoredDownloadsProvider
+ IIgnoredDownloadsService ignoredDownloadsService
) : base(
logger, cache, messageBus,
arrClientFactory, arrArrQueueIterator, downloadServiceFactory,
@@ -43,7 +43,7 @@ public sealed class DownloadCleaner : GenericHandler
)
{
_configManager = configManager;
- _ignoredDownloadsProvider = ignoredDownloadsProvider;
+ _ignoredDownloadsService = ignoredDownloadsService;
// Initialize the configuration
var configTask = _configManager.GetDownloadCleanerConfigAsync();
@@ -87,7 +87,7 @@ public sealed class DownloadCleaner : GenericHandler
return;
}
- IReadOnlyList ignoredDownloads = await _ignoredDownloadsProvider.GetIgnoredDownloads();
+ IReadOnlyList ignoredDownloads = await _ignoredDownloadsService.GetIgnoredDownloadsAsync();
await _downloadService.LoginAsync();
List