From c34c62f87924abc5912d1b888bd1cef227961f37 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:06:51 +1000 Subject: [PATCH 1/8] [TB] Move handling filenames from DownloadHelper to TorrentClients --- .../Helpers/DownloadHelper.cs | 8 ++---- .../TorrentClients/AllDebridTorrentClient.cs | 12 +++++++++ .../Services/TorrentClients/ITorrentClient.cs | 1 + .../TorrentClients/PremiumizeTorrentClient.cs | 13 +++++++++ .../TorrentClients/RealDebridTorrentClient.cs | 13 +++++++++ .../TorrentClients/TorBoxTorrentClient.cs | 27 +++++++++++++++++++ server/RdtClient.Service/Services/Torrents.cs | 4 ++- 7 files changed, 71 insertions(+), 7 deletions(-) diff --git a/server/RdtClient.Service/Helpers/DownloadHelper.cs b/server/RdtClient.Service/Helpers/DownloadHelper.cs index bbb1775..02d26f0 100644 --- a/server/RdtClient.Service/Helpers/DownloadHelper.cs +++ b/server/RdtClient.Service/Helpers/DownloadHelper.cs @@ -5,11 +5,11 @@ namespace RdtClient.Service.Helpers; public static class DownloadHelper { - public static String? GetDownloadPath(String downloadPath, Torrent torrent, Download download) + public static String? GetDownloadPath(String downloadPath, Torrent torrent, Download download, String? fileName) { var fileUrl = download.Link; - if (String.IsNullOrWhiteSpace(fileUrl) || torrent.RdName == null) + if (String.IsNullOrWhiteSpace(fileUrl) || torrent.RdName == null || fileName == null) { return null; } @@ -19,10 +19,6 @@ public static class DownloadHelper var uri = new Uri(fileUrl); var torrentPath = Path.Combine(downloadPath, directory); - var fileName = uri.Segments.Last(); - - fileName = HttpUtility.UrlDecode(fileName); - fileName = FileHelper.RemoveInvalidFileNameChars(fileName); var matchingTorrentFiles = torrent.Files.Where(m => m.Path.EndsWith(fileName)).Where(m => !String.IsNullOrWhiteSpace(m.Path)).ToList(); diff --git a/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs index bc0102c..540b3d3 100644 --- a/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs @@ -4,6 +4,7 @@ using Newtonsoft.Json; using RdtClient.Data.Enums; using RdtClient.Data.Models.TorrentClient; using RdtClient.Service.Helpers; +using System.Web; using File = AllDebridNET.File; using Torrent = RdtClient.Data.Models.Data.Torrent; @@ -262,6 +263,17 @@ public class AllDebridTorrentClient(ILogger logger, IHtt return links.Select(m => m.LinkUrl.ToString()).ToList(); } + public Task GetFileName(Data.Models.Data.Download download) + { + if (String.IsNullOrWhiteSpace(download.Link)) + { + return Task.FromResult(null); + } + + var uri = new Uri(download.Link); + + return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last())); + } private async Task GetInfo(String torrentId) { diff --git a/server/RdtClient.Service/Services/TorrentClients/ITorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/ITorrentClient.cs index da4aa39..95fb597 100644 --- a/server/RdtClient.Service/Services/TorrentClients/ITorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/ITorrentClient.cs @@ -15,4 +15,5 @@ public interface ITorrentClient Task Unrestrict(String link); Task UpdateData(Torrent torrent, TorrentClientTorrent? torrentClientTorrent); Task?> GetDownloadLinks(Torrent torrent); + Task GetFileName(Download download); } \ No newline at end of file diff --git a/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs index cb170c2..ab33428 100644 --- a/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs @@ -4,6 +4,7 @@ using PremiumizeNET; using RdtClient.Data.Enums; using RdtClient.Data.Models.TorrentClient; using RdtClient.Service.Helpers; +using System.Web; using Torrent = RdtClient.Data.Models.Data.Torrent; namespace RdtClient.Service.Services.TorrentClients; @@ -241,6 +242,18 @@ public class PremiumizeTorrentClient(ILogger logger, IH return downloadLinks; } + public Task GetFileName(Data.Models.Data.Download download) + { + if (String.IsNullOrWhiteSpace(download.Link)) + { + return Task.FromResult(null); + } + + var uri = new Uri(download.Link); + + return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last())); + } + private async Task GetInfo(String id) { var results = await GetClient().Transfers.ListAsync(); diff --git a/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs index 9cc8ad0..ecf4337 100644 --- a/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs @@ -1,4 +1,5 @@ using System.Text.RegularExpressions; +using System.Web; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using RDNET; @@ -395,6 +396,18 @@ public class RealDebridTorrentClient(ILogger logger, IH return null; } + public Task GetFileName(Data.Models.Data.Download download) + { + if (String.IsNullOrWhiteSpace(download.Link)) + { + return Task.FromResult(null); + } + + var uri = new Uri(download.Link); + + return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last())); + } + private DateTimeOffset? ChangeTimeZone(DateTimeOffset? dateTimeOffset) { if (_offset == null) diff --git a/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs index 088c928..485d8ce 100644 --- a/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs @@ -3,6 +3,7 @@ using Newtonsoft.Json; using TorBoxNET; using RdtClient.Data.Enums; using RdtClient.Data.Models.TorrentClient; +using System.Web; namespace RdtClient.Service.Services.TorrentClients; @@ -300,6 +301,32 @@ public class TorBoxTorrentClient(ILogger logger, IHttpClien return files; } + public async Task GetFileName(Data.Models.Data.Download download) + { + if (String.IsNullOrWhiteSpace(download.Link)) + { + return null; + } + + var uri = new Uri(download.Link); + + using (HttpClient client = new HttpClient()) + { + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Head, uri); + HttpResponseMessage response = await client.SendAsync(request); + if (response.Content.Headers.ContentDisposition != null) + { + var fileName = response.Content.Headers.ContentDisposition.FileName; + if (!String.IsNullOrWhiteSpace(fileName)) + { + return fileName.Trim('"'); + } + } + } + + return HttpUtility.UrlDecode(uri.Segments.Last()); + } + private DateTimeOffset? ChangeTimeZone(DateTimeOffset? dateTimeOffset) { if (_offset == null) diff --git a/server/RdtClient.Service/Services/Torrents.cs b/server/RdtClient.Service/Services/Torrents.cs index 25817cf..0d3d7ae 100644 --- a/server/RdtClient.Service/Services/Torrents.cs +++ b/server/RdtClient.Service/Services/Torrents.cs @@ -546,8 +546,10 @@ public class Torrents( } var downloadPath = DownloadPath(download.Torrent!); + + var fileName = await TorrentClient.GetFileName(download); - var filePath = DownloadHelper.GetDownloadPath(downloadPath, download.Torrent!, download); + var filePath = DownloadHelper.GetDownloadPath(downloadPath, download.Torrent!, download, fileName); if (filePath != null) { From dffce96a4514a92887c577fc661fca30af2ab82e Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:25:30 +1000 Subject: [PATCH 2/8] [TB] Add default for fileName in DownloadHelper --- server/RdtClient.Service/Helpers/DownloadHelper.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server/RdtClient.Service/Helpers/DownloadHelper.cs b/server/RdtClient.Service/Helpers/DownloadHelper.cs index 02d26f0..a4cec3a 100644 --- a/server/RdtClient.Service/Helpers/DownloadHelper.cs +++ b/server/RdtClient.Service/Helpers/DownloadHelper.cs @@ -5,7 +5,7 @@ namespace RdtClient.Service.Helpers; public static class DownloadHelper { - public static String? GetDownloadPath(String downloadPath, Torrent torrent, Download download, String? fileName) + public static String? GetDownloadPath(String downloadPath, Torrent torrent, Download download, String? fileName = null) { var fileUrl = download.Link; @@ -19,6 +19,13 @@ public static class DownloadHelper var uri = new Uri(fileUrl); var torrentPath = Path.Combine(downloadPath, directory); + if (String.IsNullOrWhiteSpace(fileName)) + { + fileName = uri.Segments.Last(); + + fileName = HttpUtility.UrlDecode(fileName); + } + fileName = FileHelper.RemoveInvalidFileNameChars(fileName); var matchingTorrentFiles = torrent.Files.Where(m => m.Path.EndsWith(fileName)).Where(m => !String.IsNullOrWhiteSpace(m.Path)).ToList(); From 3364deecbb96a18d8a9675f1315a2e1f31f23df9 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:25:45 +1000 Subject: [PATCH 3/8] [TB] Add download state for stalledDL --- .../Services/TorrentClients/TorBoxTorrentClient.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs index 485d8ce..07402ab 100644 --- a/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs @@ -258,6 +258,7 @@ public class TorBoxTorrentClient(ILogger logger, IHttpClien "checking" => TorrentStatus.Processing, "checkingResumeData" => TorrentStatus.Processing, "paused" => TorrentStatus.Downloading, + "stalledDL" => TorrentStatus.Downloading, "downloading" => TorrentStatus.Downloading, "completed" => TorrentStatus.Downloading, "uploading" => TorrentStatus.Downloading, From a1599ee7166ca66f54f0649854b4e62ef1bdc6a4 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Tue, 10 Dec 2024 23:37:32 +1000 Subject: [PATCH 4/8] [TB] Add FileName value to Download model --- ...0071621_Downloads_Add_FileName.Designer.cs | 476 ++++++++++++++++++ .../20241210071621_Downloads_Add_FileName.cs | 28 ++ .../Migrations/DataContextModelSnapshot.cs | 5 +- server/RdtClient.Data/Models/Data/Download.cs | 2 + .../RdtClient.Service/Services/Downloads.cs | 5 + 5 files changed, 515 insertions(+), 1 deletion(-) create mode 100644 server/RdtClient.Data/Migrations/20241210071621_Downloads_Add_FileName.Designer.cs create mode 100644 server/RdtClient.Data/Migrations/20241210071621_Downloads_Add_FileName.cs diff --git a/server/RdtClient.Data/Migrations/20241210071621_Downloads_Add_FileName.Designer.cs b/server/RdtClient.Data/Migrations/20241210071621_Downloads_Add_FileName.Designer.cs new file mode 100644 index 0000000..f7c9a3c --- /dev/null +++ b/server/RdtClient.Data/Migrations/20241210071621_Downloads_Add_FileName.Designer.cs @@ -0,0 +1,476 @@ +// +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("20241210071621_Downloads_Add_FileName")] + partial class Downloads_Add_FileName + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.0"); + + 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("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("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/20241210071621_Downloads_Add_FileName.cs b/server/RdtClient.Data/Migrations/20241210071621_Downloads_Add_FileName.cs new file mode 100644 index 0000000..94a8666 --- /dev/null +++ b/server/RdtClient.Data/Migrations/20241210071621_Downloads_Add_FileName.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace RdtClient.Data.Migrations +{ + /// + public partial class Downloads_Add_FileName : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "FileName", + table: "Downloads", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "FileName", + table: "Downloads"); + } + } +} diff --git a/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs b/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs index a3edd7b..ce27f40 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", "8.0.3"); + modelBuilder.HasAnnotation("ProductVersion", "9.0.0"); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { @@ -233,6 +233,9 @@ namespace RdtClient.Data.Migrations b.Property("Error") .HasColumnType("TEXT"); + b.Property("FileName") + .HasColumnType("TEXT"); + b.Property("Link") .HasColumnType("TEXT"); diff --git a/server/RdtClient.Data/Models/Data/Download.cs b/server/RdtClient.Data/Models/Data/Download.cs index e8e54b1..d078356 100644 --- a/server/RdtClient.Data/Models/Data/Download.cs +++ b/server/RdtClient.Data/Models/Data/Download.cs @@ -31,6 +31,8 @@ public class Download public String? RemoteId { get; set; } + public String? FileName { get; set; } + [NotMapped] public Int64 BytesTotal { get; set; } diff --git a/server/RdtClient.Service/Services/Downloads.cs b/server/RdtClient.Service/Services/Downloads.cs index e66954f..020b36d 100644 --- a/server/RdtClient.Service/Services/Downloads.cs +++ b/server/RdtClient.Service/Services/Downloads.cs @@ -30,6 +30,11 @@ public class Downloads(DownloadData downloadData) await downloadData.UpdateUnrestrictedLink(downloadId, unrestrictedLink); } + public async Task UpdateFileName(Guid downloadId, String fileName) + { + await downloadData.UpdateFileName(downloadId, fileName); + } + public async Task UpdateDownloadStarted(Guid downloadId, DateTimeOffset? dateTime) { await downloadData.UpdateDownloadStarted(downloadId, dateTime); From 350185b702607d4f59430a8ab62face65c2ef9b1 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Tue, 10 Dec 2024 23:45:13 +1000 Subject: [PATCH 5/8] [TB] Refactor GetFileName in Torrent Clients --- .../TorrentClients/AllDebridTorrentClient.cs | 6 +++--- .../Services/TorrentClients/ITorrentClient.cs | 2 +- .../TorrentClients/PremiumizeTorrentClient.cs | 6 +++--- .../TorrentClients/RealDebridTorrentClient.cs | 6 +++--- .../Services/TorrentClients/TorBoxTorrentClient.cs | 12 ++++++------ 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs index 540b3d3..3c7c789 100644 --- a/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs @@ -263,14 +263,14 @@ public class AllDebridTorrentClient(ILogger logger, IHtt return links.Select(m => m.LinkUrl.ToString()).ToList(); } - public Task GetFileName(Data.Models.Data.Download download) + public Task GetFileName(String downloadUrl) { - if (String.IsNullOrWhiteSpace(download.Link)) + if (String.IsNullOrWhiteSpace(downloadUrl)) { return Task.FromResult(null); } - var uri = new Uri(download.Link); + var uri = new Uri(downloadUrl); return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last())); } diff --git a/server/RdtClient.Service/Services/TorrentClients/ITorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/ITorrentClient.cs index 95fb597..2813da8 100644 --- a/server/RdtClient.Service/Services/TorrentClients/ITorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/ITorrentClient.cs @@ -15,5 +15,5 @@ public interface ITorrentClient Task Unrestrict(String link); Task UpdateData(Torrent torrent, TorrentClientTorrent? torrentClientTorrent); Task?> GetDownloadLinks(Torrent torrent); - Task GetFileName(Download download); + Task GetFileName(String downloadUrl); } \ No newline at end of file diff --git a/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs index ab33428..b91790b 100644 --- a/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs @@ -242,14 +242,14 @@ public class PremiumizeTorrentClient(ILogger logger, IH return downloadLinks; } - public Task GetFileName(Data.Models.Data.Download download) + public Task GetFileName(String downloadUrl) { - if (String.IsNullOrWhiteSpace(download.Link)) + if (String.IsNullOrWhiteSpace(downloadUrl)) { return Task.FromResult(null); } - var uri = new Uri(download.Link); + var uri = new Uri(downloadUrl); return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last())); } diff --git a/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs index ecf4337..459cb7a 100644 --- a/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs @@ -396,14 +396,14 @@ public class RealDebridTorrentClient(ILogger logger, IH return null; } - public Task GetFileName(Data.Models.Data.Download download) + public Task GetFileName(String downloadUrl) { - if (String.IsNullOrWhiteSpace(download.Link)) + if (String.IsNullOrWhiteSpace(downloadUrl)) { return Task.FromResult(null); } - var uri = new Uri(download.Link); + var uri = new Uri(downloadUrl); return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last())); } diff --git a/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs index 07402ab..6ebe877 100644 --- a/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs @@ -161,7 +161,7 @@ public class TorBoxTorrentClient(ILogger logger, IHttpClien { var availability = await GetClient().Torrents.GetAvailabilityAsync(hash, listFiles: true); - if (availability.Data != null) + if (availability.Data != null || availability.Data?.Count < 0) { return (availability.Data[0]?.Files ?? []).Select(file => new TorrentClientAvailableFile { @@ -302,18 +302,18 @@ public class TorBoxTorrentClient(ILogger logger, IHttpClien return files; } - public async Task GetFileName(Data.Models.Data.Download download) + public async Task GetFileName(String downloadUrl) { - if (String.IsNullOrWhiteSpace(download.Link)) + if (String.IsNullOrWhiteSpace(downloadUrl)) { return null; } - var uri = new Uri(download.Link); + var uri = new Uri(downloadUrl); - using (HttpClient client = new HttpClient()) + using (HttpClient client = new()) { - HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Head, uri); + HttpRequestMessage request = new(HttpMethod.Head, uri); HttpResponseMessage response = await client.SendAsync(request); if (response.Content.Headers.ContentDisposition != null) { From 9d03c20aaafa4036fbc41b85feb8d7067ac6e087 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Tue, 10 Dec 2024 23:47:55 +1000 Subject: [PATCH 6/8] [TB] Change DownloadHelper to use download.FileName Also adds the UpdateFileName function to DownloadHelper & DownloadData to push FileName to DB --- server/RdtClient.Data/Data/DownloadData.cs | 17 +++++++++++++++++ .../RdtClient.Service/Helpers/DownloadHelper.cs | 15 ++++++++++----- .../RdtClient.Service/Services/TorrentRunner.cs | 3 +++ server/RdtClient.Service/Services/Torrents.cs | 17 ++++++++++++++--- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/server/RdtClient.Data/Data/DownloadData.cs b/server/RdtClient.Data/Data/DownloadData.cs index 4488b9d..9d141f9 100644 --- a/server/RdtClient.Data/Data/DownloadData.cs +++ b/server/RdtClient.Data/Data/DownloadData.cs @@ -67,6 +67,23 @@ public class DownloadData(DataContext dataContext) await TorrentData.VoidCache(); } + public async Task UpdateFileName(Guid downloadId, String fileName) + { + var dbDownload = await dataContext.Downloads + .FirstOrDefaultAsync(m => m.DownloadId == downloadId); + + if (dbDownload == null) + { + return; + } + + dbDownload.FileName = fileName; + + await dataContext.SaveChangesAsync(); + + await TorrentData.VoidCache(); + } + public async Task UpdateDownloadStarted(Guid downloadId, DateTimeOffset? dateTime) { var dbDownload = await dataContext.Downloads diff --git a/server/RdtClient.Service/Helpers/DownloadHelper.cs b/server/RdtClient.Service/Helpers/DownloadHelper.cs index a4cec3a..06724ad 100644 --- a/server/RdtClient.Service/Helpers/DownloadHelper.cs +++ b/server/RdtClient.Service/Helpers/DownloadHelper.cs @@ -5,11 +5,11 @@ namespace RdtClient.Service.Helpers; public static class DownloadHelper { - public static String? GetDownloadPath(String downloadPath, Torrent torrent, Download download, String? fileName = null) + public static String? GetDownloadPath(String downloadPath, Torrent torrent, Download download) { var fileUrl = download.Link; - if (String.IsNullOrWhiteSpace(fileUrl) || torrent.RdName == null || fileName == null) + if (String.IsNullOrWhiteSpace(fileUrl) || torrent.RdName == null) { return null; } @@ -19,6 +19,8 @@ public static class DownloadHelper var uri = new Uri(fileUrl); var torrentPath = Path.Combine(downloadPath, directory); + var fileName = download.FileName; + if (String.IsNullOrWhiteSpace(fileName)) { fileName = uri.Segments.Last(); @@ -66,11 +68,14 @@ public static class DownloadHelper var uri = new Uri(fileUrl); var torrentPath = RemoveInvalidPathChars(torrent.RdName); - var fileName = uri.Segments.Last(); + var fileName = download.FileName; - fileName = HttpUtility.UrlDecode(fileName); + if (String.IsNullOrWhiteSpace(fileName)) + { + fileName = uri.Segments.Last(); - fileName = FileHelper.RemoveInvalidFileNameChars(fileName); + fileName = HttpUtility.UrlDecode(fileName); + } var matchingTorrentFiles = torrent.Files.Where(m => m.Path.EndsWith(fileName)).Where(m => !String.IsNullOrWhiteSpace(m.Path)).ToList(); diff --git a/server/RdtClient.Service/Services/TorrentRunner.cs b/server/RdtClient.Service/Services/TorrentRunner.cs index 6b55c83..0774d0c 100644 --- a/server/RdtClient.Service/Services/TorrentRunner.cs +++ b/server/RdtClient.Service/Services/TorrentRunner.cs @@ -349,6 +349,9 @@ public class TorrentRunner(ILogger logger, Torrents torrents, Dow var downloadLink = await torrents.UnrestrictLink(download.DownloadId); download.Link = downloadLink; + + var fileName = await torrents.RetrieveFileName(download.DownloadId); + download.FileName = fileName; } } catch (Exception ex) diff --git a/server/RdtClient.Service/Services/Torrents.cs b/server/RdtClient.Service/Services/Torrents.cs index 0d3d7ae..4f7b0a5 100644 --- a/server/RdtClient.Service/Services/Torrents.cs +++ b/server/RdtClient.Service/Services/Torrents.cs @@ -370,6 +370,19 @@ public class Torrents( return unrestrictedLink; } + public async Task RetrieveFileName(Guid downloadId) + { + var download = await downloads.GetById(downloadId) ?? throw new($"Download with ID {downloadId} not found"); + + Log($"Unrestricting link", download, download.Torrent); + + var fileName = await TorrentClient.GetFileName(download.Link!); + + await downloads.UpdateFileName(downloadId, fileName); + + return fileName; + } + public async Task GetProfile() { var user = await TorrentClient.GetUser(); @@ -547,9 +560,7 @@ public class Torrents( var downloadPath = DownloadPath(download.Torrent!); - var fileName = await TorrentClient.GetFileName(download); - - var filePath = DownloadHelper.GetDownloadPath(downloadPath, download.Torrent!, download, fileName); + var filePath = DownloadHelper.GetDownloadPath(downloadPath, download.Torrent!, download); if (filePath != null) { From ac6300a5040da6d613d7a0ad1f0af70117e9af6f Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Wed, 11 Dec 2024 00:03:43 +1000 Subject: [PATCH 7/8] [TB] Replace null with empty string for filenames in TorrentClient Not sure if its better to do this or allow null value to db, but i had this originally and it worked so i think im better off leaving it for now, but idk. Also got fix for incorrect log --- .../Services/TorrentClients/AllDebridTorrentClient.cs | 6 +++--- .../Services/TorrentClients/ITorrentClient.cs | 2 +- .../Services/TorrentClients/PremiumizeTorrentClient.cs | 6 +++--- .../Services/TorrentClients/RealDebridTorrentClient.cs | 6 +++--- .../Services/TorrentClients/TorBoxTorrentClient.cs | 4 ++-- server/RdtClient.Service/Services/Torrents.cs | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs index 3c7c789..dd3f424 100644 --- a/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs @@ -263,16 +263,16 @@ public class AllDebridTorrentClient(ILogger logger, IHtt return links.Select(m => m.LinkUrl.ToString()).ToList(); } - public Task GetFileName(String downloadUrl) + public Task GetFileName(String downloadUrl) { if (String.IsNullOrWhiteSpace(downloadUrl)) { - return Task.FromResult(null); + return Task.FromResult(""); } var uri = new Uri(downloadUrl); - return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last())); + return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last())); } private async Task GetInfo(String torrentId) diff --git a/server/RdtClient.Service/Services/TorrentClients/ITorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/ITorrentClient.cs index 2813da8..38121ae 100644 --- a/server/RdtClient.Service/Services/TorrentClients/ITorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/ITorrentClient.cs @@ -15,5 +15,5 @@ public interface ITorrentClient Task Unrestrict(String link); Task UpdateData(Torrent torrent, TorrentClientTorrent? torrentClientTorrent); Task?> GetDownloadLinks(Torrent torrent); - Task GetFileName(String downloadUrl); + Task GetFileName(String downloadUrl); } \ No newline at end of file diff --git a/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs index b91790b..4a1745b 100644 --- a/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs @@ -242,16 +242,16 @@ public class PremiumizeTorrentClient(ILogger logger, IH return downloadLinks; } - public Task GetFileName(String downloadUrl) + public Task GetFileName(String downloadUrl) { if (String.IsNullOrWhiteSpace(downloadUrl)) { - return Task.FromResult(null); + return Task.FromResult(""); } var uri = new Uri(downloadUrl); - return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last())); + return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last())); } private async Task GetInfo(String id) diff --git a/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs index 459cb7a..42b06d2 100644 --- a/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs @@ -396,16 +396,16 @@ public class RealDebridTorrentClient(ILogger logger, IH return null; } - public Task GetFileName(String downloadUrl) + public Task GetFileName(String downloadUrl) { if (String.IsNullOrWhiteSpace(downloadUrl)) { - return Task.FromResult(null); + return Task.FromResult(""); } var uri = new Uri(downloadUrl); - return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last())); + return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last())); } private DateTimeOffset? ChangeTimeZone(DateTimeOffset? dateTimeOffset) diff --git a/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs index 6ebe877..1782391 100644 --- a/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs @@ -302,11 +302,11 @@ public class TorBoxTorrentClient(ILogger logger, IHttpClien return files; } - public async Task GetFileName(String downloadUrl) + public async Task GetFileName(String downloadUrl) { if (String.IsNullOrWhiteSpace(downloadUrl)) { - return null; + return ""; } var uri = new Uri(downloadUrl); diff --git a/server/RdtClient.Service/Services/Torrents.cs b/server/RdtClient.Service/Services/Torrents.cs index 4f7b0a5..28a45b2 100644 --- a/server/RdtClient.Service/Services/Torrents.cs +++ b/server/RdtClient.Service/Services/Torrents.cs @@ -374,7 +374,7 @@ public class Torrents( { var download = await downloads.GetById(downloadId) ?? throw new($"Download with ID {downloadId} not found"); - Log($"Unrestricting link", download, download.Torrent); + Log($"Retrieving filename for", download, download.Torrent); var fileName = await TorrentClient.GetFileName(download.Link!); From e92b197ddb1f9fac55a3b9807a292772ce0f8427 Mon Sep 17 00:00:00 2001 From: Sam Heinz <54530346+asylumexp@users.noreply.github.com> Date: Wed, 11 Dec 2024 18:59:21 +1000 Subject: [PATCH 8/8] [TB] Fix UI errors --- client/src/app/setup/setup.component.html | 1 + server/RdtClient.Data/Models/Internal/DbSettings.cs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/src/app/setup/setup.component.html b/client/src/app/setup/setup.component.html index 643e618..28c81e3 100644 --- a/client/src/app/setup/setup.component.html +++ b/client/src/app/setup/setup.component.html @@ -55,6 +55,7 @@ Use this link to sign up to Premiumize. +
Use this link to sign up to TorBox.
diff --git a/server/RdtClient.Data/Models/Internal/DbSettings.cs b/server/RdtClient.Data/Models/Internal/DbSettings.cs index 9402b33..a641f13 100644 --- a/server/RdtClient.Data/Models/Internal/DbSettings.cs +++ b/server/RdtClient.Data/Models/Internal/DbSettings.cs @@ -152,11 +152,11 @@ At this point only 1 provider can be used at the time.")] [Description(@"You can find your API key here: https://real-debrid.com/apitoken or -https://alldebrid.com/apikeys/ +https://alldebrid.com/apikeys/ or https://www.premiumize.me/account/ or -https://torbox.app/settings/")] +https://torbox.app/settings/")] public String ApiKey { get; set; } = ""; [DisplayName("Automatically import and process torrents added to provider")]