Use a new Downloader from https://github.com/bezzad/Downloader, add a write and download speed test to the settings interface.
This commit is contained in:
parent
49071a7f48
commit
19e5e5eb10
12 changed files with 301 additions and 185 deletions
|
|
@ -64,22 +64,84 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="notification is-danger is-light" *ngIf="error?.length > 0">Error saving settings: {{ error }}</div>
|
<div class="field">
|
||||||
|
<label class="label">Test download path permissions</label>
|
||||||
|
<div class="control">
|
||||||
|
<button
|
||||||
|
class="button is-warning"
|
||||||
|
(click)="testDownloadPath()"
|
||||||
|
[disabled]="saving"
|
||||||
|
[ngClass]="{ 'is-loading': saving }"
|
||||||
|
*ngIf="!testPathError && !testPathSuccess"
|
||||||
|
>
|
||||||
|
Test permissions
|
||||||
|
</button>
|
||||||
|
<div class="notification is-danger is-light" *ngIf="testPathError">
|
||||||
|
Could not test your download path<br />
|
||||||
|
{{ testPathError }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="notification is-danger is-light" *ngIf="testPathError?.length > 0">
|
<div class="notification is-success is-light" *ngIf="testPathSuccess">Your download path looks good!</div>
|
||||||
Could not test your download path<br />
|
</div>
|
||||||
{{ testPathError }}
|
<div class="help">This will check if the download folder has write permissions.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="notification is-success is-light" *ngIf="testPathSuccess">Successfully tested your download path</div>
|
<div class="field">
|
||||||
|
<label class="label">Test RealDebrid download speed</label>
|
||||||
|
<div class="control">
|
||||||
|
<button
|
||||||
|
class="button is-warning"
|
||||||
|
(click)="testDownloadSpeed()"
|
||||||
|
[disabled]="saving"
|
||||||
|
[ngClass]="{ 'is-loading': saving }"
|
||||||
|
*ngIf="!testDownloadSpeedError && !testDownloadSpeedSuccess"
|
||||||
|
>
|
||||||
|
Test download speed
|
||||||
|
</button>
|
||||||
|
<div class="notification is-danger is-light" *ngIf="testDownloadSpeedError">
|
||||||
|
Could not test your download speed<br />
|
||||||
|
{{ testDownloadSpeedError }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="notification is-success is-light" *ngIf="testDownloadSpeedSuccess">
|
||||||
|
Download speed {{ testDownloadSpeedSuccess | filesize }}/s
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="help">
|
||||||
|
This will download a 10GB test file from RealDebrid. Hit cancel when you have seen enough.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Test download folder write speed</label>
|
||||||
|
<div class="control">
|
||||||
|
<button
|
||||||
|
class="button is-warning"
|
||||||
|
(click)="testWriteSpeed()"
|
||||||
|
[disabled]="saving"
|
||||||
|
[ngClass]="{ 'is-loading': saving }"
|
||||||
|
*ngIf="!testWriteSpeedError && !testWriteSpeedSuccess"
|
||||||
|
>
|
||||||
|
Test write speed
|
||||||
|
</button>
|
||||||
|
<div class="notification is-danger is-light" *ngIf="testWriteSpeedError">
|
||||||
|
Could not test your download speed<br />
|
||||||
|
{{ testWriteSpeedError }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="notification is-success is-light" *ngIf="testWriteSpeedSuccess">
|
||||||
|
Write speed {{ testWriteSpeedSuccess | filesize }}/s
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="help">This will write a small file to your download folder to see how fast it can write to it.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="notification is-danger is-light" *ngIf="error?.length > 0">Error saving settings: {{ error }}</div>
|
||||||
</section>
|
</section>
|
||||||
<footer class="modal-card-foot">
|
<footer class="modal-card-foot">
|
||||||
<button class="button is-success" (click)="ok()" [disabled]="saving" [ngClass]="{ 'is-loading': saving }">
|
<button class="button is-success" (click)="ok()" [disabled]="saving" [ngClass]="{ 'is-loading': saving }">
|
||||||
Save Settings
|
Save Settings
|
||||||
</button>
|
</button>
|
||||||
<button class="button is-warning" (click)="test()" [disabled]="saving" [ngClass]="{ 'is-loading': saving }">
|
|
||||||
Test permissions
|
|
||||||
</button>
|
|
||||||
<button class="button" (click)="cancel()" [disabled]="saving" [ngClass]="{ 'is-loading': saving }">Cancel</button>
|
<button class="button" (click)="cancel()" [disabled]="saving" [ngClass]="{ 'is-loading': saving }">Cancel</button>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,16 @@ export class SettingsComponent implements OnInit {
|
||||||
|
|
||||||
public saving = false;
|
public saving = false;
|
||||||
public error: string;
|
public error: string;
|
||||||
|
|
||||||
public testPathError: string;
|
public testPathError: string;
|
||||||
public testPathSuccess: boolean;
|
public testPathSuccess: boolean;
|
||||||
|
|
||||||
|
public testDownloadSpeedError: string;
|
||||||
|
public testDownloadSpeedSuccess: number;
|
||||||
|
|
||||||
|
public testWriteSpeedError: string;
|
||||||
|
public testWriteSpeedSuccess: number;
|
||||||
|
|
||||||
public settingRealDebridApiKey: string;
|
public settingRealDebridApiKey: string;
|
||||||
public settingDownloadPath: string;
|
public settingDownloadPath: string;
|
||||||
public settingMappedPath: string;
|
public settingMappedPath: string;
|
||||||
|
|
@ -100,7 +107,7 @@ export class SettingsComponent implements OnInit {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public test(): void {
|
public testDownloadPath(): void {
|
||||||
this.saving = true;
|
this.saving = true;
|
||||||
this.testPathError = null;
|
this.testPathError = null;
|
||||||
this.testPathSuccess = false;
|
this.testPathSuccess = false;
|
||||||
|
|
@ -111,13 +118,45 @@ export class SettingsComponent implements OnInit {
|
||||||
this.testPathSuccess = true;
|
this.testPathSuccess = true;
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
console.log(err);
|
|
||||||
this.testPathError = err.error;
|
this.testPathError = err.error;
|
||||||
this.saving = false;
|
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 {
|
public cancel(): void {
|
||||||
this.isActive = false;
|
this.isActive = false;
|
||||||
this.openChange.emit(this.open);
|
this.openChange.emit(this.open);
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,12 @@ export class SettingsService {
|
||||||
public testPath(path: string): Observable<void> {
|
public testPath(path: string): Observable<void> {
|
||||||
return this.http.post<void>(`/Api/Settings/TestPath`, { path });
|
return this.http.post<void>(`/Api/Settings/TestPath`, { path });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public testDownloadSpeed(): Observable<number> {
|
||||||
|
return this.http.get<number>(`/Api/Settings/TestDownloadSpeed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public testWriteSpeed(): Observable<number> {
|
||||||
|
return this.http.get<number>(`/Api/Settings/TestWriteSpeed`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.2" />
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.1" />
|
<PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.1">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.2">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,11 @@
|
||||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||||
<PackageReference Include="Serilog.Exceptions" Version="6.0.0" />
|
<PackageReference Include="Serilog.Exceptions" Version="6.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
<PackageReference Include="SharpCompress" Version="0.27.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Downloader\Downloader.csproj" />
|
||||||
<ProjectReference Include="..\RdtClient.Data\RdtClient.Data.csproj" />
|
<ProjectReference Include="..\RdtClient.Data\RdtClient.Data.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,22 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Downloader;
|
||||||
using RdtClient.Data.Models.Data;
|
using RdtClient.Data.Models.Data;
|
||||||
|
|
||||||
namespace RdtClient.Service.Services
|
namespace RdtClient.Service.Services
|
||||||
{
|
{
|
||||||
public class DownloadClient
|
public class DownloadClient
|
||||||
{
|
{
|
||||||
public Boolean Finished { get; private set; }
|
private readonly String _destinationPath;
|
||||||
|
|
||||||
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 Download _download;
|
private readonly Download _download;
|
||||||
private readonly String _destinationPath;
|
|
||||||
private readonly Torrent _torrent;
|
private readonly Torrent _torrent;
|
||||||
|
|
||||||
private Boolean _cancelled = false;
|
private DownloadService _downloader;
|
||||||
|
|
||||||
private Int64 _bytesLastUpdate;
|
|
||||||
private DateTime _nextUpdate;
|
|
||||||
|
|
||||||
public DownloadClient(Download download, String destinationPath)
|
public DownloadClient(Download download, String destinationPath)
|
||||||
{
|
{
|
||||||
|
|
@ -33,7 +25,18 @@ namespace RdtClient.Service.Services
|
||||||
_torrent = download.Torrent;
|
_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<Int64> AverageSpeed { get; } = new ConcurrentBag<Int64>();
|
||||||
|
|
||||||
|
public async Task Start(Boolean writeToMemory)
|
||||||
{
|
{
|
||||||
BytesDone = 0;
|
BytesDone = 0;
|
||||||
BytesTotal = 0;
|
BytesTotal = 0;
|
||||||
|
|
@ -66,10 +69,7 @@ namespace RdtClient.Service.Services
|
||||||
|
|
||||||
await Task.Factory.StartNew(async delegate
|
await Task.Factory.StartNew(async delegate
|
||||||
{
|
{
|
||||||
if (!_cancelled)
|
await Download(filePath, writeToMemory);
|
||||||
{
|
|
||||||
await Download(uri, filePath);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -81,98 +81,80 @@ namespace RdtClient.Service.Services
|
||||||
|
|
||||||
public void Cancel()
|
public void Cancel()
|
||||||
{
|
{
|
||||||
_cancelled = true;
|
_downloader?.CancelAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Download(Uri uri, String filePath)
|
private async Task Download(String filePath, Boolean writeToMemory)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_bytesLastUpdate = 0;
|
LastTick = 0;
|
||||||
_nextUpdate = DateTime.UtcNow.AddSeconds(1);
|
|
||||||
|
|
||||||
// Determine the file size
|
var downloadOpt = new DownloadConfiguration
|
||||||
var webRequest = WebRequest.Create(uri);
|
|
||||||
webRequest.Method = "HEAD";
|
|
||||||
webRequest.Timeout = 5000;
|
|
||||||
Int64 responseLength;
|
|
||||||
|
|
||||||
using (var webResponse = await webRequest.GetResponseAsync())
|
|
||||||
{
|
{
|
||||||
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);
|
AverageSpeed.Add(args.BytesPerSecondSpeed);
|
||||||
using var response = await request.GetResponseAsync();
|
LastTick = Environment.TickCount;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Error = $"An unexpected error occurred downloading {_download.Link} for torrent {_torrent.RdName}: {ex.Message}";
|
Error = $"An unexpected error occurred downloading {_download.Link} for torrent {_torrent.RdName}: {ex.Message}";
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Finished = true;
|
Finished = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using RdtClient.Data.Data;
|
using RdtClient.Data.Data;
|
||||||
using RdtClient.Data.Models.Data;
|
using RdtClient.Data.Models.Data;
|
||||||
|
|
@ -16,8 +17,8 @@ namespace RdtClient.Service.Services
|
||||||
Task<String> GetString(String key);
|
Task<String> GetString(String key);
|
||||||
Task<Int32> GetNumber(String key);
|
Task<Int32> GetNumber(String key);
|
||||||
Task TestPath(String path);
|
Task TestPath(String path);
|
||||||
Task<Double> TestDownloadSpeed();
|
Task<Double> TestDownloadSpeed(CancellationToken cancellationToken);
|
||||||
Task<Double> TestWriteSpeed(String path);
|
Task<Double> TestWriteSpeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Settings : ISettings
|
public class Settings : ISettings
|
||||||
|
|
@ -29,6 +30,9 @@ namespace RdtClient.Service.Services
|
||||||
_settingData = settingData;
|
_settingData = settingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Int64 LastTick { get; set; }
|
||||||
|
private static ConcurrentBag<Int64> AverageSpeed { get; } = new ConcurrentBag<Int64>();
|
||||||
|
|
||||||
public async Task<IList<Setting>> GetAll()
|
public async Task<IList<Setting>> GetAll()
|
||||||
{
|
{
|
||||||
return await _settingData.GetAll();
|
return await _settingData.GetAll();
|
||||||
|
|
@ -83,60 +87,68 @@ namespace RdtClient.Service.Services
|
||||||
File.Delete(testFile);
|
File.Delete(testFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Double> TestDownloadSpeed()
|
public async Task<Double> 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));
|
var testFilePath = Path.Combine(downloadPath, "testDefault.rar");
|
||||||
|
|
||||||
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<Double> TestWriteSpeed(String path)
|
|
||||||
{
|
|
||||||
var testFilePath = Path.Combine(path, "test.tmp");
|
|
||||||
|
|
||||||
const Int32 testFileSize = 1024 * 1024 * 1024;
|
|
||||||
|
|
||||||
var watch = new Stopwatch();
|
|
||||||
|
|
||||||
if (File.Exists(testFilePath))
|
if (File.Exists(testFilePath))
|
||||||
{
|
{
|
||||||
File.Delete(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<Double> 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();
|
watch.Start();
|
||||||
|
|
||||||
var rnd = new Random();
|
var rnd = new Random();
|
||||||
|
|
@ -156,6 +168,13 @@ namespace RdtClient.Service.Services
|
||||||
|
|
||||||
var writeSpeed = fileStream.Length / watch.Elapsed.TotalSeconds;
|
var writeSpeed = fileStream.Length / watch.Elapsed.TotalSeconds;
|
||||||
|
|
||||||
|
fileStream.Close();
|
||||||
|
|
||||||
|
if (File.Exists(testFilePath))
|
||||||
|
{
|
||||||
|
File.Delete(testFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
return writeSpeed;
|
return writeSpeed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ namespace RdtClient.Service.Services
|
||||||
|
|
||||||
if (TorrentRunner.ActiveDownloadClients.TryAdd(download.DownloadId, downloadClient))
|
if (TorrentRunner.ActiveDownloadClients.TryAdd(download.DownloadId, downloadClient))
|
||||||
{
|
{
|
||||||
await downloadClient.Start();
|
await downloadClient.Start(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
@ -58,24 +59,21 @@ namespace RdtClient.Web.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("TestSpeed")]
|
[Route("TestDownloadSpeed")]
|
||||||
public async Task<ActionResult> TestSpeed()
|
public async Task<ActionResult> TestDownloadSpeed(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var downloadPath = await _settings.GetString("DownloadPath");
|
var downloadSpeed = await _settings.TestDownloadSpeed(cancellationToken);
|
||||||
var mappedPath = await _settings.GetString("MappedPath");
|
|
||||||
|
|
||||||
var downloadSpeed = await _settings.TestDownloadSpeed();
|
return Ok(downloadSpeed);
|
||||||
var containerWriteSpeed = await _settings.TestWriteSpeed(downloadPath);
|
}
|
||||||
var remoteWriteSpeed = await _settings.TestWriteSpeed(mappedPath);
|
|
||||||
|
|
||||||
var result = new
|
[HttpGet]
|
||||||
{
|
[Route("TestWriteSpeed")]
|
||||||
downloadSpeed,
|
public async Task<ActionResult> TestWriteSpeed()
|
||||||
containerWriteSpeed,
|
{
|
||||||
remoteWriteSpeed
|
var writeSpeed = await _settings.TestWriteSpeed();
|
||||||
};
|
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(writeSpeed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,16 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.2" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="5.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="5.0.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.1">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.2">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="5.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="5.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="5.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="5.0.2" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="NReco.Logging.File" Version="1.1.1" />
|
<PackageReference Include="NReco.Logging.File" Version="1.1.1" />
|
||||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@ VisualStudioVersion = 16.0.29911.84
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RdtClient.Web", "RdtClient.Web\RdtClient.Web.csproj", "{A8C7F095-89C6-4CD1-AFB2-27106F470D62}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RdtClient.Web", "RdtClient.Web\RdtClient.Web.csproj", "{A8C7F095-89C6-4CD1-AFB2-27106F470D62}"
|
||||||
EndProject
|
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
|
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
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{92EF8817-AD73-4301-93BD-745D7D61DD74}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/Browsers/Enable/@EntryValue">False</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/Browsers/Enable/@EntryValue">False</s:Boolean>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=48FF995D_002DAD14_002D4E41_002DBC69_002D7A144C84E3B6/@EntryIndexedValue">ExplicitlyExcluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=92EF8817_002DAD73_002D4301_002D93BD_002D745D7D61DD74_002Fd_003AMigrations/@EntryIndexedValue">ExplicitlyExcluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=92EF8817_002DAD73_002D4301_002D93BD_002D745D7D61DD74_002Fd_003AMigrations/@EntryIndexedValue">ExplicitlyExcluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002Eappxmanifest/@EntryIndexedValue">*.appxmanifest</s:String>
|
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002Eappxmanifest/@EntryIndexedValue">*.appxmanifest</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002Ed_002Ets/@EntryIndexedValue">*.d.ts</s:String>
|
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002Ed_002Ets/@EntryIndexedValue">*.d.ts</s:String>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue