diff --git a/README.md b/README.md
index e4777eb..c8aa2eb 100644
--- a/README.md
+++ b/README.md
@@ -145,10 +145,20 @@ If you use Proxmox for your homelab, you can run rdt-client in a linux container
### Download Clients
-Currently there 2 available download clients:
+Currently there 4 available download clients:
#### Internal Downloader
+This experimental [downloader](https://github.com/rogerfar/Downloader.NET) can be used to download files with multiple sections in parallel.
+
+It has the following options:
+
+- Download speed (in MB/s): This number indicates the speed in MB/s per download over all parallel downloads and chunks.
+- Parallel connections per download: When a file is downloaded it is split in sections, this setting indicates how many sections you will download in parallel.
+- Connection Timeout: This number indicates the timeout in milliseconds before a download chunk times out. It will retry each chunk 5 times before completely failing.
+
+#### Bezzad Downloader
+
This [downloader](https://github.com/bezzad/Downloader) can be used to download files in parallel and with multiple chunks.
It has the following options:
diff --git a/server/RdtClient.Data/Enums/DownloadClient.cs b/server/RdtClient.Data/Enums/DownloadClient.cs
index dcdd14e..9f43867 100644
--- a/server/RdtClient.Data/Enums/DownloadClient.cs
+++ b/server/RdtClient.Data/Enums/DownloadClient.cs
@@ -7,6 +7,9 @@ public enum DownloadClient
[Description("Internal Downloader")]
Internal,
+ [Description("Bezzad Downloader")]
+ Bezzad,
+
[Description("Aria2c")]
Aria2c,
diff --git a/server/RdtClient.Service/RdtClient.Service.csproj b/server/RdtClient.Service/RdtClient.Service.csproj
index e3f7748..1b66179 100644
--- a/server/RdtClient.Service/RdtClient.Service.csproj
+++ b/server/RdtClient.Service/RdtClient.Service.csproj
@@ -12,6 +12,7 @@
+
diff --git a/server/RdtClient.Service/Services/DownloadClient.cs b/server/RdtClient.Service/Services/DownloadClient.cs
index 07e53f0..f909b6e 100644
--- a/server/RdtClient.Service/Services/DownloadClient.cs
+++ b/server/RdtClient.Service/Services/DownloadClient.cs
@@ -58,6 +58,7 @@ public class DownloadClient
Downloader = Settings.Get.DownloadClient.Client switch
{
Data.Enums.DownloadClient.Internal => new InternalDownloader(_download.Link, filePath),
+ Data.Enums.DownloadClient.Bezzad => new BezzadDownloader(_download.Link, filePath),
Data.Enums.DownloadClient.Aria2c => new Aria2cDownloader(_download.RemoteId, _download.Link, filePath, downloadPath),
Data.Enums.DownloadClient.Symlink => new SymlinkDownloader(_download.Link, filePath),
_ => throw new Exception($"Unknown download client {Settings.Get.DownloadClient}")
diff --git a/server/RdtClient.Service/Services/Downloaders/BezzadDownloader.cs b/server/RdtClient.Service/Services/Downloaders/BezzadDownloader.cs
new file mode 100644
index 0000000..7ae394e
--- /dev/null
+++ b/server/RdtClient.Service/Services/Downloaders/BezzadDownloader.cs
@@ -0,0 +1,186 @@
+using System.Net;
+using Downloader;
+using Serilog;
+
+namespace RdtClient.Service.Services.Downloaders;
+
+public class BezzadDownloader : IDownloader
+{
+ public event EventHandler? DownloadComplete;
+ public event EventHandler? DownloadProgress;
+
+ private readonly DownloadService _downloadService;
+ private readonly DownloadConfiguration _downloadConfiguration;
+
+ private readonly String _filePath;
+ private readonly String _uri;
+
+ private readonly ILogger _logger;
+
+ private Boolean _finished;
+
+ public BezzadDownloader(String uri, String filePath)
+ {
+ _logger = Log.ForContext();
+
+ _uri = uri;
+ _filePath = filePath;
+
+ var settingProxyServer = Settings.Get.DownloadClient.ProxyServer;
+
+ // For all options, see https://github.com/bezzad/Downloader
+ _downloadConfiguration = new DownloadConfiguration
+ {
+ MaxTryAgainOnFailover = 5,
+ RangeDownload = false,
+ ClearPackageOnCompletionWithFailure = true,
+ ReserveStorageSpaceBeforeStartingDownload = false,
+ CheckDiskSizeBeforeDownload = false,
+ MaximumMemoryBufferBytes = 1024 * 1024 * 10,
+ RequestConfiguration =
+ {
+ Accept = "*/*",
+ UserAgent = $"rdt-client",
+ ProtocolVersion = HttpVersion.Version11,
+ AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
+ KeepAlive = true,
+ UseDefaultCredentials = false
+ }
+ };
+
+ SetSettings();
+
+ if (!String.IsNullOrWhiteSpace(settingProxyServer))
+ {
+ _downloadConfiguration.RequestConfiguration.Proxy = new WebProxy(new Uri(settingProxyServer), false);
+ }
+
+ _downloadService = new DownloadService(_downloadConfiguration);
+
+ _downloadService.DownloadProgressChanged += (_, args) =>
+ {
+ if (DownloadProgress == null)
+ {
+ return;
+ }
+
+ DownloadProgress.Invoke(this,
+ new DownloadProgressEventArgs
+ {
+ Speed = (Int64)args.BytesPerSecondSpeed,
+ BytesDone = args.ReceivedBytesSize,
+ BytesTotal = args.TotalBytesToReceive
+ });
+ };
+
+ _downloadService.DownloadFileCompleted += (_, args) =>
+ {
+ String? error = null;
+
+ if (args.Cancelled)
+ {
+ error = $"The download was cancelled";
+ }
+ else if (args.Error != null)
+ {
+ error = args.Error.Message;
+ }
+
+ DownloadComplete?.Invoke(this,
+ new DownloadCompleteEventArgs
+ {
+ Error = error
+ });
+
+ _finished = true;
+ };
+ }
+
+ public Task Download()
+ {
+ _logger.Debug($"Starting download of {_uri}, writing to path: {_filePath}");
+
+ _ = Task.Run(async () =>
+ {
+ await _downloadService.DownloadFileTaskAsync(_uri, _filePath);
+ });
+
+ _ = Task.Run(StartTimer);
+
+ return Task.FromResult(Guid.NewGuid().ToString());
+ }
+
+ public Task Cancel()
+ {
+ _logger.Debug($"Cancelling download {_uri}");
+
+ _downloadService.CancelAsync();
+
+ return Task.CompletedTask;
+ }
+
+ public Task Pause()
+ {
+ return Task.CompletedTask;
+ }
+
+ public Task Resume()
+ {
+ return Task.CompletedTask;
+ }
+
+ private void SetSettings()
+ {
+ var settingDownloadMaxSpeed = Settings.Get.DownloadClient.MaxSpeed;
+
+ if (settingDownloadMaxSpeed <= 0)
+ {
+ settingDownloadMaxSpeed = 0;
+ }
+
+ settingDownloadMaxSpeed = settingDownloadMaxSpeed * 1024 * 1024;
+
+ var settingDownloadTimeout = Settings.Get.DownloadClient.Timeout;
+
+ if (settingDownloadTimeout <= 0)
+ {
+ settingDownloadTimeout = 1000;
+ }
+
+ var settingParallelCount = Settings.Get.DownloadClient.ParallelCount;
+
+ if (settingParallelCount <= 0)
+ {
+ settingParallelCount = 4;
+ }
+
+ if (Settings.Get.DownloadClient.ChunkCount <= 0)
+ {
+ _downloadConfiguration.ChunkCount = 8;
+ }
+ else
+ {
+ _downloadConfiguration.ChunkCount = Settings.Get.DownloadClient.ChunkCount;
+ }
+
+ _downloadConfiguration.MaximumBytesPerSecond = settingDownloadMaxSpeed;
+ _downloadConfiguration.ParallelDownload = settingParallelCount > 1;
+ _downloadConfiguration.ParallelCount = settingParallelCount;
+ _downloadConfiguration.Timeout = settingDownloadTimeout;
+ }
+
+ private async Task StartTimer()
+ {
+ using var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));
+
+ while (await timer.WaitForNextTickAsync())
+ {
+ if (_finished)
+ {
+ return;
+ }
+
+ SetSettings();
+ }
+ }
+}
\ No newline at end of file
diff --git a/server/RdtClient.Service/Services/Downloaders/InternalDownloader.cs b/server/RdtClient.Service/Services/Downloaders/InternalDownloader.cs
index 592af43..108bae5 100644
--- a/server/RdtClient.Service/Services/Downloaders/InternalDownloader.cs
+++ b/server/RdtClient.Service/Services/Downloaders/InternalDownloader.cs
@@ -1,6 +1,4 @@
-using System.Net;
-using Downloader;
-using Serilog;
+using Serilog;
namespace RdtClient.Service.Services.Downloaders;
@@ -9,14 +7,16 @@ public class InternalDownloader : IDownloader
public event EventHandler? DownloadComplete;
public event EventHandler? DownloadProgress;
- private readonly DownloadService _downloadService;
- private readonly DownloadConfiguration _downloadConfiguration;
+ private readonly DownloaderNET.Downloader _downloadService;
+ private readonly DownloaderNET.Settings _downloadConfiguration;
private readonly String _filePath;
private readonly String _uri;
private readonly ILogger _logger;
+ private readonly CancellationTokenSource _cancellationToken = new();
+
private Boolean _finished;
public InternalDownloader(String uri, String filePath)
@@ -25,39 +25,16 @@ public class InternalDownloader : IDownloader
_uri = uri;
_filePath = filePath;
-
- var settingProxyServer = Settings.Get.DownloadClient.ProxyServer;
-
- // For all options, see https://github.com/bezzad/Downloader
- _downloadConfiguration = new DownloadConfiguration
- {
- MaxTryAgainOnFailover = 5,
- RangeDownload = false,
- ClearPackageOnCompletionWithFailure = true,
- ReserveStorageSpaceBeforeStartingDownload = false,
- CheckDiskSizeBeforeDownload = false,
- MaximumMemoryBufferBytes = 1024 * 1024 * 10,
- RequestConfiguration =
- {
- Accept = "*/*",
- UserAgent = $"rdt-client",
- ProtocolVersion = HttpVersion.Version11,
- AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
- KeepAlive = true,
- UseDefaultCredentials = false
- }
- };
+
+ _downloadConfiguration = new DownloaderNET.Settings();
SetSettings();
- if (!String.IsNullOrWhiteSpace(settingProxyServer))
- {
- _downloadConfiguration.RequestConfiguration.Proxy = new WebProxy(new Uri(settingProxyServer), false);
- }
+ _downloadService = new DownloaderNET.Downloader(_uri, _filePath, _downloadConfiguration);
- _downloadService = new DownloadService(_downloadConfiguration);
+ //_downloadService.OnLog += message => Debug.WriteLine(message.Message);
- _downloadService.DownloadProgressChanged += (_, args) =>
+ _downloadService.OnProgress += (chunks, _) =>
{
if (DownloadProgress == null)
{
@@ -67,54 +44,41 @@ public class InternalDownloader : IDownloader
DownloadProgress.Invoke(this,
new DownloadProgressEventArgs
{
- Speed = (Int64)args.BytesPerSecondSpeed,
- BytesDone = args.ReceivedBytesSize,
- BytesTotal = args.TotalBytesToReceive
+ Speed = (Int64)chunks.Where(m => m.IsActive).Sum(m => m.Speed),
+ BytesDone = chunks.Sum(m => m.DownloadBytes),
+ BytesTotal = chunks.Sum(m => m.LengthBytes)
});
};
- _downloadService.DownloadFileCompleted += (_, args) =>
+ _downloadService.OnComplete += (_, error) =>
{
- String? error = null;
-
- if (args.Cancelled)
- {
- error = $"The download was cancelled";
- }
- else if (args.Error != null)
- {
- error = args.Error.Message;
- }
-
DownloadComplete?.Invoke(this,
new DownloadCompleteEventArgs
{
- Error = error
+ Error = error?.Message
});
_finished = true;
+
+ return Task.CompletedTask;
};
}
- public Task Download()
+ public async Task Download()
{
_logger.Debug($"Starting download of {_uri}, writing to path: {_filePath}");
- _ = Task.Run(async () =>
- {
- await _downloadService.DownloadFileTaskAsync(_uri, _filePath);
- });
-
+ await _downloadService.Download(_cancellationToken.Token);
_ = Task.Run(StartTimer);
- return Task.FromResult(Guid.NewGuid().ToString());
+ return Guid.NewGuid().ToString();
}
public Task Cancel()
{
_logger.Debug($"Cancelling download {_uri}");
- _downloadService.CancelAsync();
+ _cancellationToken.Cancel(false);
return Task.CompletedTask;
}
@@ -131,6 +95,13 @@ public class InternalDownloader : IDownloader
private void SetSettings()
{
+ var settingDownloadParallelCount = Settings.Get.DownloadClient.ParallelCount;
+
+ if (settingDownloadParallelCount <= 0)
+ {
+ settingDownloadParallelCount = 1;
+ }
+
var settingDownloadMaxSpeed = Settings.Get.DownloadClient.MaxSpeed;
if (settingDownloadMaxSpeed <= 0)
@@ -146,27 +117,11 @@ public class InternalDownloader : IDownloader
{
settingDownloadTimeout = 1000;
}
-
- var settingParallelCount = Settings.Get.DownloadClient.ParallelCount;
-
- if (settingParallelCount <= 0)
- {
- settingParallelCount = 4;
- }
-
- if (Settings.Get.DownloadClient.ChunkCount <= 0)
- {
- _downloadConfiguration.ChunkCount = 8;
- }
- else
- {
- _downloadConfiguration.ChunkCount = Settings.Get.DownloadClient.ChunkCount;
- }
+ _downloadConfiguration.Parallel = settingDownloadParallelCount;
_downloadConfiguration.MaximumBytesPerSecond = settingDownloadMaxSpeed;
- _downloadConfiguration.ParallelDownload = settingParallelCount > 1;
- _downloadConfiguration.ParallelCount = settingParallelCount;
_downloadConfiguration.Timeout = settingDownloadTimeout;
+ _downloadConfiguration.RetryCount = 5;
}
private async Task StartTimer()