From 6c47ebcfad90e9f5a57a47afd73151e86978d591 Mon Sep 17 00:00:00 2001 From: Roger Far Date: Sun, 21 Jan 2024 16:48:44 -0700 Subject: [PATCH] Added the previous downloader back in, keep the Bezzad downloader in as a secondary. --- README.md | 12 +- server/RdtClient.Data/Enums/DownloadClient.cs | 3 + .../RdtClient.Service.csproj | 1 + .../Services/DownloadClient.cs | 1 + .../Services/Downloaders/BezzadDownloader.cs | 186 ++++++++++++++++++ .../Downloaders/InternalDownloader.cs | 105 +++------- 6 files changed, 232 insertions(+), 76 deletions(-) create mode 100644 server/RdtClient.Service/Services/Downloaders/BezzadDownloader.cs 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()