diff --git a/CHANGELOG.md b/CHANGELOG.md
index a8b64aa..60fab41 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [2.0.80] - 2024-07-13
+### Changed
+- Add rate limiter to retry requests that are rate limited from Real-Debrid.
+- Optimize calls to Real-Debrid API when torrents are finished and periodic updates.
+- Update to .NET 8.0.7
+
## [2.0.79] - 2024-06-03
### Changed
- Fixed issue with qBittorrent progress sometimes throwing errors.
diff --git a/package.json b/package.json
index 7b1dfbb..204f20c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "rdt-client",
- "version": "2.0.79",
+ "version": "2.0.80",
"description": "This is a web interface to manage your torrents on Real-Debrid.",
"main": "index.js",
"dependencies": {
diff --git a/server/RdtClient.Data/RdtClient.Data.csproj b/server/RdtClient.Data/RdtClient.Data.csproj
index 8a0f20f..ca714f0 100644
--- a/server/RdtClient.Data/RdtClient.Data.csproj
+++ b/server/RdtClient.Data/RdtClient.Data.csproj
@@ -8,15 +8,15 @@
-
-
-
-
-
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/server/RdtClient.Service/BackgroundServices/UpdateChecker.cs b/server/RdtClient.Service/BackgroundServices/UpdateChecker.cs
index 1b63026..846c000 100644
--- a/server/RdtClient.Service/BackgroundServices/UpdateChecker.cs
+++ b/server/RdtClient.Service/BackgroundServices/UpdateChecker.cs
@@ -26,7 +26,7 @@ public class UpdateChecker(ILogger logger) : BackgroundService
return;
}
- CurrentVersion = $"v{version[..version.LastIndexOf(".", StringComparison.Ordinal)]}";
+ CurrentVersion = $"v{version[..version.LastIndexOf('.')]}";
logger.LogInformation("UpdateChecker started, currently on version {CurrentVersion}.", CurrentVersion);
diff --git a/server/RdtClient.Service/DiConfig.cs b/server/RdtClient.Service/DiConfig.cs
index c9a1449..e5f7aa2 100644
--- a/server/RdtClient.Service/DiConfig.cs
+++ b/server/RdtClient.Service/DiConfig.cs
@@ -1,8 +1,9 @@
-using Microsoft.AspNetCore.Authorization;
+using System.Net;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
+using Polly;
+using Polly.Extensions.Http;
using RdtClient.Service.BackgroundServices;
-using RdtClient.Service.Handlers;
using RdtClient.Service.Middleware;
using RdtClient.Service.Services;
using RdtClient.Service.Services.TorrentClients;
@@ -38,9 +39,13 @@ public static class DiConfig
public static void RegisterHttpClients(this IServiceCollection services)
{
services.AddHttpClient();
-
+
+ var retryPolicy = HttpPolicyExtensions
+ .HandleTransientHttpError()
+ .OrResult(r => r.StatusCode == HttpStatusCode.TooManyRequests)
+ .WaitAndRetryAsync(retryCount: 5, sleepDurationProvider: attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));
+
services.AddHttpClient(RD_CLIENT)
- .AddHttpMessageHandler(sp => new RateLimitingHandler(sp.GetRequiredService>(), 60, TimeSpan.FromSeconds(60)))
- .SetHandlerLifetime(Timeout.InfiniteTimeSpan);
+ .AddPolicyHandler(retryPolicy);
}
}
\ No newline at end of file
diff --git a/server/RdtClient.Service/Handlers/RateLimitingHandler.cs b/server/RdtClient.Service/Handlers/RateLimitingHandler.cs
deleted file mode 100644
index 89cfd62..0000000
--- a/server/RdtClient.Service/Handlers/RateLimitingHandler.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System.Collections.Concurrent;
-using Microsoft.Extensions.Logging;
-
-namespace RdtClient.Service.Handlers;
-
-public class RateLimitingHandler : DelegatingHandler
-{
- private readonly ConcurrentQueue _callQueue = new();
- private readonly TimeSpan _requestWindow;
- private readonly ILogger _logger;
- private readonly Int32 _requestsAllowedPerWindow;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The logger.
- /// The number of requests allowed per window.
- /// The time to delay when the maximum number of requests has been reached (The window).
- public RateLimitingHandler(ILogger logger, Int32 requestsAllowedPerWindow, TimeSpan requestWindow)
- {
- ArgumentOutOfRangeException.ThrowIfNegativeOrZero(requestsAllowedPerWindow);
- ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(requestWindow, TimeSpan.Zero);
- _logger = logger;
- _requestsAllowedPerWindow = requestsAllowedPerWindow;
- _requestWindow = requestWindow;
- }
-
- ///
- protected override async Task SendAsync(HttpRequestMessage request,
- CancellationToken cancellationToken)
- {
- var now = DateTimeOffset.UtcNow;
- _callQueue.Enqueue(now);
- while (_callQueue.TryPeek(out var oldCall) && now - oldCall > _requestWindow)
- {
- _logger.LogDebug("Removing oldest request from queue");
- _callQueue.TryDequeue(out _);
- }
- if (_callQueue.Count > _requestsAllowedPerWindow)
- {
- _logger.LogDebug("Delaying request");
- var earliestAllowedRequestTime = now.Add(-_requestWindow);
- var delayDuration = _callQueue.ElementAtOrDefault(_requestsAllowedPerWindow - 1) -
- earliestAllowedRequestTime;
- if (delayDuration > TimeSpan.Zero)
- {
- _logger.LogDebug("Delaying request for {DelayDuration}", delayDuration);
- await Task.Delay(delayDuration, cancellationToken);
- }
- }
- _logger.LogDebug("Sending request");
- return await base.SendAsync(request, cancellationToken);
- }
-}
\ No newline at end of file
diff --git a/server/RdtClient.Service/Helpers/DownloadHelper.cs b/server/RdtClient.Service/Helpers/DownloadHelper.cs
index 1dc9830..0c9a1c4 100644
--- a/server/RdtClient.Service/Helpers/DownloadHelper.cs
+++ b/server/RdtClient.Service/Helpers/DownloadHelper.cs
@@ -27,7 +27,7 @@ public static class DownloadHelper
var matchingTorrentFiles = torrent.Files.Where(m => m.Path.EndsWith(fileName)).Where(m => !String.IsNullOrWhiteSpace(m.Path)).ToList();
- if (matchingTorrentFiles.Any())
+ if (matchingTorrentFiles.Count > 0)
{
var matchingTorrentFile = matchingTorrentFiles[0];
@@ -71,7 +71,7 @@ public static class DownloadHelper
var matchingTorrentFiles = torrent.Files.Where(m => m.Path.EndsWith(fileName)).Where(m => !String.IsNullOrWhiteSpace(m.Path)).ToList();
- if (matchingTorrentFiles.Any())
+ if (matchingTorrentFiles.Count > 0)
{
var matchingTorrentFile = matchingTorrentFiles[0];
diff --git a/server/RdtClient.Service/Helpers/JsonModelBinder.cs b/server/RdtClient.Service/Helpers/JsonModelBinder.cs
index f7722b6..1a7c176 100644
--- a/server/RdtClient.Service/Helpers/JsonModelBinder.cs
+++ b/server/RdtClient.Service/Helpers/JsonModelBinder.cs
@@ -6,10 +6,7 @@ public class JsonModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
- if (bindingContext == null)
- {
- throw new ArgumentNullException(nameof(bindingContext));
- }
+ ArgumentNullException.ThrowIfNull(bindingContext);
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult != ValueProviderResult.None)
diff --git a/server/RdtClient.Service/Middleware/BaseHrefMiddleware.cs b/server/RdtClient.Service/Middleware/BaseHrefMiddleware.cs
index 747e0ce..cd64549 100644
--- a/server/RdtClient.Service/Middleware/BaseHrefMiddleware.cs
+++ b/server/RdtClient.Service/Middleware/BaseHrefMiddleware.cs
@@ -3,8 +3,17 @@ using Microsoft.AspNetCore.Http;
namespace RdtClient.Service.Middleware;
-public class BaseHrefMiddleware(RequestDelegate next, String basePath)
+public partial class BaseHrefMiddleware(RequestDelegate next, String basePath)
{
+ [GeneratedRegex(@")")]
+ private partial Regex ScriptRegex();
+
+ [GeneratedRegex("()")]
+ private partial Regex LinkRegex();
+
private readonly String _basePath = $"/{basePath.TrimStart('/').TrimEnd('/')}/";
public async Task InvokeAsync(HttpContext context)
@@ -27,10 +36,10 @@ public class BaseHrefMiddleware(RequestDelegate next, String basePath)
{
if (context.Response.ContentType?.Contains("text/html") == true)
{
- responseBody = Regex.Replace(responseBody, @")", $"$1{_basePath}$2$3");
- responseBody = Regex.Replace(responseBody, "()", $"$1{_basePath}$2$3");
+ responseBody = ScriptRegex().Replace(responseBody, $"$1{_basePath}$2$3");
+ responseBody = LinkRegex().Replace(responseBody, $"$1{_basePath}$2$3");
context.Response.Headers.Remove("Content-Length");
await context.Response.WriteAsync(responseBody);
diff --git a/server/RdtClient.Service/RdtClient.Service.csproj b/server/RdtClient.Service/RdtClient.Service.csproj
index 374e759..c736d2e 100644
--- a/server/RdtClient.Service/RdtClient.Service.csproj
+++ b/server/RdtClient.Service/RdtClient.Service.csproj
@@ -11,14 +11,17 @@
-
+
+
+
+
-
-
-
+
+
+
diff --git a/server/RdtClient.Service/Services/Downloaders/SymlinkDownloader.cs b/server/RdtClient.Service/Services/Downloaders/SymlinkDownloader.cs
index 184c63b..08d3e15 100644
--- a/server/RdtClient.Service/Services/Downloaders/SymlinkDownloader.cs
+++ b/server/RdtClient.Service/Services/Downloaders/SymlinkDownloader.cs
@@ -23,7 +23,7 @@ public class SymlinkDownloader(String uri, String destinationPath, String path)
var filePath = new FileInfo(path);
var rcloneMountPath = Settings.Get.DownloadClient.RcloneMountPath.TrimEnd(['\\', '/']);
- var searchSubDirectories = rcloneMountPath.EndsWith("*");
+ var searchSubDirectories = rcloneMountPath.EndsWith('*');
rcloneMountPath = rcloneMountPath.TrimEnd('*').TrimEnd(['\\', '/']);
if (!Directory.Exists(rcloneMountPath))
diff --git a/server/RdtClient.Service/Services/RdtHub.cs b/server/RdtClient.Service/Services/RdtHub.cs
index e8545af..f7f2367 100644
--- a/server/RdtClient.Service/Services/RdtHub.cs
+++ b/server/RdtClient.Service/Services/RdtHub.cs
@@ -7,7 +7,7 @@ public class RdtHub : Hub
{
private static readonly ConcurrentDictionary Users = new();
- public static Boolean HasConnections => Users.Any();
+ public static Boolean HasConnections => !Users.IsEmpty;
public override async Task OnConnectedAsync()
{
diff --git a/server/RdtClient.Service/Services/UnpackClient.cs b/server/RdtClient.Service/Services/UnpackClient.cs
index b840966..2724f43 100644
--- a/server/RdtClient.Service/Services/UnpackClient.cs
+++ b/server/RdtClient.Service/Services/UnpackClient.cs
@@ -131,7 +131,7 @@ public class UnpackClient(Download download, String destinationPath)
var entries = archive.Entries
.Where(entry => !entry.IsDirectory)
- .Select(m => m.Key)
+ .Select(m => m.Key!)
.ToList();
archive.Dispose();
diff --git a/server/RdtClient.Web/Controllers/QBittorrentController.cs b/server/RdtClient.Web/Controllers/QBittorrentController.cs
index b4dad0e..d0c57cb 100644
--- a/server/RdtClient.Web/Controllers/QBittorrentController.cs
+++ b/server/RdtClient.Web/Controllers/QBittorrentController.cs
@@ -282,7 +282,7 @@ public class QBittorrentController(ILogger logger, QBitto
return BadRequest();
}
- logger.LogDebug($"Delete {request.Hashes}");
+ logger.LogDebug("Delete {Hashes}", request.Hashes);
var hashes = request.Hashes.Split("|");
diff --git a/server/RdtClient.Web/RdtClient.Web.csproj b/server/RdtClient.Web/RdtClient.Web.csproj
index ef6a09f..2ec30d0 100644
--- a/server/RdtClient.Web/RdtClient.Web.csproj
+++ b/server/RdtClient.Web/RdtClient.Web.csproj
@@ -29,18 +29,18 @@
-
-
-
-
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
-
+