From 767038528af511451cf9e73a5ea294aeee99a82c Mon Sep 17 00:00:00 2001 From: Roger Far Date: Wed, 27 May 2026 22:04:30 -0600 Subject: [PATCH] Cleanup. --- server/RdtClient.Data/Data/DownloadData.cs | 5 +- server/RdtClient.Data/Data/TorrentData.cs | 10 +-- server/RdtClient.Data/Models/Data/Torrent.cs | 1 + .../WatchFolderCheckerTests.cs | 2 + .../Regression/TorrentDownloadRaceTests.cs | 1 + .../Services/QBittorrentTest.cs | 43 ++++++++----- .../Services/TorrentRunnerTest.cs | 10 +-- .../DebridClients/PremiumizeDebridClient.cs | 13 ++-- .../DebridClients/RealDebridDebridClient.cs | 3 +- .../DebridClients/TorBoxDebridClient.cs | 27 +++++--- .../RdtClient.Service/Services/QBittorrent.cs | 34 +++++------ .../Services/TorrentRunner.cs | 6 +- server/RdtClient.Service/Services/Torrents.cs | 25 ++++---- .../Controllers/QBittorrentControllerTest.cs | 61 ++++++++++--------- .../Controllers/SabnzbdHandlerTest.cs | 8 +++ .../Controllers/QBittorrentController.cs | 19 +++--- 16 files changed, 157 insertions(+), 111 deletions(-) diff --git a/server/RdtClient.Data/Data/DownloadData.cs b/server/RdtClient.Data/Data/DownloadData.cs index 7e944d9..17e0e56 100644 --- a/server/RdtClient.Data/Data/DownloadData.cs +++ b/server/RdtClient.Data/Data/DownloadData.cs @@ -74,6 +74,7 @@ public class DownloadData(DataContext dataContext, ILogger? logger return DownloadAddResult.Added; } + // These shouldn't be possible any longer, but added for safety and until confirmed. catch (DbUpdateException ex) { @@ -88,7 +89,9 @@ public class DownloadData(DataContext dataContext, ILogger? logger if (IsForeignKeyViolation(ex) && !await dataContext.Torrents.AsNoTracking().AnyAsync(m => m.TorrentId == torrentId)) { - logger?.LogDebug("Skipped download creation after the torrent was deleted concurrently. TorrentId: {torrentId}, Path: {path}", torrentId, downloadInfo.RestrictedLink); + logger?.LogDebug("Skipped download creation after the torrent was deleted concurrently. TorrentId: {torrentId}, Path: {path}", + torrentId, + downloadInfo.RestrictedLink); return DownloadAddResult.TorrentMissing; } diff --git a/server/RdtClient.Data/Data/TorrentData.cs b/server/RdtClient.Data/Data/TorrentData.cs index 45389a0..b34a4b0 100644 --- a/server/RdtClient.Data/Data/TorrentData.cs +++ b/server/RdtClient.Data/Data/TorrentData.cs @@ -10,11 +10,11 @@ public class TorrentData(DataContext dataContext, ILogger? logger = public async Task> Get() { var torrents = await dataContext.Torrents - .AsNoTracking() - .AsSplitQuery() - .Include(m => m.Downloads) - .OrderBy(m => m.Priority ?? 9999) - .ToListAsync(); + .AsNoTracking() + .AsSplitQuery() + .Include(m => m.Downloads) + .OrderBy(m => m.Priority ?? 9999) + .ToListAsync(); return torrents.OrderBy(m => m.Priority ?? 9999) .ThenBy(m => m.Added) diff --git a/server/RdtClient.Data/Models/Data/Torrent.cs b/server/RdtClient.Data/Models/Data/Torrent.cs index d060d14..3780f38 100644 --- a/server/RdtClient.Data/Models/Data/Torrent.cs +++ b/server/RdtClient.Data/Models/Data/Torrent.cs @@ -63,6 +63,7 @@ public class Torrent public DateTimeOffset? RdEnded { get; set; } public Int64? RdSpeed { get; set; } public Int64? RdSeeders { get; set; } + public String? RdFiles { get => _rdFiles; diff --git a/server/RdtClient.Service.Test/BackgroundServices/WatchFolderCheckerTests.cs b/server/RdtClient.Service.Test/BackgroundServices/WatchFolderCheckerTests.cs index 4902ca2..991b041 100644 --- a/server/RdtClient.Service.Test/BackgroundServices/WatchFolderCheckerTests.cs +++ b/server/RdtClient.Service.Test/BackgroundServices/WatchFolderCheckerTests.cs @@ -24,6 +24,7 @@ public class WatchFolderCheckerTests : IDisposable _serviceProviderMock = new(); _serviceScopeMock = new(); _scopeServiceProviderMock = new(); + _settings = new(new() { Watch = new() @@ -33,6 +34,7 @@ public class WatchFolderCheckerTests : IDisposable }, DownloadClient = new() }); + _torrentsServiceMock = new(null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, _settings, new TorrentRunnerState()); _serviceProviderMock diff --git a/server/RdtClient.Service.Test/Regression/TorrentDownloadRaceTests.cs b/server/RdtClient.Service.Test/Regression/TorrentDownloadRaceTests.cs index 3218d12..7fbdad2 100644 --- a/server/RdtClient.Service.Test/Regression/TorrentDownloadRaceTests.cs +++ b/server/RdtClient.Service.Test/Regression/TorrentDownloadRaceTests.cs @@ -162,6 +162,7 @@ public class TorrentDownloadRaceTests : IAsyncLifetime var torrentId = Guid.NewGuid(); await using var context = CreateContext(); + context.Torrents.Add(new() { TorrentId = torrentId, diff --git a/server/RdtClient.Service.Test/Services/QBittorrentTest.cs b/server/RdtClient.Service.Test/Services/QBittorrentTest.cs index e03d532..0e452db 100644 --- a/server/RdtClient.Service.Test/Services/QBittorrentTest.cs +++ b/server/RdtClient.Service.Test/Services/QBittorrentTest.cs @@ -177,19 +177,20 @@ public class QBittorrentTest // Assert Assert.NotNull(result); + Assert.Collection(result!, - first => - { - Assert.Equal(0, first.Index); - Assert.Equal("good.mkv", first.Name); - Assert.Equal(1, first.Priority); - }, - second => - { - Assert.Equal(1, second.Index); - Assert.Equal("dangerous.exe", second.Name); - Assert.Equal(0, second.Priority); - }); + first => + { + Assert.Equal(0, first.Index); + Assert.Equal("good.mkv", first.Name); + Assert.Equal(1, first.Priority); + }, + second => + { + Assert.Equal(1, second.Index); + Assert.Equal("dangerous.exe", second.Name); + Assert.Equal(0, second.Priority); + }); } [Fact] @@ -251,7 +252,11 @@ public class QBittorrentTest Type = DownloadType.Torrent }; - _torrentsMock.Setup(m => m.Get()).ReturnsAsync(new List { torrent }); + _torrentsMock.Setup(m => m.Get()) + .ReturnsAsync(new List + { + torrent + }); // Act var result = await _qBittorrent.TorrentInfo(); @@ -311,7 +316,11 @@ public class QBittorrentTest Type = DownloadType.Torrent }; - _torrentsMock.Setup(m => m.Get()).ReturnsAsync(new List { torrent }); + _torrentsMock.Setup(m => m.Get()) + .ReturnsAsync(new List + { + torrent + }); var result = await _qBittorrent.TorrentInfo(); @@ -376,7 +385,11 @@ public class QBittorrentTest Type = DownloadType.Torrent }; - _torrentsMock.Setup(m => m.Get()).ReturnsAsync(new List { torrent }); + _torrentsMock.Setup(m => m.Get()) + .ReturnsAsync(new List + { + torrent + }); var result = await _qBittorrent.TorrentInfo(); diff --git a/server/RdtClient.Service.Test/Services/TorrentRunnerTest.cs b/server/RdtClient.Service.Test/Services/TorrentRunnerTest.cs index 5d20df9..573dba1 100644 --- a/server/RdtClient.Service.Test/Services/TorrentRunnerTest.cs +++ b/server/RdtClient.Service.Test/Services/TorrentRunnerTest.cs @@ -36,10 +36,12 @@ public class TorrentRunnerTest }; var torrentDataMock = new Mock(MockBehavior.Strict); - torrentDataMock.Setup(m => m.Get()).ReturnsAsync(new List - { - erroredTorrent - }); + + torrentDataMock.Setup(m => m.Get()) + .ReturnsAsync(new List + { + erroredTorrent + }); var torrents = new Torrents(Mock.Of>(), torrentDataMock.Object, diff --git a/server/RdtClient.Service/Services/DebridClients/PremiumizeDebridClient.cs b/server/RdtClient.Service/Services/DebridClients/PremiumizeDebridClient.cs index a162c0a..e4ad491 100644 --- a/server/RdtClient.Service/Services/DebridClients/PremiumizeDebridClient.cs +++ b/server/RdtClient.Service/Services/DebridClients/PremiumizeDebridClient.cs @@ -12,7 +12,8 @@ using Torrent = RdtClient.Data.Models.Data.Torrent; namespace RdtClient.Service.Services.DebridClients; -public class PremiumizeDebridClient(ILogger logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter, ISettings settings) : IDebridClient +public class PremiumizeDebridClient(ILogger logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter, ISettings settings) + : IDebridClient { private const String TransferCreateUrl = "https://www.premiumize.me/api/transfer/create"; @@ -331,11 +332,11 @@ public class PremiumizeDebridClient(ILogger logger, IHtt private static String FormatPremiumizeError(RawTransferCreateResponse result) { - return String.Join(": ", new[] - { - result.Code, - result.Message - }.Where(m => !String.IsNullOrWhiteSpace(m))); + return String.Join(": ", + new[] + { + result.Code, result.Message + }.Where(m => !String.IsNullOrWhiteSpace(m))); } private static Boolean IsRateLimitMessage(String message) diff --git a/server/RdtClient.Service/Services/DebridClients/RealDebridDebridClient.cs b/server/RdtClient.Service/Services/DebridClients/RealDebridDebridClient.cs index ea8505a..718854c 100644 --- a/server/RdtClient.Service/Services/DebridClients/RealDebridDebridClient.cs +++ b/server/RdtClient.Service/Services/DebridClients/RealDebridDebridClient.cs @@ -12,7 +12,8 @@ using Torrent = RDNET.Torrent; namespace RdtClient.Service.Services.DebridClients; -public class RealDebridDebridClient(ILogger logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter, ISettings settings) : IDebridClient +public class RealDebridDebridClient(ILogger logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter, ISettings settings) + : IDebridClient { private TimeSpan? _offset; diff --git a/server/RdtClient.Service/Services/DebridClients/TorBoxDebridClient.cs b/server/RdtClient.Service/Services/DebridClients/TorBoxDebridClient.cs index e47237d..916e039 100644 --- a/server/RdtClient.Service/Services/DebridClients/TorBoxDebridClient.cs +++ b/server/RdtClient.Service/Services/DebridClients/TorBoxDebridClient.cs @@ -11,7 +11,12 @@ using Torrent = RdtClient.Data.Models.Data.Torrent; namespace RdtClient.Service.Services.DebridClients; -public class TorBoxDebridClient(ILogger logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter, IRateLimitCoordinator coordinator, ISettings settings) +public class TorBoxDebridClient( + ILogger logger, + IHttpClientFactory httpClientFactory, + IDownloadableFileFilter fileFilter, + IRateLimitCoordinator coordinator, + ISettings settings) : IDebridClient { private const String TorBoxApiHost = "api.torbox.app"; @@ -69,10 +74,12 @@ public class TorBoxDebridClient(ILogger logger, IHttpClientF return await HandleAddTorrentErrors(async asQueued => { var user = await GetClient().User.GetAsync(true); - var result = await GetClient(DiConfig.TORBOX_CLIENT_SLOW).Torrents.AddMagnetAsync(magnetLink, - user.Data?.Settings?.SeedTorrents ?? 3, - allowZip: Settings.Get.Provider.PreferZippedDownloads, - as_queued: asQueued); + + var result = await GetClient(DiConfig.TORBOX_CLIENT_SLOW) + .Torrents.AddMagnetAsync(magnetLink, + user.Data?.Settings?.SeedTorrents ?? 3, + allowZip: Settings.Get.Provider.PreferZippedDownloads, + as_queued: asQueued); return result.Data!.Hash!; }); @@ -83,10 +90,12 @@ public class TorBoxDebridClient(ILogger logger, IHttpClientF return await HandleAddTorrentErrors(async asQueued => { var user = await GetClient().User.GetAsync(true); - var result = await GetClient(DiConfig.TORBOX_CLIENT_SLOW).Torrents.AddFileAsync(bytes, - user.Data?.Settings?.SeedTorrents ?? 3, - allowZip: Settings.Get.Provider.PreferZippedDownloads, - as_queued: asQueued); + + var result = await GetClient(DiConfig.TORBOX_CLIENT_SLOW) + .Torrents.AddFileAsync(bytes, + user.Data?.Settings?.SeedTorrents ?? 3, + allowZip: Settings.Get.Provider.PreferZippedDownloads, + as_queued: asQueued); return result.Data!.Hash!; }); diff --git a/server/RdtClient.Service/Services/QBittorrent.cs b/server/RdtClient.Service/Services/QBittorrent.cs index 16bf364..4e022ac 100644 --- a/server/RdtClient.Service/Services/QBittorrent.cs +++ b/server/RdtClient.Service/Services/QBittorrent.cs @@ -346,13 +346,13 @@ public class QBittorrent(ILogger logger, ISettings settings, Authen } var topLevelSelectedFiles = torrent.Files - .Where(m => m.Selected && !String.IsNullOrWhiteSpace(m.Path)) - .Select(m => m.Path.Trim('/').Trim('\\')) - .Where(m => m.IndexOfAny(['/', '\\']) < 0) - .Select(Path.GetFileName) - .Where(m => !String.IsNullOrWhiteSpace(m)) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); + .Where(m => m.Selected && !String.IsNullOrWhiteSpace(m.Path)) + .Select(m => m.Path.Trim('/').Trim('\\')) + .Where(m => m.IndexOfAny(['/', '\\']) < 0) + .Select(Path.GetFileName) + .Where(m => !String.IsNullOrWhiteSpace(m)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); if (topLevelSelectedFiles.Count == 1) { @@ -501,16 +501,16 @@ public class QBittorrent(ILogger logger, ISettings settings, Authen var progress = torrent.Completed.HasValue || torrent.RdStatus == TorrentStatus.Finished ? 1f : 0f; return torrent.Files - .Select((file, index) => new TorrentFileItem - { - Index = index, - Name = file.Path, - Size = file.Bytes, - Progress = file.Selected ? progress : 0f, - Priority = file.Selected ? 1 : 0, - IsSeed = false - }) - .ToList(); + .Select((file, index) => new TorrentFileItem + { + Index = index, + Name = file.Path, + Size = file.Bytes, + Progress = file.Selected ? progress : 0f, + Priority = file.Selected ? 1 : 0, + IsSeed = false + }) + .ToList(); } public async Task TorrentProperties(String hash) diff --git a/server/RdtClient.Service/Services/TorrentRunner.cs b/server/RdtClient.Service/Services/TorrentRunner.cs index 4f1caf7..0ee1cbd 100644 --- a/server/RdtClient.Service/Services/TorrentRunner.cs +++ b/server/RdtClient.Service/Services/TorrentRunner.cs @@ -367,8 +367,10 @@ public class TorrentRunner( } // Process torrents in DebridQueue - var torrentsToAddToProvider = allTorrents.Where(m => m.Completed == null && m.Error == null && m.RdId == null && m.RdAdded == null && m.FileOrMagnet != null && m.RdStatus == TorrentStatus.Queued) - .ToList(); + var torrentsToAddToProvider = allTorrents + .Where(m => m.Completed == null && m.Error == null && m.RdId == null && m.RdAdded == null && m.FileOrMagnet != null && + m.RdStatus == TorrentStatus.Queued) + .ToList(); if (torrentsToAddToProvider.Count != 0) { diff --git a/server/RdtClient.Service/Services/Torrents.cs b/server/RdtClient.Service/Services/Torrents.cs index 1827375..ce15b4d 100644 --- a/server/RdtClient.Service/Services/Torrents.cs +++ b/server/RdtClient.Service/Services/Torrents.cs @@ -1137,18 +1137,19 @@ public class Torrents( torrent.RdFiles); } - private readonly record struct TorrentRdState(String? RdName, - Int64? RdSize, - String? RdHost, - Int64? RdSplit, - Int64? RdProgress, - TorrentStatus? RdStatus, - String? RdStatusRaw, - DateTimeOffset? RdAdded, - DateTimeOffset? RdEnded, - Int64? RdSpeed, - Int64? RdSeeders, - String? RdFiles); + private readonly record struct TorrentRdState( + String? RdName, + Int64? RdSize, + String? RdHost, + Int64? RdSplit, + Int64? RdProgress, + TorrentStatus? RdStatus, + String? RdStatusRaw, + DateTimeOffset? RdAdded, + DateTimeOffset? RdEnded, + Int64? RdSpeed, + Int64? RdSeeders, + String? RdFiles); private void Log(String message, Download? download, Torrent? torrent) { diff --git a/server/RdtClient.Web.Test/Controllers/QBittorrentControllerTest.cs b/server/RdtClient.Web.Test/Controllers/QBittorrentControllerTest.cs index c7ae2b8..8b9a930 100644 --- a/server/RdtClient.Web.Test/Controllers/QBittorrentControllerTest.cs +++ b/server/RdtClient.Web.Test/Controllers/QBittorrentControllerTest.cs @@ -21,12 +21,11 @@ public class QBittorrentControllerTest _qBittorrentMock = new(new Mock>().Object, _settings, null!, null!, null!, new TorrentRunnerState()); _torrentsMock = new(null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, _settings, new TorrentRunnerState()); - _controller = new( - new Mock>().Object, - _qBittorrentMock.Object, - new Mock().Object, - _settings, - _torrentsMock.Object); + _controller = new(new Mock>().Object, + _qBittorrentMock.Object, + new Mock().Object, + _settings, + _torrentsMock.Object); _controller.ControllerContext = new() { @@ -38,15 +37,16 @@ public class QBittorrentControllerTest public async Task TorrentsInfo_FilterAll_DoesNotFilterOutResults() { // Arrange - _qBittorrentMock.Setup(q => q.TorrentInfo()).ReturnsAsync(new List - { - new() - { - Hash = "hash1", - State = "pausedUP", - Progress = 1f - } - }); + _qBittorrentMock.Setup(q => q.TorrentInfo()) + .ReturnsAsync(new List + { + new() + { + Hash = "hash1", + State = "pausedUP", + Progress = 1f + } + }); // Act var result = await _controller.TorrentsInfo(new() @@ -66,21 +66,22 @@ public class QBittorrentControllerTest public async Task TorrentsInfo_FilterCompleted_MatchesPausedUploadTorrents() { // Arrange - _qBittorrentMock.Setup(q => q.TorrentInfo()).ReturnsAsync(new List - { - new() - { - Hash = "hash1", - State = "pausedUP", - Progress = 1f - }, - new() - { - Hash = "hash2", - State = "downloading", - Progress = 0.4f - } - }); + _qBittorrentMock.Setup(q => q.TorrentInfo()) + .ReturnsAsync(new List + { + new() + { + Hash = "hash1", + State = "pausedUP", + Progress = 1f + }, + new() + { + Hash = "hash2", + State = "downloading", + Progress = 0.4f + } + }); // Act var result = await _controller.TorrentsInfo(new() diff --git a/server/RdtClient.Web.Test/Controllers/SabnzbdHandlerTest.cs b/server/RdtClient.Web.Test/Controllers/SabnzbdHandlerTest.cs index efcd52c..80c4b04 100644 --- a/server/RdtClient.Web.Test/Controllers/SabnzbdHandlerTest.cs +++ b/server/RdtClient.Web.Test/Controllers/SabnzbdHandlerTest.cs @@ -44,6 +44,7 @@ public class SabnzbdHandlerTest { // Arrange _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; + var httpContext = new DefaultHttpContext { Request = @@ -69,6 +70,7 @@ public class SabnzbdHandlerTest { // Arrange _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; + var httpContext = new DefaultHttpContext { Request = @@ -96,6 +98,7 @@ public class SabnzbdHandlerTest _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; var httpContext = new DefaultHttpContext(); httpContext.Request.ContentType = "application/x-www-form-urlencoded"; + httpContext.Request.Form = new FormCollection(new() { { @@ -120,6 +123,7 @@ public class SabnzbdHandlerTest { // Arrange _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; + var httpContext = new DefaultHttpContext { Request = @@ -145,6 +149,7 @@ public class SabnzbdHandlerTest { // Arrange _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; + var httpContext = new DefaultHttpContext { Request = @@ -192,6 +197,7 @@ public class SabnzbdHandlerTest { // Arrange _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; + var httpContext = new DefaultHttpContext { Request = @@ -217,6 +223,7 @@ public class SabnzbdHandlerTest { // Arrange _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; + var httpContext = new DefaultHttpContext { Request = @@ -245,6 +252,7 @@ public class SabnzbdHandlerTest { // Arrange _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; + var httpContext = new DefaultHttpContext { Request = diff --git a/server/RdtClient.Web/Controllers/QBittorrentController.cs b/server/RdtClient.Web/Controllers/QBittorrentController.cs index bfe6061..06a31d4 100644 --- a/server/RdtClient.Web/Controllers/QBittorrentController.cs +++ b/server/RdtClient.Web/Controllers/QBittorrentController.cs @@ -14,7 +14,8 @@ namespace RdtClient.Web.Controllers; [ApiController] [Route("api/v2")] [Route("qbittorrent/api/v2")] -public class QBittorrentController(ILogger logger, QBittorrent qBittorrent, IHttpClientFactory httpClientFactory, ISettings settings, Torrents torrents) : Controller +public class QBittorrentController(ILogger logger, QBittorrent qBittorrent, IHttpClientFactory httpClientFactory, ISettings settings, Torrents torrents) + : Controller { [AllowAnonymous] [Route("/version/api")] @@ -369,6 +370,7 @@ public class QBittorrentController(ILogger logger, QBitto foreach (var url in urls) { Torrent? torrent; + if (url.StartsWith("magnet")) { torrent = await qBittorrent.TorrentsAddMagnet(url.Trim(), request.Category, null); @@ -394,7 +396,7 @@ public class QBittorrentController(ILogger logger, QBitto return Ok(); } - + [Authorize(Policy = "AuthSetting")] [Route("torrents/add")] [HttpPost] @@ -439,11 +441,11 @@ public class QBittorrentController(ILogger logger, QBitto } var fileIds = request.Id - .Split('|', StringSplitOptions.RemoveEmptyEntries) - .Select(value => Int32.TryParse(value, out var parsedValue) ? parsedValue : (Int32?)null) - .Where(value => value.HasValue) - .Select(value => value!.Value) - .ToList(); + .Split('|', StringSplitOptions.RemoveEmptyEntries) + .Select(value => Int32.TryParse(value, out var parsedValue) ? parsedValue : (Int32?)null) + .Where(value => value.HasValue) + .Select(value => value!.Value) + .ToList(); if (fileIds.Count == 0) { @@ -675,7 +677,7 @@ public class QBittorrentController(ILogger logger, QBitto _ => String.Equals(torrent.State, filter, StringComparison.OrdinalIgnoreCase) }; } - + private async Task WaitForTorrent(Guid torrentId) { while (true) @@ -715,7 +717,6 @@ public class QBTorrentsInfoRequest public String? Hashes { get; set; } } - public class QBTorrentsCountRequest { public String? Filter { get; set; }