Tracker list caching with configurable expiration

Add new option to general settings that allows the user to set the time in minutes to cache the tracker list. 0 disables caching. Default value is 60.
This commit is contained in:
MentalBlank 2025-05-21 00:45:14 +10:00
parent 80a8a1020c
commit 408c475af5
3 changed files with 77 additions and 16 deletions

View file

@ -77,7 +77,11 @@ Supports the following parameters:
[DisplayName("Tracker enrichment list")]
[Description("Optional. Specify the URL of a tracker list file to be appended to magnet links and torrent files.")]
public String? MagnetTrackerEnrichment { get; set; } = null;
public String? TrackerEnrichmentList { get; set; } = null;
[DisplayName("Tracker enrichment cache expiration")]
[Description("The time in minutes to cache the tracker list. Set to 0 to disable caching.")]
public Int32 TrackerEnrichmentCacheExpiration { get; set; } = 60;
[DisplayName("Disable update notifications")]
[Description("Ignore update notifications. You will still be notified if the version you are running has a security vulnerability.")]

View file

@ -18,6 +18,8 @@ public static class DiConfig
public static void RegisterRdtServices(this IServiceCollection services)
{
services.AddMemoryCache();
services.AddSingleton<IAllDebridNetClientFactory, AllDebridNetClientFactory>();
services.AddScoped<AllDebridTorrentClient>();

View file

@ -1,34 +1,89 @@
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
namespace RdtClient.Service.Services;
public class TrackerListGrabber : ITrackerListGrabber
public class TrackerListGrabber(IHttpClientFactory httpClientFactory, IMemoryCache memoryCache, ILogger<TrackerListGrabber> logger) : ITrackerListGrabber
{
private readonly IHttpClientFactory _httpClientFactory;
private const String CacheKey = "TrackerList";
public TrackerListGrabber(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
private Int32? _lastExpirationMinutes;
private static readonly SemaphoreSlim Semaphore = new(1, 1);
public async Task<String[]> GetTrackers()
{
var trackerUrlList = Settings.Get.General.MagnetTrackerEnrichment;
if (String.IsNullOrWhiteSpace(trackerUrlList))
var currentExpiration = Settings.Get.General.TrackerEnrichmentCacheExpiration;
var useCache = currentExpiration > 0;
if (!useCache)
{
return [];
memoryCache.Remove(CacheKey);
_lastExpirationMinutes = null;
}
else
{
if (_lastExpirationMinutes is not null && currentExpiration != _lastExpirationMinutes)
{
logger.LogDebug("Tracker list cache timeout changed, invalidating cache.");
memoryCache.Remove(CacheKey);
}
_lastExpirationMinutes = currentExpiration;
if (memoryCache.TryGetValue(CacheKey, out String[]? cachedTrackers) && cachedTrackers is { Length: > 0 })
{
logger.LogDebug("Using cached tracker list.");
return cachedTrackers;
}
}
await Semaphore.WaitAsync();
try
{
var httpClient = _httpClientFactory.CreateClient();
var result = await httpClient.GetStringAsync(trackerUrlList);
return result
.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
logger.LogDebug("Tracker cache miss or cache disabled. Fetching tracker list.");
var trackerUrlList = Settings.Get.General.TrackerEnrichmentList;
if (String.IsNullOrWhiteSpace(trackerUrlList))
{
return [];
}
var httpClient = httpClientFactory.CreateClient();
var response = await httpClient.GetAsync(trackerUrlList);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
var trackers = result
.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
if (useCache)
{
memoryCache.Set(CacheKey,
trackers,
new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(currentExpiration)
});
}
return trackers;
}
catch (Exception ex)
{
logger.LogError(ex, "Unable to fetch tracker list.");
throw new InvalidOperationException("Unable to fetch tracker list for enrichment.", ex);
}
finally
{
Semaphore.Release();
}
}
}