diff --git a/client/src/app/navbar/settings/settings.component.html b/client/src/app/navbar/settings/settings.component.html
index 7694977..875831a 100644
--- a/client/src/app/navbar/settings/settings.component.html
+++ b/client/src/app/navbar/settings/settings.component.html
@@ -64,22 +64,84 @@
-
0">Error saving settings: {{ error }}
+
+
+
+
+
+ Could not test your download path
+ {{ testPathError }}
+
-
0">
- Could not test your download path
- {{ testPathError }}
+
Your download path looks good!
+
+
This will check if the download folder has write permissions.
-
Successfully tested your download path
+
+
+
+
+
+ Could not test your download speed
+ {{ testDownloadSpeedError }}
+
+
+
+ Download speed {{ testDownloadSpeedSuccess | filesize }}/s
+
+
+
+ This will download a 10GB test file from RealDebrid. Hit cancel when you have seen enough.
+
+
+
+
+
+
+
+
+ Could not test your download speed
+ {{ testWriteSpeedError }}
+
+
+
+ Write speed {{ testWriteSpeedSuccess | filesize }}/s
+
+
+
This will write a small file to your download folder to see how fast it can write to it.
+
+
+
0">Error saving settings: {{ error }}
diff --git a/client/src/app/navbar/settings/settings.component.ts b/client/src/app/navbar/settings/settings.component.ts
index 355b9a5..dbd7efd 100644
--- a/client/src/app/navbar/settings/settings.component.ts
+++ b/client/src/app/navbar/settings/settings.component.ts
@@ -25,9 +25,16 @@ export class SettingsComponent implements OnInit {
public saving = false;
public error: string;
+
public testPathError: string;
public testPathSuccess: boolean;
+ public testDownloadSpeedError: string;
+ public testDownloadSpeedSuccess: number;
+
+ public testWriteSpeedError: string;
+ public testWriteSpeedSuccess: number;
+
public settingRealDebridApiKey: string;
public settingDownloadPath: string;
public settingMappedPath: string;
@@ -100,7 +107,7 @@ export class SettingsComponent implements OnInit {
);
}
- public test(): void {
+ public testDownloadPath(): void {
this.saving = true;
this.testPathError = null;
this.testPathSuccess = false;
@@ -111,13 +118,45 @@ export class SettingsComponent implements OnInit {
this.testPathSuccess = true;
},
(err) => {
- console.log(err);
this.testPathError = err.error;
this.saving = false;
}
);
}
+ public testDownloadSpeed(): void {
+ this.saving = true;
+ this.testDownloadSpeedError = null;
+ this.testDownloadSpeedSuccess = 0;
+
+ this.settingsService.testDownloadSpeed().subscribe(
+ (result) => {
+ this.saving = false;
+ this.testDownloadSpeedSuccess = result;
+ },
+ (err) => {
+ this.testDownloadSpeedError = err.error;
+ this.saving = false;
+ }
+ );
+ }
+ public testWriteSpeed(): void {
+ this.saving = true;
+ this.testWriteSpeedError = null;
+ this.testWriteSpeedSuccess = 0;
+
+ this.settingsService.testWriteSpeed().subscribe(
+ (result) => {
+ this.saving = false;
+ this.testWriteSpeedSuccess = result;
+ },
+ (err) => {
+ this.testWriteSpeedError = err.error;
+ this.saving = false;
+ }
+ );
+ }
+
public cancel(): void {
this.isActive = false;
this.openChange.emit(this.open);
diff --git a/client/src/app/settings.service.ts b/client/src/app/settings.service.ts
index 628ca18..4824d16 100644
--- a/client/src/app/settings.service.ts
+++ b/client/src/app/settings.service.ts
@@ -25,4 +25,12 @@ export class SettingsService {
public testPath(path: string): Observable {
return this.http.post(`/Api/Settings/TestPath`, { path });
}
+
+ public testDownloadSpeed(): Observable {
+ return this.http.get(`/Api/Settings/TestDownloadSpeed`);
+ }
+
+ public testWriteSpeed(): Observable {
+ return this.http.get(`/Api/Settings/TestWriteSpeed`);
+ }
}
diff --git a/server/RdtClient.Data/RdtClient.Data.csproj b/server/RdtClient.Data/RdtClient.Data.csproj
index 00e4e11..c07459f 100644
--- a/server/RdtClient.Data/RdtClient.Data.csproj
+++ b/server/RdtClient.Data/RdtClient.Data.csproj
@@ -5,11 +5,11 @@
-
-
-
-
-
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/server/RdtClient.Service/RdtClient.Service.csproj b/server/RdtClient.Service/RdtClient.Service.csproj
index 73f4056..3fc9781 100644
--- a/server/RdtClient.Service/RdtClient.Service.csproj
+++ b/server/RdtClient.Service/RdtClient.Service.csproj
@@ -12,10 +12,11 @@
-
+
+
diff --git a/server/RdtClient.Service/Services/DownloadClient.cs b/server/RdtClient.Service/Services/DownloadClient.cs
index 74e9dc5..b5e3dd1 100644
--- a/server/RdtClient.Service/Services/DownloadClient.cs
+++ b/server/RdtClient.Service/Services/DownloadClient.cs
@@ -1,30 +1,22 @@
using System;
+using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
+using Downloader;
using RdtClient.Data.Models.Data;
namespace RdtClient.Service.Services
{
public class DownloadClient
{
- public Boolean Finished { get; private set; }
-
- public String Error { get; private set; }
-
- public Int64 Speed { get; private set; }
- public Int64 BytesTotal { get; private set; }
- public Int64 BytesDone { get; private set; }
+ private readonly String _destinationPath;
private readonly Download _download;
- private readonly String _destinationPath;
private readonly Torrent _torrent;
- private Boolean _cancelled = false;
-
- private Int64 _bytesLastUpdate;
- private DateTime _nextUpdate;
+ private DownloadService _downloader;
public DownloadClient(Download download, String destinationPath)
{
@@ -33,7 +25,18 @@ namespace RdtClient.Service.Services
_torrent = download.Torrent;
}
- public async Task Start()
+ public Boolean Finished { get; private set; }
+
+ public String Error { get; private set; }
+
+ public Int64 Speed { get; private set; }
+ public Int64 BytesTotal { get; private set; }
+ public Int64 BytesDone { get; private set; }
+
+ private static Int64 LastTick { get; set; }
+ private static ConcurrentBag AverageSpeed { get; } = new ConcurrentBag();
+
+ public async Task Start(Boolean writeToMemory)
{
BytesDone = 0;
BytesTotal = 0;
@@ -58,7 +61,7 @@ namespace RdtClient.Service.Services
var fileName = uri.Segments.Last();
var filePath = Path.Combine(torrentPath, fileName);
-
+
if (File.Exists(filePath))
{
File.Delete(filePath);
@@ -66,10 +69,7 @@ namespace RdtClient.Service.Services
await Task.Factory.StartNew(async delegate
{
- if (!_cancelled)
- {
- await Download(uri, filePath);
- }
+ await Download(filePath, writeToMemory);
});
}
catch (Exception ex)
@@ -81,98 +81,80 @@ namespace RdtClient.Service.Services
public void Cancel()
{
- _cancelled = true;
+ _downloader?.CancelAsync();
}
- private async Task Download(Uri uri, String filePath)
+ private async Task Download(String filePath, Boolean writeToMemory)
{
try
{
- _bytesLastUpdate = 0;
- _nextUpdate = DateTime.UtcNow.AddSeconds(1);
+ LastTick = 0;
- // Determine the file size
- var webRequest = WebRequest.Create(uri);
- webRequest.Method = "HEAD";
- webRequest.Timeout = 5000;
- Int64 responseLength;
-
- using (var webResponse = await webRequest.GetResponseAsync())
+ var downloadOpt = new DownloadConfiguration
{
- responseLength = Int64.Parse(webResponse.Headers.Get("Content-Length"));
- }
+ MaxTryAgainOnFailover = Int32.MaxValue,
+ ParallelDownload = true,
+ ChunkCount = 8,
+ Timeout = 100,
+ OnTheFlyDownload = writeToMemory,
+ BufferBlockSize = 1024 * 8,
+ MaximumBytesPerSecond = 100 * 1024 * 1024,
+ TempDirectory = @"C:\temp",
+ RequestConfiguration =
+ {
+ Accept = "*/*",
+ UserAgent = $"rdt-client",
+ ProtocolVersion = HttpVersion.Version11,
+ AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
+ KeepAlive = true,
+ UseDefaultCredentials = false
+ }
+ };
- var timeout = DateTimeOffset.UtcNow.AddHours(1);
+ _downloader = new DownloadService(downloadOpt);
- while (timeout > DateTimeOffset.UtcNow && !_cancelled)
+ _downloader.DownloadProgressChanged += (_, args) =>
{
- try
+ var isElapsedTimeMoreThanOneSecond = Environment.TickCount - LastTick >= 1000;
+
+ if (isElapsedTimeMoreThanOneSecond)
{
- var request = WebRequest.Create(uri);
- using var response = await request.GetResponseAsync();
-
- await using var stream = response.GetResponseStream();
-
- if (stream == null)
- {
- throw new IOException("No stream");
- }
-
- await using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write);
- var buffer = new Byte[64 * 1024];
-
- while (fileStream.Length < response.ContentLength && !_cancelled)
- {
- var read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length));
-
- if (read > 0)
- {
- fileStream.Write(buffer, 0, read);
-
- BytesDone = fileStream.Length;
- BytesTotal = responseLength;
-
- if (DateTime.UtcNow > _nextUpdate)
- {
- Speed = fileStream.Length - _bytesLastUpdate;
-
- _nextUpdate = DateTime.UtcNow.AddSeconds(1);
- _bytesLastUpdate = fileStream.Length;
-
- timeout = DateTimeOffset.UtcNow.AddHours(1);
- }
- }
- else
- {
- break;
- }
- }
-
- break;
+ AverageSpeed.Add(args.BytesPerSecondSpeed);
+ LastTick = Environment.TickCount;
}
- catch (IOException)
- {
- await Task.Delay(1000);
- }
- catch (WebException)
- {
- await Task.Delay(1000);
- }
- }
- if (timeout <= DateTimeOffset.UtcNow)
+ Speed = (Int64) AverageSpeed.Average();
+ BytesDone = args.BytesReceived;
+ BytesTotal = args.TotalBytesToReceive;
+ };
+
+ _downloader.DownloadStarted += (_, args) =>
{
- throw new Exception($"Download timed out");
- }
+ AverageSpeed?.Clear();
+ Speed = 0;
+ BytesDone = 0;
+ BytesTotal = args.TotalBytesToReceive;
+ };
- Speed = 0;
+ _downloader.DownloadFileCompleted += (_, args) =>
+ {
+ if (args.Cancelled)
+ {
+ Error = $"The download was cancelled";
+ }
+ else if (args.Error != null)
+ {
+ Error = args.Error.Message;
+ }
+
+ Finished = true;
+ };
+
+ await _downloader.DownloadFileAsync(_download.Link, filePath);
}
catch (Exception ex)
{
Error = $"An unexpected error occurred downloading {_download.Link} for torrent {_torrent.RdName}: {ex.Message}";
- }
- finally
- {
Finished = true;
}
}
diff --git a/server/RdtClient.Service/Services/Settings.cs b/server/RdtClient.Service/Services/Settings.cs
index bd562f3..9606a99 100644
--- a/server/RdtClient.Service/Services/Settings.cs
+++ b/server/RdtClient.Service/Services/Settings.cs
@@ -1,8 +1,9 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
-using System.Net;
+using System.Threading;
using System.Threading.Tasks;
using RdtClient.Data.Data;
using RdtClient.Data.Models.Data;
@@ -16,8 +17,8 @@ namespace RdtClient.Service.Services
Task GetString(String key);
Task GetNumber(String key);
Task TestPath(String path);
- Task TestDownloadSpeed();
- Task TestWriteSpeed(String path);
+ Task TestDownloadSpeed(CancellationToken cancellationToken);
+ Task TestWriteSpeed();
}
public class Settings : ISettings
@@ -29,6 +30,9 @@ namespace RdtClient.Service.Services
_settingData = settingData;
}
+ private static Int64 LastTick { get; set; }
+ private static ConcurrentBag AverageSpeed { get; } = new ConcurrentBag();
+
public async Task> GetAll()
{
return await _settingData.GetAll();
@@ -83,78 +87,93 @@ namespace RdtClient.Service.Services
File.Delete(testFile);
}
- public async Task TestDownloadSpeed()
+ public async Task TestDownloadSpeed(CancellationToken cancellationToken)
{
- var watch = new Stopwatch();
+ var downloadPath = await GetString("DownloadPath");
- var request = WebRequest.Create(new Uri("https://34.download.real-debrid.com/speedtest/testDefault.rar/" + DateTime.Now.Ticks));
-
- watch.Start();
-
- using var response = await request.GetResponseAsync();
-
- await using var stream = response.GetResponseStream();
-
- if (stream == null)
- {
- throw new IOException("No stream");
- }
-
- var buffer = new Byte[64 * 1024];
- Int64 totalRead = 0;
-
- while (totalRead < response.ContentLength)
- {
- var read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length));
-
- if (read > 0)
- {
- totalRead += read;
- }
- else
- {
- break;
- }
- }
-
- watch.Stop();
-
- var downloadSpeed = totalRead / watch.Elapsed.TotalSeconds;
-
- return downloadSpeed;
- }
-
- public async Task TestWriteSpeed(String path)
- {
- var testFilePath = Path.Combine(path, "test.tmp");
-
- const Int32 testFileSize = 1024 * 1024 * 1024;
-
- var watch = new Stopwatch();
+ var testFilePath = Path.Combine(downloadPath, "testDefault.rar");
if (File.Exists(testFilePath))
{
File.Delete(testFilePath);
}
-
+
+ var download = new Download
+ {
+ Link = "https://34.download.real-debrid.com/speedtest/testDefault.rar",
+ Torrent = new Torrent
+ {
+ RdName = ""
+ }
+ };
+
+ var downloadClient = new DownloadClient(download, downloadPath);
+
+ await downloadClient.Start(true);
+
+ while (!downloadClient.Finished)
+ {
+ await Task.Delay(1000);
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ downloadClient.Cancel();
+ }
+
+ if (downloadClient.BytesDone > 1024 * 1024 * 100)
+ {
+ downloadClient.Cancel();
+ }
+ }
+
+ if (File.Exists(testFilePath))
+ {
+ File.Delete(testFilePath);
+ }
+
+ return downloadClient.Speed;
+ }
+
+ public async Task TestWriteSpeed()
+ {
+ var downloadPath = await GetString("DownloadPath");
+
+ var testFilePath = Path.Combine(downloadPath, "test.tmp");
+
+ if (File.Exists(testFilePath))
+ {
+ File.Delete(testFilePath);
+ }
+
+ const Int32 testFileSize = 64 * 1024 * 1024;
+
+ var watch = new Stopwatch();
+
watch.Start();
var rnd = new Random();
-
+
await using var fileStream = new FileStream(testFilePath, FileMode.Create, FileAccess.Write, FileShare.Write);
var buffer = new Byte[64 * 1024];
-
+
while (fileStream.Length < testFileSize)
{
rnd.NextBytes(buffer);
fileStream.Write(buffer, 0, buffer.Length);
}
-
+
watch.Stop();
var writeSpeed = fileStream.Length / watch.Elapsed.TotalSeconds;
+
+ fileStream.Close();
+
+ if (File.Exists(testFilePath))
+ {
+ File.Delete(testFilePath);
+ }
return writeSpeed;
}
diff --git a/server/RdtClient.Service/Services/TorrentRunner.cs b/server/RdtClient.Service/Services/TorrentRunner.cs
index 53e76c0..6478028 100644
--- a/server/RdtClient.Service/Services/TorrentRunner.cs
+++ b/server/RdtClient.Service/Services/TorrentRunner.cs
@@ -172,7 +172,7 @@ namespace RdtClient.Service.Services
if (TorrentRunner.ActiveDownloadClients.TryAdd(download.DownloadId, downloadClient))
{
- await downloadClient.Start();
+ await downloadClient.Start(false);
}
}
diff --git a/server/RdtClient.Web/Controllers/SettingsController.cs b/server/RdtClient.Web/Controllers/SettingsController.cs
index 83065c7..b4ae12e 100644
--- a/server/RdtClient.Web/Controllers/SettingsController.cs
+++ b/server/RdtClient.Web/Controllers/SettingsController.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -58,24 +59,21 @@ namespace RdtClient.Web.Controllers
}
[HttpGet]
- [Route("TestSpeed")]
- public async Task TestSpeed()
+ [Route("TestDownloadSpeed")]
+ public async Task TestDownloadSpeed(CancellationToken cancellationToken)
{
- var downloadPath = await _settings.GetString("DownloadPath");
- var mappedPath = await _settings.GetString("MappedPath");
+ var downloadSpeed = await _settings.TestDownloadSpeed(cancellationToken);
+
+ return Ok(downloadSpeed);
+ }
+
+ [HttpGet]
+ [Route("TestWriteSpeed")]
+ public async Task TestWriteSpeed()
+ {
+ var writeSpeed = await _settings.TestWriteSpeed();
- var downloadSpeed = await _settings.TestDownloadSpeed();
- var containerWriteSpeed = await _settings.TestWriteSpeed(downloadPath);
- var remoteWriteSpeed = await _settings.TestWriteSpeed(mappedPath);
-
- var result = new
- {
- downloadSpeed,
- containerWriteSpeed,
- remoteWriteSpeed
- };
-
- return Ok(result);
+ return Ok(writeSpeed);
}
}
diff --git a/server/RdtClient.Web/RdtClient.Web.csproj b/server/RdtClient.Web/RdtClient.Web.csproj
index 5334df7..ad95c5e 100644
--- a/server/RdtClient.Web/RdtClient.Web.csproj
+++ b/server/RdtClient.Web/RdtClient.Web.csproj
@@ -7,16 +7,16 @@
-
-
-
-
-
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/server/RdtClient.sln b/server/RdtClient.sln
index 6afaf9e..a408420 100644
--- a/server/RdtClient.sln
+++ b/server/RdtClient.sln
@@ -5,9 +5,11 @@ VisualStudioVersion = 16.0.29911.84
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RdtClient.Web", "RdtClient.Web\RdtClient.Web.csproj", "{A8C7F095-89C6-4CD1-AFB2-27106F470D62}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RdtClient.Service", "RdtClient.Service\RdtClient.Service.csproj", "{06D27710-AE9A-4FA2-B043-1A4303561716}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RdtClient.Service", "RdtClient.Service\RdtClient.Service.csproj", "{06D27710-AE9A-4FA2-B043-1A4303561716}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RdtClient.Data", "RdtClient.Data\RdtClient.Data.csproj", "{92EF8817-AD73-4301-93BD-745D7D61DD74}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RdtClient.Data", "RdtClient.Data\RdtClient.Data.csproj", "{92EF8817-AD73-4301-93BD-745D7D61DD74}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Downloader", "Downloader\Downloader.csproj", "{48FF995D-AD14-4E41-BC69-7A144C84E3B6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -27,6 +29,10 @@ Global
{92EF8817-AD73-4301-93BD-745D7D61DD74}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92EF8817-AD73-4301-93BD-745D7D61DD74}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92EF8817-AD73-4301-93BD-745D7D61DD74}.Release|Any CPU.Build.0 = Release|Any CPU
+ {48FF995D-AD14-4E41-BC69-7A144C84E3B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {48FF995D-AD14-4E41-BC69-7A144C84E3B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {48FF995D-AD14-4E41-BC69-7A144C84E3B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {48FF995D-AD14-4E41-BC69-7A144C84E3B6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/server/RdtClient.sln.DotSettings b/server/RdtClient.sln.DotSettings
index 671f10a..8f514b9 100644
--- a/server/RdtClient.sln.DotSettings
+++ b/server/RdtClient.sln.DotSettings
@@ -1,5 +1,6 @@
False
+ ExplicitlyExcluded
ExplicitlyExcluded
*.appxmanifest
*.d.ts