This commit is contained in:
Roger Far 2026-05-27 22:04:30 -06:00
parent a82e895a03
commit 767038528a
16 changed files with 157 additions and 111 deletions

View file

@ -74,6 +74,7 @@ public class DownloadData(DataContext dataContext, ILogger<DownloadData>? logger
return DownloadAddResult.Added; return DownloadAddResult.Added;
} }
// These shouldn't be possible any longer, but added for safety and until confirmed. // These shouldn't be possible any longer, but added for safety and until confirmed.
catch (DbUpdateException ex) catch (DbUpdateException ex)
{ {
@ -88,7 +89,9 @@ public class DownloadData(DataContext dataContext, ILogger<DownloadData>? logger
if (IsForeignKeyViolation(ex) && !await dataContext.Torrents.AsNoTracking().AnyAsync(m => m.TorrentId == torrentId)) 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; return DownloadAddResult.TorrentMissing;
} }

View file

@ -63,6 +63,7 @@ public class Torrent
public DateTimeOffset? RdEnded { get; set; } public DateTimeOffset? RdEnded { get; set; }
public Int64? RdSpeed { get; set; } public Int64? RdSpeed { get; set; }
public Int64? RdSeeders { get; set; } public Int64? RdSeeders { get; set; }
public String? RdFiles public String? RdFiles
{ {
get => _rdFiles; get => _rdFiles;

View file

@ -24,6 +24,7 @@ public class WatchFolderCheckerTests : IDisposable
_serviceProviderMock = new(); _serviceProviderMock = new();
_serviceScopeMock = new(); _serviceScopeMock = new();
_scopeServiceProviderMock = new(); _scopeServiceProviderMock = new();
_settings = new(new() _settings = new(new()
{ {
Watch = new() Watch = new()
@ -33,6 +34,7 @@ public class WatchFolderCheckerTests : IDisposable
}, },
DownloadClient = new() DownloadClient = new()
}); });
_torrentsServiceMock = new(null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, _settings, new TorrentRunnerState()); _torrentsServiceMock = new(null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, _settings, new TorrentRunnerState());
_serviceProviderMock _serviceProviderMock

View file

@ -162,6 +162,7 @@ public class TorrentDownloadRaceTests : IAsyncLifetime
var torrentId = Guid.NewGuid(); var torrentId = Guid.NewGuid();
await using var context = CreateContext(); await using var context = CreateContext();
context.Torrents.Add(new() context.Torrents.Add(new()
{ {
TorrentId = torrentId, TorrentId = torrentId,

View file

@ -177,6 +177,7 @@ public class QBittorrentTest
// Assert // Assert
Assert.NotNull(result); Assert.NotNull(result);
Assert.Collection(result!, Assert.Collection(result!,
first => first =>
{ {
@ -251,7 +252,11 @@ public class QBittorrentTest
Type = DownloadType.Torrent Type = DownloadType.Torrent
}; };
_torrentsMock.Setup(m => m.Get()).ReturnsAsync(new List<Torrent> { torrent }); _torrentsMock.Setup(m => m.Get())
.ReturnsAsync(new List<Torrent>
{
torrent
});
// Act // Act
var result = await _qBittorrent.TorrentInfo(); var result = await _qBittorrent.TorrentInfo();
@ -311,7 +316,11 @@ public class QBittorrentTest
Type = DownloadType.Torrent Type = DownloadType.Torrent
}; };
_torrentsMock.Setup(m => m.Get()).ReturnsAsync(new List<Torrent> { torrent }); _torrentsMock.Setup(m => m.Get())
.ReturnsAsync(new List<Torrent>
{
torrent
});
var result = await _qBittorrent.TorrentInfo(); var result = await _qBittorrent.TorrentInfo();
@ -376,7 +385,11 @@ public class QBittorrentTest
Type = DownloadType.Torrent Type = DownloadType.Torrent
}; };
_torrentsMock.Setup(m => m.Get()).ReturnsAsync(new List<Torrent> { torrent }); _torrentsMock.Setup(m => m.Get())
.ReturnsAsync(new List<Torrent>
{
torrent
});
var result = await _qBittorrent.TorrentInfo(); var result = await _qBittorrent.TorrentInfo();

View file

@ -36,7 +36,9 @@ public class TorrentRunnerTest
}; };
var torrentDataMock = new Mock<ITorrentData>(MockBehavior.Strict); var torrentDataMock = new Mock<ITorrentData>(MockBehavior.Strict);
torrentDataMock.Setup(m => m.Get()).ReturnsAsync(new List<Torrent>
torrentDataMock.Setup(m => m.Get())
.ReturnsAsync(new List<Torrent>
{ {
erroredTorrent erroredTorrent
}); });

View file

@ -12,7 +12,8 @@ using Torrent = RdtClient.Data.Models.Data.Torrent;
namespace RdtClient.Service.Services.DebridClients; namespace RdtClient.Service.Services.DebridClients;
public class PremiumizeDebridClient(ILogger<PremiumizeDebridClient> logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter, ISettings settings) : IDebridClient public class PremiumizeDebridClient(ILogger<PremiumizeDebridClient> logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter, ISettings settings)
: IDebridClient
{ {
private const String TransferCreateUrl = "https://www.premiumize.me/api/transfer/create"; private const String TransferCreateUrl = "https://www.premiumize.me/api/transfer/create";
@ -331,10 +332,10 @@ public class PremiumizeDebridClient(ILogger<PremiumizeDebridClient> logger, IHtt
private static String FormatPremiumizeError(RawTransferCreateResponse result) private static String FormatPremiumizeError(RawTransferCreateResponse result)
{ {
return String.Join(": ", new[] return String.Join(": ",
new[]
{ {
result.Code, result.Code, result.Message
result.Message
}.Where(m => !String.IsNullOrWhiteSpace(m))); }.Where(m => !String.IsNullOrWhiteSpace(m)));
} }

View file

@ -12,7 +12,8 @@ using Torrent = RDNET.Torrent;
namespace RdtClient.Service.Services.DebridClients; namespace RdtClient.Service.Services.DebridClients;
public class RealDebridDebridClient(ILogger<RealDebridDebridClient> logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter, ISettings settings) : IDebridClient public class RealDebridDebridClient(ILogger<RealDebridDebridClient> logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter, ISettings settings)
: IDebridClient
{ {
private TimeSpan? _offset; private TimeSpan? _offset;

View file

@ -11,7 +11,12 @@ using Torrent = RdtClient.Data.Models.Data.Torrent;
namespace RdtClient.Service.Services.DebridClients; namespace RdtClient.Service.Services.DebridClients;
public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter, IRateLimitCoordinator coordinator, ISettings settings) public class TorBoxDebridClient(
ILogger<TorBoxDebridClient> logger,
IHttpClientFactory httpClientFactory,
IDownloadableFileFilter fileFilter,
IRateLimitCoordinator coordinator,
ISettings settings)
: IDebridClient : IDebridClient
{ {
private const String TorBoxApiHost = "api.torbox.app"; private const String TorBoxApiHost = "api.torbox.app";
@ -69,7 +74,9 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
return await HandleAddTorrentErrors(async asQueued => return await HandleAddTorrentErrors(async asQueued =>
{ {
var user = await GetClient().User.GetAsync(true); var user = await GetClient().User.GetAsync(true);
var result = await GetClient(DiConfig.TORBOX_CLIENT_SLOW).Torrents.AddMagnetAsync(magnetLink,
var result = await GetClient(DiConfig.TORBOX_CLIENT_SLOW)
.Torrents.AddMagnetAsync(magnetLink,
user.Data?.Settings?.SeedTorrents ?? 3, user.Data?.Settings?.SeedTorrents ?? 3,
allowZip: Settings.Get.Provider.PreferZippedDownloads, allowZip: Settings.Get.Provider.PreferZippedDownloads,
as_queued: asQueued); as_queued: asQueued);
@ -83,7 +90,9 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
return await HandleAddTorrentErrors(async asQueued => return await HandleAddTorrentErrors(async asQueued =>
{ {
var user = await GetClient().User.GetAsync(true); var user = await GetClient().User.GetAsync(true);
var result = await GetClient(DiConfig.TORBOX_CLIENT_SLOW).Torrents.AddFileAsync(bytes,
var result = await GetClient(DiConfig.TORBOX_CLIENT_SLOW)
.Torrents.AddFileAsync(bytes,
user.Data?.Settings?.SeedTorrents ?? 3, user.Data?.Settings?.SeedTorrents ?? 3,
allowZip: Settings.Get.Provider.PreferZippedDownloads, allowZip: Settings.Get.Provider.PreferZippedDownloads,
as_queued: asQueued); as_queued: asQueued);

View file

@ -367,7 +367,9 @@ public class TorrentRunner(
} }
// Process torrents in DebridQueue // 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) 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(); .ToList();
if (torrentsToAddToProvider.Count != 0) if (torrentsToAddToProvider.Count != 0)

View file

@ -1137,7 +1137,8 @@ public class Torrents(
torrent.RdFiles); torrent.RdFiles);
} }
private readonly record struct TorrentRdState(String? RdName, private readonly record struct TorrentRdState(
String? RdName,
Int64? RdSize, Int64? RdSize,
String? RdHost, String? RdHost,
Int64? RdSplit, Int64? RdSplit,

View file

@ -21,8 +21,7 @@ public class QBittorrentControllerTest
_qBittorrentMock = new(new Mock<ILogger<QBittorrent>>().Object, _settings, null!, null!, null!, new TorrentRunnerState()); _qBittorrentMock = new(new Mock<ILogger<QBittorrent>>().Object, _settings, null!, null!, null!, new TorrentRunnerState());
_torrentsMock = new(null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, _settings, new TorrentRunnerState()); _torrentsMock = new(null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, _settings, new TorrentRunnerState());
_controller = new( _controller = new(new Mock<ILogger<QBittorrentController>>().Object,
new Mock<ILogger<QBittorrentController>>().Object,
_qBittorrentMock.Object, _qBittorrentMock.Object,
new Mock<IHttpClientFactory>().Object, new Mock<IHttpClientFactory>().Object,
_settings, _settings,
@ -38,7 +37,8 @@ public class QBittorrentControllerTest
public async Task TorrentsInfo_FilterAll_DoesNotFilterOutResults() public async Task TorrentsInfo_FilterAll_DoesNotFilterOutResults()
{ {
// Arrange // Arrange
_qBittorrentMock.Setup(q => q.TorrentInfo()).ReturnsAsync(new List<TorrentInfo> _qBittorrentMock.Setup(q => q.TorrentInfo())
.ReturnsAsync(new List<TorrentInfo>
{ {
new() new()
{ {
@ -66,7 +66,8 @@ public class QBittorrentControllerTest
public async Task TorrentsInfo_FilterCompleted_MatchesPausedUploadTorrents() public async Task TorrentsInfo_FilterCompleted_MatchesPausedUploadTorrents()
{ {
// Arrange // Arrange
_qBittorrentMock.Setup(q => q.TorrentInfo()).ReturnsAsync(new List<TorrentInfo> _qBittorrentMock.Setup(q => q.TorrentInfo())
.ReturnsAsync(new List<TorrentInfo>
{ {
new() new()
{ {

View file

@ -44,6 +44,7 @@ public class SabnzbdHandlerTest
{ {
// Arrange // Arrange
_settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword;
var httpContext = new DefaultHttpContext var httpContext = new DefaultHttpContext
{ {
Request = Request =
@ -69,6 +70,7 @@ public class SabnzbdHandlerTest
{ {
// Arrange // Arrange
_settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword;
var httpContext = new DefaultHttpContext var httpContext = new DefaultHttpContext
{ {
Request = Request =
@ -96,6 +98,7 @@ public class SabnzbdHandlerTest
_settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword;
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
httpContext.Request.ContentType = "application/x-www-form-urlencoded"; httpContext.Request.ContentType = "application/x-www-form-urlencoded";
httpContext.Request.Form = new FormCollection(new() httpContext.Request.Form = new FormCollection(new()
{ {
{ {
@ -120,6 +123,7 @@ public class SabnzbdHandlerTest
{ {
// Arrange // Arrange
_settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword;
var httpContext = new DefaultHttpContext var httpContext = new DefaultHttpContext
{ {
Request = Request =
@ -145,6 +149,7 @@ public class SabnzbdHandlerTest
{ {
// Arrange // Arrange
_settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword;
var httpContext = new DefaultHttpContext var httpContext = new DefaultHttpContext
{ {
Request = Request =
@ -192,6 +197,7 @@ public class SabnzbdHandlerTest
{ {
// Arrange // Arrange
_settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword;
var httpContext = new DefaultHttpContext var httpContext = new DefaultHttpContext
{ {
Request = Request =
@ -217,6 +223,7 @@ public class SabnzbdHandlerTest
{ {
// Arrange // Arrange
_settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword;
var httpContext = new DefaultHttpContext var httpContext = new DefaultHttpContext
{ {
Request = Request =
@ -245,6 +252,7 @@ public class SabnzbdHandlerTest
{ {
// Arrange // Arrange
_settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword; _settings.Current.General.AuthenticationType = AuthenticationType.UserNamePassword;
var httpContext = new DefaultHttpContext var httpContext = new DefaultHttpContext
{ {
Request = Request =

View file

@ -14,7 +14,8 @@ namespace RdtClient.Web.Controllers;
[ApiController] [ApiController]
[Route("api/v2")] [Route("api/v2")]
[Route("qbittorrent/api/v2")] [Route("qbittorrent/api/v2")]
public class QBittorrentController(ILogger<QBittorrentController> logger, QBittorrent qBittorrent, IHttpClientFactory httpClientFactory, ISettings settings, Torrents torrents) : Controller public class QBittorrentController(ILogger<QBittorrentController> logger, QBittorrent qBittorrent, IHttpClientFactory httpClientFactory, ISettings settings, Torrents torrents)
: Controller
{ {
[AllowAnonymous] [AllowAnonymous]
[Route("/version/api")] [Route("/version/api")]
@ -369,6 +370,7 @@ public class QBittorrentController(ILogger<QBittorrentController> logger, QBitto
foreach (var url in urls) foreach (var url in urls)
{ {
Torrent? torrent; Torrent? torrent;
if (url.StartsWith("magnet")) if (url.StartsWith("magnet"))
{ {
torrent = await qBittorrent.TorrentsAddMagnet(url.Trim(), request.Category, null); torrent = await qBittorrent.TorrentsAddMagnet(url.Trim(), request.Category, null);
@ -715,7 +717,6 @@ public class QBTorrentsInfoRequest
public String? Hashes { get; set; } public String? Hashes { get; set; }
} }
public class QBTorrentsCountRequest public class QBTorrentsCountRequest
{ {
public String? Filter { get; set; } public String? Filter { get; set; }