diff --git a/client/src/app/torrent-row/torrent-row.component.html b/client/src/app/torrent-row/torrent-row.component.html index cc337d2..b69355e 100644 --- a/client/src/app/torrent-row/torrent-row.component.html +++ b/client/src/app/torrent-row/torrent-row.component.html @@ -15,18 +15,10 @@ {{ torrent | status }} - - + + - - - - + diff --git a/client/src/app/torrent-row/torrent-row.component.ts b/client/src/app/torrent-row/torrent-row.component.ts index 17e5088..76554c5 100644 --- a/client/src/app/torrent-row/torrent-row.component.ts +++ b/client/src/app/torrent-row/torrent-row.component.ts @@ -1,6 +1,5 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { RealDebridStatus, Torrent } from 'src/app/models/torrent.model'; -import { TorrentService } from 'src/app/torrent.service'; +import { Torrent } from 'src/app/models/torrent.model'; @Component({ selector: '[app-torrent-row]', @@ -14,55 +13,22 @@ export class TorrentRowComponent implements OnInit { @Output('delete') public delete = new EventEmitter(); + @Output('retry') + public retry = new EventEmitter(); + public loading = false; - constructor(private torrentService: TorrentService) {} + constructor() {} ngOnInit(): void {} - public download(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 { + public deleteClick(event: Event): void { event.stopPropagation(); this.delete.emit(this.torrent.torrentId); } - public canDownload(): boolean { - return ( - (this.torrent.rdStatus === RealDebridStatus.Finished && this.torrent.downloads.length === 0) || - (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; + public retryClick(event: Event): void { + event.stopPropagation(); + this.retry.emit(this.torrent.torrentId); } } diff --git a/client/src/app/torrent-table/torrent-table.component.html b/client/src/app/torrent-table/torrent-table.component.html index 12af847..ce51221 100644 --- a/client/src/app/torrent-table/torrent-table.component.html +++ b/client/src/app/torrent-table/torrent-table.component.html @@ -4,7 +4,6 @@
- @@ -23,6 +22,7 @@ [torrent]="torrent" (click)="selectTorrent(torrent)" (delete)="showDeleteModal($event)" + (retry)="showRetryModal($event)" > @@ -88,3 +88,45 @@ + + diff --git a/client/src/app/torrent-table/torrent-table.component.ts b/client/src/app/torrent-table/torrent-table.component.ts index a0b0da6..e59569b 100644 --- a/client/src/app/torrent-table/torrent-table.component.ts +++ b/client/src/app/torrent-table/torrent-table.component.ts @@ -20,6 +20,12 @@ export class TorrentTableComponent implements OnInit, OnDestroy { public deleteRdTorrent: boolean; public deleteLocalFiles: boolean; + public isRetryModalActive: boolean; + public retryError: string; + public retrying: boolean; + public retryTorrentId: string; + public retry: number; + constructor(private torrentService: TorrentService) {} ngOnInit(): void { @@ -52,6 +58,10 @@ export class TorrentTableComponent implements OnInit, OnDestroy { } public showDeleteModal(torrentId: string): void { + this.deleteData = false; + this.deleteRdTorrent = false; + this.deleteLocalFiles = false; + this.deleteTorrentId = torrentId; 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; + } + ); + } } diff --git a/client/src/app/torrent.service.ts b/client/src/app/torrent.service.ts index 7bcc20d..0360f8d 100644 --- a/client/src/app/torrent.service.ts +++ b/client/src/app/torrent.service.ts @@ -57,14 +57,6 @@ export class TorrentService { return this.http.post(`/Api/Torrents/CheckFiles`, formData); } - public download(torrentId: string): Observable { - return this.http.get(`/Api/Torrents/Download/${torrentId}`); - } - - public unpack(torrentId: string): Observable { - return this.http.get(`/Api/Torrents/Unpack/${torrentId}`); - } - public delete( torrentId: string, deleteData: boolean, @@ -77,4 +69,10 @@ export class TorrentService { deleteLocalFiles, }); } + + public retry(torrentId: string, retry: number): Observable { + return this.http.post(`/Api/Torrents/Retry/${torrentId}`, { + retry, + }); + } } diff --git a/server/RdtClient.Data/Data/TorrentData.cs b/server/RdtClient.Data/Data/TorrentData.cs index c0bfcb4..81fe9f3 100644 --- a/server/RdtClient.Data/Data/TorrentData.cs +++ b/server/RdtClient.Data/Data/TorrentData.cs @@ -12,10 +12,10 @@ namespace RdtClient.Data.Data Task> Get(); Task GetById(Guid torrentId); Task GetByHash(String hash); - Task Add(String realDebridId, String hash, String category, Boolean autoDelete); + Task Add(String realDebridId, String hash, String category, Boolean autoDelete, String fileOrMagnetContents, Boolean isFile); Task UpdateRdData(Torrent torrent); Task UpdateCategory(Guid torrentId, String category); - Task UpdateComplete(Guid torrentId, DateTimeOffset datetime); + Task UpdateComplete(Guid torrentId, DateTimeOffset? datetime); Task Delete(Guid torrentId); } @@ -78,7 +78,7 @@ namespace RdtClient.Data.Data return dbTorrent; } - public async Task Add(String realDebridId, String hash, String category, Boolean autoDelete) + public async Task Add(String realDebridId, String hash, String category, Boolean autoDelete, String fileOrMagnetContents, Boolean isFile) { var torrent = new Torrent { @@ -87,7 +87,9 @@ namespace RdtClient.Data.Data RdId = realDebridId, Hash = hash.ToLower(), Category = category, - AutoDelete = autoDelete + AutoDelete = autoDelete, + FileOrMagnet = fileOrMagnetContents, + IsFile = isFile }; await _dataContext.Torrents.AddAsync(torrent); @@ -140,7 +142,7 @@ namespace RdtClient.Data.Data 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); diff --git a/server/RdtClient.Data/Migrations/20210313145632_Torrents_Add_FileOrMagnet.Designer.cs b/server/RdtClient.Data/Migrations/20210313145632_Torrents_Add_FileOrMagnet.Designer.cs new file mode 100644 index 0000000..227f006 --- /dev/null +++ b/server/RdtClient.Data/Migrations/20210313145632_Torrents_Add_FileOrMagnet.Designer.cs @@ -0,0 +1,418 @@ +// +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("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("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", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b => + { + b.Property("DownloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Added") + .HasColumnType("TEXT"); + + b.Property("Completed") + .HasColumnType("TEXT"); + + b.Property("DownloadFinished") + .HasColumnType("TEXT"); + + b.Property("DownloadQueued") + .HasColumnType("TEXT"); + + b.Property("DownloadStarted") + .HasColumnType("TEXT"); + + b.Property("Error") + .HasColumnType("TEXT"); + + b.Property("Link") + .HasColumnType("TEXT"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("TorrentId") + .HasColumnType("TEXT"); + + b.Property("UnpackingFinished") + .HasColumnType("TEXT"); + + b.Property("UnpackingQueued") + .HasColumnType("TEXT"); + + b.Property("UnpackingStarted") + .HasColumnType("TEXT"); + + b.HasKey("DownloadId"); + + b.HasIndex("TorrentId"); + + b.ToTable("Downloads"); + }); + + modelBuilder.Entity("RdtClient.Data.Models.Data.Setting", b => + { + b.Property("SettingId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("SettingId"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b => + { + b.Property("TorrentId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Added") + .HasColumnType("TEXT"); + + b.Property("AutoDelete") + .HasColumnType("INTEGER"); + + b.Property("Category") + .HasColumnType("TEXT"); + + b.Property("Completed") + .HasColumnType("TEXT"); + + b.Property("FileOrMagnet") + .HasColumnType("TEXT"); + + b.Property("Hash") + .HasColumnType("TEXT"); + + b.Property("IsFile") + .HasColumnType("INTEGER"); + + b.Property("RdAdded") + .HasColumnType("TEXT"); + + b.Property("RdEnded") + .HasColumnType("TEXT"); + + b.Property("RdFiles") + .HasColumnType("TEXT"); + + b.Property("RdHost") + .HasColumnType("TEXT"); + + b.Property("RdId") + .HasColumnType("TEXT"); + + b.Property("RdName") + .HasColumnType("TEXT"); + + b.Property("RdProgress") + .HasColumnType("INTEGER"); + + b.Property("RdSeeders") + .HasColumnType("INTEGER"); + + b.Property("RdSize") + .HasColumnType("INTEGER"); + + b.Property("RdSpeed") + .HasColumnType("INTEGER"); + + b.Property("RdSplit") + .HasColumnType("INTEGER"); + + b.Property("RdStatus") + .HasColumnType("INTEGER"); + + b.Property("RdStatusRaw") + .HasColumnType("TEXT"); + + b.HasKey("TorrentId"); + + b.ToTable("Torrents"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", 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", 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 + } + } +} diff --git a/server/RdtClient.Data/Migrations/20210313145632_Torrents_Add_FileOrMagnet.cs b/server/RdtClient.Data/Migrations/20210313145632_Torrents_Add_FileOrMagnet.cs new file mode 100644 index 0000000..6c06d59 --- /dev/null +++ b/server/RdtClient.Data/Migrations/20210313145632_Torrents_Add_FileOrMagnet.cs @@ -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( + name: "FileOrMagnet", + table: "Torrents", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + 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"); + } + } +} diff --git a/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs b/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs index 9bee96b..ac3dcac 100644 --- a/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs +++ b/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs @@ -291,9 +291,15 @@ namespace RdtClient.Data.Migrations b.Property("Completed") .HasColumnType("TEXT"); + b.Property("FileOrMagnet") + .HasColumnType("TEXT"); + b.Property("Hash") .HasColumnType("TEXT"); + b.Property("IsFile") + .HasColumnType("INTEGER"); + b.Property("RdAdded") .HasColumnType("TEXT"); diff --git a/server/RdtClient.Data/Models/Data/Torrent.cs b/server/RdtClient.Data/Models/Data/Torrent.cs index d16cb04..d1d550e 100644 --- a/server/RdtClient.Data/Models/Data/Torrent.cs +++ b/server/RdtClient.Data/Models/Data/Torrent.cs @@ -21,6 +21,9 @@ namespace RdtClient.Data.Models.Data public DateTimeOffset? Completed { get; set; } public Boolean AutoDelete { get; set; } + + public String FileOrMagnet { get; set; } + public Boolean IsFile { get; set; } [InverseProperty("Torrent")] public IList Downloads { get; set; } diff --git a/server/RdtClient.Service/Services/TorrentRunner.cs b/server/RdtClient.Service/Services/TorrentRunner.cs index 17cd92d..16ca179 100644 --- a/server/RdtClient.Service/Services/TorrentRunner.cs +++ b/server/RdtClient.Service/Services/TorrentRunner.cs @@ -335,7 +335,7 @@ namespace RdtClient.Service.Services } // 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"); @@ -390,8 +390,6 @@ namespace RdtClient.Service.Services // RealDebrid finished downloading the torrent, process the file to host. 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. var downloadsPending = torrent.Downloads .Where(m => m.Completed == null && @@ -413,11 +411,6 @@ namespace RdtClient.Service.Services 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. var unpackingPending = torrent.Downloads diff --git a/server/RdtClient.Service/Services/Torrents.cs b/server/RdtClient.Service/Services/Torrents.cs index f24b3f4..9d49db4 100644 --- a/server/RdtClient.Service/Services/Torrents.cs +++ b/server/RdtClient.Service/Services/Torrents.cs @@ -17,7 +17,6 @@ namespace RdtClient.Service.Services public interface ITorrents { Task> Get(); - Task GetById(Guid torrentId); Task GetByHash(String hash); Task UpdateCategory(String hash, String category); Task UploadMagnet(String magnetLink, String category, Boolean autoDelete); @@ -32,6 +31,7 @@ namespace RdtClient.Service.Services Task GetProfile(); Task UpdateComplete(Guid torrentId, DateTimeOffset datetime); Task Update(); + Task Retry(Guid id, Int32 retry); } public class Torrents : ITorrents @@ -141,7 +141,7 @@ namespace RdtClient.Service.Services 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) @@ -150,7 +150,9 @@ namespace RdtClient.Service.Services 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> 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) { await _downloads.DeleteForTorrent(torrent.TorrentId); @@ -218,6 +209,37 @@ namespace RdtClient.Service.Services { 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; } - 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); if (!w) @@ -287,7 +309,7 @@ namespace RdtClient.Service.Services 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); @@ -305,8 +327,6 @@ namespace RdtClient.Service.Services if (!w) { - - 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) { await _torrentData.UpdateComplete(torrentId, datetime); diff --git a/server/RdtClient.Web/Controllers/TorrentsController.cs b/server/RdtClient.Web/Controllers/TorrentsController.cs index ea73343..77beaf2 100644 --- a/server/RdtClient.Web/Controllers/TorrentsController.cs +++ b/server/RdtClient.Web/Controllers/TorrentsController.cs @@ -132,32 +132,12 @@ namespace RdtClient.Web.Controllers return Ok(); } - - [HttpGet] - [Route("Download/{id}")] - public async Task Download(Guid id) - { - var torrent = await _torrents.GetById(id); - - 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 Unpack(Guid id) + [HttpPost] + [Route("Retry/{id}")] + public async Task Retry(Guid id, [FromBody] TorrentControllerRetryRequest request) { - var downloads = await _downloads.GetForTorrent(id); - - foreach (var download in downloads) - { - await _torrents.Unpack(download.DownloadId); - } + await _torrents.Retry(id, request.Retry); return Ok(); } @@ -181,6 +161,11 @@ namespace RdtClient.Web.Controllers public Boolean DeleteLocalFiles { get; set; } } + public class TorrentControllerRetryRequest + { + public Int32 Retry { get; set; } + } + public class TorrentControllerCheckFilesRequest { public String MagnetLink { get; set; }
Name