From 6bc9c06faa77a385557d7b525351434089c6d786 Mon Sep 17 00:00:00 2001 From: Cucumberrbob <128094686+Cucumberrbob@users.noreply.github.com> Date: Sun, 6 Jul 2025 21:59:36 +0100 Subject: [PATCH 1/3] add setting to delay `FinishedAction` --- .../add-new-torrent.component.html | 11 + .../add-new-torrent.component.ts | 3 + client/src/app/models/torrent.model.ts | 1 + client/src/app/torrent/torrent.component.html | 9 + server/RdtClient.Data/Data/TorrentData.cs | 1 + ...6204358_AddFinishedActionDelay.Designer.cs | 482 ++++++++++++++++++ .../20250706204358_AddFinishedActionDelay.cs | 29 ++ .../Migrations/DataContextModelSnapshot.cs | 5 +- server/RdtClient.Data/Models/Data/Torrent.cs | 1 + .../Models/Internal/DbSettings.cs | 4 + .../BackgroundServices/WatchFolderChecker.cs | 1 + .../RdtClient.Service/Services/QBittorrent.cs | 2 + .../Services/TorrentRunner.cs | 100 ++-- server/RdtClient.Service/Services/Torrents.cs | 1 + 14 files changed, 602 insertions(+), 48 deletions(-) create mode 100644 server/RdtClient.Data/Migrations/20250706204358_AddFinishedActionDelay.Designer.cs create mode 100644 server/RdtClient.Data/Migrations/20250706204358_AddFinishedActionDelay.cs diff --git a/client/src/app/add-new-torrent/add-new-torrent.component.html b/client/src/app/add-new-torrent/add-new-torrent.component.html index 1e82e1b..4ea4454 100644 --- a/client/src/app/add-new-torrent/add-new-torrent.component.html +++ b/client/src/app/add-new-torrent/add-new-torrent.component.html @@ -151,6 +151,17 @@ +
+ +
+ +
+

+ When a torrent is finished downloading on the provider, perform this action. Use this setting if you only want + to add files to your debrid provider but not download them to the host. +

+
+
diff --git a/client/src/app/add-new-torrent/add-new-torrent.component.ts b/client/src/app/add-new-torrent/add-new-torrent.component.ts index fb71bc2..55411c5 100644 --- a/client/src/app/add-new-torrent/add-new-torrent.component.ts +++ b/client/src/app/add-new-torrent/add-new-torrent.component.ts @@ -23,6 +23,7 @@ export class AddNewTorrentComponent implements OnInit { public hostDownloadAction: number = 0; public downloadAction: number = 0; public finishedAction: number = 0; + public finishedActionDelay: number = 0; public downloadMinSize: number = 0; public includeRegex: string = ''; public excludeRegex: string = ''; @@ -69,6 +70,7 @@ export class AddNewTorrentComponent implements OnInit { this.downloadAction = settings.first((m) => m.key === 'Gui:Default:OnlyDownloadAvailableFiles')?.value === true ? 1 : 0; this.finishedAction = settings.first((m) => m.key === 'Gui:Default:FinishedAction')?.value as number; + this.finishedActionDelay = settings.first(m => m.key =="Gui:Default:FinishedActionDelay")?.value as number; this.downloadMinSize = settings.first((m) => m.key === 'Gui:Default:MinFileSize')?.value as number; this.includeRegex = settings.first((m) => m.key === 'Gui:Default:IncludeRegex')?.value as string; this.excludeRegex = settings.first((m) => m.key === 'Gui:Default:ExcludeRegex')?.value as string; @@ -155,6 +157,7 @@ export class AddNewTorrentComponent implements OnInit { torrent.hostDownloadAction = this.hostDownloadAction; torrent.downloadAction = this.downloadAction; torrent.finishedAction = this.finishedAction; + torrent.finishedActionDelay = this.finishedActionDelay; torrent.downloadMinSize = this.downloadMinSize; torrent.includeRegex = this.includeRegex; torrent.excludeRegex = this.excludeRegex; diff --git a/client/src/app/models/torrent.model.ts b/client/src/app/models/torrent.model.ts index 9ac34be..7ef1646 100644 --- a/client/src/app/models/torrent.model.ts +++ b/client/src/app/models/torrent.model.ts @@ -8,6 +8,7 @@ export class Torrent { public hostDownloadAction: number; public downloadAction: number; public finishedAction: number; + public finishedActionDelay: number; public downloadMinSize: number; public includeRegex: string; public excludeRegex: string; diff --git a/client/src/app/torrent/torrent.component.html b/client/src/app/torrent/torrent.component.html index fc0521a..1c51e0a 100644 --- a/client/src/app/torrent/torrent.component.html +++ b/client/src/app/torrent/torrent.component.html @@ -116,6 +116,15 @@ } }
+
+ + {{ torrent.finishedActionDelay || 0 }} + @if (torrent.finishedActionDelay === 1) { + minute + } @else { + minutes + } +
{{ torrent.downloadMinSize }}MB diff --git a/server/RdtClient.Data/Data/TorrentData.cs b/server/RdtClient.Data/Data/TorrentData.cs index 5b74f63..e3484a1 100644 --- a/server/RdtClient.Data/Data/TorrentData.cs +++ b/server/RdtClient.Data/Data/TorrentData.cs @@ -86,6 +86,7 @@ public class TorrentData(DataContext dataContext) : ITorrentData Hash = hash.ToLower(), Category = torrent.Category, HostDownloadAction = torrent.HostDownloadAction, + FinishedActionDelay = torrent.FinishedActionDelay, DownloadAction = torrent.DownloadAction, FinishedAction = torrent.FinishedAction, DownloadMinSize = torrent.DownloadMinSize, diff --git a/server/RdtClient.Data/Migrations/20250706204358_AddFinishedActionDelay.Designer.cs b/server/RdtClient.Data/Migrations/20250706204358_AddFinishedActionDelay.Designer.cs new file mode 100644 index 0000000..626839f --- /dev/null +++ b/server/RdtClient.Data/Migrations/20250706204358_AddFinishedActionDelay.Designer.cs @@ -0,0 +1,482 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RdtClient.Data.Data; + +#nullable disable + +namespace RdtClient.Data.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20250706204358_AddFinishedActionDelay")] + partial class AddFinishedActionDelay + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.5"); + + 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", (string)null); + }); + + 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", (string)null); + }); + + 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", (string)null); + }); + + 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", (string)null); + }); + + 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", (string)null); + }); + + 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", (string)null); + }); + + 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", (string)null); + }); + + 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("FileName") + .HasColumnType("TEXT"); + + b.Property("Link") + .HasColumnType("TEXT"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemoteId") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + 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("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("Category") + .HasColumnType("TEXT"); + + b.Property("ClientKind") + .HasColumnType("INTEGER"); + + b.Property("Completed") + .HasColumnType("TEXT"); + + b.Property("DeleteOnError") + .HasColumnType("INTEGER"); + + b.Property("DownloadAction") + .HasColumnType("INTEGER"); + + b.Property("DownloadClient") + .HasColumnType("INTEGER"); + + b.Property("DownloadManualFiles") + .HasColumnType("TEXT"); + + b.Property("DownloadMinSize") + .HasColumnType("INTEGER"); + + b.Property("DownloadRetryAttempts") + .HasColumnType("INTEGER"); + + b.Property("Error") + .HasColumnType("TEXT"); + + b.Property("ExcludeRegex") + .HasColumnType("TEXT"); + + b.Property("FileOrMagnet") + .HasColumnType("TEXT"); + + b.Property("FilesSelected") + .HasColumnType("TEXT"); + + b.Property("FinishedAction") + .HasColumnType("INTEGER"); + + b.Property("FinishedActionDelay") + .HasColumnType("INTEGER"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("HostDownloadAction") + .HasColumnType("INTEGER"); + + b.Property("IncludeRegex") + .HasColumnType("TEXT"); + + b.Property("IsFile") + .HasColumnType("INTEGER"); + + b.Property("Lifetime") + .HasColumnType("INTEGER"); + + b.Property("Priority") + .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.Property("Retry") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("TorrentRetryAttempts") + .HasColumnType("INTEGER"); + + 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/20250706204358_AddFinishedActionDelay.cs b/server/RdtClient.Data/Migrations/20250706204358_AddFinishedActionDelay.cs new file mode 100644 index 0000000..ba7192b --- /dev/null +++ b/server/RdtClient.Data/Migrations/20250706204358_AddFinishedActionDelay.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace RdtClient.Data.Migrations +{ + /// + public partial class AddFinishedActionDelay : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "FinishedActionDelay", + table: "Torrents", + type: "INTEGER", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "FinishedActionDelay", + table: "Torrents"); + } + } +} diff --git a/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs b/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs index f9a2002..8dfc00b 100644 --- a/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs +++ b/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs @@ -15,7 +15,7 @@ namespace RdtClient.Data.Migrations protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.0"); + modelBuilder.HasAnnotation("ProductVersion", "9.0.5"); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { @@ -332,6 +332,9 @@ namespace RdtClient.Data.Migrations b.Property("FinishedAction") .HasColumnType("INTEGER"); + b.Property("FinishedActionDelay") + .HasColumnType("INTEGER"); + b.Property("Hash") .IsRequired() .HasColumnType("TEXT"); diff --git a/server/RdtClient.Data/Models/Data/Torrent.cs b/server/RdtClient.Data/Models/Data/Torrent.cs index 57efdbf..6c51b50 100644 --- a/server/RdtClient.Data/Models/Data/Torrent.cs +++ b/server/RdtClient.Data/Models/Data/Torrent.cs @@ -17,6 +17,7 @@ public class Torrent public TorrentDownloadAction DownloadAction { get; set; } public TorrentFinishedAction FinishedAction { get; set; } + public Int32 FinishedActionDelay { get; set; } public TorrentHostDownloadAction HostDownloadAction { get; set; } public Int32 DownloadMinSize { get; set; } public String? IncludeRegex { get; set; } diff --git a/server/RdtClient.Data/Models/Internal/DbSettings.cs b/server/RdtClient.Data/Models/Internal/DbSettings.cs index dae9217..453ab22 100644 --- a/server/RdtClient.Data/Models/Internal/DbSettings.cs +++ b/server/RdtClient.Data/Models/Internal/DbSettings.cs @@ -269,6 +269,10 @@ public class DbSettingsDefaultsWithCategory : DbSettingsDefaults [DisplayName("Post Download Action")] [Description("When all files are downloaded from the provider to the host, perform this action. Does not apply when using the symlink downloader.")] public TorrentFinishedAction FinishedAction { get; set; } = TorrentFinishedAction.RemoveAllTorrents; + + [DisplayName("Finished Action Delay")] + [Description("When all files are downloaded from the provider to the host, wait this many minutes before performing the action above.")] + public Int32 FinishedActionDelay { get; set; } = 0; } public class DbSettingsDefaults diff --git a/server/RdtClient.Service/BackgroundServices/WatchFolderChecker.cs b/server/RdtClient.Service/BackgroundServices/WatchFolderChecker.cs index ba369e2..4518c12 100644 --- a/server/RdtClient.Service/BackgroundServices/WatchFolderChecker.cs +++ b/server/RdtClient.Service/BackgroundServices/WatchFolderChecker.cs @@ -82,6 +82,7 @@ public class WatchFolderChecker(ILogger logger, IServiceProv DownloadClient = Settings.Get.DownloadClient.Client, Category = Settings.Get.Watch.Default.Category, HostDownloadAction = Settings.Get.Watch.Default.HostDownloadAction, + FinishedActionDelay = Settings.Get.Watch.Default.FinishedActionDelay, DownloadAction = Settings.Get.Watch.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll, diff --git a/server/RdtClient.Service/Services/QBittorrent.cs b/server/RdtClient.Service/Services/QBittorrent.cs index 03e7a4a..4cd1513 100644 --- a/server/RdtClient.Service/Services/QBittorrent.cs +++ b/server/RdtClient.Service/Services/QBittorrent.cs @@ -462,6 +462,7 @@ public class QBittorrent(ILogger logger, Settings settings, Authent Category = category, DownloadClient = Settings.Get.DownloadClient.Client, HostDownloadAction = Settings.Get.Integrations.Default.HostDownloadAction, + FinishedActionDelay = Settings.Get.Integrations.Default.FinishedActionDelay, DownloadAction = Settings.Get.Integrations.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll, FinishedAction = TorrentFinishedAction.None, DownloadMinSize = Settings.Get.Integrations.Default.MinFileSize, @@ -486,6 +487,7 @@ public class QBittorrent(ILogger logger, Settings settings, Authent Category = category, DownloadClient = Settings.Get.DownloadClient.Client, HostDownloadAction = Settings.Get.Integrations.Default.HostDownloadAction, + FinishedActionDelay = Settings.Get.Integrations.Default.FinishedActionDelay, DownloadAction = Settings.Get.Integrations.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll, FinishedAction = TorrentFinishedAction.None, DownloadMinSize = Settings.Get.Integrations.Default.MinFileSize, diff --git a/server/RdtClient.Service/Services/TorrentRunner.cs b/server/RdtClient.Service/Services/TorrentRunner.cs index e7ba302..92968c0 100644 --- a/server/RdtClient.Service/Services/TorrentRunner.cs +++ b/server/RdtClient.Service/Services/TorrentRunner.cs @@ -348,14 +348,64 @@ public class TorrentRunner(ILogger logger, Torrents torrents, Dow allTorrents = await torrents.Get(); - allTorrents = allTorrents.Where(m => m.Completed == null).ToList(); + var completeTorrents = allTorrents.Where(m => m.Completed != null); + var torrentsToDelete = completeTorrents.Where(m => DateTime.UtcNow >= m.Completed?.AddMinutes(m.FinishedActionDelay) && m.Error == null); - if (allTorrents.Count > 0) + foreach (var torrent in torrentsToDelete) + { + if (torrent.DownloadClient == Data.Enums.DownloadClient.Symlink) + { + switch (torrent.FinishedAction) + { + case TorrentFinishedAction.RemoveAllTorrents: + Log($"Force setting FinishedAction to RemoveClient as download client is Symlink and FinishedAction is RemoveAllTorrents", torrent); + torrent.FinishedAction = TorrentFinishedAction.RemoveClient; + + break; + case TorrentFinishedAction.RemoveRealDebrid: + Log($"Force setting FinishedAction to TorrentFinishedAction.None as download client is Symlink and FinishedAction is RemoveRealDebrid", torrent); + torrent.FinishedAction = TorrentFinishedAction.None; + + break; + } + } + + switch (torrent.FinishedAction) + { + case TorrentFinishedAction.RemoveAllTorrents: + Log($"Removing torrents from debrid provider and RDT-Client, no files", torrent); + await torrents.Delete(torrent.TorrentId, true, true, false); + + break; + case TorrentFinishedAction.RemoveRealDebrid: + Log($"Removing torrents from debrid provider, no files", torrent); + await torrents.Delete(torrent.TorrentId, false, true, false); + + break; + case TorrentFinishedAction.RemoveClient: + Log($"Removing torrents from client, no files", torrent); + await torrents.Delete(torrent.TorrentId, true, false, false); + + break; + case TorrentFinishedAction.None: + Log($"Not removing torrents or files", torrent); + + break; + default: + Log($"Invalid torrent FinishedAction {torrent.FinishedAction}", torrent); + + break; + } + } + + var incompleteTorrents = allTorrents.Where(m => m.Completed == null).ToList(); + + if (incompleteTorrents.Count > 0) { Log($"Processing {allTorrents.Count} torrents"); } - foreach (var torrent in allTorrents) + foreach (var torrent in incompleteTorrents) { try { @@ -611,50 +661,6 @@ public class TorrentRunner(ILogger logger, Torrents torrents, Dow { logger.LogError(ex.Message, "Unable to run post process: {Message}", ex.Message); } - - if (torrent.DownloadClient == Data.Enums.DownloadClient.Symlink) - { - switch (torrent.FinishedAction) - { - case TorrentFinishedAction.RemoveAllTorrents: - Log($"Force setting FinishedAction to RemoveClient as download client is Symlink and FinishedAction is RemoveAllTorrents", torrent); - torrent.FinishedAction = TorrentFinishedAction.RemoveClient; - - break; - case TorrentFinishedAction.RemoveRealDebrid: - Log($"Force setting FinishedAction to TorrentFinishedAction.None as download client is Symlink and FinishedAction is RemoveRealDebrid", torrent); - torrent.FinishedAction = TorrentFinishedAction.None; - - break; - } - } - - switch (torrent.FinishedAction) - { - case TorrentFinishedAction.RemoveAllTorrents: - Log($"Removing torrents from debrid provider and RDT-Client, no files", torrent); - await torrents.Delete(torrent.TorrentId, true, true, false); - - break; - case TorrentFinishedAction.RemoveRealDebrid: - Log($"Removing torrents from debrid provider, no files", torrent); - await torrents.Delete(torrent.TorrentId, false, true, false); - - break; - case TorrentFinishedAction.RemoveClient: - Log($"Removing torrents from client, no files", torrent); - await torrents.Delete(torrent.TorrentId, true, false, false); - - break; - case TorrentFinishedAction.None: - Log($"Not removing torrents or files", torrent); - - break; - default: - Log($"Invalid torrent FinishedAction {torrent.FinishedAction}", torrent); - - break; - } } else { diff --git a/server/RdtClient.Service/Services/Torrents.cs b/server/RdtClient.Service/Services/Torrents.cs index 8d4be9b..8c20498 100644 --- a/server/RdtClient.Service/Services/Torrents.cs +++ b/server/RdtClient.Service/Services/Torrents.cs @@ -514,6 +514,7 @@ public class Torrents( DownloadClient = Settings.Get.DownloadClient.Client, DownloadAction = Settings.Get.Provider.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll, HostDownloadAction = Settings.Get.Provider.Default.HostDownloadAction, + FinishedActionDelay = Settings.Get.Provider.Default.FinishedActionDelay, FinishedAction = Settings.Get.Provider.Default.FinishedAction, DownloadMinSize = Settings.Get.Provider.Default.MinFileSize, IncludeRegex = Settings.Get.Provider.Default.IncludeRegex, From 1be3e98ad7eda878b64ddf96b1dd1e23d3d2cd7b Mon Sep 17 00:00:00 2001 From: Cucumberrbob <128094686+Cucumberrbob@users.noreply.github.com> Date: Sun, 6 Jul 2025 22:05:29 +0100 Subject: [PATCH 2/3] Update server/RdtClient.Service/Services/TorrentRunner.cs Co-authored-by: qodo-merge-pro[bot] <151058649+qodo-merge-pro[bot]@users.noreply.github.com> --- server/RdtClient.Service/Services/TorrentRunner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/RdtClient.Service/Services/TorrentRunner.cs b/server/RdtClient.Service/Services/TorrentRunner.cs index 92968c0..6d67817 100644 --- a/server/RdtClient.Service/Services/TorrentRunner.cs +++ b/server/RdtClient.Service/Services/TorrentRunner.cs @@ -349,7 +349,7 @@ public class TorrentRunner(ILogger logger, Torrents torrents, Dow allTorrents = await torrents.Get(); var completeTorrents = allTorrents.Where(m => m.Completed != null); - var torrentsToDelete = completeTorrents.Where(m => DateTime.UtcNow >= m.Completed?.AddMinutes(m.FinishedActionDelay) && m.Error == null); + var torrentsToDelete = completeTorrents.Where(m => DateTimeOffset.UtcNow >= m.Completed?.AddMinutes(m.FinishedActionDelay) && m.Error == null); foreach (var torrent in torrentsToDelete) { From 30b9b9a4adc8cbd41cb8715c2ca41a31f5c5b6fc Mon Sep 17 00:00:00 2001 From: Cucumberrbob <128094686+Cucumberrbob@users.noreply.github.com> Date: Sun, 6 Jul 2025 22:08:28 +0100 Subject: [PATCH 3/3] run prettier --- client/src/app/add-new-torrent/add-new-torrent.component.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/app/add-new-torrent/add-new-torrent.component.ts b/client/src/app/add-new-torrent/add-new-torrent.component.ts index 55411c5..828d042 100644 --- a/client/src/app/add-new-torrent/add-new-torrent.component.ts +++ b/client/src/app/add-new-torrent/add-new-torrent.component.ts @@ -50,11 +50,11 @@ export class AddNewTorrentComponent implements OnInit { private router: Router, private torrentService: TorrentService, private settingsService: SettingsService, - private activatedRoute: ActivatedRoute + private activatedRoute: ActivatedRoute, ) {} ngOnInit(): void { - this.activatedRoute.queryParams.subscribe(params => { + this.activatedRoute.queryParams.subscribe((params) => { if (params['magnet']) { this.magnetLink = decodeURIComponent(params['magnet']); } @@ -70,7 +70,7 @@ export class AddNewTorrentComponent implements OnInit { this.downloadAction = settings.first((m) => m.key === 'Gui:Default:OnlyDownloadAvailableFiles')?.value === true ? 1 : 0; this.finishedAction = settings.first((m) => m.key === 'Gui:Default:FinishedAction')?.value as number; - this.finishedActionDelay = settings.first(m => m.key =="Gui:Default:FinishedActionDelay")?.value as number; + this.finishedActionDelay = settings.first((m) => m.key == 'Gui:Default:FinishedActionDelay')?.value as number; this.downloadMinSize = settings.first((m) => m.key === 'Gui:Default:MinFileSize')?.value as number; this.includeRegex = settings.first((m) => m.key === 'Gui:Default:IncludeRegex')?.value as string; this.excludeRegex = settings.first((m) => m.key === 'Gui:Default:ExcludeRegex')?.value as string;