Upgrade packages

Moved C# code to file scoped namespaces
Merged Startup.cs and Program.cs
This commit is contained in:
Roger Far 2022-04-30 11:22:15 -06:00
parent 2cff8ec0a6
commit cd6002b5d6
69 changed files with 7484 additions and 7591 deletions

1650
client/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -10,33 +10,33 @@
},
"private": true,
"dependencies": {
"@angular/animations": "~13.2.6",
"@angular/cdk": "^13.2.6",
"@angular/common": "~13.2.6",
"@angular/compiler": "~13.2.6",
"@angular/core": "~13.2.6",
"@angular/animations": "~13.3.5",
"@angular/cdk": "^13.3.5",
"@angular/common": "~13.3.5",
"@angular/compiler": "~13.3.5",
"@angular/core": "~13.3.5",
"@angular/flex-layout": "^13.0.0-beta.38",
"@angular/forms": "~13.2.6",
"@angular/platform-browser": "~13.2.6",
"@angular/platform-browser-dynamic": "~13.2.6",
"@angular/router": "~13.2.6",
"@fortawesome/fontawesome-free": "^6.0.0",
"@microsoft/signalr": "^6.0.3",
"@angular/forms": "~13.3.5",
"@angular/platform-browser": "~13.3.5",
"@angular/platform-browser-dynamic": "~13.3.5",
"@angular/router": "~13.3.5",
"@fortawesome/fontawesome-free": "^6.1.1",
"@microsoft/signalr": "^6.0.4",
"bulma": "^0.9.3",
"curray": "^1.0.8",
"file-saver": "^2.0.5",
"ngx-filesize": "^2.0.16",
"rxjs": "~7.5.5",
"tslib": "^2.3.1",
"tslib": "^2.4.0",
"zone.js": "~0.11.5"
},
"devDependencies": {
"@angular-devkit/build-angular": "~13.2.6",
"@angular/cli": "~13.2.6",
"@angular/compiler-cli": "~13.2.6",
"@angular/language-service": "~13.2.6",
"@angular-devkit/build-angular": "~13.3.4",
"@angular/cli": "~13.3.4",
"@angular/compiler-cli": "~13.3.5",
"@angular/language-service": "~13.3.5",
"@types/file-saver": "^2.0.5",
"@types/node": "^17.0.21",
"@types/node": "^17.0.30",
"typescript": "~4.5.5"
}
}

View file

@ -1,225 +1,221 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using RdtClient.Data.Models.Data;
namespace RdtClient.Data.Data
namespace RdtClient.Data.Data;
public class DataContext : IdentityDbContext
{
public class DataContext : IdentityDbContext
public DataContext(DbContextOptions options) : base(options)
{
public DataContext(DbContextOptions options) : base(options)
{
}
}
public DbSet<Download> Downloads { get; set; }
public DbSet<Setting> Settings { get; set; }
public DbSet<Torrent> Torrents { get; set; }
public DbSet<Download> Downloads { get; set; }
public DbSet<Setting> Settings { get; set; }
public DbSet<Torrent> Torrents { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
}
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
var cascadeFKs = builder.Model.GetEntityTypes()
.SelectMany(t => t.GetForeignKeys())
.Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade);
var cascadeFKs = builder.Model.GetEntityTypes()
.SelectMany(t => t.GetForeignKeys())
.Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade);
foreach (var fk in cascadeFKs)
{
fk.DeleteBehavior = DeleteBehavior.Restrict;
}
}
public async Task Seed()
foreach (var fk in cascadeFKs)
{
var seedSettings = new List<Setting>
fk.DeleteBehavior = DeleteBehavior.Restrict;
}
}
public async Task Seed()
{
var seedSettings = new List<Setting>
{
new Setting
{
new Setting
{
SettingId = "Provider",
Type = "String",
Value = "RealDebrid"
},
new Setting
{
SettingId = "ProviderAutoImport",
Type = "Int32",
Value = "0"
},
new Setting
{
SettingId = "ProviderAutoImportCategory",
Type = "String",
Value = ""
},
new Setting
{
SettingId = "ProviderAutoDelete",
Type = "Int32",
Value = "0"
},
new Setting
{
SettingId = "DeleteOnError",
Type = "Int32",
Value = "0"
},
new Setting
{
SettingId = "TorrentLifetime",
Type = "Int32",
Value = "0"
},
new Setting
{
SettingId = "RealDebridApiKey",
Type = "String",
Value = ""
},
new Setting
{
SettingId = "DownloadPath",
Type = "String",
#if DEBUG
Value = @"C:\Temp\rdtclient"
#else
Value = "/data/downloads"
#endif
},
new Setting
{
SettingId = "DownloadClient",
Type = "String",
Value = @"Simple"
},
new Setting
{
SettingId = "TempPath",
Type = "String",
SettingId = "Provider",
Type = "String",
Value = "RealDebrid"
},
new Setting
{
SettingId = "ProviderAutoImport",
Type = "Int32",
Value = "0"
},
new Setting
{
SettingId = "ProviderAutoImportCategory",
Type = "String",
Value = ""
},
new Setting
{
SettingId = "ProviderAutoDelete",
Type = "Int32",
Value = "0"
},
new Setting
{
SettingId = "DeleteOnError",
Type = "Int32",
Value = "0"
},
new Setting
{
SettingId = "TorrentLifetime",
Type = "Int32",
Value = "0"
},
new Setting
{
SettingId = "RealDebridApiKey",
Type = "String",
Value = ""
},
new Setting
{
SettingId = "DownloadPath",
Type = "String",
#if DEBUG
Value = @"C:\Temp\rdtclient"
Value = @"C:\Temp\rdtclient"
#else
Value = "/data/downloads"
#endif
},
new Setting
{
SettingId = "MappedPath",
Type = "String",
},
new Setting
{
SettingId = "DownloadClient",
Type = "String",
Value = @"Simple"
},
new Setting
{
SettingId = "TempPath",
Type = "String",
#if DEBUG
Value = @"C:\Temp\rdtclient"
Value = @"C:\Temp\rdtclient"
#else
Value = "/data/downloads"
#endif
},
new Setting
{
SettingId = "MappedPath",
Type = "String",
#if DEBUG
Value = @"C:\Temp\rdtclient"
#else
Value = @"C:\Downloads"
#endif
},
new Setting
{
SettingId = "DownloadLimit",
Type = "Int32",
Value = "2"
},
new Setting
{
SettingId = "UnpackLimit",
Type = "Int32",
Value = "1"
},
new Setting
{
SettingId = "MinFileSize",
Type = "Int32",
Value = "0"
},
new Setting
{
SettingId = "OnlyDownloadAvailableFiles",
Type = "Int32",
Value = "1"
},
new Setting
{
SettingId = "DownloadChunkCount",
Type = "Int32",
Value = "8"
},
new Setting
{
SettingId = "DownloadMaxSpeed",
Type = "Int32",
Value = "0"
},
new Setting
{
SettingId = "ProxyServer",
Type = "String",
Value = ""
},
new Setting
{
SettingId = "LogLevel",
Type = "String",
Value = "Warning"
},
new Setting
{
SettingId = "Categories",
Type = "String",
Value = ""
},
new Setting
{
SettingId = "Aria2cUrl",
Type = "String",
Value = "http://127.0.0.1:6800/jsonrpc"
},
new Setting
{
SettingId = "Aria2cSecret",
Type = "String",
Value = ""
},
new Setting
{
SettingId = "DownloadRetryAttempts",
Type = "Int32",
Value = "3"
},
new Setting
{
SettingId = "TorrentRetryAttempts",
Type = "Int32",
Value = "1"
},
new Setting
{
SettingId = "RunOnTorrentCompleteFileName",
Type = "String",
Value = ""
},
new Setting
{
SettingId = "RunOnTorrentCompleteArguments",
Type = "String",
Value = ""
}
};
var dbSettings = await Settings.ToListAsync();
foreach (var seedSetting in seedSettings)
},
new Setting
{
var dbSetting = dbSettings.FirstOrDefault(m => m.SettingId == seedSetting.SettingId);
SettingId = "DownloadLimit",
Type = "Int32",
Value = "2"
},
new Setting
{
SettingId = "UnpackLimit",
Type = "Int32",
Value = "1"
},
new Setting
{
SettingId = "MinFileSize",
Type = "Int32",
Value = "0"
},
new Setting
{
SettingId = "OnlyDownloadAvailableFiles",
Type = "Int32",
Value = "1"
},
new Setting
{
SettingId = "DownloadChunkCount",
Type = "Int32",
Value = "8"
},
new Setting
{
SettingId = "DownloadMaxSpeed",
Type = "Int32",
Value = "0"
},
new Setting
{
SettingId = "ProxyServer",
Type = "String",
Value = ""
},
new Setting
{
SettingId = "LogLevel",
Type = "String",
Value = "Warning"
},
new Setting
{
SettingId = "Categories",
Type = "String",
Value = ""
},
new Setting
{
SettingId = "Aria2cUrl",
Type = "String",
Value = "http://127.0.0.1:6800/jsonrpc"
},
new Setting
{
SettingId = "Aria2cSecret",
Type = "String",
Value = ""
},
new Setting
{
SettingId = "DownloadRetryAttempts",
Type = "Int32",
Value = "3"
},
new Setting
{
SettingId = "TorrentRetryAttempts",
Type = "Int32",
Value = "1"
},
new Setting
{
SettingId = "RunOnTorrentCompleteFileName",
Type = "String",
Value = ""
},
new Setting
{
SettingId = "RunOnTorrentCompleteArguments",
Type = "String",
Value = ""
}
};
if (dbSetting == null)
{
await Settings.AddAsync(seedSetting);
await SaveChangesAsync();
}
var dbSettings = await Settings.ToListAsync();
foreach (var seedSetting in seedSettings)
{
var dbSetting = dbSettings.FirstOrDefault(m => m.SettingId == seedSetting.SettingId);
if (dbSetting == null)
{
await Settings.AddAsync(seedSetting);
await SaveChangesAsync();
}
}
}

View file

@ -1,267 +1,262 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Download = RdtClient.Data.Models.Data.Download;
namespace RdtClient.Data.Data
namespace RdtClient.Data.Data;
public class DownloadData
{
public class DownloadData
private readonly DataContext _dataContext;
public DownloadData(DataContext dataContext)
{
private readonly DataContext _dataContext;
_dataContext = dataContext;
}
public DownloadData(DataContext dataContext)
public async Task<List<Download>> GetForTorrent(Guid torrentId)
{
return await _dataContext.Downloads
.AsNoTracking()
.Where(m => m.TorrentId == torrentId)
.ToListAsync();
}
public async Task<Download> GetById(Guid downloadId)
{
return await _dataContext.Downloads
.Include(m => m.Torrent)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
}
public async Task<Download> Get(Guid torrentId, String path)
{
return await _dataContext.Downloads
.Include(m => m.Torrent)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.TorrentId == torrentId && m.Path == path);
}
public async Task<Download> Add(Guid torrentId, String path)
{
var download = new Download
{
_dataContext = dataContext;
DownloadId = Guid.NewGuid(),
TorrentId = torrentId,
Path = path,
Added = DateTimeOffset.UtcNow,
DownloadQueued = DateTimeOffset.UtcNow,
RetryCount = 0
};
await _dataContext.Downloads.AddAsync(download);
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
return download;
}
public async Task UpdateUnrestrictedLink(Guid downloadId, String unrestrictedLink)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
return;
}
public async Task<List<Download>> GetForTorrent(Guid torrentId)
dbDownload.Link = unrestrictedLink;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
}
public async Task UpdateDownloadStarted(Guid downloadId, DateTimeOffset? dateTime)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
return await _dataContext.Downloads
.AsNoTracking()
.Where(m => m.TorrentId == torrentId)
.ToListAsync();
return;
}
public async Task<Download> GetById(Guid downloadId)
dbDownload.DownloadStarted = dateTime;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
}
public async Task UpdateDownloadFinished(Guid downloadId, DateTimeOffset? dateTime)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
return await _dataContext.Downloads
.Include(m => m.Torrent)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
return;
}
public async Task<Download> Get(Guid torrentId, String path)
{
return await _dataContext.Downloads
.Include(m => m.Torrent)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.TorrentId == torrentId && m.Path == path);
}
public async Task<Download> Add(Guid torrentId, String path)
{
var download = new Download
{
DownloadId = Guid.NewGuid(),
TorrentId = torrentId,
Path = path,
Added = DateTimeOffset.UtcNow,
DownloadQueued = DateTimeOffset.UtcNow,
RetryCount = 0
};
await _dataContext.Downloads.AddAsync(download);
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
return download;
}
public async Task UpdateUnrestrictedLink(Guid downloadId, String unrestrictedLink)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
return;
}
dbDownload.Link = unrestrictedLink;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
}
public async Task UpdateDownloadStarted(Guid downloadId, DateTimeOffset? dateTime)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
return;
}
dbDownload.DownloadStarted = dateTime;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
}
public async Task UpdateDownloadFinished(Guid downloadId, DateTimeOffset? dateTime)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
return;
}
dbDownload.DownloadFinished = dateTime;
dbDownload.DownloadFinished = dateTime;
await _dataContext.SaveChangesAsync();
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
}
await TorrentData.VoidCache();
}
public async Task UpdateUnpackingQueued(Guid downloadId, DateTimeOffset? dateTime)
public async Task UpdateUnpackingQueued(Guid downloadId, DateTimeOffset? dateTime)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
return;
}
dbDownload.UnpackingQueued = dateTime;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
return;
}
public async Task UpdateUnpackingStarted(Guid downloadId, DateTimeOffset? dateTime)
dbDownload.UnpackingQueued = dateTime;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
}
public async Task UpdateUnpackingStarted(Guid downloadId, DateTimeOffset? dateTime)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
return;
}
dbDownload.UnpackingStarted = dateTime;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
return;
}
public async Task UpdateUnpackingFinished(Guid downloadId, DateTimeOffset? dateTime)
dbDownload.UnpackingStarted = dateTime;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
}
public async Task UpdateUnpackingFinished(Guid downloadId, DateTimeOffset? dateTime)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
return;
}
dbDownload.UnpackingFinished = dateTime;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
return;
}
dbDownload.UnpackingFinished = dateTime;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
}
public async Task UpdateCompleted(Guid downloadId, DateTimeOffset? dateTime)
public async Task UpdateCompleted(Guid downloadId, DateTimeOffset? dateTime)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
return;
}
dbDownload.Completed = dateTime;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
return;
}
public async Task UpdateError(Guid downloadId, String error)
dbDownload.Completed = dateTime;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
}
public async Task UpdateError(Guid downloadId, String error)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
return;
}
dbDownload.Error = error;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
return;
}
public async Task UpdateRetryCount(Guid downloadId, Int32 retryCount)
dbDownload.Error = error;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
}
public async Task UpdateRetryCount(Guid downloadId, Int32 retryCount)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
return;
}
dbDownload.RetryCount = retryCount;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
return;
}
public async Task UpdateRemoteId(Guid downloadId, String remoteId)
dbDownload.RetryCount = retryCount;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
}
public async Task UpdateRemoteId(Guid downloadId, String remoteId)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
return;
}
dbDownload.RemoteId = remoteId;
await _dataContext.SaveChangesAsync();
return;
}
public async Task DeleteForTorrent(Guid torrentId)
{
var downloads = await _dataContext.Downloads
.Where(m => m.TorrentId == torrentId)
.ToListAsync();
dbDownload.RemoteId = remoteId;
_dataContext.Downloads.RemoveRange(downloads);
await _dataContext.SaveChangesAsync();
}
await _dataContext.SaveChangesAsync();
public async Task DeleteForTorrent(Guid torrentId)
{
var downloads = await _dataContext.Downloads
.Where(m => m.TorrentId == torrentId)
.ToListAsync();
await TorrentData.VoidCache();
}
_dataContext.Downloads.RemoveRange(downloads);
public async Task Reset(Guid downloadId)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
await _dataContext.SaveChangesAsync();
dbDownload.RetryCount = 0;
dbDownload.Link = null;
dbDownload.Added = DateTimeOffset.UtcNow;
dbDownload.DownloadQueued = DateTimeOffset.UtcNow;
dbDownload.DownloadStarted = null;
dbDownload.DownloadFinished = null;
dbDownload.UnpackingQueued = null;
dbDownload.UnpackingStarted = null;
dbDownload.UnpackingFinished = null;
dbDownload.Completed = null;
dbDownload.Error = null;
await TorrentData.VoidCache();
}
await _dataContext.SaveChangesAsync();
public async Task Reset(Guid downloadId)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
await TorrentData.VoidCache();
}
dbDownload.RetryCount = 0;
dbDownload.Link = null;
dbDownload.Added = DateTimeOffset.UtcNow;
dbDownload.DownloadQueued = DateTimeOffset.UtcNow;
dbDownload.DownloadStarted = null;
dbDownload.DownloadFinished = null;
dbDownload.UnpackingQueued = null;
dbDownload.UnpackingStarted = null;
dbDownload.UnpackingFinished = null;
dbDownload.Completed = null;
dbDownload.Error = null;
await _dataContext.SaveChangesAsync();
await TorrentData.VoidCache();
}
}

View file

@ -1,138 +1,132 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.Internal;
using Serilog;
namespace RdtClient.Data.Data
namespace RdtClient.Data.Data;
public class SettingData
{
public class SettingData
private static readonly SemaphoreSlim _settingCacheLock = new(1);
private readonly DataContext _dataContext;
public SettingData(DataContext dataContext)
{
private static readonly SemaphoreSlim _settingCacheLock = new(1);
_dataContext = dataContext;
}
private readonly DataContext _dataContext;
public static DbSettings Get { get; private set; }
public SettingData(DataContext dataContext)
public async Task ResetCache()
{
var allSettings = await _dataContext.Settings.AsNoTracking().ToListAsync();
String GetString(String name)
{
_dataContext = dataContext;
return allSettings.FirstOrDefault(m => m.SettingId == name)?.Value;
}
public static DbSettings Get { get; private set; }
public async Task ResetCache()
Int32 GetInt32(String name)
{
var allSettings = await _dataContext.Settings.AsNoTracking().ToListAsync();
var strVal = GetString(name);
String GetString(String name)
if (!Int32.TryParse(strVal, out var intVal))
{
return allSettings.FirstOrDefault(m => m.SettingId == name)?.Value;
Log.Error("Unable to parse setting {name} to Int32", name);
return 0;
}
Int32 GetInt32(String name)
{
var strVal = GetString(name);
return intVal;
if (!Int32.TryParse(strVal, out var intVal))
}
Get = new DbSettings
{
Provider = GetString("Provider"),
ProviderAutoImport = GetInt32("ProviderAutoImport"),
ProviderAutoImportCategory = GetString("ProviderAutoImportCategory"),
ProviderAutoDelete = GetInt32("ProviderAutoDelete"),
RealDebridApiKey = GetString("RealDebridApiKey"),
DownloadPath = GetString("DownloadPath"),
DownloadClient = GetString("DownloadClient"),
TempPath = GetString("TempPath"),
MappedPath = GetString("MappedPath"),
DownloadLimit = GetInt32("DownloadLimit"),
UnpackLimit = GetInt32("UnpackLimit"),
MinFileSize = GetInt32("MinFileSize"),
OnlyDownloadAvailableFiles = GetInt32("OnlyDownloadAvailableFiles"),
DownloadChunkCount = GetInt32("DownloadChunkCount"),
DownloadMaxSpeed = GetInt32("DownloadMaxSpeed"),
ProxyServer = GetString("ProxyServer"),
LogLevel = GetString("LogLevel"),
Categories = GetString("Categories"),
Aria2cUrl = GetString("Aria2cUrl"),
Aria2cSecret = GetString("Aria2cSecret"),
DownloadRetryAttempts = GetInt32("DownloadRetryAttempts"),
TorrentRetryAttempts = GetInt32("TorrentRetryAttempts"),
DeleteOnError = GetInt32("DeleteOnError"),
TorrentLifetime = GetInt32("TorrentLifetime"),
RunOnTorrentCompleteFileName = GetString("RunOnTorrentCompleteFileName"),
RunOnTorrentCompleteArguments = GetString("RunOnTorrentCompleteArguments")
};
}
public async Task<IList<Setting>> GetAll()
{
return await _dataContext.Settings.AsNoTracking().ToListAsync();
}
public async Task Update(IList<Setting> settings)
{
await _settingCacheLock.WaitAsync();
try
{
var dbSettings = await _dataContext.Settings.ToListAsync();
foreach (var dbSetting in dbSettings)
{
var setting = settings.FirstOrDefault(m => m.SettingId == dbSetting.SettingId);
if (setting != null)
{
Log.Error("Unable to parse setting {name} to Int32", name);
return 0;
dbSetting.Value = setting.Value;
}
return intVal;
}
Get = new DbSettings
{
Provider = GetString("Provider"),
ProviderAutoImport = GetInt32("ProviderAutoImport"),
ProviderAutoImportCategory = GetString("ProviderAutoImportCategory"),
ProviderAutoDelete = GetInt32("ProviderAutoDelete"),
RealDebridApiKey = GetString("RealDebridApiKey"),
DownloadPath = GetString("DownloadPath"),
DownloadClient = GetString("DownloadClient"),
TempPath = GetString("TempPath"),
MappedPath = GetString("MappedPath"),
DownloadLimit = GetInt32("DownloadLimit"),
UnpackLimit = GetInt32("UnpackLimit"),
MinFileSize = GetInt32("MinFileSize"),
OnlyDownloadAvailableFiles = GetInt32("OnlyDownloadAvailableFiles"),
DownloadChunkCount = GetInt32("DownloadChunkCount"),
DownloadMaxSpeed = GetInt32("DownloadMaxSpeed"),
ProxyServer = GetString("ProxyServer"),
LogLevel = GetString("LogLevel"),
Categories = GetString("Categories"),
Aria2cUrl = GetString("Aria2cUrl"),
Aria2cSecret = GetString("Aria2cSecret"),
DownloadRetryAttempts = GetInt32("DownloadRetryAttempts"),
TorrentRetryAttempts = GetInt32("TorrentRetryAttempts"),
DeleteOnError = GetInt32("DeleteOnError"),
TorrentLifetime = GetInt32("TorrentLifetime"),
RunOnTorrentCompleteFileName = GetString("RunOnTorrentCompleteFileName"),
RunOnTorrentCompleteArguments = GetString("RunOnTorrentCompleteArguments")
};
await _dataContext.SaveChangesAsync();
await ResetCache();
}
public async Task<IList<Setting>> GetAll()
finally
{
return await _dataContext.Settings.AsNoTracking().ToListAsync();
}
public async Task Update(IList<Setting> settings)
{
await _settingCacheLock.WaitAsync();
try
{
var dbSettings = await _dataContext.Settings.ToListAsync();
foreach (var dbSetting in dbSettings)
{
var setting = settings.FirstOrDefault(m => m.SettingId == dbSetting.SettingId);
if (setting != null)
{
dbSetting.Value = setting.Value;
}
}
await _dataContext.SaveChangesAsync();
await ResetCache();
}
finally
{
_settingCacheLock.Release();
}
}
public async Task UpdateString(String key, String value)
{
await _settingCacheLock.WaitAsync();
try
{
var dbSetting = await _dataContext.Settings.FirstOrDefaultAsync(m => m.SettingId == key);
if (dbSetting == null)
{
throw new Exception($"Cannot find setting with key {key}");
}
dbSetting.Value = value;
await _dataContext.SaveChangesAsync();
await ResetCache();
}
finally
{
_settingCacheLock.Release();
}
_settingCacheLock.Release();
}
}
}
public async Task UpdateString(String key, String value)
{
await _settingCacheLock.WaitAsync();
try
{
var dbSetting = await _dataContext.Settings.FirstOrDefaultAsync(m => m.SettingId == key);
if (dbSetting == null)
{
throw new Exception($"Cannot find setting with key {key}");
}
dbSetting.Value = value;
await _dataContext.SaveChangesAsync();
await ResetCache();
}
finally
{
_settingCacheLock.Release();
}
}
}

View file

@ -1,316 +1,310 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using RdtClient.Data.Models.Data;
namespace RdtClient.Data.Data
namespace RdtClient.Data.Data;
public class TorrentData
{
public class TorrentData
private static IList<Torrent> _torrentCache;
private static readonly SemaphoreSlim TorrentCacheLock = new(1, 1);
private readonly DataContext _dataContext;
public TorrentData(DataContext dataContext)
{
private static IList<Torrent> _torrentCache;
private static readonly SemaphoreSlim TorrentCacheLock = new(1, 1);
_dataContext = dataContext;
}
private readonly DataContext _dataContext;
public async Task<IList<Torrent>> Get()
{
await TorrentCacheLock.WaitAsync();
public TorrentData(DataContext dataContext)
try
{
_dataContext = dataContext;
_torrentCache ??= await _dataContext.Torrents
.AsNoTracking()
.Include(m => m.Downloads)
.ToListAsync();
return _torrentCache.OrderBy(m => m.Priority ?? 9999).ThenBy(m => m.Added).ToList();
}
public async Task<IList<Torrent>> Get()
finally
{
await TorrentCacheLock.WaitAsync();
try
{
_torrentCache ??= await _dataContext.Torrents
.AsNoTracking()
.Include(m => m.Downloads)
.ToListAsync();
return _torrentCache.OrderBy(m => m.Priority ?? 9999).ThenBy(m => m.Added).ToList();
}
finally
{
TorrentCacheLock.Release();
}
}
public async Task<Torrent> GetById(Guid torrentId)
{
var dbTorrent = await _dataContext.Torrents
.AsNoTracking()
.Include(m => m.Downloads)
.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return null;
}
foreach (var file in dbTorrent.Downloads)
{
file.Torrent = null;
}
return dbTorrent;
}
public async Task<Torrent> GetByHash(String hash)
{
var dbTorrent = await _dataContext.Torrents
.AsNoTracking()
.Include(m => m.Downloads)
.FirstOrDefaultAsync(m => m.Hash.ToLower() == hash.ToLower());
if (dbTorrent == null)
{
return null;
}
foreach (var file in dbTorrent.Downloads)
{
file.Torrent = null;
}
return dbTorrent;
}
public async Task<Torrent> Add(String realDebridId,
String hash,
String fileOrMagnetContents,
Boolean isFile,
Torrent torrent)
{
var newTorrent = new Torrent
{
TorrentId = Guid.NewGuid(),
Added = DateTimeOffset.UtcNow,
RdId = realDebridId,
Hash = hash.ToLower(),
Category = torrent.Category,
DownloadAction = torrent.DownloadAction,
FinishedAction = torrent.FinishedAction,
DownloadMinSize = torrent.DownloadMinSize,
DownloadManualFiles = torrent.DownloadManualFiles,
FileOrMagnet = fileOrMagnetContents,
IsFile = isFile,
Priority = torrent.Priority,
TorrentRetryAttempts = torrent.TorrentRetryAttempts,
DownloadRetryAttempts = torrent.DownloadRetryAttempts,
DeleteOnError = torrent.DeleteOnError,
Lifetime = torrent.Lifetime
};
await _dataContext.Torrents.AddAsync(newTorrent);
await _dataContext.SaveChangesAsync();
await VoidCache();
return newTorrent;
}
public async Task UpdateRdData(Torrent torrent)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrent.TorrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.RdName = torrent.RdName;
dbTorrent.RdSize = torrent.RdSize;
dbTorrent.RdHost = torrent.RdHost;
dbTorrent.RdSplit = torrent.RdSplit;
dbTorrent.RdProgress = torrent.RdProgress;
dbTorrent.RdStatus = torrent.RdStatus;
dbTorrent.RdStatusRaw = torrent.RdStatusRaw;
dbTorrent.RdAdded = torrent.RdAdded;
dbTorrent.RdEnded = torrent.RdEnded;
dbTorrent.RdSpeed = torrent.RdSpeed;
dbTorrent.RdSeeders = torrent.RdSeeders;
if (torrent.Files != null)
{
dbTorrent.RdFiles = torrent.RdFiles;
}
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task Update(Torrent torrent)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrent.TorrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.Priority = torrent.Priority;
dbTorrent.DownloadRetryAttempts = torrent.DownloadRetryAttempts;
dbTorrent.TorrentRetryAttempts = torrent.TorrentRetryAttempts;
dbTorrent.DeleteOnError = torrent.DeleteOnError;
dbTorrent.Lifetime = torrent.Lifetime;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task UpdateCategory(Guid torrentId, String category)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.Category = category;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task UpdateComplete(Guid torrentId, String error, DateTimeOffset? datetime, Boolean retry)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
if (String.IsNullOrWhiteSpace(error))
{
var downloads = await _dataContext.Downloads.AsNoTracking().Where(m => m.TorrentId == torrentId).ToListAsync();
var downloadWithErrors = downloads.Where(m => !String.IsNullOrWhiteSpace(m.Error)).ToList();
if (downloadWithErrors.Any())
{
error = $"{downloadWithErrors.Count}/{downloads.Count} downloads failed with errors";
}
}
if (!String.IsNullOrWhiteSpace(error) && retry)
{
if (dbTorrent.RetryCount < dbTorrent.TorrentRetryAttempts)
{
dbTorrent.RetryCount += 1;
dbTorrent.Retry = DateTime.UtcNow;
}
}
dbTorrent.Completed = datetime;
dbTorrent.Error = error;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task UpdateFilesSelected(Guid torrentId, DateTimeOffset datetime)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.FilesSelected = datetime;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task UpdatePriority(Guid torrentId, Int32? priority)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.Priority = priority;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task UpdateRetry(Guid torrentId, DateTimeOffset? dateTime, Int32 retryCount)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.RetryCount = retryCount;
dbTorrent.Retry = dateTime;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task UpdateError(Guid torrentId, String error)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.Error = error;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task Delete(Guid torrentId)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
_dataContext.Torrents.Remove(dbTorrent);
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public static async Task VoidCache()
{
await TorrentCacheLock.WaitAsync();
try
{
_torrentCache = null;
}
finally
{
TorrentCacheLock.Release();
}
TorrentCacheLock.Release();
}
}
}
public async Task<Torrent> GetById(Guid torrentId)
{
var dbTorrent = await _dataContext.Torrents
.AsNoTracking()
.Include(m => m.Downloads)
.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return null;
}
foreach (var file in dbTorrent.Downloads)
{
file.Torrent = null;
}
return dbTorrent;
}
public async Task<Torrent> GetByHash(String hash)
{
var dbTorrent = await _dataContext.Torrents
.AsNoTracking()
.Include(m => m.Downloads)
.FirstOrDefaultAsync(m => m.Hash.ToLower() == hash.ToLower());
if (dbTorrent == null)
{
return null;
}
foreach (var file in dbTorrent.Downloads)
{
file.Torrent = null;
}
return dbTorrent;
}
public async Task<Torrent> Add(String realDebridId,
String hash,
String fileOrMagnetContents,
Boolean isFile,
Torrent torrent)
{
var newTorrent = new Torrent
{
TorrentId = Guid.NewGuid(),
Added = DateTimeOffset.UtcNow,
RdId = realDebridId,
Hash = hash.ToLower(),
Category = torrent.Category,
DownloadAction = torrent.DownloadAction,
FinishedAction = torrent.FinishedAction,
DownloadMinSize = torrent.DownloadMinSize,
DownloadManualFiles = torrent.DownloadManualFiles,
FileOrMagnet = fileOrMagnetContents,
IsFile = isFile,
Priority = torrent.Priority,
TorrentRetryAttempts = torrent.TorrentRetryAttempts,
DownloadRetryAttempts = torrent.DownloadRetryAttempts,
DeleteOnError = torrent.DeleteOnError,
Lifetime = torrent.Lifetime
};
await _dataContext.Torrents.AddAsync(newTorrent);
await _dataContext.SaveChangesAsync();
await VoidCache();
return newTorrent;
}
public async Task UpdateRdData(Torrent torrent)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrent.TorrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.RdName = torrent.RdName;
dbTorrent.RdSize = torrent.RdSize;
dbTorrent.RdHost = torrent.RdHost;
dbTorrent.RdSplit = torrent.RdSplit;
dbTorrent.RdProgress = torrent.RdProgress;
dbTorrent.RdStatus = torrent.RdStatus;
dbTorrent.RdStatusRaw = torrent.RdStatusRaw;
dbTorrent.RdAdded = torrent.RdAdded;
dbTorrent.RdEnded = torrent.RdEnded;
dbTorrent.RdSpeed = torrent.RdSpeed;
dbTorrent.RdSeeders = torrent.RdSeeders;
if (torrent.Files != null)
{
dbTorrent.RdFiles = torrent.RdFiles;
}
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task Update(Torrent torrent)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrent.TorrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.Priority = torrent.Priority;
dbTorrent.DownloadRetryAttempts = torrent.DownloadRetryAttempts;
dbTorrent.TorrentRetryAttempts = torrent.TorrentRetryAttempts;
dbTorrent.DeleteOnError = torrent.DeleteOnError;
dbTorrent.Lifetime = torrent.Lifetime;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task UpdateCategory(Guid torrentId, String category)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.Category = category;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task UpdateComplete(Guid torrentId, String error, DateTimeOffset? datetime, Boolean retry)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
if (String.IsNullOrWhiteSpace(error))
{
var downloads = await _dataContext.Downloads.AsNoTracking().Where(m => m.TorrentId == torrentId).ToListAsync();
var downloadWithErrors = downloads.Where(m => !String.IsNullOrWhiteSpace(m.Error)).ToList();
if (downloadWithErrors.Any())
{
error = $"{downloadWithErrors.Count}/{downloads.Count} downloads failed with errors";
}
}
if (!String.IsNullOrWhiteSpace(error) && retry)
{
if (dbTorrent.RetryCount < dbTorrent.TorrentRetryAttempts)
{
dbTorrent.RetryCount += 1;
dbTorrent.Retry = DateTime.UtcNow;
}
}
dbTorrent.Completed = datetime;
dbTorrent.Error = error;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task UpdateFilesSelected(Guid torrentId, DateTimeOffset datetime)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.FilesSelected = datetime;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task UpdatePriority(Guid torrentId, Int32? priority)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.Priority = priority;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task UpdateRetry(Guid torrentId, DateTimeOffset? dateTime, Int32 retryCount)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.RetryCount = retryCount;
dbTorrent.Retry = dateTime;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task UpdateError(Guid torrentId, String error)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.Error = error;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task Delete(Guid torrentId)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
_dataContext.Torrents.Remove(dbTorrent);
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public static async Task VoidCache()
{
await TorrentCacheLock.WaitAsync();
try
{
_torrentCache = null;
}
finally
{
TorrentCacheLock.Release();
}
}
}

View file

@ -1,21 +1,19 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
namespace RdtClient.Data.Data
namespace RdtClient.Data.Data;
public class UserData
{
public class UserData
private readonly DataContext _dataContext;
public UserData(DataContext dataContext)
{
private readonly DataContext _dataContext;
public UserData(DataContext dataContext)
{
_dataContext = dataContext;
}
public async Task<IdentityUser> GetUser()
{
return await _dataContext.Users.FirstOrDefaultAsync();
}
_dataContext = dataContext;
}
}
public async Task<IdentityUser> GetUser()
{
return await _dataContext.Users.FirstOrDefaultAsync();
}
}

View file

@ -1,16 +1,20 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using RdtClient.Data.Data;
using RdtClient.Data.Models.Internal;
namespace RdtClient.Data
namespace RdtClient.Data;
public static class DiConfig
{
public static class DiConfig
public static void Config(IServiceCollection services, AppSettings appSettings)
{
public static void Config(IServiceCollection services)
{
services.AddScoped<DownloadData>();
services.AddScoped<SettingData>();
services.AddScoped<TorrentData>();
services.AddScoped<UserData>();
}
var connectionString = $"Data Source={appSettings.Database.Path}";
services.AddDbContext<DataContext>(options => options.UseSqlite(connectionString));
services.AddScoped<DownloadData>();
services.AddScoped<SettingData>();
services.AddScoped<TorrentData>();
services.AddScoped<UserData>();
}
}
}

View file

@ -1,9 +1,8 @@
namespace RdtClient.Data.Enums
namespace RdtClient.Data.Enums;
public enum TorrentDownloadAction
{
public enum TorrentDownloadAction
{
DownloadAll = 0,
DownloadAvailableFiles = 1,
DownloadManual = 2
}
}
DownloadAll = 0,
DownloadAvailableFiles = 1,
DownloadManual = 2
}

View file

@ -1,9 +1,8 @@
namespace RdtClient.Data.Enums
namespace RdtClient.Data.Enums;
public enum TorrentFinishedAction
{
public enum TorrentFinishedAction
{
None = 0,
RemoveAllTorrents = 1,
RemoveRealDebrid = 2
}
}
None = 0,
RemoveAllTorrents = 1,
RemoveRealDebrid = 2
}

View file

@ -1,13 +1,12 @@
namespace RdtClient.Data.Enums
{
public enum TorrentStatus
{
Processing = 0,
WaitingForFileSelection = 1,
Downloading = 2,
Finished = 3,
Uploading = 4,
namespace RdtClient.Data.Enums;
Error = 99
}
}
public enum TorrentStatus
{
Processing = 0,
WaitingForFileSelection = 1,
Downloading = 2,
Finished = 3,
Uploading = 4,
Error = 99
}

View file

@ -1,44 +1,42 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RdtClient.Data.Models.Data
namespace RdtClient.Data.Models.Data;
public class Download
{
public class Download
{
[Key]
public Guid DownloadId { get; set; }
[Key]
public Guid DownloadId { get; set; }
public Guid TorrentId { get; set; }
public Guid TorrentId { get; set; }
[ForeignKey("TorrentId")]
public Torrent Torrent { get; set; }
[ForeignKey("TorrentId")]
public Torrent Torrent { get; set; }
public String Path { get; set; }
public String Link { get; set; }
public String Path { get; set; }
public String Link { get; set; }
public DateTimeOffset Added { get; set; }
public DateTimeOffset? DownloadQueued { get; set; }
public DateTimeOffset? DownloadStarted { get; set; }
public DateTimeOffset? DownloadFinished { get; set; }
public DateTimeOffset? UnpackingQueued { get; set; }
public DateTimeOffset? UnpackingStarted { get; set; }
public DateTimeOffset? UnpackingFinished { get; set; }
public DateTimeOffset? Completed { get; set; }
public DateTimeOffset Added { get; set; }
public DateTimeOffset? DownloadQueued { get; set; }
public DateTimeOffset? DownloadStarted { get; set; }
public DateTimeOffset? DownloadFinished { get; set; }
public DateTimeOffset? UnpackingQueued { get; set; }
public DateTimeOffset? UnpackingStarted { get; set; }
public DateTimeOffset? UnpackingFinished { get; set; }
public DateTimeOffset? Completed { get; set; }
public Int32 RetryCount { get; set; }
public Int32 RetryCount { get; set; }
public String Error { get; set; }
public String Error { get; set; }
public String RemoteId { get; set; }
public String RemoteId { get; set; }
[NotMapped]
public Int64 BytesTotal { get; set; }
[NotMapped]
public Int64 BytesTotal { get; set; }
[NotMapped]
public Int64 BytesDone { get; set; }
[NotMapped]
public Int64 BytesDone { get; set; }
[NotMapped]
public Int64 Speed { get; set; }
}
}
[NotMapped]
public Int64 Speed { get; set; }
}

View file

@ -1,15 +1,13 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
namespace RdtClient.Data.Models.Data
namespace RdtClient.Data.Models.Data;
public class Setting
{
public class Setting
{
[Key]
public String SettingId { get; set; }
[Key]
public String SettingId { get; set; }
public String Value { get; set; }
public String Value { get; set; }
public String Type { get; set; }
}
}
public String Type { get; set; }
}

View file

@ -1,94 +1,91 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json;
using RdtClient.Data.Enums;
using RdtClient.Data.Models.TorrentClient;
namespace RdtClient.Data.Models.Data
namespace RdtClient.Data.Models.Data;
public class Torrent
{
public class Torrent
{
[Key]
public Guid TorrentId { get; set; }
[Key]
public Guid TorrentId { get; set; }
public String Hash { get; set; }
public String Hash { get; set; }
public String Category { get; set; }
public String Category { get; set; }
public TorrentDownloadAction DownloadAction { get; set; }
public TorrentFinishedAction FinishedAction { get; set; }
public Int32 DownloadMinSize { get; set; }
public String DownloadManualFiles { get; set; }
public TorrentDownloadAction DownloadAction { get; set; }
public TorrentFinishedAction FinishedAction { get; set; }
public Int32 DownloadMinSize { get; set; }
public String DownloadManualFiles { get; set; }
public DateTimeOffset Added { get; set; }
public DateTimeOffset? FilesSelected { get; set; }
public DateTimeOffset? Completed { get; set; }
public DateTimeOffset? Retry { get; set; }
public DateTimeOffset Added { get; set; }
public DateTimeOffset? FilesSelected { get; set; }
public DateTimeOffset? Completed { get; set; }
public DateTimeOffset? Retry { get; set; }
public String FileOrMagnet { get; set; }
public Boolean IsFile { get; set; }
public String FileOrMagnet { get; set; }
public Boolean IsFile { get; set; }
public Int32? Priority { get; set; }
public Int32 RetryCount { get; set; }
public Int32 DownloadRetryAttempts { get; set; }
public Int32 TorrentRetryAttempts { get; set; }
public Int32 DeleteOnError { get; set; }
public Int32 Lifetime { get; set; }
public Int32? Priority { get; set; }
public Int32 RetryCount { get; set; }
public Int32 DownloadRetryAttempts { get; set; }
public Int32 TorrentRetryAttempts { get; set; }
public Int32 DeleteOnError { get; set; }
public Int32 Lifetime { get; set; }
public String Error { get; set; }
public String Error { get; set; }
[InverseProperty("Torrent")]
public IList<Download> Downloads { get; set; }
[InverseProperty("Torrent")]
public IList<Download> Downloads { get; set; }
public String RdId { get; set; }
public String RdName { get; set; }
public Int64 RdSize { get; set; }
public String RdHost { get; set; }
public Int64 RdSplit { get; set; }
public Int64 RdProgress { get; set; }
public TorrentStatus RdStatus { get; set; }
public String RdStatusRaw { get; set; }
public DateTimeOffset RdAdded { get; set; }
public DateTimeOffset? RdEnded { get; set; }
public Int64? RdSpeed { get; set; }
public Int64? RdSeeders { get; set; }
public String RdFiles { get; set; }
public String RdId { get; set; }
public String RdName { get; set; }
public Int64 RdSize { get; set; }
public String RdHost { get; set; }
public Int64 RdSplit { get; set; }
public Int64 RdProgress { get; set; }
public TorrentStatus RdStatus { get; set; }
public String RdStatusRaw { get; set; }
public DateTimeOffset RdAdded { get; set; }
public DateTimeOffset? RdEnded { get; set; }
public Int64? RdSpeed { get; set; }
public Int64? RdSeeders { get; set; }
public String RdFiles { get; set; }
[NotMapped]
public IList<TorrentClientFile> Files
[NotMapped]
public IList<TorrentClientFile> Files
{
get
{
get
if (String.IsNullOrWhiteSpace(RdFiles))
{
if (String.IsNullOrWhiteSpace(RdFiles))
{
return new List<TorrentClientFile>();
}
try
{
return JsonSerializer.Deserialize<List<TorrentClientFile>>(RdFiles);
}
catch
{
return new List<TorrentClientFile>();
}
return new List<TorrentClientFile>();
}
}
[NotMapped]
public IList<String> ManualFiles
{
get
try
{
if (String.IsNullOrWhiteSpace(DownloadManualFiles))
{
return new List<String>();
}
return DownloadManualFiles.Split(",");
return JsonSerializer.Deserialize<List<TorrentClientFile>>(RdFiles);
}
catch
{
return new List<TorrentClientFile>();
}
}
}
}
[NotMapped]
public IList<String> ManualFiles
{
get
{
if (String.IsNullOrWhiteSpace(DownloadManualFiles))
{
return new List<String>();
}
return DownloadManualFiles.Split(",");
}
}
}

View file

@ -1,31 +1,26 @@
using System;
namespace RdtClient.Data.Models.Internal;
namespace RdtClient.Data.Models.Internal
public class AppSettings
{
public class AppSettings
{
public AppSettingsLogging Logging { get; set; }
public AppSettingsDatabase Database { get; set; }
public AppSettingsLogging Logging { get; set; }
public AppSettingsDatabase Database { get; set; }
public String AllowedHosts { get; set; }
public String HostUrl { get; set; }
}
public class AppSettingsLogging
{
public AppSettingsLoggingFile File { get; set; }
}
public class AppSettingsLoggingFile
{
public String Path { get; set; }
public String Append { get; set; }
public Int64 FileSizeLimitBytes { get; set; }
public Int32 MaxRollingFiles { get; set; }
}
public class AppSettingsDatabase
{
public String Path { get; set; }
}
public Int32 Port { get; set; }
}
public class AppSettingsLogging
{
public AppSettingsLoggingFile File { get; set; }
}
public class AppSettingsLoggingFile
{
public String Path { get; set; }
public Int64 FileSizeLimitBytes { get; set; }
public Int32 MaxRollingFiles { get; set; }
}
public class AppSettingsDatabase
{
public String Path { get; set; }
}

View file

@ -1,34 +1,31 @@
using System;
namespace RdtClient.Data.Models.Internal;
namespace RdtClient.Data.Models.Internal
public class DbSettings
{
public class DbSettings
{
public String Provider { get; set; }
public Int32 ProviderAutoImport { get; set; }
public String ProviderAutoImportCategory { get; set; }
public Int32 ProviderAutoDelete { get; set; }
public String RealDebridApiKey { get; set; }
public String DownloadPath { get; set; }
public String DownloadClient { get; set; }
public String TempPath { get; set; }
public String MappedPath { get; set; }
public Int32 DownloadLimit { get; set; }
public Int32 UnpackLimit { get; set; }
public Int32 MinFileSize { get; set; }
public Int32 OnlyDownloadAvailableFiles { get; set; }
public Int32 DownloadChunkCount { get; set; }
public Int32 DownloadMaxSpeed { get; set; }
public String ProxyServer { get; set; }
public String LogLevel { get; set; }
public String Categories { get; set; }
public String Aria2cUrl { get; set; }
public String Aria2cSecret { get; set; }
public Int32 DownloadRetryAttempts { get; set; }
public Int32 TorrentRetryAttempts { get; set; }
public Int32 DeleteOnError { get; set; }
public Int32 TorrentLifetime { get; set; }
public String RunOnTorrentCompleteFileName { get; set; }
public String RunOnTorrentCompleteArguments { get; set; }
}
}
public String Provider { get; set; }
public Int32 ProviderAutoImport { get; set; }
public String ProviderAutoImportCategory { get; set; }
public Int32 ProviderAutoDelete { get; set; }
public String RealDebridApiKey { get; set; }
public String DownloadPath { get; set; }
public String DownloadClient { get; set; }
public String TempPath { get; set; }
public String MappedPath { get; set; }
public Int32 DownloadLimit { get; set; }
public Int32 UnpackLimit { get; set; }
public Int32 MinFileSize { get; set; }
public Int32 OnlyDownloadAvailableFiles { get; set; }
public Int32 DownloadChunkCount { get; set; }
public Int32 DownloadMaxSpeed { get; set; }
public String ProxyServer { get; set; }
public String LogLevel { get; set; }
public String Categories { get; set; }
public String Aria2cUrl { get; set; }
public String Aria2cSecret { get; set; }
public Int32 DownloadRetryAttempts { get; set; }
public Int32 TorrentRetryAttempts { get; set; }
public Int32 DeleteOnError { get; set; }
public Int32 TorrentLifetime { get; set; }
public String RunOnTorrentCompleteFileName { get; set; }
public String RunOnTorrentCompleteArguments { get; set; }
}

View file

@ -1,13 +1,10 @@
using System;
namespace RdtClient.Data.Models.Internal;
namespace RdtClient.Data.Models.Internal
public class Profile
{
public class Profile
{
public String Provider { get; set; }
public String UserName { get; set; }
public DateTimeOffset? Expiration { get; set; }
public String CurrentVersion { get; set; }
public String LatestVersion { get; set; }
}
}
public String Provider { get; set; }
public String UserName { get; set; }
public DateTimeOffset? Expiration { get; set; }
public String CurrentVersion { get; set; }
public String LatestVersion { get; set; }
}

View file

@ -1,26 +1,24 @@
using System;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace RdtClient.Data.Models.QBittorrent
namespace RdtClient.Data.Models.QBittorrent;
public class AppBuildInfo
{
public class AppBuildInfo
{
[JsonPropertyName("bitness")]
public Int64 Bitness { get; set; }
[JsonPropertyName("bitness")]
public Int64 Bitness { get; set; }
[JsonPropertyName("boost")]
public String Boost { get; set; }
[JsonPropertyName("boost")]
public String Boost { get; set; }
[JsonPropertyName("libtorrent")]
public String Libtorrent { get; set; }
[JsonPropertyName("libtorrent")]
public String Libtorrent { get; set; }
[JsonPropertyName("openssl")]
public String Openssl { get; set; }
[JsonPropertyName("openssl")]
public String Openssl { get; set; }
[JsonPropertyName("qt")]
public String Qt { get; set; }
[JsonPropertyName("qt")]
public String Qt { get; set; }
[JsonPropertyName("zlib")]
public String Zlib { get; set; }
}
[JsonPropertyName("zlib")]
public String Zlib { get; set; }
}

View file

@ -1,435 +1,430 @@
using System;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace RdtClient.Data.Models.QBittorrent
namespace RdtClient.Data.Models.QBittorrent;
public class AppPreferences
{
namespace QuickType
{
public class AppPreferences
{
[JsonPropertyName("add_trackers")]
public String AddTrackers { get; set; }
[JsonPropertyName("add_trackers")]
public String AddTrackers { get; set; }
[JsonPropertyName("add_trackers_enabled")]
public Boolean AddTrackersEnabled { get; set; }
[JsonPropertyName("add_trackers_enabled")]
public Boolean AddTrackersEnabled { get; set; }
[JsonPropertyName("alt_dl_limit")]
public Int64 AltDlLimit { get; set; }
[JsonPropertyName("alt_dl_limit")]
public Int64 AltDlLimit { get; set; }
[JsonPropertyName("alt_up_limit")]
public Int64 AltUpLimit { get; set; }
[JsonPropertyName("alt_up_limit")]
public Int64 AltUpLimit { get; set; }
[JsonPropertyName("alternative_webui_enabled")]
public Boolean AlternativeWebuiEnabled { get; set; }
[JsonPropertyName("alternative_webui_enabled")]
public Boolean AlternativeWebuiEnabled { get; set; }
[JsonPropertyName("alternative_webui_path")]
public String AlternativeWebuiPath { get; set; }
[JsonPropertyName("alternative_webui_path")]
public String AlternativeWebuiPath { get; set; }
[JsonPropertyName("announce_ip")]
public String AnnounceIp { get; set; }
[JsonPropertyName("announce_ip")]
public String AnnounceIp { get; set; }
[JsonPropertyName("announce_to_all_tiers")]
public Boolean AnnounceToAllTiers { get; set; }
[JsonPropertyName("announce_to_all_tiers")]
public Boolean AnnounceToAllTiers { get; set; }
[JsonPropertyName("announce_to_all_trackers")]
public Boolean AnnounceToAllTrackers { get; set; }
[JsonPropertyName("announce_to_all_trackers")]
public Boolean AnnounceToAllTrackers { get; set; }
[JsonPropertyName("anonymous_mode")]
public Boolean AnonymousMode { get; set; }
[JsonPropertyName("anonymous_mode")]
public Boolean AnonymousMode { get; set; }
[JsonPropertyName("async_io_threads")]
public Int64 AsyncIoThreads { get; set; }
[JsonPropertyName("async_io_threads")]
public Int64 AsyncIoThreads { get; set; }
[JsonPropertyName("auto_delete_mode")]
public Int64 AutoDeleteMode { get; set; }
[JsonPropertyName("auto_delete_mode")]
public Int64 AutoDeleteMode { get; set; }
[JsonPropertyName("auto_tmm_enabled")]
public Boolean AutoTmmEnabled { get; set; }
[JsonPropertyName("auto_tmm_enabled")]
public Boolean AutoTmmEnabled { get; set; }
[JsonPropertyName("autorun_enabled")]
public Boolean AutorunEnabled { get; set; }
[JsonPropertyName("autorun_enabled")]
public Boolean AutorunEnabled { get; set; }
[JsonPropertyName("autorun_program")]
public String AutorunProgram { get; set; }
[JsonPropertyName("autorun_program")]
public String AutorunProgram { get; set; }
[JsonPropertyName("banned_IPs")]
public String BannedIPs { get; set; }
[JsonPropertyName("banned_IPs")]
public String BannedIPs { get; set; }
[JsonPropertyName("bittorrent_protocol")]
public Int64 BittorrentProtocol { get; set; }
[JsonPropertyName("bittorrent_protocol")]
public Int64 BittorrentProtocol { get; set; }
[JsonPropertyName("bypass_auth_subnet_whitelist")]
public String BypassAuthSubnetWhitelist { get; set; }
[JsonPropertyName("bypass_auth_subnet_whitelist")]
public String BypassAuthSubnetWhitelist { get; set; }
[JsonPropertyName("bypass_auth_subnet_whitelist_enabled")]
public Boolean BypassAuthSubnetWhitelistEnabled { get; set; }
[JsonPropertyName("bypass_auth_subnet_whitelist_enabled")]
public Boolean BypassAuthSubnetWhitelistEnabled { get; set; }
[JsonPropertyName("bypass_local_auth")]
public Boolean BypassLocalAuth { get; set; }
[JsonPropertyName("bypass_local_auth")]
public Boolean BypassLocalAuth { get; set; }
[JsonPropertyName("category_changed_tmm_enabled")]
public Boolean CategoryChangedTmmEnabled { get; set; }
[JsonPropertyName("category_changed_tmm_enabled")]
public Boolean CategoryChangedTmmEnabled { get; set; }
[JsonPropertyName("checking_memory_use")]
public Int64 CheckingMemoryUse { get; set; }
[JsonPropertyName("checking_memory_use")]
public Int64 CheckingMemoryUse { get; set; }
[JsonPropertyName("create_subfolder_enabled")]
public Boolean CreateSubfolderEnabled { get; set; }
[JsonPropertyName("create_subfolder_enabled")]
public Boolean CreateSubfolderEnabled { get; set; }
[JsonPropertyName("current_interface_address")]
public String CurrentInterfaceAddress { get; set; }
[JsonPropertyName("current_interface_address")]
public String CurrentInterfaceAddress { get; set; }
[JsonPropertyName("current_network_interface")]
public String CurrentNetworkInterface { get; set; }
[JsonPropertyName("current_network_interface")]
public String CurrentNetworkInterface { get; set; }
[JsonPropertyName("dht")]
public Boolean Dht { get; set; }
[JsonPropertyName("dht")]
public Boolean Dht { get; set; }
[JsonPropertyName("disk_cache")]
public Int64 DiskCache { get; set; }
[JsonPropertyName("disk_cache")]
public Int64 DiskCache { get; set; }
[JsonPropertyName("disk_cache_ttl")]
public Int64 DiskCacheTtl { get; set; }
[JsonPropertyName("disk_cache_ttl")]
public Int64 DiskCacheTtl { get; set; }
[JsonPropertyName("dl_limit")]
public Int64 DlLimit { get; set; }
[JsonPropertyName("dl_limit")]
public Int64 DlLimit { get; set; }
[JsonPropertyName("dont_count_slow_torrents")]
public Boolean DontCountSlowTorrents { get; set; }
[JsonPropertyName("dont_count_slow_torrents")]
public Boolean DontCountSlowTorrents { get; set; }
[JsonPropertyName("dyndns_domain")]
public String DyndnsDomain { get; set; }
[JsonPropertyName("dyndns_domain")]
public String DyndnsDomain { get; set; }
[JsonPropertyName("dyndns_enabled")]
public Boolean DyndnsEnabled { get; set; }
[JsonPropertyName("dyndns_enabled")]
public Boolean DyndnsEnabled { get; set; }
[JsonPropertyName("dyndns_password")]
public String DyndnsPassword { get; set; }
[JsonPropertyName("dyndns_password")]
public String DyndnsPassword { get; set; }
[JsonPropertyName("dyndns_service")]
public Int64 DyndnsService { get; set; }
[JsonPropertyName("dyndns_service")]
public Int64 DyndnsService { get; set; }
[JsonPropertyName("dyndns_username")]
public String DyndnsUsername { get; set; }
[JsonPropertyName("dyndns_username")]
public String DyndnsUsername { get; set; }
[JsonPropertyName("embedded_tracker_port")]
public Int64 EmbeddedTrackerPort { get; set; }
[JsonPropertyName("embedded_tracker_port")]
public Int64 EmbeddedTrackerPort { get; set; }
[JsonPropertyName("enable_coalesce_read_write")]
public Boolean EnableCoalesceReadWrite { get; set; }
[JsonPropertyName("enable_coalesce_read_write")]
public Boolean EnableCoalesceReadWrite { get; set; }
[JsonPropertyName("enable_embedded_tracker")]
public Boolean EnableEmbeddedTracker { get; set; }
[JsonPropertyName("enable_embedded_tracker")]
public Boolean EnableEmbeddedTracker { get; set; }
[JsonPropertyName("enable_multi_connections_from_same_ip")]
public Boolean EnableMultiConnectionsFromSameIp { get; set; }
[JsonPropertyName("enable_multi_connections_from_same_ip")]
public Boolean EnableMultiConnectionsFromSameIp { get; set; }
[JsonPropertyName("enable_os_cache")]
public Boolean EnableOsCache { get; set; }
[JsonPropertyName("enable_os_cache")]
public Boolean EnableOsCache { get; set; }
[JsonPropertyName("enable_piece_extent_affinity")]
public Boolean EnablePieceExtentAffinity { get; set; }
[JsonPropertyName("enable_piece_extent_affinity")]
public Boolean EnablePieceExtentAffinity { get; set; }
[JsonPropertyName("enable_super_seeding")]
public Boolean EnableSuperSeeding { get; set; }
[JsonPropertyName("enable_super_seeding")]
public Boolean EnableSuperSeeding { get; set; }
[JsonPropertyName("enable_upload_suggestions")]
public Boolean EnableUploadSuggestions { get; set; }
[JsonPropertyName("enable_upload_suggestions")]
public Boolean EnableUploadSuggestions { get; set; }
[JsonPropertyName("encryption")]
public Int64 Encryption { get; set; }
[JsonPropertyName("encryption")]
public Int64 Encryption { get; set; }
[JsonPropertyName("export_dir")]
public String ExportDir { get; set; }
[JsonPropertyName("export_dir")]
public String ExportDir { get; set; }
[JsonPropertyName("export_dir_fin")]
public String ExportDirFin { get; set; }
[JsonPropertyName("export_dir_fin")]
public String ExportDirFin { get; set; }
[JsonPropertyName("file_pool_size")]
public Int64 FilePoolSize { get; set; }
[JsonPropertyName("file_pool_size")]
public Int64 FilePoolSize { get; set; }
[JsonPropertyName("incomplete_files_ext")]
public Boolean IncompleteFilesExt { get; set; }
[JsonPropertyName("incomplete_files_ext")]
public Boolean IncompleteFilesExt { get; set; }
[JsonPropertyName("ip_filter_enabled")]
public Boolean IpFilterEnabled { get; set; }
[JsonPropertyName("ip_filter_enabled")]
public Boolean IpFilterEnabled { get; set; }
[JsonPropertyName("ip_filter_path")]
public String IpFilterPath { get; set; }
[JsonPropertyName("ip_filter_path")]
public String IpFilterPath { get; set; }
[JsonPropertyName("ip_filter_trackers")]
public Boolean IpFilterTrackers { get; set; }
[JsonPropertyName("ip_filter_trackers")]
public Boolean IpFilterTrackers { get; set; }
[JsonPropertyName("limit_lan_peers")]
public Boolean LimitLanPeers { get; set; }
[JsonPropertyName("limit_lan_peers")]
public Boolean LimitLanPeers { get; set; }
[JsonPropertyName("limit_tcp_overhead")]
public Boolean LimitTcpOverhead { get; set; }
[JsonPropertyName("limit_tcp_overhead")]
public Boolean LimitTcpOverhead { get; set; }
[JsonPropertyName("limit_utp_rate")]
public Boolean LimitUtpRate { get; set; }
[JsonPropertyName("limit_utp_rate")]
public Boolean LimitUtpRate { get; set; }
[JsonPropertyName("listen_port")]
public Int64 ListenPort { get; set; }
[JsonPropertyName("listen_port")]
public Int64 ListenPort { get; set; }
[JsonPropertyName("locale")]
public String Locale { get; set; }
[JsonPropertyName("locale")]
public String Locale { get; set; }
[JsonPropertyName("lsd")]
public Boolean Lsd { get; set; }
[JsonPropertyName("lsd")]
public Boolean Lsd { get; set; }
[JsonPropertyName("mail_notification_auth_enabled")]
public Boolean MailNotificationAuthEnabled { get; set; }
[JsonPropertyName("mail_notification_auth_enabled")]
public Boolean MailNotificationAuthEnabled { get; set; }
[JsonPropertyName("mail_notification_email")]
public String MailNotificationEmail { get; set; }
[JsonPropertyName("mail_notification_email")]
public String MailNotificationEmail { get; set; }
[JsonPropertyName("mail_notification_enabled")]
public Boolean MailNotificationEnabled { get; set; }
[JsonPropertyName("mail_notification_enabled")]
public Boolean MailNotificationEnabled { get; set; }
[JsonPropertyName("mail_notification_password")]
public String MailNotificationPassword { get; set; }
[JsonPropertyName("mail_notification_password")]
public String MailNotificationPassword { get; set; }
[JsonPropertyName("mail_notification_sender")]
public String MailNotificationSender { get; set; }
[JsonPropertyName("mail_notification_sender")]
public String MailNotificationSender { get; set; }
[JsonPropertyName("mail_notification_smtp")]
public String MailNotificationSmtp { get; set; }
[JsonPropertyName("mail_notification_smtp")]
public String MailNotificationSmtp { get; set; }
[JsonPropertyName("mail_notification_ssl_enabled")]
public Boolean MailNotificationSslEnabled { get; set; }
[JsonPropertyName("mail_notification_ssl_enabled")]
public Boolean MailNotificationSslEnabled { get; set; }
[JsonPropertyName("mail_notification_username")]
public String MailNotificationUsername { get; set; }
[JsonPropertyName("mail_notification_username")]
public String MailNotificationUsername { get; set; }
[JsonPropertyName("max_active_downloads")]
public Int64 MaxActiveDownloads { get; set; }
[JsonPropertyName("max_active_downloads")]
public Int64 MaxActiveDownloads { get; set; }
[JsonPropertyName("max_active_torrents")]
public Int64 MaxActiveTorrents { get; set; }
[JsonPropertyName("max_active_torrents")]
public Int64 MaxActiveTorrents { get; set; }
[JsonPropertyName("max_active_uploads")]
public Int64 MaxActiveUploads { get; set; }
[JsonPropertyName("max_active_uploads")]
public Int64 MaxActiveUploads { get; set; }
[JsonPropertyName("max_connec")]
public Int64 MaxConnec { get; set; }
[JsonPropertyName("max_connec")]
public Int64 MaxConnec { get; set; }
[JsonPropertyName("max_connec_per_torrent")]
public Int64 MaxConnecPerTorrent { get; set; }
[JsonPropertyName("max_connec_per_torrent")]
public Int64 MaxConnecPerTorrent { get; set; }
[JsonPropertyName("max_ratio")]
public Int64 MaxRatio { get; set; }
[JsonPropertyName("max_ratio")]
public Int64 MaxRatio { get; set; }
[JsonPropertyName("max_ratio_act")]
public Int64 MaxRatioAct { get; set; }
[JsonPropertyName("max_ratio_act")]
public Int64 MaxRatioAct { get; set; }
[JsonPropertyName("max_ratio_enabled")]
public Boolean MaxRatioEnabled { get; set; }
[JsonPropertyName("max_ratio_enabled")]
public Boolean MaxRatioEnabled { get; set; }
[JsonPropertyName("max_seeding_time")]
public Int64 MaxSeedingTime { get; set; }
[JsonPropertyName("max_seeding_time")]
public Int64 MaxSeedingTime { get; set; }
[JsonPropertyName("max_seeding_time_enabled")]
public Boolean MaxSeedingTimeEnabled { get; set; }
[JsonPropertyName("max_seeding_time_enabled")]
public Boolean MaxSeedingTimeEnabled { get; set; }
[JsonPropertyName("max_uploads")]
public Int64 MaxUploads { get; set; }
[JsonPropertyName("max_uploads")]
public Int64 MaxUploads { get; set; }
[JsonPropertyName("max_uploads_per_torrent")]
public Int64 MaxUploadsPerTorrent { get; set; }
[JsonPropertyName("max_uploads_per_torrent")]
public Int64 MaxUploadsPerTorrent { get; set; }
[JsonPropertyName("outgoing_ports_max")]
public Int64 OutgoingPortsMax { get; set; }
[JsonPropertyName("outgoing_ports_max")]
public Int64 OutgoingPortsMax { get; set; }
[JsonPropertyName("outgoing_ports_min")]
public Int64 OutgoingPortsMin { get; set; }
[JsonPropertyName("outgoing_ports_min")]
public Int64 OutgoingPortsMin { get; set; }
[JsonPropertyName("pex")]
public Boolean Pex { get; set; }
[JsonPropertyName("pex")]
public Boolean Pex { get; set; }
[JsonPropertyName("preallocate_all")]
public Boolean PreallocateAll { get; set; }
[JsonPropertyName("preallocate_all")]
public Boolean PreallocateAll { get; set; }
[JsonPropertyName("proxy_auth_enabled")]
public Boolean ProxyAuthEnabled { get; set; }
[JsonPropertyName("proxy_auth_enabled")]
public Boolean ProxyAuthEnabled { get; set; }
[JsonPropertyName("proxy_ip")]
public String ProxyIp { get; set; }
[JsonPropertyName("proxy_ip")]
public String ProxyIp { get; set; }
[JsonPropertyName("proxy_password")]
public String ProxyPassword { get; set; }
[JsonPropertyName("proxy_password")]
public String ProxyPassword { get; set; }
[JsonPropertyName("proxy_peer_connections")]
public Boolean ProxyPeerConnections { get; set; }
[JsonPropertyName("proxy_peer_connections")]
public Boolean ProxyPeerConnections { get; set; }
[JsonPropertyName("proxy_port")]
public Int64 ProxyPort { get; set; }
[JsonPropertyName("proxy_port")]
public Int64 ProxyPort { get; set; }
[JsonPropertyName("proxy_torrents_only")]
public Boolean ProxyTorrentsOnly { get; set; }
[JsonPropertyName("proxy_torrents_only")]
public Boolean ProxyTorrentsOnly { get; set; }
[JsonPropertyName("proxy_type")]
public Int64 ProxyType { get; set; }
[JsonPropertyName("proxy_type")]
public Int64 ProxyType { get; set; }
[JsonPropertyName("proxy_username")]
public String ProxyUsername { get; set; }
[JsonPropertyName("proxy_username")]
public String ProxyUsername { get; set; }
[JsonPropertyName("queueing_enabled")]
public Boolean QueueingEnabled { get; set; }
[JsonPropertyName("queueing_enabled")]
public Boolean QueueingEnabled { get; set; }
[JsonPropertyName("random_port")]
public Boolean RandomPort { get; set; }
[JsonPropertyName("random_port")]
public Boolean RandomPort { get; set; }
[JsonPropertyName("recheck_completed_torrents")]
public Boolean RecheckCompletedTorrents { get; set; }
[JsonPropertyName("recheck_completed_torrents")]
public Boolean RecheckCompletedTorrents { get; set; }
[JsonPropertyName("resolve_peer_countries")]
public Boolean ResolvePeerCountries { get; set; }
[JsonPropertyName("resolve_peer_countries")]
public Boolean ResolvePeerCountries { get; set; }
[JsonPropertyName("rss_auto_downloading_enabled")]
public Boolean RssAutoDownloadingEnabled { get; set; }
[JsonPropertyName("rss_auto_downloading_enabled")]
public Boolean RssAutoDownloadingEnabled { get; set; }
[JsonPropertyName("rss_max_articles_per_feed")]
public Int64 RssMaxArticlesPerFeed { get; set; }
[JsonPropertyName("rss_max_articles_per_feed")]
public Int64 RssMaxArticlesPerFeed { get; set; }
[JsonPropertyName("rss_processing_enabled")]
public Boolean RssProcessingEnabled { get; set; }
[JsonPropertyName("rss_processing_enabled")]
public Boolean RssProcessingEnabled { get; set; }
[JsonPropertyName("rss_refresh_interval")]
public Int64 RssRefreshInterval { get; set; }
[JsonPropertyName("rss_refresh_interval")]
public Int64 RssRefreshInterval { get; set; }
[JsonPropertyName("save_path")]
public String SavePath { get; set; }
[JsonPropertyName("save_path")]
public String SavePath { get; set; }
[JsonPropertyName("save_path_changed_tmm_enabled")]
public Boolean SavePathChangedTmmEnabled { get; set; }
[JsonPropertyName("save_path_changed_tmm_enabled")]
public Boolean SavePathChangedTmmEnabled { get; set; }
[JsonPropertyName("save_resume_data_interval")]
public Int64 SaveResumeDataInterval { get; set; }
[JsonPropertyName("save_resume_data_interval")]
public Int64 SaveResumeDataInterval { get; set; }
[JsonPropertyName("scan_dirs")]
public ScanDirs ScanDirs { get; set; }
[JsonPropertyName("scan_dirs")]
public ScanDirs ScanDirs { get; set; }
[JsonPropertyName("schedule_from_hour")]
public Int64 ScheduleFromHour { get; set; }
[JsonPropertyName("schedule_from_hour")]
public Int64 ScheduleFromHour { get; set; }
[JsonPropertyName("schedule_from_min")]
public Int64 ScheduleFromMin { get; set; }
[JsonPropertyName("schedule_from_min")]
public Int64 ScheduleFromMin { get; set; }
[JsonPropertyName("schedule_to_hour")]
public Int64 ScheduleToHour { get; set; }
[JsonPropertyName("schedule_to_hour")]
public Int64 ScheduleToHour { get; set; }
[JsonPropertyName("schedule_to_min")]
public Int64 ScheduleToMin { get; set; }
[JsonPropertyName("schedule_to_min")]
public Int64 ScheduleToMin { get; set; }
[JsonPropertyName("scheduler_days")]
public Int64 SchedulerDays { get; set; }
[JsonPropertyName("scheduler_days")]
public Int64 SchedulerDays { get; set; }
[JsonPropertyName("scheduler_enabled")]
public Boolean SchedulerEnabled { get; set; }
[JsonPropertyName("scheduler_enabled")]
public Boolean SchedulerEnabled { get; set; }
[JsonPropertyName("send_buffer_low_watermark")]
public Int64 SendBufferLowWatermark { get; set; }
[JsonPropertyName("send_buffer_low_watermark")]
public Int64 SendBufferLowWatermark { get; set; }
[JsonPropertyName("send_buffer_watermark")]
public Int64 SendBufferWatermark { get; set; }
[JsonPropertyName("send_buffer_watermark")]
public Int64 SendBufferWatermark { get; set; }
[JsonPropertyName("send_buffer_watermark_factor")]
public Int64 SendBufferWatermarkFactor { get; set; }
[JsonPropertyName("send_buffer_watermark_factor")]
public Int64 SendBufferWatermarkFactor { get; set; }
[JsonPropertyName("slow_torrent_dl_rate_threshold")]
public Int64 SlowTorrentDlRateThreshold { get; set; }
[JsonPropertyName("slow_torrent_dl_rate_threshold")]
public Int64 SlowTorrentDlRateThreshold { get; set; }
[JsonPropertyName("slow_torrent_inactive_timer")]
public Int64 SlowTorrentInactiveTimer { get; set; }
[JsonPropertyName("slow_torrent_inactive_timer")]
public Int64 SlowTorrentInactiveTimer { get; set; }
[JsonPropertyName("slow_torrent_ul_rate_threshold")]
public Int64 SlowTorrentUlRateThreshold { get; set; }
[JsonPropertyName("slow_torrent_ul_rate_threshold")]
public Int64 SlowTorrentUlRateThreshold { get; set; }
[JsonPropertyName("socket_backlog_size")]
public Int64 SocketBacklogSize { get; set; }
[JsonPropertyName("socket_backlog_size")]
public Int64 SocketBacklogSize { get; set; }
[JsonPropertyName("start_paused_enabled")]
public Boolean StartPausedEnabled { get; set; }
[JsonPropertyName("start_paused_enabled")]
public Boolean StartPausedEnabled { get; set; }
[JsonPropertyName("stop_tracker_timeout")]
public Int64 StopTrackerTimeout { get; set; }
[JsonPropertyName("stop_tracker_timeout")]
public Int64 StopTrackerTimeout { get; set; }
[JsonPropertyName("temp_path")]
public String TempPath { get; set; }
[JsonPropertyName("temp_path")]
public String TempPath { get; set; }
[JsonPropertyName("temp_path_enabled")]
public Boolean TempPathEnabled { get; set; }
[JsonPropertyName("temp_path_enabled")]
public Boolean TempPathEnabled { get; set; }
[JsonPropertyName("torrent_changed_tmm_enabled")]
public Boolean TorrentChangedTmmEnabled { get; set; }
[JsonPropertyName("torrent_changed_tmm_enabled")]
public Boolean TorrentChangedTmmEnabled { get; set; }
[JsonPropertyName("up_limit")]
public Int64 UpLimit { get; set; }
[JsonPropertyName("up_limit")]
public Int64 UpLimit { get; set; }
[JsonPropertyName("upload_choking_algorithm")]
public Int64 UploadChokingAlgorithm { get; set; }
[JsonPropertyName("upload_choking_algorithm")]
public Int64 UploadChokingAlgorithm { get; set; }
[JsonPropertyName("upload_slots_behavior")]
public Int64 UploadSlotsBehavior { get; set; }
[JsonPropertyName("upload_slots_behavior")]
public Int64 UploadSlotsBehavior { get; set; }
[JsonPropertyName("upnp")]
public Boolean Upnp { get; set; }
[JsonPropertyName("upnp")]
public Boolean Upnp { get; set; }
[JsonPropertyName("upnp_lease_duration")]
public Int64 UpnpLeaseDuration { get; set; }
[JsonPropertyName("upnp_lease_duration")]
public Int64 UpnpLeaseDuration { get; set; }
[JsonPropertyName("use_https")]
public Boolean UseHttps { get; set; }
[JsonPropertyName("use_https")]
public Boolean UseHttps { get; set; }
[JsonPropertyName("utp_tcp_mixed_mode")]
public Int64 UtpTcpMixedMode { get; set; }
[JsonPropertyName("utp_tcp_mixed_mode")]
public Int64 UtpTcpMixedMode { get; set; }
[JsonPropertyName("web_ui_address")]
public String WebUiAddress { get; set; }
[JsonPropertyName("web_ui_address")]
public String WebUiAddress { get; set; }
[JsonPropertyName("web_ui_ban_duration")]
public Int64 WebUiBanDuration { get; set; }
[JsonPropertyName("web_ui_ban_duration")]
public Int64 WebUiBanDuration { get; set; }
[JsonPropertyName("web_ui_clickjacking_protection_enabled")]
public Boolean WebUiClickjackingProtectionEnabled { get; set; }
[JsonPropertyName("web_ui_clickjacking_protection_enabled")]
public Boolean WebUiClickjackingProtectionEnabled { get; set; }
[JsonPropertyName("web_ui_csrf_protection_enabled")]
public Boolean WebUiCsrfProtectionEnabled { get; set; }
[JsonPropertyName("web_ui_csrf_protection_enabled")]
public Boolean WebUiCsrfProtectionEnabled { get; set; }
[JsonPropertyName("web_ui_domain_list")]
public String WebUiDomainList { get; set; }
[JsonPropertyName("web_ui_domain_list")]
public String WebUiDomainList { get; set; }
[JsonPropertyName("web_ui_host_header_validation_enabled")]
public Boolean WebUiHostHeaderValidationEnabled { get; set; }
[JsonPropertyName("web_ui_host_header_validation_enabled")]
public Boolean WebUiHostHeaderValidationEnabled { get; set; }
[JsonPropertyName("web_ui_https_cert_path")]
public String WebUiHttpsCertPath { get; set; }
[JsonPropertyName("web_ui_https_cert_path")]
public String WebUiHttpsCertPath { get; set; }
[JsonPropertyName("web_ui_https_key_path")]
public String WebUiHttpsKeyPath { get; set; }
[JsonPropertyName("web_ui_https_key_path")]
public String WebUiHttpsKeyPath { get; set; }
[JsonPropertyName("web_ui_max_auth_fail_count")]
public Int64 WebUiMaxAuthFailCount { get; set; }
[JsonPropertyName("web_ui_max_auth_fail_count")]
public Int64 WebUiMaxAuthFailCount { get; set; }
[JsonPropertyName("web_ui_port")]
public Int64 WebUiPort { get; set; }
[JsonPropertyName("web_ui_port")]
public Int64 WebUiPort { get; set; }
[JsonPropertyName("web_ui_secure_cookie_enabled")]
public Boolean WebUiSecureCookieEnabled { get; set; }
[JsonPropertyName("web_ui_secure_cookie_enabled")]
public Boolean WebUiSecureCookieEnabled { get; set; }
[JsonPropertyName("web_ui_session_timeout")]
public Int64 WebUiSessionTimeout { get; set; }
[JsonPropertyName("web_ui_session_timeout")]
public Int64 WebUiSessionTimeout { get; set; }
[JsonPropertyName("web_ui_upnp")]
public Boolean WebUiUpnp { get; set; }
[JsonPropertyName("web_ui_upnp")]
public Boolean WebUiUpnp { get; set; }
[JsonPropertyName("web_ui_username")]
public String WebUiUsername { get; set; }
}
[JsonPropertyName("web_ui_username")]
public String WebUiUsername { get; set; }
}
public class ScanDirs
{
}
}
public class ScanDirs
{
}

View file

@ -1,106 +1,102 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using RdtClient.Data.Models.QBittorrent.QuickType;
using System.Text.Json.Serialization;
namespace RdtClient.Data.Models.QBittorrent
namespace RdtClient.Data.Models.QBittorrent;
public class SyncMetaData
{
public class SyncMetaData
{
[JsonPropertyName("categories")]
public IDictionary<String, TorrentCategory> Categories { get; set; }
[JsonPropertyName("categories")]
public IDictionary<String, TorrentCategory> Categories { get; set; }
[JsonPropertyName("full_update")]
public Boolean FullUpdate { get; set; }
[JsonPropertyName("full_update")]
public Boolean FullUpdate { get; set; }
[JsonPropertyName("rid")]
public Int64 Rid { get; set; }
[JsonPropertyName("rid")]
public Int64 Rid { get; set; }
[JsonPropertyName("server_state")]
public SyncMetaDataServerState ServerState { get; set; }
[JsonPropertyName("server_state")]
public SyncMetaDataServerState ServerState { get; set; }
[JsonPropertyName("tags")]
public IList<Object> Tags { get; set; }
[JsonPropertyName("tags")]
public IList<Object> Tags { get; set; }
[JsonPropertyName("torrents")]
public IDictionary<String, TorrentInfo> Torrents { get; set; }
[JsonPropertyName("torrents")]
public IDictionary<String, TorrentInfo> Torrents { get; set; }
[JsonPropertyName("trackers")]
public IDictionary<String, List<String>> Trackers { get; set; }
}
public class SyncMetaDataServerState
{
[JsonPropertyName("alltime_dl")]
public Int64 AlltimeDl { get; set; }
[JsonPropertyName("alltime_ul")]
public Int64 AlltimeUl { get; set; }
[JsonPropertyName("average_time_queue")]
public Int64 AverageTimeQueue { get; set; }
[JsonPropertyName("connection_status")]
public String ConnectionStatus { get; set; }
[JsonPropertyName("dht_nodes")]
public Int64 DhtNodes { get; set; }
[JsonPropertyName("dl_info_data")]
public Int64 DlInfoData { get; set; }
[JsonPropertyName("dl_info_speed")]
public Int64 DlInfoSpeed { get; set; }
[JsonPropertyName("dl_rate_limit")]
public Int64 DlRateLimit { get; set; }
[JsonPropertyName("free_space_on_disk")]
public Int64 FreeSpaceOnDisk { get; set; }
[JsonPropertyName("global_ratio")]
public String GlobalRatio { get; set; }
[JsonPropertyName("queued_io_jobs")]
public Int64 QueuedIoJobs { get; set; }
[JsonPropertyName("queueing")]
public Boolean Queueing { get; set; }
[JsonPropertyName("read_cache_hits")]
public String ReadCacheHits { get; set; }
[JsonPropertyName("read_cache_overload")]
public String ReadCacheOverload { get; set; }
[JsonPropertyName("refresh_interval")]
public Int64 RefreshInterval { get; set; }
[JsonPropertyName("total_buffers_size")]
public Int64 TotalBuffersSize { get; set; }
[JsonPropertyName("total_peer_connections")]
public Int64 TotalPeerConnections { get; set; }
[JsonPropertyName("total_queued_size")]
public Int64 TotalQueuedSize { get; set; }
[JsonPropertyName("total_wasted_session")]
public Int64 TotalWastedSession { get; set; }
[JsonPropertyName("up_info_data")]
public Int64 UpInfoData { get; set; }
[JsonPropertyName("up_info_speed")]
public Int64 UpInfoSpeed { get; set; }
[JsonPropertyName("up_rate_limit")]
public Int64 UpRateLimit { get; set; }
[JsonPropertyName("use_alt_speed_limits")]
public Boolean UseAltSpeedLimits { get; set; }
[JsonPropertyName("write_cache_overload")]
public String WriteCacheOverload { get; set; }
}
[JsonPropertyName("trackers")]
public IDictionary<String, List<String>> Trackers { get; set; }
}
public class SyncMetaDataServerState
{
[JsonPropertyName("alltime_dl")]
public Int64 AlltimeDl { get; set; }
[JsonPropertyName("alltime_ul")]
public Int64 AlltimeUl { get; set; }
[JsonPropertyName("average_time_queue")]
public Int64 AverageTimeQueue { get; set; }
[JsonPropertyName("connection_status")]
public String ConnectionStatus { get; set; }
[JsonPropertyName("dht_nodes")]
public Int64 DhtNodes { get; set; }
[JsonPropertyName("dl_info_data")]
public Int64 DlInfoData { get; set; }
[JsonPropertyName("dl_info_speed")]
public Int64 DlInfoSpeed { get; set; }
[JsonPropertyName("dl_rate_limit")]
public Int64 DlRateLimit { get; set; }
[JsonPropertyName("free_space_on_disk")]
public Int64 FreeSpaceOnDisk { get; set; }
[JsonPropertyName("global_ratio")]
public String GlobalRatio { get; set; }
[JsonPropertyName("queued_io_jobs")]
public Int64 QueuedIoJobs { get; set; }
[JsonPropertyName("queueing")]
public Boolean Queueing { get; set; }
[JsonPropertyName("read_cache_hits")]
public String ReadCacheHits { get; set; }
[JsonPropertyName("read_cache_overload")]
public String ReadCacheOverload { get; set; }
[JsonPropertyName("refresh_interval")]
public Int64 RefreshInterval { get; set; }
[JsonPropertyName("total_buffers_size")]
public Int64 TotalBuffersSize { get; set; }
[JsonPropertyName("total_peer_connections")]
public Int64 TotalPeerConnections { get; set; }
[JsonPropertyName("total_queued_size")]
public Int64 TotalQueuedSize { get; set; }
[JsonPropertyName("total_wasted_session")]
public Int64 TotalWastedSession { get; set; }
[JsonPropertyName("up_info_data")]
public Int64 UpInfoData { get; set; }
[JsonPropertyName("up_info_speed")]
public Int64 UpInfoSpeed { get; set; }
[JsonPropertyName("up_rate_limit")]
public Int64 UpRateLimit { get; set; }
[JsonPropertyName("use_alt_speed_limits")]
public Boolean UseAltSpeedLimits { get; set; }
[JsonPropertyName("write_cache_overload")]
public String WriteCacheOverload { get; set; }
}

View file

@ -1,14 +1,12 @@
using System;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace RdtClient.Data.Models.QBittorrent
namespace RdtClient.Data.Models.QBittorrent;
public class TorrentCategory
{
public class TorrentCategory
{
[JsonPropertyName("name")]
public String Name { get; set; }
[JsonPropertyName("name")]
public String Name { get; set; }
[JsonPropertyName("savePath")]
public String SavePath { get; set; }
}
}
[JsonPropertyName("savePath")]
public String SavePath { get; set; }
}

View file

@ -1,11 +1,9 @@
using System;
using System.Text.Json.Serialization;
namespace RdtClient.Data.Models.QBittorrent
namespace RdtClient.Data.Models.QBittorrent;
public class TorrentFileItem
{
public class TorrentFileItem
{
[JsonPropertyName("name")]
public String Name { get; set; }
}
}
[JsonPropertyName("name")]
public String Name { get; set; }
}

View file

@ -1,143 +1,138 @@
using System;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace RdtClient.Data.Models.QBittorrent
namespace RdtClient.Data.Models.QBittorrent;
public class TorrentInfo
{
namespace QuickType
{
public class TorrentInfo
{
[JsonPropertyName("added_on")]
public Int64 AddedOn { get; set; }
[JsonPropertyName("added_on")]
public Int64 AddedOn { get; set; }
[JsonPropertyName("amount_left")]
public Int64 AmountLeft { get; set; }
[JsonPropertyName("amount_left")]
public Int64 AmountLeft { get; set; }
[JsonPropertyName("auto_tmm")]
public Boolean AutoTmm { get; set; }
[JsonPropertyName("auto_tmm")]
public Boolean AutoTmm { get; set; }
[JsonPropertyName("availability")]
public Decimal Availability { get; set; }
[JsonPropertyName("availability")]
public Decimal Availability { get; set; }
[JsonPropertyName("category")]
public String Category { get; set; }
[JsonPropertyName("category")]
public String Category { get; set; }
[JsonPropertyName("completed")]
public Int64 Completed { get; set; }
[JsonPropertyName("completed")]
public Int64 Completed { get; set; }
[JsonPropertyName("completion_on")]
public Int64? CompletionOn { get; set; }
[JsonPropertyName("completion_on")]
public Int64? CompletionOn { get; set; }
[JsonPropertyName("content_path")]
public String ContentPath { get; set; }
[JsonPropertyName("content_path")]
public String ContentPath { get; set; }
[JsonPropertyName("dl_limit")]
public Int64 DlLimit { get; set; }
[JsonPropertyName("dl_limit")]
public Int64 DlLimit { get; set; }
[JsonPropertyName("dlspeed")]
public Int64 Dlspeed { get; set; }
[JsonPropertyName("dlspeed")]
public Int64 Dlspeed { get; set; }
[JsonPropertyName("downloaded")]
public Int64 Downloaded { get; set; }
[JsonPropertyName("downloaded")]
public Int64 Downloaded { get; set; }
[JsonPropertyName("downloaded_session")]
public Int64 DownloadedSession { get; set; }
[JsonPropertyName("downloaded_session")]
public Int64 DownloadedSession { get; set; }
[JsonPropertyName("eta")]
public Int64 Eta { get; set; }
[JsonPropertyName("eta")]
public Int64 Eta { get; set; }
[JsonPropertyName("f_l_piece_prio")]
public Boolean FlPiecePrio { get; set; }
[JsonPropertyName("f_l_piece_prio")]
public Boolean FlPiecePrio { get; set; }
[JsonPropertyName("force_start")]
public Boolean ForceStart { get; set; }
[JsonPropertyName("force_start")]
public Boolean ForceStart { get; set; }
[JsonPropertyName("hash")]
public String Hash { get; set; }
[JsonPropertyName("hash")]
public String Hash { get; set; }
[JsonPropertyName("last_activity")]
public Int64 LastActivity { get; set; }
[JsonPropertyName("last_activity")]
public Int64 LastActivity { get; set; }
[JsonPropertyName("magnet_uri")]
public String MagnetUri { get; set; }
[JsonPropertyName("magnet_uri")]
public String MagnetUri { get; set; }
[JsonPropertyName("max_ratio")]
public Int64 MaxRatio { get; set; }
[JsonPropertyName("max_ratio")]
public Int64 MaxRatio { get; set; }
[JsonPropertyName("max_seeding_time")]
public Int64 MaxSeedingTime { get; set; }
[JsonPropertyName("max_seeding_time")]
public Int64 MaxSeedingTime { get; set; }
[JsonPropertyName("name")]
public String Name { get; set; }
[JsonPropertyName("name")]
public String Name { get; set; }
[JsonPropertyName("num_complete")]
public Int64 NumComplete { get; set; }
[JsonPropertyName("num_complete")]
public Int64 NumComplete { get; set; }
[JsonPropertyName("num_incomplete")]
public Int64 NumIncomplete { get; set; }
[JsonPropertyName("num_incomplete")]
public Int64 NumIncomplete { get; set; }
[JsonPropertyName("num_leechs")]
public Int64 NumLeechs { get; set; }
[JsonPropertyName("num_leechs")]
public Int64 NumLeechs { get; set; }
[JsonPropertyName("num_seeds")]
public Int64 NumSeeds { get; set; }
[JsonPropertyName("num_seeds")]
public Int64 NumSeeds { get; set; }
[JsonPropertyName("priority")]
public Int64 Priority { get; set; }
[JsonPropertyName("priority")]
public Int64 Priority { get; set; }
[JsonPropertyName("progress")]
public Single Progress { get; set; }
[JsonPropertyName("progress")]
public Single Progress { get; set; }
[JsonPropertyName("ratio")]
public Int64 Ratio { get; set; }
[JsonPropertyName("ratio")]
public Int64 Ratio { get; set; }
[JsonPropertyName("ratio_limit")]
public Int64 RatioLimit { get; set; }
[JsonPropertyName("ratio_limit")]
public Int64 RatioLimit { get; set; }
[JsonPropertyName("save_path")]
public String SavePath { get; set; }
[JsonPropertyName("save_path")]
public String SavePath { get; set; }
[JsonPropertyName("seeding_time_limit")]
public Int64 SeedingTimeLimit { get; set; }
[JsonPropertyName("seeding_time_limit")]
public Int64 SeedingTimeLimit { get; set; }
[JsonPropertyName("seen_complete")]
public Int64 SeenComplete { get; set; }
[JsonPropertyName("seen_complete")]
public Int64 SeenComplete { get; set; }
[JsonPropertyName("seq_dl")]
public Boolean SeqDl { get; set; }
[JsonPropertyName("seq_dl")]
public Boolean SeqDl { get; set; }
[JsonPropertyName("size")]
public Int64 Size { get; set; }
[JsonPropertyName("size")]
public Int64 Size { get; set; }
[JsonPropertyName("state")]
public String State { get; set; }
[JsonPropertyName("state")]
public String State { get; set; }
[JsonPropertyName("super_seeding")]
public Boolean SuperSeeding { get; set; }
[JsonPropertyName("super_seeding")]
public Boolean SuperSeeding { get; set; }
[JsonPropertyName("tags")]
public String Tags { get; set; }
[JsonPropertyName("tags")]
public String Tags { get; set; }
[JsonPropertyName("time_active")]
public Int64 TimeActive { get; set; }
[JsonPropertyName("time_active")]
public Int64 TimeActive { get; set; }
[JsonPropertyName("total_size")]
public Int64 TotalSize { get; set; }
[JsonPropertyName("total_size")]
public Int64 TotalSize { get; set; }
[JsonPropertyName("tracker")]
public String Tracker { get; set; }
[JsonPropertyName("tracker")]
public String Tracker { get; set; }
[JsonPropertyName("up_limit")]
public Int64 UpLimit { get; set; }
[JsonPropertyName("up_limit")]
public Int64 UpLimit { get; set; }
[JsonPropertyName("uploaded")]
public Int64 Uploaded { get; set; }
[JsonPropertyName("uploaded")]
public Int64 Uploaded { get; set; }
[JsonPropertyName("uploaded_session")]
public Int64 UploadedSession { get; set; }
[JsonPropertyName("uploaded_session")]
public Int64 UploadedSession { get; set; }
[JsonPropertyName("upspeed")]
public Int64 Upspeed { get; set; }
}
}
[JsonPropertyName("upspeed")]
public Int64 Upspeed { get; set; }
}

View file

@ -1,107 +1,105 @@
using System;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace RdtClient.Data.Models.QBittorrent
namespace RdtClient.Data.Models.QBittorrent;
public class TorrentProperties
{
public class TorrentProperties
{
[JsonPropertyName("addition_date")]
public Int64 AdditionDate { get; set; }
[JsonPropertyName("addition_date")]
public Int64 AdditionDate { get; set; }
[JsonPropertyName("comment")]
public String Comment { get; set; }
[JsonPropertyName("comment")]
public String Comment { get; set; }
[JsonPropertyName("completion_date")]
public Int64 CompletionDate { get; set; }
[JsonPropertyName("completion_date")]
public Int64 CompletionDate { get; set; }
[JsonPropertyName("created_by")]
public String CreatedBy { get; set; }
[JsonPropertyName("created_by")]
public String CreatedBy { get; set; }
[JsonPropertyName("creation_date")]
public Int64 CreationDate { get; set; }
[JsonPropertyName("creation_date")]
public Int64 CreationDate { get; set; }
[JsonPropertyName("dl_limit")]
public Int64 DlLimit { get; set; }
[JsonPropertyName("dl_limit")]
public Int64 DlLimit { get; set; }
[JsonPropertyName("dl_speed")]
public Int64 DlSpeed { get; set; }
[JsonPropertyName("dl_speed")]
public Int64 DlSpeed { get; set; }
[JsonPropertyName("dl_speed_avg")]
public Int64 DlSpeedAvg { get; set; }
[JsonPropertyName("dl_speed_avg")]
public Int64 DlSpeedAvg { get; set; }
[JsonPropertyName("eta")]
public Int64 Eta { get; set; }
[JsonPropertyName("eta")]
public Int64 Eta { get; set; }
[JsonPropertyName("last_seen")]
public Int64 LastSeen { get; set; }
[JsonPropertyName("last_seen")]
public Int64 LastSeen { get; set; }
[JsonPropertyName("nb_connections")]
public Int64 NbConnections { get; set; }
[JsonPropertyName("nb_connections")]
public Int64 NbConnections { get; set; }
[JsonPropertyName("nb_connections_limit")]
public Int64 NbConnectionsLimit { get; set; }
[JsonPropertyName("nb_connections_limit")]
public Int64 NbConnectionsLimit { get; set; }
[JsonPropertyName("peers")]
public Int64 Peers { get; set; }
[JsonPropertyName("peers")]
public Int64 Peers { get; set; }
[JsonPropertyName("peers_total")]
public Int64 PeersTotal { get; set; }
[JsonPropertyName("peers_total")]
public Int64 PeersTotal { get; set; }
[JsonPropertyName("piece_size")]
public Int64 PieceSize { get; set; }
[JsonPropertyName("piece_size")]
public Int64 PieceSize { get; set; }
[JsonPropertyName("pieces_have")]
public Int64 PiecesHave { get; set; }
[JsonPropertyName("pieces_have")]
public Int64 PiecesHave { get; set; }
[JsonPropertyName("pieces_num")]
public Int64 PiecesNum { get; set; }
[JsonPropertyName("pieces_num")]
public Int64 PiecesNum { get; set; }
[JsonPropertyName("reannounce")]
public Int64 Reannounce { get; set; }
[JsonPropertyName("reannounce")]
public Int64 Reannounce { get; set; }
[JsonPropertyName("save_path")]
public String SavePath { get; set; }
[JsonPropertyName("save_path")]
public String SavePath { get; set; }
[JsonPropertyName("seeding_time")]
public Int64 SeedingTime { get; set; }
[JsonPropertyName("seeding_time")]
public Int64 SeedingTime { get; set; }
[JsonPropertyName("seeds")]
public Int64 Seeds { get; set; }
[JsonPropertyName("seeds")]
public Int64 Seeds { get; set; }
[JsonPropertyName("seeds_total")]
public Int64 SeedsTotal { get; set; }
[JsonPropertyName("seeds_total")]
public Int64 SeedsTotal { get; set; }
[JsonPropertyName("share_ratio")]
public Int64 ShareRatio { get; set; }
[JsonPropertyName("share_ratio")]
public Int64 ShareRatio { get; set; }
[JsonPropertyName("time_elapsed")]
public Int64 TimeElapsed { get; set; }
[JsonPropertyName("time_elapsed")]
public Int64 TimeElapsed { get; set; }
[JsonPropertyName("total_downloaded")]
public Int64 TotalDownloaded { get; set; }
[JsonPropertyName("total_downloaded")]
public Int64 TotalDownloaded { get; set; }
[JsonPropertyName("total_downloaded_session")]
public Int64 TotalDownloadedSession { get; set; }
[JsonPropertyName("total_downloaded_session")]
public Int64 TotalDownloadedSession { get; set; }
[JsonPropertyName("total_size")]
public Int64 TotalSize { get; set; }
[JsonPropertyName("total_size")]
public Int64 TotalSize { get; set; }
[JsonPropertyName("total_uploaded")]
public Int64 TotalUploaded { get; set; }
[JsonPropertyName("total_uploaded")]
public Int64 TotalUploaded { get; set; }
[JsonPropertyName("total_uploaded_session")]
public Int64 TotalUploadedSession { get; set; }
[JsonPropertyName("total_uploaded_session")]
public Int64 TotalUploadedSession { get; set; }
[JsonPropertyName("total_wasted")]
public Int64 TotalWasted { get; set; }
[JsonPropertyName("total_wasted")]
public Int64 TotalWasted { get; set; }
[JsonPropertyName("up_limit")]
public Int64 UpLimit { get; set; }
[JsonPropertyName("up_limit")]
public Int64 UpLimit { get; set; }
[JsonPropertyName("up_speed")]
public Int64 UpSpeed { get; set; }
[JsonPropertyName("up_speed")]
public Int64 UpSpeed { get; set; }
[JsonPropertyName("up_speed_avg")]
public Int64 UpSpeedAvg { get; set; }
}
[JsonPropertyName("up_speed_avg")]
public Int64 UpSpeedAvg { get; set; }
}

View file

@ -1,11 +1,8 @@
using System;
namespace RdtClient.Data.Models.TorrentClient;
namespace RdtClient.Data.Models.TorrentClient
public class TorrentClientAvailableFile
{
public class TorrentClientAvailableFile
{
public String Filename { get; set; }
public String Filename { get; set; }
public Int64 Filesize { get; set; }
}
}
public Int64 Filesize { get; set; }
}

View file

@ -1,12 +1,9 @@
using System;
namespace RdtClient.Data.Models.TorrentClient;
namespace RdtClient.Data.Models.TorrentClient
public class TorrentClientFile
{
public class TorrentClientFile
{
public Int64 Id { get; set; }
public String Path { get; set; }
public Int64 Bytes { get; set; }
public Boolean Selected { get; set; }
}
}
public Int64 Id { get; set; }
public String Path { get; set; }
public Int64 Bytes { get; set; }
public Boolean Selected { get; set; }
}

View file

@ -1,26 +1,22 @@
using System;
using System.Collections.Generic;
namespace RdtClient.Data.Models.TorrentClient;
namespace RdtClient.Data.Models.TorrentClient
public class TorrentClientTorrent
{
public class TorrentClientTorrent
{
public String Id { get; set; }
public String Filename { get; set; }
public String OriginalFilename { get; set; }
public String Hash { get; set; }
public Int64 Bytes { get; set; }
public Int64 OriginalBytes { get; set; }
public String Host { get; set; }
public Int64 Split { get; set; }
public Int64 Progress { get; set; }
public String Status { get; set; }
public Int64 StatusCode { get; set; }
public DateTimeOffset Added { get; set; }
public List<TorrentClientFile> Files { get; set; }
public List<String> Links { get; set; }
public DateTimeOffset? Ended { get; set; }
public Int64? Speed { get; set; }
public Int64? Seeders { get; set; }
}
}
public String Id { get; set; }
public String Filename { get; set; }
public String OriginalFilename { get; set; }
public String Hash { get; set; }
public Int64 Bytes { get; set; }
public Int64 OriginalBytes { get; set; }
public String Host { get; set; }
public Int64 Split { get; set; }
public Int64 Progress { get; set; }
public String Status { get; set; }
public Int64 StatusCode { get; set; }
public DateTimeOffset Added { get; set; }
public List<TorrentClientFile> Files { get; set; }
public List<String> Links { get; set; }
public DateTimeOffset? Ended { get; set; }
public Int64? Speed { get; set; }
public Int64? Seeders { get; set; }
}

View file

@ -1,10 +1,7 @@
using System;
namespace RdtClient.Data.Models.TorrentClient;
namespace RdtClient.Data.Models.TorrentClient
public class TorrentClientUser
{
public class TorrentClientUser
{
public String Username { get; set; }
public DateTimeOffset? Expiration { get; set; }
}
}
public String Username { get; set; }
public DateTimeOffset? Expiration { get; set; }
}

View file

@ -2,18 +2,21 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>disable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.3" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="6.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.3">
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.4" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="6.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog" Version="2.11.0" />
</ItemGroup>
</Project>

View file

@ -2,23 +2,24 @@
using RdtClient.Service.Services;
using RdtClient.Service.Services.TorrentClients;
namespace RdtClient.Service
namespace RdtClient.Service;
public static class DiConfig
{
public static class DiConfig
public static void Config(IServiceCollection services)
{
public static void Config(IServiceCollection services)
{
services.AddScoped<AllDebridTorrentClient>();
services.AddScoped<Authentication>();
services.AddScoped<Downloads>();
services.AddScoped<QBittorrent>();
services.AddScoped<RemoteService>();
services.AddScoped<RealDebridTorrentClient>();
services.AddScoped<Settings>();
services.AddScoped<Torrents>();
services.AddScoped<TorrentRunner>();
services.AddScoped<AllDebridTorrentClient>();
services.AddScoped<Authentication>();
services.AddScoped<Downloads>();
services.AddScoped<QBittorrent>();
services.AddScoped<RemoteService>();
services.AddScoped<RealDebridTorrentClient>();
services.AddScoped<Settings>();
services.AddScoped<Torrents>();
services.AddScoped<TorrentRunner>();
services.AddHostedService<Startup>();
}
services.AddHostedService<Startup>();
services.AddHostedService<TaskRunner>();
services.AddHostedService<UpdateChecker>();
}
}

View file

@ -1,37 +1,33 @@
using System;
using System.IO;
using System.Linq;
using System.Web;
using System.Web;
using RdtClient.Data.Models.Data;
namespace RdtClient.Service.Helpers
namespace RdtClient.Service.Helpers;
public static class DownloadHelper
{
public static class DownloadHelper
public static String GetDownloadPath(String downloadPath, Torrent torrent, Download download)
{
public static String GetDownloadPath(String downloadPath, Torrent torrent, Download download)
var fileUrl = download.Link;
if (String.IsNullOrWhiteSpace(fileUrl))
{
var fileUrl = download.Link;
if (String.IsNullOrWhiteSpace(fileUrl))
{
return null;
}
var uri = new Uri(fileUrl);
var torrentPath = Path.Combine(downloadPath, torrent.RdName);
if (!Directory.Exists(torrentPath))
{
Directory.CreateDirectory(torrentPath);
}
var fileName = uri.Segments.Last();
fileName = HttpUtility.UrlDecode(fileName);
var filePath = Path.Combine(torrentPath, fileName);
return filePath;
return null;
}
var uri = new Uri(fileUrl);
var torrentPath = Path.Combine(downloadPath, torrent.RdName);
if (!Directory.Exists(torrentPath))
{
Directory.CreateDirectory(torrentPath);
}
var fileName = uri.Segments.Last();
fileName = HttpUtility.UrlDecode(fileName);
var filePath = Path.Combine(torrentPath, fileName);
return filePath;
}
}
}

View file

@ -1,45 +1,40 @@
using System;
using System.IO;
using System.Threading.Tasks;
namespace RdtClient.Service.Helpers;
namespace RdtClient.Service.Helpers
public static class FileHelper
{
public static class FileHelper
public static async Task Delete(String path)
{
public static async Task Delete(String path)
if (String.IsNullOrWhiteSpace(path))
{
if (String.IsNullOrWhiteSpace(path))
return;
}
if (!File.Exists(path))
{
return;
}
var retry = 0;
while (true)
{
try
{
return;
File.Delete(path);
break;
}
if (!File.Exists(path))
catch
{
return;
}
var retry = 0;
while (true)
{
try
if (retry >= 3)
{
File.Delete(path);
break;
throw;
}
catch
{
if (retry >= 3)
{
throw;
}
retry++;
retry++;
await Task.Delay(1000 * retry);
}
await Task.Delay(1000 * retry);
}
}
}
}
}

View file

@ -1,33 +1,30 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace RdtClient.Service.Helpers
namespace RdtClient.Service.Helpers;
public class JsonModelBinder : IModelBinder
{
public class JsonModelBinder : IModelBinder
public Task BindModelAsync(ModelBindingContext bindingContext)
{
public Task BindModelAsync(ModelBindingContext bindingContext)
if (bindingContext == null)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult != ValueProviderResult.None)
{
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
var valueAsString = valueProviderResult.FirstValue;
var result = Newtonsoft.Json.JsonConvert.DeserializeObject(valueAsString, bindingContext.ModelType);
if (result != null)
{
bindingContext.Result = ModelBindingResult.Success(result);
return Task.CompletedTask;
}
}
return Task.CompletedTask;
throw new ArgumentNullException(nameof(bindingContext));
}
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult != ValueProviderResult.None)
{
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
var valueAsString = valueProviderResult.FirstValue;
var result = Newtonsoft.Json.JsonConvert.DeserializeObject(valueAsString, bindingContext.ModelType);
if (result != null)
{
bindingContext.Result = ModelBindingResult.Success(result);
return Task.CompletedTask;
}
}
return Task.CompletedTask;
}
}
}

View file

@ -1,31 +1,28 @@
using System;
using System.Linq;
using System.Web;
using System.Web;
using RdtClient.Data.Models.Data;
namespace RdtClient.Service.Helpers
namespace RdtClient.Service.Helpers;
public static class Logger
{
public static class Logger
public static String ToLog(this Download download)
{
public static String ToLog(this Download download)
var fileName = download.Path;
if (!String.IsNullOrWhiteSpace(download.Link))
{
var fileName = download.Path;
if (!String.IsNullOrWhiteSpace(download.Link))
{
var uri = new Uri(download.Link);
fileName = uri.Segments.Last();
fileName = HttpUtility.UrlDecode(fileName);
}
var done = (Int32)((Double)download.BytesDone / download.BytesTotal) * 100;
return $"for download {fileName} {done}% ({download.DownloadId})";
var uri = new Uri(download.Link);
fileName = uri.Segments.Last();
fileName = HttpUtility.UrlDecode(fileName);
}
public static String ToLog(this Torrent torrent)
{
return $"for torrent {torrent.RdName} ({torrent.RdId} - {torrent.RdStatusRaw} {torrent.RdProgress}%) ({torrent.TorrentId})";
}
var done = (Int32)((Double)download.BytesDone / download.BytesTotal) * 100;
return $"for download {fileName} {done}% ({download.DownloadId})";
}
}
public static String ToLog(this Torrent torrent)
{
return $"for torrent {torrent.RdName} ({torrent.RdId} - {torrent.RdStatusRaw} {torrent.RdProgress}%) ({torrent.TorrentId})";
}
}

View file

@ -1,34 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.Data;
namespace RdtClient.Service.Helpers
namespace RdtClient.Service.Helpers;
public static class SettingsHelper
{
public static class SettingsHelper
public static String GetString(this IList<Setting> settings, String key)
{
public static String GetString(this IList<Setting> settings, String key)
var setting = settings.FirstOrDefault(m => m.SettingId == key);
if (setting == null)
{
var setting = settings.FirstOrDefault(m => m.SettingId == key);
if (setting == null)
{
throw new Exception($"Setting with key {key} not found");
}
return setting.Value;
throw new Exception($"Setting with key {key} not found");
}
public static Int32 GetNumber(this IList<Setting> settings, String key)
{
var setting = settings.FirstOrDefault(m => m.SettingId == key);
if (setting == null)
{
throw new Exception($"Setting with key {key} not found");
}
return Int32.Parse(setting.Value);
}
return setting.Value;
}
}
public static Int32 GetNumber(this IList<Setting> settings, String key)
{
var setting = settings.FirstOrDefault(m => m.SettingId == key);
if (setting == null)
{
throw new Exception($"Setting with key {key} not found");
}
return Int32.Parse(setting.Value);
}
}

View file

@ -1,32 +1,29 @@
using System;
using System.Net;
using System.Threading.Tasks;
using System.Net;
using Microsoft.AspNetCore.Http;
namespace RdtClient.Service.Middleware
namespace RdtClient.Service.Middleware;
public class AuthorizeMiddleware
{
public class AuthorizeMiddleware
private readonly RequestDelegate _next;
public AuthorizeMiddleware(RequestDelegate next)
{
private readonly RequestDelegate _next;
_next = next;
}
public AuthorizeMiddleware(RequestDelegate next)
/// <summary>
/// Return a 403 instead of a 401, it's quirk that QBittorrent has.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Invoke(HttpContext context)
{
await _next(context);
if (context.Response.StatusCode == (Int32) HttpStatusCode.Unauthorized)
{
_next = next;
}
/// <summary>
/// Return a 403 instead of a 401, it's quirk that QBittorrent has.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Invoke(HttpContext context)
{
await _next(context);
if (context.Response.StatusCode == (Int32) HttpStatusCode.Unauthorized)
{
context.Response.StatusCode = (Int32) HttpStatusCode.Forbidden;
}
context.Response.StatusCode = (Int32) HttpStatusCode.Forbidden;
}
}
}

View file

@ -1,29 +1,27 @@
using System;
using System.Net;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
namespace RdtClient.Service.Middleware
{
public static class ExceptionMiddlewareExtensions
{
public static void ConfigureExceptionHandler(this IApplicationBuilder app)
{
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
context.Response.StatusCode = (Int32) HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";
namespace RdtClient.Service.Middleware;
var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
if (contextFeature != null)
{
await context.Response.WriteAsync(contextFeature.Error.Message);
}
});
public static class ExceptionMiddlewareExtensions
{
public static void ConfigureExceptionHandler(this IApplicationBuilder app)
{
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
context.Response.StatusCode = (Int32) HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";
var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
if (contextFeature != null)
{
await context.Response.WriteAsync(contextFeature.Error.Message);
}
});
}
});
}
}

View file

@ -1,20 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<OutputType>library</OutputType>
<Nullable>disable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="AllDebrid.NET" Version="1.0.7" />
<PackageReference Include="Aria2.NET" Version="1.0.4" />
<PackageReference Include="Downloader" Version="2.3.3" />
<PackageReference Include="MonoTorrent" Version="2.0.4" />
<PackageReference Include="MonoTorrent" Version="2.0.5" />
<PackageReference Include="RD.NET" Version="2.1.2" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog" Version="2.11.0" />
<PackageReference Include="Serilog.Exceptions" Version="8.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="SharpCompress" Version="0.30.1" />
<PackageReference Include="SharpCompress" Version="0.31.0" />
</ItemGroup>
<ItemGroup>

View file

@ -1,79 +1,76 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity;
using RdtClient.Data.Data;
namespace RdtClient.Service.Services
namespace RdtClient.Service.Services;
public class Authentication
{
public class Authentication
private readonly SignInManager<IdentityUser> _signInManager;
private readonly UserManager<IdentityUser> _userManager;
private readonly UserData _userData;
public Authentication(SignInManager<IdentityUser> signInManager, UserManager<IdentityUser> userManager, UserData userData)
{
private readonly SignInManager<IdentityUser> _signInManager;
private readonly UserManager<IdentityUser> _userManager;
private readonly UserData _userData;
public Authentication(SignInManager<IdentityUser> signInManager, UserManager<IdentityUser> userManager, UserData userData)
{
_signInManager = signInManager;
_userManager = userManager;
_userData = userData;
}
public async Task<IdentityResult> Register(String userName, String password)
{
var user = new IdentityUser(userName);
var result = await _userManager.CreateAsync(user, password);
return result;
}
public async Task<SignInResult> Login(String userName, String password)
{
if (String.IsNullOrWhiteSpace(userName) || String.IsNullOrWhiteSpace(password))
{
return SignInResult.Failed;
}
var result = await _signInManager.PasswordSignInAsync(userName, password, true, false);
return result;
}
public async Task<IdentityUser> GetUser()
{
return await _userData.GetUser();
}
public async Task Logout()
{
await _signInManager.SignOutAsync();
}
public async Task<IdentityResult> Update(String newUserName, String newPassword)
{
var user = await GetUser();
if (user == null)
{
throw new Exception("No logged in user found");
}
if (!String.IsNullOrWhiteSpace(newUserName))
{
user.UserName = newUserName;
}
await _userManager.UpdateAsync(user);
if (!String.IsNullOrWhiteSpace(newPassword))
{
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
var result = await _userManager.ResetPasswordAsync(user, token, newPassword);
return result;
}
return IdentityResult.Success;
}
_signInManager = signInManager;
_userManager = userManager;
_userData = userData;
}
}
public async Task<IdentityResult> Register(String userName, String password)
{
var user = new IdentityUser(userName);
var result = await _userManager.CreateAsync(user, password);
return result;
}
public async Task<SignInResult> Login(String userName, String password)
{
if (String.IsNullOrWhiteSpace(userName) || String.IsNullOrWhiteSpace(password))
{
return SignInResult.Failed;
}
var result = await _signInManager.PasswordSignInAsync(userName, password, true, false);
return result;
}
public async Task<IdentityUser> GetUser()
{
return await _userData.GetUser();
}
public async Task Logout()
{
await _signInManager.SignOutAsync();
}
public async Task<IdentityResult> Update(String newUserName, String newPassword)
{
var user = await GetUser();
if (user == null)
{
throw new Exception("No logged in user found");
}
if (!String.IsNullOrWhiteSpace(newUserName))
{
user.UserName = newUserName;
}
await _userManager.UpdateAsync(user);
if (!String.IsNullOrWhiteSpace(newPassword))
{
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
var result = await _userManager.ResetPasswordAsync(user, token, newPassword);
return result;
}
return IdentityResult.Success;
}
}

View file

@ -1,126 +1,123 @@
using System;
using System.Threading.Tasks;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.Internal;
using RdtClient.Service.Helpers;
using RdtClient.Service.Services.Downloaders;
namespace RdtClient.Service.Services
namespace RdtClient.Service.Services;
public class DownloadClient
{
public class DownloadClient
private readonly String _destinationPath;
private readonly Download _download;
private readonly Torrent _torrent;
public IDownloader Downloader;
public DownloadClient(Download download, Torrent torrent, String destinationPath)
{
private readonly String _destinationPath;
_download = download;
_torrent = torrent;
_destinationPath = destinationPath;
}
private readonly Download _download;
private readonly Torrent _torrent;
public String Type { get; set; }
public IDownloader Downloader;
public Boolean Finished { get; private set; }
public DownloadClient(Download download, Torrent torrent, 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; }
public async Task<String> Start(DbSettings settings)
{
BytesDone = 0;
BytesTotal = 0;
Speed = 0;
try
{
_download = download;
_torrent = torrent;
_destinationPath = destinationPath;
}
var filePath = DownloadHelper.GetDownloadPath(_destinationPath, _torrent, _download);
public String Type { get; set; }
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; }
public async Task<String> Start(DbSettings settings)
{
BytesDone = 0;
BytesTotal = 0;
Speed = 0;
try
if (filePath == null)
{
var filePath = DownloadHelper.GetDownloadPath(_destinationPath, _torrent, _download);
if (filePath == null)
{
throw new Exception("Invalid download path");
}
await FileHelper.Delete(filePath);
Type = settings.DownloadClient;
Downloader = settings.DownloadClient switch
{
"Simple" => new SimpleDownloader(_download.Link, filePath),
"MultiPart" => new MultiDownloader(_download.Link, filePath, settings),
"Aria2c" => new Aria2cDownloader(_download.RemoteId, _download.Link, filePath, settings),
_ => throw new Exception($"Unknown download client {settings.DownloadClient}")
};
Downloader.DownloadComplete += (_, args) =>
{
Finished = true;
Error ??= args.Error;
};
Downloader.DownloadProgress += (_, args) =>
{
Speed = args.Speed;
BytesDone = args.BytesDone;
BytesTotal = args.BytesTotal;
};
var result = await Downloader.Download();
await Task.Delay(1000);
return result;
throw new Exception("Invalid download path");
}
catch (Exception ex)
{
if (Downloader != null)
{
await Downloader.Cancel();
}
await FileHelper.Delete(filePath);
Error = $"An unexpected error occurred preparing download {_download.Link} for torrent {_torrent.RdName}: {ex.Message}";
Type = settings.DownloadClient;
Downloader = settings.DownloadClient switch
{
"Simple" => new SimpleDownloader(_download.Link, filePath),
"MultiPart" => new MultiDownloader(_download.Link, filePath, settings),
"Aria2c" => new Aria2cDownloader(_download.RemoteId, _download.Link, filePath, settings),
_ => throw new Exception($"Unknown download client {settings.DownloadClient}")
};
Downloader.DownloadComplete += (_, args) =>
{
Finished = true;
Error ??= args.Error;
};
return null;
}
Downloader.DownloadProgress += (_, args) =>
{
Speed = args.Speed;
BytesDone = args.BytesDone;
BytesTotal = args.BytesTotal;
};
var result = await Downloader.Download();
await Task.Delay(1000);
return result;
}
public async Task Cancel()
catch (Exception ex)
{
if (Downloader != null)
{
await Downloader.Cancel();
}
Error = $"An unexpected error occurred preparing download {_download.Link} for torrent {_torrent.RdName}: {ex.Message}";
Finished = true;
Error = null;
if (Downloader == null)
{
return;
}
await Downloader.Cancel();
}
public async Task Pause()
{
if (Downloader == null)
{
return;
}
await Downloader.Pause();
}
public async Task Resume()
{
if (Downloader == null)
{
return;
}
await Downloader.Resume();
return null;
}
}
}
public async Task Cancel()
{
Finished = true;
Error = null;
if (Downloader == null)
{
return;
}
await Downloader.Cancel();
}
public async Task Pause()
{
if (Downloader == null)
{
return;
}
await Downloader.Pause();
}
public async Task Resume()
{
if (Downloader == null)
{
return;
}
await Downloader.Resume();
}
}

View file

@ -1,272 +1,265 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Aria2NET;
using Aria2NET;
using RdtClient.Data.Models.Internal;
using Serilog;
namespace RdtClient.Service.Services.Downloaders
namespace RdtClient.Service.Services.Downloaders;
public class Aria2cDownloader : IDownloader
{
public class Aria2cDownloader : IDownloader
public event EventHandler<DownloadCompleteEventArgs> DownloadComplete;
public event EventHandler<DownloadProgressEventArgs> DownloadProgress;
private const Int32 RetryCount = 5;
private readonly Aria2NetClient _aria2NetClient;
private readonly ILogger _logger;
private readonly String _uri;
private readonly String _filePath;
private String _gid;
public Aria2cDownloader(String gid, String uri, String filePath, DbSettings settings)
{
public event EventHandler<DownloadCompleteEventArgs> DownloadComplete;
public event EventHandler<DownloadProgressEventArgs> DownloadProgress;
_logger = Log.ForContext<Aria2cDownloader>();
_gid = gid;
_uri = uri;
_filePath = filePath;
private const Int32 RetryCount = 5;
private readonly Aria2NetClient _aria2NetClient;
private readonly ILogger _logger;
private readonly String _uri;
private readonly String _filePath;
private String _gid;
public Aria2cDownloader(String gid, String uri, String filePath, DbSettings settings)
var httpClient = new HttpClient
{
_logger = Log.ForContext<Aria2cDownloader>();
_gid = gid;
_uri = uri;
_filePath = filePath;
Timeout = TimeSpan.FromSeconds(10)
};
var httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(10)
};
_aria2NetClient = new Aria2NetClient(settings.Aria2cUrl, settings.Aria2cSecret, httpClient, 10);
}
_aria2NetClient = new Aria2NetClient(settings.Aria2cUrl, settings.Aria2cSecret, httpClient, 10);
}
public async Task<String> Download()
public async Task<String> Download()
{
var path = Path.GetDirectoryName(_filePath);
var fileName = Path.GetFileName(_filePath);
_logger.Debug($"Starting download of {_uri}, writing to path: {path}, fileName: {fileName}");
if (String.IsNullOrWhiteSpace(_gid))
{
var path = Path.GetDirectoryName(_filePath);
var fileName = Path.GetFileName(_filePath);
var isAlreadyAdded = await CheckIfAdded();
_logger.Debug($"Starting download of {_uri}, writing to path: {path}, fileName: {fileName}");
if (String.IsNullOrWhiteSpace(_gid))
if (isAlreadyAdded)
{
var isAlreadyAdded = await CheckIfAdded();
if (isAlreadyAdded)
{
throw new Exception($"The download link {_uri} has already been added to Aria2");
}
throw new Exception($"The download link {_uri} has already been added to Aria2");
}
}
var retryCount = 0;
while(true)
var retryCount = 0;
while(true)
{
try
{
try
if (_gid != null)
{
if (_gid != null)
try
{
try
{
await _aria2NetClient.TellStatus(_gid);
await _aria2NetClient.TellStatus(_gid);
return _gid;
}
catch
{
_gid = null;
}
return _gid;
}
catch
{
_gid = null;
}
}
_gid ??= await _aria2NetClient.AddUri(new List<String>
_gid ??= await _aria2NetClient.AddUri(new List<String>
{
_uri
},
new Dictionary<String, Object>
{
{
_uri
"dir", path
},
new Dictionary<String, Object>
{
{
"dir", path
},
{
"out", fileName
}
});
"out", fileName
}
});
_logger.Debug($"Added download to Aria2, received ID {_gid}");
_logger.Debug($"Added download to Aria2, received ID {_gid}");
await _aria2NetClient.TellStatus(_gid);
await _aria2NetClient.TellStatus(_gid);
_logger.Debug($"Download with ID {_gid} found in Aria2");
_logger.Debug($"Download with ID {_gid} found in Aria2");
return _gid;
return _gid;
}
catch (Exception ex)
{
if (retryCount >= RetryCount)
{
throw;
}
catch (Exception ex)
{
if (retryCount >= RetryCount)
{
throw;
}
_logger.Debug($"Error starting download: {ex.Message}. Retrying {retryCount}/{RetryCount}");
_logger.Debug($"Error starting download: {ex.Message}. Retrying {retryCount}/{RetryCount}");
await Task.Delay(retryCount * 1000);
await Task.Delay(retryCount * 1000);
retryCount++;
}
retryCount++;
}
}
public async Task Cancel()
{
await Remove();
}
public async Task Pause()
{
_logger.Debug($"Pausing download {_uri} {_gid}");
if (String.IsNullOrWhiteSpace(_gid))
{
return;
}
try
{
await _aria2NetClient.Pause(_gid);
}
catch
{
// ignored
}
}
public async Task Resume()
{
_logger.Debug($"Resuming download {_uri} {_gid}");
if (String.IsNullOrWhiteSpace(_gid))
{
return;
}
try
{
await _aria2NetClient.Unpause(_gid);
}
catch
{
// ignored
}
}
public async Task Update(IEnumerable<DownloadStatusResult> allDownloads)
{
if (_gid == null)
{
return;
}
var download = allDownloads.FirstOrDefault(m => m.Gid == _gid);
if (download == null)
{
DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs
{
Error = $"Download was not found in Aria2"
});
return;
}
if (!String.IsNullOrWhiteSpace(download.ErrorMessage) || download.Status == "error")
{
await Remove();
DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs
{
Error = $"{download.ErrorCode}: {download.ErrorMessage}"
});
return;
}
if (download.Status == "complete" || download.Status == "removed")
{
_logger.Debug($"Aria2 download found as complete {_gid}");
await Remove();
var retryCount = 0;
while (true)
{
if (retryCount >= 10)
{
DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs
{
Error = $"File not found at {_filePath}"
});
break;
}
if (File.Exists(_filePath))
{
DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs());
break;
}
await Task.Delay(1000 * retryCount);
retryCount++;
}
return;
}
DownloadProgress?.Invoke(this, new DownloadProgressEventArgs
{
BytesDone = download.CompletedLength,
BytesTotal = download.TotalLength,
Speed = download.DownloadSpeed
});
}
private async Task Remove()
{
if (String.IsNullOrWhiteSpace(_gid))
{
return;
}
_logger.Debug($"Remove download {_uri} {_gid} from Aria2");
try
{
await _aria2NetClient.ForceRemove(_gid);
}
catch
{
// ignored
}
try
{
await _aria2NetClient.RemoveDownloadResult(_gid);
}
catch
{
// ignored
}
}
private async Task<Boolean> CheckIfAdded()
{
var allDownloads = await _aria2NetClient.TellAll();
foreach (var download in allDownloads.Where(m => m.Files != null))
{
foreach (var file in download.Files.Where(m => m.Uris != null))
{
if (file.Uris.Any(uri => uri.Uri == _uri))
{
return true;
}
}
}
return false;
}
}
}
public async Task Cancel()
{
await Remove();
}
public async Task Pause()
{
_logger.Debug($"Pausing download {_uri} {_gid}");
if (String.IsNullOrWhiteSpace(_gid))
{
return;
}
try
{
await _aria2NetClient.Pause(_gid);
}
catch
{
// ignored
}
}
public async Task Resume()
{
_logger.Debug($"Resuming download {_uri} {_gid}");
if (String.IsNullOrWhiteSpace(_gid))
{
return;
}
try
{
await _aria2NetClient.Unpause(_gid);
}
catch
{
// ignored
}
}
public async Task Update(IEnumerable<DownloadStatusResult> allDownloads)
{
if (_gid == null)
{
return;
}
var download = allDownloads.FirstOrDefault(m => m.Gid == _gid);
if (download == null)
{
DownloadComplete.Invoke(this, new DownloadCompleteEventArgs
{
Error = $"Download was not found in Aria2"
});
return;
}
if (!String.IsNullOrWhiteSpace(download.ErrorMessage) || download.Status == "error")
{
await Remove();
DownloadComplete.Invoke(this, new DownloadCompleteEventArgs
{
Error = $"{download.ErrorCode}: {download.ErrorMessage}"
});
return;
}
if (download.Status == "complete" || download.Status == "removed")
{
_logger.Debug($"Aria2 download found as complete {_gid}");
await Remove();
var retryCount = 0;
while (true)
{
if (retryCount >= 10)
{
DownloadComplete.Invoke(this, new DownloadCompleteEventArgs
{
Error = $"File not found at {_filePath}"
});
break;
}
if (File.Exists(_filePath))
{
DownloadComplete.Invoke(this, new DownloadCompleteEventArgs());
break;
}
await Task.Delay(1000 * retryCount);
retryCount++;
}
return;
}
DownloadProgress.Invoke(this, new DownloadProgressEventArgs
{
BytesDone = download.CompletedLength,
BytesTotal = download.TotalLength,
Speed = download.DownloadSpeed
});
}
private async Task Remove()
{
if (String.IsNullOrWhiteSpace(_gid))
{
return;
}
_logger.Debug($"Remove download {_uri} {_gid} from Aria2");
try
{
await _aria2NetClient.ForceRemove(_gid);
}
catch
{
// ignored
}
try
{
await _aria2NetClient.RemoveDownloadResult(_gid);
}
catch
{
// ignored
}
}
private async Task<Boolean> CheckIfAdded()
{
var allDownloads = await _aria2NetClient.TellAll();
foreach (var download in allDownloads.Where(m => m.Files != null))
{
foreach (var file in download.Files.Where(m => m.Uris != null))
{
if (file.Uris.Any(uri => uri.Uri == _uri))
{
return true;
}
}
}
return false;
}
}

View file

@ -1,27 +1,23 @@
using System;
using System.Threading.Tasks;
namespace RdtClient.Service.Services.Downloaders;
namespace RdtClient.Service.Services.Downloaders
public class DownloadCompleteEventArgs
{
public class DownloadCompleteEventArgs
{
public String Error { get; set; }
}
public class DownloadProgressEventArgs
{
public Int64 Speed { get; set; }
public Int64 BytesDone { get; set; }
public Int64 BytesTotal { get; set; }
}
public interface IDownloader
{
event EventHandler<DownloadCompleteEventArgs> DownloadComplete;
event EventHandler<DownloadProgressEventArgs> DownloadProgress;
Task<String> Download();
Task Cancel();
Task Pause();
Task Resume();
}
public String Error { get; set; }
}
public class DownloadProgressEventArgs
{
public Int64 Speed { get; set; }
public Int64 BytesDone { get; set; }
public Int64 BytesTotal { get; set; }
}
public interface IDownloader
{
event EventHandler<DownloadCompleteEventArgs> DownloadComplete;
event EventHandler<DownloadProgressEventArgs> DownloadProgress;
Task<String> Download();
Task Cancel();
Task Pause();
Task Resume();
}

View file

@ -1,147 +1,143 @@
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using System.Net;
using Downloader;
using RdtClient.Data.Models.Internal;
using Serilog;
namespace RdtClient.Service.Services.Downloaders
namespace RdtClient.Service.Services.Downloaders;
public class MultiDownloader : IDownloader
{
public class MultiDownloader : IDownloader
public event EventHandler<DownloadCompleteEventArgs> DownloadComplete;
public event EventHandler<DownloadProgressEventArgs> DownloadProgress;
private readonly DownloadService _downloadService;
private readonly String _filePath;
private readonly String _uri;
private readonly ILogger _logger;
public MultiDownloader(String uri, String filePath, DbSettings settings)
{
public event EventHandler<DownloadCompleteEventArgs> DownloadComplete;
public event EventHandler<DownloadProgressEventArgs> DownloadProgress;
_logger = Log.ForContext<MultiDownloader>();
private readonly DownloadService _downloadService;
private readonly String _filePath;
private readonly String _uri;
_uri = uri;
_filePath = filePath;
private readonly ILogger _logger;
var settingTempPath = settings.TempPath;
public MultiDownloader(String uri, String filePath, DbSettings settings)
if (String.IsNullOrWhiteSpace(settingTempPath))
{
_logger = Log.ForContext<MultiDownloader>();
settingTempPath = Path.GetTempPath();
}
_uri = uri;
_filePath = filePath;
var settingDownloadChunkCount = settings.DownloadChunkCount;
var settingTempPath = settings.TempPath;
if (settingDownloadChunkCount <= 0)
{
settingDownloadChunkCount = 1;
}
if (String.IsNullOrWhiteSpace(settingTempPath))
var settingDownloadMaxSpeed = settings.DownloadMaxSpeed;
if (settingDownloadMaxSpeed <= 0)
{
settingDownloadMaxSpeed = 0;
}
settingDownloadMaxSpeed = settingDownloadMaxSpeed * 1024 * 1024;
var settingProxyServer = settings.ProxyServer;
var downloadOpt = new DownloadConfiguration
{
MaxTryAgainOnFailover = Int32.MaxValue,
ParallelDownload = settingDownloadChunkCount > 1,
ChunkCount = settingDownloadChunkCount,
Timeout = 1000,
OnTheFlyDownload = false,
BufferBlockSize = 1024 * 8,
MaximumBytesPerSecond = settingDownloadMaxSpeed,
TempDirectory = settingTempPath,
RequestConfiguration =
{
settingTempPath = Path.GetTempPath();
Accept = "*/*",
UserAgent = $"rdt-client",
ProtocolVersion = HttpVersion.Version11,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
KeepAlive = true,
UseDefaultCredentials = false
}
};
if (!String.IsNullOrWhiteSpace(settingProxyServer))
{
downloadOpt.RequestConfiguration.Proxy = new WebProxy(new Uri(settingProxyServer), false);
}
_downloadService = new DownloadService(downloadOpt);
_downloadService.DownloadProgressChanged += (_, args) =>
{
if (DownloadProgress == null)
{
return;
}
var settingDownloadChunkCount = settings.DownloadChunkCount;
DownloadProgress.Invoke(this,
new DownloadProgressEventArgs
{
Speed = (Int64)args.BytesPerSecondSpeed,
BytesDone = args.ReceivedBytesSize,
BytesTotal = args.TotalBytesToReceive
});
};
if (settingDownloadChunkCount <= 0)
_downloadService.DownloadFileCompleted += (_, args) =>
{
String error = null;
if (args.Cancelled)
{
settingDownloadChunkCount = 1;
error = $"The download was cancelled";
}
else if (args.Error != null)
{
error = args.Error.Message;
}
var settingDownloadMaxSpeed = settings.DownloadMaxSpeed;
if (settingDownloadMaxSpeed <= 0)
{
settingDownloadMaxSpeed = 0;
}
settingDownloadMaxSpeed = settingDownloadMaxSpeed * 1024 * 1024;
var settingProxyServer = settings.ProxyServer;
var downloadOpt = new DownloadConfiguration
{
MaxTryAgainOnFailover = Int32.MaxValue,
ParallelDownload = settingDownloadChunkCount > 1,
ChunkCount = settingDownloadChunkCount,
Timeout = 1000,
OnTheFlyDownload = false,
BufferBlockSize = 1024 * 8,
MaximumBytesPerSecond = settingDownloadMaxSpeed,
TempDirectory = settingTempPath,
RequestConfiguration =
{
Accept = "*/*",
UserAgent = $"rdt-client",
ProtocolVersion = HttpVersion.Version11,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
KeepAlive = true,
UseDefaultCredentials = false
}
};
if (!String.IsNullOrWhiteSpace(settingProxyServer))
{
downloadOpt.RequestConfiguration.Proxy = new WebProxy(new Uri(settingProxyServer), false);
}
_downloadService = new DownloadService(downloadOpt);
_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
});
};
}
public async Task<String> Download()
{
_logger.Debug($"Starting download of {_uri}, writing to path: {_filePath}");
await _downloadService.DownloadFileTaskAsync(_uri, _filePath);
return null;
}
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;
}
DownloadComplete?.Invoke(this,
new DownloadCompleteEventArgs
{
Error = error
});
};
}
}
public async Task<String> Download()
{
_logger.Debug($"Starting download of {_uri}, writing to path: {_filePath}");
await _downloadService.DownloadFileTaskAsync(_uri, _filePath);
return null;
}
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;
}
}

View file

@ -1,185 +1,179 @@
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using Serilog;
namespace RdtClient.Service.Services.Downloaders
namespace RdtClient.Service.Services.Downloaders;
public class SimpleDownloader : IDownloader
{
public class SimpleDownloader : IDownloader
private const Int32 BufferSize = 8 * 1024;
public event EventHandler<DownloadCompleteEventArgs> DownloadComplete;
public event EventHandler<DownloadProgressEventArgs> DownloadProgress;
private readonly String _uri;
private readonly String _filePath;
private Int64 _bytesTotal;
private Int64 _bytesDone;
private Boolean _cancelled;
private DateTime _nextUpdate;
private readonly ILogger _logger;
public SimpleDownloader(String uri, String filePath)
{
private const Int32 BufferSize = 8 * 1024;
_logger = Log.ForContext<SimpleDownloader>();
public event EventHandler<DownloadCompleteEventArgs> DownloadComplete;
public event EventHandler<DownloadProgressEventArgs> DownloadProgress;
_uri = uri;
_filePath = filePath;
}
private readonly String _uri;
private readonly String _filePath;
public Task<String> Download()
{
_logger.Debug($"Starting download of {_uri}, writing to path: {_filePath}");
private Int64 _bytesTotal;
private Int64 _bytesDone;
private Boolean _cancelled;
private DateTime _nextUpdate;
private readonly ILogger _logger;
public SimpleDownloader(String uri, String filePath)
_ = Task.Run(async () =>
{
_logger = Log.ForContext<SimpleDownloader>();
_uri = uri;
_filePath = filePath;
}
public Task<String> Download()
{
_logger.Debug($"Starting download of {_uri}, writing to path: {_filePath}");
_ = Task.Run(async () =>
{
await StartDownloadTask();
});
await StartDownloadTask();
});
return Task.FromResult<String>(null);
}
return Task.FromResult<String>(null);
}
public Task Cancel()
public Task Cancel()
{
_logger.Debug($"Cancelling download {_uri}");
_cancelled = true;
return Task.CompletedTask;
}
private async Task StartDownloadTask()
{
try
{
_logger.Debug($"Cancelling download {_uri}");
_nextUpdate = DateTime.UtcNow.AddSeconds(1);
_cancelled = true;
return Task.CompletedTask;
}
private async Task StartDownloadTask()
{
try
{
_nextUpdate = DateTime.UtcNow.AddSeconds(1);
_bytesTotal = await GetContentSize();
_bytesTotal = await GetContentSize();
var timeout = DateTimeOffset.UtcNow.AddHours(1);
var timeout = DateTimeOffset.UtcNow.AddHours(1);
var httpClient = new HttpClient();
var httpClient = new HttpClient();
while (timeout > DateTimeOffset.UtcNow && !_cancelled)
while (timeout > DateTimeOffset.UtcNow && !_cancelled)
{
try
{
try
var responseStream = await httpClient.GetStreamAsync(_uri);
if (responseStream == null)
{
var responseStream = await httpClient.GetStreamAsync(_uri);
throw new IOException("No stream");
}
if (responseStream == null)
{
throw new IOException("No stream");
}
var speedLimit = Settings.Get.DownloadMaxSpeed;
var speedLimit = Settings.Get.DownloadMaxSpeed;
await using var destinationStream = new ThrottledStream(responseStream, speedLimit * 1000L * 1000L);
await using var destinationStream = new ThrottledStream(responseStream, speedLimit * 1000L * 1000L);
await using var fileStream = new FileStream(_filePath, FileMode.Create, FileAccess.Write, FileShare.Write);
await using var fileStream = new FileStream(_filePath, FileMode.Create, FileAccess.Write, FileShare.Write);
var readSize = 1;
var buffer = new Byte[BufferSize * 8];
var readSize = 1;
var buffer = new Byte[BufferSize * 8];
while (readSize > 0 && !_cancelled)
while (readSize > 0 && !_cancelled)
{
// ReSharper disable once ConvertToUsingDeclaration
using (var innerCts = new CancellationTokenSource(1000))
{
// ReSharper disable once ConvertToUsingDeclaration
using (var innerCts = new CancellationTokenSource(1000))
readSize = await destinationStream.ReadAsync(buffer.AsMemory(0, buffer.Length), innerCts.Token).ConfigureAwait(false);
await fileStream.WriteAsync(buffer.AsMemory(0, readSize), innerCts.Token);
_bytesDone = fileStream.Length;
if (DateTime.UtcNow > _nextUpdate)
{
readSize = await destinationStream.ReadAsync(buffer.AsMemory(0, buffer.Length), innerCts.Token).ConfigureAwait(false);
_nextUpdate = DateTime.UtcNow.AddSeconds(1);
await fileStream.WriteAsync(buffer.AsMemory(0, readSize), innerCts.Token);
timeout = DateTimeOffset.UtcNow.AddHours(1);
_bytesDone = fileStream.Length;
DownloadProgress?.Invoke(this,
new DownloadProgressEventArgs
{
Speed = destinationStream.Speed,
BytesDone = _bytesDone,
BytesTotal = _bytesTotal
});
if (DateTime.UtcNow > _nextUpdate)
if (Settings.Get.DownloadMaxSpeed != speedLimit)
{
_nextUpdate = DateTime.UtcNow.AddSeconds(1);
timeout = DateTimeOffset.UtcNow.AddHours(1);
DownloadProgress?.Invoke(this,
new DownloadProgressEventArgs
{
Speed = destinationStream.Speed,
BytesDone = _bytesDone,
BytesTotal = _bytesTotal
});
if (Settings.Get.DownloadMaxSpeed != speedLimit)
{
speedLimit = Settings.Get.DownloadMaxSpeed;
destinationStream.BandwidthLimit = speedLimit * 1000L * 1000L;
}
speedLimit = Settings.Get.DownloadMaxSpeed;
destinationStream.BandwidthLimit = speedLimit * 1000L * 1000L;
}
}
}
break;
}
catch (IOException)
{
await Task.Delay(1000);
}
catch (WebException)
{
await Task.Delay(1000);
}
break;
}
if (_cancelled)
catch (IOException)
{
throw new Exception("Download cancelled");
await Task.Delay(1000);
}
if (timeout <= DateTimeOffset.UtcNow)
catch (WebException)
{
throw new Exception($"Download timed out");
await Task.Delay(1000);
}
DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs());
}
catch (Exception ex)
{
DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs
{
Error = ex.Message
});
}
}
private async Task<Int64> GetContentSize()
{
var httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(5)
};
var responseHeaders = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, _uri));
if (!responseHeaders.IsSuccessStatusCode)
{
return -1;
}
return responseHeaders.Content.Headers.ContentLength ?? -1;
}
if (_cancelled)
{
throw new Exception("Download cancelled");
}
public Task Pause()
{
return Task.CompletedTask;
}
if (timeout <= DateTimeOffset.UtcNow)
{
throw new Exception($"Download timed out");
}
public Task Resume()
DownloadComplete.Invoke(this, new DownloadCompleteEventArgs());
}
catch (Exception ex)
{
return Task.CompletedTask;
DownloadComplete.Invoke(this, new DownloadCompleteEventArgs
{
Error = ex.Message
});
}
}
}
private async Task<Int64> GetContentSize()
{
var httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(5)
};
var responseHeaders = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, _uri));
if (!responseHeaders.IsSuccessStatusCode)
{
return -1;
}
return responseHeaders.Content.Headers.ContentLength ?? -1;
}
public Task Pause()
{
return Task.CompletedTask;
}
public Task Resume()
{
return Task.CompletedTask;
}
}

View file

@ -1,98 +1,94 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using RdtClient.Data.Data;
using RdtClient.Data.Data;
using Download = RdtClient.Data.Models.Data.Download;
namespace RdtClient.Service.Services
namespace RdtClient.Service.Services;
public class Downloads
{
public class Downloads
private readonly DownloadData _downloadData;
public Downloads(DownloadData downloadData)
{
private readonly DownloadData _downloadData;
public Downloads(DownloadData downloadData)
{
_downloadData = downloadData;
}
public async Task<List<Download>> GetForTorrent(Guid torrentId)
{
return await _downloadData.GetForTorrent(torrentId);
}
public async Task<Download> GetById(Guid downloadId)
{
return await _downloadData.GetById(downloadId);
}
public async Task<Download> Get(Guid torrentId, String path)
{
return await _downloadData.Get(torrentId, path);
}
public async Task<Download> Add(Guid torrentId, String path)
{
return await _downloadData.Add(torrentId, path);
}
public async Task UpdateUnrestrictedLink(Guid downloadId, String unrestrictedLink)
{
await _downloadData.UpdateUnrestrictedLink(downloadId, unrestrictedLink);
}
public async Task UpdateDownloadStarted(Guid downloadId, DateTimeOffset? dateTime)
{
await _downloadData.UpdateDownloadStarted(downloadId, dateTime);
}
public async Task UpdateDownloadFinished(Guid downloadId, DateTimeOffset? dateTime)
{
await _downloadData.UpdateDownloadFinished(downloadId, dateTime);
}
public async Task UpdateUnpackingQueued(Guid downloadId, DateTimeOffset? dateTime)
{
await _downloadData.UpdateUnpackingQueued(downloadId, dateTime);
}
public async Task UpdateUnpackingStarted(Guid downloadId, DateTimeOffset? dateTime)
{
await _downloadData.UpdateUnpackingStarted(downloadId, dateTime);
}
public async Task UpdateUnpackingFinished(Guid downloadId, DateTimeOffset? dateTime)
{
await _downloadData.UpdateUnpackingFinished(downloadId, dateTime);
}
public async Task UpdateCompleted(Guid downloadId, DateTimeOffset? dateTime)
{
await _downloadData.UpdateCompleted(downloadId, dateTime);
}
public async Task UpdateError(Guid downloadId, String error)
{
await _downloadData.UpdateError(downloadId, error);
}
public async Task UpdateRetryCount(Guid downloadId, Int32 retryCount)
{
await _downloadData.UpdateRetryCount(downloadId, retryCount);
}
public async Task UpdateRemoteId(Guid downloadId, String remoteId)
{
await _downloadData.UpdateRemoteId(downloadId, remoteId);
}
public async Task DeleteForTorrent(Guid torrentId)
{
await _downloadData.DeleteForTorrent(torrentId);
}
public async Task Reset(Guid downloadId)
{
await _downloadData.Reset(downloadId);
}
_downloadData = downloadData;
}
}
public async Task<List<Download>> GetForTorrent(Guid torrentId)
{
return await _downloadData.GetForTorrent(torrentId);
}
public async Task<Download> GetById(Guid downloadId)
{
return await _downloadData.GetById(downloadId);
}
public async Task<Download> Get(Guid torrentId, String path)
{
return await _downloadData.Get(torrentId, path);
}
public async Task<Download> Add(Guid torrentId, String path)
{
return await _downloadData.Add(torrentId, path);
}
public async Task UpdateUnrestrictedLink(Guid downloadId, String unrestrictedLink)
{
await _downloadData.UpdateUnrestrictedLink(downloadId, unrestrictedLink);
}
public async Task UpdateDownloadStarted(Guid downloadId, DateTimeOffset? dateTime)
{
await _downloadData.UpdateDownloadStarted(downloadId, dateTime);
}
public async Task UpdateDownloadFinished(Guid downloadId, DateTimeOffset? dateTime)
{
await _downloadData.UpdateDownloadFinished(downloadId, dateTime);
}
public async Task UpdateUnpackingQueued(Guid downloadId, DateTimeOffset? dateTime)
{
await _downloadData.UpdateUnpackingQueued(downloadId, dateTime);
}
public async Task UpdateUnpackingStarted(Guid downloadId, DateTimeOffset? dateTime)
{
await _downloadData.UpdateUnpackingStarted(downloadId, dateTime);
}
public async Task UpdateUnpackingFinished(Guid downloadId, DateTimeOffset? dateTime)
{
await _downloadData.UpdateUnpackingFinished(downloadId, dateTime);
}
public async Task UpdateCompleted(Guid downloadId, DateTimeOffset? dateTime)
{
await _downloadData.UpdateCompleted(downloadId, dateTime);
}
public async Task UpdateError(Guid downloadId, String error)
{
await _downloadData.UpdateError(downloadId, error);
}
public async Task UpdateRetryCount(Guid downloadId, Int32 retryCount)
{
await _downloadData.UpdateRetryCount(downloadId, retryCount);
}
public async Task UpdateRemoteId(Guid downloadId, String remoteId)
{
await _downloadData.UpdateRemoteId(downloadId, remoteId);
}
public async Task DeleteForTorrent(Guid torrentId)
{
await _downloadData.DeleteForTorrent(torrentId);
}
public async Task Reset(Guid downloadId)
{
await _downloadData.Reset(downloadId);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,27 +1,23 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using Microsoft.AspNetCore.SignalR;
namespace RdtClient.Service.Services
namespace RdtClient.Service.Services;
public class RdtHub : Hub
{
public class RdtHub : Hub
private static readonly ConcurrentDictionary<String, String> Users = new();
public static Boolean HasConnections => Users.Any();
public override async Task OnConnectedAsync()
{
private static readonly ConcurrentDictionary<String, String> Users = new();
public static Boolean HasConnections => Users.Any();
public override async Task OnConnectedAsync()
{
Users.TryAdd(Context.ConnectionId, Context.ConnectionId);
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
Users.TryRemove(Context.ConnectionId, out _);
await base.OnDisconnectedAsync(exception);
}
Users.TryAdd(Context.ConnectionId, Context.ConnectionId);
await base.OnConnectedAsync();
}
}
public override async Task OnDisconnectedAsync(Exception exception)
{
Users.TryRemove(Context.ConnectionId, out _);
await base.OnDisconnectedAsync(exception);
}
}

View file

@ -1,36 +1,32 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR;
namespace RdtClient.Service.Services
namespace RdtClient.Service.Services;
public class RemoteService
{
public class RemoteService
private readonly IHubContext<RdtHub> _hub;
private readonly Torrents _torrents;
public RemoteService(IHubContext<RdtHub> hub, Torrents torrents)
{
private readonly IHubContext<RdtHub> _hub;
private readonly Torrents _torrents;
public RemoteService(IHubContext<RdtHub> hub, Torrents torrents)
{
_hub = hub;
_torrents = torrents;
}
public async Task Update()
{
var torrents = await _torrents.Get();
// Prevent infinite recursion when serializing
foreach (var file in torrents.SelectMany(torrent => torrent.Downloads))
{
file.Torrent = null;
}
await _hub.Clients.All.SendCoreAsync("update",
new Object[]
{
torrents
});
}
_hub = hub;
_torrents = torrents;
}
}
public async Task Update()
{
var torrents = await _torrents.Get();
// Prevent infinite recursion when serializing
foreach (var file in torrents.SelectMany(torrent => torrent.Downloads))
{
file.Torrent = null;
}
await _hub.Clients.All.SendCoreAsync("update",
new Object[]
{
torrents
});
}
}

View file

@ -1,182 +1,180 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using Aria2NET;
using RdtClient.Data.Data;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.Internal;
using RdtClient.Service.Helpers;
using Serilog.Core;
using Serilog.Events;
namespace RdtClient.Service.Services
namespace RdtClient.Service.Services;
public class Settings
{
public class Settings
public static readonly LoggingLevelSwitch LoggingLevelSwitch = new(LogEventLevel.Debug);
private readonly SettingData _settingData;
public Settings(SettingData settingData)
{
private readonly SettingData _settingData;
_settingData = settingData;
}
public Settings(SettingData settingData)
public static DbSettings Get => SettingData.Get;
public async Task<IList<Setting>> GetAll()
{
return await _settingData.GetAll();
}
public async Task Update(IList<Setting> settings)
{
await _settingData.Update(settings);
}
public async Task UpdateString(String key, String value)
{
await _settingData.UpdateString(key, value);
}
public async Task TestPath(String path)
{
if (String.IsNullOrWhiteSpace(path))
{
_settingData = settingData;
throw new Exception("Path is not set");
}
public static DbSettings Get => SettingData.Get;
path = path.TrimEnd('/').TrimEnd('\\');
public async Task<IList<Setting>> GetAll()
if (!Directory.Exists(path))
{
return await _settingData.GetAll();
throw new Exception($"Path {path} does not exist");
}
public async Task Update(IList<Setting> settings)
{
await _settingData.Update(settings);
}
var testFile = $"{path}/test.txt";
public async Task UpdateString(String key, String value)
{
await _settingData.UpdateString(key, value);
}
public async Task TestPath(String path)
{
if (String.IsNullOrWhiteSpace(path))
{
throw new Exception("Path is not set");
}
path = path.TrimEnd('/').TrimEnd('\\');
if (!Directory.Exists(path))
{
throw new Exception($"Path {path} does not exist");
}
var testFile = $"{path}/test.txt";
await File.WriteAllTextAsync(testFile, "RealDebridClient Test File, you can remove this file.");
await File.WriteAllTextAsync(testFile, "RealDebridClient Test File, you can remove this file.");
await FileHelper.Delete(testFile);
}
await FileHelper.Delete(testFile);
}
public async Task<Double> TestDownloadSpeed(CancellationToken cancellationToken)
public async Task<Double> TestDownloadSpeed(CancellationToken cancellationToken)
{
var downloadPath = Get.DownloadPath;
var testFilePath = Path.Combine(downloadPath, "testDefault.rar");
await FileHelper.Delete(testFilePath);
var download = new Download
{
var downloadPath = Get.DownloadPath;
var testFilePath = Path.Combine(downloadPath, "testDefault.rar");
await FileHelper.Delete(testFilePath);
var download = new Download
Link = "https://34.download.real-debrid.com/speedtest/testDefault.rar",
Torrent = new Torrent
{
Link = "https://34.download.real-debrid.com/speedtest/testDefault.rar",
Torrent = new Torrent
{
RdName = ""
}
};
RdName = ""
}
};
var downloadClient = new DownloadClient(download, download.Torrent, downloadPath);
var downloadClient = new DownloadClient(download, download.Torrent, downloadPath);
await downloadClient.Start(Get);
await downloadClient.Start(Get);
while (!downloadClient.Finished)
{
while (!downloadClient.Finished)
{
#pragma warning disable CA2016 // Forward the 'CancellationToken' parameter to methods that take one
// ReSharper disable once MethodSupportsCancellation
await Task.Delay(10);
// ReSharper disable once MethodSupportsCancellation
await Task.Delay(10);
#pragma warning restore CA2016 // Forward the 'CancellationToken' parameter to methods that take one
if (cancellationToken.IsCancellationRequested)
{
await downloadClient.Cancel();
}
if (downloadClient.BytesDone > 1024 * 1024 * 50)
{
await downloadClient.Cancel();
break;
}
if (cancellationToken.IsCancellationRequested)
{
await downloadClient.Cancel();
}
await FileHelper.Delete(testFilePath);
if (downloadClient.BytesDone > 1024 * 1024 * 50)
{
await downloadClient.Cancel();
await Clean();
return downloadClient.Speed;
break;
}
}
public async Task<Double> TestWriteSpeed()
await FileHelper.Delete(testFilePath);
await Clean();
return downloadClient.Speed;
}
public async Task<Double> TestWriteSpeed()
{
var downloadPath = Get.DownloadPath;
var testFilePath = Path.Combine(downloadPath, "test.tmp");
await FileHelper.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)
{
var downloadPath = Get.DownloadPath;
rnd.NextBytes(buffer);
var testFilePath = Path.Combine(downloadPath, "test.tmp");
await FileHelper.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);
await fileStream.WriteAsync(buffer.AsMemory(0, buffer.Length));
}
await fileStream.WriteAsync(buffer.AsMemory(0, buffer.Length));
}
watch.Stop();
watch.Stop();
var writeSpeed = fileStream.Length / watch.Elapsed.TotalSeconds;
var writeSpeed = fileStream.Length / watch.Elapsed.TotalSeconds;
fileStream.Close();
fileStream.Close();
await FileHelper.Delete(testFilePath);
await FileHelper.Delete(testFilePath);
return writeSpeed;
}
return writeSpeed;
}
public async Task<VersionResult> GetAria2cVersion(String url, String secret)
public async Task<VersionResult> GetAria2cVersion(String url, String secret)
{
var client = new Aria2NetClient(url, secret);
return await client.GetVersion();
}
public async Task Clean()
{
try
{
var client = new Aria2NetClient(url, secret);
var tempPath = Get.TempPath;
return await client.GetVersion();
}
public async Task Clean()
{
try
if (!String.IsNullOrWhiteSpace(tempPath))
{
var tempPath = Get.TempPath;
var files = Directory.GetFiles(tempPath, "*.dsc", SearchOption.TopDirectoryOnly);
if (!String.IsNullOrWhiteSpace(tempPath))
foreach (var file in files)
{
var files = Directory.GetFiles(tempPath, "*.dsc", SearchOption.TopDirectoryOnly);
foreach (var file in files)
{
await FileHelper.Delete(file);
}
await FileHelper.Delete(file);
}
}
catch
{
// ignored
}
}
public async Task ResetCache()
catch
{
await _settingData.ResetCache();
// ignored
}
}
}
public async Task ResetCache()
{
await _settingData.ResetCache();
}
}

View file

@ -1,32 +1,55 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using RdtClient.Data.Data;
using Serilog;
using Serilog.Events;
namespace RdtClient.Service.Services
namespace RdtClient.Service.Services;
public class Startup : IHostedService
{
public class Startup : IHostedService
private readonly IServiceProvider _serviceProvider;
public Startup(IServiceProvider serviceProvider)
{
private readonly IServiceProvider _serviceProvider;
public Startup(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
using var scope = _serviceProvider.CreateScope();
var settings = scope.ServiceProvider.GetRequiredService<Settings>();
await settings.ResetCache();
await settings.Clean();
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
_serviceProvider = serviceProvider;
}
}
public async Task StartAsync(CancellationToken cancellationToken)
{
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<DataContext>();
await dbContext.Database.MigrateAsync(cancellationToken);
await dbContext.Seed();
var logLevelSettingDb = await dbContext.Settings.FirstOrDefaultAsync(m => m.SettingId == "LogLevel", cancellationToken);
var logLevelSetting = "Warning";
if (logLevelSettingDb != null)
{
logLevelSetting = logLevelSettingDb.Value;
}
if (!Enum.TryParse<LogEventLevel>(logLevelSetting, out var logLevel))
{
logLevel = LogEventLevel.Warning;
}
Settings.LoggingLevelSwitch.MinimumLevel = logLevel;
var version = Assembly.GetEntryAssembly()?.GetName().Version;
Log.Warning($"Starting host on version {version}");
var settings = scope.ServiceProvider.GetRequiredService<Settings>();
await settings.ResetCache();
await settings.Clean();
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

View file

@ -1,49 +1,45 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace RdtClient.Service.Services
namespace RdtClient.Service.Services;
public class TaskRunner : BackgroundService
{
public class TaskRunner : BackgroundService
private readonly ILogger<TaskRunner> _logger;
private readonly IServiceProvider _serviceProvider;
public TaskRunner(ILogger<TaskRunner> logger, IServiceProvider serviceProvider)
{
private readonly ILogger<TaskRunner> _logger;
private readonly IServiceProvider _serviceProvider;
_logger = logger;
_serviceProvider = serviceProvider;
}
public TaskRunner(ILogger<TaskRunner> logger, IServiceProvider serviceProvider)
{
_logger = logger;
_serviceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
using var scope = _serviceProvider.CreateScope();
var torrentRunner = scope.ServiceProvider.GetRequiredService<TorrentRunner>();
using var scope = _serviceProvider.CreateScope();
var torrentRunner = scope.ServiceProvider.GetRequiredService<TorrentRunner>();
_logger.LogInformation("TaskRunner started.");
_logger.LogInformation("TaskRunner started.");
await torrentRunner.Initialize();
await torrentRunner.Initialize();
while (!stoppingToken.IsCancellationRequested)
while (!stoppingToken.IsCancellationRequested)
{
try
{
try
{
await torrentRunner.Tick();
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error occurred in TorrentDownloadManager.Tick");
}
await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);
await torrentRunner.Tick();
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error occurred in TorrentDownloadManager.Tick");
}
_logger.LogInformation("TaskRunner stopped.");
await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);
}
_logger.LogInformation("TaskRunner stopped.");
}
}

View file

@ -1,211 +1,205 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace RdtClient.Service.Services;
namespace RdtClient.Service.Services
/// <summary>
/// Class for streaming data with throttling support.
/// Taken from Downloader: https://github.com/bezzad/Downloader
/// </summary>
public class ThrottledStream : Stream
{
public Int64 Speed => (Int64)_bandwidth.AverageSpeed;
private Bandwidth _bandwidth;
private Int64 _bandwidthLimit;
private readonly Stream _baseStream;
/// <summary>
/// Class for streaming data with throttling support.
/// Taken from Downloader: https://github.com/bezzad/Downloader
/// Initializes a new instance of the <see cref="T:ThrottledStream" /> class.
/// </summary>
public class ThrottledStream : Stream
/// <param name="baseStream">The base stream.</param>
/// <param name="bandwidthLimit">The maximum bytes per second that can be transferred through the base stream.</param>
/// <exception cref="ArgumentNullException">Thrown when <see cref="baseStream" /> is a null reference.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when <see cref="BandwidthLimit" /> is a negative value.</exception>
public ThrottledStream(Stream baseStream, Int64 bandwidthLimit)
{
public Int64 Speed => (Int64)_bandwidth.AverageSpeed;
private Bandwidth _bandwidth;
private Int64 _bandwidthLimit;
private readonly Stream _baseStream;
/// <summary>
/// Initializes a new instance of the <see cref="T:ThrottledStream" /> class.
/// </summary>
/// <param name="baseStream">The base stream.</param>
/// <param name="bandwidthLimit">The maximum bytes per second that can be transferred through the base stream.</param>
/// <exception cref="ArgumentNullException">Thrown when <see cref="baseStream" /> is a null reference.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when <see cref="BandwidthLimit" /> is a negative value.</exception>
public ThrottledStream(Stream baseStream, Int64 bandwidthLimit)
if (bandwidthLimit < 0)
{
if (bandwidthLimit < 0)
{
throw new ArgumentOutOfRangeException(nameof(bandwidthLimit),
bandwidthLimit,
"The maximum number of bytes per second can't be negative.");
}
_baseStream = baseStream ?? throw new ArgumentNullException(nameof(baseStream));
BandwidthLimit = bandwidthLimit;
throw new ArgumentOutOfRangeException(nameof(bandwidthLimit),
bandwidthLimit,
"The maximum number of bytes per second can't be negative.");
}
public static Int64 Infinite => Int64.MaxValue;
_baseStream = baseStream ?? throw new ArgumentNullException(nameof(baseStream));
BandwidthLimit = bandwidthLimit;
}
/// <summary>
/// Bandwidth Limit (in B/s)
/// </summary>
/// <value>The maximum bytes per second.</value>
public Int64 BandwidthLimit
public static Int64 Infinite => Int64.MaxValue;
/// <summary>
/// Bandwidth Limit (in B/s)
/// </summary>
/// <value>The maximum bytes per second.</value>
public Int64 BandwidthLimit
{
get => _bandwidthLimit;
set
{
get => _bandwidthLimit;
set
{
_bandwidthLimit = value <= 0 ? Infinite : value;
_bandwidth ??= new Bandwidth();
_bandwidth.BandwidthLimit = _bandwidthLimit;
}
}
/// <inheritdoc />
public override Boolean CanRead => _baseStream.CanRead;
/// <inheritdoc />
public override Boolean CanSeek => _baseStream.CanSeek;
/// <inheritdoc />
public override Boolean CanWrite => _baseStream.CanWrite;
/// <inheritdoc />
public override Int64 Length => _baseStream.Length;
/// <inheritdoc />
public override Int64 Position
{
get => _baseStream.Position;
set => _baseStream.Position = value;
}
/// <inheritdoc />
public override void Flush()
{
_baseStream.Flush();
}
/// <inheritdoc />
public override Int64 Seek(Int64 offset, SeekOrigin origin)
{
return _baseStream.Seek(offset, origin);
}
/// <inheritdoc />
public override void SetLength(Int64 value)
{
_baseStream.SetLength(value);
}
/// <inheritdoc />
public override Int32 Read(Byte[] buffer, Int32 offset, Int32 count)
{
Throttle(count).Wait();
return _baseStream.Read(buffer, offset, count);
}
public override async Task<Int32> ReadAsync(Byte[] buffer,
Int32 offset,
Int32 count,
CancellationToken cancellationToken)
{
await Throttle(count).ConfigureAwait(false);
return await _baseStream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc />
public override void Write(Byte[] buffer, Int32 offset, Int32 count)
{
Throttle(count).Wait();
_baseStream.Write(buffer, offset, count);
}
/// <inheritdoc />
public override async Task WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
{
await Throttle(count).ConfigureAwait(false);
await _baseStream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
}
private async Task Throttle(Int32 transmissionVolume)
{
// Make sure the buffer isn't empty.
if (BandwidthLimit > 0 && transmissionVolume > 0)
{
// Calculate the time to sleep.
_bandwidth.CalculateSpeed(transmissionVolume);
await Sleep(_bandwidth.PopSpeedRetrieveTime()).ConfigureAwait(false);
}
}
private static async Task Sleep(Int32 time)
{
if (time > 0)
{
await Task.Delay(time).ConfigureAwait(false);
}
}
/// <inheritdoc />
public override String ToString()
{
return _baseStream.ToString();
_bandwidthLimit = value <= 0 ? Infinite : value;
_bandwidth ??= new Bandwidth();
_bandwidth.BandwidthLimit = _bandwidthLimit;
}
}
internal class Bandwidth
/// <inheritdoc />
public override Boolean CanRead => _baseStream.CanRead;
/// <inheritdoc />
public override Boolean CanSeek => _baseStream.CanSeek;
/// <inheritdoc />
public override Boolean CanWrite => _baseStream.CanWrite;
/// <inheritdoc />
public override Int64 Length => _baseStream.Length;
/// <inheritdoc />
public override Int64 Position
{
private const Double OneSecond = 1000; // millisecond
private Int64 _count;
private Int32 _lastSecondCheckpoint;
private Int64 _lastTransferredBytesCount;
private Int32 _speedRetrieveTime;
get => _baseStream.Position;
set => _baseStream.Position = value;
}
/// <inheritdoc />
public override void Flush()
{
_baseStream.Flush();
}
public Bandwidth()
/// <inheritdoc />
public override Int64 Seek(Int64 offset, SeekOrigin origin)
{
return _baseStream.Seek(offset, origin);
}
/// <inheritdoc />
public override void SetLength(Int64 value)
{
_baseStream.SetLength(value);
}
/// <inheritdoc />
public override Int32 Read(Byte[] buffer, Int32 offset, Int32 count)
{
Throttle(count).Wait();
return _baseStream.Read(buffer, offset, count);
}
public override async Task<Int32> ReadAsync(Byte[] buffer,
Int32 offset,
Int32 count,
CancellationToken cancellationToken)
{
await Throttle(count).ConfigureAwait(false);
return await _baseStream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc />
public override void Write(Byte[] buffer, Int32 offset, Int32 count)
{
Throttle(count).Wait();
_baseStream.Write(buffer, offset, count);
}
/// <inheritdoc />
public override async Task WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
{
await Throttle(count).ConfigureAwait(false);
await _baseStream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
}
private async Task Throttle(Int32 transmissionVolume)
{
// Make sure the buffer isn't empty.
if (BandwidthLimit > 0 && transmissionVolume > 0)
{
BandwidthLimit = Int64.MaxValue;
Reset();
// Calculate the time to sleep.
_bandwidth.CalculateSpeed(transmissionVolume);
await Sleep(_bandwidth.PopSpeedRetrieveTime()).ConfigureAwait(false);
}
}
public Double Speed { get; private set; }
public Double AverageSpeed { get; private set; }
public Int64 BandwidthLimit { get; set; }
public void CalculateSpeed(Int64 receivedBytesCount)
private static async Task Sleep(Int32 time)
{
if (time > 0)
{
var elapsedTime = (Environment.TickCount - _lastSecondCheckpoint) + 1;
receivedBytesCount = Interlocked.Add(ref _lastTransferredBytesCount, receivedBytesCount);
var momentSpeed = (receivedBytesCount * OneSecond) / elapsedTime; // B/s
if (OneSecond < elapsedTime)
{
Speed = momentSpeed;
AverageSpeed = ((AverageSpeed * _count) + Speed) / (_count + 1);
_count++;
SecondCheckpoint();
}
if (momentSpeed >= BandwidthLimit)
{
var expectedTime = (receivedBytesCount * OneSecond) / BandwidthLimit;
Interlocked.Add(ref _speedRetrieveTime, (Int32) expectedTime - elapsedTime);
}
await Task.Delay(time).ConfigureAwait(false);
}
}
public Int32 PopSpeedRetrieveTime()
{
return Interlocked.Exchange(ref _speedRetrieveTime, 0);
}
public void Reset()
{
SecondCheckpoint();
_count = 0;
Speed = 0;
AverageSpeed = 0;
}
private void SecondCheckpoint()
{
Interlocked.Exchange(ref _lastSecondCheckpoint, Environment.TickCount);
Interlocked.Exchange(ref _lastTransferredBytesCount, 0);
}
/// <inheritdoc />
public override String ToString()
{
return _baseStream.ToString();
}
}
internal class Bandwidth
{
private const Double OneSecond = 1000; // millisecond
private Int64 _count;
private Int32 _lastSecondCheckpoint;
private Int64 _lastTransferredBytesCount;
private Int32 _speedRetrieveTime;
public Bandwidth()
{
BandwidthLimit = Int64.MaxValue;
Reset();
}
public Double Speed { get; private set; }
public Double AverageSpeed { get; private set; }
public Int64 BandwidthLimit { get; set; }
public void CalculateSpeed(Int64 receivedBytesCount)
{
var elapsedTime = (Environment.TickCount - _lastSecondCheckpoint) + 1;
receivedBytesCount = Interlocked.Add(ref _lastTransferredBytesCount, receivedBytesCount);
var momentSpeed = (receivedBytesCount * OneSecond) / elapsedTime; // B/s
if (OneSecond < elapsedTime)
{
Speed = momentSpeed;
AverageSpeed = ((AverageSpeed * _count) + Speed) / (_count + 1);
_count++;
SecondCheckpoint();
}
if (momentSpeed >= BandwidthLimit)
{
var expectedTime = (receivedBytesCount * OneSecond) / BandwidthLimit;
Interlocked.Add(ref _speedRetrieveTime, (Int32) expectedTime - elapsedTime);
}
}
public Int32 PopSpeedRetrieveTime()
{
return Interlocked.Exchange(ref _speedRetrieveTime, 0);
}
public void Reset()
{
SecondCheckpoint();
_count = 0;
Speed = 0;
AverageSpeed = 0;
}
private void SecondCheckpoint()
{
Interlocked.Exchange(ref _lastSecondCheckpoint, Environment.TickCount);
Interlocked.Exchange(ref _lastTransferredBytesCount, 0);
}
}

View file

@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using AllDebridNET;
using AllDebridNET;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using RDNET;
@ -11,324 +6,324 @@ using RDNET.Exceptions;
using RdtClient.Data.Enums;
using RdtClient.Data.Models.TorrentClient;
using RdtClient.Service.Helpers;
using File = AllDebridNET.File;
using Torrent = RdtClient.Data.Models.Data.Torrent;
namespace RdtClient.Service.Services.TorrentClients
namespace RdtClient.Service.Services.TorrentClients;
public class AllDebridTorrentClient : ITorrentClient
{
public class AllDebridTorrentClient : ITorrentClient
private readonly ILogger<AllDebridTorrentClient> _logger;
private readonly IHttpClientFactory _httpClientFactory;
public AllDebridTorrentClient(ILogger<AllDebridTorrentClient> logger, IHttpClientFactory httpClientFactory)
{
private readonly ILogger<AllDebridTorrentClient> _logger;
private readonly IHttpClientFactory _httpClientFactory;
public AllDebridTorrentClient(ILogger<AllDebridTorrentClient> logger, IHttpClientFactory httpClientFactory)
{
_logger = logger;
_httpClientFactory = httpClientFactory;
}
private AllDebridNETClient GetClient()
{
var apiKey = Settings.Get.RealDebridApiKey;
if (String.IsNullOrWhiteSpace(apiKey))
{
throw new Exception("All-Debrid API Key not set in the settings");
}
var httpClient = _httpClientFactory.CreateClient();
httpClient.Timeout = TimeSpan.FromSeconds(10);
var allDebridNetClient = new AllDebridNETClient("RealDebridClient", apiKey);
return allDebridNetClient;
}
private static TorrentClientTorrent Map(Magnet torrent)
{
return new TorrentClientTorrent
{
Id = torrent.Id.ToString(),
Filename = torrent.Filename,
OriginalFilename = torrent.Filename,
Hash = torrent.Hash,
Bytes = torrent.Size,
OriginalBytes = torrent.Size,
Host = null,
Split = 0,
Progress = (Int64) Math.Round(torrent.Downloaded * 100.0 / torrent.Size),
Status = torrent.Status,
StatusCode = torrent.StatusCode,
Added = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(torrent.UploadDate),
Files = (torrent.Links ?? new List<Link>()).Select((m, i) => new TorrentClientFile
{
Path = m.Filename,
Bytes = m.Size,
Id = i,
Selected = true,
}).ToList(),
Links = (torrent.Links ?? new List<Link>()).Select(m => m.LinkUrl.ToString()).ToList(),
Ended = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(torrent.CompletionDate),
Speed = torrent.DownloadSpeed,
Seeders = torrent.Seeders
};
}
public async Task<IList<TorrentClientTorrent>> GetTorrents()
{
var results = await GetClient().Magnet.StatusAllAsync();
return results.Select(Map).ToList();
}
public async Task<TorrentClientUser> GetUser()
{
var user = await GetClient().User.GetAsync();
return new TorrentClientUser
{
Username = user.Username,
Expiration = user.PremiumUntil > 0 ? new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(user.PremiumUntil) : null
};
}
public async Task<String> AddMagnet(String magnetLink)
{
var result = await GetClient().Magnet.UploadMagnetAsync(magnetLink);
return result.Id.ToString();
}
public async Task<String> AddFile(Byte[] bytes)
{
var result = await GetClient().Magnet.UploadFileAsync(bytes);
return result.Id.ToString();
}
public async Task<IList<TorrentClientAvailableFile>> GetAvailableFiles(String hash)
{
var isAvailable = await GetClient().Magnet.InstantAvailabilityAsync(hash);
if (isAvailable)
{
return new List<TorrentClientAvailableFile>
{
new TorrentClientAvailableFile
{
Filename = "All files",
Filesize = 0
}
};
}
return new List<TorrentClientAvailableFile>();
}
public Task SelectFiles(Torrent torrent)
{
return Task.CompletedTask;
}
public async Task<TorrentClientTorrent> GetInfo(String torrentId)
{
var result = await GetClient().Magnet.StatusAsync(torrentId);
return Map(result);
}
public async Task Delete(String torrentId)
{
await GetClient().Magnet.DeleteAsync(torrentId);
}
public async Task<String> Unrestrict(String link)
{
var result = await GetClient().Links.DownloadLinkAsync(link);
return result.Link;
}
public async Task<Torrent> UpdateData(Torrent torrent, TorrentClientTorrent torrentClientTorrent)
{
try
{
torrentClientTorrent ??= await GetInfo(torrent.RdId);
if (!String.IsNullOrWhiteSpace(torrentClientTorrent.Filename))
{
torrent.RdName = torrentClientTorrent.Filename;
}
if (torrentClientTorrent.Bytes > 0)
{
torrent.RdSize = torrentClientTorrent.Bytes;
}
if (torrentClientTorrent.Files != null)
{
torrent.RdFiles = JsonConvert.SerializeObject(torrentClientTorrent.Files);
}
torrent.RdHost = torrentClientTorrent.Host;
torrent.RdSplit = torrentClientTorrent.Split;
torrent.RdProgress = torrentClientTorrent.Progress;
torrent.RdAdded = torrentClientTorrent.Added;
torrent.RdEnded = torrentClientTorrent.Ended;
torrent.RdSpeed = torrentClientTorrent.Speed;
torrent.RdSeeders = torrentClientTorrent.Seeders;
torrent.RdStatusRaw = torrentClientTorrent.Status;
torrent.RdStatus = torrentClientTorrent.StatusCode switch
{
0 => TorrentStatus.Processing, // Processing In Queue.
1 => TorrentStatus.Downloading, // Processing Downloading.
2 => TorrentStatus.Downloading, // Processing Compressing / Moving.
3 => TorrentStatus.Uploading, // Processing Uploading.
4 => TorrentStatus.Finished, // Finished Ready.
5 => TorrentStatus.Error, // Error Upload fail.
6 => TorrentStatus.Error, // Error Internal error on unpacking.
7 => TorrentStatus.Error, // Error Not downloaded in 20 min.
8 => TorrentStatus.Error, // Error File too big.
9 => TorrentStatus.Error, // Error Internal error.
10 => TorrentStatus.Error, // Error Download took more than 72h.
11 => TorrentStatus.Error, // Error Deleted on the hoster website
_ => TorrentStatus.Error
};
}
catch (AllDebridException ex)
{
if (ex.ErrorCode == "MAGNET_INVALID_ID")
{
torrent.RdStatusRaw = "deleted";
}
else
{
throw;
}
}
catch (RealDebridException ex)
{
if (ex.ErrorCode == 7)
{
torrent.RdStatusRaw = "deleted";
}
else
{
throw;
}
}
return torrent;
}
public async Task<IList<String>> GetDownloadLinks(Torrent torrent)
{
var magnet = await GetClient().Magnet.StatusAsync(torrent.RdId);
var links = magnet.Links;
Log($"Getting download links", torrent);
if (torrent.DownloadMinSize > 0)
{
var minFileSize = torrent.DownloadMinSize * 1024 * 1024;
Log($"Determining which files are over {minFileSize} bytes", torrent);
links = links.Where(m => m.Size > minFileSize)
.ToList();
Log($"Found {links.Count} files that match the minimum file size criterea", torrent);
}
if (links.Count == 0)
{
Log($"Filtered all files out! Downloading ALL files instead!", torrent);
links = magnet.Links;
}
Log($"Selecting links:");
foreach (var link in links)
{
if (link.Files == null)
{
continue;
}
var fileList = GetFiles(link.Files, "");
Log($"{link.Filename} ({link.Size}b) {link.LinkUrl}, contains files:{Environment.NewLine}{String.Join(Environment.NewLine, fileList)}");
}
Log("", torrent);
return links.Select(m => m.LinkUrl.ToString()).ToList();
}
private static IEnumerable<String> GetFiles(IList<File> files, String parent)
{
var result = new List<String>();
foreach (var file in files)
{
if (!String.IsNullOrWhiteSpace(file.N))
{
result.Add($"{parent}/{file.N}");
}
if (file.E != null && file.E.Value.PurpleEArray != null && file.E.Value.PurpleEArray.Count > 0)
{
result.AddRange(GetFiles(file.E.Value.PurpleEArray, file.N));
}
}
return result;
}
private static IEnumerable<String> GetFiles(IList<FileE1> files, String parent)
{
var result = new List<String>();
foreach (var file in files)
{
if (!String.IsNullOrWhiteSpace(file.N))
{
result.Add($"{parent}/{file.N}");
}
if (file.E != null && file.E.Count > 0)
{
result.AddRange(GetFiles(file.E, file.N));
}
}
return result;
}
private static IEnumerable<String> GetFiles(IList<FileE2> files, String parent)
{
var result = new List<String>();
foreach (var file in files)
{
if (!String.IsNullOrWhiteSpace(file.N))
{
result.Add($"{parent}/{file.N}");
}
}
return result;
}
private void Log(String message, Torrent torrent = null)
{
if (torrent != null)
{
message = $"{message} {torrent.ToLog()}";
}
_logger.LogDebug(message);
}
_logger = logger;
_httpClientFactory = httpClientFactory;
}
}
private AllDebridNETClient GetClient()
{
var apiKey = Settings.Get.RealDebridApiKey;
if (String.IsNullOrWhiteSpace(apiKey))
{
throw new Exception("All-Debrid API Key not set in the settings");
}
var httpClient = _httpClientFactory.CreateClient();
httpClient.Timeout = TimeSpan.FromSeconds(10);
var allDebridNetClient = new AllDebridNETClient("RealDebridClient", apiKey);
return allDebridNetClient;
}
private static TorrentClientTorrent Map(Magnet torrent)
{
return new TorrentClientTorrent
{
Id = torrent.Id.ToString(),
Filename = torrent.Filename,
OriginalFilename = torrent.Filename,
Hash = torrent.Hash,
Bytes = torrent.Size,
OriginalBytes = torrent.Size,
Host = null,
Split = 0,
Progress = (Int64) Math.Round(torrent.Downloaded * 100.0 / torrent.Size),
Status = torrent.Status,
StatusCode = torrent.StatusCode,
Added = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(torrent.UploadDate),
Files = (torrent.Links ?? new List<Link>()).Select((m, i) => new TorrentClientFile
{
Path = m.Filename,
Bytes = m.Size,
Id = i,
Selected = true,
}).ToList(),
Links = (torrent.Links ?? new List<Link>()).Select(m => m.LinkUrl.ToString()).ToList(),
Ended = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(torrent.CompletionDate),
Speed = torrent.DownloadSpeed,
Seeders = torrent.Seeders
};
}
public async Task<IList<TorrentClientTorrent>> GetTorrents()
{
var results = await GetClient().Magnet.StatusAllAsync();
return results.Select(Map).ToList();
}
public async Task<TorrentClientUser> GetUser()
{
var user = await GetClient().User.GetAsync();
return new TorrentClientUser
{
Username = user.Username,
Expiration = user.PremiumUntil > 0 ? new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(user.PremiumUntil) : null
};
}
public async Task<String> AddMagnet(String magnetLink)
{
var result = await GetClient().Magnet.UploadMagnetAsync(magnetLink);
return result.Id.ToString();
}
public async Task<String> AddFile(Byte[] bytes)
{
var result = await GetClient().Magnet.UploadFileAsync(bytes);
return result.Id.ToString();
}
public async Task<IList<TorrentClientAvailableFile>> GetAvailableFiles(String hash)
{
var isAvailable = await GetClient().Magnet.InstantAvailabilityAsync(hash);
if (isAvailable)
{
return new List<TorrentClientAvailableFile>
{
new TorrentClientAvailableFile
{
Filename = "All files",
Filesize = 0
}
};
}
return new List<TorrentClientAvailableFile>();
}
public Task SelectFiles(Torrent torrent)
{
return Task.CompletedTask;
}
public async Task<TorrentClientTorrent> GetInfo(String torrentId)
{
var result = await GetClient().Magnet.StatusAsync(torrentId);
return Map(result);
}
public async Task Delete(String torrentId)
{
await GetClient().Magnet.DeleteAsync(torrentId);
}
public async Task<String> Unrestrict(String link)
{
var result = await GetClient().Links.DownloadLinkAsync(link);
return result.Link;
}
public async Task<Torrent> UpdateData(Torrent torrent, TorrentClientTorrent torrentClientTorrent)
{
try
{
torrentClientTorrent ??= await GetInfo(torrent.RdId);
if (!String.IsNullOrWhiteSpace(torrentClientTorrent.Filename))
{
torrent.RdName = torrentClientTorrent.Filename;
}
if (torrentClientTorrent.Bytes > 0)
{
torrent.RdSize = torrentClientTorrent.Bytes;
}
if (torrentClientTorrent.Files != null)
{
torrent.RdFiles = JsonConvert.SerializeObject(torrentClientTorrent.Files);
}
torrent.RdHost = torrentClientTorrent.Host;
torrent.RdSplit = torrentClientTorrent.Split;
torrent.RdProgress = torrentClientTorrent.Progress;
torrent.RdAdded = torrentClientTorrent.Added;
torrent.RdEnded = torrentClientTorrent.Ended;
torrent.RdSpeed = torrentClientTorrent.Speed;
torrent.RdSeeders = torrentClientTorrent.Seeders;
torrent.RdStatusRaw = torrentClientTorrent.Status;
torrent.RdStatus = torrentClientTorrent.StatusCode switch
{
0 => TorrentStatus.Processing, // Processing In Queue.
1 => TorrentStatus.Downloading, // Processing Downloading.
2 => TorrentStatus.Downloading, // Processing Compressing / Moving.
3 => TorrentStatus.Uploading, // Processing Uploading.
4 => TorrentStatus.Finished, // Finished Ready.
5 => TorrentStatus.Error, // Error Upload fail.
6 => TorrentStatus.Error, // Error Internal error on unpacking.
7 => TorrentStatus.Error, // Error Not downloaded in 20 min.
8 => TorrentStatus.Error, // Error File too big.
9 => TorrentStatus.Error, // Error Internal error.
10 => TorrentStatus.Error, // Error Download took more than 72h.
11 => TorrentStatus.Error, // Error Deleted on the hoster website
_ => TorrentStatus.Error
};
}
catch (AllDebridException ex)
{
if (ex.ErrorCode == "MAGNET_INVALID_ID")
{
torrent.RdStatusRaw = "deleted";
}
else
{
throw;
}
}
catch (RealDebridException ex)
{
if (ex.ErrorCode == 7)
{
torrent.RdStatusRaw = "deleted";
}
else
{
throw;
}
}
return torrent;
}
public async Task<IList<String>> GetDownloadLinks(Torrent torrent)
{
var magnet = await GetClient().Magnet.StatusAsync(torrent.RdId);
var links = magnet.Links;
Log($"Getting download links", torrent);
if (torrent.DownloadMinSize > 0)
{
var minFileSize = torrent.DownloadMinSize * 1024 * 1024;
Log($"Determining which files are over {minFileSize} bytes", torrent);
links = links.Where(m => m.Size > minFileSize)
.ToList();
Log($"Found {links.Count} files that match the minimum file size criterea", torrent);
}
if (links.Count == 0)
{
Log($"Filtered all files out! Downloading ALL files instead!", torrent);
links = magnet.Links;
}
Log($"Selecting links:");
foreach (var link in links)
{
if (link.Files == null)
{
continue;
}
var fileList = GetFiles(link.Files, "");
Log($"{link.Filename} ({link.Size}b) {link.LinkUrl}, contains files:{Environment.NewLine}{String.Join(Environment.NewLine, fileList)}");
}
Log("", torrent);
return links.Select(m => m.LinkUrl.ToString()).ToList();
}
private static IEnumerable<String> GetFiles(IList<File> files, String parent)
{
var result = new List<String>();
foreach (var file in files)
{
if (!String.IsNullOrWhiteSpace(file.N))
{
result.Add($"{parent}/{file.N}");
}
if (file.E != null && file.E.Value.PurpleEArray != null && file.E.Value.PurpleEArray.Count > 0)
{
result.AddRange(GetFiles(file.E.Value.PurpleEArray, file.N));
}
}
return result;
}
private static IEnumerable<String> GetFiles(IList<FileE1> files, String parent)
{
var result = new List<String>();
foreach (var file in files)
{
if (!String.IsNullOrWhiteSpace(file.N))
{
result.Add($"{parent}/{file.N}");
}
if (file.E != null && file.E.Count > 0)
{
result.AddRange(GetFiles(file.E, file.N));
}
}
return result;
}
private static IEnumerable<String> GetFiles(IList<FileE2> files, String parent)
{
var result = new List<String>();
foreach (var file in files)
{
if (!String.IsNullOrWhiteSpace(file.N))
{
result.Add($"{parent}/{file.N}");
}
}
return result;
}
private void Log(String message, Torrent torrent = null)
{
if (torrent != null)
{
message = $"{message} {torrent.ToLog()}";
}
_logger.LogDebug(message);
}
}

View file

@ -1,23 +1,19 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.TorrentClient;
namespace RdtClient.Service.Services.TorrentClients
namespace RdtClient.Service.Services.TorrentClients;
public interface ITorrentClient
{
public interface ITorrentClient
{
Task<IList<TorrentClientTorrent>> GetTorrents();
Task<TorrentClientUser> GetUser();
Task<String> AddMagnet(String magnetLink);
Task<String> AddFile(Byte[] bytes);
Task<IList<TorrentClientAvailableFile>> GetAvailableFiles(String hash);
Task SelectFiles(Torrent torrent);
Task<TorrentClientTorrent> GetInfo(String torrentId);
Task Delete(String torrentId);
Task<String> Unrestrict(String link);
Task<Torrent> UpdateData(Torrent torrent, TorrentClientTorrent torrentClientTorrent);
Task<IList<String>> GetDownloadLinks(Torrent torrent);
}
}
Task<IList<TorrentClientTorrent>> GetTorrents();
Task<TorrentClientUser> GetUser();
Task<String> AddMagnet(String magnetLink);
Task<String> AddFile(Byte[] bytes);
Task<IList<TorrentClientAvailableFile>> GetAvailableFiles(String hash);
Task SelectFiles(Torrent torrent);
Task<TorrentClientTorrent> GetInfo(String torrentId);
Task Delete(String torrentId);
Task<String> Unrestrict(String link);
Task<Torrent> UpdateData(Torrent torrent, TorrentClientTorrent torrentClientTorrent);
Task<IList<String>> GetDownloadLinks(Torrent torrent);
}

View file

@ -1,355 +1,349 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using RDNET;
using RdtClient.Data.Enums;
using RdtClient.Data.Models.TorrentClient;
using RdtClient.Service.Helpers;
namespace RdtClient.Service.Services.TorrentClients
namespace RdtClient.Service.Services.TorrentClients;
public class RealDebridTorrentClient : ITorrentClient
{
public class RealDebridTorrentClient : ITorrentClient
private readonly ILogger<RealDebridTorrentClient> _logger;
private readonly IHttpClientFactory _httpClientFactory;
private TimeSpan _offset;
public RealDebridTorrentClient(ILogger<RealDebridTorrentClient> logger, IHttpClientFactory httpClientFactory)
{
private readonly ILogger<RealDebridTorrentClient> _logger;
private readonly IHttpClientFactory _httpClientFactory;
private TimeSpan _offset;
_logger = logger;
_httpClientFactory = httpClientFactory;
}
public RealDebridTorrentClient(ILogger<RealDebridTorrentClient> logger, IHttpClientFactory httpClientFactory)
private RdNetClient GetClient()
{
var apiKey = Settings.Get.RealDebridApiKey;
if (String.IsNullOrWhiteSpace(apiKey))
{
_logger = logger;
_httpClientFactory = httpClientFactory;
throw new Exception("Real-Debrid API Key not set in the settings");
}
private RdNetClient GetClient()
{
var apiKey = Settings.Get.RealDebridApiKey;
var httpClient = _httpClientFactory.CreateClient();
httpClient.Timeout = TimeSpan.FromSeconds(10);
if (String.IsNullOrWhiteSpace(apiKey))
var rdtNetClient = new RdNetClient(null, httpClient, 5);
rdtNetClient.UseApiAuthentication(apiKey);
// Get the server time to fix up the timezones on results
var serverTime = rdtNetClient.Api.GetIsoTimeAsync().Result;
_offset = serverTime.Offset;
return rdtNetClient;
}
private TorrentClientTorrent Map(Torrent torrent)
{
return new TorrentClientTorrent
{
Id = torrent.Id,
Filename = torrent.Filename,
OriginalFilename = torrent.OriginalFilename,
Hash = torrent.Hash,
Bytes = torrent.Bytes,
OriginalBytes = torrent.OriginalBytes,
Host = torrent.Host,
Split = torrent.Split,
Progress = torrent.Progress,
Status = torrent.Status,
Added = ChangeTimeZone(torrent.Added).Value,
Files = (torrent.Files ?? new List<TorrentFile>()).Select(m => new TorrentClientFile
{
throw new Exception("Real-Debrid API Key not set in the settings");
Path = m.Path,
Bytes = m.Bytes,
Id = m.Id,
Selected = m.Selected
}).ToList(),
Links = torrent.Links,
Ended = ChangeTimeZone(torrent.Ended),
Speed = torrent.Speed,
Seeders = torrent.Seeders,
};
}
public async Task<IList<TorrentClientTorrent>> GetTorrents()
{
var page = 0;
var results = new List<Torrent>();
while (true)
{
var pagedResults = await GetClient().Torrents.GetAsync(page, 100);
results.AddRange(pagedResults);
if (pagedResults.Count == 0)
{
break;
}
var httpClient = _httpClientFactory.CreateClient();
httpClient.Timeout = TimeSpan.FromSeconds(10);
var rdtNetClient = new RdNetClient(null, httpClient, 5);
rdtNetClient.UseApiAuthentication(apiKey);
// Get the server time to fix up the timezones on results
var serverTime = rdtNetClient.Api.GetIsoTimeAsync().Result;
_offset = serverTime.Offset;
return rdtNetClient;
page += 100;
}
private TorrentClientTorrent Map(Torrent torrent)
{
return new TorrentClientTorrent
{
Id = torrent.Id,
Filename = torrent.Filename,
OriginalFilename = torrent.OriginalFilename,
Hash = torrent.Hash,
Bytes = torrent.Bytes,
OriginalBytes = torrent.OriginalBytes,
Host = torrent.Host,
Split = torrent.Split,
Progress = torrent.Progress,
Status = torrent.Status,
Added = ChangeTimeZone(torrent.Added).Value,
Files = (torrent.Files ?? new List<TorrentFile>()).Select(m => new TorrentClientFile
{
Path = m.Path,
Bytes = m.Bytes,
Id = m.Id,
Selected = m.Selected
}).ToList(),
Links = torrent.Links,
Ended = ChangeTimeZone(torrent.Ended),
Speed = torrent.Speed,
Seeders = torrent.Seeders,
};
}
return results.Select(Map).ToList();
}
public async Task<IList<TorrentClientTorrent>> GetTorrents()
{
var page = 0;
var results = new List<Torrent>();
while (true)
{
var pagedResults = await GetClient().Torrents.GetAsync(page, 100);
results.AddRange(pagedResults);
if (pagedResults.Count == 0)
{
break;
}
page += 100;
}
return results.Select(Map).ToList();
}
public async Task<TorrentClientUser> GetUser()
{
var user = await GetClient().User.GetAsync();
public async Task<TorrentClientUser> GetUser()
{
var user = await GetClient().User.GetAsync();
return new TorrentClientUser
{
Username = user.Username,
Expiration = user.Premium > 0 ? user.Expiration : null
};
return new TorrentClientUser
{
Username = user.Username,
Expiration = user.Premium > 0 ? user.Expiration : null
};
}
public async Task<String> AddMagnet(String magnetLink)
{
var result = await GetClient().Torrents.AddMagnetAsync(magnetLink);
return result.Id;
}
public async Task<String> AddFile(Byte[] bytes)
{
var result = await GetClient().Torrents.AddFileAsync(bytes);
return result.Id;
}
public async Task<IList<TorrentClientAvailableFile>> GetAvailableFiles(String hash)
{
var result = await GetClient().Torrents.GetAvailableFiles(hash);
var files = result.SelectMany(m => m.Value).SelectMany(m => m.Value).SelectMany(m => m.Values);
var groups = files.GroupBy(m => $"{m.Filename}-{m.Filesize}");
var torrentClientAvailableFiles = groups.Select(m => new TorrentClientAvailableFile
{
Filename = m.First().Filename,
Filesize = m.First().Filesize
} ).ToList();
return torrentClientAvailableFiles;
}
public async Task SelectFiles(Data.Models.Data.Torrent torrent)
{
var files = torrent.Files;
Log("Seleting files", torrent);
if (torrent.DownloadAction == TorrentDownloadAction.DownloadAvailableFiles)
{
Log($"Determining which files are already available on RealDebrid", torrent);
var availableFiles = await GetAvailableFiles(torrent.Hash);
Log($"Found {files.Count}/{torrent.Files.Count} available files on RealDebrid", torrent);
files = torrent.Files.Where(m => availableFiles.Any(f => m.Path.EndsWith(f.Filename))).ToList();
}
else if (torrent.DownloadAction == TorrentDownloadAction.DownloadAll)
{
Log("Selecting all files", torrent);
files = torrent.Files.ToList();
}
else if (torrent.DownloadAction == TorrentDownloadAction.DownloadManual)
{
Log("Selecting manual selected files", torrent);
files = torrent.Files.Where(m => torrent.ManualFiles.Any(f => m.Path.EndsWith(f))).ToList();
}
public async Task<String> AddMagnet(String magnetLink)
{
var result = await GetClient().Torrents.AddMagnetAsync(magnetLink);
Log($"Selecting {files.Count}/{torrent.Files.Count} files", torrent);
return result.Id;
if (torrent.DownloadAction != TorrentDownloadAction.DownloadManual && torrent.DownloadMinSize > 0)
{
var minFileSize = torrent.DownloadMinSize * 1024 * 1024;
Log($"Determining which files are over {minFileSize} bytes", torrent);
files = files.Where(m => m.Bytes > minFileSize)
.ToList();
Log($"Found {files.Count} files that match the minimum file size criterea", torrent);
}
public async Task<String> AddFile(Byte[] bytes)
if (files.Count == 0)
{
var result = await GetClient().Torrents.AddFileAsync(bytes);
Log($"Filtered all files out! Downloading ALL files instead!", torrent);
return result.Id;
files = torrent.Files;
}
public async Task<IList<TorrentClientAvailableFile>> GetAvailableFiles(String hash)
var fileIds = files.Select(m => m.Id.ToString()).ToArray();
Log($"Selecting files:");
foreach (var file in files)
{
var result = await GetClient().Torrents.GetAvailableFiles(hash);
var files = result.SelectMany(m => m.Value).SelectMany(m => m.Value).SelectMany(m => m.Values);
var groups = files.GroupBy(m => $"{m.Filename}-{m.Filesize}");
var torrentClientAvailableFiles = groups.Select(m => new TorrentClientAvailableFile
{
Filename = m.First().Filename,
Filesize = m.First().Filesize
} ).ToList();
return torrentClientAvailableFiles;
Log($"{file.Id}: {file.Path} ({file.Bytes}b)");
}
public async Task SelectFiles(Data.Models.Data.Torrent torrent)
Log("", torrent);
await GetClient().Torrents.SelectFilesAsync(torrent.RdId, fileIds.ToArray());
}
public async Task<TorrentClientTorrent> GetInfo(String torrentId)
{
var result = await GetClient().Torrents.GetInfoAsync(torrentId);
return Map(result);
}
public async Task Delete(String torrentId)
{
await GetClient().Torrents.DeleteAsync(torrentId);
}
public async Task<String> Unrestrict(String link)
{
var result = await GetClient().Unrestrict.LinkAsync(link);
return result.Download;
}
public async Task<Data.Models.Data.Torrent> UpdateData(Data.Models.Data.Torrent torrent, TorrentClientTorrent torrentClientTorrent)
{
try
{
var files = torrent.Files;
Log("Seleting files", torrent);
if (torrent.DownloadAction == TorrentDownloadAction.DownloadAvailableFiles)
if (torrent == null)
{
Log($"Determining which files are already available on RealDebrid", torrent);
var availableFiles = await GetAvailableFiles(torrent.Hash);
Log($"Found {files.Count}/{torrent.Files.Count} available files on RealDebrid", torrent);
files = torrent.Files.Where(m => availableFiles.Any(f => m.Path.EndsWith(f.Filename))).ToList();
}
else if (torrent.DownloadAction == TorrentDownloadAction.DownloadAll)
{
Log("Selecting all files", torrent);
files = torrent.Files.ToList();
}
else if (torrent.DownloadAction == TorrentDownloadAction.DownloadManual)
{
Log("Selecting manual selected files", torrent);
files = torrent.Files.Where(m => torrent.ManualFiles.Any(f => m.Path.EndsWith(f))).ToList();
return null;
}
Log($"Selecting {files.Count}/{torrent.Files.Count} files", torrent);
if (torrent.DownloadAction != TorrentDownloadAction.DownloadManual && torrent.DownloadMinSize > 0)
{
var minFileSize = torrent.DownloadMinSize * 1024 * 1024;
Log($"Determining which files are over {minFileSize} bytes", torrent);
files = files.Where(m => m.Bytes > minFileSize)
.ToList();
Log($"Found {files.Count} files that match the minimum file size criterea", torrent);
}
if (files.Count == 0)
{
Log($"Filtered all files out! Downloading ALL files instead!", torrent);
files = torrent.Files;
}
var fileIds = files.Select(m => m.Id.ToString()).ToArray();
Log($"Selecting files:");
foreach (var file in files)
{
Log($"{file.Id}: {file.Path} ({file.Bytes}b)");
}
Log("", torrent);
await GetClient().Torrents.SelectFilesAsync(torrent.RdId, fileIds.ToArray());
}
public async Task<TorrentClientTorrent> GetInfo(String torrentId)
{
var result = await GetClient().Torrents.GetInfoAsync(torrentId);
return Map(result);
}
public async Task Delete(String torrentId)
{
await GetClient().Torrents.DeleteAsync(torrentId);
}
public async Task<String> Unrestrict(String link)
{
var result = await GetClient().Unrestrict.LinkAsync(link);
return result.Download;
}
public async Task<Data.Models.Data.Torrent> UpdateData(Data.Models.Data.Torrent torrent, TorrentClientTorrent torrentClientTorrent)
{
try
{
if (torrent == null)
{
return null;
}
var rdTorrent = await GetInfo(torrent.RdId);
if (rdTorrent == null)
{
return torrent;
}
if (!String.IsNullOrWhiteSpace(rdTorrent.Filename))
{
torrent.RdName = rdTorrent.Filename;
}
if (!String.IsNullOrWhiteSpace(rdTorrent.OriginalFilename))
{
torrent.RdName = rdTorrent.OriginalFilename;
}
if (rdTorrent.Bytes > 0)
{
torrent.RdSize = rdTorrent.Bytes;
}
else if (rdTorrent.OriginalBytes > 0)
{
torrent.RdSize = rdTorrent.OriginalBytes;
}
if (rdTorrent.Files != null)
{
torrent.RdFiles = JsonConvert.SerializeObject(rdTorrent.Files);
}
torrent.RdHost = rdTorrent.Host;
torrent.RdSplit = rdTorrent.Split;
torrent.RdProgress = rdTorrent.Progress;
torrent.RdAdded = rdTorrent.Added;
torrent.RdEnded = rdTorrent.Ended;
torrent.RdSpeed = rdTorrent.Speed;
torrent.RdSeeders = rdTorrent.Seeders;
torrent.RdStatusRaw = rdTorrent.Status;
torrent.RdStatus = rdTorrent.Status switch
{
"magnet_error" => TorrentStatus.Error,
"magnet_conversion" => TorrentStatus.Processing,
"waiting_files_selection" => TorrentStatus.WaitingForFileSelection,
"queued" => TorrentStatus.Downloading,
"downloading" => TorrentStatus.Downloading,
"downloaded" => TorrentStatus.Finished,
"error" => TorrentStatus.Error,
"virus" => TorrentStatus.Error,
"compressing" => TorrentStatus.Downloading,
"uploading" => TorrentStatus.Uploading,
"dead" => TorrentStatus.Error,
_ => TorrentStatus.Error
};
}
catch (Exception ex)
{
if (ex.Message == "Resource not found")
{
if (torrent != null)
{
torrent.RdStatusRaw = "deleted";
}
}
else
{
throw;
}
}
return torrent;
}
public async Task<IList<String>> GetDownloadLinks(Data.Models.Data.Torrent torrent)
{
var rdTorrent = await GetInfo(torrent.RdId);
var downloadLinks = rdTorrent.Links.Where(m => !String.IsNullOrWhiteSpace(m)).ToList();
Log($"Found {downloadLinks.Count} links", torrent);
foreach (var link in downloadLinks)
if (rdTorrent == null)
{
Log($"{link}", torrent);
return torrent;
}
// Check if all the links are set that have been selected
if (torrent.Files.Count(m => m.Selected) == downloadLinks.Count)
if (!String.IsNullOrWhiteSpace(rdTorrent.Filename))
{
return downloadLinks;
torrent.RdName = rdTorrent.Filename;
}
// Check if all all the links are set for manual selection
if (torrent.ManualFiles.Count == downloadLinks.Count)
if (!String.IsNullOrWhiteSpace(rdTorrent.OriginalFilename))
{
return downloadLinks;
torrent.RdName = rdTorrent.OriginalFilename;
}
// If there is only 1 link, delay for 1 minute to see if more links pop up.
if (downloadLinks.Count == 1 && torrent.RdEnded.HasValue && DateTime.UtcNow > torrent.RdEnded.Value.ToUniversalTime().AddMinutes(1))
if (rdTorrent.Bytes > 0)
{
return downloadLinks;
torrent.RdSize = rdTorrent.Bytes;
}
return null;
else if (rdTorrent.OriginalBytes > 0)
{
torrent.RdSize = rdTorrent.OriginalBytes;
}
if (rdTorrent.Files != null)
{
torrent.RdFiles = JsonConvert.SerializeObject(rdTorrent.Files);
}
torrent.RdHost = rdTorrent.Host;
torrent.RdSplit = rdTorrent.Split;
torrent.RdProgress = rdTorrent.Progress;
torrent.RdAdded = rdTorrent.Added;
torrent.RdEnded = rdTorrent.Ended;
torrent.RdSpeed = rdTorrent.Speed;
torrent.RdSeeders = rdTorrent.Seeders;
torrent.RdStatusRaw = rdTorrent.Status;
torrent.RdStatus = rdTorrent.Status switch
{
"magnet_error" => TorrentStatus.Error,
"magnet_conversion" => TorrentStatus.Processing,
"waiting_files_selection" => TorrentStatus.WaitingForFileSelection,
"queued" => TorrentStatus.Downloading,
"downloading" => TorrentStatus.Downloading,
"downloaded" => TorrentStatus.Finished,
"error" => TorrentStatus.Error,
"virus" => TorrentStatus.Error,
"compressing" => TorrentStatus.Downloading,
"uploading" => TorrentStatus.Uploading,
"dead" => TorrentStatus.Error,
_ => TorrentStatus.Error
};
}
private DateTimeOffset? ChangeTimeZone(DateTimeOffset? dateTimeOffset)
catch (Exception ex)
{
return dateTimeOffset?.Subtract(_offset).ToOffset(_offset);
}
private void Log(String message, Data.Models.Data.Torrent torrent = null)
{
if (torrent != null)
if (ex.Message == "Resource not found")
{
message = $"{message} {torrent.ToLog()}";
if (torrent != null)
{
torrent.RdStatusRaw = "deleted";
}
}
else
{
throw;
}
_logger.LogDebug(message);
}
return torrent;
}
}
public async Task<IList<String>> GetDownloadLinks(Data.Models.Data.Torrent torrent)
{
var rdTorrent = await GetInfo(torrent.RdId);
var downloadLinks = rdTorrent.Links.Where(m => !String.IsNullOrWhiteSpace(m)).ToList();
Log($"Found {downloadLinks.Count} links", torrent);
foreach (var link in downloadLinks)
{
Log($"{link}", torrent);
}
// Check if all the links are set that have been selected
if (torrent.Files.Count(m => m.Selected) == downloadLinks.Count)
{
return downloadLinks;
}
// Check if all all the links are set for manual selection
if (torrent.ManualFiles.Count == downloadLinks.Count)
{
return downloadLinks;
}
// If there is only 1 link, delay for 1 minute to see if more links pop up.
if (downloadLinks.Count == 1 && torrent.RdEnded.HasValue && DateTime.UtcNow > torrent.RdEnded.Value.ToUniversalTime().AddMinutes(1))
{
return downloadLinks;
}
return null;
}
private DateTimeOffset? ChangeTimeZone(DateTimeOffset? dateTimeOffset)
{
return dateTimeOffset?.Subtract(_offset).ToOffset(_offset);
}
private void Log(String message, Data.Models.Data.Torrent torrent = null)
{
if (torrent != null)
{
message = $"{message} {torrent.ToLog()}";
}
_logger.LogDebug(message);
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,149 +1,143 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.Data;
using RdtClient.Service.Helpers;
using SharpCompress.Archives;
using SharpCompress.Archives.Rar;
using SharpCompress.Common;
namespace RdtClient.Service.Services
namespace RdtClient.Service.Services;
public class UnpackClient
{
public class UnpackClient
public Boolean Finished { get; private set; }
public String Error { get; private set; }
public Int64 BytesTotal { get; private set; }
public Int64 BytesDone { get; private set; }
private readonly Download _download;
private readonly String _destinationPath;
private readonly Torrent _torrent;
private Boolean _cancelled;
private RarArchiveEntry _rarCurrentEntry;
private Dictionary<String, Int64> _rarfileStatus;
public UnpackClient(Download download, String destinationPath)
{
public Boolean Finished { get; private set; }
public String Error { get; private set; }
public Int64 BytesTotal { get; private set; }
public Int64 BytesDone { get; private set; }
private readonly Download _download;
private readonly String _destinationPath;
private readonly Torrent _torrent;
_download = download;
_destinationPath = destinationPath;
_torrent = download.Torrent;
}
private Boolean _cancelled;
private RarArchiveEntry _rarCurrentEntry;
private Dictionary<String, Int64> _rarfileStatus;
public void Start()
{
BytesDone = 0;
BytesTotal = 0;
public UnpackClient(Download download, String destinationPath)
try
{
_download = download;
_destinationPath = destinationPath;
_torrent = download.Torrent;
}
var filePath = DownloadHelper.GetDownloadPath(_destinationPath, _torrent, _download);
public void Start()
{
BytesDone = 0;
BytesTotal = 0;
try
if (filePath == null)
{
var filePath = DownloadHelper.GetDownloadPath(_destinationPath, _torrent, _download);
throw new Exception("Invalid download path");
}
if (filePath == null)
Task.Run(async delegate
{
if (!_cancelled)
{
throw new Exception("Invalid download path");
await Unpack(filePath);
}
Task.Run(async delegate
{
if (!_cancelled)
{
await Unpack(filePath);
}
});
}
catch (Exception ex)
{
Error = $"An unexpected error occurred preparing download {_download.Link} for torrent {_torrent.RdName}: {ex.Message}";
Finished = true;
}
});
}
public void Cancel()
catch (Exception ex)
{
_cancelled = true;
Error = $"An unexpected error occurred preparing download {_download.Link} for torrent {_torrent.RdName}: {ex.Message}";
Finished = true;
}
}
private async Task Unpack(String filePath)
public void Cancel()
{
_cancelled = true;
}
private async Task Unpack(String filePath)
{
try
{
try
{
if (!File.Exists(filePath))
{
return;
}
await using (Stream stream = File.OpenRead(filePath))
{
using var archive = RarArchive.Open(stream);
BytesTotal = archive.TotalSize;
var entries = archive.Entries.Where(entry => !entry.IsDirectory)
.ToList();
_rarfileStatus = entries.ToDictionary(entry => entry.Key, _ => 0L);
_rarCurrentEntry = null;
archive.CompressedBytesRead += ArchiveOnCompressedBytesRead;
var extractPath = _destinationPath;
if (!entries.Any(m => m.Key.StartsWith(_torrent.RdName + @"\")) && !entries.Any(m => m.Key.StartsWith(_torrent.RdName + @"/")))
{
extractPath = Path.Combine(_destinationPath, _torrent.RdName);
}
if (entries.Any(m => m.Key.Contains(".r00")))
{
extractPath = Path.Combine(extractPath, "Temp");
}
foreach (var entry in entries)
{
if (_cancelled)
{
return;
}
_rarCurrentEntry = entry;
entry.WriteToDirectory(extractPath,
new ExtractionOptions
{
ExtractFullPath = true,
Overwrite = true
});
}
}
await FileHelper.Delete(filePath);
}
catch (Exception ex)
{
Error = $"An unexpected error occurred unpacking {_download.Link} for torrent {_torrent.RdName}: {ex.Message}";
}
finally
{
Finished = true;
}
}
private void ArchiveOnCompressedBytesRead(Object sender, CompressedBytesReadEventArgs e)
{
if (_rarCurrentEntry == null)
if (!File.Exists(filePath))
{
return;
}
_rarfileStatus[_rarCurrentEntry.Key] = e.CompressedBytesRead;
await using (Stream stream = File.OpenRead(filePath))
{
using var archive = RarArchive.Open(stream);
BytesDone = _rarfileStatus.Sum(m => m.Value);
BytesTotal = archive.TotalSize;
var entries = archive.Entries.Where(entry => !entry.IsDirectory)
.ToList();
_rarfileStatus = entries.ToDictionary(entry => entry.Key, _ => 0L);
_rarCurrentEntry = null;
archive.CompressedBytesRead += ArchiveOnCompressedBytesRead;
var extractPath = _destinationPath;
if (!entries.Any(m => m.Key.StartsWith(_torrent.RdName + @"\")) && !entries.Any(m => m.Key.StartsWith(_torrent.RdName + @"/")))
{
extractPath = Path.Combine(_destinationPath, _torrent.RdName);
}
if (entries.Any(m => m.Key.Contains(".r00")))
{
extractPath = Path.Combine(extractPath, "Temp");
}
foreach (var entry in entries)
{
if (_cancelled)
{
return;
}
_rarCurrentEntry = entry;
entry.WriteToDirectory(extractPath,
new ExtractionOptions
{
ExtractFullPath = true,
Overwrite = true
});
}
}
await FileHelper.Delete(filePath);
}
catch (Exception ex)
{
Error = $"An unexpected error occurred unpacking {_download.Link} for torrent {_torrent.RdName}: {ex.Message}";
}
finally
{
Finished = true;
}
}
}
private void ArchiveOnCompressedBytesRead(Object sender, CompressedBytesReadEventArgs e)
{
if (_rarCurrentEntry == null)
{
return;
}
_rarfileStatus[_rarCurrentEntry.Key] = e.CompressedBytesRead;
BytesDone = _rarfileStatus.Sum(m => m.Value);
}
}

View file

@ -1,83 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Headers;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace RdtClient.Service.Services
namespace RdtClient.Service.Services;
public class UpdateChecker : BackgroundService
{
public class UpdateChecker : BackgroundService
public static String CurrentVersion { get; private set; }
public static String LatestVersion { get; private set; }
private readonly ILogger<TaskRunner> _logger;
public UpdateChecker(ILogger<TaskRunner> logger)
{
public static String CurrentVersion { get; private set; }
public static String LatestVersion { get; private set; }
private readonly ILogger<TaskRunner> _logger;
public UpdateChecker(ILogger<TaskRunner> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var version = Assembly.GetEntryAssembly()?.GetName().Version?.ToString();
if (String.IsNullOrWhiteSpace(version))
{
CurrentVersion = "";
return;
}
CurrentVersion = $"v{version[..version.LastIndexOf(".", StringComparison.Ordinal)]}";
_logger.LogInformation("UpdateChecker started, currently on version {CurrentVersion}.", CurrentVersion);
while (!stoppingToken.IsCancellationRequested)
{
try
{
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("RdtClient", CurrentVersion));
var response = await httpClient.GetStringAsync($"https://api.github.com/repos/rogerfar/rdt-client/tags?per_page=1", stoppingToken);
var gitHubReleases = JsonConvert.DeserializeObject<List<GitHubReleasesResponse>>(response);
if (gitHubReleases == null || gitHubReleases.Count == 0)
{
return;
}
var latestRelease = gitHubReleases.First().Name;
if (latestRelease != CurrentVersion)
{
_logger.LogInformation("New version found on GitHub: {latestRelease}", latestRelease);
}
LatestVersion = latestRelease;
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error occurred in TorrentDownloadManager.Tick");
}
await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
}
_logger.LogInformation("UpdateChecker stopped.");
}
_logger = logger;
}
public class GitHubReleasesResponse
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
[JsonProperty("name")]
public String Name { get; set; }
var version = Assembly.GetEntryAssembly()?.GetName().Version?.ToString();
if (String.IsNullOrWhiteSpace(version))
{
CurrentVersion = "";
return;
}
CurrentVersion = $"v{version[..version.LastIndexOf(".", StringComparison.Ordinal)]}";
_logger.LogInformation("UpdateChecker started, currently on version {CurrentVersion}.", CurrentVersion);
while (!stoppingToken.IsCancellationRequested)
{
try
{
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("RdtClient", CurrentVersion));
var response = await httpClient.GetStringAsync($"https://api.github.com/repos/rogerfar/rdt-client/tags?per_page=1", stoppingToken);
var gitHubReleases = JsonConvert.DeserializeObject<List<GitHubReleasesResponse>>(response);
if (gitHubReleases == null || gitHubReleases.Count == 0)
{
return;
}
var latestRelease = gitHubReleases.First().Name;
if (latestRelease != CurrentVersion)
{
_logger.LogInformation("New version found on GitHub: {latestRelease}", latestRelease);
}
LatestVersion = latestRelease;
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error occurred in TorrentDownloadManager.Tick");
}
await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
}
_logger.LogInformation("UpdateChecker stopped.");
}
}
public class GitHubReleasesResponse
{
[JsonProperty("name")]
public String Name { get; set; }
}

View file

@ -1,120 +1,116 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using RdtClient.Service.Services;
namespace RdtClient.Web.Controllers
namespace RdtClient.Web.Controllers;
[Route("Api/Authentication")]
public class AuthController : Controller
{
[Route("Api/Authentication")]
public class AuthController : Controller
private readonly Authentication _authentication;
public AuthController(Authentication authentication)
{
private readonly Authentication _authentication;
public AuthController(Authentication authentication)
{
_authentication = authentication;
}
_authentication = authentication;
}
[AllowAnonymous]
[Route("IsLoggedIn")]
[HttpGet]
public async Task<ActionResult> IsLoggedIn()
{
if (User.Identity?.IsAuthenticated == false)
{
var user = await _authentication.GetUser();
if (user == null)
{
return StatusCode(402, "Setup required");
}
return Unauthorized();
}
return Ok();
}
[AllowAnonymous]
[Route("Create")]
[HttpPost]
public async Task<ActionResult> Create([FromBody] AuthControllerLoginRequest request)
{
var user = await _authentication.GetUser();
if (user != null)
{
return StatusCode(401);
}
var registerResult = await _authentication.Register(request.UserName, request.Password);
if (!registerResult.Succeeded)
{
return BadRequest(registerResult.Errors.First().Description);
}
await _authentication.Login(request.UserName, request.Password);
return Ok();
}
[AllowAnonymous]
[Route("Login")]
[HttpPost]
public async Task<ActionResult> Login([FromBody] AuthControllerLoginRequest request)
[AllowAnonymous]
[Route("IsLoggedIn")]
[HttpGet]
public async Task<ActionResult> IsLoggedIn()
{
if (User.Identity?.IsAuthenticated == false)
{
var user = await _authentication.GetUser();
if (user == null)
{
return StatusCode(402);
return StatusCode(402, "Setup required");
}
var result = await _authentication.Login(request.UserName, request.Password);
if (!result.Succeeded)
{
return BadRequest("Invalid credentials");
}
return Ok();
}
[Route("Logout")]
[HttpPost]
public async Task<ActionResult> Logout()
{
await _authentication.Logout();
return Ok();
}
[Route("Update")]
[HttpPost]
public async Task<ActionResult> Update([FromBody] AuthControllerUpdateRequest request)
{
var updateResult = await _authentication.Update(request.UserName, request.Password);
if (!updateResult.Succeeded)
{
return BadRequest(updateResult.Errors.First().Description);
}
return Ok();
return Unauthorized();
}
return Ok();
}
[AllowAnonymous]
[Route("Create")]
[HttpPost]
public async Task<ActionResult> Create([FromBody] AuthControllerLoginRequest request)
{
var user = await _authentication.GetUser();
if (user != null)
{
return StatusCode(401);
}
var registerResult = await _authentication.Register(request.UserName, request.Password);
if (!registerResult.Succeeded)
{
return BadRequest(registerResult.Errors.First().Description);
}
await _authentication.Login(request.UserName, request.Password);
return Ok();
}
public class AuthControllerLoginRequest
[AllowAnonymous]
[Route("Login")]
[HttpPost]
public async Task<ActionResult> Login([FromBody] AuthControllerLoginRequest request)
{
public String UserName { get; set; }
public String Password { get; set; }
}
var user = await _authentication.GetUser();
public class AuthControllerUpdateRequest
if (user == null)
{
return StatusCode(402);
}
var result = await _authentication.Login(request.UserName, request.Password);
if (!result.Succeeded)
{
return BadRequest("Invalid credentials");
}
return Ok();
}
[Route("Logout")]
[HttpPost]
public async Task<ActionResult> Logout()
{
public String UserName { get; set; }
public String Password { get; set; }
await _authentication.Logout();
return Ok();
}
[Route("Update")]
[HttpPost]
public async Task<ActionResult> Update([FromBody] AuthControllerUpdateRequest request)
{
var updateResult = await _authentication.Update(request.UserName, request.Password);
if (!updateResult.Succeeded)
{
return BadRequest(updateResult.Errors.First().Description);
}
return Ok();
}
}
public class AuthControllerLoginRequest
{
public String UserName { get; set; }
public String Password { get; set; }
}
public class AuthControllerUpdateRequest
{
public String UserName { get; set; }
public String Password { get; set; }
}

View file

@ -1,500 +1,501 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using RdtClient.Data.Models.QBittorrent;
using RdtClient.Data.Models.QBittorrent.QuickType;
using RdtClient.Service.Services;
namespace RdtClient.Web.Controllers
namespace RdtClient.Web.Controllers;
/// <summary>
/// This API behaves as a regular QBittorrent 4+ API
/// Documentation is found here: https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)
/// </summary>
[ApiController]
[Route("api/v2")]
public class QBittorrentController : Controller
{
/// <summary>
/// This API behaves as a regular QBittorrent 4+ API
/// Documentation is found here: https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)
/// </summary>
[ApiController]
[Route("api/v2")]
public class QBittorrentController : Controller
private readonly QBittorrent _qBittorrent;
public QBittorrentController(QBittorrent qBittorrent)
{
private readonly QBittorrent _qBittorrent;
_qBittorrent = qBittorrent;
}
public QBittorrentController(QBittorrent qBittorrent)
[AllowAnonymous]
[Route("auth/login")]
[HttpGet]
public async Task<ActionResult> AuthLogin([FromQuery] QBAuthLoginRequest request)
{
var result = await _qBittorrent.AuthLogin(request.UserName, request.Password);
if (result)
{
_qBittorrent = qBittorrent;
return Ok("Ok.");
}
[AllowAnonymous]
[Route("auth/login")]
[HttpGet]
public async Task<ActionResult> AuthLogin([FromQuery] QBAuthLoginRequest request)
{
var result = await _qBittorrent.AuthLogin(request.UserName, request.Password);
if (result)
{
return Ok("Ok.");
}
return Ok("Fails.");
}
return Ok("Fails.");
}
[AllowAnonymous]
[Route("auth/login")]
[HttpPost]
public async Task<ActionResult> AuthLoginPost([FromForm] QBAuthLoginRequest request)
{
return await AuthLogin(request);
}
[AllowAnonymous]
[Route("auth/login")]
[HttpPost]
public async Task<ActionResult> AuthLoginPost([FromForm] QBAuthLoginRequest request)
{
return await AuthLogin(request);
}
[Authorize]
[Route("auth/logout")]
[HttpGet]
[HttpPost]
public async Task<ActionResult> AuthLogout()
{
await _qBittorrent.AuthLogout();
return Ok();
}
[Authorize]
[Route("auth/logout")]
[HttpGet]
[HttpPost]
public async Task<ActionResult> AuthLogout()
{
await _qBittorrent.AuthLogout();
return Ok();
}
[Route("app/version")]
[HttpGet]
[HttpPost]
public ActionResult AppVersion()
{
return Ok("v4.3.2");
}
[Route("app/version")]
[HttpGet]
[HttpPost]
public ActionResult AppVersion()
{
return Ok("v4.3.2");
}
[Route("app/webapiVersion")]
[HttpGet]
[HttpPost]
public ActionResult AppWebVersion()
{
return Ok("2.7");
}
[Route("app/webapiVersion")]
[HttpGet]
[HttpPost]
public ActionResult AppWebVersion()
{
return Ok("2.7");
}
[Route("app/buildInfo")]
[HttpGet]
[HttpPost]
public ActionResult AppBuildInfo()
[Route("app/buildInfo")]
[HttpGet]
[HttpPost]
public ActionResult AppBuildInfo()
{
var result = new AppBuildInfo
{
var result = new AppBuildInfo
{
Bitness = 64,
Boost = "1.75.0",
Libtorrent = "1.2.11.0",
Openssl = "1.1.1i",
Qt = "5.15.2",
Zlib = "1.2.11"
};
return Ok(result);
}
Bitness = 64,
Boost = "1.75.0",
Libtorrent = "1.2.11.0",
Openssl = "1.1.1i",
Qt = "5.15.2",
Zlib = "1.2.11"
};
return Ok(result);
}
[Authorize]
[Route("app/shutdown")]
[HttpGet]
[HttpPost]
public ActionResult AppShutdown()
{
return Ok();
}
[Authorize]
[Route("app/shutdown")]
[HttpGet]
[HttpPost]
public ActionResult AppShutdown()
{
return Ok();
}
[AllowAnonymous]
[Route("app/preferences")]
[HttpGet]
[HttpPost]
public async Task<ActionResult<AppPreferences>> AppPreferences()
{
var result = await _qBittorrent.AppPreferences();
return Ok(result);
}
[AllowAnonymous]
[Route("app/preferences")]
[HttpGet]
[HttpPost]
public async Task<ActionResult<AppPreferences>> AppPreferences()
{
var result = await _qBittorrent.AppPreferences();
return Ok(result);
}
[Authorize]
[Route("app/setPreferences")]
[HttpGet]
[HttpPost]
public ActionResult AppSetPreferences()
{
return Ok();
}
[Authorize]
[Route("app/setPreferences")]
[HttpGet]
[HttpPost]
public ActionResult AppSetPreferences()
{
return Ok();
}
[Authorize]
[Route("app/defaultSavePath")]
[HttpGet]
[HttpPost]
public ActionResult<AppPreferences> AppDefaultSavePath()
{
var result = _qBittorrent.AppDefaultSavePath();
return Ok(result);
}
[Authorize]
[Route("app/defaultSavePath")]
[HttpGet]
[HttpPost]
public ActionResult<AppPreferences> AppDefaultSavePath()
{
var result = _qBittorrent.AppDefaultSavePath();
return Ok(result);
}
[Authorize]
[Route("torrents/info")]
[HttpGet]
[HttpPost]
public async Task<ActionResult<IList<TorrentInfo>>> TorrentsInfo([FromQuery] QBTorrentsHashRequest request)
[Authorize]
[Route("torrents/info")]
[HttpGet]
[HttpPost]
public async Task<ActionResult<IList<TorrentInfo>>> TorrentsInfo([FromQuery] QBTorrentsInfoRequest request)
{
var results = await _qBittorrent.TorrentInfo();
if (!String.IsNullOrWhiteSpace(request.Category))
{
var results = await _qBittorrent.TorrentInfo();
IList<TorrentInfo> filteredResults = new List<TorrentInfo>();
if (request.Category == null) {
filteredResults = results;
} else if (request.Category == "") {
filteredResults = results.Where(m => m.Category == null).ToList();
} else {
filteredResults = results.Where(m => m.Category == request.Category).ToList();
}
return Ok(filteredResults);
results = results.Where(m => m.Category == request.Category).ToList();
}
[Authorize]
[Route("torrents/files")]
[HttpGet]
public async Task<ActionResult<IList<TorrentFileItem>>> TorrentsFiles([FromQuery] QBTorrentsHashRequest request)
{
var result = await _qBittorrent.TorrentFileContents(request.Hash);
return Ok(results);
}
if (result == null)
[Authorize]
[Route("torrents/info")]
[HttpPost]
public async Task<ActionResult<IList<TorrentInfo>>> TorrentsFilesPost([FromForm] QBTorrentsInfoRequest request)
{
return await TorrentsInfo(request);
}
[Authorize]
[Route("torrents/files")]
[HttpGet]
public async Task<ActionResult<IList<TorrentFileItem>>> TorrentsFiles([FromQuery] QBTorrentsHashRequest request)
{
var result = await _qBittorrent.TorrentFileContents(request.Hash);
if (result == null)
{
return NotFound();
}
return Ok(result);
}
[Authorize]
[Route("torrents/files")]
[HttpPost]
public async Task<ActionResult<IList<TorrentFileItem>>> TorrentsFilesPost([FromForm] QBTorrentsHashRequest request)
{
return await TorrentsFiles(request);
}
[Authorize]
[Route("torrents/properties")]
[HttpGet]
public async Task<ActionResult<IList<TorrentInfo>>> TorrentsProperties([FromQuery] QBTorrentsHashRequest request)
{
var result = await _qBittorrent.TorrentProperties(request.Hash);
if (result == null)
{
return NotFound();
}
return Ok(result);
}
[Authorize]
[Route("torrents/properties")]
[HttpPost]
public async Task<ActionResult<IList<TorrentInfo>>> TorrentsPropertiesPost([FromForm] QBTorrentsHashRequest request)
{
return await TorrentsProperties(request);
}
[Authorize]
[Route("torrents/pause")]
[HttpGet]
public async Task<ActionResult> TorrentsPause([FromQuery] QBTorrentsHashesRequest request)
{
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
{
await _qBittorrent.TorrentPause(hash);
}
return Ok();
}
[Authorize]
[Route("torrents/topPrio")]
[HttpPost]
public async Task<ActionResult> TorrentsPausePost([FromForm] QBTorrentsHashesRequest request)
{
return await TorrentsPause(request);
}
[Authorize]
[Route("torrents/resume")]
[HttpGet]
public async Task<ActionResult> TorrentsResume([FromQuery] QBTorrentsHashesRequest request)
{
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
{
await _qBittorrent.TorrentResume(hash);
}
return Ok();
}
[Authorize]
[Route("torrents/topPrio")]
[HttpPost]
public async Task<ActionResult> TorrentsResumePost([FromForm] QBTorrentsHashesRequest request)
{
return await TorrentsResume(request);
}
[Authorize]
[Route("torrents/setShareLimits")]
[HttpGet]
[HttpPost]
public ActionResult TorrentsSetShareLimits()
{
return Ok();
}
[Authorize]
[Route("torrents/delete")]
[HttpGet]
public async Task<ActionResult> TorrentsDelete([FromQuery] QBTorrentsDeleteRequest request)
{
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
{
await _qBittorrent.TorrentsDelete(hash, request.DeleteFiles);
}
return Ok();
}
[Authorize]
[Route("torrents/delete")]
[HttpPost]
public async Task<ActionResult> TorrentsDeletePost([FromForm] QBTorrentsDeleteRequest request)
{
return await TorrentsDelete(request);
}
[Authorize]
[Route("torrents/add")]
[HttpGet]
public async Task<ActionResult> TorrentsAdd([FromQuery] QBTorrentsAddRequest request)
{
var urls = request.Urls.Split("\n");
foreach (var url in urls)
{
if (url.StartsWith("magnet"))
{
return NotFound();
await _qBittorrent.TorrentsAddMagnet(url.Trim(), request.Category, null);
}
return Ok(result);
}
[Authorize]
[Route("torrents/files")]
[HttpPost]
public async Task<ActionResult<IList<TorrentFileItem>>> TorrentsFilesPost([FromForm] QBTorrentsHashRequest request)
{
return await TorrentsFiles(request);
}
[Authorize]
[Route("torrents/properties")]
[HttpGet]
public async Task<ActionResult<IList<TorrentInfo>>> TorrentsProperties([FromQuery] QBTorrentsHashRequest request)
{
var result = await _qBittorrent.TorrentProperties(request.Hash);
if (result == null)
else if (url.StartsWith("http"))
{
return NotFound();
var httpClient = new HttpClient();
var result = await httpClient.GetByteArrayAsync(url);
await _qBittorrent.TorrentsAddFile(result, request.Category, null);
}
return Ok(result);
}
[Authorize]
[Route("torrents/properties")]
[HttpPost]
public async Task<ActionResult<IList<TorrentInfo>>> TorrentsPropertiesPost([FromForm] QBTorrentsHashRequest request)
{
return await TorrentsProperties(request);
}
[Authorize]
[Route("torrents/pause")]
[HttpGet]
public async Task<ActionResult> TorrentsPause([FromQuery] QBTorrentsHashesRequest request)
{
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
else
{
await _qBittorrent.TorrentPause(hash);
throw new Exception($"Invalid torrent link format {url}");
}
return Ok();
}
[Authorize]
[Route("torrents/topPrio")]
[HttpPost]
public async Task<ActionResult> TorrentsPausePost([FromForm] QBTorrentsHashesRequest request)
{
return await TorrentsPause(request);
}
return Ok();
}
[Authorize]
[Route("torrents/resume")]
[HttpGet]
public async Task<ActionResult> TorrentsResume([FromQuery] QBTorrentsHashesRequest request)
[Authorize]
[Route("torrents/add")]
[HttpPost]
public async Task<ActionResult> TorrentsAddPost([FromForm] QBTorrentsAddRequest request)
{
foreach (var file in Request.Form.Files)
{
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
if (file.Length > 0)
{
await _qBittorrent.TorrentResume(hash);
await using var target = new MemoryStream();
await file.CopyToAsync(target);
var fileBytes = target.ToArray();
await _qBittorrent.TorrentsAddFile(fileBytes, request.Category, request.Priority);
}
return Ok();
}
[Authorize]
[Route("torrents/topPrio")]
[HttpPost]
public async Task<ActionResult> TorrentsResumePost([FromForm] QBTorrentsHashesRequest request)
{
return await TorrentsResume(request);
}
[Authorize]
[Route("torrents/setShareLimits")]
[HttpGet]
[HttpPost]
public ActionResult TorrentsSetShareLimits()
{
return Ok();
}
[Authorize]
[Route("torrents/delete")]
[HttpGet]
public async Task<ActionResult> TorrentsDelete([FromQuery] QBTorrentsDeleteRequest request)
{
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
{
await _qBittorrent.TorrentsDelete(hash, request.DeleteFiles);
}
return Ok();
}
[Authorize]
[Route("torrents/delete")]
[HttpPost]
public async Task<ActionResult> TorrentsDeletePost([FromForm] QBTorrentsDeleteRequest request)
{
return await TorrentsDelete(request);
}
[Authorize]
[Route("torrents/add")]
[HttpGet]
public async Task<ActionResult> TorrentsAdd([FromQuery] QBTorrentsAddRequest request)
{
var urls = request.Urls.Split("\n");
foreach (var url in urls)
{
if (url.StartsWith("magnet"))
{
await _qBittorrent.TorrentsAddMagnet(url.Trim(), request.Category, null);
}
else if (url.StartsWith("http"))
{
var httpClient = new HttpClient();
var result = await httpClient.GetByteArrayAsync(url);
await _qBittorrent.TorrentsAddFile(result, request.Category, null);
}
else
{
throw new Exception($"Invalid torrent link format {url}");
}
}
return Ok();
}
[Authorize]
[Route("torrents/add")]
[HttpPost]
public async Task<ActionResult> TorrentsAddPost([FromForm] QBTorrentsAddRequest request)
{
foreach (var file in Request.Form.Files)
{
if (file.Length > 0)
{
await using var target = new MemoryStream();
await file.CopyToAsync(target);
var fileBytes = target.ToArray();
await _qBittorrent.TorrentsAddFile(fileBytes, request.Category, request.Priority);
}
}
if (request.Urls != null)
{
return await TorrentsAdd(request);
}
return Ok();
if (request.Urls != null)
{
return await TorrentsAdd(request);
}
return Ok();
}
[Authorize]
[Route("torrents/setCategory")]
[HttpGet]
public async Task<ActionResult> TorrentsSetCategory([FromQuery] QBTorrentsSetCategoryRequest request)
[Authorize]
[Route("torrents/setCategory")]
[HttpGet]
public async Task<ActionResult> TorrentsSetCategory([FromQuery] QBTorrentsSetCategoryRequest request)
{
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
{
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
{
await _qBittorrent.TorrentsSetCategory(hash, request.Category);
}
return Ok();
await _qBittorrent.TorrentsSetCategory(hash, request.Category);
}
[Authorize]
[Route("torrents/setCategory")]
[HttpPost]
public async Task<ActionResult> TorrentsSetCategoryPost([FromForm] QBTorrentsSetCategoryRequest request)
return Ok();
}
[Authorize]
[Route("torrents/setCategory")]
[HttpPost]
public async Task<ActionResult> TorrentsSetCategoryPost([FromForm] QBTorrentsSetCategoryRequest request)
{
return await TorrentsSetCategory(request);
}
[Authorize]
[Route("torrents/categories")]
[HttpGet]
[HttpPost]
public async Task<ActionResult<IDictionary<String, TorrentCategory>>> TorrentsCategories()
{
var categories = await _qBittorrent.TorrentsCategories();
return Ok(categories);
}
[Authorize]
[Route("torrents/createCategory")]
[HttpGet]
[HttpPost]
public async Task<ActionResult> TorrentsCreateCategory([FromForm] QBTorrentsCreateCategoryRequest request)
{
if (String.IsNullOrWhiteSpace(request.Category))
{
return await TorrentsSetCategory(request);
return BadRequest("category name is empty");
}
[Authorize]
[Route("torrents/categories")]
[HttpGet]
[HttpPost]
public async Task<ActionResult<IDictionary<String, TorrentCategory>>> TorrentsCategories()
{
var categories = await _qBittorrent.TorrentsCategories();
await _qBittorrent.CategoryCreate(request.Category.Trim());
return Ok(categories);
}
[Authorize]
[Route("torrents/createCategory")]
[HttpGet]
[HttpPost]
public async Task<ActionResult> TorrentsCreateCategory([FromForm] QBTorrentsCreateCategoryRequest request)
{
if (String.IsNullOrWhiteSpace(request.Category))
{
return BadRequest("category name is empty");
}
await _qBittorrent.CategoryCreate(request.Category.Trim());
return Ok();
}
return Ok();
}
[Authorize]
[Route("torrents/removeCategories")]
[HttpGet]
[HttpPost]
public async Task<ActionResult> TorrentsRemoveCategories([FromForm] QBTorrentsRemoveCategoryRequest request)
{
if (String.IsNullOrWhiteSpace(request.Categories))
{
return Ok();
}
var categories = request.Categories.Split('\n', StringSplitOptions.RemoveEmptyEntries);
foreach (var category in categories)
{
await _qBittorrent.CategoryRemove(category.Trim());
}
return Ok();
}
[Authorize]
[Route("torrents/setForcestart")]
[HttpGet]
[HttpPost]
public ActionResult TorrentsSetForceStart()
{
return Ok();
}
[Authorize]
[Route("torrents/topPrio")]
[HttpGet]
public async Task<ActionResult> TorrentsTopPrio([FromQuery] QBTorrentsHashesRequest request)
{
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
{
await _qBittorrent.TorrentsTopPrio(hash);
}
return Ok();
}
[Authorize]
[Route("torrents/topPrio")]
[HttpPost]
public async Task<ActionResult> TorrentsTopPrioPost([FromForm] QBTorrentsHashesRequest request)
{
return await TorrentsTopPrio(request);
}
[Authorize]
[Route("sync/maindata")]
[HttpGet]
public async Task<ActionResult> SyncMainData()
{
var result = await _qBittorrent.SyncMainData();
return Ok(result);
}
[Authorize]
[Route("sync/maindata")]
[HttpPost]
public async Task<ActionResult> SyncMainDataPost()
{
return await SyncMainData();
}
}
public class QBAuthLoginRequest
[Authorize]
[Route("torrents/removeCategories")]
[HttpGet]
[HttpPost]
public async Task<ActionResult> TorrentsRemoveCategories([FromForm] QBTorrentsRemoveCategoryRequest request)
{
public String UserName { get; set; }
public String Password { get; set; }
if (String.IsNullOrWhiteSpace(request.Categories))
{
return Ok();
}
var categories = request.Categories.Split('\n', StringSplitOptions.RemoveEmptyEntries);
foreach (var category in categories)
{
await _qBittorrent.CategoryRemove(category.Trim());
}
return Ok();
}
[Authorize]
[Route("torrents/setForcestart")]
[HttpGet]
[HttpPost]
public ActionResult TorrentsSetForceStart()
{
return Ok();
}
[Authorize]
[Route("torrents/topPrio")]
[HttpGet]
public async Task<ActionResult> TorrentsTopPrio([FromQuery] QBTorrentsHashesRequest request)
{
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
{
await _qBittorrent.TorrentsTopPrio(hash);
}
return Ok();
}
[Authorize]
[Route("torrents/topPrio")]
[HttpPost]
public async Task<ActionResult> TorrentsTopPrioPost([FromForm] QBTorrentsHashesRequest request)
{
return await TorrentsTopPrio(request);
}
[Authorize]
[Route("sync/maindata")]
[HttpGet]
public async Task<ActionResult> SyncMainData()
{
var result = await _qBittorrent.SyncMainData();
return Ok(result);
}
[Authorize]
[Route("sync/maindata")]
[HttpPost]
public async Task<ActionResult> SyncMainDataPost()
{
return await SyncMainData();
}
}
public class QBAuthLoginRequest
{
public String UserName { get; set; }
public String Password { get; set; }
}
public class QBTorrentsInfoRequest
{
public String Category { get; set; }
}
public class QBTorrentsHashRequest
{
public String Hash { get; set; }
public String Category { get; set; }
}
public class QBTorrentsHashRequest
{
public String Hash { get; set; }
public String Category { get; set; }
}
public class QBTorrentsDeleteRequest
{
public String Hashes { get; set; }
public Boolean DeleteFiles { get; set; }
}
public class QBTorrentsDeleteRequest
{
public String Hashes { get; set; }
public Boolean DeleteFiles { get; set; }
}
public class QBTorrentsAddRequest
{
public String Urls { get; set; }
public String Category { get; set; }
public Int32? Priority { get; set; }
}
public class QBTorrentsAddRequest
{
public String Urls { get; set; }
public String Category { get; set; }
public Int32? Priority { get; set; }
}
public class QBTorrentsSetCategoryRequest
{
public String Hashes { get; set; }
public String Category { get; set; }
}
public class QBTorrentsSetCategoryRequest
{
public String Hashes { get; set; }
public String Category { get; set; }
}
public class QBTorrentsCreateCategoryRequest
{
public String Category { get; set; }
}
public class QBTorrentsCreateCategoryRequest
{
public String Category { get; set; }
}
public class QBTorrentsRemoveCategoryRequest
{
public String Categories { get; set; }
}
public class QBTorrentsRemoveCategoryRequest
{
public String Categories { get; set; }
}
public class QBTorrentsHashesRequest
{
public String Hashes { get; set; }
}
public class QBTorrentsHashesRequest
{
public String Hashes { get; set; }
}

View file

@ -1,111 +1,106 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.Internal;
using RdtClient.Service.Services;
using Serilog.Events;
namespace RdtClient.Web.Controllers
namespace RdtClient.Web.Controllers;
[Authorize]
[Route("Api/Settings")]
public class SettingsController : Controller
{
[Authorize]
[Route("Api/Settings")]
public class SettingsController : Controller
private readonly Settings _settings;
private readonly Torrents _torrents;
public SettingsController(Settings settings, Torrents torrents)
{
private readonly Settings _settings;
private readonly Torrents _torrents;
_settings = settings;
_torrents = torrents;
}
public SettingsController(Settings settings, Torrents torrents)
{
_settings = settings;
_torrents = torrents;
}
[HttpGet]
[Route("")]
public async Task<ActionResult<IList<Setting>>> Get()
{
var result = await _settings.GetAll();
return Ok(result);
}
[HttpGet]
[Route("")]
public async Task<ActionResult<IList<Setting>>> Get()
{
var result = await _settings.GetAll();
return Ok(result);
}
[HttpPut]
[Route("")]
public async Task<ActionResult> Update([FromBody] SettingsControllerUpdateRequest request)
{
await _settings.Update(request.Settings);
[HttpPut]
[Route("")]
public async Task<ActionResult> Update([FromBody] SettingsControllerUpdateRequest request)
{
await _settings.Update(request.Settings);
if (!Enum.TryParse<LogEventLevel>(Settings.Get.LogLevel, out var logLevel))
{
logLevel = LogEventLevel.Information;
}
Program.LoggingLevelSwitch.MinimumLevel = logLevel;
return Ok();
}
[HttpGet]
[Route("Profile")]
public async Task<ActionResult<Profile>> Profile()
if (!Enum.TryParse<LogEventLevel>(Settings.Get.LogLevel, out var logLevel))
{
var profile = await _torrents.GetProfile();
return Ok(profile);
logLevel = LogEventLevel.Information;
}
Settings.LoggingLevelSwitch.MinimumLevel = logLevel;
return Ok();
}
[HttpGet]
[Route("Profile")]
public async Task<ActionResult<Profile>> Profile()
{
var profile = await _torrents.GetProfile();
return Ok(profile);
}
[HttpPost]
[Route("TestPath")]
public async Task<ActionResult> TestPath([FromBody] SettingsControllerTestPathRequest request)
{
await _settings.TestPath(request.Path);
[HttpPost]
[Route("TestPath")]
public async Task<ActionResult> TestPath([FromBody] SettingsControllerTestPathRequest request)
{
await _settings.TestPath(request.Path);
return Ok();
}
return Ok();
}
[HttpGet]
[Route("TestDownloadSpeed")]
public async Task<ActionResult> TestDownloadSpeed(CancellationToken cancellationToken)
{
var downloadSpeed = await _settings.TestDownloadSpeed(cancellationToken);
[HttpGet]
[Route("TestDownloadSpeed")]
public async Task<ActionResult> TestDownloadSpeed(CancellationToken cancellationToken)
{
var downloadSpeed = await _settings.TestDownloadSpeed(cancellationToken);
return Ok(downloadSpeed);
}
return Ok(downloadSpeed);
}
[HttpGet]
[Route("TestWriteSpeed")]
public async Task<ActionResult> TestWriteSpeed()
{
var writeSpeed = await _settings.TestWriteSpeed();
[HttpGet]
[Route("TestWriteSpeed")]
public async Task<ActionResult> TestWriteSpeed()
{
var writeSpeed = await _settings.TestWriteSpeed();
return Ok(writeSpeed);
}
[HttpPost]
[Route("TestAria2cConnection")]
public async Task<ActionResult<String>> TestAria2cConnection([FromBody] SettingsControllerTestAria2cConnectionRequest request)
{
var version = await _settings.GetAria2cVersion(request.Url, request.Secret);
return Ok(version);
}
return Ok(writeSpeed);
}
public class SettingsControllerUpdateRequest
[HttpPost]
[Route("TestAria2cConnection")]
public async Task<ActionResult<String>> TestAria2cConnection([FromBody] SettingsControllerTestAria2cConnectionRequest request)
{
public IList<Setting> Settings { get; set; }
}
var version = await _settings.GetAria2cVersion(request.Url, request.Secret);
public class SettingsControllerTestPathRequest
{
public String Path { get; set; }
return Ok(version);
}
}
public class SettingsControllerTestAria2cConnectionRequest
{
public String Url { get; set; }
public String Secret { get; set; }
}
public class SettingsControllerUpdateRequest
{
public IList<Setting> Settings { get; set; }
}
public class SettingsControllerTestPathRequest
{
public String Path { get; set; }
}
public class SettingsControllerTestAria2cConnectionRequest
{
public String Url { get; set; }
public String Secret { get; set; }
}

View file

@ -1,220 +1,213 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MonoTorrent;
using RdtClient.Service.Helpers;
using RdtClient.Service.Services;
using Torrent = RdtClient.Data.Models.Data.Torrent;
namespace RdtClient.Web.Controllers
{
[Authorize]
[Route("Api/Torrents")]
public class TorrentsController : Controller
{
private readonly TorrentRunner _torrentRunner;
private readonly Torrents _torrents;
namespace RdtClient.Web.Controllers;
public TorrentsController(Torrents torrents, TorrentRunner torrentRunner)
[Authorize]
[Route("Api/Torrents")]
public class TorrentsController : Controller
{
private readonly TorrentRunner _torrentRunner;
private readonly Torrents _torrents;
public TorrentsController(Torrents torrents, TorrentRunner torrentRunner)
{
_torrents = torrents;
_torrentRunner = torrentRunner;
}
[HttpGet]
[Route("")]
public async Task<ActionResult<IList<Torrent>>> GetAll()
{
var results = await _torrents.Get();
// Prevent infinite recursion when serializing
foreach (var file in results.SelectMany(torrent => torrent.Downloads))
{
_torrents = torrents;
_torrentRunner = torrentRunner;
file.Torrent = null;
}
[HttpGet]
[Route("")]
public async Task<ActionResult<IList<Torrent>>> GetAll()
{
var results = await _torrents.Get();
return Ok(results);
}
// Prevent infinite recursion when serializing
foreach (var file in results.SelectMany(torrent => torrent.Downloads))
[HttpGet]
[Route("Get/{torrentId:guid}")]
public async Task<ActionResult<Torrent>> GetById(Guid torrentId)
{
var torrent = await _torrents.GetById(torrentId);
if (torrent?.Downloads != null)
{
foreach (var file in torrent.Downloads)
{
file.Torrent = null;
}
return Ok(results);
}
[HttpGet]
[Route("Get/{torrentId:guid}")]
public async Task<ActionResult<Torrent>> GetById(Guid torrentId)
return Ok(torrent);
}
/// <summary>
/// Used for debugging only. Force a tick.
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("Tick")]
public async Task<ActionResult> Tick()
{
await _torrentRunner.Tick();
return Ok();
}
[HttpPost]
[Route("UploadFile")]
public async Task<ActionResult> UploadFile([FromForm] IFormFile file,
[ModelBinder(BinderType = typeof(JsonModelBinder))]
TorrentControllerUploadFileRequest formData)
{
if (!ModelState.IsValid)
{
var torrent = await _torrents.GetById(torrentId);
var errors = ModelState.Select(x => x.Value.Errors)
.Where(y => y.Count > 0)
.ToList();
if (torrent?.Downloads != null)
{
foreach (var file in torrent.Downloads)
{
file.Torrent = null;
}
}
return Ok(torrent);
return BadRequest(errors);
}
/// <summary>
/// Used for debugging only. Force a tick.
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("Tick")]
public async Task<ActionResult> Tick()
if (file == null || file.Length <= 0)
{
await _torrentRunner.Tick();
return Ok();
throw new Exception("Invalid torrent file");
}
[HttpPost]
[Route("UploadFile")]
public async Task<ActionResult> UploadFile([FromForm] IFormFile file,
[ModelBinder(BinderType = typeof(JsonModelBinder))]
TorrentControllerUploadFileRequest formData)
var fileStream = file.OpenReadStream();
await using var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream);
var bytes = memoryStream.ToArray();
await _torrents.UploadFile(bytes, formData.Torrent);
return Ok();
}
[HttpPost]
[Route("UploadMagnet")]
public async Task<ActionResult> UploadMagnet([FromBody] TorrentControllerUploadMagnetRequest request)
{
if (!ModelState.IsValid)
{
if (!ModelState.IsValid)
{
var errors = ModelState.Select(x => x.Value.Errors)
.Where(y => y.Count > 0)
.ToList();
var errors = ModelState.Select(x => x.Value.Errors)
.Where(y => y.Count > 0)
.ToList();
return BadRequest(errors);
}
if (file == null || file.Length <= 0)
{
throw new Exception("Invalid torrent file");
}
var fileStream = file.OpenReadStream();
await using var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream);
var bytes = memoryStream.ToArray();
await _torrents.UploadFile(bytes, formData.Torrent);
return Ok();
return BadRequest(errors);
}
[HttpPost]
[Route("UploadMagnet")]
public async Task<ActionResult> UploadMagnet([FromBody] TorrentControllerUploadMagnetRequest request)
await _torrents.UploadMagnet(request.MagnetLink, request.Torrent);
return Ok();
}
[HttpPost]
[Route("CheckFiles")]
public async Task<ActionResult> CheckFiles([FromForm] IFormFile file)
{
if (file == null || file.Length <= 0)
{
if (!ModelState.IsValid)
{
var errors = ModelState.Select(x => x.Value.Errors)
.Where(y => y.Count > 0)
.ToList();
return BadRequest(errors);
}
await _torrents.UploadMagnet(request.MagnetLink, request.Torrent);
return Ok();
throw new Exception("Invalid torrent file");
}
[HttpPost]
[Route("CheckFiles")]
public async Task<ActionResult> CheckFiles([FromForm] IFormFile file)
{
if (file == null || file.Length <= 0)
{
throw new Exception("Invalid torrent file");
}
var fileStream = file.OpenReadStream();
var fileStream = file.OpenReadStream();
await using var memoryStream = new MemoryStream();
await using var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream);
await fileStream.CopyToAsync(memoryStream);
var bytes = memoryStream.ToArray();
var bytes = memoryStream.ToArray();
var torrent = await MonoTorrent.Torrent.LoadAsync(bytes);
var torrent = await MonoTorrent.Torrent.LoadAsync(bytes);
var result = await _torrents.GetAvailableFiles(torrent.InfoHash.ToHex());
var result = await _torrents.GetAvailableFiles(torrent.InfoHash.ToHex());
return Ok(result);
}
return Ok(result);
}
[HttpPost]
[Route("CheckFilesMagnet")]
public async Task<ActionResult> CheckFilesMagnet([FromBody] TorrentControllerCheckFilesRequest request)
{
var magnet = MagnetLink.Parse(request.MagnetLink);
[HttpPost]
[Route("CheckFilesMagnet")]
public async Task<ActionResult> CheckFilesMagnet([FromBody] TorrentControllerCheckFilesRequest request)
{
var magnet = MagnetLink.Parse(request.MagnetLink);
var result = await _torrents.GetAvailableFiles(magnet.InfoHash.ToHex());
var result = await _torrents.GetAvailableFiles(magnet.InfoHash.ToHex());
return Ok(result);
}
return Ok(result);
}
[HttpPost]
[Route("Delete/{torrentId:guid}")]
public async Task<ActionResult> Delete(Guid torrentId, [FromBody] TorrentControllerDeleteRequest request)
{
await _torrents.Delete(torrentId, request.DeleteData, request.DeleteRdTorrent, request.DeleteLocalFiles);
[HttpPost]
[Route("Delete/{torrentId:guid}")]
public async Task<ActionResult> Delete(Guid torrentId, [FromBody] TorrentControllerDeleteRequest request)
{
await _torrents.Delete(torrentId, request.DeleteData, request.DeleteRdTorrent, request.DeleteLocalFiles);
return Ok();
}
return Ok();
}
[HttpPost]
[Route("Retry/{torrentId:guid}")]
public async Task<ActionResult> Retry(Guid torrentId)
{
await _torrents.UpdateRetry(torrentId, DateTimeOffset.UtcNow, 0);
await _torrents.RetryTorrent(torrentId, 0);
[HttpPost]
[Route("Retry/{torrentId:guid}")]
public async Task<ActionResult> Retry(Guid torrentId)
{
await _torrents.UpdateRetry(torrentId, DateTimeOffset.UtcNow, 0);
await _torrents.RetryTorrent(torrentId, 0);
return Ok();
}
return Ok();
}
[HttpPost]
[Route("RetryDownload/{downloadId:guid}")]
public async Task<ActionResult> RetryDownload(Guid downloadId)
{
await _torrents.RetryDownload(downloadId);
[HttpPost]
[Route("RetryDownload/{downloadId:guid}")]
public async Task<ActionResult> RetryDownload(Guid downloadId)
{
await _torrents.RetryDownload(downloadId);
return Ok();
}
return Ok();
}
[HttpPut]
[Route("Update")]
public async Task<ActionResult> Update([FromBody] Torrent torrent)
{
await _torrents.Update(torrent);
return Ok();
}
}
public class TorrentControllerUploadFileRequest
[HttpPut]
[Route("Update")]
public async Task<ActionResult> Update([FromBody] Torrent torrent)
{
public Torrent Torrent { get; set; }
}
await _torrents.Update(torrent);
public class TorrentControllerUploadMagnetRequest
{
public String MagnetLink { get; set; }
public Torrent Torrent { get; set; }
}
public class TorrentControllerDeleteRequest
{
public Boolean DeleteData { get; set; }
public Boolean DeleteRdTorrent { get; set; }
public Boolean DeleteLocalFiles { get; set; }
}
public class TorrentControllerCheckFilesRequest
{
public String MagnetLink { get; set; }
return Ok();
}
}
public class TorrentControllerUploadFileRequest
{
public Torrent Torrent { get; set; }
}
public class TorrentControllerUploadMagnetRequest
{
public String MagnetLink { get; set; }
public Torrent Torrent { get; set; }
}
public class TorrentControllerDeleteRequest
{
public Boolean DeleteData { get; set; }
public Boolean DeleteRdTorrent { get; set; }
public Boolean DeleteLocalFiles { get; set; }
}
public class TorrentControllerCheckFilesRequest
{
public String MagnetLink { get; set; }
}

View file

@ -1,126 +1,180 @@
using System;
using System.Diagnostics;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Net;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Identity;
using RdtClient.Data.Data;
using RdtClient.Data.Models.Internal;
using RdtClient.Service.Middleware;
using RdtClient.Service.Services;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using Serilog.Exceptions;
namespace RdtClient.Web
var builder = WebApplication.CreateBuilder(args);
// Bind the AppSettings from the appsettings.json files.
builder.Configuration.AddJsonFile("appsettings.json", false, false);
builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", true, false);
// Bind AppSettings
var appSettings = new AppSettings();
builder.Configuration.Bind(appSettings);
builder.Services.AddSingleton(appSettings);
// Configure URLs
if (appSettings.Port <= 0)
{
public class Program
{
public static readonly LoggingLevelSwitch LoggingLevelSwitch = new(LogEventLevel.Debug);
public static async Task Main(String[] args)
{
try
{
var host = CreateHostBuilder(args).Build();
// Perform migrations
using (var scope = host.Services.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<DataContext>();
await dbContext.Database.MigrateAsync();
await dbContext.Seed();
var logLevelSettingDb = await dbContext.Settings.FirstOrDefaultAsync(m => m.SettingId == "LogLevel");
var logLevelSetting = "Warning";
if (logLevelSettingDb != null)
{
logLevelSetting = logLevelSettingDb.Value;
}
if (!Enum.TryParse<LogEventLevel>(logLevelSetting, out var logLevel))
{
logLevel = LogEventLevel.Warning;
}
LoggingLevelSwitch.MinimumLevel = logLevel;
}
var version = Assembly.GetEntryAssembly()?.GetName().Version;
Log.Warning($"Starting host on version {version}");
await host.RunAsync();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}
// ReSharper disable once MemberCanBePrivate.Global
public static IHostBuilder CreateHostBuilder(String[] args)
{
var configuration = new ConfigurationBuilder()
#if DEBUG
.AddJsonFile("appsettings.Development.json", true, false)
#else
.AddJsonFile("appsettings.json", true, false)
#endif
.Build();
var appSettings = new AppSettings();
configuration.Bind(appSettings);
if (String.IsNullOrWhiteSpace(appSettings.HostUrl))
{
appSettings.HostUrl = "http://0.0.0.0:6500";
}
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.Enrich.WithExceptionDetails()
.WriteTo.File(appSettings.Logging.File.Path,
rollOnFileSizeLimit: true,
fileSizeLimitBytes: appSettings.Logging.File.FileSizeLimitBytes,
retainedFileCountLimit: appSettings.Logging.File.MaxRollingFiles,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext}: {Message:lj}{NewLine}{Exception}")
.WriteTo.Console()
.MinimumLevel.ControlledBy(LoggingLevelSwitch)
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("System.Net.Http", LogEventLevel.Warning)
.CreateLogger();
Serilog.Debugging.SelfLog.Enable(msg =>
{
Debug.Print(msg);
Debugger.Break();
Console.WriteLine(msg);
Debug.WriteLine(msg);
});
return Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((_, services) =>
{
services.AddHostedService<TaskRunner>();
services.AddHostedService<UpdateChecker>();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseUrls(appSettings.HostUrl)
.UseKestrel()
.UseStartup<Startup>();
})
.UseSerilog();
}
}
appSettings.Port = 6500;
}
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenAnyIP(appSettings.Port);
});
builder.Host.UseSerilog((_, lc) => lc.Enrich.FromLogContext()
.Enrich.WithExceptionDetails()
.WriteTo.File(appSettings.Logging.File.Path,
rollOnFileSizeLimit: true,
fileSizeLimitBytes: appSettings.Logging.File.FileSizeLimitBytes,
retainedFileCountLimit: appSettings.Logging.File.MaxRollingFiles,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext}: {Message:lj}{NewLine}{Exception}")
.WriteTo.Console()
.MinimumLevel.ControlledBy(Settings.LoggingLevelSwitch)
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("System.Net.Http", LogEventLevel.Warning));
Serilog.Debugging.SelfLog.Enable(msg =>
{
Debug.Print(msg);
Debugger.Break();
Console.WriteLine(msg);
Debug.WriteLine(msg);
});
Log.Information("Starting RealDebridClient host");
builder.Services.AddControllers();
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.SlidingExpiration = true;
});
builder.Services.AddAuthorization();
builder.Services.AddIdentity<IdentityUser, IdentityRole>(options =>
{
options.User.RequireUniqueEmail = false;
options.Password.RequiredLength = 10;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredUniqueChars = 5;
})
.AddEntityFrameworkStores<DataContext>()
.AddDefaultTokenProviders();
builder.Services.ConfigureApplicationCookie(options =>
{
options.Events.OnRedirectToLogin = context =>
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
};
options.Cookie.Name = "SID";
});
// Configure development cors.
builder.Services.AddCors(options =>
{
options.AddPolicy("Dev",
corsBuilder => corsBuilder.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
// Configure misc services.
builder.Services.AddResponseCaching();
builder.Services.AddMemoryCache();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddHttpClient();
builder.Services.AddHttpContextAccessor();
builder.Services.AddSession();
builder.Services.AddSpaStaticFiles(spaBuilder =>
{
spaBuilder.RootPath = "wwwroot";
});
builder.Services.AddSignalR(hubOptions =>
{
hubOptions.EnableDetailedErrors = true;
});
builder.Host.UseWindowsService();
RdtClient.Data.DiConfig.Config(builder.Services, appSettings);
RdtClient.Service.DiConfig.Config(builder.Services);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
try
{
// Build the app
var app = builder.Build();
if (builder.Environment.IsDevelopment())
{
app.UseCors("Dev");
app.UseDeveloperExceptionPage();
}
app.ConfigureExceptionHandler();
app.UseMiddleware<AuthorizeMiddleware>();
app.Use(async (context, next) =>
{
await next.Invoke();
if (context.Response.StatusCode != 200)
{
Log.Warning("{StatusCode}: {Value}", context.Response.StatusCode, context.Request.Path.Value);
}
});
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<RdtHub>("/hub");
endpoints.MapControllers();
});
app.MapWhen(x => !x.Request.Path.StartsWithSegments("/api"), routeBuilder =>
{
routeBuilder.UseSpaStaticFiles();
routeBuilder.UseSpa(spa =>
{
spa.Options.SourcePath = "wwwroot";
spa.Options.DefaultPage = "/index.html";
});
});
// Run the app
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}

View file

@ -5,6 +5,9 @@
<OutputType>Exe</OutputType>
<UserSecretsId>94c24cba-f03f-4453-a671-3640b517c573</UserSecretsId>
<Version>2.0.12</Version>
<Nullable>disable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
@ -26,16 +29,16 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="6.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.3">
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="6.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="6.0.3" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="6.0.4" />
<PackageReference Include="Serilog" Version="2.11.0" />
<PackageReference Include="Serilog.AspNetCore" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</ItemGroup>

View file

@ -1,137 +0,0 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using RdtClient.Data.Data;
using RdtClient.Data.Models.Internal;
using RdtClient.Service.Middleware;
using RdtClient.Service.Services;
namespace RdtClient.Web
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
private IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
var appSettings = new AppSettings();
Configuration.Bind(appSettings);
services.AddSingleton(appSettings);
var connectionString = $"Data Source={appSettings.Database.Path}";
services.AddDbContext<DataContext>(options => options.UseSqlite(connectionString));
services.AddControllers();
services.AddSpaStaticFiles(configuration => { configuration.RootPath = "wwwroot"; });
services.AddSignalR(options =>
{
options.EnableDetailedErrors = true;
});
services.AddHttpContextAccessor();
services.AddCors(options =>
{
options.AddPolicy("Dev",
builder =>
{
builder.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin();
});
});
services.AddHttpClient();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options => { options.SlidingExpiration = true; });
services.AddAuthorization();
services.AddIdentity<IdentityUser, IdentityRole>(options =>
{
options.User.RequireUniqueEmail = false;
options.Password.RequiredLength = 10;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredUniqueChars = 5;
})
.AddEntityFrameworkStores<DataContext>()
.AddDefaultTokenProviders();
services.ConfigureApplicationCookie(options =>
{
options.Events.OnRedirectToLogin = context =>
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
};
options.Cookie.Name = "SID";
});
Data.DiConfig.Config(services);
Service.DiConfig.Config(services);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
if (env.IsDevelopment())
{
app.UseCors("Dev");
}
app.ConfigureExceptionHandler();
app.UseMiddleware<AuthorizeMiddleware>();
app.Use(async (context, next) =>
{
await next.Invoke();
if (context.Response.StatusCode != 200)
{
logger.LogWarning("{StatusCode}: {Value}", context.Response.StatusCode, context.Request.Path.Value);
}
});
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<RdtHub>("/hub");
endpoints.MapControllers();
});
app.MapWhen(x => !x.Request.Path.Value.StartsWith("/api"), builder =>
{
builder.UseSpaStaticFiles();
builder.UseSpa(spa =>
{
spa.Options.SourcePath = "wwwroot";
spa.Options.DefaultPage = "/index.html";
});
});
}
}
}

View file

@ -2,7 +2,6 @@
"Logging": {
"File": {
"Path": "C:/Temp/rdtclient/rdtclient.log",
"Append": "True",
"FileSizeLimitBytes": 5242880,
"MaxRollingFiles": 5
}
@ -10,6 +9,5 @@
"Database": {
"Path": "C:/Temp/rdtclient/rdtclient.db"
},
"AllowedHosts": "*",
"HostUrl": "http://0.0.0.0:6500"
"Port": "6500"
}

View file

@ -2,7 +2,6 @@
"Logging": {
"File": {
"Path": "/data/db/rdtclient.log",
"Append": "True",
"FileSizeLimitBytes": 5242880,
"MaxRollingFiles": 5
}
@ -10,6 +9,5 @@
"Database": {
"Path": "/data/db/rdtclient.db"
},
"AllowedHosts": "*",
"HostUrl": "http://0.0.0.0:6500"
"Port": "6500"
}

View file

@ -1,7 +1,5 @@
<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: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/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_002Edebug_002Ejs/@EntryIndexedValue">*.debug.js</s:String>
@ -29,6 +27,7 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeLocalFunctionBody/@EntryIndexedValue">SUGGESTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeMethodOrOperatorBody/@EntryIndexedValue">SUGGESTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeModifiersOrder/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeNamespaceBody/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeRedundantParentheses/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeThisQualifier/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeTypeMemberModifiers/@EntryIndexedValue">ERROR</s:String>
@ -178,6 +177,7 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HCP/@EntryIndexedValue">HCP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=QA/@EntryIndexedValue">QA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=QB/@EntryIndexedValue">QB</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=US/@EntryIndexedValue">US</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XML/@EntryIndexedValue">XML</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Constants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=LocalConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>