Add nullable to the projects.

This commit is contained in:
Roger Far 2022-05-13 18:51:04 -06:00
parent e65942adb7
commit e3b38d2834
57 changed files with 945 additions and 747 deletions

View file

@ -4,6 +4,8 @@ using RdtClient.Data.Models.Data;
namespace RdtClient.Data.Data;
#nullable disable
public class DataContext : IdentityDbContext
{
public DataContext(DbContextOptions options) : base(options)

View file

@ -20,7 +20,7 @@ public class DownloadData
.ToListAsync();
}
public async Task<Download> GetById(Guid downloadId)
public async Task<Download?> GetById(Guid downloadId)
{
return await _dataContext.Downloads
.Include(m => m.Torrent)
@ -28,7 +28,7 @@ public class DownloadData
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
}
public async Task<Download> Get(Guid torrentId, String path)
public async Task<Download?> Get(Guid torrentId, String path)
{
return await _dataContext.Downloads
.Include(m => m.Torrent)
@ -176,7 +176,7 @@ public class DownloadData
await TorrentData.VoidCache();
}
public async Task UpdateError(Guid downloadId, String error)
public async Task UpdateError(Guid downloadId, String? error)
{
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
@ -243,6 +243,11 @@ public class DownloadData
var dbDownload = await _dataContext.Downloads
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
if (dbDownload == null)
{
throw new Exception($"Cannot find download with ID {downloadId}");
}
dbDownload.RetryCount = 0;
dbDownload.Link = null;
dbDownload.Added = DateTimeOffset.UtcNow;

View file

@ -13,16 +13,16 @@ public class SettingData
public static DbSettings Get { get; } = new DbSettings();
public static IList<SettingProperty> GetAll()
{
return GetSettings(Get, null).ToList();
}
public SettingData(DataContext dataContext)
{
_dataContext = dataContext;
}
public IList<SettingProperty> GetAll()
{
return GetSettings(Get, null).ToList();
}
public async Task Update(IList<SettingProperty> settings)
{
var dbSettings = await _dataContext.Settings.ToListAsync();
@ -33,7 +33,7 @@ public class SettingData
if (setting != null)
{
dbSetting.Value = setting.Value.ToString();
dbSetting.Value = setting.Value?.ToString();
}
}
@ -42,7 +42,7 @@ public class SettingData
await ResetCache();
}
public async Task Update(String settingId, Object value)
public async Task Update(String settingId, Object? value)
{
var dbSetting = await _dataContext.Settings.FirstOrDefaultAsync(m => m.SettingId == settingId);
@ -51,7 +51,7 @@ public class SettingData
return;
}
dbSetting.Value = value.ToString();
dbSetting.Value = value?.ToString();
await _dataContext.SaveChangesAsync();
@ -97,7 +97,7 @@ public class SettingData
}
}
private static IEnumerable<SettingProperty> GetSettings(Object defaultSetting, String parent)
private static IEnumerable<SettingProperty> GetSettings(Object defaultSetting, String? parent)
{
var result = new List<SettingProperty>();
@ -105,8 +105,8 @@ public class SettingData
foreach (var property in properties)
{
var displayName = (DisplayNameAttribute) Attribute.GetCustomAttribute(property, typeof(DisplayNameAttribute));
var description = (DescriptionAttribute) Attribute.GetCustomAttribute(property, typeof(DescriptionAttribute));
var displayName = Attribute.GetCustomAttribute(property, typeof(DisplayNameAttribute)) as DisplayNameAttribute;
var description = Attribute.GetCustomAttribute(property, typeof(DescriptionAttribute)) as DescriptionAttribute;
var propertyName = property.Name;
if (parent != null)
@ -137,7 +137,7 @@ public class SettingData
{
var enumMember = property.PropertyType.GetMember(e.ToString()).First();
var enumDescriptionAttribute = enumMember.GetCustomAttribute<DescriptionAttribute>();
var enumName = enumDescriptionAttribute?.Description ?? Enum.GetName(property.PropertyType, e);
var enumName = enumDescriptionAttribute?.Description ?? Enum.GetName(property.PropertyType, e) ?? "Unknown value";
settingProperty.EnumValues.Add((Int32)(Object)e, enumName);
}
}
@ -149,7 +149,7 @@ public class SettingData
settingProperty.Type = "Object";
result.Add(settingProperty);
var childResults = GetSettings(property.GetValue(defaultSetting), propertyName);
var childResults = GetSettings(property.GetValue(defaultSetting)!, propertyName);
result.AddRange(childResults);
}
}
@ -157,7 +157,7 @@ public class SettingData
return result;
}
private static void SetSettings(IList<Setting> settings, Object defaultSetting, String parent)
private static void SetSettings(IList<Setting> settings, Object defaultSetting, String? parent)
{
var properties = defaultSetting.GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
@ -180,14 +180,18 @@ public class SettingData
{
if (property.PropertyType.IsEnum)
{
var newValue = Enum.Parse(property.PropertyType, setting.Value);
var newValue = Enum.Parse(property.PropertyType, setting.Value ?? "0");
property.SetValue(defaultSetting, newValue);
}
else
{
var converter = TypeDescriptor.GetConverter(property.PropertyType);
if (converter.IsValid(setting.Value))
if (setting.Value == null)
{
property.SetValue(defaultSetting, null);
}
else if (converter.IsValid(setting.Value))
{
var newValue = converter.ConvertFrom(setting.Value);
property.SetValue(defaultSetting, newValue);
@ -201,7 +205,7 @@ public class SettingData
}
else
{
SetSettings(settings, property.GetValue(defaultSetting), propertyName);
SetSettings(settings, property.GetValue(defaultSetting)!, propertyName);
}
}
}

View file

@ -5,7 +5,8 @@ namespace RdtClient.Data.Data;
public class TorrentData
{
private static IList<Torrent> _torrentCache;
private static IList<Torrent>? _torrentCache;
private static readonly SemaphoreSlim TorrentCacheLock = new(1, 1);
private readonly DataContext _dataContext;
@ -34,7 +35,7 @@ public class TorrentData
}
}
public async Task<Torrent> GetById(Guid torrentId)
public async Task<Torrent?> GetById(Guid torrentId)
{
var dbTorrent = await _dataContext.Torrents
.AsNoTracking()
@ -54,7 +55,7 @@ public class TorrentData
return dbTorrent;
}
public async Task<Torrent> GetByHash(String hash)
public async Task<Torrent?> GetByHash(String hash)
{
var dbTorrent = await _dataContext.Torrents
.AsNoTracking()
@ -76,7 +77,7 @@ public class TorrentData
public async Task<Torrent> Add(String realDebridId,
String hash,
String fileOrMagnetContents,
String? fileOrMagnetContents,
Boolean isFile,
Torrent torrent)
{
@ -129,12 +130,8 @@ public class TorrentData
dbTorrent.RdEnded = torrent.RdEnded;
dbTorrent.RdSpeed = torrent.RdSpeed;
dbTorrent.RdSeeders = torrent.RdSeeders;
if (torrent.Files != null)
{
dbTorrent.RdFiles = torrent.RdFiles;
}
dbTorrent.RdFiles = torrent.RdFiles;
await _dataContext.SaveChangesAsync();
await VoidCache();
@ -160,7 +157,7 @@ public class TorrentData
await VoidCache();
}
public async Task UpdateCategory(Guid torrentId, String category)
public async Task UpdateCategory(Guid torrentId, String? category)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
@ -176,7 +173,7 @@ public class TorrentData
await VoidCache();
}
public async Task UpdateComplete(Guid torrentId, String error, DateTimeOffset? datetime, Boolean retry)
public async Task UpdateComplete(Guid torrentId, String? error, DateTimeOffset? datetime, Boolean retry)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);

View file

@ -12,7 +12,7 @@ public class UserData
_dataContext = dataContext;
}
public async Task<IdentityUser> GetUser()
public async Task<IdentityUser?> GetUser()
{
return await _dataContext.Users.FirstOrDefaultAsync();
}

View file

@ -9,6 +9,11 @@ public static class DiConfig
{
public static void Config(IServiceCollection services, AppSettings appSettings)
{
if (String.IsNullOrWhiteSpace(appSettings.Database?.Path))
{
throw new Exception("Invalid database path found in appSettings");
}
var connectionString = $"Data Source={appSettings.Database.Path}";
services.AddDbContext<DataContext>(options => options.UseSqlite(connectionString));

View file

@ -1,8 +1,15 @@
namespace RdtClient.Data.Enums;
using System.ComponentModel;
namespace RdtClient.Data.Enums;
public enum DownloadClient
{
[Description("Simple Downloader")]
Simple,
[Description("Multi-Part Downloader")]
MultiPart,
[Description("Aria2c")]
Aria2c
}

View file

@ -11,10 +11,10 @@ public class Download
public Guid TorrentId { get; set; }
[ForeignKey("TorrentId")]
public Torrent Torrent { get; set; }
public Torrent? Torrent { get; set; }
public String Path { get; set; }
public String Link { get; set; }
public String Path { get; set; } = null!;
public String? Link { get; set; }
public DateTimeOffset Added { get; set; }
public DateTimeOffset? DownloadQueued { get; set; }
@ -27,9 +27,9 @@ public class Download
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; }

View file

@ -5,7 +5,7 @@ namespace RdtClient.Data.Models.Data;
public class Setting
{
[Key]
public String SettingId { get; set; }
public String SettingId { get; set; } = null!;
public String Value { get; set; }
public String? Value { get; set; }
}

View file

@ -11,21 +11,21 @@ public class Torrent
[Key]
public Guid TorrentId { get; set; }
public String Hash { get; set; }
public String Hash { get; set; } = null!;
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 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 String FileOrMagnet { get; set; }
public String? FileOrMagnet { get; set; }
public Boolean IsFile { get; set; }
public Int32? Priority { get; set; }
@ -35,24 +35,24 @@ public class Torrent
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; }
public IList<Download> Downloads { get; set; } = new List<Download>();
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 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? RdFiles { get; set; }
[NotMapped]
public IList<TorrentClientFile> Files
@ -66,7 +66,7 @@ public class Torrent
try
{
return JsonSerializer.Deserialize<List<TorrentClientFile>>(RdFiles);
return JsonSerializer.Deserialize<List<TorrentClientFile>>(RdFiles) ?? new List<TorrentClientFile>();
}
catch
{

View file

@ -2,25 +2,25 @@
public class AppSettings
{
public AppSettingsLogging Logging { get; set; }
public AppSettingsDatabase Database { get; set; }
public AppSettingsLogging? Logging { get; set; }
public AppSettingsDatabase? Database { get; set; }
public Int32 Port { get; set; }
}
public class AppSettingsLogging
{
public AppSettingsLoggingFile File { get; set; }
public AppSettingsLoggingFile? File { get; set; }
}
public class AppSettingsLoggingFile
{
public String Path { get; set; }
public String? Path { get; set; }
public Int64 FileSizeLimitBytes { get; set; }
public Int32 MaxRollingFiles { get; set; }
}
public class AppSettingsDatabase
{
public String Path { get; set; }
public String? Path { get; set; }
}

View file

@ -2,9 +2,9 @@
public class Profile
{
public String Provider { get; set; }
public String UserName { 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; }
public String? CurrentVersion { get; set; }
public String? LatestVersion { get; set; }
}

View file

@ -2,10 +2,10 @@
public class SettingProperty
{
public String Key { get; set; }
public Object Value { get; set; }
public String DisplayName { get; set; }
public String Description { get; set; }
public String Type { get; set; }
public Dictionary<Int32, String> EnumValues { get; set; }
public String Key { get; set; } = default!;
public Object? Value { get; set; }
public String? DisplayName { get; set; }
public String? Description { get; set; }
public String Type { get; set; } = default!;
public Dictionary<Int32, String>? EnumValues { get; set; }
}

View file

@ -5,20 +5,20 @@ namespace RdtClient.Data.Models.QBittorrent;
public class AppBuildInfo
{
[JsonPropertyName("bitness")]
public Int64 Bitness { get; set; }
public Int64? Bitness { get; set; }
[JsonPropertyName("boost")]
public String Boost { get; set; }
public String? Boost { get; set; }
[JsonPropertyName("libtorrent")]
public String Libtorrent { get; set; }
public String? Libtorrent { get; set; }
[JsonPropertyName("openssl")]
public String Openssl { get; set; }
public String? Openssl { get; set; }
[JsonPropertyName("qt")]
public String Qt { get; set; }
public String? Qt { get; set; }
[JsonPropertyName("zlib")]
public String Zlib { get; set; }
public String? Zlib { get; set; }
}

View file

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

View file

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

View file

@ -5,8 +5,8 @@ namespace RdtClient.Data.Models.QBittorrent;
public class TorrentCategory
{
[JsonPropertyName("name")]
public String Name { get; set; }
public String? Name { get; set; }
[JsonPropertyName("savePath")]
public String SavePath { get; set; }
public String? SavePath { get; set; }
}

View file

@ -5,5 +5,5 @@ namespace RdtClient.Data.Models.QBittorrent;
public class TorrentFileItem
{
[JsonPropertyName("name")]
public String Name { get; set; }
public String? Name { get; set; }
}

View file

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

View file

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

View file

@ -2,7 +2,7 @@
public class TorrentClientAvailableFile
{
public String Filename { get; set; }
public String Filename { get; set; } = default!;
public Int64 Filesize { get; set; }
}

View file

@ -3,7 +3,7 @@
public class TorrentClientFile
{
public Int64 Id { get; set; }
public String Path { get; set; }
public String Path { get; set; } = default!;
public Int64 Bytes { get; set; }
public Boolean Selected { get; set; }
}

View file

@ -2,20 +2,20 @@
public class TorrentClientTorrent
{
public String Id { get; set; }
public String Filename { get; set; }
public String OriginalFilename { get; set; }
public String Hash { get; set; }
public String Id { get; set; } = default!;
public String Filename { get; set; } = default!;
public String? OriginalFilename { get; set; } = default!;
public String Hash { get; set; } = default!;
public Int64 Bytes { get; set; }
public Int64 OriginalBytes { get; set; }
public String Host { get; set; }
public String? Host { get; set; }
public Int64 Split { get; set; }
public Int64 Progress { get; set; }
public String Status { get; set; }
public String? Status { get; set; } = default!;
public Int64 StatusCode { get; set; }
public DateTimeOffset Added { get; set; }
public List<TorrentClientFile> Files { get; set; }
public List<String> Links { 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

@ -2,6 +2,6 @@
public class TorrentClientUser
{
public String Username { get; set; }
public String? Username { get; set; }
public DateTimeOffset? Expiration { get; set; }
}

View file

@ -2,25 +2,21 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>disable</Nullable>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<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">
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.5" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="6.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Serilog" Version="2.11.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Attributes\" />
</ItemGroup>
</Project>

View file

@ -32,7 +32,7 @@ public class Startup : IHostedService
var settings = scope.ServiceProvider.GetRequiredService<Settings>();
await settings.Seed();
await settings.ResetCache();
await settings.Clean();
await Settings.Clean();
Ready = true;
}

View file

@ -8,8 +8,8 @@ namespace RdtClient.Service.BackgroundServices;
public class UpdateChecker : BackgroundService
{
public static String CurrentVersion { get; private set; }
public static String LatestVersion { get; private set; }
public static String? CurrentVersion { get; private set; }
public static String? LatestVersion { get; private set; }
private readonly ILogger<UpdateChecker> _logger;
@ -53,7 +53,13 @@ public class UpdateChecker : BackgroundService
return;
}
var latestRelease = gitHubReleases.First().Name;
var latestRelease = gitHubReleases.FirstOrDefault(m => m.Name != null)?.Name;
if (latestRelease == null)
{
_logger.LogWarning($"Unable to find latest version on GitHub");
return;
}
if (latestRelease != CurrentVersion)
{
@ -77,5 +83,5 @@ public class UpdateChecker : BackgroundService
public class GitHubReleasesResponse
{
[JsonProperty("name")]
public String Name { get; set; }
public String? Name { get; set; }
}

View file

@ -23,5 +23,6 @@ public static class DiConfig
services.AddHostedService<Startup>();
services.AddHostedService<TaskRunner>();
services.AddHostedService<UpdateChecker>();
services.AddHostedService<WatchFolderChecker>();
}
}

View file

@ -0,0 +1,8 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "Bug in Serilog", Scope = "NamespaceAndDescendants", Target = "N:RdtClient.Service")]

View file

@ -5,11 +5,11 @@ namespace RdtClient.Service.Helpers;
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))
if (String.IsNullOrWhiteSpace(fileUrl) || torrent.RdName == null)
{
return null;
}

View file

@ -16,7 +16,7 @@ public class JsonModelBinder : IModelBinder
{
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
var valueAsString = valueProviderResult.FirstValue;
var valueAsString = valueProviderResult.FirstValue ?? "";
var result = Newtonsoft.Json.JsonConvert.DeserializeObject(valueAsString, bindingContext.ModelType);
if (result != null)
{

View file

@ -1,30 +0,0 @@
using RdtClient.Data.Models.Data;
namespace RdtClient.Service.Helpers;
public static class SettingsHelper
{
public static String GetString(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 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

@ -2,18 +2,18 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>disable</Nullable>
<Nullable>enable</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="AllDebrid.NET" Version="1.0.9" />
<PackageReference Include="Aria2.NET" Version="1.0.5" />
<PackageReference Include="Downloader" Version="2.3.5" />
<PackageReference Include="MonoTorrent" Version="2.0.5" />
<PackageReference Include="RD.NET" Version="2.1.2" />
<PackageReference Include="RD.NET" Version="2.1.3" />
<PackageReference Include="Serilog" Version="2.11.0" />
<PackageReference Include="Serilog.Exceptions" Version="8.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />

View file

@ -37,7 +37,7 @@ public class Authentication
return result;
}
public async Task<IdentityUser> GetUser()
public async Task<IdentityUser?> GetUser()
{
return await _userData.GetUser();
}

View file

@ -12,7 +12,17 @@ public class DownloadClient
private readonly Download _download;
private readonly Torrent _torrent;
public IDownloader Downloader;
public IDownloader? Downloader;
public Data.Enums.DownloadClient 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 DownloadClient(Download download, Torrent torrent, String destinationPath)
{
@ -21,17 +31,7 @@ public class DownloadClient
_destinationPath = destinationPath;
}
public Data.Enums.DownloadClient 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)
public async Task<String?> Start(DbSettings settings)
{
BytesDone = 0;
BytesTotal = 0;
@ -39,13 +39,18 @@ public class DownloadClient
try
{
if (_download.Link == null)
{
throw new Exception($"Invalid download link");
}
var filePath = DownloadHelper.GetDownloadPath(_destinationPath, _torrent, _download);
if (filePath == null)
{
throw new Exception("Invalid download path");
}
await FileHelper.Delete(filePath);
Type = settings.DownloadClient.Client;

View file

@ -6,8 +6,8 @@ namespace RdtClient.Service.Services.Downloaders;
public class Aria2cDownloader : IDownloader
{
public event EventHandler<DownloadCompleteEventArgs> DownloadComplete;
public event EventHandler<DownloadProgressEventArgs> DownloadProgress;
public event EventHandler<DownloadCompleteEventArgs>? DownloadComplete;
public event EventHandler<DownloadProgressEventArgs>? DownloadProgress;
private const Int32 RetryCount = 5;
@ -17,9 +17,9 @@ public class Aria2cDownloader : IDownloader
private readonly String _uri;
private readonly String _filePath;
private String _gid;
private String? _gid;
public Aria2cDownloader(String gid, String uri, String filePath, DbSettings settings)
public Aria2cDownloader(String? gid, String uri, String filePath, DbSettings settings)
{
_logger = Log.ForContext<Aria2cDownloader>();
_gid = gid;
@ -34,9 +34,15 @@ public class Aria2cDownloader : IDownloader
_aria2NetClient = new Aria2NetClient(settings.DownloadClient.Aria2cUrl, settings.DownloadClient.Aria2cSecret, httpClient, 10);
}
public async Task<String> Download()
public async Task<String?> Download()
{
var path = Path.GetDirectoryName(_filePath);
if (path == null)
{
throw new Exception($"Invalid file path {_filePath}");
}
var fileName = Path.GetFileName(_filePath);
_logger.Debug($"Starting download of {_uri}, writing to path: {path}, fileName: {fileName}");
@ -60,7 +66,7 @@ public class Aria2cDownloader : IDownloader
{
try
{
await _aria2NetClient.TellStatus(_gid);
await _aria2NetClient.TellStatusAsync(_gid);
return _gid;
}
@ -70,23 +76,23 @@ public class Aria2cDownloader : IDownloader
}
}
_gid ??= await _aria2NetClient.AddUri(new List<String>
{
_uri
},
new Dictionary<String, Object>
{
{
"dir", path
},
{
"out", fileName
}
});
_gid ??= await _aria2NetClient.AddUriAsync(new List<String>
{
_uri
},
new Dictionary<String, Object>
{
{
"dir", path
},
{
"out", fileName
}
});
_logger.Debug($"Added download to Aria2, received ID {_gid}");
await _aria2NetClient.TellStatus(_gid);
await _aria2NetClient.TellStatusAsync(_gid);
_logger.Debug($"Download with ID {_gid} found in Aria2");
@ -124,7 +130,7 @@ public class Aria2cDownloader : IDownloader
try
{
await _aria2NetClient.Pause(_gid);
await _aria2NetClient.PauseAsync(_gid);
}
catch
{
@ -143,7 +149,7 @@ public class Aria2cDownloader : IDownloader
try
{
await _aria2NetClient.Unpause(_gid);
await _aria2NetClient.UnpauseAsync(_gid);
}
catch
{
@ -228,7 +234,7 @@ public class Aria2cDownloader : IDownloader
try
{
await _aria2NetClient.ForceRemove(_gid);
await _aria2NetClient.ForceRemoveAsync(_gid);
}
catch
{
@ -237,7 +243,7 @@ public class Aria2cDownloader : IDownloader
try
{
await _aria2NetClient.RemoveDownloadResult(_gid);
await _aria2NetClient.RemoveDownloadResultAsync(_gid);
}
catch
{
@ -247,11 +253,11 @@ public class Aria2cDownloader : IDownloader
private async Task<Boolean> CheckIfAdded()
{
var allDownloads = await _aria2NetClient.TellAll();
var allDownloads = await _aria2NetClient.TellAllAsync();
foreach (var download in allDownloads.Where(m => m.Files != null))
foreach (var download in allDownloads)
{
foreach (var file in download.Files.Where(m => m.Uris != null))
foreach (var file in download.Files)
{
if (file.Uris.Any(uri => uri.Uri == _uri))
{

View file

@ -2,7 +2,7 @@
public class DownloadCompleteEventArgs
{
public String Error { get; set; }
public String? Error { get; set; }
}
public class DownloadProgressEventArgs
@ -14,9 +14,9 @@ public class DownloadProgressEventArgs
public interface IDownloader
{
event EventHandler<DownloadCompleteEventArgs> DownloadComplete;
event EventHandler<DownloadProgressEventArgs> DownloadProgress;
Task<String> Download();
event EventHandler<DownloadCompleteEventArgs>? DownloadComplete;
event EventHandler<DownloadProgressEventArgs>? DownloadProgress;
Task<String?> Download();
Task Cancel();
Task Pause();
Task Resume();

View file

@ -7,8 +7,8 @@ namespace RdtClient.Service.Services.Downloaders;
public class MultiDownloader : IDownloader
{
public event EventHandler<DownloadCompleteEventArgs> DownloadComplete;
public event EventHandler<DownloadProgressEventArgs> DownloadProgress;
public event EventHandler<DownloadCompleteEventArgs>? DownloadComplete;
public event EventHandler<DownloadProgressEventArgs>? DownloadProgress;
private readonly DownloadService _downloadService;
private readonly String _filePath;
@ -94,7 +94,7 @@ public class MultiDownloader : IDownloader
_downloadService.DownloadFileCompleted += (_, args) =>
{
String error = null;
String? error = null;
if (args.Cancelled)
{
@ -113,7 +113,7 @@ public class MultiDownloader : IDownloader
};
}
public async Task<String> Download()
public async Task<String?> Download()
{
_logger.Debug($"Starting download of {_uri}, writing to path: {_filePath}");

View file

@ -7,8 +7,8 @@ public class SimpleDownloader : IDownloader
{
private const Int32 BufferSize = 8 * 1024;
public event EventHandler<DownloadCompleteEventArgs> DownloadComplete;
public event EventHandler<DownloadProgressEventArgs> DownloadProgress;
public event EventHandler<DownloadCompleteEventArgs>? DownloadComplete;
public event EventHandler<DownloadProgressEventArgs>? DownloadProgress;
private readonly String _uri;
private readonly String _filePath;
@ -30,7 +30,7 @@ public class SimpleDownloader : IDownloader
_filePath = filePath;
}
public Task<String> Download()
public Task<String?> Download()
{
_logger.Debug($"Starting download of {_uri}, writing to path: {_filePath}");
@ -39,7 +39,7 @@ public class SimpleDownloader : IDownloader
await StartDownloadTask();
});
return Task.FromResult<String>(null);
return Task.FromResult<String?>(null);
}
public Task Cancel()
@ -86,6 +86,7 @@ public class SimpleDownloader : IDownloader
while (readSize > 0 && !_cancelled)
{
// ReSharper disable once ConvertToUsingDeclaration
#pragma warning disable IDE0063 // Use simple 'using' statement
using (var innerCts = new CancellationTokenSource(1000))
{
readSize = await destinationStream.ReadAsync(buffer.AsMemory(0, buffer.Length), innerCts.Token).ConfigureAwait(false);
@ -115,6 +116,7 @@ public class SimpleDownloader : IDownloader
}
}
}
#pragma warning restore IDE0063 // Use simple 'using' statement
}
break;

View file

@ -17,12 +17,12 @@ public class Downloads
return await _downloadData.GetForTorrent(torrentId);
}
public async Task<Download> GetById(Guid downloadId)
public async Task<Download?> GetById(Guid downloadId)
{
return await _downloadData.GetById(downloadId);
}
public async Task<Download> Get(Guid torrentId, String path)
public async Task<Download?> Get(Guid torrentId, String path)
{
return await _downloadData.Get(torrentId, path);
}
@ -67,7 +67,7 @@ public class Downloads
await _downloadData.UpdateCompleted(downloadId, dateTime);
}
public async Task UpdateError(Guid downloadId, String error)
public async Task UpdateError(Guid downloadId, String? error)
{
await _downloadData.UpdateError(downloadId, error);
}

View file

@ -1,6 +1,5 @@
using RdtClient.Data.Enums;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.Internal;
using RdtClient.Data.Models.QBittorrent;
namespace RdtClient.Service.Services;
@ -178,7 +177,7 @@ public class QBittorrent
WebUiUsername = ""
};
var savePath = AppDefaultSavePath();
var savePath = Settings.AppDefaultSavePath;
preferences.SavePath = savePath;
preferences.TempPath = $"{savePath}temp{Path.DirectorySeparatorChar}";
@ -193,21 +192,9 @@ public class QBittorrent
return preferences;
}
public String AppDefaultSavePath()
{
var downloadPath = Settings.Get.DownloadClient.MappedPath;
downloadPath = downloadPath.TrimEnd('\\')
.TrimEnd('/');
downloadPath += Path.DirectorySeparatorChar;
return downloadPath;
}
public async Task<IList<TorrentInfo>> TorrentInfo()
{
var savePath = AppDefaultSavePath();
var savePath = Settings.AppDefaultSavePath;
var results = new List<TorrentInfo>();
@ -241,9 +228,9 @@ public class QBittorrent
speed = (Int32) torrent.Downloads.Average(m => m.Speed);
}
var progress = (bytesDone / (Single)bytesTotal);
var progress = (bytesDone / (Single?)bytesTotal);
if (!Single.IsNormal(progress))
if (progress == null || !Single.IsNormal(progress.Value))
{
progress = 0;
}
@ -276,7 +263,7 @@ public class QBittorrent
NumLeechs = 100,
NumSeeds = 100,
Priority = ++prio,
Progress = progress,
Progress = (Single) progress,
Ratio = 1,
RatioLimit = 1,
SavePath = downloadPath,
@ -325,7 +312,7 @@ public class QBittorrent
return results;
}
public async Task<IList<TorrentFileItem>> TorrentFileContents(String hash)
public async Task<IList<TorrentFileItem>?> TorrentFileContents(String hash)
{
var results = new List<TorrentFileItem>();
@ -349,9 +336,9 @@ public class QBittorrent
return results;
}
public async Task<TorrentProperties> TorrentProperties(String hash)
public async Task<TorrentProperties?> TorrentProperties(String hash)
{
var savePath = AppDefaultSavePath();
var savePath = Settings.AppDefaultSavePath;
var torrent = await _torrents.GetByHash(hash);
@ -428,7 +415,7 @@ public class QBittorrent
await _torrents.Delete(torrent.TorrentId, true, true, deleteFiles);
}
public async Task TorrentsAddMagnet(String magnetLink, String category, Int32? priority)
public async Task TorrentsAddMagnet(String magnetLink, String? category, Int32? priority)
{
var torrent = new Torrent
{
@ -446,7 +433,7 @@ public class QBittorrent
await _torrents.UploadMagnet(magnetLink, torrent);
}
public async Task TorrentsAddFile(Byte[] fileBytes, String category, Int32? priority)
public async Task TorrentsAddFile(Byte[] fileBytes, String? category, Int32? priority)
{
var torrent = new Torrent
{
@ -464,7 +451,7 @@ public class QBittorrent
await _torrents.UploadFile(fileBytes, torrent);
}
public async Task TorrentsSetCategory(String hash, String category)
public async Task TorrentsSetCategory(String hash, String? category)
{
await _torrents.UpdateCategory(hash, category);
}
@ -474,7 +461,7 @@ public class QBittorrent
var torrents = await _torrents.Get();
var torrentsToGroup = torrents.Where(m => !String.IsNullOrWhiteSpace(m.Category))
.Select(m => m.Category.ToLower())
.Select(m => m.Category!.ToLower())
.ToList();
var categoryList = (Settings.Get.General.Categories ?? "")
@ -494,14 +481,14 @@ public class QBittorrent
m => new TorrentCategory
{
Name = m,
SavePath = Path.Combine(AppDefaultSavePath(), m)
SavePath = Path.Combine(Settings.AppDefaultSavePath, m)
});
}
return results;
}
public async Task CategoryCreate(String category)
public async Task CategoryCreate(String? category)
{
if (category == null)
{
@ -528,7 +515,7 @@ public class QBittorrent
await _settings.Update("General:Categories", categoriesSetting);
}
public async Task CategoryRemove(String category)
public async Task CategoryRemove(String? category)
{
if (category == null)
{

View file

@ -15,7 +15,7 @@ public class RdtHub : Hub
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
public override async Task OnDisconnectedAsync(Exception? exception)
{
Users.TryRemove(Context.ConnectionId, out _);
await base.OnDisconnectedAsync(exception);

View file

@ -1,11 +1,7 @@
using System.Diagnostics;
using Aria2NET;
using RdtClient.Data.Data;
using RdtClient.Data.Data;
using RdtClient.Data.Enums;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.Internal;
using RdtClient.Service.Helpers;
using RdtClient.Service.Services.Downloaders;
using Serilog.Core;
using Serilog.Events;
@ -24,9 +20,19 @@ public class Settings
public static DbSettings Get => SettingData.Get;
public IList<SettingProperty> GetAll()
public static String AppDefaultSavePath
{
return _settingData.GetAll();
get
{
var downloadPath = Get.DownloadClient.MappedPath;
downloadPath = downloadPath.TrimEnd('\\')
.TrimEnd('/');
downloadPath += Path.DirectorySeparatorChar;
return downloadPath;
}
}
public async Task Update(IList<SettingProperty> settings)
@ -34,137 +40,12 @@ public class Settings
await _settingData.Update(settings);
}
public async Task Update(String settingId, Object value)
public async Task Update(String settingId, Object? value)
{
await _settingData.Update(settingId, 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 FileHelper.Delete(testFile);
}
public async Task<Double> TestDownloadSpeed(CancellationToken cancellationToken)
{
var downloadPath = Get.DownloadClient.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
{
RdName = ""
}
};
var downloadClient = new DownloadClient(download, download.Torrent, downloadPath);
await downloadClient.Start(Get);
var httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(10)
};
while (!downloadClient.Finished)
{
await Task.Delay(1000, CancellationToken.None);
if (cancellationToken.IsCancellationRequested)
{
await downloadClient.Cancel();
}
if (downloadClient.Downloader is Aria2cDownloader aria2Downloader)
{
var aria2NetClient = new Aria2NetClient(Settings.Get.DownloadClient.Aria2cUrl, Settings.Get.DownloadClient.Aria2cSecret, httpClient, 1);
var allDownloads = await aria2NetClient.TellAll(cancellationToken);
await aria2Downloader.Update(allDownloads);
}
if (downloadClient.BytesDone > 1024 * 1024 * 50)
{
await downloadClient.Cancel();
break;
}
}
await FileHelper.Delete(testFilePath);
await Clean();
return downloadClient.Speed;
}
public async Task<Double> TestWriteSpeed()
{
var downloadPath = Get.DownloadClient.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)
{
rnd.NextBytes(buffer);
await fileStream.WriteAsync(buffer.AsMemory(0, buffer.Length));
}
watch.Stop();
var writeSpeed = fileStream.Length / watch.Elapsed.TotalSeconds;
fileStream.Close();
await FileHelper.Delete(testFilePath);
return writeSpeed;
}
public async Task<VersionResult> GetAria2cVersion(String url, String secret)
{
var client = new Aria2NetClient(url, secret);
return await client.GetVersion();
}
public async Task Clean()
public static async Task Clean()
{
try
{

View file

@ -6,9 +6,9 @@
/// </summary>
public class ThrottledStream : Stream
{
public Int64 Speed => (Int64)_bandwidth.AverageSpeed;
public Int64 Speed => (Int64) (_bandwidth?.AverageSpeed ?? 0);
private Bandwidth _bandwidth;
private Bandwidth? _bandwidth;
private Int64 _bandwidthLimit;
private readonly Stream _baseStream;
@ -90,7 +90,6 @@ public class ThrottledStream : Stream
public override Int32 Read(Byte[] buffer, Int32 offset, Int32 count)
{
Throttle(count).Wait();
return _baseStream.Read(buffer, offset, count);
}
@ -100,8 +99,13 @@ public class ThrottledStream : Stream
CancellationToken cancellationToken)
{
await Throttle(count).ConfigureAwait(false);
return await _baseStream.ReadAsync(new Memory<Byte>(buffer, offset, count), cancellationToken).ConfigureAwait(false);
}
return await _baseStream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
public override async ValueTask<Int32> ReadAsync(Memory<Byte> buffer, CancellationToken cancellationToken = new CancellationToken())
{
await Throttle(buffer.Length).ConfigureAwait(false);
return await base.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc />
@ -115,7 +119,13 @@ public class ThrottledStream : Stream
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);
await _baseStream.WriteAsync(new ReadOnlyMemory<Byte>(buffer, offset, count), cancellationToken).ConfigureAwait(false);
}
public override async ValueTask WriteAsync(ReadOnlyMemory<Byte> buffer, CancellationToken cancellationToken = new CancellationToken())
{
await Throttle(buffer.Length).ConfigureAwait(false);
await base.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
}
private async Task Throttle(Int32 transmissionVolume)
@ -124,9 +134,8 @@ public class ThrottledStream : Stream
if (BandwidthLimit > 0 && transmissionVolume > 0)
{
// Calculate the time to sleep.
_bandwidth.CalculateSpeed(transmissionVolume);
await Sleep(_bandwidth.PopSpeedRetrieveTime()).ConfigureAwait(false);
_bandwidth!.CalculateSpeed(transmissionVolume);
await Sleep(_bandwidth!.PopSpeedRetrieveTime()).ConfigureAwait(false);
}
}
@ -139,7 +148,7 @@ public class ThrottledStream : Stream
}
/// <inheritdoc />
public override String ToString()
public override String? ToString()
{
return _baseStream.ToString();
}

View file

@ -70,14 +70,14 @@ public class AllDebridTorrentClient : ITorrentClient
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
Files = torrent.Links.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(),
Links = torrent.Links.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
@ -94,6 +94,11 @@ public class AllDebridTorrentClient : ITorrentClient
{
var user = await GetClient().User.GetAsync();
if (user == null)
{
throw new Exception("Unable to get user");
}
return new TorrentClientUser
{
Username = user.Username,
@ -105,14 +110,38 @@ public class AllDebridTorrentClient : ITorrentClient
{
var result = await GetClient().Magnet.UploadMagnetAsync(magnetLink);
return result.Id.ToString();
if (result?.Id == null)
{
throw new Exception("Unable to add magnet link");
}
var resultId = result.Id.ToString();
if (resultId == null)
{
throw new Exception($"Invalid responseID {result.Id}");
}
return resultId;
}
public async Task<String> AddFile(Byte[] bytes)
{
var result = await GetClient().Magnet.UploadFileAsync(bytes);
return result.Id.ToString();
if (result?.Id == null)
{
throw new Exception("Unable to add torrent file");
}
var resultId = result.Id.ToString();
if (resultId == null)
{
throw new Exception($"Invalid responseID {result.Id}");
}
return resultId;
}
public async Task<IList<TorrentClientAvailableFile>> GetAvailableFiles(String hash)
@ -143,6 +172,11 @@ public class AllDebridTorrentClient : ITorrentClient
{
var result = await GetClient().Magnet.StatusAsync(torrentId);
if (result == null)
{
throw new Exception($"Unable to find magnet with ID {torrentId}");
}
return Map(result);
}
@ -155,13 +189,23 @@ public class AllDebridTorrentClient : ITorrentClient
{
var result = await GetClient().Links.DownloadLinkAsync(link);
if (result.Link == null)
{
throw new Exception("Invalid result link");
}
return result.Link;
}
public async Task<Torrent> UpdateData(Torrent torrent, TorrentClientTorrent torrentClientTorrent)
public async Task<Torrent> UpdateData(Torrent torrent, TorrentClientTorrent? torrentClientTorrent)
{
try
{
if (torrent.RdId == null)
{
return torrent;
}
torrentClientTorrent ??= await GetInfo(torrent.RdId);
if (!String.IsNullOrWhiteSpace(torrentClientTorrent.Filename))
@ -231,10 +275,20 @@ public class AllDebridTorrentClient : ITorrentClient
return torrent;
}
public async Task<IList<String>> GetDownloadLinks(Torrent torrent)
public async Task<IList<String>?> GetDownloadLinks(Torrent torrent)
{
if (torrent.RdId == null)
{
return null;
}
var magnet = await GetClient().Magnet.StatusAsync(torrent.RdId);
if (magnet == null)
{
return null;
}
var links = magnet.Links;
Log($"Getting download links", torrent);
@ -262,11 +316,6 @@ public class AllDebridTorrentClient : ITorrentClient
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)}");
@ -332,7 +381,7 @@ public class AllDebridTorrentClient : ITorrentClient
return result;
}
private void Log(String message, Torrent torrent = null)
private void Log(String message, Torrent? torrent = null)
{
if (torrent != null)
{

View file

@ -11,9 +11,8 @@ public interface ITorrentClient
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<Torrent> UpdateData(Torrent torrent, TorrentClientTorrent? torrentClientTorrent);
Task<IList<String>?> GetDownloadLinks(Torrent torrent);
}

View file

@ -141,11 +141,11 @@ public class RealDebridTorrentClient : ITorrentClient
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 groups = files.Where(m => m.Filename != null).GroupBy(m => $"{m.Filename}-{m.Filesize}");
var torrentClientAvailableFiles = groups.Select(m => new TorrentClientAvailableFile
{
Filename = m.First().Filename,
Filename = m.First().Filename!,
Filesize = m.First().Filesize
} ).ToList();
@ -211,7 +211,7 @@ public class RealDebridTorrentClient : ITorrentClient
Log("", torrent);
await GetClient().Torrents.SelectFilesAsync(torrent.RdId, fileIds.ToArray());
await GetClient().Torrents.SelectFilesAsync(torrent.RdId!, fileIds.ToArray());
}
public async Task<TorrentClientTorrent> GetInfo(String torrentId)
@ -230,23 +230,28 @@ public class RealDebridTorrentClient : ITorrentClient
{
var result = await GetClient().Unrestrict.LinkAsync(link);
if (result.Download == null)
{
throw new Exception($"Unrestrict returned an invalid download");
}
return result.Download;
}
public async Task<Data.Models.Data.Torrent> UpdateData(Data.Models.Data.Torrent torrent, TorrentClientTorrent torrentClientTorrent)
public async Task<Data.Models.Data.Torrent> UpdateData(Data.Models.Data.Torrent torrent, TorrentClientTorrent? torrentClientTorrent)
{
try
{
if (torrent == null)
if (torrent.RdId == null)
{
return null;
return torrent;
}
var rdTorrent = await GetInfo(torrent.RdId);
if (rdTorrent == null)
{
return torrent;
throw new Exception($"Resource not found");
}
if (!String.IsNullOrWhiteSpace(rdTorrent.Filename))
@ -302,10 +307,7 @@ public class RealDebridTorrentClient : ITorrentClient
{
if (ex.Message == "Resource not found")
{
if (torrent != null)
{
torrent.RdStatusRaw = "deleted";
}
torrent.RdStatusRaw = "deleted";
}
else
{
@ -316,10 +318,20 @@ public class RealDebridTorrentClient : ITorrentClient
return torrent;
}
public async Task<IList<String>> GetDownloadLinks(Data.Models.Data.Torrent torrent)
public async Task<IList<String>?> GetDownloadLinks(Data.Models.Data.Torrent torrent)
{
if (torrent.RdId == null)
{
return null;
}
var rdTorrent = await GetInfo(torrent.RdId);
if (rdTorrent.Links == null)
{
return null;
}
var downloadLinks = rdTorrent.Links.Where(m => !String.IsNullOrWhiteSpace(m)).ToList();
Log($"Found {downloadLinks.Count} links", torrent);
@ -360,7 +372,7 @@ public class RealDebridTorrentClient : ITorrentClient
return dateTimeOffset?.Subtract(_offset.Value).ToOffset(_offset.Value);
}
private void Log(String message, Data.Models.Data.Torrent torrent = null)
private void Log(String message, Data.Models.Data.Torrent? torrent = null)
{
if (torrent != null)
{

View file

@ -121,7 +121,7 @@ public class TorrentRunner
var aria2NetClient = new Aria2NetClient(Settings.Get.DownloadClient.Aria2cUrl, Settings.Get.DownloadClient.Aria2cSecret, _httpClient, 1);
var allDownloads = await aria2NetClient.TellAll();
var allDownloads = await aria2NetClient.TellAllAsync();
Log($"Found {allDownloads.Count} Aria2 downloads");
@ -162,7 +162,7 @@ public class TorrentRunner
{
// Retry the download if an error is encountered.
Log($"Download reported an error: {downloadClient.Error}", download, download.Torrent);
Log($"Download retry count {download.RetryCount}/{download.Torrent.DownloadRetryAttempts}, torrent retry count {download.Torrent.RetryCount}/{download.Torrent.TorrentRetryAttempts}", download, download.Torrent);
Log($"Download retry count {download.RetryCount}/{download.Torrent!.DownloadRetryAttempts}, torrent retry count {download.Torrent.RetryCount}/{download.Torrent.TorrentRetryAttempts}", download, download.Torrent);
if (download.RetryCount < download.Torrent.DownloadRetryAttempts)
{
@ -570,7 +570,7 @@ public class TorrentRunner
}
}
private void Log(String message, Download download, Torrent torrent)
private void Log(String message, Download? download, Torrent? torrent)
{
if (download != null)
{
@ -585,7 +585,7 @@ public class TorrentRunner
_logger.LogDebug(message);
}
private void Log(String message, Torrent torrent = null)
private void Log(String message, Torrent? torrent = null)
{
if (torrent != null)
{

View file

@ -42,7 +42,7 @@ public class Torrents
{
Provider.RealDebrid => realDebridTorrentClient,
Provider.AllDebrid => allDebridTorrentClient,
_ => null
_ => throw new Exception("Invalid Provider")
};
}
@ -72,7 +72,7 @@ public class Torrents
return torrents;
}
public async Task<Torrent> GetByHash(String hash)
public async Task<Torrent?> GetByHash(String hash)
{
var torrent = await _torrentData.GetByHash(hash);
@ -84,7 +84,7 @@ public class Torrents
return torrent;
}
public async Task UpdateCategory(String hash, String category)
public async Task UpdateCategory(String hash, String? category)
{
var torrent = await _torrentData.GetByHash(hash);
@ -256,7 +256,7 @@ public class Torrents
await _torrentData.Delete(torrentId);
}
if (deleteRdTorrent)
if (deleteRdTorrent && torrent.RdId != null)
{
Log($"Deleting RealDebrid Torrent", torrent);
@ -495,14 +495,17 @@ public class Torrents
await Task.Delay(100);
}
var downloadPath = DownloadPath(download.Torrent);
var downloadPath = DownloadPath(download.Torrent!);
var filePath = DownloadHelper.GetDownloadPath(downloadPath, download.Torrent, download);
var filePath = DownloadHelper.GetDownloadPath(downloadPath, download.Torrent!, download);
if (filePath != null)
{
Log($"Deleting {filePath}", download, download.Torrent);
await FileHelper.Delete(filePath);
}
Log($"Deleting {filePath}", download, download.Torrent);
await FileHelper.Delete(filePath);
Log($"Resetting", download, download.Torrent);
await _downloads.Reset(downloadId);
@ -510,7 +513,7 @@ public class Torrents
await _torrentData.UpdateComplete(download.TorrentId, null, null, false);
}
public async Task UpdateComplete(Guid torrentId, String error, DateTimeOffset datetime, Boolean retry)
public async Task UpdateComplete(Guid torrentId, String? error, DateTimeOffset datetime, Boolean retry)
{
await _torrentData.UpdateComplete(torrentId, error, datetime, retry);
}
@ -542,7 +545,7 @@ public class Torrents
await _torrentData.UpdateError(torrentId, error);
}
public async Task<Torrent> GetById(Guid torrentId)
public async Task<Torrent?> GetById(Guid torrentId)
{
var torrent = await _torrentData.GetById(torrentId);
@ -630,6 +633,12 @@ public class Torrents
}
var torrent = await _torrentData.GetById(torrentId);
if (torrent == null)
{
throw new Exception($"Cannot find Torrent with ID {torrentId}");
}
var downloads = await _downloads.GetForTorrent(torrentId);
var fileName = Settings.Get.General.RunOnTorrentCompleteFileName;
@ -638,7 +647,7 @@ public class Torrents
Log($"Parsing external program {fileName} with arguments {arguments}", torrent);
var downloadPath = DownloadPath(torrent);
var torrentPath = Path.Combine(downloadPath, torrent.RdName);
var torrentPath = Path.Combine(downloadPath, torrent.RdName ?? "Unknown");
arguments = arguments.Replace("%N", $"\"{torrent.RdName}\"");
arguments = arguments.Replace("%L", $"\"{torrent.Category}\"");
@ -646,7 +655,7 @@ public class Torrents
arguments = arguments.Replace("%R", $"\"{downloadPath}\"");
arguments = arguments.Replace("%D", $"\"{torrentPath}\"");
arguments = arguments.Replace("%C", downloads.Count.ToString(CultureInfo.InvariantCulture).Replace(",", "").Replace(".", ""));
arguments = arguments.Replace("%Z", torrent.RdSize.ToString(CultureInfo.InvariantCulture).Replace(",", "").Replace(".", ""));
arguments = arguments.Replace("%Z", torrent.RdSize?.ToString(CultureInfo.InvariantCulture).Replace(",", "").Replace(".", ""));
arguments = arguments.Replace("%I", torrent.Hash);
Log($"Executing external program {fileName} with arguments {arguments}", torrent);
@ -707,7 +716,7 @@ public class Torrents
}
}
private async Task UpdateTorrentClientData(Torrent torrent, TorrentClientTorrent torrentClientTorrent = null)
private async Task UpdateTorrentClientData(Torrent torrent, TorrentClientTorrent? torrentClientTorrent = null)
{
try
{
@ -736,7 +745,7 @@ public class Torrents
}
}
private void Log(String message, Data.Models.Data.Download download, Torrent torrent)
private void Log(String message, Data.Models.Data.Download? download, Torrent? torrent)
{
if (download != null)
{
@ -751,7 +760,7 @@ public class Torrents
_logger.LogDebug(message);
}
private void Log(String message, Torrent torrent = null)
private void Log(String message, Torrent? torrent = null)
{
if (torrent != null)
{

View file

@ -10,7 +10,7 @@ public class UnpackClient
{
public Boolean Finished { get; private set; }
public String Error { get; private set; }
public String? Error { get; private set; }
public Int64 BytesTotal { get; private set; }
public Int64 BytesDone { get; private set; }
@ -21,14 +21,14 @@ public class UnpackClient
private Boolean _cancelled;
private RarArchiveEntry _rarCurrentEntry;
private Dictionary<String, Int64> _rarfileStatus;
private RarArchiveEntry? _rarCurrentEntry;
private Dictionary<String, Int64>? _rarfileStatus;
public UnpackClient(Download download, String destinationPath)
{
_download = download;
_destinationPath = destinationPath;
_torrent = download.Torrent;
_torrent = download.Torrent ?? throw new Exception($"Torrent is null");
}
public void Start()
@ -91,7 +91,7 @@ public class UnpackClient
if (!entries.Any(m => m.Key.StartsWith(_torrent.RdName + @"\")) && !entries.Any(m => m.Key.StartsWith(_torrent.RdName + @"/")))
{
extractPath = Path.Combine(_destinationPath, _torrent.RdName);
extractPath = Path.Combine(_destinationPath, _torrent.RdName!);
}
if (entries.Any(m => m.Key.Contains(".r00")))
@ -129,14 +129,14 @@ public class UnpackClient
}
}
private void ArchiveOnCompressedBytesRead(Object sender, CompressedBytesReadEventArgs e)
private void ArchiveOnCompressedBytesRead(Object? sender, CompressedBytesReadEventArgs e)
{
if (_rarCurrentEntry == null)
{
return;
}
_rarfileStatus[_rarCurrentEntry.Key] = e.CompressedBytesRead;
_rarfileStatus![_rarCurrentEntry.Key] = e.CompressedBytesRead;
BytesDone = _rarfileStatus.Sum(m => m.Value);
}

View file

@ -1,7 +1,5 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.Internal;
using RdtClient.Service.Services;
namespace RdtClient.Web.Controllers;
@ -41,14 +39,24 @@ public class AuthController : Controller
[AllowAnonymous]
[Route("Create")]
[HttpPost]
public async Task<ActionResult> Create([FromBody] AuthControllerLoginRequest request)
public async Task<ActionResult> Create([FromBody] AuthControllerLoginRequest? request)
{
if (request == null)
{
return BadRequest();
}
var user = await _authentication.GetUser();
if (user != null)
{
return StatusCode(401);
}
if (String.IsNullOrEmpty(request.UserName) || String.IsNullOrEmpty(request.Password))
{
return BadRequest("Invalid UserName or Password");
}
var registerResult = await _authentication.Register(request.UserName, request.Password);
@ -65,8 +73,13 @@ public class AuthController : Controller
[AllowAnonymous]
[Route("SetupProvider")]
[HttpPost]
public async Task<ActionResult> SetupProvider([FromBody] AuthControllerSetupProviderRequest request)
public async Task<ActionResult> SetupProvider([FromBody] AuthControllerSetupProviderRequest? request)
{
if (request == null)
{
return BadRequest();
}
if (!String.IsNullOrEmpty(Settings.Get.Provider.ApiKey))
{
return StatusCode(401);
@ -81,8 +94,13 @@ public class AuthController : Controller
[AllowAnonymous]
[Route("Login")]
[HttpPost]
public async Task<ActionResult> Login([FromBody] AuthControllerLoginRequest request)
public async Task<ActionResult> Login([FromBody] AuthControllerLoginRequest? request)
{
if (request == null)
{
return BadRequest();
}
var user = await _authentication.GetUser();
if (user == null)
@ -90,6 +108,11 @@ public class AuthController : Controller
return StatusCode(402);
}
if (String.IsNullOrEmpty(request.UserName) || String.IsNullOrEmpty(request.Password))
{
return BadRequest("Invalid credentials");
}
var result = await _authentication.Login(request.UserName, request.Password);
if (!result.Succeeded)
@ -110,8 +133,18 @@ public class AuthController : Controller
[Route("Update")]
[HttpPost]
public async Task<ActionResult> Update([FromBody] AuthControllerUpdateRequest request)
public async Task<ActionResult> Update([FromBody] AuthControllerUpdateRequest? request)
{
if (request == null)
{
return BadRequest();
}
if (String.IsNullOrEmpty(request.UserName) || String.IsNullOrEmpty(request.Password))
{
return BadRequest("Invalid UserName or Password");
}
var updateResult = await _authentication.Update(request.UserName, request.Password);
if (!updateResult.Succeeded)
@ -125,18 +158,18 @@ public class AuthController : Controller
public class AuthControllerLoginRequest
{
public String UserName { get; set; }
public String Password { get; set; }
public String? UserName { get; set; }
public String? Password { get; set; }
}
public class AuthControllerSetupProviderRequest
{
public Int32 Provider { get; set; }
public String Token { get; set; }
public String? Token { get; set; }
}
public class AuthControllerUpdateRequest
{
public String UserName { get; set; }
public String Password { get; set; }
public String? UserName { get; set; }
public String? Password { get; set; }
}

View file

@ -26,6 +26,11 @@ public class QBittorrentController : Controller
[HttpGet]
public async Task<ActionResult> AuthLogin([FromQuery] QBAuthLoginRequest request)
{
if (String.IsNullOrWhiteSpace(request.UserName) || String.IsNullOrEmpty(request.Password))
{
return Ok("Fails.");
}
var result = await _qBittorrent.AuthLogin(request.UserName, request.Password);
if (result)
@ -121,7 +126,7 @@ public class QBittorrentController : Controller
[HttpPost]
public ActionResult<AppPreferences> AppDefaultSavePath()
{
var result = _qBittorrent.AppDefaultSavePath();
var result = Settings.AppDefaultSavePath;
return Ok(result);
}
@ -154,6 +159,11 @@ public class QBittorrentController : Controller
[HttpGet]
public async Task<ActionResult<IList<TorrentFileItem>>> TorrentsFiles([FromQuery] QBTorrentsHashRequest request)
{
if (String.IsNullOrWhiteSpace(request.Hash))
{
return BadRequest();
}
var result = await _qBittorrent.TorrentFileContents(request.Hash);
if (result == null)
@ -177,6 +187,11 @@ public class QBittorrentController : Controller
[HttpGet]
public async Task<ActionResult<IList<TorrentInfo>>> TorrentsProperties([FromQuery] QBTorrentsHashRequest request)
{
if (String.IsNullOrWhiteSpace(request.Hash))
{
return BadRequest();
}
var result = await _qBittorrent.TorrentProperties(request.Hash);
if (result == null)
@ -200,6 +215,11 @@ public class QBittorrentController : Controller
[HttpGet]
public async Task<ActionResult> TorrentsPause([FromQuery] QBTorrentsHashesRequest request)
{
if (String.IsNullOrWhiteSpace(request.Hashes))
{
return BadRequest();
}
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
@ -223,6 +243,11 @@ public class QBittorrentController : Controller
[HttpGet]
public async Task<ActionResult> TorrentsResume([FromQuery] QBTorrentsHashesRequest request)
{
if (String.IsNullOrWhiteSpace(request.Hashes))
{
return BadRequest();
}
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
@ -255,6 +280,11 @@ public class QBittorrentController : Controller
[HttpGet]
public async Task<ActionResult> TorrentsDelete([FromQuery] QBTorrentsDeleteRequest request)
{
if (String.IsNullOrWhiteSpace(request.Hashes))
{
return BadRequest();
}
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
@ -278,6 +308,11 @@ public class QBittorrentController : Controller
[HttpGet]
public async Task<ActionResult> TorrentsAdd([FromQuery] QBTorrentsAddRequest request)
{
if (String.IsNullOrWhiteSpace(request.Urls))
{
return BadRequest();
}
var urls = request.Urls.Split("\n");
foreach (var url in urls)
@ -294,7 +329,7 @@ public class QBittorrentController : Controller
}
else
{
throw new Exception($"Invalid torrent link format {url}");
return BadRequest($"Invalid torrent link format {url}");
}
}
@ -332,6 +367,11 @@ public class QBittorrentController : Controller
[HttpGet]
public async Task<ActionResult> TorrentsSetCategory([FromQuery] QBTorrentsSetCategoryRequest request)
{
if (String.IsNullOrWhiteSpace(request.Hashes))
{
return BadRequest();
}
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
@ -412,6 +452,11 @@ public class QBittorrentController : Controller
[HttpGet]
public async Task<ActionResult> TorrentsTopPrio([FromQuery] QBTorrentsHashesRequest request)
{
if (String.IsNullOrWhiteSpace(request.Hashes))
{
return BadRequest();
}
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
@ -451,51 +496,50 @@ public class QBittorrentController : Controller
public class QBAuthLoginRequest
{
public String UserName { get; set; }
public String Password { get; set; }
public String? UserName { get; set; }
public String? Password { get; set; }
}
public class QBTorrentsInfoRequest
{
public String Category { get; set; }
public String? Category { get; set; }
}
public class QBTorrentsHashRequest
{
public String Hash { get; set; }
public String Category { get; set; }
public String? Hash { get; set; }
}
public class QBTorrentsDeleteRequest
{
public String Hashes { get; set; }
public String? Hashes { get; set; }
public Boolean DeleteFiles { get; set; }
}
public class QBTorrentsAddRequest
{
public String Urls { get; set; }
public String Category { get; set; }
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 String? Hashes { get; set; }
public String? Category { get; set; }
}
public class QBTorrentsCreateCategoryRequest
{
public String Category { get; set; }
public String? Category { get; set; }
}
public class QBTorrentsRemoveCategoryRequest
{
public String Categories { get; set; }
public String? Categories { get; set; }
}
public class QBTorrentsHashesRequest
{
public String Hashes { get; set; }
public String? Hashes { get; set; }
}

View file

@ -1,7 +1,13 @@
using Microsoft.AspNetCore.Authorization;
using System.Diagnostics;
using Aria2NET;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using RdtClient.Data.Data;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.Internal;
using RdtClient.Service.Helpers;
using RdtClient.Service.Services;
using RdtClient.Service.Services.Downloaders;
namespace RdtClient.Web.Controllers;
@ -22,14 +28,19 @@ public class SettingsController : Controller
[Route("")]
public ActionResult Get()
{
var result = _settings.GetAll();
var result = SettingData.GetAll();
return Ok(result);
}
[HttpPut]
[Route("")]
public async Task<ActionResult> Update([FromBody] IList<SettingProperty> settings)
public async Task<ActionResult> Update([FromBody] IList<SettingProperty>? settings)
{
if (settings == null)
{
return BadRequest();
}
await _settings.Update(settings);
return Ok();
@ -45,9 +56,30 @@ public class SettingsController : Controller
[HttpPost]
[Route("TestPath")]
public async Task<ActionResult> TestPath([FromBody] SettingsControllerTestPathRequest request)
public async Task<ActionResult> TestPath([FromBody] SettingsControllerTestPathRequest? request)
{
await _settings.TestPath(request.Path);
if (request == null)
{
return BadRequest();
}
if (String.IsNullOrEmpty(request.Path))
{
return BadRequest("Invalid path");
}
var path = request.Path.TrimEnd('/').TrimEnd('\\');
if (!Directory.Exists(path))
{
throw new Exception($"Path {path} does not exist");
}
var testFile = $"{path}/test.txt";
await System.IO.File.WriteAllTextAsync(testFile, "RealDebridClient Test File, you can remove this file.");
await FileHelper.Delete(testFile);
return Ok();
}
@ -56,25 +88,120 @@ public class SettingsController : Controller
[Route("TestDownloadSpeed")]
public async Task<ActionResult> TestDownloadSpeed(CancellationToken cancellationToken)
{
var downloadSpeed = await _settings.TestDownloadSpeed(cancellationToken);
var downloadPath = Settings.Get.DownloadClient.DownloadPath;
return Ok(downloadSpeed);
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
{
RdName = ""
}
};
var downloadClient = new DownloadClient(download, download.Torrent, downloadPath);
await downloadClient.Start(Settings.Get);
var httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(10)
};
while (!downloadClient.Finished)
{
await Task.Delay(1000, CancellationToken.None);
if (cancellationToken.IsCancellationRequested)
{
await downloadClient.Cancel();
}
if (downloadClient.Downloader is Aria2cDownloader aria2Downloader)
{
var aria2NetClient = new Aria2NetClient(Settings.Get.DownloadClient.Aria2cUrl, Settings.Get.DownloadClient.Aria2cSecret, httpClient, 1);
var allDownloads = await aria2NetClient.TellAllAsync(cancellationToken);
await aria2Downloader.Update(allDownloads);
}
if (downloadClient.BytesDone > 1024 * 1024 * 50)
{
await downloadClient.Cancel();
break;
}
}
await FileHelper.Delete(testFilePath);
await Settings.Clean();
return Ok(downloadClient.Speed);
}
[HttpGet]
[Route("TestWriteSpeed")]
public async Task<ActionResult> TestWriteSpeed()
{
var writeSpeed = await _settings.TestWriteSpeed();
var downloadPath = Settings.Get.DownloadClient.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)
{
rnd.NextBytes(buffer);
await fileStream.WriteAsync(buffer.AsMemory(0, buffer.Length));
}
watch.Stop();
var writeSpeed = fileStream.Length / watch.Elapsed.TotalSeconds;
fileStream.Close();
await FileHelper.Delete(testFilePath);
return Ok(writeSpeed);
}
[HttpPost]
[Route("TestAria2cConnection")]
public async Task<ActionResult<String>> TestAria2cConnection([FromBody] SettingsControllerTestAria2cConnectionRequest request)
public async Task<ActionResult<String>> TestAria2cConnection([FromBody] SettingsControllerTestAria2cConnectionRequest? request)
{
var version = await _settings.GetAria2cVersion(request.Url, request.Secret);
if (request == null)
{
return BadRequest();
}
if (String.IsNullOrEmpty(request.Url))
{
return BadRequest("Invalid Url");
}
var client = new Aria2NetClient(request.Url, request.Secret);
var version = await client.GetVersionAsync();
return Ok(version);
}
@ -82,11 +209,11 @@ public class SettingsController : Controller
public class SettingsControllerTestPathRequest
{
public String Path { get; set; }
public String? Path { get; set; }
}
public class SettingsControllerTestAria2cConnectionRequest
{
public String Url { get; set; }
public String Secret { get; set; }
public String? Url { get; set; }
public String? Secret { get; set; }
}

View file

@ -67,22 +67,25 @@ public class TorrentsController : Controller
[HttpPost]
[Route("UploadFile")]
public async Task<ActionResult> UploadFile([FromForm] IFormFile file,
public async Task<ActionResult> UploadFile([FromForm] IFormFile? file,
[ModelBinder(BinderType = typeof(JsonModelBinder))]
TorrentControllerUploadFileRequest formData)
TorrentControllerUploadFileRequest? formData)
{
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.Select(m => m.ErrorMessage)).ToList();
return BadRequest(errors);
}
if (file == null || file.Length <= 0)
{
throw new Exception("Invalid torrent file");
return BadRequest("Invalid torrent file");
}
if (formData?.Torrent == null)
{
return BadRequest("Invalid Torrent");
}
var fileStream = file.OpenReadStream();
@ -100,17 +103,29 @@ public class TorrentsController : Controller
[HttpPost]
[Route("UploadMagnet")]
public async Task<ActionResult> UploadMagnet([FromBody] TorrentControllerUploadMagnetRequest request)
public async Task<ActionResult> UploadMagnet([FromBody] TorrentControllerUploadMagnetRequest? request)
{
if (request == null)
{
return BadRequest();
}
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.Select(m => m.ErrorMessage)).ToList();
return BadRequest(errors);
}
if (String.IsNullOrEmpty(request.MagnetLink))
{
return BadRequest("Invalid magnet link");
}
if (request.Torrent == null)
{
return BadRequest("Invalid Torrent");
}
await _torrents.UploadMagnet(request.MagnetLink, request.Torrent);
return Ok();
@ -118,11 +133,11 @@ public class TorrentsController : Controller
[HttpPost]
[Route("CheckFiles")]
public async Task<ActionResult> CheckFiles([FromForm] IFormFile file)
public async Task<ActionResult> CheckFiles([FromForm] IFormFile? file)
{
if (file == null || file.Length <= 0)
{
throw new Exception("Invalid torrent file");
return BadRequest("Invalid torrent file");
}
var fileStream = file.OpenReadStream();
@ -142,8 +157,13 @@ public class TorrentsController : Controller
[HttpPost]
[Route("CheckFilesMagnet")]
public async Task<ActionResult> CheckFilesMagnet([FromBody] TorrentControllerCheckFilesRequest request)
public async Task<ActionResult> CheckFilesMagnet([FromBody] TorrentControllerCheckFilesRequest? request)
{
if (request == null)
{
return BadRequest();
}
var magnet = MagnetLink.Parse(request.MagnetLink);
var result = await _torrents.GetAvailableFiles(magnet.InfoHash.ToHex());
@ -153,8 +173,13 @@ public class TorrentsController : Controller
[HttpPost]
[Route("Delete/{torrentId:guid}")]
public async Task<ActionResult> Delete(Guid torrentId, [FromBody] TorrentControllerDeleteRequest request)
public async Task<ActionResult> Delete(Guid torrentId, [FromBody] TorrentControllerDeleteRequest? request)
{
if (request == null)
{
return BadRequest();
}
await _torrents.Delete(torrentId, request.DeleteData, request.DeleteRdTorrent, request.DeleteLocalFiles);
return Ok();
@ -181,8 +206,13 @@ public class TorrentsController : Controller
[HttpPut]
[Route("Update")]
public async Task<ActionResult> Update([FromBody] Torrent torrent)
public async Task<ActionResult> Update([FromBody] Torrent? torrent)
{
if (torrent == null)
{
return BadRequest();
}
await _torrents.Update(torrent);
return Ok();
@ -191,13 +221,13 @@ public class TorrentsController : Controller
public class TorrentControllerUploadFileRequest
{
public Torrent Torrent { get; set; }
public Torrent? Torrent { get; set; }
}
public class TorrentControllerUploadMagnetRequest
{
public String MagnetLink { get; set; }
public Torrent Torrent { get; set; }
public String? MagnetLink { get; set; }
public Torrent? Torrent { get; set; }
}
public class TorrentControllerDeleteRequest
@ -209,5 +239,5 @@ public class TorrentControllerDeleteRequest
public class TorrentControllerCheckFilesRequest
{
public String MagnetLink { get; set; }
public String? MagnetLink { get; set; }
}

View file

@ -32,17 +32,20 @@ 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));
if (appSettings.Logging?.File?.Path != null)
{
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 =>
{

View file

@ -5,7 +5,7 @@
<OutputType>Exe</OutputType>
<UserSecretsId>94c24cba-f03f-4453-a671-3640b517c573</UserSecretsId>
<Version>2.0.12</Version>
<Nullable>disable</Nullable>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
</PropertyGroup>
@ -29,15 +29,15 @@
</ItemGroup>
<ItemGroup>
<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">
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="6.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.5">
<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.4" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="6.0.5" />
<PackageReference Include="Serilog" Version="2.11.0" />
<PackageReference Include="Serilog.AspNetCore" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />

View file

@ -179,6 +179,7 @@
<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/Abbreviations/=XML/@EntryIndexedValue">Aria2c</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>
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>