Add a retry button in the interface.
Fixed issue with some downloads not processed. Fixed issue with deletion and files in use.
This commit is contained in:
parent
8b85521839
commit
3e1b30ad1a
13 changed files with 650 additions and 118 deletions
|
|
@ -15,18 +15,10 @@
|
||||||
{{ torrent | status }}
|
{{ torrent | status }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span
|
<span class="icon sync-icon" (click)="retryClick($event)" title="Retry" *ngIf="!loading">
|
||||||
class="icon download-icon"
|
<i class="fas fa-sync"></i>
|
||||||
(click)="download($event)"
|
|
||||||
title="Download to disk"
|
|
||||||
*ngIf="!loading && canDownload()"
|
|
||||||
>
|
|
||||||
<i class="fas fa-download"></i>
|
|
||||||
</span>
|
</span>
|
||||||
<span class="icon download-icon" (click)="unpack($event)" title="Unpack" *ngIf="!loading && canUnpack()">
|
<span class="icon delete-icon" (click)="deleteClick($event)" title="Delete torrent" *ngIf="!loading">
|
||||||
<i class="fas fa-box-open"></i>
|
|
||||||
</span>
|
|
||||||
<span class="icon delete-icon" (click)="delete1($event)" title="Delete torrent" *ngIf="!loading">
|
|
||||||
<i class="fas fa-times"></i>
|
<i class="fas fa-times"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="icon loading-icon" *ngIf="loading">
|
<span class="icon loading-icon" *ngIf="loading">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||||
import { RealDebridStatus, Torrent } from 'src/app/models/torrent.model';
|
import { Torrent } from 'src/app/models/torrent.model';
|
||||||
import { TorrentService } from 'src/app/torrent.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: '[app-torrent-row]',
|
selector: '[app-torrent-row]',
|
||||||
|
|
@ -14,55 +13,22 @@ export class TorrentRowComponent implements OnInit {
|
||||||
@Output('delete')
|
@Output('delete')
|
||||||
public delete = new EventEmitter();
|
public delete = new EventEmitter();
|
||||||
|
|
||||||
|
@Output('retry')
|
||||||
|
public retry = new EventEmitter();
|
||||||
|
|
||||||
public loading = false;
|
public loading = false;
|
||||||
|
|
||||||
constructor(private torrentService: TorrentService) {}
|
constructor() {}
|
||||||
|
|
||||||
ngOnInit(): void {}
|
ngOnInit(): void {}
|
||||||
|
|
||||||
public download(event: Event): void {
|
public deleteClick(event: Event): void {
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
this.loading = true;
|
|
||||||
this.torrentService.download(this.torrent.torrentId).subscribe(
|
|
||||||
() => {
|
|
||||||
this.loading = false;
|
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
this.loading = false;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unpack(event: Event): void {
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
this.loading = true;
|
|
||||||
this.torrentService.unpack(this.torrent.torrentId).subscribe(
|
|
||||||
() => {
|
|
||||||
this.loading = false;
|
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
this.loading = false;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public delete1(event: Event): void {
|
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this.delete.emit(this.torrent.torrentId);
|
this.delete.emit(this.torrent.torrentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public canDownload(): boolean {
|
public retryClick(event: Event): void {
|
||||||
return (
|
event.stopPropagation();
|
||||||
(this.torrent.rdStatus === RealDebridStatus.Finished && this.torrent.downloads.length === 0) ||
|
this.retry.emit(this.torrent.torrentId);
|
||||||
(this.torrent.downloads.length > 0 && this.torrent.downloads.any((m) => m.error != null))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public canUnpack(): boolean {
|
|
||||||
const downloadsDone = this.torrent.downloads.any((m) => m.downloadFinished != null);
|
|
||||||
const downloadsUnpacked = this.torrent.downloads.any((m) => m.unpackingQueued != null);
|
|
||||||
return downloadsDone && !downloadsUnpacked;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<table class="table is-fullwidth is-hoverable">
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
|
|
@ -23,6 +22,7 @@
|
||||||
[torrent]="torrent"
|
[torrent]="torrent"
|
||||||
(click)="selectTorrent(torrent)"
|
(click)="selectTorrent(torrent)"
|
||||||
(delete)="showDeleteModal($event)"
|
(delete)="showDeleteModal($event)"
|
||||||
|
(retry)="showRetryModal($event)"
|
||||||
></tr>
|
></tr>
|
||||||
<ng-container *ngIf="showFiles[torrent.torrentId]">
|
<ng-container *ngIf="showFiles[torrent.torrentId]">
|
||||||
<tr class="separator">
|
<tr class="separator">
|
||||||
|
|
@ -88,3 +88,45 @@
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal" [class.is-active]="isRetryModalActive">
|
||||||
|
<div class="modal-background"></div>
|
||||||
|
<div class="modal-card">
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">Retry torrent</p>
|
||||||
|
<button class="delete" aria-label="close" (click)="retryCancel()"></button>
|
||||||
|
</header>
|
||||||
|
<section class="modal-card-body">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Retry</label>
|
||||||
|
<div class="control">
|
||||||
|
<label class="radio">
|
||||||
|
<input type="radio" name="retry" [value]="0" [(ngModel)]="retry" />
|
||||||
|
Retry on Real-Debrid
|
||||||
|
</label>
|
||||||
|
<br />
|
||||||
|
<label class="radio">
|
||||||
|
<input type="radio" name="retry" [value]="1" [(ngModel)]="retry" />
|
||||||
|
Retry downloading locally
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="notification is-danger is-light" *ngIf="retryError?.length > 0">
|
||||||
|
Error retrying torrent: {{ retryError }}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<button
|
||||||
|
class="button is-success"
|
||||||
|
(click)="retryOk()"
|
||||||
|
[disabled]="retrying"
|
||||||
|
[ngClass]="{ 'is-loading': retrying }"
|
||||||
|
>
|
||||||
|
Retry
|
||||||
|
</button>
|
||||||
|
<button class="button" (click)="retryCancel()" [disabled]="retrying" [ngClass]="{ 'is-loading': retrying }">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,12 @@ export class TorrentTableComponent implements OnInit, OnDestroy {
|
||||||
public deleteRdTorrent: boolean;
|
public deleteRdTorrent: boolean;
|
||||||
public deleteLocalFiles: boolean;
|
public deleteLocalFiles: boolean;
|
||||||
|
|
||||||
|
public isRetryModalActive: boolean;
|
||||||
|
public retryError: string;
|
||||||
|
public retrying: boolean;
|
||||||
|
public retryTorrentId: string;
|
||||||
|
public retry: number;
|
||||||
|
|
||||||
constructor(private torrentService: TorrentService) {}
|
constructor(private torrentService: TorrentService) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|
@ -52,6 +58,10 @@ export class TorrentTableComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
public showDeleteModal(torrentId: string): void {
|
public showDeleteModal(torrentId: string): void {
|
||||||
|
this.deleteData = false;
|
||||||
|
this.deleteRdTorrent = false;
|
||||||
|
this.deleteLocalFiles = false;
|
||||||
|
|
||||||
this.deleteTorrentId = torrentId;
|
this.deleteTorrentId = torrentId;
|
||||||
this.isDeleteModalActive = true;
|
this.isDeleteModalActive = true;
|
||||||
}
|
}
|
||||||
|
|
@ -76,4 +86,30 @@ export class TorrentTableComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public showRetryModal(torrentId: string): void {
|
||||||
|
this.retry = 0;
|
||||||
|
|
||||||
|
this.retryTorrentId = torrentId;
|
||||||
|
this.isRetryModalActive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public retryCancel(): void {
|
||||||
|
this.isRetryModalActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public retryOk(): void {
|
||||||
|
this.retrying = true;
|
||||||
|
|
||||||
|
this.torrentService.retry(this.retryTorrentId, this.retry).subscribe(
|
||||||
|
() => {
|
||||||
|
this.isRetryModalActive = false;
|
||||||
|
this.retrying = false;
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
this.retryError = err.error;
|
||||||
|
this.retrying = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,14 +57,6 @@ export class TorrentService {
|
||||||
return this.http.post<string[]>(`/Api/Torrents/CheckFiles`, formData);
|
return this.http.post<string[]>(`/Api/Torrents/CheckFiles`, formData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public download(torrentId: string): Observable<void> {
|
|
||||||
return this.http.get<void>(`/Api/Torrents/Download/${torrentId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unpack(torrentId: string): Observable<void> {
|
|
||||||
return this.http.get<void>(`/Api/Torrents/Unpack/${torrentId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
public delete(
|
public delete(
|
||||||
torrentId: string,
|
torrentId: string,
|
||||||
deleteData: boolean,
|
deleteData: boolean,
|
||||||
|
|
@ -77,4 +69,10 @@ export class TorrentService {
|
||||||
deleteLocalFiles,
|
deleteLocalFiles,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public retry(torrentId: string, retry: number): Observable<void> {
|
||||||
|
return this.http.post<void>(`/Api/Torrents/Retry/${torrentId}`, {
|
||||||
|
retry,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ namespace RdtClient.Data.Data
|
||||||
Task<IList<Torrent>> Get();
|
Task<IList<Torrent>> Get();
|
||||||
Task<Torrent> GetById(Guid torrentId);
|
Task<Torrent> GetById(Guid torrentId);
|
||||||
Task<Torrent> GetByHash(String hash);
|
Task<Torrent> GetByHash(String hash);
|
||||||
Task<Torrent> Add(String realDebridId, String hash, String category, Boolean autoDelete);
|
Task<Torrent> Add(String realDebridId, String hash, String category, Boolean autoDelete, String fileOrMagnetContents, Boolean isFile);
|
||||||
Task UpdateRdData(Torrent torrent);
|
Task UpdateRdData(Torrent torrent);
|
||||||
Task UpdateCategory(Guid torrentId, String category);
|
Task UpdateCategory(Guid torrentId, String category);
|
||||||
Task UpdateComplete(Guid torrentId, DateTimeOffset datetime);
|
Task UpdateComplete(Guid torrentId, DateTimeOffset? datetime);
|
||||||
Task Delete(Guid torrentId);
|
Task Delete(Guid torrentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,7 +78,7 @@ namespace RdtClient.Data.Data
|
||||||
return dbTorrent;
|
return dbTorrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Torrent> Add(String realDebridId, String hash, String category, Boolean autoDelete)
|
public async Task<Torrent> Add(String realDebridId, String hash, String category, Boolean autoDelete, String fileOrMagnetContents, Boolean isFile)
|
||||||
{
|
{
|
||||||
var torrent = new Torrent
|
var torrent = new Torrent
|
||||||
{
|
{
|
||||||
|
|
@ -87,7 +87,9 @@ namespace RdtClient.Data.Data
|
||||||
RdId = realDebridId,
|
RdId = realDebridId,
|
||||||
Hash = hash.ToLower(),
|
Hash = hash.ToLower(),
|
||||||
Category = category,
|
Category = category,
|
||||||
AutoDelete = autoDelete
|
AutoDelete = autoDelete,
|
||||||
|
FileOrMagnet = fileOrMagnetContents,
|
||||||
|
IsFile = isFile
|
||||||
};
|
};
|
||||||
|
|
||||||
await _dataContext.Torrents.AddAsync(torrent);
|
await _dataContext.Torrents.AddAsync(torrent);
|
||||||
|
|
@ -140,7 +142,7 @@ namespace RdtClient.Data.Data
|
||||||
await _dataContext.SaveChangesAsync();
|
await _dataContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateComplete(Guid torrentId, DateTimeOffset datetime)
|
public async Task UpdateComplete(Guid torrentId, DateTimeOffset? datetime)
|
||||||
{
|
{
|
||||||
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
|
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
|
||||||
|
|
||||||
|
|
|
||||||
418
server/RdtClient.Data/Migrations/20210313145632_Torrents_Add_FileOrMagnet.Designer.cs
generated
Normal file
418
server/RdtClient.Data/Migrations/20210313145632_Torrents_Add_FileOrMagnet.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,418 @@
|
||||||
|
// <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("20210313145632_Torrents_Add_FileOrMagnet")]
|
||||||
|
partial class Torrents_Add_FileOrMagnet
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "5.0.3");
|
||||||
|
|
||||||
|
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<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<bool>("AutoDelete")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Category")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("Completed")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("FileOrMagnet")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Hash")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsFile")
|
||||||
|
.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.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace RdtClient.Data.Migrations
|
||||||
|
{
|
||||||
|
public partial class Torrents_Add_FileOrMagnet : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "FileOrMagnet",
|
||||||
|
table: "Torrents",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "IsFile",
|
||||||
|
table: "Torrents",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "FileOrMagnet",
|
||||||
|
table: "Torrents");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "IsFile",
|
||||||
|
table: "Torrents");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -291,9 +291,15 @@ namespace RdtClient.Data.Migrations
|
||||||
b.Property<DateTimeOffset?>("Completed")
|
b.Property<DateTimeOffset?>("Completed")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("FileOrMagnet")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("Hash")
|
b.Property<string>("Hash")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsFile")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("RdAdded")
|
b.Property<DateTimeOffset>("RdAdded")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@ namespace RdtClient.Data.Models.Data
|
||||||
|
|
||||||
public Boolean AutoDelete { get; set; }
|
public Boolean AutoDelete { get; set; }
|
||||||
|
|
||||||
|
public String FileOrMagnet { get; set; }
|
||||||
|
public Boolean IsFile { get; set; }
|
||||||
|
|
||||||
[InverseProperty("Torrent")]
|
[InverseProperty("Torrent")]
|
||||||
public IList<Download> Downloads { get; set; }
|
public IList<Download> Downloads { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -335,7 +335,7 @@ namespace RdtClient.Service.Services
|
||||||
}
|
}
|
||||||
|
|
||||||
// RealDebrid is waiting for file selection, select which files to download.
|
// RealDebrid is waiting for file selection, select which files to download.
|
||||||
if (torrent.RdStatus == RealDebridStatus.WaitingForFileSelection && torrent.Downloads.Count == 0)
|
if (torrent.RdStatus == RealDebridStatus.WaitingForFileSelection || torrent.Downloads.Count == 0)
|
||||||
{
|
{
|
||||||
Log.Debug($"Torrent {torrent.RdId} selecting files");
|
Log.Debug($"Torrent {torrent.RdId} selecting files");
|
||||||
|
|
||||||
|
|
@ -390,8 +390,6 @@ namespace RdtClient.Service.Services
|
||||||
// RealDebrid finished downloading the torrent, process the file to host.
|
// RealDebrid finished downloading the torrent, process the file to host.
|
||||||
if (torrent.RdStatus == RealDebridStatus.Finished)
|
if (torrent.RdStatus == RealDebridStatus.Finished)
|
||||||
{
|
{
|
||||||
Log.Debug($"Torrent {torrent.RdId} completed, download starting");
|
|
||||||
|
|
||||||
// If the torrent has any files that need starting to be downloaded, download them.
|
// If the torrent has any files that need starting to be downloaded, download them.
|
||||||
var downloadsPending = torrent.Downloads
|
var downloadsPending = torrent.Downloads
|
||||||
.Where(m => m.Completed == null &&
|
.Where(m => m.Completed == null &&
|
||||||
|
|
@ -413,11 +411,6 @@ namespace RdtClient.Service.Services
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (torrent.RdStatus == RealDebridStatus.Finished)
|
|
||||||
{
|
|
||||||
Log.Debug($"Torrent {torrent.RdId} completed, unpack starting");
|
|
||||||
|
|
||||||
// If all files are finished downloading, move to the unpacking step.
|
// If all files are finished downloading, move to the unpacking step.
|
||||||
var unpackingPending = torrent.Downloads
|
var unpackingPending = torrent.Downloads
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ namespace RdtClient.Service.Services
|
||||||
public interface ITorrents
|
public interface ITorrents
|
||||||
{
|
{
|
||||||
Task<IList<Torrent>> Get();
|
Task<IList<Torrent>> Get();
|
||||||
Task<Torrent> GetById(Guid torrentId);
|
|
||||||
Task<Torrent> GetByHash(String hash);
|
Task<Torrent> GetByHash(String hash);
|
||||||
Task UpdateCategory(String hash, String category);
|
Task UpdateCategory(String hash, String category);
|
||||||
Task UploadMagnet(String magnetLink, String category, Boolean autoDelete);
|
Task UploadMagnet(String magnetLink, String category, Boolean autoDelete);
|
||||||
|
|
@ -32,6 +31,7 @@ namespace RdtClient.Service.Services
|
||||||
Task<Profile> GetProfile();
|
Task<Profile> GetProfile();
|
||||||
Task UpdateComplete(Guid torrentId, DateTimeOffset datetime);
|
Task UpdateComplete(Guid torrentId, DateTimeOffset datetime);
|
||||||
Task Update();
|
Task Update();
|
||||||
|
Task Retry(Guid id, Int32 retry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Torrents : ITorrents
|
public class Torrents : ITorrents
|
||||||
|
|
@ -141,7 +141,7 @@ namespace RdtClient.Service.Services
|
||||||
|
|
||||||
var rdTorrent = await GetRdNetClient().AddTorrentMagnetAsync(magnetLink);
|
var rdTorrent = await GetRdNetClient().AddTorrentMagnetAsync(magnetLink);
|
||||||
|
|
||||||
await Add(rdTorrent.Id, magnet.InfoHash.ToHex(), category, autoDelete);
|
await Add(rdTorrent.Id, magnet.InfoHash.ToHex(), category, autoDelete, magnetLink, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UploadFile(Byte[] bytes, String category, Boolean autoDelete)
|
public async Task UploadFile(Byte[] bytes, String category, Boolean autoDelete)
|
||||||
|
|
@ -150,7 +150,9 @@ namespace RdtClient.Service.Services
|
||||||
|
|
||||||
var rdTorrent = await GetRdNetClient().AddTorrentFileAsync(bytes);
|
var rdTorrent = await GetRdNetClient().AddTorrentFileAsync(bytes);
|
||||||
|
|
||||||
await Add(rdTorrent.Id, torrent.InfoHash.ToHex(), category, autoDelete);
|
var fileAsBase64 = Convert.ToBase64String(bytes);
|
||||||
|
|
||||||
|
await Add(rdTorrent.Id, torrent.InfoHash.ToHex(), category, autoDelete, fileAsBase64, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<String>> GetAvailableFiles(String hash)
|
public async Task<List<String>> GetAvailableFiles(String hash)
|
||||||
|
|
@ -197,17 +199,6 @@ namespace RdtClient.Service.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deleteLocalFiles)
|
|
||||||
{
|
|
||||||
var downloadPath = await DownloadPath(torrent);
|
|
||||||
downloadPath = Path.Combine(downloadPath, torrent.RdName);
|
|
||||||
|
|
||||||
if (Directory.Exists(downloadPath))
|
|
||||||
{
|
|
||||||
Directory.Delete(downloadPath, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deleteData)
|
if (deleteData)
|
||||||
{
|
{
|
||||||
await _downloads.DeleteForTorrent(torrent.TorrentId);
|
await _downloads.DeleteForTorrent(torrent.TorrentId);
|
||||||
|
|
@ -218,6 +209,37 @@ namespace RdtClient.Service.Services
|
||||||
{
|
{
|
||||||
await GetRdNetClient().DeleteTorrentAsync(torrent.RdId);
|
await GetRdNetClient().DeleteTorrentAsync(torrent.RdId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (deleteLocalFiles)
|
||||||
|
{
|
||||||
|
var downloadPath = await DownloadPath(torrent);
|
||||||
|
downloadPath = Path.Combine(downloadPath, torrent.RdName);
|
||||||
|
|
||||||
|
if (Directory.Exists(downloadPath))
|
||||||
|
{
|
||||||
|
var retry = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.Delete(downloadPath, true);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
retry++;
|
||||||
|
if (retry >= 3)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,7 +292,7 @@ namespace RdtClient.Service.Services
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Add(String rdTorrentId, String infoHash, String category, Boolean autoDelete)
|
private async Task Add(String rdTorrentId, String infoHash, String category, Boolean autoDelete, String fileOrMagnetContents, Boolean isFile)
|
||||||
{
|
{
|
||||||
var w = await SemaphoreSlim.WaitAsync(60000);
|
var w = await SemaphoreSlim.WaitAsync(60000);
|
||||||
if (!w)
|
if (!w)
|
||||||
|
|
@ -287,7 +309,7 @@ namespace RdtClient.Service.Services
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newTorrent = await _torrentData.Add(rdTorrentId, infoHash, category, autoDelete);
|
var newTorrent = await _torrentData.Add(rdTorrentId, infoHash, category, autoDelete, fileOrMagnetContents, isFile);
|
||||||
|
|
||||||
var rdTorrent = await GetRdNetClient().GetTorrentInfoAsync(rdTorrentId);
|
var rdTorrent = await GetRdNetClient().GetTorrentInfoAsync(rdTorrentId);
|
||||||
|
|
||||||
|
|
@ -305,8 +327,6 @@ namespace RdtClient.Service.Services
|
||||||
|
|
||||||
if (!w)
|
if (!w)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,6 +372,43 @@ namespace RdtClient.Service.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task Retry(Guid id, Int32 retry)
|
||||||
|
{
|
||||||
|
var torrent = await _torrentData.GetById(id);
|
||||||
|
|
||||||
|
if (retry == 0)
|
||||||
|
{
|
||||||
|
await Delete(id, true, true, true);
|
||||||
|
|
||||||
|
if (String.IsNullOrWhiteSpace(torrent.FileOrMagnet))
|
||||||
|
{
|
||||||
|
throw new Exception($"Cannot re-add this torrent, original magnet or file not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (torrent.IsFile)
|
||||||
|
{
|
||||||
|
var bytes = Convert.FromBase64String(torrent.FileOrMagnet);
|
||||||
|
|
||||||
|
await UploadFile(bytes, torrent.Category, torrent.AutoDelete);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await UploadMagnet(torrent.FileOrMagnet, torrent.Category, torrent.AutoDelete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (retry == 1)
|
||||||
|
{
|
||||||
|
await Delete(id, false, false, true);
|
||||||
|
|
||||||
|
await _torrentData.UpdateComplete(id, null);
|
||||||
|
await _downloads.DeleteForTorrent(id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception($"Invalid retry option {retry}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task UpdateComplete(Guid torrentId, DateTimeOffset datetime)
|
public async Task UpdateComplete(Guid torrentId, DateTimeOffset datetime)
|
||||||
{
|
{
|
||||||
await _torrentData.UpdateComplete(torrentId, datetime);
|
await _torrentData.UpdateComplete(torrentId, datetime);
|
||||||
|
|
|
||||||
|
|
@ -133,31 +133,11 @@ namespace RdtClient.Web.Controllers
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpPost]
|
||||||
[Route("Download/{id}")]
|
[Route("Retry/{id}")]
|
||||||
public async Task<ActionResult> Download(Guid id)
|
public async Task<ActionResult> Retry(Guid id, [FromBody] TorrentControllerRetryRequest request)
|
||||||
{
|
{
|
||||||
var torrent = await _torrents.GetById(id);
|
await _torrents.Retry(id, request.Retry);
|
||||||
|
|
||||||
foreach (var link in torrent.Files.Where(m => m.Selected))
|
|
||||||
{
|
|
||||||
await _downloads.Add(id, link.Path);
|
|
||||||
await _torrents.UnrestrictLink(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
[Route("Unpack/{id}")]
|
|
||||||
public async Task<ActionResult> Unpack(Guid id)
|
|
||||||
{
|
|
||||||
var downloads = await _downloads.GetForTorrent(id);
|
|
||||||
|
|
||||||
foreach (var download in downloads)
|
|
||||||
{
|
|
||||||
await _torrents.Unpack(download.DownloadId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
@ -181,6 +161,11 @@ namespace RdtClient.Web.Controllers
|
||||||
public Boolean DeleteLocalFiles { get; set; }
|
public Boolean DeleteLocalFiles { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class TorrentControllerRetryRequest
|
||||||
|
{
|
||||||
|
public Int32 Retry { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class TorrentControllerCheckFilesRequest
|
public class TorrentControllerCheckFilesRequest
|
||||||
{
|
{
|
||||||
public String MagnetLink { get; set; }
|
public String MagnetLink { get; set; }
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue