Add retry count as a setting per torrent and global.

Add error status on the torrent itself too.
Improved torrent retrying.
This commit is contained in:
Roger Far 2021-10-30 10:25:53 -06:00
parent a0d000138c
commit b3dd856410
44 changed files with 2143 additions and 627 deletions

View file

@ -84,12 +84,43 @@
<div class="field"> <div class="field">
<label class="label">Priority</label> <label class="label">Priority</label>
<div class="control"> <div class="control">
<input class="input" type="number" step="1" [(ngModel)]="priority" /> <input class="input" type="number" step="1" [(ngModel)]="priority" />
</div> </div>
<p class="help"> <p class="help">
Set the priority for this torrent where 1 is the highest. When empty it will be assigned the lowest priority. Set the priority for this torrent where 1 is the highest. When empty it will be assigned the lowest priority.
</p> </p>
</div> </div>
<div class="field">
<label class="label">Automatic retry downloads</label>
<div class="control">
<input
class="input"
type="number"
max="1000"
min="0"
step="1"
[(ngModel)]="downloadRetryAttempts"
/>
</div>
<p class="help">When a single download fails it will retry it this many times before marking it as failed.</p>
</div>
<div class="field">
<label class="label">Automatic retry torrent</label>
<div class="control">
<input
class="input"
type="number"
max="1000"
min="0"
step="1"
[(ngModel)]="torrentRetryAttempts"
/>
</div>
<p class="help">
When a single download has failed multiple times (see setting above) or when the torrent itself received an
error it will retry the full torrent this many times before marking it failed.
</p>
</div>
</div> </div>
<div fxFlex> <div fxFlex>
<div class="field"> <div class="field">

View file

@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { TorrentService } from 'src/app/torrent.service'; import { TorrentService } from 'src/app/torrent.service';
import { TorrentFileAvailability } from '../models/torrent.model'; import { Torrent, TorrentFileAvailability } from '../models/torrent.model';
@Component({ @Component({
selector: 'app-add-new-torrent', selector: 'app-add-new-torrent',
@ -21,6 +21,9 @@ export class AddNewTorrentComponent implements OnInit {
public downloadMinSize: number = 0; public downloadMinSize: number = 0;
public downloadRetryAttempts: number = 3;
public torrentRetryAttempts: number = 1;
public availableFiles: TorrentFileAvailability[] = []; public availableFiles: TorrentFileAvailability[] = [];
public downloadFiles: { [key: string]: boolean } = {}; public downloadFiles: { [key: string]: boolean } = {};
public allSelected: boolean; public allSelected: boolean;
@ -91,46 +94,36 @@ export class AddNewTorrentComponent implements OnInit {
downloadManualFiles = selectedFiles.join(','); downloadManualFiles = selectedFiles.join(',');
} }
const torrent = new Torrent();
torrent.category = this.category;
torrent.downloadAction = this.downloadAction;
torrent.finishedAction = this.finishedAction;
torrent.downloadMinSize = this.downloadMinSize;
torrent.downloadManualFiles = downloadManualFiles;
torrent.priority = this.priority;
torrent.torrentRetryAttempts = this.torrentRetryAttempts;
torrent.downloadRetryAttempts = this.downloadRetryAttempts;
if (this.magnetLink) { if (this.magnetLink) {
this.torrentService this.torrentService.uploadMagnet(this.magnetLink, torrent).subscribe(
.uploadMagnet( () => {
this.magnetLink, this.router.navigate(['/']);
this.category, },
this.downloadAction, (err) => {
this.finishedAction, this.error = err.error;
this.downloadMinSize, this.saving = false;
downloadManualFiles, }
this.priority );
)
.subscribe(
() => {
this.router.navigate(['/']);
},
(err) => {
this.error = err.error;
this.saving = false;
}
);
} else if (this.selectedFile) { } else if (this.selectedFile) {
this.torrentService this.torrentService.uploadFile(this.selectedFile, torrent).subscribe(
.uploadFile( () => {
this.selectedFile, this.router.navigate(['/']);
this.category, },
this.downloadAction, (err) => {
this.finishedAction, this.error = err.error;
this.downloadMinSize, this.saving = false;
downloadManualFiles, }
this.priority );
)
.subscribe(
() => {
this.router.navigate(['/']);
},
(err) => {
this.error = err.error;
this.saving = false;
}
);
} else { } else {
this.error = 'No magnet or file uploaded'; this.error = 'No magnet or file uploaded';
this.saving = false; this.saving = false;

View file

@ -17,7 +17,11 @@ export class Torrent {
public isFile: boolean; public isFile: boolean;
public retryCount: number; public retryCount: number;
public downloadRetryAttempts: number;
public torrentRetryAttempts: number;
public priority: number; public priority: number;
public error: string;
public rdId: string; public rdId: string;
public rdName: string; public rdName: string;

View file

@ -204,6 +204,25 @@
</div> </div>
</div> </div>
</div> </div>
<div class="field">
<label class="label">Automatic retry downloads</label>
<div class="control">
<input class="input" type="number" max="1000" min="0" step="1" [(ngModel)]="downloadRetryAttempts" />
</div>
<p class="help">When a single download fails it will retry it this many times before marking it as failed.</p>
</div>
<div class="field">
<label class="label">Automatic retry torrent</label>
<div class="control">
<input class="input" type="number" max="1000" min="0" step="1" [(ngModel)]="torrentRetryAttempts" />
</div>
<p class="help">
When a single download has failed multiple times (see setting above) or when the torrent itself received an error
it will retry the full torrent this many times before marking it failed.
</p>
</div>
</div> </div>
<div *ngIf="activeTab === 3"> <div *ngIf="activeTab === 3">

View file

@ -42,6 +42,9 @@ export class SettingsComponent implements OnInit {
public aria2cUrl: string; public aria2cUrl: string;
public aria2cSecret: string; public aria2cSecret: string;
public downloadRetryAttempts: number;
public torrentRetryAttempts: number;
constructor(private settingsService: SettingsService) {} constructor(private settingsService: SettingsService) {}
ngOnInit(): void { ngOnInit(): void {
@ -69,6 +72,8 @@ export class SettingsComponent implements OnInit {
this.settingProxyServer = this.getSetting(results, 'ProxyServer'); this.settingProxyServer = this.getSetting(results, 'ProxyServer');
this.aria2cUrl = this.getSetting(results, 'Aria2cUrl'); this.aria2cUrl = this.getSetting(results, 'Aria2cUrl');
this.aria2cSecret = this.getSetting(results, 'Aria2cSecret'); this.aria2cSecret = this.getSetting(results, 'Aria2cSecret');
this.downloadRetryAttempts = parseInt(this.getSetting(results, 'DownloadRetryAttempts'), 10);
this.torrentRetryAttempts = parseInt(this.getSetting(results, 'TorrentRetryAttempts'), 10);
}, },
(err) => { (err) => {
this.error = err.error; this.error = err.error;
@ -141,6 +146,14 @@ export class SettingsComponent implements OnInit {
settingId: 'Aria2cSecret', settingId: 'Aria2cSecret',
value: this.aria2cSecret, value: this.aria2cSecret,
}, },
{
settingId: 'DownloadRetryAttempts',
value: (this.downloadRetryAttempts ?? 0).toString(),
},
{
settingId: 'TorrentRetryAttempts',
value: (this.torrentRetryAttempts ?? 0).toString(),
},
]; ];
this.settingsService.update(settings).subscribe( this.settingsService.update(settings).subscribe(

View file

@ -9,6 +9,11 @@ export class TorrentStatusPipe implements PipeTransform {
constructor(private pipe: FileSizePipe) {} constructor(private pipe: FileSizePipe) {}
transform(torrent: Torrent): string { transform(torrent: Torrent): string {
if (torrent.error) {
return torrent.error;
}
if (torrent.downloads.length > 0) { if (torrent.downloads.length > 0) {
const errors = torrent.downloads.where((m) => m.error != null); const errors = torrent.downloads.where((m) => m.error != null);

View file

@ -37,41 +37,17 @@ export class TorrentService {
return this.http.get<Torrent>(`/Api/Torrents/Get/${torrentId}`); return this.http.get<Torrent>(`/Api/Torrents/Get/${torrentId}`);
} }
public uploadMagnet( public uploadMagnet(magnetLink: string, torrent: Torrent): Observable<void> {
magnetLink: string,
category: string,
downloadAction: number,
finishedAction: number,
downloadMinSize: number,
downloadManualFiles: string,
priority: number
): Observable<void> {
return this.http.post<void>(`/Api/Torrents/UploadMagnet`, { return this.http.post<void>(`/Api/Torrents/UploadMagnet`, {
magnetLink, magnetLink,
category, torrent,
downloadAction,
finishedAction,
downloadMinSize,
downloadManualFiles,
priority,
}); });
} }
public uploadFile( public uploadFile(file: File, torrent: Torrent): Observable<void> {
file: File,
category: string,
downloadAction: number,
finishedAction: number,
downloadMinSize: number,
downloadManualFiles: string,
priority: number
): Observable<void> {
const formData: FormData = new FormData(); const formData: FormData = new FormData();
formData.append('file', file); formData.append('file', file);
formData.append( formData.append('formData', JSON.stringify(torrent));
'formData',
JSON.stringify({ category, downloadAction, finishedAction, downloadMinSize, downloadManualFiles, priority })
);
return this.http.post<void>(`/Api/Torrents/UploadFile`, formData); return this.http.post<void>(`/Api/Torrents/UploadFile`, formData);
} }

View file

@ -28,7 +28,7 @@
<button class="button is-primary" (click)="showRetryModal()">Retry Torrent</button> <button class="button is-primary" (click)="showRetryModal()">Retry Torrent</button>
</div> </div>
<div class="control"> <div class="control">
<button class="button is-light" (click)="showUpdateSettingsModal()">Change priority</button> <button class="button is-light" (click)="showUpdateSettingsModal()">Change Settings</button>
</div> </div>
</div> </div>
<div class="field"> <div class="field">
@ -37,7 +37,7 @@
</div> </div>
<div class="field"> <div class="field">
<label class="label">Retry count</label> <label class="label">Retry count</label>
{{ torrent.retryCount }} / 2 {{ torrent.retryCount }} / {{ torrent.torrentRetryAttempts}}
</div> </div>
<div class="field"> <div class="field">
<label class="label">Hash</label> <label class="label">Hash</label>
@ -256,7 +256,7 @@
</div> </div>
<div class="field"> <div class="field">
<label class="label">Retry Count</label> <label class="label">Retry Count</label>
{{ download.retryCount }} / 3 {{ download.retryCount }} / {{ torrent.downloadRetryAttempts }}
</div> </div>
</div> </div>
<div fxFlex> <div fxFlex>
@ -470,6 +470,37 @@
Set the priority for this torrent where 1 is the highest. When empty it will be assigned the lowest priority. Set the priority for this torrent where 1 is the highest. When empty it will be assigned the lowest priority.
</p> </p>
</div> </div>
<div class="field">
<label class="label">Automatic retry downloads</label>
<div class="control">
<input
class="input"
type="number"
max="1000"
min="0"
step="1"
[(ngModel)]="updateSettingsDownloadRetryAttempts"
/>
</div>
<p class="help">When a single download fails it will retry it this many times before marking it as failed.</p>
</div>
<div class="field">
<label class="label">Automatic retry torrent</label>
<div class="control">
<input
class="input"
type="number"
max="1000"
min="0"
step="1"
[(ngModel)]="updateSettingsTorrentRetryAttempts"
/>
</div>
<p class="help">
When a single download has failed multiple times (see setting above) or when the torrent itself received an
error it will retry the full torrent this many times before marking it failed.
</p>
</div>
</section> </section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<button <button

View file

@ -36,6 +36,8 @@ export class TorrentComponent implements OnInit {
public isUpdateSettingsModalActive: boolean; public isUpdateSettingsModalActive: boolean;
public updateSettingsPriority: number; public updateSettingsPriority: number;
public updateSettingsDownloadRetryAttempts: number;
public updateSettingsTorrentRetryAttempts: number;
public updating: boolean; public updating: boolean;
constructor(private activatedRoute: ActivatedRoute, private router: Router, private torrentService: TorrentService) {} constructor(private activatedRoute: ActivatedRoute, private router: Router, private torrentService: TorrentService) {}
@ -170,6 +172,8 @@ export class TorrentComponent implements OnInit {
public showUpdateSettingsModal(): void { public showUpdateSettingsModal(): void {
this.updateSettingsPriority = this.torrent.priority; this.updateSettingsPriority = this.torrent.priority;
this.updateSettingsDownloadRetryAttempts = this.torrent.downloadRetryAttempts;
this.updateSettingsTorrentRetryAttempts = this.torrent.torrentRetryAttempts;
this.isUpdateSettingsModalActive = true; this.isUpdateSettingsModalActive = true;
} }
@ -182,6 +186,8 @@ export class TorrentComponent implements OnInit {
this.updating = true; this.updating = true;
this.torrent.priority = this.updateSettingsPriority; this.torrent.priority = this.updateSettingsPriority;
this.torrent.downloadRetryAttempts = this.updateSettingsDownloadRetryAttempts;
this.torrent.torrentRetryAttempts = this.updateSettingsTorrentRetryAttempts;
this.torrentService.update(this.torrent).subscribe( this.torrentService.update(this.torrent).subscribe(
() => { () => {

View file

@ -147,6 +147,18 @@ namespace RdtClient.Data.Data
SettingId = "Aria2cSecret", SettingId = "Aria2cSecret",
Type = "String", Type = "String",
Value = "" Value = ""
},
new Setting
{
SettingId = "DownloadRetryAttempts",
Type = "Int32",
Value = "3"
},
new Setting
{
SettingId = "TorrentRetryAttempts",
Type = "Int32",
Value = "1"
} }
}; };

View file

@ -64,6 +64,8 @@ namespace RdtClient.Data.Data
Categories = GetString("Categories"), Categories = GetString("Categories"),
Aria2cUrl = GetString("Aria2cUrl"), Aria2cUrl = GetString("Aria2cUrl"),
Aria2cSecret = GetString("Aria2cSecret"), Aria2cSecret = GetString("Aria2cSecret"),
DownloadRetryAttempts = GetInt32("DownloadRetryAttempts"),
TorrentRetryAttempts = GetInt32("TorrentRetryAttempts")
}; };
} }

View file

@ -4,7 +4,6 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using RdtClient.Data.Enums;
using RdtClient.Data.Models.Data; using RdtClient.Data.Models.Data;
namespace RdtClient.Data.Data namespace RdtClient.Data.Data
@ -12,7 +11,7 @@ namespace RdtClient.Data.Data
public class TorrentData public class TorrentData
{ {
private static IList<Torrent> _torrentCache; private static IList<Torrent> _torrentCache;
private static readonly SemaphoreSlim _torrentCacheLock = new(1); private static readonly SemaphoreSlim TorrentCacheLock = new(1, 1);
private readonly DataContext _dataContext; private readonly DataContext _dataContext;
@ -23,7 +22,7 @@ namespace RdtClient.Data.Data
public async Task<IList<Torrent>> Get() public async Task<IList<Torrent>> Get()
{ {
await _torrentCacheLock.WaitAsync(); await TorrentCacheLock.WaitAsync();
try try
{ {
@ -36,7 +35,7 @@ namespace RdtClient.Data.Data
} }
finally finally
{ {
_torrentCacheLock.Release(); TorrentCacheLock.Release();
} }
} }
@ -82,38 +81,35 @@ namespace RdtClient.Data.Data
public async Task<Torrent> Add(String realDebridId, public async Task<Torrent> Add(String realDebridId,
String hash, String hash,
String category,
TorrentDownloadAction downloadAction,
TorrentFinishedAction finishedAction,
Int32 downloadMinSize,
String downloadManualFiles,
String fileOrMagnetContents, String fileOrMagnetContents,
Boolean isFile, Boolean isFile,
Int32? priority) Torrent torrent)
{ {
var torrent = new Torrent var newTorrent = new Torrent
{ {
TorrentId = Guid.NewGuid(), TorrentId = Guid.NewGuid(),
Added = DateTimeOffset.UtcNow, Added = DateTimeOffset.UtcNow,
RdId = realDebridId, RdId = realDebridId,
Hash = hash.ToLower(), Hash = hash.ToLower(),
Category = category, Category = torrent.Category,
DownloadAction = downloadAction, DownloadAction = torrent.DownloadAction,
FinishedAction = finishedAction, FinishedAction = torrent.FinishedAction,
DownloadMinSize = downloadMinSize, DownloadMinSize = torrent.DownloadMinSize,
DownloadManualFiles = downloadManualFiles, DownloadManualFiles = torrent.DownloadManualFiles,
FileOrMagnet = fileOrMagnetContents, FileOrMagnet = fileOrMagnetContents,
IsFile = isFile, IsFile = isFile,
Priority = priority Priority = torrent.Priority,
TorrentRetryAttempts = torrent.TorrentRetryAttempts,
DownloadRetryAttempts = torrent.DownloadRetryAttempts
}; };
await _dataContext.Torrents.AddAsync(torrent); await _dataContext.Torrents.AddAsync(newTorrent);
await _dataContext.SaveChangesAsync(); await _dataContext.SaveChangesAsync();
await VoidCache(); await VoidCache();
return torrent; return newTorrent;
} }
public async Task UpdateRdData(Torrent torrent) public async Task UpdateRdData(Torrent torrent)
@ -157,6 +153,8 @@ namespace RdtClient.Data.Data
} }
dbTorrent.Priority = torrent.Priority; dbTorrent.Priority = torrent.Priority;
dbTorrent.DownloadRetryAttempts = torrent.DownloadRetryAttempts;
dbTorrent.TorrentRetryAttempts = torrent.TorrentRetryAttempts;
await _dataContext.SaveChangesAsync(); await _dataContext.SaveChangesAsync();
@ -179,7 +177,7 @@ namespace RdtClient.Data.Data
await VoidCache(); await VoidCache();
} }
public async Task UpdateComplete(Guid torrentId, DateTimeOffset? datetime) public async Task UpdateComplete(Guid torrentId, String error, DateTimeOffset? datetime)
{ {
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId); var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
@ -188,7 +186,28 @@ namespace RdtClient.Data.Data
return; return;
} }
if (String.IsNullOrWhiteSpace(error))
{
var downloads = await _dataContext.Downloads.AsNoTracking().Where(m => m.TorrentId == torrentId).ToListAsync();
var downloadWithErrors = downloads.Where(m => !String.IsNullOrWhiteSpace(m.Error)).ToList();
if (downloads.Any())
{
error = $"{downloadWithErrors.Count}/{downloads.Count} downloads failed with errors";
}
}
if (!String.IsNullOrWhiteSpace(error))
{
if (dbTorrent.RetryCount < dbTorrent.TorrentRetryAttempts)
{
dbTorrent.RetryCount += 1;
dbTorrent.Retry = DateTime.UtcNow;
}
}
dbTorrent.Completed = datetime; dbTorrent.Completed = datetime;
dbTorrent.Error = error;
await _dataContext.SaveChangesAsync(); await _dataContext.SaveChangesAsync();
@ -211,22 +230,6 @@ namespace RdtClient.Data.Data
await VoidCache(); await VoidCache();
} }
public async Task UpdateRetryCount(Guid torrentId, Int32 retryCount)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.RetryCount = retryCount;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task UpdatePriority(Guid torrentId, Int32? priority) public async Task UpdatePriority(Guid torrentId, Int32? priority)
{ {
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId); var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
@ -243,6 +246,39 @@ namespace RdtClient.Data.Data
await VoidCache(); await VoidCache();
} }
public async Task UpdateRetry(Guid torrentId, DateTimeOffset? dateTime, Int32 retryCount)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.RetryCount = retryCount;
dbTorrent.Retry = dateTime;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task UpdateError(Guid torrentId, String error)
{
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
if (dbTorrent == null)
{
return;
}
dbTorrent.Error = error;
await _dataContext.SaveChangesAsync();
await VoidCache();
}
public async Task Delete(Guid torrentId) public async Task Delete(Guid torrentId)
{ {
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId); var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
@ -261,7 +297,7 @@ namespace RdtClient.Data.Data
public static async Task VoidCache() public static async Task VoidCache()
{ {
await _torrentCacheLock.WaitAsync(); await TorrentCacheLock.WaitAsync();
try try
{ {
@ -269,7 +305,7 @@ namespace RdtClient.Data.Data
} }
finally finally
{ {
_torrentCacheLock.Release(); TorrentCacheLock.Release();
} }
} }
} }

View file

@ -0,0 +1,445 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using RdtClient.Data.Data;
namespace RdtClient.Data.Migrations
{
[DbContext(typeof(DataContext))]
[Migration("20211029131846_Torrents_Add_Retry")]
partial class Torrents_Add_Retry
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "5.0.11");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b =>
{
b.Property<Guid>("DownloadId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("Added")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("Completed")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("DownloadFinished")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("DownloadQueued")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("DownloadStarted")
.HasColumnType("TEXT");
b.Property<string>("Error")
.HasColumnType("TEXT");
b.Property<string>("Link")
.HasColumnType("TEXT");
b.Property<string>("Path")
.HasColumnType("TEXT");
b.Property<string>("RemoteId")
.HasColumnType("TEXT");
b.Property<int>("RetryCount")
.HasColumnType("INTEGER");
b.Property<Guid>("TorrentId")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("UnpackingFinished")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("UnpackingQueued")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("UnpackingStarted")
.HasColumnType("TEXT");
b.HasKey("DownloadId");
b.HasIndex("TorrentId");
b.ToTable("Downloads");
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Setting", b =>
{
b.Property<string>("SettingId")
.HasColumnType("TEXT");
b.Property<string>("Type")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("SettingId");
b.ToTable("Settings");
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
{
b.Property<Guid>("TorrentId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("Added")
.HasColumnType("TEXT");
b.Property<string>("Category")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("Completed")
.HasColumnType("TEXT");
b.Property<int>("DownloadAction")
.HasColumnType("INTEGER");
b.Property<string>("DownloadManualFiles")
.HasColumnType("TEXT");
b.Property<int>("DownloadMinSize")
.HasColumnType("INTEGER");
b.Property<string>("FileOrMagnet")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("FilesSelected")
.HasColumnType("TEXT");
b.Property<int>("FinishedAction")
.HasColumnType("INTEGER");
b.Property<string>("Hash")
.HasColumnType("TEXT");
b.Property<bool>("IsFile")
.HasColumnType("INTEGER");
b.Property<int?>("Priority")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("RdAdded")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("RdEnded")
.HasColumnType("TEXT");
b.Property<string>("RdFiles")
.HasColumnType("TEXT");
b.Property<string>("RdHost")
.HasColumnType("TEXT");
b.Property<string>("RdId")
.HasColumnType("TEXT");
b.Property<string>("RdName")
.HasColumnType("TEXT");
b.Property<long>("RdProgress")
.HasColumnType("INTEGER");
b.Property<long?>("RdSeeders")
.HasColumnType("INTEGER");
b.Property<long>("RdSize")
.HasColumnType("INTEGER");
b.Property<long?>("RdSpeed")
.HasColumnType("INTEGER");
b.Property<long>("RdSplit")
.HasColumnType("INTEGER");
b.Property<int>("RdStatus")
.HasColumnType("INTEGER");
b.Property<string>("RdStatusRaw")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("Retry")
.HasColumnType("TEXT");
b.Property<int>("RetryCount")
.HasColumnType("INTEGER");
b.HasKey("TorrentId");
b.ToTable("Torrents");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b =>
{
b.HasOne("RdtClient.Data.Models.Data.Torrent", "Torrent")
.WithMany("Downloads")
.HasForeignKey("TorrentId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Torrent");
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
{
b.Navigation("Downloads");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,24 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace RdtClient.Data.Migrations
{
public partial class Torrents_Add_Retry : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTimeOffset>(
name: "Retry",
table: "Torrents",
type: "TEXT",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Retry",
table: "Torrents");
}
}
}

View file

@ -0,0 +1,448 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using RdtClient.Data.Data;
namespace RdtClient.Data.Migrations
{
[DbContext(typeof(DataContext))]
[Migration("20211029133429_Torrents_Error")]
partial class Torrents_Error
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "5.0.11");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b =>
{
b.Property<Guid>("DownloadId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("Added")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("Completed")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("DownloadFinished")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("DownloadQueued")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("DownloadStarted")
.HasColumnType("TEXT");
b.Property<string>("Error")
.HasColumnType("TEXT");
b.Property<string>("Link")
.HasColumnType("TEXT");
b.Property<string>("Path")
.HasColumnType("TEXT");
b.Property<string>("RemoteId")
.HasColumnType("TEXT");
b.Property<int>("RetryCount")
.HasColumnType("INTEGER");
b.Property<Guid>("TorrentId")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("UnpackingFinished")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("UnpackingQueued")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("UnpackingStarted")
.HasColumnType("TEXT");
b.HasKey("DownloadId");
b.HasIndex("TorrentId");
b.ToTable("Downloads");
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Setting", b =>
{
b.Property<string>("SettingId")
.HasColumnType("TEXT");
b.Property<string>("Type")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("SettingId");
b.ToTable("Settings");
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
{
b.Property<Guid>("TorrentId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("Added")
.HasColumnType("TEXT");
b.Property<string>("Category")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("Completed")
.HasColumnType("TEXT");
b.Property<int>("DownloadAction")
.HasColumnType("INTEGER");
b.Property<string>("DownloadManualFiles")
.HasColumnType("TEXT");
b.Property<int>("DownloadMinSize")
.HasColumnType("INTEGER");
b.Property<string>("Error")
.HasColumnType("TEXT");
b.Property<string>("FileOrMagnet")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("FilesSelected")
.HasColumnType("TEXT");
b.Property<int>("FinishedAction")
.HasColumnType("INTEGER");
b.Property<string>("Hash")
.HasColumnType("TEXT");
b.Property<bool>("IsFile")
.HasColumnType("INTEGER");
b.Property<int?>("Priority")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("RdAdded")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("RdEnded")
.HasColumnType("TEXT");
b.Property<string>("RdFiles")
.HasColumnType("TEXT");
b.Property<string>("RdHost")
.HasColumnType("TEXT");
b.Property<string>("RdId")
.HasColumnType("TEXT");
b.Property<string>("RdName")
.HasColumnType("TEXT");
b.Property<long>("RdProgress")
.HasColumnType("INTEGER");
b.Property<long?>("RdSeeders")
.HasColumnType("INTEGER");
b.Property<long>("RdSize")
.HasColumnType("INTEGER");
b.Property<long?>("RdSpeed")
.HasColumnType("INTEGER");
b.Property<long>("RdSplit")
.HasColumnType("INTEGER");
b.Property<int>("RdStatus")
.HasColumnType("INTEGER");
b.Property<string>("RdStatusRaw")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("Retry")
.HasColumnType("TEXT");
b.Property<int>("RetryCount")
.HasColumnType("INTEGER");
b.HasKey("TorrentId");
b.ToTable("Torrents");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b =>
{
b.HasOne("RdtClient.Data.Models.Data.Torrent", "Torrent")
.WithMany("Downloads")
.HasForeignKey("TorrentId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Torrent");
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
{
b.Navigation("Downloads");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace RdtClient.Data.Migrations
{
public partial class Torrents_Error : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Error",
table: "Torrents",
type: "TEXT",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Error",
table: "Torrents");
}
}
}

View file

@ -0,0 +1,454 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using RdtClient.Data.Data;
namespace RdtClient.Data.Migrations
{
[DbContext(typeof(DataContext))]
[Migration("20211029142739_Torrents_Add_RetryAttempts")]
partial class Torrents_Add_RetryAttempts
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "5.0.11");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b =>
{
b.Property<Guid>("DownloadId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("Added")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("Completed")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("DownloadFinished")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("DownloadQueued")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("DownloadStarted")
.HasColumnType("TEXT");
b.Property<string>("Error")
.HasColumnType("TEXT");
b.Property<string>("Link")
.HasColumnType("TEXT");
b.Property<string>("Path")
.HasColumnType("TEXT");
b.Property<string>("RemoteId")
.HasColumnType("TEXT");
b.Property<int>("RetryCount")
.HasColumnType("INTEGER");
b.Property<Guid>("TorrentId")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("UnpackingFinished")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("UnpackingQueued")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("UnpackingStarted")
.HasColumnType("TEXT");
b.HasKey("DownloadId");
b.HasIndex("TorrentId");
b.ToTable("Downloads");
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Setting", b =>
{
b.Property<string>("SettingId")
.HasColumnType("TEXT");
b.Property<string>("Type")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("SettingId");
b.ToTable("Settings");
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
{
b.Property<Guid>("TorrentId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("Added")
.HasColumnType("TEXT");
b.Property<string>("Category")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("Completed")
.HasColumnType("TEXT");
b.Property<int>("DownloadAction")
.HasColumnType("INTEGER");
b.Property<string>("DownloadManualFiles")
.HasColumnType("TEXT");
b.Property<int>("DownloadMinSize")
.HasColumnType("INTEGER");
b.Property<int>("DownloadRetryAttempts")
.HasColumnType("INTEGER");
b.Property<string>("Error")
.HasColumnType("TEXT");
b.Property<string>("FileOrMagnet")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("FilesSelected")
.HasColumnType("TEXT");
b.Property<int>("FinishedAction")
.HasColumnType("INTEGER");
b.Property<string>("Hash")
.HasColumnType("TEXT");
b.Property<bool>("IsFile")
.HasColumnType("INTEGER");
b.Property<int?>("Priority")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("RdAdded")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("RdEnded")
.HasColumnType("TEXT");
b.Property<string>("RdFiles")
.HasColumnType("TEXT");
b.Property<string>("RdHost")
.HasColumnType("TEXT");
b.Property<string>("RdId")
.HasColumnType("TEXT");
b.Property<string>("RdName")
.HasColumnType("TEXT");
b.Property<long>("RdProgress")
.HasColumnType("INTEGER");
b.Property<long?>("RdSeeders")
.HasColumnType("INTEGER");
b.Property<long>("RdSize")
.HasColumnType("INTEGER");
b.Property<long?>("RdSpeed")
.HasColumnType("INTEGER");
b.Property<long>("RdSplit")
.HasColumnType("INTEGER");
b.Property<int>("RdStatus")
.HasColumnType("INTEGER");
b.Property<string>("RdStatusRaw")
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("Retry")
.HasColumnType("TEXT");
b.Property<int>("RetryCount")
.HasColumnType("INTEGER");
b.Property<int>("TorrentRetryAttempts")
.HasColumnType("INTEGER");
b.HasKey("TorrentId");
b.ToTable("Torrents");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b =>
{
b.HasOne("RdtClient.Data.Models.Data.Torrent", "Torrent")
.WithMany("Downloads")
.HasForeignKey("TorrentId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Torrent");
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
{
b.Navigation("Downloads");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace RdtClient.Data.Migrations
{
public partial class Torrents_Add_RetryAttempts : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "DownloadRetryAttempts",
table: "Torrents",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "TorrentRetryAttempts",
table: "Torrents",
type: "INTEGER",
nullable: false,
defaultValue: 0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DownloadRetryAttempts",
table: "Torrents");
migrationBuilder.DropColumn(
name: "TorrentRetryAttempts",
table: "Torrents");
}
}
}

View file

@ -14,7 +14,7 @@ namespace RdtClient.Data.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "5.0.10"); .HasAnnotation("ProductVersion", "5.0.11");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{ {
@ -303,6 +303,12 @@ namespace RdtClient.Data.Migrations
b.Property<int>("DownloadMinSize") b.Property<int>("DownloadMinSize")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int>("DownloadRetryAttempts")
.HasColumnType("INTEGER");
b.Property<string>("Error")
.HasColumnType("TEXT");
b.Property<string>("FileOrMagnet") b.Property<string>("FileOrMagnet")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
@ -360,9 +366,15 @@ namespace RdtClient.Data.Migrations
b.Property<string>("RdStatusRaw") b.Property<string>("RdStatusRaw")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<DateTimeOffset?>("Retry")
.HasColumnType("TEXT");
b.Property<int>("RetryCount") b.Property<int>("RetryCount")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int>("TorrentRetryAttempts")
.HasColumnType("INTEGER");
b.HasKey("TorrentId"); b.HasKey("TorrentId");
b.ToTable("Torrents"); b.ToTable("Torrents");

View file

@ -11,24 +11,19 @@ namespace RdtClient.Data.Models.Data
public Guid TorrentId { get; set; } public Guid TorrentId { get; set; }
public String Path { get; set; } [ForeignKey("TorrentId")]
public Torrent Torrent { get; set; }
public String Path { get; set; }
public String Link { get; set; } public String Link { get; set; }
public DateTimeOffset Added { get; set; } public DateTimeOffset Added { get; set; }
public DateTimeOffset? DownloadQueued { get; set; } public DateTimeOffset? DownloadQueued { get; set; }
public DateTimeOffset? DownloadStarted { get; set; } public DateTimeOffset? DownloadStarted { get; set; }
public DateTimeOffset? DownloadFinished { get; set; } public DateTimeOffset? DownloadFinished { get; set; }
public DateTimeOffset? UnpackingQueued { get; set; } public DateTimeOffset? UnpackingQueued { get; set; }
public DateTimeOffset? UnpackingStarted { get; set; } public DateTimeOffset? UnpackingStarted { get; set; }
public DateTimeOffset? UnpackingFinished { get; set; } public DateTimeOffset? UnpackingFinished { get; set; }
public DateTimeOffset? Completed { get; set; } public DateTimeOffset? Completed { get; set; }
public Int32 RetryCount { get; set; } public Int32 RetryCount { get; set; }
@ -37,9 +32,6 @@ namespace RdtClient.Data.Models.Data
public String RemoteId { get; set; } public String RemoteId { get; set; }
[ForeignKey("TorrentId")]
public Torrent Torrent { get; set; }
[NotMapped] [NotMapped]
public Int64 BytesTotal { get; set; } public Int64 BytesTotal { get; set; }

View file

@ -3,8 +3,8 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json; using Newtonsoft.Json;
using RDNET;
using RdtClient.Data.Enums; using RdtClient.Data.Enums;
using RdtClient.Data.Models.TorrentClient;
namespace RdtClient.Data.Models.Data namespace RdtClient.Data.Models.Data
{ {
@ -25,12 +25,17 @@ namespace RdtClient.Data.Models.Data
public DateTimeOffset Added { get; set; } public DateTimeOffset Added { get; set; }
public DateTimeOffset? FilesSelected { get; set; } public DateTimeOffset? FilesSelected { get; set; }
public DateTimeOffset? Completed { 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 Boolean IsFile { get; set; }
public Int32? Priority { get; set; } public Int32? Priority { get; set; }
public Int32 RetryCount { get; set; } public Int32 RetryCount { get; set; }
public Int32 DownloadRetryAttempts { get; set; }
public Int32 TorrentRetryAttempts { get; set; }
public String Error { get; set; }
[InverseProperty("Torrent")] [InverseProperty("Torrent")]
public IList<Download> Downloads { get; set; } public IList<Download> Downloads { get; set; }
@ -50,22 +55,22 @@ namespace RdtClient.Data.Models.Data
public String RdFiles { get; set; } public String RdFiles { get; set; }
[NotMapped] [NotMapped]
public IList<TorrentFile> Files public IList<TorrentClientFile> Files
{ {
get get
{ {
if (String.IsNullOrWhiteSpace(RdFiles)) if (String.IsNullOrWhiteSpace(RdFiles))
{ {
return new List<TorrentFile>(); return new List<TorrentClientFile>();
} }
try try
{ {
return JsonConvert.DeserializeObject<List<TorrentFile>>(RdFiles); return JsonConvert.DeserializeObject<List<TorrentClientFile>>(RdFiles);
} }
catch catch
{ {
return new List<TorrentFile>(); return new List<TorrentClientFile>();
} }
} }
} }

View file

@ -20,5 +20,7 @@ namespace RdtClient.Data.Models.Internal
public String Categories { get; set; } public String Categories { get; set; }
public String Aria2cUrl { get; set; } public String Aria2cUrl { get; set; }
public String Aria2cSecret { get; set; } public String Aria2cSecret { get; set; }
public Int32 DownloadRetryAttempts { get; set; }
public Int32 TorrentRetryAttempts { get; set; }
} }
} }

View file

@ -1,6 +1,6 @@
using System; using System;
namespace RdtClient.Service.Models namespace RdtClient.Data.Models.Internal
{ {
public class Profile public class Profile
{ {

View file

@ -1,7 +1,7 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace RdtClient.Service.Models.QBittorrent namespace RdtClient.Data.Models.QBittorrent
{ {
public class AppBuildInfo public class AppBuildInfo
{ {

View file

@ -1,7 +1,7 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace RdtClient.Service.Models.QBittorrent namespace RdtClient.Data.Models.QBittorrent
{ {
namespace QuickType namespace QuickType
{ {

View file

@ -1,7 +1,7 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace RdtClient.Service.Models.QBittorrent namespace RdtClient.Data.Models.QBittorrent
{ {
public class TorrentCategory public class TorrentCategory
{ {

View file

@ -1,7 +1,7 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace RdtClient.Service.Models.QBittorrent namespace RdtClient.Data.Models.QBittorrent
{ {
public class TorrentFileItem public class TorrentFileItem
{ {

View file

@ -1,7 +1,7 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace RdtClient.Service.Models.QBittorrent namespace RdtClient.Data.Models.QBittorrent
{ {
namespace QuickType namespace QuickType
{ {

View file

@ -1,7 +1,7 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace RdtClient.Service.Models.QBittorrent namespace RdtClient.Data.Models.QBittorrent
{ {
public class TorrentProperties public class TorrentProperties
{ {

View file

@ -1,6 +1,6 @@
using System; using System;
namespace RdtClient.Service.Models.TorrentClient namespace RdtClient.Data.Models.TorrentClient
{ {
public class TorrentClientAvailableFile public class TorrentClientAvailableFile
{ {

View file

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

View file

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace RdtClient.Service.Models.TorrentClient namespace RdtClient.Data.Models.TorrentClient
{ {
public class TorrentClientTorrent public class TorrentClientTorrent
{ {
@ -16,18 +16,10 @@ namespace RdtClient.Service.Models.TorrentClient
public Int64 Progress { get; set; } public Int64 Progress { get; set; }
public String Status { get; set; } public String Status { get; set; }
public DateTimeOffset Added { get; set; } public DateTimeOffset Added { get; set; }
public List<TorrentClientTorrentFile> Files { get; set; } public List<TorrentClientFile> Files { get; set; }
public List<String> Links { get; set; } public List<String> Links { get; set; }
public DateTimeOffset? Ended { get; set; } public DateTimeOffset? Ended { get; set; }
public Int64? Speed { get; set; } public Int64? Speed { get; set; }
public Int64? Seeders { get; set; } public Int64? Seeders { get; set; }
} }
public class TorrentClientTorrentFile
{
public Int64 Id { get; set; }
public String Path { get; set; }
public Int64 Bytes { get; set; }
public Boolean Selected { get; set; }
}
} }

View file

@ -1,6 +1,6 @@
using System; using System;
namespace RdtClient.Service.Models.TorrentClient namespace RdtClient.Data.Models.TorrentClient
{ {
public class TorrentClientUser public class TorrentClientUser
{ {

View file

@ -14,14 +14,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="RD.NET" Version="2.0.0" />
<PackageReference Include="Serilog" Version="2.10.0" /> <PackageReference Include="Serilog" Version="2.10.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Reference Include="RDNET">
<HintPath>..\libs\RDNET.dll</HintPath>
</Reference>
</ItemGroup>
</Project> </Project>

View file

@ -6,9 +6,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AllDebrid.NET" Version="1.0.1" />
<PackageReference Include="Aria2.NET" Version="1.0.4" /> <PackageReference Include="Aria2.NET" Version="1.0.4" />
<PackageReference Include="Downloader" Version="2.2.9" /> <PackageReference Include="Downloader" Version="2.2.9" />
<PackageReference Include="MonoTorrent" Version="2.0.0" /> <PackageReference Include="MonoTorrent" Version="2.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="RD.NET" Version="2.1.0" /> <PackageReference Include="RD.NET" Version="2.1.0" />
<PackageReference Include="Serilog" Version="2.10.0" /> <PackageReference Include="Serilog" Version="2.10.0" />

View file

@ -4,8 +4,9 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using RdtClient.Data.Enums; using RdtClient.Data.Enums;
using RdtClient.Service.Models.QBittorrent; using RdtClient.Data.Models.Data;
using RdtClient.Service.Models.QBittorrent.QuickType; using RdtClient.Data.Models.QBittorrent;
using RdtClient.Data.Models.QBittorrent.QuickType;
namespace RdtClient.Service.Services namespace RdtClient.Service.Services
{ {
@ -293,7 +294,15 @@ namespace RdtClient.Service.Services
Upspeed = speed Upspeed = speed
}; };
if (torrent.Completed.HasValue) if (torrent.Retry != null)
{
result.State = "downloading";
}
else if (!String.IsNullOrWhiteSpace(torrent.Error))
{
result.State = torrent.Error;
}
else if (torrent.Completed.HasValue)
{ {
var allDownloadsComplete = torrent.Downloads.All(m => m.Completed.HasValue); var allDownloadsComplete = torrent.Downloads.All(m => m.Completed.HasValue);
var hasDownloadsWithErrors = torrent.Downloads.Any(m => m.Error != null); var hasDownloadsWithErrors = torrent.Downloads.Any(m => m.Error != null);
@ -419,16 +428,34 @@ namespace RdtClient.Service.Services
public async Task TorrentsAddMagnet(String magnetLink, String category, Int32? priority) public async Task TorrentsAddMagnet(String magnetLink, String category, Int32? priority)
{ {
var downloadAction = Settings.Get.OnlyDownloadAvailableFiles == 1 ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll; var torrent = new Torrent
{
Category = category,
DownloadAction = Settings.Get.OnlyDownloadAvailableFiles == 1 ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
FinishedAction = TorrentFinishedAction.None,
DownloadMinSize = Settings.Get.MinFileSize,
TorrentRetryAttempts = Settings.Get.TorrentRetryAttempts,
DownloadRetryAttempts = Settings.Get.DownloadRetryAttempts,
Priority = priority
};
await _torrents.UploadMagnet(magnetLink, category, downloadAction, TorrentFinishedAction.None, Settings.Get.MinFileSize, null, priority); 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 downloadAction = Settings.Get.OnlyDownloadAvailableFiles == 1 ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll; var torrent = new Torrent
{
Category = category,
DownloadAction = Settings.Get.OnlyDownloadAvailableFiles == 1 ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
FinishedAction = TorrentFinishedAction.None,
DownloadMinSize = Settings.Get.MinFileSize,
TorrentRetryAttempts = Settings.Get.TorrentRetryAttempts,
DownloadRetryAttempts = Settings.Get.DownloadRetryAttempts,
Priority = priority
};
await _torrents.UploadFile(fileBytes, category, downloadAction, TorrentFinishedAction.None, Settings.Get.MinFileSize, null, priority); await _torrents.UploadFile(fileBytes, torrent);
} }
public async Task TorrentsSetCategory(String hash, String category) public async Task TorrentsSetCategory(String hash, String category)

View file

@ -3,8 +3,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using RDNET; using AllDebridNET;
using RdtClient.Service.Models.TorrentClient; using RdtClient.Data.Models.TorrentClient;
namespace RdtClient.Service.Services.TorrentClients namespace RdtClient.Service.Services.TorrentClients
{ {
@ -17,139 +17,110 @@ namespace RdtClient.Service.Services.TorrentClients
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
} }
private RdNetClient GetRdNetClient() private AllDebridNETClient GetClient()
{ {
var apiKey = Settings.Get.RealDebridApiKey; var apiKey = Settings.Get.RealDebridApiKey;
if (String.IsNullOrWhiteSpace(apiKey)) if (String.IsNullOrWhiteSpace(apiKey))
{ {
throw new Exception("Real-Debrid API Key not set in the settings"); throw new Exception("All-Debrid API Key not set in the settings");
} }
var httpClient = _httpClientFactory.CreateClient(); var httpClient = _httpClientFactory.CreateClient();
httpClient.Timeout = TimeSpan.FromSeconds(10); httpClient.Timeout = TimeSpan.FromSeconds(10);
var rdtNetClient = new RdNetClient(null, httpClient, 5); var allDebridNetClient = new AllDebridNETClient("RealDebridClient", apiKey);
rdtNetClient.UseApiAuthentication(apiKey);
return rdtNetClient; return allDebridNetClient;
} }
private static TorrentClientTorrent Map(Torrent torrent) private static TorrentClientTorrent Map(Magnet torrent)
{ {
return new TorrentClientTorrent return new TorrentClientTorrent
{ {
Id = torrent.Id, Id = torrent.Id.ToString(),
Filename = torrent.Filename, Filename = torrent.Filename,
OriginalFilename = torrent.OriginalFilename, OriginalFilename = torrent.Filename,
Hash = torrent.Hash, Hash = torrent.Hash,
Bytes = torrent.Bytes, Bytes = torrent.Size,
OriginalBytes = torrent.OriginalBytes, OriginalBytes = torrent.Size,
Host = torrent.Host, Host = null,
Split = torrent.Split, Split = 0,
Progress = torrent.Progress, Progress = torrent.ProcessingPerc,
Status = torrent.Status, Status = torrent.Status,
Added = torrent.Added, Added = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(torrent.UploadDate),
Files = (torrent.Files ?? new List<TorrentFile>()).Select(m => new TorrentClientTorrentFile Files = (torrent.Links ?? new List<Link>()).Select(m => new TorrentClientFile
{ {
Path = m.Path, Path = m.Filename,
Bytes = m.Bytes, Bytes = m.Size,
Id = m.Id, Id = 0,
Selected = m.Selected Selected = true
}).ToList(), }).ToList(),
Links = torrent.Links, Links = (torrent.Links ?? new List<Link>()).Select(m => m.LinkUrl.ToString()).ToList(),
Ended = torrent.Ended, Ended = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(torrent.CompletionDate),
Speed = torrent.Speed, Speed = torrent.DownloadSpeed,
Seeders = torrent.Seeders, Seeders = torrent.Seeders
}; };
} }
public async Task<IList<TorrentClientTorrent>> GetTorrents() public async Task<IList<TorrentClientTorrent>> GetTorrents()
{ {
var page = 0; var results = await GetClient().Magnet.StatusAllAsync();
var results = new List<Torrent>();
while (true)
{
var pagedResults = await GetRdNetClient().Torrents.GetAsync(page, 100);
results.AddRange(pagedResults);
if (pagedResults.Count == 0)
{
break;
}
page += 100;
}
return results.Select(Map).ToList(); return results.Select(Map).ToList();
} }
public async Task<TorrentClientUser> GetUser() public async Task<TorrentClientUser> GetUser()
{ {
var user = await GetRdNetClient().User.GetAsync(); var user = await GetClient().User.GetAsync();
return new TorrentClientUser return new TorrentClientUser
{ {
Username = user.Username, Username = user.Username,
Expiration = user.Expiration Expiration = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(user.PremiumUntil)
}; };
} }
public async Task<String> AddMagnet(String magnetLink) public async Task<String> AddMagnet(String magnetLink)
{ {
var result = await GetRdNetClient().Torrents.AddMagnetAsync(magnetLink); var result = await GetClient().Magnet.UploadMagnetAsync(magnetLink);
return result.Id; return result.Id.ToString();
} }
public async Task<String> AddFile(Byte[] bytes) public async Task<String> AddFile(Byte[] bytes)
{ {
var result = await GetRdNetClient().Torrents.AddFileAsync(bytes); var result = await GetClient().Magnet.UploadFileAsync(bytes);
return result.Id; return result.Id.ToString();
} }
public async Task<List<TorrentClientAvailableFile>> GetAvailableFiles(String hash) public Task<List<TorrentClientAvailableFile>> GetAvailableFiles(String hash)
{ {
var result = await GetRdNetClient().Torrents.GetAvailableFiles(hash); return Task.FromResult(new List<TorrentClientAvailableFile>());
var files = result.SelectMany(m => m.Value).SelectMany(m => m.Value).SelectMany(m => m.Values);
var groups = files.GroupBy(m => $"{m.Filename}-{m.Filesize}");
var torrentClientAvailableFiles = groups.Select(m => new TorrentClientAvailableFile
{
Filename = m.First().Filename,
Filesize = m.First().Filesize
} ).ToList();
return torrentClientAvailableFiles;
} }
public async Task SelectFiles(String torrentId, IList<String> fileIds) public Task SelectFiles(String torrentId, IList<String> fileIds)
{ {
await GetRdNetClient().Torrents.SelectFilesAsync(torrentId, fileIds.ToArray()); return Task.CompletedTask;
} }
public async Task<TorrentClientTorrent> GetInfo(String torrentId) public async Task<TorrentClientTorrent> GetInfo(String torrentId)
{ {
var result = await GetRdNetClient().Torrents.GetInfoAsync(torrentId); var result = await GetClient().Magnet.StatusAsync(torrentId);
return Map(result); return Map(result);
} }
public async Task Delete(String torrentId) public async Task Delete(String torrentId)
{ {
await GetRdNetClient().Torrents.DeleteAsync(torrentId); await GetClient().Magnet.DeleteAsync(torrentId);
} }
public async Task<String> Unrestrict(String link) public async Task<String> Unrestrict(String link)
{ {
var result = await GetRdNetClient().Unrestrict.LinkAsync(link); var result = await GetClient().Links.DownloadLinkAsync(link);
return result.Download; return result.Link;
} }
} }
} }

View file

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using RdtClient.Service.Models.TorrentClient; using RdtClient.Data.Models.TorrentClient;
namespace RdtClient.Service.Services.TorrentClients namespace RdtClient.Service.Services.TorrentClients
{ {

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using RDNET; using RDNET;
using RdtClient.Service.Models.TorrentClient; using RdtClient.Data.Models.TorrentClient;
namespace RdtClient.Service.Services.TorrentClients namespace RdtClient.Service.Services.TorrentClients
{ {
@ -17,7 +17,7 @@ namespace RdtClient.Service.Services.TorrentClients
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
} }
private RdNetClient GetRdNetClient() private RdNetClient GetClient()
{ {
var apiKey = Settings.Get.RealDebridApiKey; var apiKey = Settings.Get.RealDebridApiKey;
@ -50,7 +50,7 @@ namespace RdtClient.Service.Services.TorrentClients
Progress = torrent.Progress, Progress = torrent.Progress,
Status = torrent.Status, Status = torrent.Status,
Added = torrent.Added, Added = torrent.Added,
Files = (torrent.Files ?? new List<TorrentFile>()).Select(m => new TorrentClientTorrentFile Files = (torrent.Files ?? new List<TorrentFile>()).Select(m => new TorrentClientFile
{ {
Path = m.Path, Path = m.Path,
Bytes = m.Bytes, Bytes = m.Bytes,
@ -71,7 +71,7 @@ namespace RdtClient.Service.Services.TorrentClients
while (true) while (true)
{ {
var pagedResults = await GetRdNetClient().Torrents.GetAsync(page, 100); var pagedResults = await GetClient().Torrents.GetAsync(page, 100);
results.AddRange(pagedResults); results.AddRange(pagedResults);
@ -88,7 +88,7 @@ namespace RdtClient.Service.Services.TorrentClients
public async Task<TorrentClientUser> GetUser() public async Task<TorrentClientUser> GetUser()
{ {
var user = await GetRdNetClient().User.GetAsync(); var user = await GetClient().User.GetAsync();
return new TorrentClientUser return new TorrentClientUser
{ {
@ -99,21 +99,21 @@ namespace RdtClient.Service.Services.TorrentClients
public async Task<String> AddMagnet(String magnetLink) public async Task<String> AddMagnet(String magnetLink)
{ {
var result = await GetRdNetClient().Torrents.AddMagnetAsync(magnetLink); var result = await GetClient().Torrents.AddMagnetAsync(magnetLink);
return result.Id; return result.Id;
} }
public async Task<String> AddFile(Byte[] bytes) public async Task<String> AddFile(Byte[] bytes)
{ {
var result = await GetRdNetClient().Torrents.AddFileAsync(bytes); var result = await GetClient().Torrents.AddFileAsync(bytes);
return result.Id; return result.Id;
} }
public async Task<List<TorrentClientAvailableFile>> GetAvailableFiles(String hash) public async Task<List<TorrentClientAvailableFile>> GetAvailableFiles(String hash)
{ {
var result = await GetRdNetClient().Torrents.GetAvailableFiles(hash); var result = await GetClient().Torrents.GetAvailableFiles(hash);
var files = result.SelectMany(m => m.Value).SelectMany(m => m.Value).SelectMany(m => m.Values); var files = result.SelectMany(m => m.Value).SelectMany(m => m.Value).SelectMany(m => m.Values);
@ -130,24 +130,24 @@ namespace RdtClient.Service.Services.TorrentClients
public async Task SelectFiles(String torrentId, IList<String> fileIds) public async Task SelectFiles(String torrentId, IList<String> fileIds)
{ {
await GetRdNetClient().Torrents.SelectFilesAsync(torrentId, fileIds.ToArray()); await GetClient().Torrents.SelectFilesAsync(torrentId, fileIds.ToArray());
} }
public async Task<TorrentClientTorrent> GetInfo(String torrentId) public async Task<TorrentClientTorrent> GetInfo(String torrentId)
{ {
var result = await GetRdNetClient().Torrents.GetInfoAsync(torrentId); var result = await GetClient().Torrents.GetInfoAsync(torrentId);
return Map(result); return Map(result);
} }
public async Task Delete(String torrentId) public async Task Delete(String torrentId)
{ {
await GetRdNetClient().Torrents.DeleteAsync(torrentId); await GetClient().Torrents.DeleteAsync(torrentId);
} }
public async Task<String> Unrestrict(String link) public async Task<String> Unrestrict(String link)
{ {
var result = await GetRdNetClient().Unrestrict.LinkAsync(link); var result = await GetClient().Unrestrict.LinkAsync(link);
return result.Download; return result.Download;
} }

View file

@ -4,11 +4,9 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web; using System.Web;
using Aria2NET; using Aria2NET;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using RdtClient.Data.Enums; using RdtClient.Data.Enums;
@ -21,27 +19,20 @@ namespace RdtClient.Service.Services
{ {
public class TorrentRunner public class TorrentRunner
{ {
private const Int32 DownloadRetryCount = 3;
private const Int32 TorrentRetryCount = 2;
private static DateTime _nextUpdate = DateTime.UtcNow; private static DateTime _nextUpdate = DateTime.UtcNow;
public static readonly ConcurrentDictionary<Guid, DownloadClient> ActiveDownloadClients = new(); public static readonly ConcurrentDictionary<Guid, DownloadClient> ActiveDownloadClients = new();
public static readonly ConcurrentDictionary<Guid, UnpackClient> ActiveUnpackClients = new(); public static readonly ConcurrentDictionary<Guid, UnpackClient> ActiveUnpackClients = new();
public static readonly SemaphoreSlim TorrentResetLock = new(1, 1);
private readonly ILogger<TorrentRunner> _logger; private readonly ILogger<TorrentRunner> _logger;
private readonly IServiceProvider _serviceProvider;
private readonly Torrents _torrents; private readonly Torrents _torrents;
private readonly Downloads _downloads; private readonly Downloads _downloads;
private readonly RemoteService _remoteService; private readonly RemoteService _remoteService;
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
public TorrentRunner(ILogger<TorrentRunner> logger, IServiceProvider serviceProvider, Torrents torrents, Downloads downloads, RemoteService remoteService) public TorrentRunner(ILogger<TorrentRunner> logger, Torrents torrents, Downloads downloads, RemoteService remoteService)
{ {
_logger = logger; _logger = logger;
_serviceProvider = serviceProvider;
_torrents = torrents; _torrents = torrents;
_downloads = downloads; _downloads = downloads;
_remoteService = remoteService; _remoteService = remoteService;
@ -174,38 +165,22 @@ namespace RdtClient.Service.Services
Log("Processing download", download, download.Torrent); Log("Processing download", download, download.Torrent);
if (downloadClient.Error != null) if (!String.IsNullOrWhiteSpace(downloadClient.Error))
{ {
// Retry the download if an error is encountered. // Retry the download if an error is encountered.
Log($"Download reported an error: {downloadClient.Error}", download, download.Torrent); Log($"Download reported an error: {downloadClient.Error}", download, download.Torrent);
Log($"Download retry count {download.RetryCount}/{DownloadRetryCount}, torrent retry count {download.Torrent.RetryCount}/{TorrentRetryCount}", 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 < DownloadRetryCount) if (download.RetryCount < download.Torrent.DownloadRetryAttempts)
{ {
Log($"Retrying download", download, download.Torrent); Log($"Retrying download", download, download.Torrent);
await _downloads.Reset(downloadId); await _downloads.Reset(downloadId);
await _downloads.UpdateRetryCount(downloadId, download.RetryCount + 1); await _downloads.UpdateRetryCount(downloadId, download.RetryCount + 1);
} }
else if (download.Torrent.RetryCount < TorrentRetryCount)
{
Log($"Retrying torrent", download, download.Torrent);
_ = Task.Run(async () =>
{
using var blockScope = _serviceProvider.CreateScope();
var torrentsService = (Torrents) blockScope.ServiceProvider.GetService(typeof(Torrents));
await torrentsService.RetryTorrent(download.TorrentId, download.Torrent.RetryCount + 1);
});
// Force a delay and return the main loop to prevent concurrency issues.
await Task.Delay(10000);
return;
}
else else
{ {
Log($"Not retrying", download, download.Torrent); Log($"Not retrying download", download, download.Torrent);
await _downloads.UpdateError(downloadId, downloadClient.Error); await _downloads.UpdateError(downloadId, downloadClient.Error);
await _downloads.UpdateCompleted(downloadId, DateTimeOffset.UtcNow); await _downloads.UpdateCompleted(downloadId, DateTimeOffset.UtcNow);
@ -268,6 +243,29 @@ namespace RdtClient.Service.Services
var torrents = await _torrents.Get(); var torrents = await _torrents.Get();
// Process torrent retries
foreach (var torrent in torrents.Where(m => m.Retry != null))
{
try
{
_logger.LogDebug($"Retrying torrent {torrent.RetryCount}/{torrent.TorrentRetryAttempts}", torrent);
if (torrent.RetryCount > torrent.TorrentRetryAttempts)
{
await _torrents.UpdateRetry(torrent.TorrentId, null, torrent.RetryCount);
_logger.LogDebug($"Torrent reach max retry count", torrent);
continue;
}
await _torrents.RetryTorrent(torrent.TorrentId, torrent.RetryCount);
}
catch (Exception ex)
{
await _torrents.UpdateRetry(torrent.TorrentId, null, torrent.RetryCount);
await _torrents.UpdateError(torrent.TorrentId, ex.Message);
}
}
torrents = torrents.Where(m => m.Completed == null).ToList(); torrents = torrents.Where(m => m.Completed == null).ToList();
// Only poll Real-Debrid every second when a hub is connected, otherwise every 30 seconds // Only poll Real-Debrid every second when a hub is connected, otherwise every 30 seconds
@ -308,305 +306,301 @@ namespace RdtClient.Service.Services
foreach (var torrent in torrents) foreach (var torrent in torrents)
{ {
// Check if there are any downloads that are queued and can be started. try
var queuedDownloads = torrent.Downloads
.Where(m => m.Completed == null && m.DownloadQueued != null && m.DownloadStarted == null && m.Error == null)
.OrderBy(m => m.DownloadQueued)
.ToList();
foreach (var download in queuedDownloads)
{ {
Log($"Processing to download", download, torrent); // Check if there are any downloads that are queued and can be started.
var queuedDownloads = torrent.Downloads
.Where(m => m.Completed == null && m.DownloadQueued != null && m.DownloadStarted == null && m.Error == null)
.OrderBy(m => m.DownloadQueued)
.ToList();
if (ActiveDownloadClients.Count >= settingDownloadLimit) foreach (var download in queuedDownloads)
{ {
Log($"Not starting download because there are already the max number of downloads active", download, torrent); Log($"Processing to download", download, torrent);
continue; if (ActiveDownloadClients.Count >= settingDownloadLimit)
}
if (ActiveDownloadClients.ContainsKey(download.DownloadId))
{
Log($"Not starting download because this download is already active", download, torrent);
continue;
}
try
{
Log($"Unrestricting links", download, torrent);
var downloadLink = await _torrents.UnrestrictLink(download.DownloadId);
download.Link = downloadLink;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Cannot unrestrict link: {ex.Message}");
await _downloads.UpdateError(download.DownloadId, ex.Message);
await _downloads.UpdateCompleted(download.DownloadId, DateTimeOffset.UtcNow);
download.Error = ex.Message;
download.Completed = DateTimeOffset.UtcNow;
continue;
}
Log($"Marking download as started", download, torrent);
download.DownloadStarted = DateTime.UtcNow;
await _downloads.UpdateDownloadStarted(download.DownloadId, download.DownloadStarted);
var downloadPath = settingDownloadPath;
if (!String.IsNullOrWhiteSpace(torrent.Category))
{
downloadPath = Path.Combine(downloadPath, torrent.Category);
}
Log($"Setting download path to {downloadPath}", download, torrent);
// Start the download process
var downloadClient = new DownloadClient(download, torrent, downloadPath);
if (ActiveDownloadClients.TryAdd(download.DownloadId, downloadClient))
{
Log($"Starting download", download, torrent);
var remoteId = await downloadClient.Start(Settings.Get);
if (!String.IsNullOrWhiteSpace(remoteId) && download.RemoteId != remoteId)
{ {
Log($"Received ID {remoteId}", download, torrent); Log($"Not starting download because there are already the max number of downloads active", download, torrent);
await _downloads.UpdateRemoteId(download.DownloadId, remoteId); continue;
} }
else
if (ActiveDownloadClients.ContainsKey(download.DownloadId))
{ {
Log($"No ID received", download, torrent); Log($"Not starting download because this download is already active", download, torrent);
continue;
}
try
{
Log($"Unrestricting links", download, torrent);
var downloadLink = await _torrents.UnrestrictLink(download.DownloadId);
download.Link = downloadLink;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Cannot unrestrict link: {ex.Message}");
await _downloads.UpdateError(download.DownloadId, ex.Message);
await _downloads.UpdateCompleted(download.DownloadId, DateTimeOffset.UtcNow);
download.Error = ex.Message;
download.Completed = DateTimeOffset.UtcNow;
continue;
}
Log($"Marking download as started", download, torrent);
download.DownloadStarted = DateTime.UtcNow;
await _downloads.UpdateDownloadStarted(download.DownloadId, download.DownloadStarted);
var downloadPath = settingDownloadPath;
if (!String.IsNullOrWhiteSpace(torrent.Category))
{
downloadPath = Path.Combine(downloadPath, torrent.Category);
}
Log($"Setting download path to {downloadPath}", download, torrent);
// Start the download process
var downloadClient = new DownloadClient(download, torrent, downloadPath);
if (ActiveDownloadClients.TryAdd(download.DownloadId, downloadClient))
{
Log($"Starting download", download, torrent);
var remoteId = await downloadClient.Start(Settings.Get);
if (!String.IsNullOrWhiteSpace(remoteId) && download.RemoteId != remoteId)
{
Log($"Received ID {remoteId}", download, torrent);
await _downloads.UpdateRemoteId(download.DownloadId, remoteId);
}
else
{
Log($"No ID received", download, torrent);
}
} }
} }
}
// Check if there are any unpacks that are queued and can be started. // Check if there are any unpacks that are queued and can be started.
var queuedUnpacks = torrent.Downloads var queuedUnpacks = torrent.Downloads
.Where(m => m.Completed == null && m.UnpackingQueued != null && m.UnpackingStarted == null && m.Error == null) .Where(m => m.Completed == null && m.UnpackingQueued != null && m.UnpackingStarted == null && m.Error == null)
.OrderBy(m => m.DownloadQueued) .OrderBy(m => m.DownloadQueued)
.ToList(); .ToList();
foreach (var download in queuedUnpacks) foreach (var download in queuedUnpacks)
{
Log($"Starting unpack", download, torrent);
if (download.Link == null)
{
Log($"No download link found", download, torrent);
await _downloads.UpdateError(download.DownloadId, "Download Link cannot be null");
await _downloads.UpdateCompleted(download.DownloadId, DateTimeOffset.UtcNow);
continue;
}
// Check if the unpacking process is even needed
var uri = new Uri(download.Link);
var fileName = uri.Segments.Last();
fileName = HttpUtility.UrlDecode(fileName);
Log($"Found file name {fileName}", download, torrent);
var extension = Path.GetExtension(fileName);
if (extension != ".rar")
{
Log($"No need to unpack, setting it as unpacked", download, torrent);
download.UnpackingStarted = DateTimeOffset.UtcNow;
download.UnpackingFinished = DateTimeOffset.UtcNow;
download.Completed = DateTimeOffset.UtcNow;
await _downloads.UpdateUnpackingStarted(download.DownloadId, download.UnpackingStarted);
await _downloads.UpdateUnpackingFinished(download.DownloadId, download.UnpackingFinished);
await _downloads.UpdateCompleted(download.DownloadId, download.Completed);
continue;
}
// Check if we have reached the download limit, if so queue the download, but don't start it.
if (TorrentRunner.ActiveUnpackClients.Count >= settingUnpackLimit)
{
Log($"Not starting unpack because there are already the max number of unpacks active", download, torrent);
continue;
}
if (TorrentRunner.ActiveUnpackClients.ContainsKey(download.DownloadId))
{
Log($"Not starting unpack because this download is already active", download, torrent);
continue;
}
download.UnpackingStarted = DateTimeOffset.UtcNow;
await _downloads.UpdateUnpackingStarted(download.DownloadId, download.UnpackingStarted);
var downloadPath = settingDownloadPath;
if (!String.IsNullOrWhiteSpace(torrent.Category))
{
downloadPath = Path.Combine(downloadPath, torrent.Category);
}
Log($"Setting unpack path to {downloadPath}", download, torrent);
// Start the unpacking process
var unpackClient = new UnpackClient(download, downloadPath);
if (TorrentRunner.ActiveUnpackClients.TryAdd(download.DownloadId, unpackClient))
{ {
Log($"Starting unpack", download, torrent); Log($"Starting unpack", download, torrent);
unpackClient.Start(); if (download.Link == null)
}
}
Log("Processing", torrent);
// If torrent is erroring out on the Real-Debrid side.
if (torrent.RdStatus == RealDebridStatus.Error)
{
Log($"Torrent reported an error: {torrent.RdStatusRaw}", torrent);
Log($"Torrent retry count {torrent.RetryCount}/{TorrentRetryCount}", torrent);
if (torrent.RetryCount < TorrentRetryCount)
{
Log($"Retrying torrent", torrent);
_ = Task.Run(async () =>
{ {
using var blockScope = _serviceProvider.CreateScope(); Log($"No download link found", download, torrent);
var torrentsService = (Torrents) blockScope.ServiceProvider.GetService(typeof(Torrents));
await torrentsService.RetryTorrent(torrent.TorrentId, torrent.RetryCount + 1);
});
// Force a delay and return the main loop to prevent concurrency issues. await _downloads.UpdateError(download.DownloadId, "Download Link cannot be null");
await Task.Delay(10000); await _downloads.UpdateCompleted(download.DownloadId, DateTimeOffset.UtcNow);
return; continue;
} }
Log($"Received RealDebrid error: {torrent.RdStatusRaw}, not processing further", torrent); // Check if the unpacking process is even needed
await _torrents.UpdateComplete(torrent.TorrentId, DateTimeOffset.Now); var uri = new Uri(download.Link);
var fileName = uri.Segments.Last();
continue; fileName = HttpUtility.UrlDecode(fileName);
}
// Real-Debrid is waiting for file selection, select which files to download. Log($"Found file name {fileName}", download, torrent);
if ((torrent.RdStatus == RealDebridStatus.WaitingForFileSelection || torrent.RdStatus == RealDebridStatus.Finished) &&
torrent.FilesSelected == null &&
torrent.Downloads.Count == 0 &&
torrent.FilesSelected == null)
{
Log($"Selecting files", torrent);
var files = torrent.Files; var extension = Path.GetExtension(fileName);
if (torrent.DownloadAction == TorrentDownloadAction.DownloadAvailableFiles) if (extension != ".rar")
{
Log($"Determining which files are already available on RealDebrid", torrent);
var availableFiles = await _torrents.GetAvailableFiles(torrent.Hash);
Log($"Found {files.Count}/{torrent.Files.Count} available files on RealDebrid", torrent);
files = torrent.Files.Where(m => availableFiles.Any(f => m.Path.EndsWith(f.Filename))).ToList();
}
else if (torrent.DownloadAction == TorrentDownloadAction.DownloadAll)
{
Log("Selecting all files", torrent);
files = torrent.Files.ToList();
}
else if (torrent.DownloadAction == TorrentDownloadAction.DownloadManual)
{
Log("Selecting manual selected files", torrent);
files = torrent.Files.Where(m => torrent.ManualFiles.Any(f => m.Path.EndsWith(f))).ToList();
}
Log($"Selecting {files.Count}/{torrent.Files.Count} files", torrent);
if (torrent.DownloadAction != TorrentDownloadAction.DownloadManual && torrent.DownloadMinSize > 0)
{
var minFileSize = torrent.DownloadMinSize * 1024 * 1024;
Log($"Determining which files are over {minFileSize} bytes", torrent);
files = files.Where(m => m.Bytes > minFileSize)
.ToList();
Log($"Found {files.Count} files that match the minimum file size criterea", torrent);
}
if (files.Count == 0)
{
Log($"Filtered all files out! Downloading ALL files instead!", torrent);
files = torrent.Files;
}
var fileIds = files.Select(m => m.Id.ToString()).ToArray();
Log($"Selecting files:{Environment.NewLine}{String.Join(Environment.NewLine, files.Select(m => m.Path))}", torrent);
await _torrents.SelectFiles(torrent.TorrentId, fileIds);
await _torrents.UpdateFilesSelected(torrent.TorrentId, DateTime.UtcNow);
}
// Real-Debrid finished downloading the torrent, process the file to host.
if (torrent.RdStatus == RealDebridStatus.Finished)
{
// The files are selected but there are no downloads yet, check if Real-Debrid has generated links yet.
if (torrent.Downloads.Count == 0 && torrent.FilesSelected != null)
{
Log($"Checking for links", torrent);
await _torrents.CheckForLinks(torrent.TorrentId);
}
}
// Check if torrent is complete
if (torrent.Downloads.Count > 0)
{
var allComplete = torrent.Downloads.Count(m => m.Completed != null);
if (allComplete == torrent.Downloads.Count)
{
Log($"All downloads complete, marking torrent as complete", torrent);
await _torrents.UpdateComplete(torrent.TorrentId, DateTimeOffset.UtcNow);
switch (torrent.FinishedAction)
{ {
case TorrentFinishedAction.RemoveAllTorrents: Log($"No need to unpack, setting it as unpacked", download, torrent);
Log($"Removing torrents from Real-Debrid and Real-Debrid Client, no files", torrent);
await _torrents.Delete(torrent.TorrentId, true, true, false); download.UnpackingStarted = DateTimeOffset.UtcNow;
break; download.UnpackingFinished = DateTimeOffset.UtcNow;
case TorrentFinishedAction.RemoveRealDebrid: download.Completed = DateTimeOffset.UtcNow;
Log($"Removing torrents from Real-Debrid, no files", torrent);
await _torrents.Delete(torrent.TorrentId, false, true, false); await _downloads.UpdateUnpackingStarted(download.DownloadId, download.UnpackingStarted);
break; await _downloads.UpdateUnpackingFinished(download.DownloadId, download.UnpackingFinished);
case TorrentFinishedAction.None: await _downloads.UpdateCompleted(download.DownloadId, download.Completed);
Log($"Not removing torrents or files", torrent);
break; continue;
default: }
Log($"Invalid torrent FinishedAction {torrent.FinishedAction}", torrent);
break; // Check if we have reached the download limit, if so queue the download, but don't start it.
if (TorrentRunner.ActiveUnpackClients.Count >= settingUnpackLimit)
{
Log($"Not starting unpack because there are already the max number of unpacks active", download, torrent);
continue;
}
if (TorrentRunner.ActiveUnpackClients.ContainsKey(download.DownloadId))
{
Log($"Not starting unpack because this download is already active", download, torrent);
continue;
}
download.UnpackingStarted = DateTimeOffset.UtcNow;
await _downloads.UpdateUnpackingStarted(download.DownloadId, download.UnpackingStarted);
var downloadPath = settingDownloadPath;
if (!String.IsNullOrWhiteSpace(torrent.Category))
{
downloadPath = Path.Combine(downloadPath, torrent.Category);
}
Log($"Setting unpack path to {downloadPath}", download, torrent);
// Start the unpacking process
var unpackClient = new UnpackClient(download, downloadPath);
if (TorrentRunner.ActiveUnpackClients.TryAdd(download.DownloadId, unpackClient))
{
Log($"Starting unpack", download, torrent);
unpackClient.Start();
} }
} }
else
Log("Processing", torrent);
// If torrent is erroring out on the Real-Debrid side.
if (torrent.RdStatus == RealDebridStatus.Error)
{ {
Log($"Waiting for downloads to complete. {allComplete}/{torrent.Downloads.Count} complete", torrent); Log($"Torrent reported an error: {torrent.RdStatusRaw}", torrent);
Log($"Torrent retry count {torrent.RetryCount}/{torrent.TorrentRetryAttempts}", torrent);
Log($"Received RealDebrid error: {torrent.RdStatusRaw}, not processing further", torrent);
await _torrents.UpdateComplete(torrent.TorrentId, $"Received RealDebrid error: {torrent.RdStatusRaw}.", DateTimeOffset.UtcNow);
continue;
} }
// Real-Debrid is waiting for file selection, select which files to download.
if ((torrent.RdStatus == RealDebridStatus.WaitingForFileSelection || torrent.RdStatus == RealDebridStatus.Finished) &&
torrent.FilesSelected == null &&
torrent.Downloads.Count == 0 &&
torrent.FilesSelected == null)
{
Log($"Selecting files", torrent);
var files = torrent.Files;
if (torrent.DownloadAction == TorrentDownloadAction.DownloadAvailableFiles)
{
Log($"Determining which files are already available on RealDebrid", torrent);
var availableFiles = await _torrents.GetAvailableFiles(torrent.Hash);
Log($"Found {files.Count}/{torrent.Files.Count} available files on RealDebrid", torrent);
files = torrent.Files.Where(m => availableFiles.Any(f => m.Path.EndsWith(f.Filename))).ToList();
}
else if (torrent.DownloadAction == TorrentDownloadAction.DownloadAll)
{
Log("Selecting all files", torrent);
files = torrent.Files.ToList();
}
else if (torrent.DownloadAction == TorrentDownloadAction.DownloadManual)
{
Log("Selecting manual selected files", torrent);
files = torrent.Files.Where(m => torrent.ManualFiles.Any(f => m.Path.EndsWith(f))).ToList();
}
Log($"Selecting {files.Count}/{torrent.Files.Count} files", torrent);
if (torrent.DownloadAction != TorrentDownloadAction.DownloadManual && torrent.DownloadMinSize > 0)
{
var minFileSize = torrent.DownloadMinSize * 1024 * 1024;
Log($"Determining which files are over {minFileSize} bytes", torrent);
files = files.Where(m => m.Bytes > minFileSize)
.ToList();
Log($"Found {files.Count} files that match the minimum file size criterea", torrent);
}
if (files.Count == 0)
{
Log($"Filtered all files out! Downloading ALL files instead!", torrent);
files = torrent.Files;
}
var fileIds = files.Select(m => m.Id.ToString()).ToArray();
Log($"Selecting files:{Environment.NewLine}{String.Join(Environment.NewLine, files.Select(m => m.Path))}", torrent);
await _torrents.SelectFiles(torrent.TorrentId, fileIds);
await _torrents.UpdateFilesSelected(torrent.TorrentId, DateTime.UtcNow);
}
// Real-Debrid finished downloading the torrent, process the file to host.
if (torrent.RdStatus == RealDebridStatus.Finished)
{
// The files are selected but there are no downloads yet, check if Real-Debrid has generated links yet.
if (torrent.Downloads.Count == 0 && torrent.FilesSelected != null)
{
Log($"Checking for links", torrent);
await _torrents.CheckForLinks(torrent.TorrentId);
}
}
// Check if torrent is complete
if (torrent.Downloads.Count > 0)
{
var allComplete = torrent.Downloads.Count(m => m.Completed != null);
if (allComplete == torrent.Downloads.Count)
{
Log($"All downloads complete, marking torrent as complete", torrent);
await _torrents.UpdateComplete(torrent.TorrentId, null, DateTimeOffset.UtcNow);
switch (torrent.FinishedAction)
{
case TorrentFinishedAction.RemoveAllTorrents:
Log($"Removing torrents from Real-Debrid and Real-Debrid Client, no files", torrent);
await _torrents.Delete(torrent.TorrentId, true, true, false);
break;
case TorrentFinishedAction.RemoveRealDebrid:
Log($"Removing torrents from Real-Debrid, no files", torrent);
await _torrents.Delete(torrent.TorrentId, false, true, false);
break;
case TorrentFinishedAction.None:
Log($"Not removing torrents or files", torrent);
break;
default:
Log($"Invalid torrent FinishedAction {torrent.FinishedAction}", torrent);
break;
}
}
else
{
Log($"Waiting for downloads to complete. {allComplete}/{torrent.Downloads.Count} complete", torrent);
}
}
}
catch (Exception ex)
{
_logger.LogError(ex.Message, $"Torrent processing result in an unexpected exception: {ex.Message}");
await _torrents.UpdateComplete(torrent.TorrentId, ex.Message, DateTimeOffset.UtcNow);
} }
} }

View file

@ -9,9 +9,9 @@ using MonoTorrent;
using Newtonsoft.Json; using Newtonsoft.Json;
using RdtClient.Data.Data; using RdtClient.Data.Data;
using RdtClient.Data.Enums; using RdtClient.Data.Enums;
using RdtClient.Data.Models.Internal;
using RdtClient.Data.Models.TorrentClient;
using RdtClient.Service.Helpers; using RdtClient.Service.Helpers;
using RdtClient.Service.Models;
using RdtClient.Service.Models.TorrentClient;
using RdtClient.Service.Services.TorrentClients; using RdtClient.Service.Services.TorrentClients;
using Torrent = RdtClient.Data.Models.Data.Torrent; using Torrent = RdtClient.Data.Models.Data.Torrent;
@ -93,13 +93,7 @@ namespace RdtClient.Service.Services
await _torrentData.UpdateCategory(torrent.TorrentId, category); await _torrentData.UpdateCategory(torrent.TorrentId, category);
} }
public async Task<Torrent> UploadMagnet(String magnetLink, public async Task<Torrent> UploadMagnet(String magnetLink, Torrent torrent)
String category,
TorrentDownloadAction downloadAction,
TorrentFinishedAction finishedAction,
Int32 downloadMinSize,
String downloadManualFiles,
Int32? priority)
{ {
MagnetLink magnet; MagnetLink magnet;
@ -117,20 +111,14 @@ namespace RdtClient.Service.Services
var hash = magnet.InfoHash.ToHex(); var hash = magnet.InfoHash.ToHex();
var newTorrent = await Add(id, hash, category, downloadAction, finishedAction, downloadMinSize, downloadManualFiles, magnetLink, false, priority); var newTorrent = await Add(id, hash, magnetLink, false, torrent);
Log($"Adding {hash} magnet link {magnetLink}", newTorrent); Log($"Adding {hash} magnet link {magnetLink}", newTorrent);
return newTorrent; return newTorrent;
} }
public async Task<Torrent> UploadFile(Byte[] bytes, public async Task<Torrent> UploadFile(Byte[] bytes, Torrent torrent)
String category,
TorrentDownloadAction downloadAction,
TorrentFinishedAction finishedAction,
Int32 downloadMinSize,
String downloadManualFiles,
Int32? priority)
{ {
MonoTorrent.Torrent monoTorrent; MonoTorrent.Torrent monoTorrent;
@ -149,7 +137,7 @@ namespace RdtClient.Service.Services
var hash = monoTorrent.InfoHash.ToHex(); var hash = monoTorrent.InfoHash.ToHex();
var newTorrent = await Add(id, hash, category, downloadAction, finishedAction, downloadMinSize, downloadManualFiles, fileAsBase64, true, priority); var newTorrent = await Add(id, hash, fileAsBase64, true, torrent);
Log($"Adding {hash} torrent file {fileAsBase64}", newTorrent); Log($"Adding {hash} torrent file {fileAsBase64}", newTorrent);
@ -221,7 +209,7 @@ namespace RdtClient.Service.Services
Log($"Deleting", torrent); Log($"Deleting", torrent);
await UpdateComplete(torrentId, DateTimeOffset.UtcNow); await UpdateComplete(torrentId, "Torrent deleted", DateTimeOffset.UtcNow);
foreach (var download in torrent.Downloads) foreach (var download in torrent.Downloads)
{ {
@ -248,7 +236,6 @@ namespace RdtClient.Service.Services
{ {
Log($"Deleting RdtClient data", torrent); Log($"Deleting RdtClient data", torrent);
await _torrentData.UpdateComplete(torrent.TorrentId, DateTimeOffset.UtcNow);
await _downloads.DeleteForTorrent(torrent.TorrentId); await _downloads.DeleteForTorrent(torrent.TorrentId);
await _torrentData.Delete(torrentId); await _torrentData.Delete(torrentId);
} }
@ -380,31 +367,31 @@ namespace RdtClient.Service.Services
{ {
var torrent = await _torrentData.GetById(torrentId); var torrent = await _torrentData.GetById(torrentId);
if (torrent == null) if (torrent?.Retry == null)
{ {
return; return;
} }
Log($"Retrying Torrent", torrent); Log($"Retrying Torrent", torrent);
await UpdateComplete(torrent.TorrentId, DateTimeOffset.Now); await UpdateComplete(torrent.TorrentId, "Retrying Torrent", DateTimeOffset.UtcNow);
foreach (var download in torrent.Downloads) foreach (var download in torrent.Downloads)
{ {
await _downloads.UpdateError(download.DownloadId, null); await _downloads.UpdateError(download.DownloadId, null);
await _downloads.UpdateCompleted(download.DownloadId, DateTimeOffset.Now); await _downloads.UpdateCompleted(download.DownloadId, DateTimeOffset.UtcNow);
} }
foreach (var download in torrent.Downloads) foreach (var download in torrent.Downloads)
{ {
while (TorrentRunner.ActiveDownloadClients.TryGetValue(download.DownloadId, out var downloadClient)) while (TorrentRunner.ActiveDownloadClients.TryRemove(download.DownloadId, out var downloadClient))
{ {
await downloadClient.Cancel(); await downloadClient.Cancel();
await Task.Delay(100); await Task.Delay(100);
} }
while (TorrentRunner.ActiveUnpackClients.TryGetValue(download.DownloadId, out var unpackClient)) while (TorrentRunner.ActiveUnpackClients.TryRemove(download.DownloadId, out var unpackClient))
{ {
unpackClient.Cancel(); unpackClient.Cancel();
@ -425,26 +412,14 @@ namespace RdtClient.Service.Services
{ {
var bytes = Convert.FromBase64String(torrent.FileOrMagnet); var bytes = Convert.FromBase64String(torrent.FileOrMagnet);
newTorrent = await UploadFile(bytes, newTorrent = await UploadFile(bytes, torrent);
torrent.Category,
torrent.DownloadAction,
torrent.FinishedAction,
torrent.DownloadMinSize,
torrent.DownloadManualFiles,
torrent.Priority);
} }
else else
{ {
newTorrent = await UploadMagnet(torrent.FileOrMagnet, newTorrent = await UploadMagnet(torrent.FileOrMagnet, torrent);
torrent.Category,
torrent.DownloadAction,
torrent.FinishedAction,
torrent.DownloadMinSize,
torrent.DownloadManualFiles,
torrent.Priority);
} }
await _torrentData.UpdateRetryCount(newTorrent.TorrentId, retryCount); await _torrentData.UpdateRetry(newTorrent.TorrentId, null, retryCount);
} }
finally finally
{ {
@ -485,16 +460,16 @@ namespace RdtClient.Service.Services
await FileHelper.Delete(filePath); await FileHelper.Delete(filePath);
await _torrentData.UpdateComplete(download.TorrentId, null);
Log($"Resetting", download, download.Torrent); Log($"Resetting", download, download.Torrent);
await _downloads.Reset(downloadId); await _downloads.Reset(downloadId);
await _torrentData.UpdateComplete(download.TorrentId, null, null);
} }
public async Task UpdateComplete(Guid torrentId, DateTimeOffset datetime) public async Task UpdateComplete(Guid torrentId, String error, DateTimeOffset datetime)
{ {
await _torrentData.UpdateComplete(torrentId, datetime); await _torrentData.UpdateComplete(torrentId, error, datetime);
} }
public async Task UpdateFilesSelected(Guid torrentId, DateTimeOffset datetime) public async Task UpdateFilesSelected(Guid torrentId, DateTimeOffset datetime)
@ -514,6 +489,16 @@ namespace RdtClient.Service.Services
await _torrentData.UpdatePriority(torrent.TorrentId, priority); await _torrentData.UpdatePriority(torrent.TorrentId, priority);
} }
public async Task UpdateRetry(Guid torrentId, DateTimeOffset? datetime, Int32 retry)
{
await _torrentData.UpdateRetry(torrentId, datetime, retry);
}
public async Task UpdateError(Guid torrentId, String error)
{
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); var torrent = await _torrentData.GetById(torrentId);
@ -558,36 +543,26 @@ namespace RdtClient.Service.Services
private async Task<Torrent> Add(String rdTorrentId, private async Task<Torrent> Add(String rdTorrentId,
String infoHash, String infoHash,
String category,
TorrentDownloadAction downloadAction,
TorrentFinishedAction finishedAction,
Int32 downloadMinSize,
String downloadManualFiles,
String fileOrMagnetContents, String fileOrMagnetContents,
Boolean isFile, Boolean isFile,
Int32? priority) Torrent torrent)
{ {
await RealDebridUpdateLock.WaitAsync(); await RealDebridUpdateLock.WaitAsync();
try try
{ {
var torrent = await _torrentData.GetByHash(infoHash); var existingTorrent = await _torrentData.GetByHash(infoHash);
if (torrent != null) if (existingTorrent != null)
{ {
return torrent; return existingTorrent;
} }
var newTorrent = await _torrentData.Add(rdTorrentId, var newTorrent = await _torrentData.Add(rdTorrentId,
infoHash, infoHash,
category,
downloadAction,
finishedAction,
downloadMinSize,
downloadManualFiles,
fileOrMagnetContents, fileOrMagnetContents,
isFile, isFile,
priority); torrent);
await UpdateRdData(newTorrent); await UpdateRdData(newTorrent);
@ -598,7 +573,7 @@ namespace RdtClient.Service.Services
RealDebridUpdateLock.Release(); RealDebridUpdateLock.Release();
} }
} }
public async Task Update(Torrent torrent) public async Task Update(Torrent torrent)
{ {
await _torrentData.Update(torrent); await _torrentData.Update(torrent);
@ -703,7 +678,7 @@ namespace RdtClient.Service.Services
_logger.LogDebug(message); _logger.LogDebug(message);
} }
private void Log(String message, Torrent torrent = null) private void Log(String message, Torrent torrent = null)
{ {
if (torrent != null) if (torrent != null)

View file

@ -5,8 +5,8 @@ using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using RdtClient.Service.Models.QBittorrent; using RdtClient.Data.Models.QBittorrent;
using RdtClient.Service.Models.QBittorrent.QuickType; using RdtClient.Data.Models.QBittorrent.QuickType;
using RdtClient.Service.Services; using RdtClient.Service.Services;

View file

@ -5,7 +5,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using RdtClient.Data.Models.Data; using RdtClient.Data.Models.Data;
using RdtClient.Service.Models; using RdtClient.Data.Models.Internal;
using RdtClient.Service.Services; using RdtClient.Service.Services;
using Serilog.Events; using Serilog.Events;

View file

@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MonoTorrent; using MonoTorrent;
using RdtClient.Data.Enums;
using RdtClient.Service.Helpers; using RdtClient.Service.Helpers;
using RdtClient.Service.Services; using RdtClient.Service.Services;
using Torrent = RdtClient.Data.Models.Data.Torrent; using Torrent = RdtClient.Data.Models.Data.Torrent;
@ -91,7 +90,7 @@ namespace RdtClient.Web.Controllers
var bytes = memoryStream.ToArray(); var bytes = memoryStream.ToArray();
await _torrents.UploadFile(bytes, formData.Category, formData.DownloadAction, formData.FinishedAction, formData.DownloadMinSize, formData.DownloadManualFiles, formData.Priority); await _torrents.UploadFile(bytes, formData.Torrent);
return Ok(); return Ok();
} }
@ -100,13 +99,7 @@ namespace RdtClient.Web.Controllers
[Route("UploadMagnet")] [Route("UploadMagnet")]
public async Task<ActionResult> UploadMagnet([FromBody] TorrentControllerUploadMagnetRequest request) public async Task<ActionResult> UploadMagnet([FromBody] TorrentControllerUploadMagnetRequest request)
{ {
await _torrents.UploadMagnet(request.MagnetLink, await _torrents.UploadMagnet(request.MagnetLink, request.Torrent);
request.Category,
request.DownloadAction,
request.FinishedAction,
request.DownloadMinSize,
request.DownloadManualFiles,
request.Priority);
return Ok(); return Ok();
} }
@ -185,23 +178,13 @@ namespace RdtClient.Web.Controllers
public class TorrentControllerUploadFileRequest public class TorrentControllerUploadFileRequest
{ {
public String Category { get; set; } public Torrent Torrent { get; set; }
public TorrentDownloadAction DownloadAction { get; set; }
public TorrentFinishedAction FinishedAction { get; set; }
public Int32 DownloadMinSize { get; set; }
public String DownloadManualFiles { get; set; }
public Int32? Priority { get; set; }
} }
public class TorrentControllerUploadMagnetRequest public class TorrentControllerUploadMagnetRequest
{ {
public String MagnetLink { get; set; } public String MagnetLink { get; set; }
public String Category { get; set; } public Torrent Torrent { get; set; }
public TorrentDownloadAction DownloadAction { get; set; }
public TorrentFinishedAction FinishedAction { get; set; }
public Int32 DownloadMinSize { get; set; }
public String DownloadManualFiles { get; set; }
public Int32? Priority { get; set; }
} }
public class TorrentControllerDeleteRequest public class TorrentControllerDeleteRequest