Add quick fix to reject torrents that error out as soon as they are added.

This commit is contained in:
Roger Far 2026-05-27 21:50:21 -06:00
parent 157fc8b2f2
commit d6f747c8d8
8 changed files with 66 additions and 43 deletions

View file

@ -1,3 +1 @@
using Xunit;
[assembly: CollectionBehavior(DisableTestParallelization = true)] [assembly: CollectionBehavior(DisableTestParallelization = true)]

View file

@ -11,17 +11,17 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AllDebrid.NET" Version="1.0.18" /> <PackageReference Include="AllDebrid.NET" Version="1.0.18" />
<PackageReference Include="coverlet.collector" Version="10.0.0"> <PackageReference Include="coverlet.collector" Version="10.0.1">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
<PackageReference Include="Moq" Version="4.20.72" /> <PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="SharpCompress" Version="[0.42.1]" /> <PackageReference Include="SharpCompress" Version="[0.42.1]" />
<PackageReference Include="TestableIO.System.IO.Abstractions" Version="22.1.1" /> <PackageReference Include="TestableIO.System.IO.Abstractions" Version="22.1.1" />
<PackageReference Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="22.1.1" /> <PackageReference Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="22.1.1" />
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="22.1.1" /> <PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="22.1.1" />
<PackageReference Include="TorBox.NET" Version="1.8.1" /> <PackageReference Include="TorBox.NET" Version="2.0.0" />
<PackageReference Include="xunit" Version="2.9.3" /> <PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5"> <PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

View file

@ -26,7 +26,7 @@
<PackageReference Include="Synology.Api.Client" Version="[0.3.93]" /> <PackageReference Include="Synology.Api.Client" Version="[0.3.93]" />
<PackageReference Include="TestableIO.System.IO.Abstractions" Version="22.1.1" /> <PackageReference Include="TestableIO.System.IO.Abstractions" Version="22.1.1" />
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="22.1.1" /> <PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="22.1.1" />
<PackageReference Include="TorBox.NET" Version="1.8.1" /> <PackageReference Include="TorBox.NET" Version="2.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -633,7 +633,7 @@ public class QBittorrent(ILogger<QBittorrent> logger, Settings settings, Authent
} }
} }
public async Task TorrentsAddMagnet(String magnetLink, String? category, Int32? priority) public async Task<Torrent> TorrentsAddMagnet(String magnetLink, String? category, Int32? priority)
{ {
logger.LogDebug($"Add magnet {category}"); logger.LogDebug($"Add magnet {category}");
@ -655,10 +655,10 @@ public class QBittorrent(ILogger<QBittorrent> logger, Settings settings, Authent
Priority = priority ?? (Settings.Get.Integrations.Default.Priority > 0 ? Settings.Get.Integrations.Default.Priority : null) Priority = priority ?? (Settings.Get.Integrations.Default.Priority > 0 ? Settings.Get.Integrations.Default.Priority : null)
}; };
await torrents.AddMagnetToDebridQueue(magnetLink, torrent); return await torrents.AddMagnetToDebridQueue(magnetLink, torrent);
} }
public async Task TorrentsAddFile(Byte[] fileBytes, String? category, Int32? priority) public async Task<Torrent> TorrentsAddFile(Byte[] fileBytes, String? category, Int32? priority)
{ {
logger.LogDebug($"Add file {category}"); logger.LogDebug($"Add file {category}");
@ -680,7 +680,7 @@ public class QBittorrent(ILogger<QBittorrent> logger, Settings settings, Authent
Priority = priority ?? (Settings.Get.Integrations.Default.Priority > 0 ? Settings.Get.Integrations.Default.Priority : null) Priority = priority ?? (Settings.Get.Integrations.Default.Priority > 0 ? Settings.Get.Integrations.Default.Priority : null)
}; };
await torrents.AddFileToDebridQueue(fileBytes, torrent); return await torrents.AddFileToDebridQueue(fileBytes, torrent);
} }
public async Task TorrentsSetCategory(String hash, String? category) public async Task TorrentsSetCategory(String hash, String? category)

View file

@ -12,16 +12,14 @@ public class QBittorrentControllerTest
{ {
private readonly QBittorrentController _controller; private readonly QBittorrentController _controller;
private readonly Mock<QBittorrent> _qBittorrentMock; private readonly Mock<QBittorrent> _qBittorrentMock;
private readonly Mock<Torrents> _torrentsMock;
public QBittorrentControllerTest() public QBittorrentControllerTest()
{ {
_qBittorrentMock = new(new Mock<ILogger<QBittorrent>>().Object, null!, null!, null!, null!); _qBittorrentMock = new(new Mock<ILogger<QBittorrent>>().Object, null!, null!, null!, null!);
_torrentsMock = new(null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!);
_controller = new( _controller = new(new Mock<ILogger<QBittorrentController>>().Object, _qBittorrentMock.Object, new Mock<IHttpClientFactory>().Object, _torrentsMock.Object);
new Mock<ILogger<QBittorrentController>>().Object,
_qBittorrentMock.Object,
new Mock<IHttpClientFactory>().Object);
_controller.ControllerContext = new() _controller.ControllerContext = new()
{ {
HttpContext = new DefaultHttpContext() HttpContext = new DefaultHttpContext()

View file

@ -1,3 +1 @@
using Xunit;
[assembly: CollectionBehavior(DisableTestParallelization = true)] [assembly: CollectionBehavior(DisableTestParallelization = true)]

View file

@ -10,7 +10,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
<PackageReference Include="Moq" Version="4.20.72" /> <PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="SharpCompress" Version="[0.42.1]" /> <PackageReference Include="SharpCompress" Version="[0.42.1]" />
<PackageReference Include="xunit" Version="2.9.3" /> <PackageReference Include="xunit" Version="2.9.3" />

View file

@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc;
using RdtClient.Data.Enums; using RdtClient.Data.Enums;
using RdtClient.Data.Models.QBittorrent; using RdtClient.Data.Models.QBittorrent;
using RdtClient.Service.Services; using RdtClient.Service.Services;
using RealDebridException = RDNET.RealDebridException; using Torrent = RdtClient.Data.Models.Data.Torrent;
namespace RdtClient.Web.Controllers; namespace RdtClient.Web.Controllers;
@ -14,7 +14,7 @@ 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) : Controller public class QBittorrentController(ILogger<QBittorrentController> logger, QBittorrent qBittorrent, IHttpClientFactory httpClientFactory, Torrents torrents) : Controller
{ {
[AllowAnonymous] [AllowAnonymous]
[Route("/version/api")] [Route("/version/api")]
@ -368,36 +368,33 @@ public class QBittorrentController(ILogger<QBittorrentController> logger, QBitto
foreach (var url in urls) foreach (var url in urls)
{ {
try Torrent? torrent;
if (url.StartsWith("magnet"))
{ {
if (url.StartsWith("magnet")) torrent = await qBittorrent.TorrentsAddMagnet(url.Trim(), request.Category, null);
{
await qBittorrent.TorrentsAddMagnet(url.Trim(), request.Category, null);
}
else if (url.StartsWith("http"))
{
var httpClient = httpClientFactory.CreateClient();
var result = await httpClient.GetByteArrayAsync(url);
await qBittorrent.TorrentsAddFile(result, request.Category, null);
}
else
{
return BadRequest($"Invalid torrent link format {url}");
}
} }
catch (RealDebridException ex) else if (url.StartsWith("http"))
{ {
// Infringing file. var httpClient = httpClientFactory.CreateClient();
if (ex.ErrorCode == 35) var result = await httpClient.GetByteArrayAsync(url);
{ torrent = await qBittorrent.TorrentsAddFile(result, request.Category, null);
return Ok("Fails."); }
} else
{
return BadRequest($"Invalid torrent link format {url}");
}
var addResult = await WaitForTorrent(torrent.TorrentId);
if (!addResult)
{
return Ok("Fails.");
} }
} }
return Ok(); return Ok();
} }
[Authorize(Policy = "AuthSetting")] [Authorize(Policy = "AuthSetting")]
[Route("torrents/add")] [Route("torrents/add")]
[HttpPost] [HttpPost]
@ -412,7 +409,14 @@ public class QBittorrentController(ILogger<QBittorrentController> logger, QBitto
await file.CopyToAsync(target); await file.CopyToAsync(target);
var fileBytes = target.ToArray(); var fileBytes = target.ToArray();
await qBittorrent.TorrentsAddFile(fileBytes, request.Category, request.Priority); var torrent = await qBittorrent.TorrentsAddFile(fileBytes, request.Category, request.Priority);
var addResult = await WaitForTorrent(torrent.TorrentId);
if (!addResult)
{
return Ok("Fails.");
}
} }
} }
@ -671,6 +675,31 @@ public class QBittorrentController(ILogger<QBittorrentController> logger, QBitto
_ => String.Equals(torrent.State, filter, StringComparison.OrdinalIgnoreCase) _ => String.Equals(torrent.State, filter, StringComparison.OrdinalIgnoreCase)
}; };
} }
private async Task<Boolean> WaitForTorrent(Guid torrentId)
{
while (true)
{
var torrent = await torrents.GetById(torrentId);
if (torrent == null)
{
throw new($"Failed to add torrent: Not Found");
}
if (torrent.RdStatus == TorrentStatus.Error || torrent.Error != null)
{
return false;
}
if (torrent.RdStatus != TorrentStatus.Queued)
{
return true;
}
await Task.Delay(1000);
}
}
} }
public class QBAuthLoginRequest public class QBAuthLoginRequest