Cleanup.
This commit is contained in:
parent
81dd3dd9f8
commit
41f816ab80
49 changed files with 1416 additions and 952 deletions
|
|
@ -9,7 +9,7 @@ namespace RdtClient.Data.Data;
|
|||
|
||||
public class SettingData(DataContext dataContext, ILogger<SettingData> logger)
|
||||
{
|
||||
public static DbSettings Get { get; private set; } = new();
|
||||
public static DbSettings Get { get; } = new();
|
||||
|
||||
public static IList<SettingProperty> GetAll()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@
|
|||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("Style", "IDE0305:Simplify collection initialization", Justification = "Code style", Scope = "NamespaceAndDescendants", Target = "N:RdtClient.Web")]
|
||||
[assembly: SuppressMessage("Style", "IDE0305:Simplify collection initialization", Justification = "Code style", Scope = "NamespaceAndDescendants", Target = "N:RdtClient.Data")]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using RdtClient.Data.Enums;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using RdtClient.Data.Enums;
|
||||
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
|
||||
|
|
|
|||
|
|
@ -21,4 +21,4 @@ public class SabnzbdCategory
|
|||
|
||||
[JsonPropertyName("script")]
|
||||
public String Script { get; set; } = "None";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,4 +12,4 @@ public class SabnzbdConfig
|
|||
|
||||
[JsonPropertyName("servers")]
|
||||
public List<Object> Servers { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,4 +12,4 @@ public class SabnzbdHistory
|
|||
|
||||
[JsonPropertyName("slots")]
|
||||
public List<SabnzbdHistorySlot> Slots { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,4 +21,4 @@ public class SabnzbdHistorySlot
|
|||
|
||||
[JsonPropertyName("storage")]
|
||||
public String Path { get; set; } = "";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@ public class SabnzbdMisc
|
|||
|
||||
[JsonPropertyName("version")]
|
||||
public String Version { get; set; } = "4.4.0";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,4 +24,4 @@ public class SabnzbdQueue
|
|||
|
||||
[JsonPropertyName("slots")]
|
||||
public List<SabnzbdQueueSlot> Slots { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,4 +33,4 @@ public class SabnzbdQueueSlot
|
|||
|
||||
[JsonPropertyName("priority")]
|
||||
public String Priority { get; set; } = "Normal";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,4 +27,4 @@ public class SabnzbdResponse
|
|||
|
||||
[JsonPropertyName("categories")]
|
||||
public List<String>? Categories { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,23 @@
|
|||
using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using RdtClient.Data.Data;
|
||||
using RdtClient.Data.Models.Data;
|
||||
using RdtClient.Data.Models.Internal;
|
||||
using RdtClient.Service.BackgroundServices;
|
||||
using RdtClient.Service.Services;
|
||||
using System.Reflection;
|
||||
|
||||
namespace RdtClient.Service.Test.BackgroundServices;
|
||||
|
||||
public class WatchFolderCheckerTests : IDisposable
|
||||
{
|
||||
private readonly Mock<ILogger<WatchFolderChecker>> _loggerMock;
|
||||
private readonly Mock<IServiceProvider> _scopeServiceProviderMock;
|
||||
private readonly Mock<IServiceProvider> _serviceProviderMock;
|
||||
private readonly Mock<IServiceScope> _serviceScopeMock;
|
||||
private readonly Mock<IServiceProvider> _scopeServiceProviderMock;
|
||||
private readonly Mock<Torrents> _torrentsServiceMock;
|
||||
private readonly String _testPath;
|
||||
private readonly Mock<Torrents> _torrentsServiceMock;
|
||||
|
||||
public WatchFolderCheckerTests()
|
||||
{
|
||||
|
|
@ -63,7 +64,7 @@ public class WatchFolderCheckerTests : IDisposable
|
|||
|
||||
private static void ResetSettings()
|
||||
{
|
||||
var settings = new Data.Models.Internal.DbSettings
|
||||
var settings = new DbSettings
|
||||
{
|
||||
Watch = new()
|
||||
{
|
||||
|
|
@ -104,13 +105,20 @@ public class WatchFolderCheckerTests : IDisposable
|
|||
var task = checker.StartAsync(cts.Token);
|
||||
await Task.Delay(500); // Give it some time to process
|
||||
await cts.CancelAsync();
|
||||
try { await task; } catch (OperationCanceledException) { }
|
||||
|
||||
try
|
||||
{
|
||||
await task;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
// Assert
|
||||
_torrentsServiceMock.Verify(x => x.AddFileToDebridQueue(
|
||||
It.Is<Byte[]>(b => b.SequenceEqual(content)),
|
||||
It.IsAny<Torrent>()), Times.AtLeastOnce);
|
||||
|
||||
_torrentsServiceMock.Verify(x => x.AddFileToDebridQueue(It.Is<Byte[]>(b => b.AsEnumerable().SequenceEqual(content)),
|
||||
It.IsAny<Torrent>()),
|
||||
Times.AtLeastOnce);
|
||||
|
||||
Assert.False(File.Exists(filePath));
|
||||
Assert.True(File.Exists(Path.Combine(_testPath, "processed", "test.torrent")));
|
||||
}
|
||||
|
|
@ -136,13 +144,20 @@ public class WatchFolderCheckerTests : IDisposable
|
|||
var task = checker.StartAsync(cts.Token);
|
||||
await Task.Delay(500);
|
||||
await cts.CancelAsync();
|
||||
try { await task; } catch (OperationCanceledException) { }
|
||||
|
||||
try
|
||||
{
|
||||
await task;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
// Assert
|
||||
_torrentsServiceMock.Verify(x => x.AddMagnetToDebridQueue(
|
||||
content,
|
||||
It.IsAny<Torrent>()), Times.AtLeastOnce);
|
||||
|
||||
_torrentsServiceMock.Verify(x => x.AddMagnetToDebridQueue(content,
|
||||
It.IsAny<Torrent>()),
|
||||
Times.AtLeastOnce);
|
||||
|
||||
Assert.False(File.Exists(filePath));
|
||||
Assert.True(File.Exists(Path.Combine(_testPath, "processed", "test.magnet")));
|
||||
}
|
||||
|
|
@ -168,14 +183,21 @@ public class WatchFolderCheckerTests : IDisposable
|
|||
var task = checker.StartAsync(cts.Token);
|
||||
await Task.Delay(500);
|
||||
await cts.CancelAsync();
|
||||
try { await task; } catch (OperationCanceledException) { }
|
||||
|
||||
try
|
||||
{
|
||||
await task;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
// Assert
|
||||
_torrentsServiceMock.Verify(x => x.AddNzbFileToDebridQueue(
|
||||
It.Is<Byte[]>(b => b.SequenceEqual(content)),
|
||||
"test.nzb",
|
||||
It.IsAny<Torrent>()), Times.AtLeastOnce);
|
||||
|
||||
_torrentsServiceMock.Verify(x => x.AddNzbFileToDebridQueue(It.Is<Byte[]>(b => b.AsEnumerable().SequenceEqual(content)),
|
||||
"test.nzb",
|
||||
It.IsAny<Torrent>()),
|
||||
Times.AtLeastOnce);
|
||||
|
||||
Assert.False(File.Exists(filePath));
|
||||
Assert.True(File.Exists(Path.Combine(_testPath, "processed", "test.nzb")));
|
||||
}
|
||||
|
|
@ -196,18 +218,28 @@ public class WatchFolderCheckerTests : IDisposable
|
|||
var task = checker.StartAsync(cts.Token);
|
||||
await Task.Delay(500);
|
||||
await cts.CancelAsync();
|
||||
try { await task; } catch (OperationCanceledException) { }
|
||||
|
||||
try
|
||||
{
|
||||
await task;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
// Assert
|
||||
_torrentsServiceMock.Verify(x => x.AddFileToDebridQueue(It.IsAny<Byte[]>(), It.IsAny<Torrent>()), Times.Never);
|
||||
_torrentsServiceMock.Verify(x => x.AddMagnetToDebridQueue(It.IsAny<String>(), It.IsAny<Torrent>()), Times.Never);
|
||||
_torrentsServiceMock.Verify(x => x.AddNzbFileToDebridQueue(It.IsAny<Byte[]>(), It.IsAny<String>(), It.IsAny<Torrent>()), Times.Never);
|
||||
|
||||
|
||||
Assert.True(File.Exists(filePath));
|
||||
}
|
||||
|
||||
private class MockScopeFactory(IServiceScope scope) : IServiceScopeFactory
|
||||
{
|
||||
public IServiceScope CreateScope() => scope;
|
||||
public IServiceScope CreateScope()
|
||||
{
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ public class DownloadHelperTest
|
|||
};
|
||||
|
||||
String fileRelativePath;
|
||||
|
||||
if (OSHelper.IsLinux)
|
||||
{
|
||||
fileRelativePath = "inside/lots/of/subdirectories/file.txt";
|
||||
|
|
@ -226,6 +227,7 @@ public class DownloadHelperTest
|
|||
};
|
||||
|
||||
String fileRelativePath;
|
||||
|
||||
if (OSHelper.IsLinux)
|
||||
{
|
||||
fileRelativePath = "inside/lots/of/subdirectories/file.txt";
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ public class RateLimitHandlerTest
|
|||
{
|
||||
InnerHandler = new MockHttpMessageHandler(HttpStatusCode.TooManyRequests, 3600)
|
||||
};
|
||||
|
||||
var client = new HttpClient(handler);
|
||||
|
||||
// Act & Assert
|
||||
|
|
@ -30,6 +31,7 @@ public class RateLimitHandlerTest
|
|||
{
|
||||
InnerHandler = new MockHttpMessageHandler(HttpStatusCode.TooManyRequests, null)
|
||||
};
|
||||
|
||||
var client = new HttpClient(handler);
|
||||
|
||||
// Act & Assert
|
||||
|
|
@ -42,10 +44,12 @@ public class RateLimitHandlerTest
|
|||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new HttpResponseMessage(statusCode);
|
||||
|
||||
if (retryAfterSeconds.HasValue)
|
||||
{
|
||||
response.Headers.RetryAfter = new System.Net.Http.Headers.RetryConditionHeaderValue(TimeSpan.FromSeconds(retryAfterSeconds.Value));
|
||||
response.Headers.RetryAfter = new(TimeSpan.FromSeconds(retryAfterSeconds.Value));
|
||||
}
|
||||
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,16 +9,16 @@ namespace RdtClient.Service.Test.Services;
|
|||
|
||||
public class NzbTorrentsTest
|
||||
{
|
||||
private readonly Mocks _mocks;
|
||||
private readonly MockFileSystem _fileSystem;
|
||||
private readonly Mocks _mocks;
|
||||
private readonly TorrentsService _torrents;
|
||||
|
||||
public NzbTorrentsTest()
|
||||
{
|
||||
_mocks = new();
|
||||
_fileSystem = new();
|
||||
_torrents = new(
|
||||
_mocks.TorrentsLoggerMock.Object,
|
||||
|
||||
_torrents = new(_mocks.TorrentsLoggerMock.Object,
|
||||
_mocks.TorrentDataMock.Object,
|
||||
_mocks.DownloadsMock.Object,
|
||||
_mocks.ProcessFactoryMock.Object,
|
||||
|
|
@ -28,8 +28,7 @@ public class NzbTorrentsTest
|
|||
null!,
|
||||
null!,
|
||||
null!,
|
||||
null!
|
||||
);
|
||||
null!);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -37,21 +36,26 @@ public class NzbTorrentsTest
|
|||
{
|
||||
// Arrange
|
||||
var nzbLink = "http://example.com/test.nzb";
|
||||
|
||||
var torrent = new Torrent
|
||||
{
|
||||
DownloadClient = DownloadClient.Bezzad
|
||||
};
|
||||
|
||||
_mocks.TorrentDataMock.Setup(t => t.GetByHash(It.IsAny<String>())).ReturnsAsync((Torrent)null!);
|
||||
_mocks.TorrentDataMock.Setup(t => t.Add(
|
||||
null,
|
||||
It.IsAny<String>(),
|
||||
nzbLink,
|
||||
false,
|
||||
DownloadType.Nzb,
|
||||
torrent.DownloadClient,
|
||||
It.IsAny<Torrent>()
|
||||
)).ReturnsAsync(new Torrent { Hash = "mockHash", RdName = "test.nzb" });
|
||||
|
||||
_mocks.TorrentDataMock.Setup(t => t.Add(null,
|
||||
It.IsAny<String>(),
|
||||
nzbLink,
|
||||
false,
|
||||
DownloadType.Nzb,
|
||||
torrent.DownloadClient,
|
||||
It.IsAny<Torrent>()))
|
||||
.ReturnsAsync(new Torrent
|
||||
{
|
||||
Hash = "mockHash",
|
||||
RdName = "test.nzb"
|
||||
});
|
||||
|
||||
// Act
|
||||
var result = await _torrents.AddNzbLinkToDebridQueue(nzbLink, torrent);
|
||||
|
|
@ -60,15 +64,15 @@ public class NzbTorrentsTest
|
|||
Assert.NotNull(result);
|
||||
Assert.Equal("test.nzb", torrent.RdName);
|
||||
Assert.Equal(TorrentStatus.Queued, torrent.RdStatus);
|
||||
_mocks.TorrentDataMock.Verify(t => t.Add(
|
||||
null,
|
||||
It.IsAny<String>(),
|
||||
nzbLink,
|
||||
false,
|
||||
DownloadType.Nzb,
|
||||
torrent.DownloadClient,
|
||||
torrent
|
||||
), Times.Once);
|
||||
|
||||
_mocks.TorrentDataMock.Verify(t => t.Add(null,
|
||||
It.IsAny<String>(),
|
||||
nzbLink,
|
||||
false,
|
||||
DownloadType.Nzb,
|
||||
torrent.DownloadClient,
|
||||
torrent),
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -86,23 +90,30 @@ public class NzbTorrentsTest
|
|||
public async Task AddNzbFileToDebridQueue_ValidFile_AddsToQueue()
|
||||
{
|
||||
// Arrange
|
||||
var nzbContent = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n<nzb xmlns=\"http://www.newzbin.com/DTD/2003/nzb\">\r\n <head>\r\n <meta type=\"title\">Test NZB Title</meta>\r\n </head>\r\n</nzb>";
|
||||
var nzbContent =
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n<nzb xmlns=\"http://www.newzbin.com/DTD/2003/nzb\">\r\n <head>\r\n <meta type=\"title\">Test NZB Title</meta>\r\n </head>\r\n</nzb>";
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(nzbContent);
|
||||
|
||||
var torrent = new Torrent
|
||||
{
|
||||
DownloadClient = DownloadClient.Bezzad
|
||||
};
|
||||
|
||||
_mocks.TorrentDataMock.Setup(t => t.GetByHash(It.IsAny<String>())).ReturnsAsync((Torrent)null!);
|
||||
_mocks.TorrentDataMock.Setup(t => t.Add(
|
||||
null,
|
||||
It.IsAny<String>(),
|
||||
It.IsAny<String>(),
|
||||
true,
|
||||
DownloadType.Nzb,
|
||||
torrent.DownloadClient,
|
||||
It.IsAny<Torrent>()
|
||||
)).ReturnsAsync(new Torrent { Hash = "mockHash", RdName = "Test NZB Title" });
|
||||
|
||||
_mocks.TorrentDataMock.Setup(t => t.Add(null,
|
||||
It.IsAny<String>(),
|
||||
It.IsAny<String>(),
|
||||
true,
|
||||
DownloadType.Nzb,
|
||||
torrent.DownloadClient,
|
||||
It.IsAny<Torrent>()))
|
||||
.ReturnsAsync(new Torrent
|
||||
{
|
||||
Hash = "mockHash",
|
||||
RdName = "Test NZB Title"
|
||||
});
|
||||
|
||||
// Act
|
||||
var result = await _torrents.AddNzbFileToDebridQueue(bytes, "filename.nzb", torrent);
|
||||
|
|
@ -111,15 +122,15 @@ public class NzbTorrentsTest
|
|||
Assert.NotNull(result);
|
||||
Assert.Equal("Test NZB Title", torrent.RdName);
|
||||
Assert.Equal(TorrentStatus.Queued, torrent.RdStatus);
|
||||
_mocks.TorrentDataMock.Verify(t => t.Add(
|
||||
null,
|
||||
It.IsAny<String>(),
|
||||
Convert.ToBase64String(bytes),
|
||||
true,
|
||||
DownloadType.Nzb,
|
||||
torrent.DownloadClient,
|
||||
torrent
|
||||
), Times.Once);
|
||||
|
||||
_mocks.TorrentDataMock.Verify(t => t.Add(null,
|
||||
It.IsAny<String>(),
|
||||
Convert.ToBase64String(bytes),
|
||||
true,
|
||||
DownloadType.Nzb,
|
||||
torrent.DownloadClient,
|
||||
torrent),
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -8,17 +8,17 @@ namespace RdtClient.Service.Test.Services;
|
|||
|
||||
public class QBittorrentTest
|
||||
{
|
||||
private readonly Mock<ILogger<QBittorrent>> _loggerMock;
|
||||
private readonly Mock<Torrents> _torrentsMock;
|
||||
private readonly Mock<Authentication> _authenticationMock;
|
||||
private readonly Mock<ILogger<QBittorrent>> _loggerMock;
|
||||
private readonly QBittorrent _qBittorrent;
|
||||
private readonly Mock<Torrents> _torrentsMock;
|
||||
|
||||
public QBittorrentTest()
|
||||
{
|
||||
_loggerMock = new();
|
||||
_torrentsMock = new(null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!);
|
||||
_authenticationMock = new(null!, null!, null!);
|
||||
|
||||
|
||||
_qBittorrent = new(_loggerMock.Object, null!, _authenticationMock.Object, _torrentsMock.Object, null!);
|
||||
}
|
||||
|
||||
|
|
@ -54,13 +54,14 @@ public class QBittorrentTest
|
|||
Assert.Equal("hash1", result[0].Hash);
|
||||
Assert.Equal("Torrent 1", result[0].Name);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task TorrentInfo_ShouldReport100Percent_WhenDownloadIsComplete()
|
||||
{
|
||||
// Arrange
|
||||
var downloadId = Guid.NewGuid();
|
||||
var torrentId = Guid.NewGuid();
|
||||
|
||||
var allTorrents = new List<Torrent>
|
||||
{
|
||||
new()
|
||||
|
|
@ -72,12 +73,17 @@ public class QBittorrentTest
|
|||
Type = DownloadType.Torrent,
|
||||
Downloads = new List<Download>
|
||||
{
|
||||
new() { DownloadId = downloadId, TorrentId = torrentId }
|
||||
new()
|
||||
{
|
||||
DownloadId = downloadId,
|
||||
TorrentId = torrentId
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_torrentsMock.Setup(m => m.Get()).ReturnsAsync(allTorrents);
|
||||
|
||||
// Local download is also 100%
|
||||
_torrentsMock.Setup(m => m.GetDownloadStats(downloadId)).Returns((0, 1000, 1000));
|
||||
|
||||
|
|
@ -95,6 +101,7 @@ public class QBittorrentTest
|
|||
// Arrange
|
||||
var downloadId = Guid.NewGuid();
|
||||
var torrentId = Guid.NewGuid();
|
||||
|
||||
var allTorrents = new List<Torrent>
|
||||
{
|
||||
new()
|
||||
|
|
@ -106,12 +113,17 @@ public class QBittorrentTest
|
|||
Type = DownloadType.Torrent,
|
||||
Downloads = new List<Download>
|
||||
{
|
||||
new() { DownloadId = downloadId, TorrentId = torrentId }
|
||||
new()
|
||||
{
|
||||
DownloadId = downloadId,
|
||||
TorrentId = torrentId
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_torrentsMock.Setup(m => m.Get()).ReturnsAsync(allTorrents);
|
||||
|
||||
// Local download is 80%
|
||||
_torrentsMock.Setup(m => m.GetDownloadStats(downloadId)).Returns((0, 1000, 800));
|
||||
|
||||
|
|
@ -120,6 +132,7 @@ public class QBittorrentTest
|
|||
|
||||
// Assert
|
||||
Assert.Single(result);
|
||||
|
||||
// Current behavior is (1.0 + 0.8) / 2 = 0.9
|
||||
Assert.Equal(0.9f, result[0].Progress);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using RdtClient.Data.Data;
|
||||
using RdtClient.Data.Enums;
|
||||
using RdtClient.Data.Models.Data;
|
||||
using RdtClient.Data.Models.Internal;
|
||||
|
|
@ -9,9 +10,13 @@ namespace RdtClient.Service.Test.Services;
|
|||
|
||||
public class SabnzbdTest
|
||||
{
|
||||
private readonly AppSettings _appSettings = new()
|
||||
{
|
||||
Port = 6500
|
||||
};
|
||||
|
||||
private readonly Mock<ILogger<Sabnzbd>> _loggerMock = new();
|
||||
private readonly Mock<Torrents> _torrentsMock;
|
||||
private readonly AppSettings _appSettings = new() { Port = 6500 };
|
||||
|
||||
public SabnzbdTest()
|
||||
{
|
||||
|
|
@ -34,7 +39,10 @@ public class SabnzbdTest
|
|||
Downloads = new List<Download>
|
||||
{
|
||||
new()
|
||||
{ BytesTotal = 1000, BytesDone = 500 }
|
||||
{
|
||||
BytesTotal = 1000,
|
||||
BytesDone = 500
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -52,6 +60,7 @@ public class SabnzbdTest
|
|||
Assert.Single(result.Slots);
|
||||
Assert.Equal("hash1", result.Slots[0].NzoId);
|
||||
Assert.Equal("Name 1", result.Slots[0].Filename);
|
||||
|
||||
// (50% debrid + 50% download) / 2 = 50%
|
||||
Assert.Equal("50", result.Slots[0].Percentage);
|
||||
}
|
||||
|
|
@ -71,7 +80,10 @@ public class SabnzbdTest
|
|||
Downloads = new List<Download>
|
||||
{
|
||||
new()
|
||||
{ BytesTotal = 1000, BytesDone = 0 }
|
||||
{
|
||||
BytesTotal = 1000,
|
||||
BytesDone = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -94,6 +106,7 @@ public class SabnzbdTest
|
|||
// Arrange
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var added = now.AddMinutes(-10);
|
||||
|
||||
var torrentList = new List<Torrent>
|
||||
{
|
||||
new()
|
||||
|
|
@ -113,6 +126,7 @@ public class SabnzbdTest
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Overall progress = (1.0 + 0.0) / 2 = 0.5
|
||||
// Elapsed = 10 minutes
|
||||
// Total estimated = 10 / 0.5 = 20 minutes
|
||||
|
|
@ -130,7 +144,7 @@ public class SabnzbdTest
|
|||
var timeLeftParts = result.Slots[0].TimeLeft.Split(':');
|
||||
var hours = Int32.Parse(timeLeftParts[0]);
|
||||
var minutes = Int32.Parse(timeLeftParts[1]);
|
||||
|
||||
|
||||
Assert.Equal(0, hours);
|
||||
Assert.InRange(minutes, 9, 11);
|
||||
}
|
||||
|
|
@ -142,6 +156,7 @@ public class SabnzbdTest
|
|||
var now = DateTimeOffset.UtcNow;
|
||||
var added = now.AddMinutes(-20);
|
||||
var retry = now.AddMinutes(-10);
|
||||
|
||||
var torrentList = new List<Torrent>
|
||||
{
|
||||
new()
|
||||
|
|
@ -162,6 +177,7 @@ public class SabnzbdTest
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Later of Added and Retry is Retry (-10 mins)
|
||||
// Overall progress = (1.0 + 0.0) / 2 = 0.5
|
||||
// Elapsed = 10 minutes
|
||||
|
|
@ -179,7 +195,7 @@ public class SabnzbdTest
|
|||
var timeLeftParts = result.Slots[0].TimeLeft.Split(':');
|
||||
var hours = Int32.Parse(timeLeftParts[0]);
|
||||
var minutes = Int32.Parse(timeLeftParts[1]);
|
||||
|
||||
|
||||
Assert.Equal(0, hours);
|
||||
Assert.InRange(minutes, 9, 11);
|
||||
}
|
||||
|
|
@ -259,7 +275,7 @@ public class SabnzbdTest
|
|||
{
|
||||
// Arrange
|
||||
var savePath = @"C:\Downloads";
|
||||
Data.Data.SettingData.Get.DownloadClient.MappedPath = savePath;
|
||||
SettingData.Get.DownloadClient.MappedPath = savePath;
|
||||
|
||||
var torrentList = new List<Torrent>
|
||||
{
|
||||
|
|
@ -292,7 +308,7 @@ public class SabnzbdTest
|
|||
{
|
||||
// Arrange
|
||||
var savePath = @"C:\Downloads";
|
||||
Data.Data.SettingData.Get.DownloadClient.MappedPath = savePath;
|
||||
SettingData.Get.DownloadClient.MappedPath = savePath;
|
||||
|
||||
var torrentList = new List<Torrent>
|
||||
{
|
||||
|
|
@ -383,7 +399,7 @@ public class SabnzbdTest
|
|||
|
||||
_torrentsMock.Setup(t => t.Get()).ReturnsAsync(torrentList);
|
||||
|
||||
Data.Data.SettingData.Get.General.Categories = "TV, Music, *";
|
||||
SettingData.Get.General.Categories = "TV, Music, *";
|
||||
|
||||
var sabnzbd = new Sabnzbd(_loggerMock.Object, _torrentsMock.Object, _appSettings);
|
||||
|
||||
|
|
|
|||
|
|
@ -522,8 +522,8 @@ public class AllDebridDebridClientTest
|
|||
{
|
||||
public readonly Mock<IAllDebridNetClientFactory> AllDebridClientFactoryMock;
|
||||
public readonly Mock<IAllDebridNETClient> AllDebridClientMock;
|
||||
public readonly Mock<ILogger<AllDebridDebridClient>> LoggerMock;
|
||||
public readonly Mock<IDownloadableFileFilter> FileFilterMock;
|
||||
public readonly Mock<ILogger<AllDebridDebridClient>> LoggerMock;
|
||||
|
||||
public Mocks()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,24 +9,26 @@ using RdtClient.Data.Models.Internal;
|
|||
using RdtClient.Service.Services;
|
||||
using RdtClient.Service.Services.DebridClients;
|
||||
using TorBoxNET;
|
||||
using DownloadClient = RdtClient.Data.Enums.DownloadClient;
|
||||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||
|
||||
namespace RdtClient.Service.Test.Services.TorrentClients;
|
||||
|
||||
public class TorBoxDebridClientTest
|
||||
{
|
||||
private readonly Mock<ILogger<TorBoxDebridClient>> _loggerMock;
|
||||
private readonly Mock<IHttpClientFactory> _httpClientFactoryMock;
|
||||
private readonly Mock<IDownloadableFileFilter> _fileFilterMock;
|
||||
private readonly Mock<IHttpClientFactory> _httpClientFactoryMock;
|
||||
private readonly Mock<ILogger<TorBoxDebridClient>> _loggerMock;
|
||||
|
||||
public TorBoxDebridClientTest()
|
||||
{
|
||||
_loggerMock = new();
|
||||
_httpClientFactoryMock = new();
|
||||
_fileFilterMock = new();
|
||||
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
_httpClientFactoryMock.Setup(m => m.CreateClient(It.IsAny<String>())).Returns(httpClient);
|
||||
|
||||
|
||||
Settings.Get.Provider.ApiKey = "test-api-key";
|
||||
Settings.Get.Provider.Timeout = 100;
|
||||
}
|
||||
|
|
@ -37,11 +39,26 @@ public class TorBoxDebridClientTest
|
|||
// Arrange
|
||||
var torrents = new List<TorrentInfoResult>
|
||||
{
|
||||
new() { Hash = "hash1", Name = "torrent1", Size = 1000, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow }
|
||||
new()
|
||||
{
|
||||
Hash = "hash1",
|
||||
Name = "torrent1",
|
||||
Size = 1000,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
}
|
||||
};
|
||||
|
||||
var nzbs = new List<UsenetInfoResult>
|
||||
{
|
||||
new() { Hash = "hash2", Name = "nzb1", Size = 2000, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow }
|
||||
new()
|
||||
{
|
||||
Hash = "hash2",
|
||||
Name = "nzb1",
|
||||
Size = 2000,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
}
|
||||
};
|
||||
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object);
|
||||
|
|
@ -55,11 +72,11 @@ public class TorBoxDebridClientTest
|
|||
|
||||
// Assert
|
||||
Assert.Equal(2, result.Count);
|
||||
|
||||
|
||||
var torrentResult = result.FirstOrDefault(r => r.Id == "hash1");
|
||||
Assert.NotNull(torrentResult);
|
||||
Assert.Equal(DownloadType.Torrent, torrentResult.Type);
|
||||
|
||||
|
||||
var nzbResult = result.FirstOrDefault(r => r.Id == "hash2");
|
||||
Assert.NotNull(nzbResult);
|
||||
Assert.Equal(DownloadType.Nzb, nzbResult.Type);
|
||||
|
|
@ -70,6 +87,7 @@ public class TorBoxDebridClientTest
|
|||
{
|
||||
// Arrange
|
||||
var hash = "test-hash";
|
||||
|
||||
var availability = new Response<List<AvailableTorrent?>>
|
||||
{
|
||||
Data = new()
|
||||
|
|
@ -78,8 +96,16 @@ public class TorBoxDebridClientTest
|
|||
{
|
||||
Files = new()
|
||||
{
|
||||
new() { Name = "file1.mkv", Size = 100 },
|
||||
new() { Name = "file2.txt", Size = 10 }
|
||||
new()
|
||||
{
|
||||
Name = "file1.mkv",
|
||||
Size = 100
|
||||
},
|
||||
new()
|
||||
{
|
||||
Name = "file2.txt",
|
||||
Size = 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -104,7 +130,12 @@ public class TorBoxDebridClientTest
|
|||
{
|
||||
// Arrange
|
||||
var hash = "test-hash";
|
||||
var torrentAvailability = new Response<List<AvailableTorrent?>> { Data = new() };
|
||||
|
||||
var torrentAvailability = new Response<List<AvailableTorrent?>>
|
||||
{
|
||||
Data = new()
|
||||
};
|
||||
|
||||
var usenetAvailability = new Response<List<AvailableUsenet?>>
|
||||
{
|
||||
Data = new()
|
||||
|
|
@ -113,7 +144,11 @@ public class TorBoxDebridClientTest
|
|||
{
|
||||
Files = new()
|
||||
{
|
||||
new() { Name = "file1.nzb", Size = 200 }
|
||||
new()
|
||||
{
|
||||
Name = "file1.nzb",
|
||||
Size = 200
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -137,8 +172,16 @@ public class TorBoxDebridClientTest
|
|||
{
|
||||
// Arrange
|
||||
var hash = "test-hash";
|
||||
var torrentAvailability = new Response<List<AvailableTorrent?>> { Data = new() };
|
||||
var usenetAvailability = new Response<List<AvailableUsenet?>> { Data = new() };
|
||||
|
||||
var torrentAvailability = new Response<List<AvailableTorrent?>>
|
||||
{
|
||||
Data = new()
|
||||
};
|
||||
|
||||
var usenetAvailability = new Response<List<AvailableUsenet?>>
|
||||
{
|
||||
Data = new()
|
||||
};
|
||||
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object);
|
||||
clientMock.Protected().Setup<Task<Response<List<AvailableTorrent?>>>>("GetTorrentAvailability", hash).ReturnsAsync(torrentAvailability);
|
||||
|
|
@ -164,7 +207,7 @@ public class TorBoxDebridClientTest
|
|||
var torrentsApiMock = new Mock<ITorrentsApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object);
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Torrents).Returns(torrentsApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
|
@ -188,7 +231,7 @@ public class TorBoxDebridClientTest
|
|||
var usenetApiMock = new Mock<IUsenetApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object);
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Usenet).Returns(usenetApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
|
@ -208,17 +251,21 @@ public class TorBoxDebridClientTest
|
|||
RdId = "torrent-id",
|
||||
Type = DownloadType.Torrent
|
||||
};
|
||||
|
||||
var link = "https://torbox.app/d/123/456";
|
||||
|
||||
var torrentsApiMock = new Mock<ITorrentsApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object);
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Torrents).Returns(torrentsApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
torrentsApiMock.Setup(m => m.RequestDownloadAsync(123, 456, false, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<String> { Data = "https://unrestricted-link" });
|
||||
.ReturnsAsync(new Response<String>
|
||||
{
|
||||
Data = "https://unrestricted-link"
|
||||
});
|
||||
|
||||
// Act
|
||||
var result = await clientMock.Object.Unrestrict(torrent, link);
|
||||
|
|
@ -237,17 +284,21 @@ public class TorBoxDebridClientTest
|
|||
RdId = "nzb-id",
|
||||
Type = DownloadType.Nzb
|
||||
};
|
||||
|
||||
var link = "https://torbox.app/d/123/456";
|
||||
|
||||
var usenetApiMock = new Mock<IUsenetApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object);
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Usenet).Returns(usenetApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
usenetApiMock.Setup(m => m.RequestDownloadAsync(123, 456, false, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<String> { Data = "https://unrestricted-link-nzb" });
|
||||
.ReturnsAsync(new Response<String>
|
||||
{
|
||||
Data = "https://unrestricted-link-nzb"
|
||||
});
|
||||
|
||||
// Act
|
||||
var result = await clientMock.Object.Unrestrict(torrent, link);
|
||||
|
|
@ -261,17 +312,32 @@ public class TorBoxDebridClientTest
|
|||
public async Task AddNzbFile_CallsUsenetAddFileAsyncWithName()
|
||||
{
|
||||
// Arrange
|
||||
var bytes = new Byte[] { 1, 2, 3 };
|
||||
var bytes = new Byte[]
|
||||
{
|
||||
1, 2, 3
|
||||
};
|
||||
|
||||
var name = "test-nzb";
|
||||
var usenetApiMock = new Mock<IUsenetApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object) { CallBase = true };
|
||||
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object)
|
||||
{
|
||||
CallBase = true
|
||||
};
|
||||
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Usenet).Returns(usenetApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
usenetApiMock.Setup(m => m.AddFileAsync(bytes, It.IsAny<Int32>(), name, It.IsAny<String?>(), It.IsAny<Boolean>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<UsenetAddResult> { Data = new UsenetAddResult { Hash = "new-hash" } });
|
||||
.ReturnsAsync(new Response<UsenetAddResult>
|
||||
{
|
||||
Data = new()
|
||||
{
|
||||
Hash = "new-hash"
|
||||
}
|
||||
});
|
||||
|
||||
// Act
|
||||
var result = await clientMock.Object.AddNzbFile(bytes, name);
|
||||
|
|
@ -280,15 +346,27 @@ public class TorBoxDebridClientTest
|
|||
Assert.Equal("new-hash", result);
|
||||
usenetApiMock.Verify(m => m.AddFileAsync(bytes, It.IsAny<Int32>(), name, It.IsAny<String?>(), It.IsAny<Boolean>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetDownloadInfos_GeneratesCorrectFakedlLinks_ForIndividualFiles()
|
||||
{
|
||||
// Arrange
|
||||
var files = new List<DebridClientFile>
|
||||
{
|
||||
new() { Id = 1, Path = "file1.mkv", Bytes = 1000 },
|
||||
new() { Id = 2, Path = "file2.mkv", Bytes = 2000 }
|
||||
new()
|
||||
{
|
||||
Id = 1,
|
||||
Path = "file1.mkv",
|
||||
Bytes = 1000
|
||||
},
|
||||
new()
|
||||
{
|
||||
Id = 2,
|
||||
Path = "file2.mkv",
|
||||
Bytes = 2000
|
||||
}
|
||||
};
|
||||
|
||||
var torrent = new Torrent
|
||||
{
|
||||
Hash = "test-hash",
|
||||
|
|
@ -298,12 +376,15 @@ public class TorBoxDebridClientTest
|
|||
var torrentsApiMock = new Mock<ITorrentsApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object);
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Torrents).Returns(torrentsApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
torrentsApiMock.Setup(m => m.GetHashInfoAsync("test-hash", true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new TorrentInfoResult { Id = 12345 });
|
||||
.ReturnsAsync(new TorrentInfoResult
|
||||
{
|
||||
Id = 12345
|
||||
});
|
||||
|
||||
_fileFilterMock.Setup(m => m.IsDownloadable(torrent, It.IsAny<String>(), It.IsAny<Int64>())).Returns(true);
|
||||
|
||||
|
|
@ -325,14 +406,20 @@ public class TorBoxDebridClientTest
|
|||
// Arrange
|
||||
var files = new List<DebridClientFile>
|
||||
{
|
||||
new() { Id = 1, Path = "file1.mkv", Bytes = 1000 }
|
||||
new()
|
||||
{
|
||||
Id = 1,
|
||||
Path = "file1.mkv",
|
||||
Bytes = 1000
|
||||
}
|
||||
};
|
||||
|
||||
var torrent = new Torrent
|
||||
{
|
||||
Hash = "test-hash",
|
||||
RdName = "TestTorrent",
|
||||
RdFiles = JsonConvert.SerializeObject(files),
|
||||
DownloadClient = Data.Enums.DownloadClient.Aria2c
|
||||
DownloadClient = DownloadClient.Aria2c
|
||||
};
|
||||
|
||||
Settings.Get.Provider.PreferZippedDownloads = true;
|
||||
|
|
@ -340,12 +427,15 @@ public class TorBoxDebridClientTest
|
|||
var torrentsApiMock = new Mock<ITorrentsApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object);
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Torrents).Returns(torrentsApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
torrentsApiMock.Setup(m => m.GetHashInfoAsync("test-hash", true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new TorrentInfoResult { Id = 12345 });
|
||||
.ReturnsAsync(new TorrentInfoResult
|
||||
{
|
||||
Id = 12345
|
||||
});
|
||||
|
||||
_fileFilterMock.Setup(m => m.IsDownloadable(torrent, It.IsAny<String>(), It.IsAny<Int64>())).Returns(true);
|
||||
|
||||
|
|
@ -367,17 +457,21 @@ public class TorBoxDebridClientTest
|
|||
{
|
||||
Type = DownloadType.Torrent
|
||||
};
|
||||
|
||||
var link = "https://torbox.app/fakedl/12345/6789";
|
||||
|
||||
var torrentsApiMock = new Mock<ITorrentsApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object);
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Torrents).Returns(torrentsApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
torrentsApiMock.Setup(m => m.RequestDownloadAsync(12345, 6789, false, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<String> { Data = "https://real-download-link" });
|
||||
.ReturnsAsync(new Response<String>
|
||||
{
|
||||
Data = "https://real-download-link"
|
||||
});
|
||||
|
||||
// Act
|
||||
var result = await clientMock.Object.Unrestrict(torrent, link);
|
||||
|
|
@ -395,17 +489,21 @@ public class TorBoxDebridClientTest
|
|||
{
|
||||
Type = DownloadType.Torrent
|
||||
};
|
||||
|
||||
var link = "https://torbox.app/fakedl/12345/zip";
|
||||
|
||||
var torrentsApiMock = new Mock<ITorrentsApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object);
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Torrents).Returns(torrentsApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
torrentsApiMock.Setup(m => m.RequestDownloadAsync(12345, 0, true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<String> { Data = "https://real-zip-download-link" });
|
||||
.ReturnsAsync(new Response<String>
|
||||
{
|
||||
Data = "https://real-zip-download-link"
|
||||
});
|
||||
|
||||
// Act
|
||||
var result = await clientMock.Object.Unrestrict(torrent, link);
|
||||
|
|
@ -414,14 +512,21 @@ public class TorBoxDebridClientTest
|
|||
Assert.Equal("https://real-zip-download-link", result);
|
||||
torrentsApiMock.Verify(m => m.RequestDownloadAsync(12345, 0, true, It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetDownloadInfos_GeneratesCorrectFakedlLinks_ForUsenet()
|
||||
{
|
||||
// Arrange
|
||||
var files = new List<DebridClientFile>
|
||||
{
|
||||
new() { Id = 1, Path = "file1.nzb", Bytes = 1000 }
|
||||
new()
|
||||
{
|
||||
Id = 1,
|
||||
Path = "file1.nzb",
|
||||
Bytes = 1000
|
||||
}
|
||||
};
|
||||
|
||||
var torrent = new Torrent
|
||||
{
|
||||
Type = DownloadType.Nzb,
|
||||
|
|
@ -432,12 +537,19 @@ public class TorBoxDebridClientTest
|
|||
var usenetApiMock = new Mock<IUsenetApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object);
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Usenet).Returns(usenetApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
usenetApiMock.Setup(m => m.GetCurrentAsync(true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new List<UsenetInfoResult> { new() { Hash = "nzb-hash", Id = 98765 } });
|
||||
.ReturnsAsync(new List<UsenetInfoResult>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Hash = "nzb-hash",
|
||||
Id = 98765
|
||||
}
|
||||
});
|
||||
|
||||
_fileFilterMock.Setup(m => m.IsDownloadable(torrent, It.IsAny<String>(), It.IsAny<Int64>())).Returns(true);
|
||||
|
||||
|
|
@ -460,17 +572,21 @@ public class TorBoxDebridClientTest
|
|||
{
|
||||
Type = DownloadType.Nzb
|
||||
};
|
||||
|
||||
var link = "https://torbox.app/fakedl/98765/4321";
|
||||
|
||||
var usenetApiMock = new Mock<IUsenetApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object);
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Usenet).Returns(usenetApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
usenetApiMock.Setup(m => m.RequestDownloadAsync(98765, 4321, false, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<String> { Data = "https://real-usenet-link" });
|
||||
.ReturnsAsync(new Response<String>
|
||||
{
|
||||
Data = "https://real-usenet-link"
|
||||
});
|
||||
|
||||
// Act
|
||||
var result = await clientMock.Object.Unrestrict(torrent, link);
|
||||
|
|
@ -479,6 +595,7 @@ public class TorBoxDebridClientTest
|
|||
Assert.Equal("https://real-usenet-link", result);
|
||||
usenetApiMock.Verify(m => m.RequestDownloadAsync(98765, 4321, false, It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddTorrentMagnet_ThrowsRateLimitException_OnActiveLimit()
|
||||
{
|
||||
|
|
@ -486,19 +603,33 @@ public class TorBoxDebridClientTest
|
|||
var magnetLink = "magnet:?xt=urn:btih:test";
|
||||
var torrentsApiMock = new Mock<ITorrentsApi>();
|
||||
var userApiMock = new Mock<IUserApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object) { CallBase = true };
|
||||
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object)
|
||||
{
|
||||
CallBase = true
|
||||
};
|
||||
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Torrents).Returns(torrentsApiMock.Object);
|
||||
torBoxClientMock.Setup(m => m.User).Returns(userApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
userApiMock.Setup(m => m.GetAsync(true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<User> { Data = new User { Settings = new UserSettings { SeedTorrents = 5 } } });
|
||||
.ReturnsAsync(new Response<User>
|
||||
{
|
||||
Data = new()
|
||||
{
|
||||
Settings = new()
|
||||
{
|
||||
SeedTorrents = 5
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
torrentsApiMock.Setup(m => m.AddMagnetAsync(magnetLink, 5, It.IsAny<Boolean>(), It.IsAny<String?>(), false, It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new TorBoxException("active_limit", "Active limit reached"));
|
||||
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<RateLimitException>(() => clientMock.Object.AddTorrentMagnet(magnetLink));
|
||||
torrentsApiMock.Verify(m => m.AddMagnetAsync(magnetLink, 5, It.IsAny<Boolean>(), It.IsAny<String?>(), false, It.IsAny<CancellationToken>()), Times.Once);
|
||||
|
|
@ -512,18 +643,32 @@ public class TorBoxDebridClientTest
|
|||
var magnetLink = "magnet:?xt=urn:btih:test";
|
||||
var torrentsApiMock = new Mock<ITorrentsApi>();
|
||||
var userApiMock = new Mock<IUserApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object) { CallBase = true };
|
||||
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object)
|
||||
{
|
||||
CallBase = true
|
||||
};
|
||||
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Torrents).Returns(torrentsApiMock.Object);
|
||||
torBoxClientMock.Setup(m => m.User).Returns(userApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
userApiMock.Setup(m => m.GetAsync(true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<User> { Data = new User { Settings = new UserSettings { SeedTorrents = 5 } } });
|
||||
.ReturnsAsync(new Response<User>
|
||||
{
|
||||
Data = new()
|
||||
{
|
||||
Settings = new()
|
||||
{
|
||||
SeedTorrents = 5
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
torrentsApiMock.Setup(m => m.AddMagnetAsync(magnetLink, 5, It.IsAny<Boolean>(), It.IsAny<String?>(), false, It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new Exception("slow_down"));
|
||||
.ThrowsAsync(new("slow_down"));
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<RateLimitException>(() => clientMock.Object.AddTorrentMagnet(magnetLink));
|
||||
|
|
@ -536,15 +681,29 @@ public class TorBoxDebridClientTest
|
|||
var magnetLink = "magnet:?xt=urn:btih:test";
|
||||
var torrentsApiMock = new Mock<ITorrentsApi>();
|
||||
var userApiMock = new Mock<IUserApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object) { CallBase = true };
|
||||
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object)
|
||||
{
|
||||
CallBase = true
|
||||
};
|
||||
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Torrents).Returns(torrentsApiMock.Object);
|
||||
torBoxClientMock.Setup(m => m.User).Returns(userApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
userApiMock.Setup(m => m.GetAsync(true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<User> { Data = new User { Settings = new UserSettings { SeedTorrents = 5 } } });
|
||||
.ReturnsAsync(new Response<User>
|
||||
{
|
||||
Data = new()
|
||||
{
|
||||
Settings = new()
|
||||
{
|
||||
SeedTorrents = 5
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
torrentsApiMock.Setup(m => m.AddMagnetAsync(magnetLink, 5, It.IsAny<Boolean>(), It.IsAny<String?>(), false, It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new RateLimitException("TorBox rate limit exceeded", TimeSpan.FromMinutes(60)));
|
||||
|
|
@ -561,18 +720,32 @@ public class TorBoxDebridClientTest
|
|||
var magnetLink = "magnet:?xt=urn:btih:test";
|
||||
var torrentsApiMock = new Mock<ITorrentsApi>();
|
||||
var userApiMock = new Mock<IUserApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object) { CallBase = true };
|
||||
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object)
|
||||
{
|
||||
CallBase = true
|
||||
};
|
||||
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Torrents).Returns(torrentsApiMock.Object);
|
||||
torBoxClientMock.Setup(m => m.User).Returns(userApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
userApiMock.Setup(m => m.GetAsync(true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<User> { Data = new User { Settings = new UserSettings { SeedTorrents = 5 } } });
|
||||
.ReturnsAsync(new Response<User>
|
||||
{
|
||||
Data = new()
|
||||
{
|
||||
Settings = new()
|
||||
{
|
||||
SeedTorrents = 5
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
torrentsApiMock.Setup(m => m.AddMagnetAsync(magnetLink, 5, It.IsAny<Boolean>(), It.IsAny<String?>(), false, It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new Exception("Wrapped", new RateLimitException("TorBox rate limit exceeded", TimeSpan.FromMinutes(60))));
|
||||
.ThrowsAsync(new("Wrapped", new RateLimitException("TorBox rate limit exceeded", TimeSpan.FromMinutes(60))));
|
||||
|
||||
// Act & Assert
|
||||
var ex = await Assert.ThrowsAsync<RateLimitException>(() => clientMock.Object.AddTorrentMagnet(magnetLink));
|
||||
|
|
@ -583,18 +756,36 @@ public class TorBoxDebridClientTest
|
|||
public async Task AddTorrentFile_ThrowsRateLimitException_OnActiveLimit()
|
||||
{
|
||||
// Arrange
|
||||
var bytes = new Byte[] { 1, 2, 3 };
|
||||
var bytes = new Byte[]
|
||||
{
|
||||
1, 2, 3
|
||||
};
|
||||
|
||||
var torrentsApiMock = new Mock<ITorrentsApi>();
|
||||
var userApiMock = new Mock<IUserApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object) { CallBase = true };
|
||||
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object)
|
||||
{
|
||||
CallBase = true
|
||||
};
|
||||
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Torrents).Returns(torrentsApiMock.Object);
|
||||
torBoxClientMock.Setup(m => m.User).Returns(userApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
userApiMock.Setup(m => m.GetAsync(true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<User> { Data = new User { Settings = new UserSettings { SeedTorrents = 5 } } });
|
||||
.ReturnsAsync(new Response<User>
|
||||
{
|
||||
Data = new()
|
||||
{
|
||||
Settings = new()
|
||||
{
|
||||
SeedTorrents = 5
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
torrentsApiMock.Setup(m => m.AddFileAsync(bytes, 5, It.IsAny<Boolean>(), It.IsAny<String?>(), false, It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new TorBoxException("active_limit", "Active limit reached"));
|
||||
|
|
@ -611,12 +802,17 @@ public class TorBoxDebridClientTest
|
|||
// Arrange
|
||||
var nzbLink = "https://example.com/test.nzb";
|
||||
var usenetApiMock = new Mock<IUsenetApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object) { CallBase = true };
|
||||
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object)
|
||||
{
|
||||
CallBase = true
|
||||
};
|
||||
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Usenet).Returns(usenetApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
usenetApiMock.Setup(m => m.AddLinkAsync(nzbLink, It.IsAny<Int32>(), It.IsAny<String?>(), It.IsAny<String?>(), false, It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new TorBoxException("active_limit", "Active limit reached"));
|
||||
|
||||
|
|
@ -630,15 +826,24 @@ public class TorBoxDebridClientTest
|
|||
public async Task AddNzbFile_ThrowsRateLimitException_OnActiveLimit()
|
||||
{
|
||||
// Arrange
|
||||
var bytes = new Byte[] { 1, 2, 3 };
|
||||
var bytes = new Byte[]
|
||||
{
|
||||
1, 2, 3
|
||||
};
|
||||
|
||||
var name = "test.nzb";
|
||||
var usenetApiMock = new Mock<IUsenetApi>();
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object) { CallBase = true };
|
||||
|
||||
var clientMock = new Mock<TorBoxDebridClient>(_loggerMock.Object, _httpClientFactoryMock.Object, _fileFilterMock.Object)
|
||||
{
|
||||
CallBase = true
|
||||
};
|
||||
|
||||
var torBoxClientMock = new Mock<ITorBoxNetClient>();
|
||||
|
||||
|
||||
torBoxClientMock.Setup(m => m.Usenet).Returns(usenetApiMock.Object);
|
||||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient").Returns(torBoxClientMock.Object);
|
||||
|
||||
|
||||
usenetApiMock.Setup(m => m.AddFileAsync(bytes, It.IsAny<Int32>(), name, It.IsAny<String?>(), false, It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new TorBoxException("active_limit", "Active limit reached"));
|
||||
|
||||
|
|
@ -657,6 +862,7 @@ public class TorBoxDebridClientTest
|
|||
RdId = "test-rd-id",
|
||||
RdStatus = TorrentStatus.Downloading
|
||||
};
|
||||
|
||||
var torrentClientTorrent = new DebridClientTorrent
|
||||
{
|
||||
Status = "failed (Aborted, cannot be completed - https://sabnzbd.org/not-complete)",
|
||||
|
|
@ -682,6 +888,7 @@ public class TorBoxDebridClientTest
|
|||
RdStatus = TorrentStatus.Downloading,
|
||||
RdName = "test-torrent"
|
||||
};
|
||||
|
||||
var torrentClientTorrent = new DebridClientTorrent
|
||||
{
|
||||
Status = "some-unknown-status",
|
||||
|
|
@ -694,13 +901,11 @@ public class TorBoxDebridClientTest
|
|||
await clientMock.Object.UpdateData(torrent, torrentClientTorrent);
|
||||
|
||||
// Assert
|
||||
_loggerMock.Verify(
|
||||
x => x.Log(
|
||||
Microsoft.Extensions.Logging.LogLevel.Information,
|
||||
It.IsAny<EventId>(),
|
||||
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("unmapped status") && v.ToString()!.Contains("some-unknown-status")),
|
||||
It.IsAny<Exception>(),
|
||||
It.Is<Func<It.IsAnyType, Exception?, String>>((v, t) => true)),
|
||||
Times.Once);
|
||||
_loggerMock.Verify(x => x.Log(LogLevel.Information,
|
||||
It.IsAny<EventId>(),
|
||||
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("unmapped status") && v.ToString()!.Contains("some-unknown-status")),
|
||||
It.IsAny<Exception>(),
|
||||
It.Is<Func<It.IsAnyType, Exception?, String>>((v, t) => true)),
|
||||
Times.Once);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,12 @@ using RdtClient.Data.Models.Internal;
|
|||
using RdtClient.Service.Services;
|
||||
using RdtClient.Service.Test.Helpers;
|
||||
using RdtClient.Service.Wrappers;
|
||||
using DownloadClient = RdtClient.Data.Enums.DownloadClient;
|
||||
using TorrentsService = RdtClient.Service.Services.Torrents;
|
||||
|
||||
namespace RdtClient.Service.Test.Services;
|
||||
|
||||
class Mocks
|
||||
internal class Mocks
|
||||
{
|
||||
public readonly Mock<IDownloads> DownloadsMock;
|
||||
public readonly Mock<IEnricher> EnricherMock;
|
||||
|
|
@ -92,6 +93,7 @@ public class TorrentsTest
|
|||
String downloadPath;
|
||||
String torrentPath;
|
||||
String filePath;
|
||||
|
||||
if (OSHelper.IsLinux)
|
||||
{
|
||||
downloadPath = $"{settings.DownloadClient.DownloadPath}/{torrent.Category}";
|
||||
|
|
@ -337,19 +339,23 @@ public class TorrentsTest
|
|||
{
|
||||
// Arrange
|
||||
var mocks = new Mocks();
|
||||
|
||||
var torrent = new Torrent
|
||||
{
|
||||
TorrentId = Guid.NewGuid()
|
||||
};
|
||||
var nzbContent = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n<nzb xmlns=\"http://www.newzbin.com/DTD/2003/nzb\">\r\n <head>\r\n <meta type=\"title\">Test NZB Title</meta>\r\n </head>\r\n</nzb>";
|
||||
|
||||
var nzbContent =
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n<nzb xmlns=\"http://www.newzbin.com/DTD/2003/nzb\">\r\n <head>\r\n <meta type=\"title\">Test NZB Title</meta>\r\n </head>\r\n</nzb>";
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(nzbContent);
|
||||
|
||||
|
||||
mocks.TorrentDataMock.Setup(t => t.Add(It.IsAny<String>(),
|
||||
It.IsAny<String>(),
|
||||
It.IsAny<String>(),
|
||||
true,
|
||||
DownloadType.Nzb,
|
||||
It.IsAny<Data.Enums.DownloadClient>(),
|
||||
It.IsAny<DownloadClient>(),
|
||||
It.IsAny<Torrent>()))
|
||||
.ReturnsAsync(new Torrent());
|
||||
|
||||
|
|
@ -374,8 +380,9 @@ public class TorrentsTest
|
|||
It.IsAny<String>(),
|
||||
true,
|
||||
DownloadType.Nzb,
|
||||
It.IsAny<Data.Enums.DownloadClient>(),
|
||||
It.IsAny<Torrent>()), Times.Once);
|
||||
It.IsAny<DownloadClient>(),
|
||||
It.IsAny<Torrent>()),
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -383,10 +390,12 @@ public class TorrentsTest
|
|||
{
|
||||
// Arrange
|
||||
var mocks = new Mocks();
|
||||
|
||||
var torrent = new Torrent
|
||||
{
|
||||
TorrentId = Guid.NewGuid()
|
||||
};
|
||||
|
||||
var link = "http://example.com/test.nzb";
|
||||
|
||||
mocks.TorrentDataMock.Setup(t => t.Add(It.IsAny<String>(),
|
||||
|
|
@ -394,7 +403,7 @@ public class TorrentsTest
|
|||
It.IsAny<String>(),
|
||||
false,
|
||||
DownloadType.Nzb,
|
||||
It.IsAny<Data.Enums.DownloadClient>(),
|
||||
It.IsAny<DownloadClient>(),
|
||||
It.IsAny<Torrent>()))
|
||||
.ReturnsAsync(new Torrent());
|
||||
|
||||
|
|
@ -419,7 +428,8 @@ public class TorrentsTest
|
|||
link,
|
||||
false,
|
||||
DownloadType.Nzb,
|
||||
It.IsAny<Data.Enums.DownloadClient>(),
|
||||
It.IsAny<Torrent>()), Times.Once);
|
||||
It.IsAny<DownloadClient>(),
|
||||
It.IsAny<Torrent>()),
|
||||
Times.Once);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,11 +81,11 @@ public class UpdateChecker(ILogger<UpdateChecker> logger, IHttpClientFactory htt
|
|||
|
||||
private async Task<T?> GitHubRequest<T>(String endpoint, CancellationToken cancellationToken)
|
||||
{
|
||||
var httpClient = httpClientFactory.CreateClient();
|
||||
httpClient.DefaultRequestHeaders.UserAgent.Add(new("RdtClient", CurrentVersion));
|
||||
var response = await httpClient.GetStringAsync($"https://api.github.com{endpoint}", cancellationToken);
|
||||
|
||||
return JsonConvert.DeserializeObject<T>(response);
|
||||
var httpClient = httpClientFactory.CreateClient();
|
||||
httpClient.DefaultRequestHeaders.UserAgent.Add(new("RdtClient", CurrentVersion));
|
||||
var response = await httpClient.GetStringAsync($"https://api.github.com{endpoint}", cancellationToken);
|
||||
|
||||
return JsonConvert.DeserializeObject<T>(response);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,11 +29,13 @@ public class WatchFolderChecker(ILogger<WatchFolderChecker> logger, IServiceProv
|
|||
try
|
||||
{
|
||||
var nextCheck = _prevCheck.AddSeconds(Math.Max(Settings.Get.Watch.Interval, 10));
|
||||
|
||||
if (DateTime.Now < nextCheck)
|
||||
{
|
||||
var delay = nextCheck - DateTime.Now;
|
||||
await Task.Delay(delay, stoppingToken);
|
||||
}
|
||||
|
||||
_prevCheck = DateTime.Now;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(Settings.Get.Watch.Path))
|
||||
|
|
@ -54,7 +56,6 @@ public class WatchFolderChecker(ILogger<WatchFolderChecker> logger, IServiceProv
|
|||
errorStorePath = Settings.Get.Watch.ErrorPath;
|
||||
}
|
||||
|
||||
|
||||
var torrentFiles = Directory.GetFiles(Settings.Get.Watch.Path, "*.*", SearchOption.TopDirectoryOnly);
|
||||
|
||||
foreach (var torrentFile in torrentFiles)
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ public static class DiConfig
|
|||
public static void RegisterHttpClients(this IServiceCollection services)
|
||||
{
|
||||
services.AddHttpClient();
|
||||
|
||||
services.ConfigureHttpClientDefaults(builder =>
|
||||
{
|
||||
builder.ConfigureHttpClient(httpClient =>
|
||||
|
|
@ -74,7 +75,7 @@ public static class DiConfig
|
|||
});
|
||||
|
||||
services.AddTransient<RateLimitHandler>();
|
||||
|
||||
|
||||
services.AddHttpClient(RD_CLIENT)
|
||||
.AddHttpMessageHandler<RateLimitHandler>()
|
||||
.AddResilienceHandler("rd_client_handler", ConfigureResiliencePipeline);
|
||||
|
|
@ -90,12 +91,14 @@ public static class DiConfig
|
|||
{
|
||||
builder.AddTimeout(new TimeoutStrategyOptions
|
||||
{
|
||||
TimeoutGenerator = _ => new ValueTask<TimeSpan>(TimeSpan.FromSeconds(Settings.Get.Provider.Timeout))
|
||||
TimeoutGenerator = _ => new(TimeSpan.FromSeconds(Settings.Get.Provider.Timeout))
|
||||
});
|
||||
|
||||
builder.AddRateLimitHeaders(options =>
|
||||
{
|
||||
options.EnableProactiveThrottling = true;
|
||||
});
|
||||
|
||||
builder.AddRetry(new()
|
||||
{
|
||||
ShouldHandle = args => args.Outcome switch
|
||||
|
|
@ -126,10 +129,10 @@ public static class DiConfig
|
|||
throw new RateLimitException("Provider rate limit exceeded", delay);
|
||||
}
|
||||
|
||||
return new ValueTask<TimeSpan?>(delay);
|
||||
return new(delay);
|
||||
}
|
||||
|
||||
return new ValueTask<TimeSpan?>((TimeSpan?)null);
|
||||
return new((TimeSpan?)null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Reflection;
|
||||
using RdtClient.Service.Services;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
using RdtClient.Service.Services;
|
||||
|
||||
namespace RdtClient.Service.Helpers;
|
||||
|
||||
|
|
@ -11,18 +12,21 @@ public class CredentialRedactorEnricher : ILogEventEnricher
|
|||
var sensitiveValues = new List<String>();
|
||||
|
||||
var apiKey = Settings.Get.Provider.ApiKey;
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(apiKey) && apiKey.Length > 5)
|
||||
{
|
||||
sensitiveValues.Add(apiKey);
|
||||
}
|
||||
|
||||
var aria2Secret = Settings.Get.DownloadClient.Aria2cSecret;
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(aria2Secret) && aria2Secret.Length > 5)
|
||||
{
|
||||
sensitiveValues.Add(aria2Secret);
|
||||
}
|
||||
|
||||
var dsPassword = Settings.Get.DownloadClient.DownloadStationPassword;
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(dsPassword) && dsPassword.Length > 5)
|
||||
{
|
||||
sensitiveValues.Add(dsPassword);
|
||||
|
|
@ -39,15 +43,16 @@ public class CredentialRedactorEnricher : ILogEventEnricher
|
|||
if (logEvent.MessageTemplate.Text.Contains(sensitiveValue))
|
||||
{
|
||||
var newText = logEvent.MessageTemplate.Text.Replace(sensitiveValue, "*****");
|
||||
|
||||
var field = typeof(MessageTemplate).GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
|
||||
|
||||
var field = typeof(MessageTemplate).GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
.FirstOrDefault(f => f.FieldType == typeof(String));
|
||||
|
||||
|
||||
field?.SetValue(logEvent.MessageTemplate, newText);
|
||||
}
|
||||
|
||||
// Redact in properties
|
||||
var propertiesToUpdate = new List<LogEventProperty>();
|
||||
|
||||
foreach (var property in logEvent.Properties)
|
||||
{
|
||||
if (property.Value is ScalarValue scalarValue && scalarValue.Value is String stringValue && stringValue.Contains(sensitiveValue))
|
||||
|
|
|
|||
|
|
@ -79,13 +79,14 @@ public static class FileHelper
|
|||
public static String RemoveInvalidFileNameChars(String filename)
|
||||
{
|
||||
var invalidChars = Path.GetInvalidFileNameChars()
|
||||
.Concat(['/', '\\'])
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
.Concat(['/', '\\'])
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
|
||||
var cleaned = String.Concat(filename.Split(invalidChars));
|
||||
cleaned = cleaned.Replace("..", "");
|
||||
cleaned = cleaned.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,11 @@ public static class FileSizeHelper
|
|||
return "0 B";
|
||||
}
|
||||
|
||||
String[] units = { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
|
||||
String[] units =
|
||||
{
|
||||
"B", "KB", "MB", "GB", "TB", "PB", "EB"
|
||||
};
|
||||
|
||||
var unitIndex = 0;
|
||||
Double size = bytes.Value;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public static class Logger
|
|||
}
|
||||
|
||||
var stats = TorrentRunner.GetStats(download.DownloadId);
|
||||
var done = (Int32)((Double)stats.BytesDone / stats.BytesTotal * 100);
|
||||
var done = (Int32)(((Double)stats.BytesDone / stats.BytesTotal) * 100);
|
||||
|
||||
if (done < 0 || Double.IsNaN(done) || Double.IsInfinity(done))
|
||||
{
|
||||
|
|
@ -32,4 +32,4 @@ public static class Logger
|
|||
{
|
||||
return $"for torrent {torrent.RdName} ({torrent.RdId} - {torrent.RdStatusRaw} {torrent.RdProgress}%) ({torrent.TorrentId})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public class RateLimitHandler : DelegatingHandler
|
|||
}
|
||||
|
||||
response.Dispose();
|
||||
|
||||
throw new RateLimitException("TorBox rate limit exceeded", delay);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,12 +18,14 @@ public class SabnzbdHandler(Authentication authentication, IHttpContextAccessor
|
|||
if (context.User.Identity?.IsAuthenticated == true)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Settings.Get.General.AuthenticationType == AuthenticationType.None)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -34,6 +36,7 @@ public class SabnzbdHandler(Authentication authentication, IHttpContextAccessor
|
|||
String? GetParam(String name)
|
||||
{
|
||||
var value = request.Query[name].ToString();
|
||||
|
||||
if (String.IsNullOrWhiteSpace(value) && request.HasFormContentType)
|
||||
{
|
||||
value = request.Form[name].ToString();
|
||||
|
|
@ -48,20 +51,22 @@ public class SabnzbdHandler(Authentication authentication, IHttpContextAccessor
|
|||
if (!String.IsNullOrWhiteSpace(maUsername) && !String.IsNullOrWhiteSpace(maPassword))
|
||||
{
|
||||
var loginResult = await authentication.Login(maUsername, maPassword);
|
||||
|
||||
if (loginResult.Succeeded)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Invalid credentials provided
|
||||
context.Fail();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Authentication required but missing credentials
|
||||
context.Fail();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,32 +56,7 @@ public class AllDebridDebridClient(ILogger<AllDebridDebridClient> logger, IAllDe
|
|||
{
|
||||
private static readonly Int64 SessionId = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
private static List<DebridClientTorrent> _cache = [];
|
||||
private static Int64 _sessionCounter = 0;
|
||||
|
||||
private static DebridClientTorrent Map(Magnet torrent)
|
||||
{
|
||||
var files = GetFiles(torrent.Files);
|
||||
return new()
|
||||
{
|
||||
Id = torrent.Id.ToString(),
|
||||
Filename = torrent.Filename ?? "",
|
||||
OriginalFilename = torrent.Filename,
|
||||
Hash = torrent.Hash ?? "",
|
||||
Bytes = torrent.Size ?? 0,
|
||||
OriginalBytes = torrent.Size ?? 0,
|
||||
Host = null,
|
||||
Split = 0,
|
||||
Progress = (Int64)Math.Round((torrent.Downloaded ?? 0) * 100.0 / (torrent.Size ?? 1)),
|
||||
Status = torrent.Status,
|
||||
StatusCode = torrent.StatusCode ?? 0,
|
||||
Added = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(torrent.UploadDate ?? 0),
|
||||
Files = files,
|
||||
Links = [],
|
||||
Ended = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(torrent.CompletionDate ?? 0),
|
||||
Speed = torrent.DownloadSpeed,
|
||||
Seeders = torrent.Seeders
|
||||
};
|
||||
}
|
||||
private static Int64 _sessionCounter;
|
||||
|
||||
public async Task<IList<DebridClientTorrent>> GetDownloads()
|
||||
{
|
||||
|
|
@ -141,7 +116,7 @@ public class AllDebridDebridClient(ILogger<AllDebridDebridClient> logger, IAllDe
|
|||
return resultId;
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("slow_down", StringComparison.OrdinalIgnoreCase) ||
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
|
|
@ -163,7 +138,7 @@ public class AllDebridDebridClient(ILogger<AllDebridDebridClient> logger, IAllDe
|
|||
return resultId;
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("slow_down", StringComparison.OrdinalIgnoreCase) ||
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
|
|
@ -306,12 +281,39 @@ public class AllDebridDebridClient(ILogger<AllDebridDebridClient> logger, IAllDe
|
|||
return Task.FromResult(download.FileName);
|
||||
}
|
||||
|
||||
private static DebridClientTorrent Map(Magnet torrent)
|
||||
{
|
||||
var files = GetFiles(torrent.Files);
|
||||
|
||||
return new()
|
||||
{
|
||||
Id = torrent.Id.ToString(),
|
||||
Filename = torrent.Filename ?? "",
|
||||
OriginalFilename = torrent.Filename,
|
||||
Hash = torrent.Hash ?? "",
|
||||
Bytes = torrent.Size ?? 0,
|
||||
OriginalBytes = torrent.Size ?? 0,
|
||||
Host = null,
|
||||
Split = 0,
|
||||
Progress = (Int64)Math.Round(((torrent.Downloaded ?? 0) * 100.0) / (torrent.Size ?? 1)),
|
||||
Status = torrent.Status,
|
||||
StatusCode = torrent.StatusCode ?? 0,
|
||||
Added = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(torrent.UploadDate ?? 0),
|
||||
Files = files,
|
||||
Links = [],
|
||||
Ended = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(torrent.CompletionDate ?? 0),
|
||||
Speed = torrent.DownloadSpeed,
|
||||
Seeders = torrent.Seeders
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<DebridClientTorrent> GetInfo(String torrentId)
|
||||
{
|
||||
var result = await allDebridNetClientFactory.GetClient().Magnet.StatusAsync(torrentId) ?? throw new($"Unable to find magnet with ID {torrentId}");
|
||||
|
||||
return Map(result);
|
||||
}
|
||||
|
||||
private static List<DebridClientFile> GetFiles(List<File>? files, String parentPath = "")
|
||||
{
|
||||
if (files == null)
|
||||
|
|
@ -325,7 +327,7 @@ public class AllDebridDebridClient(ILogger<AllDebridDebridClient> logger, IAllDe
|
|||
? file.FolderOrFileName
|
||||
: Path.Combine(parentPath, file.FolderOrFileName);
|
||||
|
||||
var result = new List<DebridClientFile>();
|
||||
var result = new List<DebridClientFile>();
|
||||
|
||||
// If it's a file (has size)
|
||||
if (file.Size.HasValue)
|
||||
|
|
|
|||
|
|
@ -14,78 +14,6 @@ namespace RdtClient.Service.Services.DebridClients;
|
|||
|
||||
public class DebridLinkClient(ILogger<DebridLinkClient> logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter) : IDebridClient
|
||||
{
|
||||
private DebridLinkFrNETClient GetClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
var apiKey = Settings.Get.Provider.ApiKey;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
throw new("DebridLink API Key not set in the settings");
|
||||
}
|
||||
|
||||
var httpClient = httpClientFactory.CreateClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(Settings.Get.Provider.Timeout);
|
||||
|
||||
var debridLinkClient = new DebridLinkFrNETClient(apiKey, httpClient);
|
||||
|
||||
return debridLinkClient;
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
foreach (var inner in ae.InnerExceptions)
|
||||
{
|
||||
logger.LogError(inner, $"The connection to DebridLink has failed: {inner.Message}");
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to DebridLink has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to DebridLink has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private DebridClientTorrent Map(Torrent torrent)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Id = torrent.Id ?? "",
|
||||
Filename = torrent.Name ?? "",
|
||||
OriginalFilename = torrent.Name ?? "",
|
||||
Hash = torrent.HashString ?? "",
|
||||
Bytes = torrent.TotalSize,
|
||||
OriginalBytes = 0,
|
||||
Host = torrent.ServerId ?? "",
|
||||
Split = 0,
|
||||
Progress = torrent.DownloadPercent,
|
||||
Status = torrent.Status.ToString(),
|
||||
Added = DateTimeOffset.FromUnixTimeSeconds(torrent.Created),
|
||||
Files = (torrent.Files ?? []).Select((m, i) => new DebridClientFile
|
||||
{
|
||||
Path = m.Name ?? "",
|
||||
Bytes = m.Size,
|
||||
Id = i,
|
||||
Selected = true,
|
||||
DownloadLink = m.DownloadUrl
|
||||
})
|
||||
.ToList(),
|
||||
Links = torrent.Files?.Select(m => m.DownloadUrl.ToString()).ToList(),
|
||||
Ended = null,
|
||||
Speed = torrent.UploadSpeed,
|
||||
Seeders = torrent.PeersConnected,
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IList<DebridClientTorrent>> GetDownloads()
|
||||
{
|
||||
var page = 0;
|
||||
|
|
@ -128,7 +56,7 @@ public class DebridLinkClient(ILogger<DebridLinkClient> logger, IHttpClientFacto
|
|||
return result.Id ?? "";
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("slow_down", StringComparison.OrdinalIgnoreCase) ||
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
|
|
@ -143,7 +71,7 @@ public class DebridLinkClient(ILogger<DebridLinkClient> logger, IHttpClientFacto
|
|||
return result.Id ?? "";
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("slow_down", StringComparison.OrdinalIgnoreCase) ||
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
|
|
@ -296,6 +224,78 @@ public class DebridLinkClient(ILogger<DebridLinkClient> logger, IHttpClientFacto
|
|||
return Task.FromResult(download.FileName);
|
||||
}
|
||||
|
||||
private DebridLinkFrNETClient GetClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
var apiKey = Settings.Get.Provider.ApiKey;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
throw new("DebridLink API Key not set in the settings");
|
||||
}
|
||||
|
||||
var httpClient = httpClientFactory.CreateClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(Settings.Get.Provider.Timeout);
|
||||
|
||||
var debridLinkClient = new DebridLinkFrNETClient(apiKey, httpClient);
|
||||
|
||||
return debridLinkClient;
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
foreach (var inner in ae.InnerExceptions)
|
||||
{
|
||||
logger.LogError(inner, $"The connection to DebridLink has failed: {inner.Message}");
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to DebridLink has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to DebridLink has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private DebridClientTorrent Map(Torrent torrent)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Id = torrent.Id ?? "",
|
||||
Filename = torrent.Name ?? "",
|
||||
OriginalFilename = torrent.Name ?? "",
|
||||
Hash = torrent.HashString ?? "",
|
||||
Bytes = torrent.TotalSize,
|
||||
OriginalBytes = 0,
|
||||
Host = torrent.ServerId ?? "",
|
||||
Split = 0,
|
||||
Progress = torrent.DownloadPercent,
|
||||
Status = torrent.Status.ToString(),
|
||||
Added = DateTimeOffset.FromUnixTimeSeconds(torrent.Created),
|
||||
Files = (torrent.Files ?? []).Select((m, i) => new DebridClientFile
|
||||
{
|
||||
Path = m.Name ?? "",
|
||||
Bytes = m.Size,
|
||||
Id = i,
|
||||
Selected = true,
|
||||
DownloadLink = m.DownloadUrl
|
||||
})
|
||||
.ToList(),
|
||||
Links = torrent.Files?.Select(m => m.DownloadUrl.ToString()).ToList(),
|
||||
Ended = null,
|
||||
Speed = torrent.UploadSpeed,
|
||||
Seeders = torrent.PeersConnected
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<DebridClientTorrent> GetInfo(String torrentId)
|
||||
{
|
||||
var result = await GetClient().Seedbox.ListAsync(torrentId);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ public interface IDebridClient
|
|||
Task<String> AddNzbLink(String nzbLink);
|
||||
Task<String> AddNzbFile(Byte[] bytes, String? name);
|
||||
Task<IList<DebridClientAvailableFile>> GetAvailableFiles(String hash);
|
||||
|
||||
/// <summary>
|
||||
/// Tell the debrid provider which files to download.
|
||||
/// </summary>
|
||||
|
|
@ -21,6 +22,7 @@ public interface IDebridClient
|
|||
/// <param name="torrent">The torrent to select files for</param>
|
||||
/// <returns>Number of files selected</returns>
|
||||
Task<Int32?> SelectFiles(Torrent torrent);
|
||||
|
||||
Task Delete(Torrent torrent);
|
||||
Task<String> Unrestrict(Torrent torrent, String link);
|
||||
Task<Torrent> UpdateData(Torrent torrent, DebridClientTorrent? torrentClientTorrent);
|
||||
|
|
|
|||
|
|
@ -13,66 +13,6 @@ namespace RdtClient.Service.Services.DebridClients;
|
|||
|
||||
public class PremiumizeDebridClient(ILogger<PremiumizeDebridClient> logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter) : IDebridClient
|
||||
{
|
||||
private PremiumizeNETClient GetClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
var apiKey = Settings.Get.Provider.ApiKey;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
throw new("Premiumize API Key not set in the settings");
|
||||
}
|
||||
|
||||
var httpClient = httpClientFactory.CreateClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(10);
|
||||
|
||||
var premiumizeNetClient = new PremiumizeNETClient(apiKey, httpClient);
|
||||
|
||||
return premiumizeNetClient;
|
||||
}
|
||||
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to Premiumize has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to Premiumize has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static DebridClientTorrent Map(Transfer transfer)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Id = transfer.Id,
|
||||
Filename = transfer.Name,
|
||||
OriginalFilename = transfer.Name,
|
||||
Hash = transfer.Src,
|
||||
Bytes = 0,
|
||||
OriginalBytes = 0,
|
||||
Host = null,
|
||||
Split = 0,
|
||||
Progress = (Int64) ((transfer.Progress ?? 1.0) * 100.0),
|
||||
Status = transfer.Status,
|
||||
Message = transfer.Message,
|
||||
StatusCode = 0,
|
||||
Added = null,
|
||||
Files = [],
|
||||
Links =
|
||||
[
|
||||
transfer.FolderId
|
||||
],
|
||||
Ended = null,
|
||||
Speed = 0,
|
||||
Seeders = 0
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IList<DebridClientTorrent>> GetDownloads()
|
||||
{
|
||||
var results = await GetClient().Transfers.ListAsync();
|
||||
|
|
@ -107,7 +47,7 @@ public class PremiumizeDebridClient(ILogger<PremiumizeDebridClient> logger, IHtt
|
|||
return resultId;
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("slow_down", StringComparison.OrdinalIgnoreCase) ||
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
|
|
@ -129,7 +69,7 @@ public class PremiumizeDebridClient(ILogger<PremiumizeDebridClient> logger, IHtt
|
|||
return resultId;
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("slow_down", StringComparison.OrdinalIgnoreCase) ||
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
|
|
@ -281,6 +221,66 @@ public class PremiumizeDebridClient(ILogger<PremiumizeDebridClient> logger, IHtt
|
|||
return Task.FromResult(download.FileName);
|
||||
}
|
||||
|
||||
private PremiumizeNETClient GetClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
var apiKey = Settings.Get.Provider.ApiKey;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
throw new("Premiumize API Key not set in the settings");
|
||||
}
|
||||
|
||||
var httpClient = httpClientFactory.CreateClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(10);
|
||||
|
||||
var premiumizeNetClient = new PremiumizeNETClient(apiKey, httpClient);
|
||||
|
||||
return premiumizeNetClient;
|
||||
}
|
||||
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to Premiumize has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to Premiumize has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static DebridClientTorrent Map(Transfer transfer)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Id = transfer.Id,
|
||||
Filename = transfer.Name,
|
||||
OriginalFilename = transfer.Name,
|
||||
Hash = transfer.Src,
|
||||
Bytes = 0,
|
||||
OriginalBytes = 0,
|
||||
Host = null,
|
||||
Split = 0,
|
||||
Progress = (Int64)((transfer.Progress ?? 1.0) * 100.0),
|
||||
Status = transfer.Status,
|
||||
Message = transfer.Message,
|
||||
StatusCode = 0,
|
||||
Added = null,
|
||||
Files = [],
|
||||
Links =
|
||||
[
|
||||
transfer.FolderId
|
||||
],
|
||||
Ended = null,
|
||||
Speed = 0,
|
||||
Seeders = 0
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<DebridClientTorrent> GetInfo(String id)
|
||||
{
|
||||
var results = await GetClient().Transfers.ListAsync();
|
||||
|
|
|
|||
|
|
@ -16,84 +16,6 @@ public class RealDebridDebridClient(ILogger<RealDebridDebridClient> logger, IHtt
|
|||
{
|
||||
private TimeSpan? _offset;
|
||||
|
||||
private RdNetClient GetClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
var apiKey = Settings.Get.Provider.ApiKey;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
throw new("Real-Debrid API Key not set in the settings");
|
||||
}
|
||||
|
||||
var httpClient = httpClientFactory.CreateClient(DiConfig.RD_CLIENT);
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(Settings.Get.Provider.Timeout);
|
||||
|
||||
var rdtNetClient = new RdNetClient(null, httpClient, 5, Settings.Get.Provider.ApiHostname);
|
||||
rdtNetClient.UseApiAuthentication(apiKey);
|
||||
|
||||
// Get the server time to fix up the timezones on results
|
||||
if (_offset == null)
|
||||
{
|
||||
var serverTime = rdtNetClient.Api.GetIsoTimeAsync().Result;
|
||||
_offset = serverTime.Offset;
|
||||
}
|
||||
|
||||
return rdtNetClient;
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
foreach (var inner in ae.InnerExceptions)
|
||||
{
|
||||
logger.LogError(inner, $"The connection to RealDebrid has failed: {inner.Message}");
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to RealDebrid has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to RealDebrid has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private DebridClientTorrent Map(Torrent torrent)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Id = torrent.Id,
|
||||
Filename = torrent.Filename,
|
||||
OriginalFilename = torrent.OriginalFilename,
|
||||
Hash = torrent.Hash,
|
||||
Bytes = torrent.Bytes,
|
||||
OriginalBytes = torrent.OriginalBytes,
|
||||
Host = torrent.Host,
|
||||
Split = torrent.Split,
|
||||
Progress = torrent.Progress,
|
||||
Status = torrent.Status,
|
||||
Added = ChangeTimeZone(torrent.Added)!.Value,
|
||||
Files = (torrent.Files ?? []).Select(m => new DebridClientFile
|
||||
{
|
||||
Path = m.Path,
|
||||
Bytes = m.Bytes,
|
||||
Id = m.Id,
|
||||
Selected = m.Selected
|
||||
}).ToList(),
|
||||
Links = torrent.Links,
|
||||
Ended = ChangeTimeZone(torrent.Ended),
|
||||
Speed = torrent.Speed,
|
||||
Seeders = torrent.Seeders,
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IList<DebridClientTorrent>> GetDownloads()
|
||||
{
|
||||
var offset = 0;
|
||||
|
|
@ -138,7 +60,7 @@ public class RealDebridDebridClient(ILogger<RealDebridDebridClient> logger, IHtt
|
|||
return result.Id;
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("slow_down", StringComparison.OrdinalIgnoreCase) ||
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
|
|
@ -155,7 +77,7 @@ public class RealDebridDebridClient(ILogger<RealDebridDebridClient> logger, IHtt
|
|||
return result.Id;
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("slow_down", StringComparison.OrdinalIgnoreCase) ||
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
ex.Message.Contains("rate limit exceeded", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
|
|
@ -388,6 +310,84 @@ public class RealDebridDebridClient(ILogger<RealDebridDebridClient> logger, IHtt
|
|||
return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last()));
|
||||
}
|
||||
|
||||
private RdNetClient GetClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
var apiKey = Settings.Get.Provider.ApiKey;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
throw new("Real-Debrid API Key not set in the settings");
|
||||
}
|
||||
|
||||
var httpClient = httpClientFactory.CreateClient(DiConfig.RD_CLIENT);
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(Settings.Get.Provider.Timeout);
|
||||
|
||||
var rdtNetClient = new RdNetClient(null, httpClient, 5, Settings.Get.Provider.ApiHostname);
|
||||
rdtNetClient.UseApiAuthentication(apiKey);
|
||||
|
||||
// Get the server time to fix up the timezones on results
|
||||
if (_offset == null)
|
||||
{
|
||||
var serverTime = rdtNetClient.Api.GetIsoTimeAsync().Result;
|
||||
_offset = serverTime.Offset;
|
||||
}
|
||||
|
||||
return rdtNetClient;
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
foreach (var inner in ae.InnerExceptions)
|
||||
{
|
||||
logger.LogError(inner, $"The connection to RealDebrid has failed: {inner.Message}");
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to RealDebrid has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to RealDebrid has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private DebridClientTorrent Map(Torrent torrent)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Id = torrent.Id,
|
||||
Filename = torrent.Filename,
|
||||
OriginalFilename = torrent.OriginalFilename,
|
||||
Hash = torrent.Hash,
|
||||
Bytes = torrent.Bytes,
|
||||
OriginalBytes = torrent.OriginalBytes,
|
||||
Host = torrent.Host,
|
||||
Split = torrent.Split,
|
||||
Progress = torrent.Progress,
|
||||
Status = torrent.Status,
|
||||
Added = ChangeTimeZone(torrent.Added)!.Value,
|
||||
Files = (torrent.Files ?? []).Select(m => new DebridClientFile
|
||||
{
|
||||
Path = m.Path,
|
||||
Bytes = m.Bytes,
|
||||
Id = m.Id,
|
||||
Selected = m.Selected
|
||||
})
|
||||
.ToList(),
|
||||
Links = torrent.Links,
|
||||
Ended = ChangeTimeZone(torrent.Ended),
|
||||
Speed = torrent.Speed,
|
||||
Seeders = torrent.Seeders
|
||||
};
|
||||
}
|
||||
|
||||
private DateTimeOffset? ChangeTimeZone(DateTimeOffset? dateTimeOffset)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ using System.Diagnostics;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using RdtClient.Data.Enums;
|
||||
using RdtClient.Data.Models.DebridClient;
|
||||
using RdtClient.Data.Models.Data;
|
||||
using RdtClient.Data.Models.DebridClient;
|
||||
using RdtClient.Data.Models.Internal;
|
||||
using RdtClient.Service.Helpers;
|
||||
using TorBoxNET;
|
||||
|
|
@ -14,166 +14,34 @@ namespace RdtClient.Service.Services.DebridClients;
|
|||
public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter) : IDebridClient
|
||||
{
|
||||
private TimeSpan? _offset;
|
||||
protected virtual ITorBoxNetClient GetClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
var apiKey = Settings.Get.Provider.ApiKey;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
throw new("TorBox API Key not set in the settings");
|
||||
}
|
||||
|
||||
var httpClient = httpClientFactory.CreateClient(DiConfig.TORBOX_CLIENT);
|
||||
var torBoxNetClient = new TorBoxNetClient(null, httpClient, 1);
|
||||
torBoxNetClient.UseApiAuthentication(apiKey);
|
||||
|
||||
// Get the server time to fix up the timezones on results
|
||||
if (_offset == null)
|
||||
{
|
||||
var serverTime = DateTimeOffset.UtcNow;
|
||||
_offset = serverTime.Offset;
|
||||
}
|
||||
|
||||
return torBoxNetClient;
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
foreach (var inner in ae.InnerExceptions)
|
||||
{
|
||||
logger.LogError(inner, $"The connection to TorBox has failed: {inner.Message}");
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to TorBox has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to TorBox has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual async Task<IEnumerable<TorrentInfoResult>?> GetCurrentTorrents()
|
||||
{
|
||||
return await GetClient().Torrents.GetCurrentAsync(true);
|
||||
}
|
||||
|
||||
protected virtual async Task<IEnumerable<TorrentInfoResult>?> GetQueuedTorrents()
|
||||
{
|
||||
return await GetClient().Torrents.GetQueuedAsync(true);
|
||||
}
|
||||
|
||||
protected virtual async Task<IEnumerable<UsenetInfoResult>?> GetCurrentUsenet()
|
||||
{
|
||||
return await GetClient().Usenet.GetCurrentAsync(true);
|
||||
}
|
||||
|
||||
protected virtual async Task<IEnumerable<UsenetInfoResult>?> GetQueuedUsenet()
|
||||
{
|
||||
return await GetClient().Usenet.GetQueuedAsync(true);
|
||||
}
|
||||
|
||||
protected virtual async Task<Response<List<AvailableTorrent?>>> GetTorrentAvailability(String hash)
|
||||
{
|
||||
return await GetClient().Torrents.GetAvailabilityAsync(hash, listFiles: true);
|
||||
}
|
||||
|
||||
protected virtual async Task<Response<List<AvailableUsenet?>>> GetUsenetAvailability(String hash)
|
||||
{
|
||||
return await GetClient().Usenet.GetAvailabilityAsync(hash, listFiles: true);
|
||||
}
|
||||
|
||||
private DebridClientTorrent Map(TorrentInfoResult torrent)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Id = torrent.Hash,
|
||||
Filename = torrent.Name,
|
||||
OriginalFilename = torrent.Name,
|
||||
Hash = torrent.Hash,
|
||||
Bytes = torrent.Size,
|
||||
OriginalBytes = torrent.Size,
|
||||
Host = torrent.DownloadPresent.ToString(),
|
||||
Split = 0,
|
||||
Progress = (Int64)(torrent.Progress * 100.0),
|
||||
Status = torrent.DownloadState,
|
||||
Type = DownloadType.Torrent,
|
||||
Added = ChangeTimeZone(torrent.CreatedAt)!.Value,
|
||||
Files = (torrent.Files ?? []).Select(m => new DebridClientFile
|
||||
{
|
||||
Path = String.Join("/", m.Name.Split('/').Skip(1)),
|
||||
Bytes = m.Size,
|
||||
Id = m.Id,
|
||||
Selected = true
|
||||
}).ToList(),
|
||||
Links = [],
|
||||
Ended = ChangeTimeZone(torrent.UpdatedAt),
|
||||
Speed = torrent.DownloadSpeed,
|
||||
Seeders = torrent.Seeds,
|
||||
};
|
||||
}
|
||||
|
||||
private DebridClientTorrent Map(UsenetInfoResult usenet)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Id = usenet.Hash,
|
||||
Filename = usenet.Name,
|
||||
OriginalFilename = usenet.Name,
|
||||
Hash = usenet.Hash,
|
||||
Bytes = usenet.Size,
|
||||
OriginalBytes = usenet.Size,
|
||||
Host = usenet.DownloadPresent.ToString(),
|
||||
Split = 0,
|
||||
Progress = (Int64)(usenet.Progress * 100.0),
|
||||
Status = usenet.DownloadState,
|
||||
Type = DownloadType.Nzb,
|
||||
Added = ChangeTimeZone(usenet.CreatedAt)!.Value,
|
||||
Files = (usenet.Files ?? []).Select(m => new DebridClientFile
|
||||
{
|
||||
Path = String.Join("/", m.Name.Split('/').Skip(1)),
|
||||
Bytes = m.Size,
|
||||
Id = m.Id,
|
||||
Selected = true
|
||||
}).ToList(),
|
||||
Links = [],
|
||||
Ended = ChangeTimeZone(usenet.UpdatedAt),
|
||||
Speed = usenet.DownloadSpeed,
|
||||
Seeders = 0,
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IList<DebridClientTorrent>> GetDownloads()
|
||||
{
|
||||
var results = new List<DebridClientTorrent>();
|
||||
|
||||
var currentTorrents = await GetCurrentTorrents();
|
||||
|
||||
if (currentTorrents != null)
|
||||
{
|
||||
results.AddRange(currentTorrents.Select(Map));
|
||||
}
|
||||
|
||||
var queuedTorrents = await GetQueuedTorrents();
|
||||
|
||||
if (queuedTorrents != null)
|
||||
{
|
||||
results.AddRange(queuedTorrents.Select(Map));
|
||||
}
|
||||
|
||||
var currentNzbs = await GetCurrentUsenet();
|
||||
|
||||
if (currentNzbs != null)
|
||||
{
|
||||
results.AddRange(currentNzbs.Select(Map));
|
||||
}
|
||||
|
||||
var queuedNzbs = await GetQueuedUsenet();
|
||||
|
||||
if (queuedNzbs != null)
|
||||
{
|
||||
results.AddRange(queuedNzbs.Select(Map));
|
||||
|
|
@ -193,60 +61,13 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
|
|||
};
|
||||
}
|
||||
|
||||
private async Task<String> HandleAddTorrentErrors(Func<Boolean, Task<String>> action)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await action(false);
|
||||
}
|
||||
catch (RateLimitException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex) when (ex.InnerException is RateLimitException rateLimitException)
|
||||
{
|
||||
throw rateLimitException;
|
||||
}
|
||||
catch (TorBoxException ex) when (ex.Error.Equals("active_limit", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("slow_down", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<String> HandleAddUsenetErrors(Func<Boolean, Task<String>> action)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await action(false);
|
||||
}
|
||||
catch (RateLimitException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex) when (ex.InnerException is RateLimitException rateLimitException)
|
||||
{
|
||||
throw rateLimitException;
|
||||
}
|
||||
catch (TorBoxException ex) when (ex.Error.Equals("active_limit", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("slow_down", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<String> AddTorrentMagnet(String magnetLink)
|
||||
{
|
||||
return await HandleAddTorrentErrors(async asQueued =>
|
||||
{
|
||||
var user = await GetClient().User.GetAsync(true);
|
||||
var result = await GetClient().Torrents.AddMagnetAsync(magnetLink, user.Data?.Settings?.SeedTorrents ?? 3, as_queued: asQueued);
|
||||
|
||||
return result.Data!.Hash!;
|
||||
});
|
||||
}
|
||||
|
|
@ -257,6 +78,7 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
|
|||
{
|
||||
var user = await GetClient().User.GetAsync(true);
|
||||
var result = await GetClient().Torrents.AddFileAsync(bytes, user.Data?.Settings?.SeedTorrents ?? 3, as_queued: asQueued);
|
||||
|
||||
return result.Data!.Hash!;
|
||||
});
|
||||
}
|
||||
|
|
@ -266,6 +88,7 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
|
|||
return await HandleAddUsenetErrors(async asQueued =>
|
||||
{
|
||||
var result = await GetClient().Usenet.AddLinkAsync(nzbLink, as_queued: asQueued);
|
||||
|
||||
return result.Data!.Hash!;
|
||||
});
|
||||
}
|
||||
|
|
@ -275,6 +98,7 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
|
|||
return await HandleAddUsenetErrors(async asQueued =>
|
||||
{
|
||||
var result = await GetClient().Usenet.AddFileAsync(bytes, name: name, as_queued: asQueued);
|
||||
|
||||
return result.Data!.Hash!;
|
||||
});
|
||||
}
|
||||
|
|
@ -286,10 +110,11 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
|
|||
if (availability.Data != null && availability.Data.Count > 0)
|
||||
{
|
||||
return (availability.Data[0]?.Files ?? []).Select(file => new DebridClientAvailableFile
|
||||
{
|
||||
Filename = file.Name,
|
||||
Filesize = file.Size
|
||||
}).ToList();
|
||||
{
|
||||
Filename = file.Name,
|
||||
Filesize = file.Size
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
var usenetAvailability = await GetUsenetAvailability(hash);
|
||||
|
|
@ -297,10 +122,11 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
|
|||
if (usenetAvailability.Data != null && usenetAvailability.Data.Count > 0)
|
||||
{
|
||||
return (usenetAvailability.Data[0]?.Files ?? []).Select(file => new DebridClientAvailableFile
|
||||
{
|
||||
Filename = file.Name,
|
||||
Filesize = file.Size
|
||||
}).ToList();
|
||||
{
|
||||
Filename = file.Name,
|
||||
Filesize = file.Size
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return [];
|
||||
|
|
@ -337,6 +163,7 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
|
|||
}
|
||||
|
||||
var segments = link.Split('/');
|
||||
|
||||
if (segments is not [_, _, _, _, var torrentIdStr, var fileIdStrOrZip])
|
||||
{
|
||||
throw new ArgumentException($"Invalid link format: {link}", nameof(link));
|
||||
|
|
@ -426,6 +253,7 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
|
|||
else
|
||||
{
|
||||
logger.LogTrace("Updating status for {TorrentName} from {OldStatus} to {NewStatus}", torrent.RdName, torrent.RdStatus, rdTorrent.Status);
|
||||
|
||||
torrent.RdStatus = rdTorrent.Status switch
|
||||
{
|
||||
"allocating" => TorrentStatus.Processing,
|
||||
|
|
@ -482,7 +310,7 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
|
|||
}
|
||||
else
|
||||
{
|
||||
var torrentId = await GetClient().Torrents.GetHashInfoAsync(torrent.Hash, skipCache: true);
|
||||
var torrentId = await GetClient().Torrents.GetHashInfoAsync(torrent.Hash, true);
|
||||
id = torrentId?.Id;
|
||||
}
|
||||
|
||||
|
|
@ -490,6 +318,7 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
|
|||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var downloadableFiles = torrent.Files.Where(file => fileFilter.IsDownloadable(torrent, file.Path, file.Bytes)).ToList();
|
||||
|
||||
if (downloadableFiles.Count == torrent.Files.Count && torrent.DownloadClient != Data.Enums.DownloadClient.Symlink && Settings.Get.Provider.PreferZippedDownloads)
|
||||
|
|
@ -525,6 +354,192 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
|
|||
return Task.FromResult(download.FileName);
|
||||
}
|
||||
|
||||
protected virtual ITorBoxNetClient GetClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
var apiKey = Settings.Get.Provider.ApiKey;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
throw new("TorBox API Key not set in the settings");
|
||||
}
|
||||
|
||||
var httpClient = httpClientFactory.CreateClient(DiConfig.TORBOX_CLIENT);
|
||||
var torBoxNetClient = new TorBoxNetClient(null, httpClient);
|
||||
torBoxNetClient.UseApiAuthentication(apiKey);
|
||||
|
||||
// Get the server time to fix up the timezones on results
|
||||
if (_offset == null)
|
||||
{
|
||||
var serverTime = DateTimeOffset.UtcNow;
|
||||
_offset = serverTime.Offset;
|
||||
}
|
||||
|
||||
return torBoxNetClient;
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
foreach (var inner in ae.InnerExceptions)
|
||||
{
|
||||
logger.LogError(inner, $"The connection to TorBox has failed: {inner.Message}");
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to TorBox has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
logger.LogError(ex, $"The connection to TorBox has timed out: {ex.Message}");
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual async Task<IEnumerable<TorrentInfoResult>?> GetCurrentTorrents()
|
||||
{
|
||||
return await GetClient().Torrents.GetCurrentAsync(true);
|
||||
}
|
||||
|
||||
protected virtual async Task<IEnumerable<TorrentInfoResult>?> GetQueuedTorrents()
|
||||
{
|
||||
return await GetClient().Torrents.GetQueuedAsync(true);
|
||||
}
|
||||
|
||||
protected virtual async Task<IEnumerable<UsenetInfoResult>?> GetCurrentUsenet()
|
||||
{
|
||||
return await GetClient().Usenet.GetCurrentAsync(true);
|
||||
}
|
||||
|
||||
protected virtual async Task<IEnumerable<UsenetInfoResult>?> GetQueuedUsenet()
|
||||
{
|
||||
return await GetClient().Usenet.GetQueuedAsync(true);
|
||||
}
|
||||
|
||||
protected virtual async Task<Response<List<AvailableTorrent?>>> GetTorrentAvailability(String hash)
|
||||
{
|
||||
return await GetClient().Torrents.GetAvailabilityAsync(hash, true);
|
||||
}
|
||||
|
||||
protected virtual async Task<Response<List<AvailableUsenet?>>> GetUsenetAvailability(String hash)
|
||||
{
|
||||
return await GetClient().Usenet.GetAvailabilityAsync(hash, true);
|
||||
}
|
||||
|
||||
private DebridClientTorrent Map(TorrentInfoResult torrent)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Id = torrent.Hash,
|
||||
Filename = torrent.Name,
|
||||
OriginalFilename = torrent.Name,
|
||||
Hash = torrent.Hash,
|
||||
Bytes = torrent.Size,
|
||||
OriginalBytes = torrent.Size,
|
||||
Host = torrent.DownloadPresent.ToString(),
|
||||
Split = 0,
|
||||
Progress = (Int64)(torrent.Progress * 100.0),
|
||||
Status = torrent.DownloadState,
|
||||
Type = DownloadType.Torrent,
|
||||
Added = ChangeTimeZone(torrent.CreatedAt)!.Value,
|
||||
Files = (torrent.Files ?? []).Select(m => new DebridClientFile
|
||||
{
|
||||
Path = String.Join("/", m.Name.Split('/').Skip(1)),
|
||||
Bytes = m.Size,
|
||||
Id = m.Id,
|
||||
Selected = true
|
||||
})
|
||||
.ToList(),
|
||||
Links = [],
|
||||
Ended = ChangeTimeZone(torrent.UpdatedAt),
|
||||
Speed = torrent.DownloadSpeed,
|
||||
Seeders = torrent.Seeds
|
||||
};
|
||||
}
|
||||
|
||||
private DebridClientTorrent Map(UsenetInfoResult usenet)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Id = usenet.Hash,
|
||||
Filename = usenet.Name,
|
||||
OriginalFilename = usenet.Name,
|
||||
Hash = usenet.Hash,
|
||||
Bytes = usenet.Size,
|
||||
OriginalBytes = usenet.Size,
|
||||
Host = usenet.DownloadPresent.ToString(),
|
||||
Split = 0,
|
||||
Progress = (Int64)(usenet.Progress * 100.0),
|
||||
Status = usenet.DownloadState,
|
||||
Type = DownloadType.Nzb,
|
||||
Added = ChangeTimeZone(usenet.CreatedAt)!.Value,
|
||||
Files = (usenet.Files ?? []).Select(m => new DebridClientFile
|
||||
{
|
||||
Path = String.Join("/", m.Name.Split('/').Skip(1)),
|
||||
Bytes = m.Size,
|
||||
Id = m.Id,
|
||||
Selected = true
|
||||
})
|
||||
.ToList(),
|
||||
Links = [],
|
||||
Ended = ChangeTimeZone(usenet.UpdatedAt),
|
||||
Speed = usenet.DownloadSpeed,
|
||||
Seeders = 0
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<String> HandleAddTorrentErrors(Func<Boolean, Task<String>> action)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await action(false);
|
||||
}
|
||||
catch (RateLimitException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex) when (ex.InnerException is RateLimitException rateLimitException)
|
||||
{
|
||||
throw rateLimitException;
|
||||
}
|
||||
catch (TorBoxException ex) when (ex.Error.Equals("active_limit", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("slow_down", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<String> HandleAddUsenetErrors(Func<Boolean, Task<String>> action)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await action(false);
|
||||
}
|
||||
catch (RateLimitException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex) when (ex.InnerException is RateLimitException rateLimitException)
|
||||
{
|
||||
throw rateLimitException;
|
||||
}
|
||||
catch (TorBoxException ex) when (ex.Error.Equals("active_limit", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("slow_down", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new RateLimitException(ex.Message, TimeSpan.FromMinutes(2));
|
||||
}
|
||||
}
|
||||
|
||||
private DateTimeOffset? ChangeTimeZone(DateTimeOffset? dateTimeOffset)
|
||||
{
|
||||
|
|
@ -540,7 +555,8 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
|
|||
{
|
||||
if (type == DownloadType.Nzb)
|
||||
{
|
||||
var usenet = await GetClient().Usenet.GetHashInfoAsync(id, skipCache: true);
|
||||
var usenet = await GetClient().Usenet.GetHashInfoAsync(id, true);
|
||||
|
||||
if (usenet != null)
|
||||
{
|
||||
return Map(usenet);
|
||||
|
|
@ -548,7 +564,7 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
|
|||
}
|
||||
else
|
||||
{
|
||||
var result = await GetClient().Torrents.GetHashInfoAsync(id, skipCache: true);
|
||||
var result = await GetClient().Torrents.GetHashInfoAsync(id, true);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
|
|
@ -568,6 +584,7 @@ public class TorBoxDebridClient(ILogger<TorBoxDebridClient> logger, IHttpClientF
|
|||
var innerFolder = Directory.GetDirectories(hashDir)[0];
|
||||
|
||||
var moveDir = extractPath;
|
||||
|
||||
if (!extractPath.EndsWith(torrent.RdName!))
|
||||
{
|
||||
moveDir = hashDir;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using RdtClient.Data.Enums;
|
||||
using RdtClient.Data.Models.Data;
|
||||
using RdtClient.Service.Helpers;
|
||||
using RdtClient.Service.Services.Downloaders;
|
||||
using RdtClient.Service.Services.DebridClients;
|
||||
using RdtClient.Service.Services.Downloaders;
|
||||
|
||||
namespace RdtClient.Service.Services;
|
||||
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ public class QBittorrent(ILogger<QBittorrent> logger, Settings settings, Authent
|
|||
var bytesDone = (Int64)(bytesTotal * rdProgress);
|
||||
|
||||
Double progress;
|
||||
|
||||
if (torrent.Completed != null)
|
||||
{
|
||||
progress = 1.0;
|
||||
|
|
@ -251,6 +252,7 @@ public class QBittorrent(ILogger<QBittorrent> logger, Settings settings, Authent
|
|||
}
|
||||
|
||||
var remaining = TimeSpan.Zero;
|
||||
|
||||
if (progress > 0 && progress < 1.0)
|
||||
{
|
||||
var startTime = torrent.Retry > torrent.Added ? torrent.Retry.Value : torrent.Added;
|
||||
|
|
|
|||
|
|
@ -10,70 +10,73 @@ public class RemoteService(IHubContext<RdtHub> hub, Torrents torrents)
|
|||
var allTorrents = await torrents.Get();
|
||||
|
||||
var torrentDtos = allTorrents.Select(torrent => new TorrentDto
|
||||
{
|
||||
TorrentId = torrent.TorrentId,
|
||||
Hash = torrent.Hash,
|
||||
Category = torrent.Category,
|
||||
DownloadAction = torrent.DownloadAction,
|
||||
FinishedAction = torrent.FinishedAction,
|
||||
FinishedActionDelay = torrent.FinishedActionDelay,
|
||||
HostDownloadAction = torrent.HostDownloadAction,
|
||||
DownloadMinSize = torrent.DownloadMinSize,
|
||||
IncludeRegex = torrent.IncludeRegex,
|
||||
ExcludeRegex = torrent.ExcludeRegex,
|
||||
DownloadManualFiles = torrent.DownloadManualFiles,
|
||||
DownloadClient = torrent.DownloadClient,
|
||||
Added = torrent.Added,
|
||||
FilesSelected = torrent.FilesSelected,
|
||||
Completed = torrent.Completed,
|
||||
Type = torrent.Type,
|
||||
IsFile = torrent.IsFile,
|
||||
Priority = torrent.Priority,
|
||||
RetryCount = torrent.RetryCount,
|
||||
DownloadRetryAttempts = torrent.DownloadRetryAttempts,
|
||||
TorrentRetryAttempts = torrent.TorrentRetryAttempts,
|
||||
DeleteOnError = torrent.DeleteOnError,
|
||||
Lifetime = torrent.Lifetime,
|
||||
Error = torrent.Error,
|
||||
RdId = torrent.RdId,
|
||||
RdName = torrent.RdName,
|
||||
RdSize = torrent.RdSize,
|
||||
RdHost = torrent.RdHost,
|
||||
RdSplit = torrent.RdSplit,
|
||||
RdProgress = torrent.RdProgress,
|
||||
RdStatus = torrent.RdStatus,
|
||||
RdStatusRaw = torrent.RdStatusRaw,
|
||||
RdAdded = torrent.RdAdded,
|
||||
RdEnded = torrent.RdEnded,
|
||||
RdSpeed = torrent.RdSpeed,
|
||||
RdSeeders = torrent.RdSeeders,
|
||||
Files = torrent.Files,
|
||||
Downloads = torrent.Downloads.Select(download =>
|
||||
{
|
||||
var (speed, bytesTotal, bytesDone) = torrents.GetDownloadStats(download.DownloadId);
|
||||
{
|
||||
TorrentId = torrent.TorrentId,
|
||||
Hash = torrent.Hash,
|
||||
Category = torrent.Category,
|
||||
DownloadAction = torrent.DownloadAction,
|
||||
FinishedAction = torrent.FinishedAction,
|
||||
FinishedActionDelay = torrent.FinishedActionDelay,
|
||||
HostDownloadAction = torrent.HostDownloadAction,
|
||||
DownloadMinSize = torrent.DownloadMinSize,
|
||||
IncludeRegex = torrent.IncludeRegex,
|
||||
ExcludeRegex = torrent.ExcludeRegex,
|
||||
DownloadManualFiles = torrent.DownloadManualFiles,
|
||||
DownloadClient = torrent.DownloadClient,
|
||||
Added = torrent.Added,
|
||||
FilesSelected = torrent.FilesSelected,
|
||||
Completed = torrent.Completed,
|
||||
Type = torrent.Type,
|
||||
IsFile = torrent.IsFile,
|
||||
Priority = torrent.Priority,
|
||||
RetryCount = torrent.RetryCount,
|
||||
DownloadRetryAttempts = torrent.DownloadRetryAttempts,
|
||||
TorrentRetryAttempts = torrent.TorrentRetryAttempts,
|
||||
DeleteOnError = torrent.DeleteOnError,
|
||||
Lifetime = torrent.Lifetime,
|
||||
Error = torrent.Error,
|
||||
RdId = torrent.RdId,
|
||||
RdName = torrent.RdName,
|
||||
RdSize = torrent.RdSize,
|
||||
RdHost = torrent.RdHost,
|
||||
RdSplit = torrent.RdSplit,
|
||||
RdProgress = torrent.RdProgress,
|
||||
RdStatus = torrent.RdStatus,
|
||||
RdStatusRaw = torrent.RdStatusRaw,
|
||||
RdAdded = torrent.RdAdded,
|
||||
RdEnded = torrent.RdEnded,
|
||||
RdSpeed = torrent.RdSpeed,
|
||||
RdSeeders = torrent.RdSeeders,
|
||||
Files = torrent.Files,
|
||||
Downloads = torrent.Downloads.Select(download =>
|
||||
{
|
||||
var (speed, bytesTotal, bytesDone) = torrents.GetDownloadStats(download.DownloadId);
|
||||
|
||||
return new DownloadDto
|
||||
{
|
||||
DownloadId = download.DownloadId,
|
||||
TorrentId = download.TorrentId,
|
||||
Path = download.Path,
|
||||
Link = download.Link,
|
||||
Added = download.Added,
|
||||
DownloadQueued = download.DownloadQueued,
|
||||
DownloadStarted = download.DownloadStarted,
|
||||
DownloadFinished = download.DownloadFinished,
|
||||
UnpackingQueued = download.UnpackingQueued,
|
||||
UnpackingStarted = download.UnpackingStarted,
|
||||
UnpackingFinished = download.UnpackingFinished,
|
||||
Completed = download.Completed,
|
||||
RetryCount = download.RetryCount,
|
||||
Error = download.Error,
|
||||
BytesTotal = bytesTotal,
|
||||
BytesDone = bytesDone,
|
||||
Speed = speed
|
||||
};
|
||||
})
|
||||
.ToList()
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return new DownloadDto
|
||||
{
|
||||
DownloadId = download.DownloadId,
|
||||
TorrentId = download.TorrentId,
|
||||
Path = download.Path,
|
||||
Link = download.Link,
|
||||
Added = download.Added,
|
||||
DownloadQueued = download.DownloadQueued,
|
||||
DownloadStarted = download.DownloadStarted,
|
||||
DownloadFinished = download.DownloadFinished,
|
||||
UnpackingQueued = download.UnpackingQueued,
|
||||
UnpackingStarted = download.UnpackingStarted,
|
||||
UnpackingFinished = download.UnpackingFinished,
|
||||
Completed = download.Completed,
|
||||
RetryCount = download.RetryCount,
|
||||
Error = download.Error,
|
||||
BytesTotal = bytesTotal,
|
||||
BytesDone = bytesDone,
|
||||
Speed = speed
|
||||
};
|
||||
}).ToList()
|
||||
}).ToList();
|
||||
await hub.Clients.All.SendCoreAsync("update",
|
||||
[
|
||||
torrentDtos
|
||||
|
|
|
|||
|
|
@ -18,62 +18,65 @@ public class Sabnzbd(ILogger<Sabnzbd> logger, Torrents torrents, AppSettings app
|
|||
{
|
||||
NoOfSlots = activeTorrents.Count,
|
||||
Slots = activeTorrents.Select((t, index) =>
|
||||
{
|
||||
var rdProgress = Math.Clamp(t.RdProgress ?? 0.0, 0.0, 100.0) / 100.0;
|
||||
Double progress;
|
||||
{
|
||||
var rdProgress = Math.Clamp(t.RdProgress ?? 0.0, 0.0, 100.0) / 100.0;
|
||||
Double progress;
|
||||
|
||||
var dlStats = t.Downloads.Select(m => torrents.GetDownloadStats(m.DownloadId)).ToList();
|
||||
if (dlStats.Count > 0)
|
||||
{
|
||||
var bytesDone = dlStats.Sum(m => m.BytesDone);
|
||||
var bytesTotal = dlStats.Sum(m => m.BytesTotal);
|
||||
var downloadProgress = bytesTotal > 0 ? Math.Clamp((Double)bytesDone / bytesTotal, 0.0, 1.0) : 0;
|
||||
progress = (rdProgress + downloadProgress) / 2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
progress = rdProgress;
|
||||
}
|
||||
var dlStats = t.Downloads.Select(m => torrents.GetDownloadStats(m.DownloadId)).ToList();
|
||||
|
||||
var timeLeft = "0:00:00";
|
||||
var startTime = t.Retry > t.Added ? t.Retry.Value : t.Added;
|
||||
var elapsed = DateTimeOffset.UtcNow - startTime;
|
||||
if (dlStats.Count > 0)
|
||||
{
|
||||
var bytesDone = dlStats.Sum(m => m.BytesDone);
|
||||
var bytesTotal = dlStats.Sum(m => m.BytesTotal);
|
||||
var downloadProgress = bytesTotal > 0 ? Math.Clamp((Double)bytesDone / bytesTotal, 0.0, 1.0) : 0;
|
||||
progress = (rdProgress + downloadProgress) / 2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
progress = rdProgress;
|
||||
}
|
||||
|
||||
if (progress is > 0 and < 1.0)
|
||||
{
|
||||
var totalEstimatedTime = TimeSpan.FromTicks((Int64)(elapsed.Ticks / progress));
|
||||
var remaining = totalEstimatedTime - elapsed;
|
||||
if (remaining.TotalSeconds > 0)
|
||||
{
|
||||
timeLeft = $"{(Int32)remaining.TotalHours}:{remaining.Minutes:D2}:{remaining.Seconds:D2}";
|
||||
}
|
||||
}
|
||||
var timeLeft = "0:00:00";
|
||||
var startTime = t.Retry > t.Added ? t.Retry.Value : t.Added;
|
||||
var elapsed = DateTimeOffset.UtcNow - startTime;
|
||||
|
||||
return new SabnzbdQueueSlot
|
||||
{
|
||||
Index = index,
|
||||
NzoId = t.Hash,
|
||||
Filename = t.RdName ?? t.Hash,
|
||||
Size = FileSizeHelper.FormatSize(dlStats.Sum(d => d.BytesTotal)),
|
||||
SizeLeft = FileSizeHelper.FormatSize(dlStats.Sum(d => d.BytesTotal - d.BytesDone)),
|
||||
Percentage = (progress * 100.0).ToString("0"),
|
||||
if (progress is > 0 and < 1.0)
|
||||
{
|
||||
var totalEstimatedTime = TimeSpan.FromTicks((Int64)(elapsed.Ticks / progress));
|
||||
var remaining = totalEstimatedTime - elapsed;
|
||||
|
||||
Status = t.RdStatus switch
|
||||
{
|
||||
TorrentStatus.Queued => "Queued",
|
||||
TorrentStatus.Processing => "Downloading",
|
||||
TorrentStatus.WaitingForFileSelection => "Downloading",
|
||||
TorrentStatus.Downloading => "Downloading",
|
||||
TorrentStatus.Uploading => "Downloading",
|
||||
TorrentStatus.Finished => "Completed",
|
||||
TorrentStatus.Error => "Failed",
|
||||
_ => "Downloading"
|
||||
},
|
||||
Category = t.Category ?? "*",
|
||||
Priority = "Normal",
|
||||
TimeLeft = timeLeft
|
||||
};
|
||||
}).ToList()
|
||||
if (remaining.TotalSeconds > 0)
|
||||
{
|
||||
timeLeft = $"{(Int32)remaining.TotalHours}:{remaining.Minutes:D2}:{remaining.Seconds:D2}";
|
||||
}
|
||||
}
|
||||
|
||||
return new SabnzbdQueueSlot
|
||||
{
|
||||
Index = index,
|
||||
NzoId = t.Hash,
|
||||
Filename = t.RdName ?? t.Hash,
|
||||
Size = FileSizeHelper.FormatSize(dlStats.Sum(d => d.BytesTotal)),
|
||||
SizeLeft = FileSizeHelper.FormatSize(dlStats.Sum(d => d.BytesTotal - d.BytesDone)),
|
||||
Percentage = (progress * 100.0).ToString("0"),
|
||||
|
||||
Status = t.RdStatus switch
|
||||
{
|
||||
TorrentStatus.Queued => "Queued",
|
||||
TorrentStatus.Processing => "Downloading",
|
||||
TorrentStatus.WaitingForFileSelection => "Downloading",
|
||||
TorrentStatus.Downloading => "Downloading",
|
||||
TorrentStatus.Uploading => "Downloading",
|
||||
TorrentStatus.Finished => "Completed",
|
||||
TorrentStatus.Error => "Failed",
|
||||
_ => "Downloading"
|
||||
},
|
||||
Category = t.Category ?? "*",
|
||||
Priority = "Normal",
|
||||
TimeLeft = timeLeft
|
||||
};
|
||||
})
|
||||
.ToList()
|
||||
};
|
||||
|
||||
return queue;
|
||||
|
|
@ -91,29 +94,30 @@ public class Sabnzbd(ILogger<Sabnzbd> logger, Torrents torrents, AppSettings app
|
|||
NoOfSlots = completedTorrents.Count,
|
||||
TotalSlots = completedTorrents.Count,
|
||||
Slots = completedTorrents.Select(t =>
|
||||
{
|
||||
var path = savePath;
|
||||
{
|
||||
var path = savePath;
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(t.Category))
|
||||
{
|
||||
path = Path.Combine(path, t.Category);
|
||||
}
|
||||
if (!String.IsNullOrWhiteSpace(t.Category))
|
||||
{
|
||||
path = Path.Combine(path, t.Category);
|
||||
}
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(t.RdName))
|
||||
{
|
||||
path = Path.Combine(path, t.RdName);
|
||||
}
|
||||
if (!String.IsNullOrWhiteSpace(t.RdName))
|
||||
{
|
||||
path = Path.Combine(path, t.RdName);
|
||||
}
|
||||
|
||||
return new SabnzbdHistorySlot
|
||||
{
|
||||
NzoId = t.Hash,
|
||||
Name = t.RdName ?? t.Hash,
|
||||
Size = FileSizeHelper.FormatSize(t.Downloads.Sum(d => d.BytesTotal)),
|
||||
Status = String.IsNullOrWhiteSpace(t.Error) ? "Completed" : "Failed",
|
||||
Category = t.Category ?? "Default",
|
||||
Path = path
|
||||
};
|
||||
}).ToList()
|
||||
return new SabnzbdHistorySlot
|
||||
{
|
||||
NzoId = t.Hash,
|
||||
Name = t.RdName ?? t.Hash,
|
||||
Size = FileSizeHelper.FormatSize(t.Downloads.Sum(d => d.BytesTotal)),
|
||||
Status = String.IsNullOrWhiteSpace(t.Error) ? "Completed" : "Failed",
|
||||
Category = t.Category ?? "Default",
|
||||
Path = path
|
||||
};
|
||||
})
|
||||
.ToList()
|
||||
};
|
||||
|
||||
return history;
|
||||
|
|
@ -142,6 +146,7 @@ public class Sabnzbd(ILogger<Sabnzbd> logger, Torrents torrents, AppSettings app
|
|||
};
|
||||
|
||||
var result = await torrents.AddNzbFileToDebridQueue(fileBytes, fileName, torrent);
|
||||
|
||||
return result.Hash;
|
||||
}
|
||||
|
||||
|
|
@ -168,6 +173,7 @@ public class Sabnzbd(ILogger<Sabnzbd> logger, Torrents torrents, AppSettings app
|
|||
};
|
||||
|
||||
var result = await torrents.AddNzbLinkToDebridQueue(url, torrent);
|
||||
|
||||
return result.Hash;
|
||||
}
|
||||
|
||||
|
|
@ -179,7 +185,7 @@ public class Sabnzbd(ILogger<Sabnzbd> logger, Torrents torrents, AppSettings app
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
switch (Settings.Get.Integrations.Default.FinishedAction)
|
||||
{
|
||||
case TorrentFinishedAction.RemoveAllTorrents:
|
||||
|
|
@ -229,11 +235,12 @@ public class Sabnzbd(ILogger<Sabnzbd> logger, Torrents torrents, AppSettings app
|
|||
var categoryList = GetCategories();
|
||||
|
||||
var categories = categoryList.Select((c, i) => new SabnzbdCategory
|
||||
{
|
||||
Name = c,
|
||||
Order = i,
|
||||
Dir = c == "*" ? "" : Path.Combine(savePath, c)
|
||||
}).ToList();
|
||||
{
|
||||
Name = c,
|
||||
Order = i,
|
||||
Dir = c == "*" ? "" : Path.Combine(savePath, c)
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var config = new SabnzbdConfig
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ public class TorrentRunner(ILogger<TorrentRunner> logger, Torrents torrents, Dow
|
|||
public static readonly ConcurrentDictionary<Guid, DownloadClient> ActiveDownloadClients = new();
|
||||
public static readonly ConcurrentDictionary<Guid, UnpackClient> ActiveUnpackClients = new();
|
||||
|
||||
public static Boolean IsPausedForLowDiskSpace { get; set; }
|
||||
|
||||
public static DateTimeOffset NextDequeueTime { get; private set; } = DateTimeOffset.MinValue;
|
||||
|
||||
public static (Int64 Speed, Int64 BytesTotal, Int64 BytesDone) GetStats(Guid downloadId)
|
||||
{
|
||||
if (ActiveDownloadClients.TryGetValue(downloadId, out var downloadClient))
|
||||
|
|
@ -31,10 +35,6 @@ public class TorrentRunner(ILogger<TorrentRunner> logger, Torrents torrents, Dow
|
|||
return (0, 0, 0);
|
||||
}
|
||||
|
||||
public static Boolean IsPausedForLowDiskSpace { get; set; }
|
||||
|
||||
public static DateTimeOffset NextDequeueTime { get; private set; } = DateTimeOffset.MinValue;
|
||||
|
||||
public async Task Initialize()
|
||||
{
|
||||
Log("Initializing TorrentRunner");
|
||||
|
|
@ -359,7 +359,7 @@ public class TorrentRunner(ILogger<TorrentRunner> logger, Torrents torrents, Dow
|
|||
{
|
||||
NextDequeueTime = DateTimeOffset.MinValue;
|
||||
|
||||
await remoteService.UpdateRateLimitStatus(new RateLimitStatus
|
||||
await remoteService.UpdateRateLimitStatus(new()
|
||||
{
|
||||
NextDequeueTime = null,
|
||||
SecondsRemaining = 0
|
||||
|
|
@ -568,6 +568,7 @@ public class TorrentRunner(ILogger<TorrentRunner> logger, Torrents torrents, Dow
|
|||
catch (Exception ex)
|
||||
{
|
||||
LogError($"Unable to start download: {ex.Message}", download, torrent);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -755,7 +756,7 @@ public class TorrentRunner(ILogger<TorrentRunner> logger, Torrents torrents, Dow
|
|||
|
||||
Log($"Rate-limit reached, pausing dequeuing for {retryAfter.TotalMinutes} minutes (until {NextDequeueTime}): {message}");
|
||||
|
||||
await remoteService.UpdateRateLimitStatus(new RateLimitStatus
|
||||
await remoteService.UpdateRateLimitStatus(new()
|
||||
{
|
||||
NextDequeueTime = NextDequeueTime,
|
||||
SecondsRemaining = retryAfter.TotalSeconds
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Globalization;
|
||||
using System.IO.Abstractions;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
|
@ -10,8 +11,8 @@ using MonoTorrent;
|
|||
using RdtClient.Data.Data;
|
||||
using RdtClient.Data.Enums;
|
||||
using RdtClient.Data.Models.Data;
|
||||
using RdtClient.Data.Models.Internal;
|
||||
using RdtClient.Data.Models.DebridClient;
|
||||
using RdtClient.Data.Models.Internal;
|
||||
using RdtClient.Service.BackgroundServices;
|
||||
using RdtClient.Service.Helpers;
|
||||
using RdtClient.Service.Services.DebridClients;
|
||||
|
|
@ -40,6 +41,8 @@ public class Torrents(
|
|||
ReferenceHandler = ReferenceHandler.IgnoreCycles
|
||||
};
|
||||
|
||||
private static readonly SemaphoreSlim TorrentResetLock = new(1, 1);
|
||||
|
||||
private IDebridClient DebridClient
|
||||
{
|
||||
get
|
||||
|
|
@ -56,8 +59,6 @@ public class Torrents(
|
|||
}
|
||||
}
|
||||
|
||||
private static readonly SemaphoreSlim TorrentResetLock = new(1, 1);
|
||||
|
||||
public virtual (Int64 Speed, Int64 BytesTotal, Int64 BytesDone) GetDownloadStats(Guid downloadId)
|
||||
{
|
||||
return TorrentRunner.GetStats(downloadId);
|
||||
|
|
@ -99,18 +100,20 @@ public class Torrents(
|
|||
public virtual async Task<Torrent> AddNzbLinkToDebridQueue(String nzbLink, Torrent torrent)
|
||||
{
|
||||
torrent.RdStatus = TorrentStatus.Queued;
|
||||
|
||||
try
|
||||
{
|
||||
var uri = new Uri(nzbLink);
|
||||
var lastSegment = uri.Segments.LastOrDefault()?.TrimEnd('/');
|
||||
torrent.RdName = !String.IsNullOrWhiteSpace(lastSegment) ? lastSegment : "Unknown NZB";
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "{ex.Message}, trying to parse {nzbLink}", ex.Message, nzbLink);
|
||||
throw new ($"{ex.Message}, trying to parse {nzbLink}");
|
||||
|
||||
throw new($"{ex.Message}, trying to parse {nzbLink}");
|
||||
}
|
||||
|
||||
|
||||
var nzbHash = ComputeMd5Hash(nzbLink);
|
||||
var nzbNewTorrent = await AddQueued(nzbHash, nzbLink, false, DownloadType.Nzb, torrent);
|
||||
Log($"Adding {nzbLink} with hash {nzbHash} (nzb link) to queue");
|
||||
|
|
@ -124,14 +127,17 @@ public class Torrents(
|
|||
{
|
||||
torrent.RdName = fileName ?? "Unknown NZB";
|
||||
torrent.RdStatus = TorrentStatus.Queued;
|
||||
|
||||
try
|
||||
{
|
||||
using var stream = new MemoryStream(bytes);
|
||||
|
||||
var settings = new XmlReaderSettings
|
||||
{
|
||||
DtdProcessing = DtdProcessing.Ignore,
|
||||
XmlResolver = null
|
||||
};
|
||||
|
||||
using var reader = XmlReader.Create(stream, settings);
|
||||
var doc = XDocument.Load(reader);
|
||||
var nzbNamespace = doc.Root?.GetDefaultNamespace() ?? XNamespace.None;
|
||||
|
|
@ -139,7 +145,8 @@ public class Torrents(
|
|||
var title = doc.Root?
|
||||
.Elements(nzbNamespace + "head")
|
||||
.Elements(nzbNamespace + "meta")
|
||||
.FirstOrDefault(x => x.Attribute("type")?.Value == "name")?
|
||||
.FirstOrDefault(x => x.Attribute("type")?.Value == "name")
|
||||
?
|
||||
.Value;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(title))
|
||||
|
|
@ -147,7 +154,8 @@ public class Torrents(
|
|||
title = doc.Root?
|
||||
.Elements(nzbNamespace + "head")
|
||||
.Elements(nzbNamespace + "meta")
|
||||
.FirstOrDefault(x => x.Attribute("type")?.Value == "title")?
|
||||
.FirstOrDefault(x => x.Attribute("type")?.Value == "title")
|
||||
?
|
||||
.Value;
|
||||
}
|
||||
|
||||
|
|
@ -156,9 +164,10 @@ public class Torrents(
|
|||
torrent.RdName = title.Trim();
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "{ex.Message}, trying to parse NZB file contents", ex.Message);
|
||||
|
||||
throw new($"{ex.Message}, trying to parse NZB file contents");
|
||||
}
|
||||
|
||||
|
|
@ -649,7 +658,8 @@ public class Torrents(
|
|||
{
|
||||
Category = Settings.Get.Provider.Default.Category,
|
||||
DownloadClient = Settings.Get.DownloadClient.Client,
|
||||
DownloadAction = Settings.Get.Provider.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
||||
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,
|
||||
|
|
@ -1056,24 +1066,27 @@ public class Torrents(
|
|||
|
||||
private static String ComputeSha1Hash(String input)
|
||||
{
|
||||
using var sha1 = System.Security.Cryptography.SHA1.Create();
|
||||
using var sha1 = SHA1.Create();
|
||||
var bytes = Encoding.UTF8.GetBytes(input);
|
||||
var hashBytes = sha1.ComputeHash(bytes);
|
||||
|
||||
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
|
||||
private static String ComputeMd5Hash(String input)
|
||||
{
|
||||
using var md5 = System.Security.Cryptography.MD5.Create();
|
||||
using var md5 = MD5.Create();
|
||||
var bytes = Encoding.UTF8.GetBytes(input);
|
||||
var hashBytes = md5.ComputeHash(bytes);
|
||||
|
||||
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
|
||||
private static String ComputeMd5HashFromBytes(Byte[] bytes)
|
||||
{
|
||||
using var md5 = System.Security.Cryptography.MD5.Create();
|
||||
using var md5 = MD5.Create();
|
||||
var hashBytes = md5.ComputeHash(bytes);
|
||||
|
||||
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ public class UnpackClient(Download download, String destinationPath)
|
|||
{
|
||||
Progess = (Int32)Math.Round(d);
|
||||
},
|
||||
cancellationToken: cancellationToken);
|
||||
cancellationToken);
|
||||
|
||||
archive.Dispose();
|
||||
|
||||
|
|
|
|||
|
|
@ -3,32 +3,36 @@ using Microsoft.AspNetCore.Http;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using RdtClient.Data.Data;
|
||||
using RdtClient.Data.Enums;
|
||||
using RdtClient.Data.Models.Sabnzbd;
|
||||
using RdtClient.Service.Services;
|
||||
using RdtClient.Web.Controllers;
|
||||
using SignInResult = Microsoft.AspNetCore.Identity.SignInResult;
|
||||
|
||||
namespace RdtClient.Web.Test.Controllers;
|
||||
|
||||
public class SabnzbdControllerTest
|
||||
{
|
||||
private readonly Mock<Sabnzbd> _sabnzbdMock;
|
||||
private readonly Mock<Authentication> _authenticationMock;
|
||||
private readonly SabnzbdController _controller;
|
||||
private readonly Mock<Sabnzbd> _sabnzbdMock;
|
||||
|
||||
public SabnzbdControllerTest()
|
||||
{
|
||||
Data.Data.SettingData.Get.General.AuthenticationType = Data.Enums.AuthenticationType.None;
|
||||
Data.Data.SettingData.Get.Provider.ApiKey = "test-api-key";
|
||||
SettingData.Get.General.AuthenticationType = AuthenticationType.None;
|
||||
SettingData.Get.Provider.ApiKey = "test-api-key";
|
||||
|
||||
var torrentsMock = new Mock<Torrents>(null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!);
|
||||
var sabnzbdLoggerMock = new Mock<ILogger<Sabnzbd>>();
|
||||
_sabnzbdMock = new(sabnzbdLoggerMock.Object, torrentsMock.Object, null!);
|
||||
var loggerMock = new Mock<ILogger<SabnzbdController>>();
|
||||
_authenticationMock = new(null!, null!, null!);
|
||||
|
||||
|
||||
_controller = new(loggerMock.Object, _sabnzbdMock.Object);
|
||||
|
||||
|
||||
var httpContext = new DefaultHttpContext();
|
||||
|
||||
_controller.ControllerContext = new()
|
||||
{
|
||||
HttpContext = httpContext
|
||||
|
|
@ -39,7 +43,11 @@ public class SabnzbdControllerTest
|
|||
public async Task GetQueue_ReturnsOk()
|
||||
{
|
||||
// Arrange
|
||||
var queue = new SabnzbdQueue { NoOfSlots = 1 };
|
||||
var queue = new SabnzbdQueue
|
||||
{
|
||||
NoOfSlots = 1
|
||||
};
|
||||
|
||||
_sabnzbdMock.Setup(s => s.GetQueue()).ReturnsAsync(queue);
|
||||
|
||||
// Act
|
||||
|
|
@ -56,10 +64,15 @@ public class SabnzbdControllerTest
|
|||
public async Task GetQueue_Unauthorized_ReturnsOk_BecauseFilterIsSkippedInUnitTests()
|
||||
{
|
||||
// Arrange
|
||||
Data.Data.SettingData.Get.General.AuthenticationType = Data.Enums.AuthenticationType.UserNamePassword;
|
||||
var queue = new SabnzbdQueue { NoOfSlots = 1 };
|
||||
SettingData.Get.General.AuthenticationType = AuthenticationType.UserNamePassword;
|
||||
|
||||
var queue = new SabnzbdQueue
|
||||
{
|
||||
NoOfSlots = 1
|
||||
};
|
||||
|
||||
_sabnzbdMock.Setup(s => s.GetQueue()).ReturnsAsync(queue);
|
||||
|
||||
|
||||
// Act
|
||||
var result = await _controller.Queue();
|
||||
|
||||
|
|
@ -73,14 +86,18 @@ public class SabnzbdControllerTest
|
|||
public async Task GetQueue_WithMaAuth_ReturnsOk()
|
||||
{
|
||||
// Arrange
|
||||
Data.Data.SettingData.Get.General.AuthenticationType = Data.Enums.AuthenticationType.UserNamePassword;
|
||||
SettingData.Get.General.AuthenticationType = AuthenticationType.UserNamePassword;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Request.QueryString = new("?ma_username=user&ma_password=pass");
|
||||
_controller.ControllerContext.HttpContext = httpContext;
|
||||
|
||||
_authenticationMock.Setup(a => a.Login("user", "pass")).ReturnsAsync(Microsoft.AspNetCore.Identity.SignInResult.Success);
|
||||
|
||||
var queue = new SabnzbdQueue { NoOfSlots = 1 };
|
||||
|
||||
_authenticationMock.Setup(a => a.Login("user", "pass")).ReturnsAsync(SignInResult.Success);
|
||||
|
||||
var queue = new SabnzbdQueue
|
||||
{
|
||||
NoOfSlots = 1
|
||||
};
|
||||
|
||||
_sabnzbdMock.Setup(s => s.GetQueue()).ReturnsAsync(queue);
|
||||
|
||||
// Act
|
||||
|
|
@ -94,7 +111,11 @@ public class SabnzbdControllerTest
|
|||
public async Task GetHistory_ReturnsOk()
|
||||
{
|
||||
// Arrange
|
||||
var history = new SabnzbdHistory { NoOfSlots = 1 };
|
||||
var history = new SabnzbdHistory
|
||||
{
|
||||
NoOfSlots = 1
|
||||
};
|
||||
|
||||
_sabnzbdMock.Setup(s => s.GetHistory()).ReturnsAsync(history);
|
||||
|
||||
// Act
|
||||
|
|
@ -125,7 +146,7 @@ public class SabnzbdControllerTest
|
|||
public void GetVersion_ReturnsOk()
|
||||
{
|
||||
// Arrange
|
||||
Data.Data.SettingData.Get.General.AuthenticationType = Data.Enums.AuthenticationType.UserNamePassword;
|
||||
SettingData.Get.General.AuthenticationType = AuthenticationType.UserNamePassword;
|
||||
|
||||
// Act
|
||||
var result = _controller.Version();
|
||||
|
|
@ -140,8 +161,14 @@ public class SabnzbdControllerTest
|
|||
public void GetConfig_ReturnsOk()
|
||||
{
|
||||
// Arrange
|
||||
var config = new SabnzbdConfig { Misc = new()
|
||||
{ Port = "6500" } };
|
||||
var config = new SabnzbdConfig
|
||||
{
|
||||
Misc = new()
|
||||
{
|
||||
Port = "6500"
|
||||
}
|
||||
};
|
||||
|
||||
_sabnzbdMock.Setup(s => s.GetConfig()).Returns(config);
|
||||
|
||||
// Act
|
||||
|
|
@ -158,7 +185,12 @@ public class SabnzbdControllerTest
|
|||
public void GetCategories_ReturnsOk()
|
||||
{
|
||||
// Arrange
|
||||
var categories = new List<String> { "*", "Default" };
|
||||
var categories = new List<String>
|
||||
{
|
||||
"*",
|
||||
"Default"
|
||||
};
|
||||
|
||||
_sabnzbdMock.Setup(s => s.GetCategories()).Returns(categories);
|
||||
|
||||
// Act
|
||||
|
|
@ -200,10 +232,14 @@ public class SabnzbdControllerTest
|
|||
// Arrange
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Request.ContentType = "application/x-www-form-urlencoded";
|
||||
|
||||
httpContext.Request.Form = new FormCollection(new()
|
||||
{
|
||||
{ "mode", "unknown_form" }
|
||||
{
|
||||
"mode", "unknown_form"
|
||||
}
|
||||
});
|
||||
|
||||
_controller.ControllerContext.HttpContext = httpContext;
|
||||
|
||||
// Act
|
||||
|
|
@ -220,7 +256,7 @@ public class SabnzbdControllerTest
|
|||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Request.Method = "POST";
|
||||
httpContext.Request.QueryString = new("?cat=radarr&priority=-100");
|
||||
|
||||
|
||||
// Mocking multipart form data
|
||||
var fileMock = new Mock<IFormFile>();
|
||||
var content = "test content";
|
||||
|
|
@ -233,13 +269,19 @@ public class SabnzbdControllerTest
|
|||
fileMock.Setup(_ => _.OpenReadStream()).Returns(ms);
|
||||
fileMock.Setup(_ => _.FileName).Returns(fileName);
|
||||
fileMock.Setup(_ => _.Length).Returns(ms.Length);
|
||||
|
||||
fileMock.Setup(_ => _.CopyToAsync(It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
|
||||
.Callback<Stream, CancellationToken>((s, _) => ms.CopyTo(s))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
httpContext.Request.ContentType = "multipart/form-data; boundary=something";
|
||||
httpContext.Request.Form = new FormCollection(new(), new FormFileCollection { fileMock.Object });
|
||||
|
||||
|
||||
httpContext.Request.Form = new FormCollection(new(),
|
||||
new FormFileCollection
|
||||
{
|
||||
fileMock.Object
|
||||
});
|
||||
|
||||
_controller.ControllerContext.HttpContext = httpContext;
|
||||
_sabnzbdMock.Setup(s => s.AddFile(It.IsAny<Byte[]>(), fileName, "radarr", -100)).ReturnsAsync("nzo_id_123");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Moq;
|
||||
using RdtClient.Data.Data;
|
||||
using RdtClient.Data.Enums;
|
||||
using RdtClient.Service.Middleware;
|
||||
using RdtClient.Service.Services;
|
||||
|
|
@ -10,22 +13,22 @@ namespace RdtClient.Web.Test.Controllers;
|
|||
public class SabnzbdHandlerTest
|
||||
{
|
||||
private readonly Mock<Authentication> _authenticationMock;
|
||||
private readonly Mock<IHttpContextAccessor> _httpContextAccessorMock;
|
||||
private readonly SabnzbdHandler _handler;
|
||||
private readonly Mock<IHttpContextAccessor> _httpContextAccessorMock;
|
||||
|
||||
public SabnzbdHandlerTest()
|
||||
{
|
||||
_authenticationMock = new(null!, null!, null!);
|
||||
_httpContextAccessorMock = new();
|
||||
_handler = new(_authenticationMock.Object, _httpContextAccessorMock.Object);
|
||||
Data.Data.SettingData.Get.General.AuthenticationType = AuthenticationType.UserNamePassword;
|
||||
SettingData.Get.General.AuthenticationType = AuthenticationType.UserNamePassword;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAsync_AuthNone_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
Data.Data.SettingData.Get.General.AuthenticationType = AuthenticationType.None;
|
||||
SettingData.Get.General.AuthenticationType = AuthenticationType.None;
|
||||
var context = CreateContext();
|
||||
|
||||
// Act
|
||||
|
|
@ -43,9 +46,9 @@ public class SabnzbdHandlerTest
|
|||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Request.QueryString = new("?ma_username=user&ma_password=pass");
|
||||
_httpContextAccessorMock.Setup(a => a.HttpContext).Returns(httpContext);
|
||||
|
||||
|
||||
var context = CreateContext(httpContext);
|
||||
_authenticationMock.Setup(a => a.Login("user", "pass")).ReturnsAsync(Microsoft.AspNetCore.Identity.SignInResult.Success);
|
||||
_authenticationMock.Setup(a => a.Login("user", "pass")).ReturnsAsync(SignInResult.Success);
|
||||
|
||||
// Act
|
||||
await _handler.HandleAsync(context);
|
||||
|
|
@ -60,9 +63,9 @@ public class SabnzbdHandlerTest
|
|||
// Arrange
|
||||
Settings.Get.General.AuthenticationType = AuthenticationType.UserNamePassword;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var claimsPrincipal = new System.Security.Claims.ClaimsPrincipal(new System.Security.Claims.ClaimsIdentity("TestAuth"));
|
||||
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity("TestAuth"));
|
||||
httpContext.User = claimsPrincipal;
|
||||
|
||||
|
||||
var context = CreateContext(httpContext);
|
||||
|
||||
// Act
|
||||
|
|
@ -81,9 +84,9 @@ public class SabnzbdHandlerTest
|
|||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Request.QueryString = new("?ma_username=user&ma_password=wrong");
|
||||
_httpContextAccessorMock.Setup(a => a.HttpContext).Returns(httpContext);
|
||||
|
||||
|
||||
var context = CreateContext(httpContext);
|
||||
_authenticationMock.Setup(a => a.Login("user", "wrong")).ReturnsAsync(Microsoft.AspNetCore.Identity.SignInResult.Failed);
|
||||
_authenticationMock.Setup(a => a.Login("user", "wrong")).ReturnsAsync(SignInResult.Failed);
|
||||
|
||||
// Act
|
||||
await _handler.HandleAsync(context);
|
||||
|
|
@ -99,7 +102,7 @@ public class SabnzbdHandlerTest
|
|||
Settings.Get.General.AuthenticationType = AuthenticationType.UserNamePassword;
|
||||
var httpContext = new DefaultHttpContext();
|
||||
_httpContextAccessorMock.Setup(a => a.HttpContext).Returns(httpContext);
|
||||
|
||||
|
||||
var context = CreateContext(httpContext);
|
||||
|
||||
// Act
|
||||
|
|
@ -112,7 +115,8 @@ public class SabnzbdHandlerTest
|
|||
private AuthorizationHandlerContext CreateContext(HttpContext? httpContext = null)
|
||||
{
|
||||
var requirement = new SabnzbdRequirement();
|
||||
var user = httpContext?.User ?? new System.Security.Claims.ClaimsPrincipal();
|
||||
var user = httpContext?.User ?? new ClaimsPrincipal();
|
||||
|
||||
return new([requirement], user, null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ namespace RdtClient.Web.Test.Controllers;
|
|||
|
||||
public class TorrentsControllerNzbTest
|
||||
{
|
||||
private readonly Mock<Torrents> _torrentsMock;
|
||||
private readonly Mock<ILogger<TorrentsController>> _loggerMock;
|
||||
private readonly TorrentsController _controller;
|
||||
private readonly Mock<ILogger<TorrentsController>> _loggerMock;
|
||||
private readonly Mock<Torrents> _torrentsMock;
|
||||
|
||||
public TorrentsControllerNzbTest()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,20 +23,28 @@ public class SabnzbdController(ILogger<SabnzbdController> logger, Sabnzbd sabnzb
|
|||
|
||||
if (String.IsNullOrWhiteSpace(mode))
|
||||
{
|
||||
return BadRequest(new SabnzbdResponse { Error = "No mode specified" });
|
||||
return BadRequest(new SabnzbdResponse
|
||||
{
|
||||
Error = "No mode specified"
|
||||
});
|
||||
}
|
||||
|
||||
logger.LogWarning($"Sabnzbd API called (not implemented) - Method: {Request.Method}, Query: {Request.QueryString}");
|
||||
|
||||
return NotFound(new SabnzbdResponse());
|
||||
}
|
||||
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet]
|
||||
[SabnzbdMode("version")]
|
||||
public ActionResult Version()
|
||||
{
|
||||
logger.LogDebug("Sabnzbd mode: version");
|
||||
return Ok(new SabnzbdResponse { Version = "4.4.0" });
|
||||
|
||||
return Ok(new SabnzbdResponse
|
||||
{
|
||||
Version = "4.4.0"
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
|
@ -57,11 +65,19 @@ public class SabnzbdController(ILogger<SabnzbdController> logger, Sabnzbd sabnzb
|
|||
Error = "No value specified for delete operation"
|
||||
});
|
||||
}
|
||||
|
||||
await sabnzbd.Delete(value ?? "");
|
||||
return Ok(new SabnzbdResponse { Status = true });
|
||||
|
||||
return Ok(new SabnzbdResponse
|
||||
{
|
||||
Status = true
|
||||
});
|
||||
}
|
||||
|
||||
return Ok(new SabnzbdResponse { Queue = await sabnzbd.GetQueue() });
|
||||
return Ok(new SabnzbdResponse
|
||||
{
|
||||
Queue = await sabnzbd.GetQueue()
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
|
@ -69,7 +85,11 @@ public class SabnzbdController(ILogger<SabnzbdController> logger, Sabnzbd sabnzb
|
|||
public async Task<ActionResult> History()
|
||||
{
|
||||
logger.LogDebug("Sabnzbd mode: history");
|
||||
return Ok(new SabnzbdResponse { History = await sabnzbd.GetHistory() });
|
||||
|
||||
return Ok(new SabnzbdResponse
|
||||
{
|
||||
History = await sabnzbd.GetHistory()
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
|
@ -77,7 +97,11 @@ public class SabnzbdController(ILogger<SabnzbdController> logger, Sabnzbd sabnzb
|
|||
public ActionResult GetConfig()
|
||||
{
|
||||
logger.LogDebug("Sabnzbd mode: get_config");
|
||||
return Ok(new SabnzbdResponse { Config = sabnzbd.GetConfig() });
|
||||
|
||||
return Ok(new SabnzbdResponse
|
||||
{
|
||||
Config = sabnzbd.GetConfig()
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
|
@ -85,7 +109,11 @@ public class SabnzbdController(ILogger<SabnzbdController> logger, Sabnzbd sabnzb
|
|||
public ActionResult GetCats()
|
||||
{
|
||||
logger.LogDebug("Sabnzbd mode: get_cats");
|
||||
return Ok(new SabnzbdResponse { Categories = sabnzbd.GetCategories() });
|
||||
|
||||
return Ok(new SabnzbdResponse
|
||||
{
|
||||
Categories = sabnzbd.GetCategories()
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
|
@ -101,7 +129,12 @@ public class SabnzbdController(ILogger<SabnzbdController> logger, Sabnzbd sabnzb
|
|||
Int32? priority = Int32.TryParse(priorityStr, out var p) ? p : null;
|
||||
|
||||
var result = await sabnzbd.AddUrl(url ?? "", category, priority);
|
||||
return Ok(new SabnzbdResponse { Status = true, NzoIds = [result] });
|
||||
|
||||
return Ok(new SabnzbdResponse
|
||||
{
|
||||
Status = true,
|
||||
NzoIds = [result]
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
|
|
@ -109,12 +142,14 @@ public class SabnzbdController(ILogger<SabnzbdController> logger, Sabnzbd sabnzb
|
|||
public async Task<ActionResult> AddFile()
|
||||
{
|
||||
logger.LogDebug("Sabnzbd mode: addfile");
|
||||
|
||||
if (!Request.HasFormContentType)
|
||||
{
|
||||
return BadRequest("Expected multipart/form-data");
|
||||
}
|
||||
|
||||
var file = Request.Form.Files.FirstOrDefault();
|
||||
|
||||
if (file == null)
|
||||
{
|
||||
return BadRequest("No file uploaded");
|
||||
|
|
@ -127,8 +162,12 @@ public class SabnzbdController(ILogger<SabnzbdController> logger, Sabnzbd sabnzb
|
|||
using var ms = new MemoryStream();
|
||||
await file.CopyToAsync(ms);
|
||||
var result = await sabnzbd.AddFile(ms.ToArray(), file.FileName, category, priority);
|
||||
|
||||
return Ok(new SabnzbdResponse { Status = true, NzoIds = [result] });
|
||||
|
||||
return Ok(new SabnzbdResponse
|
||||
{
|
||||
Status = true,
|
||||
NzoIds = [result]
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
|
@ -136,16 +175,23 @@ public class SabnzbdController(ILogger<SabnzbdController> logger, Sabnzbd sabnzb
|
|||
public async Task<ActionResult> FullStatus()
|
||||
{
|
||||
logger.LogDebug("Sabnzbd mode: fullstatus");
|
||||
return Ok(new SabnzbdResponse { Version = "4.4.0", Queue = await sabnzbd.GetQueue() });
|
||||
|
||||
return Ok(new SabnzbdResponse
|
||||
{
|
||||
Version = "4.4.0",
|
||||
Queue = await sabnzbd.GetQueue()
|
||||
});
|
||||
}
|
||||
|
||||
private String? GetParam(String name)
|
||||
{
|
||||
var value = Request.Query[name].ToString();
|
||||
|
||||
if (String.IsNullOrWhiteSpace(value) && Request.HasFormContentType)
|
||||
{
|
||||
value = Request.Form[name].ToString();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ public class SabnzbdModeAttribute(String mode) : Attribute, IActionConstraint
|
|||
public Boolean Accept(ActionConstraintContext context)
|
||||
{
|
||||
var request = context.RouteContext.HttpContext.Request;
|
||||
|
||||
|
||||
String? modeValue = request.Query["mode"];
|
||||
|
||||
if (String.IsNullOrWhiteSpace(modeValue) && request.HasFormContentType)
|
||||
|
|
|
|||
|
|
@ -22,70 +22,72 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
|
|||
var results = await torrents.Get();
|
||||
|
||||
var torrentDtos = results.Select(torrent => new TorrentDto
|
||||
{
|
||||
TorrentId = torrent.TorrentId,
|
||||
Hash = torrent.Hash,
|
||||
Category = torrent.Category,
|
||||
DownloadAction = torrent.DownloadAction,
|
||||
FinishedAction = torrent.FinishedAction,
|
||||
FinishedActionDelay = torrent.FinishedActionDelay,
|
||||
HostDownloadAction = torrent.HostDownloadAction,
|
||||
DownloadMinSize = torrent.DownloadMinSize,
|
||||
IncludeRegex = torrent.IncludeRegex,
|
||||
ExcludeRegex = torrent.ExcludeRegex,
|
||||
DownloadManualFiles = torrent.DownloadManualFiles,
|
||||
DownloadClient = torrent.DownloadClient,
|
||||
Added = torrent.Added,
|
||||
FilesSelected = torrent.FilesSelected,
|
||||
Completed = torrent.Completed,
|
||||
Type = torrent.Type,
|
||||
IsFile = torrent.IsFile,
|
||||
Priority = torrent.Priority,
|
||||
RetryCount = torrent.RetryCount,
|
||||
DownloadRetryAttempts = torrent.DownloadRetryAttempts,
|
||||
TorrentRetryAttempts = torrent.TorrentRetryAttempts,
|
||||
DeleteOnError = torrent.DeleteOnError,
|
||||
Lifetime = torrent.Lifetime,
|
||||
Error = torrent.Error,
|
||||
RdId = torrent.RdId,
|
||||
RdName = torrent.RdName,
|
||||
RdSize = torrent.RdSize,
|
||||
RdHost = torrent.RdHost,
|
||||
RdSplit = torrent.RdSplit,
|
||||
RdProgress = torrent.RdProgress,
|
||||
RdStatus = torrent.RdStatus,
|
||||
RdStatusRaw = torrent.RdStatusRaw,
|
||||
RdAdded = torrent.RdAdded,
|
||||
RdEnded = torrent.RdEnded,
|
||||
RdSpeed = torrent.RdSpeed,
|
||||
RdSeeders = torrent.RdSeeders,
|
||||
Files = torrent.Files,
|
||||
Downloads = torrent.Downloads.Select(download =>
|
||||
{
|
||||
var (speed, bytesTotal, bytesDone) = torrents.GetDownloadStats(download.DownloadId);
|
||||
{
|
||||
TorrentId = torrent.TorrentId,
|
||||
Hash = torrent.Hash,
|
||||
Category = torrent.Category,
|
||||
DownloadAction = torrent.DownloadAction,
|
||||
FinishedAction = torrent.FinishedAction,
|
||||
FinishedActionDelay = torrent.FinishedActionDelay,
|
||||
HostDownloadAction = torrent.HostDownloadAction,
|
||||
DownloadMinSize = torrent.DownloadMinSize,
|
||||
IncludeRegex = torrent.IncludeRegex,
|
||||
ExcludeRegex = torrent.ExcludeRegex,
|
||||
DownloadManualFiles = torrent.DownloadManualFiles,
|
||||
DownloadClient = torrent.DownloadClient,
|
||||
Added = torrent.Added,
|
||||
FilesSelected = torrent.FilesSelected,
|
||||
Completed = torrent.Completed,
|
||||
Type = torrent.Type,
|
||||
IsFile = torrent.IsFile,
|
||||
Priority = torrent.Priority,
|
||||
RetryCount = torrent.RetryCount,
|
||||
DownloadRetryAttempts = torrent.DownloadRetryAttempts,
|
||||
TorrentRetryAttempts = torrent.TorrentRetryAttempts,
|
||||
DeleteOnError = torrent.DeleteOnError,
|
||||
Lifetime = torrent.Lifetime,
|
||||
Error = torrent.Error,
|
||||
RdId = torrent.RdId,
|
||||
RdName = torrent.RdName,
|
||||
RdSize = torrent.RdSize,
|
||||
RdHost = torrent.RdHost,
|
||||
RdSplit = torrent.RdSplit,
|
||||
RdProgress = torrent.RdProgress,
|
||||
RdStatus = torrent.RdStatus,
|
||||
RdStatusRaw = torrent.RdStatusRaw,
|
||||
RdAdded = torrent.RdAdded,
|
||||
RdEnded = torrent.RdEnded,
|
||||
RdSpeed = torrent.RdSpeed,
|
||||
RdSeeders = torrent.RdSeeders,
|
||||
Files = torrent.Files,
|
||||
Downloads = torrent.Downloads.Select(download =>
|
||||
{
|
||||
var (speed, bytesTotal, bytesDone) = torrents.GetDownloadStats(download.DownloadId);
|
||||
|
||||
return new DownloadDto
|
||||
{
|
||||
DownloadId = download.DownloadId,
|
||||
TorrentId = download.TorrentId,
|
||||
Path = download.Path,
|
||||
Link = download.Link,
|
||||
Added = download.Added,
|
||||
DownloadQueued = download.DownloadQueued,
|
||||
DownloadStarted = download.DownloadStarted,
|
||||
DownloadFinished = download.DownloadFinished,
|
||||
UnpackingQueued = download.UnpackingQueued,
|
||||
UnpackingStarted = download.UnpackingStarted,
|
||||
UnpackingFinished = download.UnpackingFinished,
|
||||
Completed = download.Completed,
|
||||
RetryCount = download.RetryCount,
|
||||
Error = download.Error,
|
||||
BytesTotal = bytesTotal,
|
||||
BytesDone = bytesDone,
|
||||
Speed = speed
|
||||
};
|
||||
}).ToList()
|
||||
}).ToList();
|
||||
return new DownloadDto
|
||||
{
|
||||
DownloadId = download.DownloadId,
|
||||
TorrentId = download.TorrentId,
|
||||
Path = download.Path,
|
||||
Link = download.Link,
|
||||
Added = download.Added,
|
||||
DownloadQueued = download.DownloadQueued,
|
||||
DownloadStarted = download.DownloadStarted,
|
||||
DownloadFinished = download.DownloadFinished,
|
||||
UnpackingQueued = download.UnpackingQueued,
|
||||
UnpackingStarted = download.UnpackingStarted,
|
||||
UnpackingFinished = download.UnpackingFinished,
|
||||
Completed = download.Completed,
|
||||
RetryCount = download.RetryCount,
|
||||
Error = download.Error,
|
||||
BytesTotal = bytesTotal,
|
||||
BytesDone = bytesDone,
|
||||
Speed = speed
|
||||
};
|
||||
})
|
||||
.ToList()
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return Ok(torrentDtos);
|
||||
}
|
||||
|
|
@ -146,30 +148,31 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
|
|||
RdSeeders = torrent.RdSeeders,
|
||||
Files = torrent.Files,
|
||||
Downloads = torrent.Downloads.Select(download =>
|
||||
{
|
||||
var (speed, bytesTotal, bytesDone) = torrents.GetDownloadStats(download.DownloadId);
|
||||
{
|
||||
var (speed, bytesTotal, bytesDone) = torrents.GetDownloadStats(download.DownloadId);
|
||||
|
||||
return new DownloadDto
|
||||
{
|
||||
DownloadId = download.DownloadId,
|
||||
TorrentId = download.TorrentId,
|
||||
Path = download.Path,
|
||||
Link = download.Link,
|
||||
Added = download.Added,
|
||||
DownloadQueued = download.DownloadQueued,
|
||||
DownloadStarted = download.DownloadStarted,
|
||||
DownloadFinished = download.DownloadFinished,
|
||||
UnpackingQueued = download.UnpackingQueued,
|
||||
UnpackingStarted = download.UnpackingStarted,
|
||||
UnpackingFinished = download.UnpackingFinished,
|
||||
Completed = download.Completed,
|
||||
RetryCount = download.RetryCount,
|
||||
Error = download.Error,
|
||||
BytesTotal = bytesTotal,
|
||||
BytesDone = bytesDone,
|
||||
Speed = speed
|
||||
};
|
||||
}).ToList()
|
||||
return new DownloadDto
|
||||
{
|
||||
DownloadId = download.DownloadId,
|
||||
TorrentId = download.TorrentId,
|
||||
Path = download.Path,
|
||||
Link = download.Link,
|
||||
Added = download.Added,
|
||||
DownloadQueued = download.DownloadQueued,
|
||||
DownloadStarted = download.DownloadStarted,
|
||||
DownloadFinished = download.DownloadFinished,
|
||||
UnpackingQueued = download.UnpackingQueued,
|
||||
UnpackingStarted = download.UnpackingStarted,
|
||||
UnpackingFinished = download.UnpackingFinished,
|
||||
Completed = download.Completed,
|
||||
RetryCount = download.RetryCount,
|
||||
Error = download.Error,
|
||||
BytesTotal = bytesTotal,
|
||||
BytesDone = bytesDone,
|
||||
Speed = speed
|
||||
};
|
||||
})
|
||||
.ToList()
|
||||
};
|
||||
|
||||
return Ok(torrentDto);
|
||||
|
|
@ -180,6 +183,7 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
|
|||
public ActionResult<DiskSpaceStatus?> GetDiskSpaceStatus()
|
||||
{
|
||||
var status = DiskSpaceMonitor.GetCurrentStatus();
|
||||
|
||||
return Ok(status);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ using Microsoft.Extensions.Hosting.WindowsServices;
|
|||
using RdtClient.Data.Data;
|
||||
using RdtClient.Data.Models.Internal;
|
||||
using RdtClient.Service;
|
||||
using RdtClient.Service.Helpers;
|
||||
using RdtClient.Service.Middleware;
|
||||
using RdtClient.Service.Services;
|
||||
using RdtClient.Service.Helpers;
|
||||
using Serilog;
|
||||
using Serilog.Debugging;
|
||||
using Serilog.Events;
|
||||
|
|
@ -74,8 +74,16 @@ builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationSc
|
|||
});
|
||||
|
||||
builder.Services.AddAuthorizationBuilder()
|
||||
.AddPolicy("AuthSetting", policyCorrectUser => { policyCorrectUser.Requirements.Add(new AuthSettingRequirement()); })
|
||||
.AddPolicy("Sabnzbd", policyCorrectUser => { policyCorrectUser.Requirements.Add(new SabnzbdRequirement()); });
|
||||
.AddPolicy("AuthSetting",
|
||||
policyCorrectUser =>
|
||||
{
|
||||
policyCorrectUser.Requirements.Add(new AuthSettingRequirement());
|
||||
})
|
||||
.AddPolicy("Sabnzbd",
|
||||
policyCorrectUser =>
|
||||
{
|
||||
policyCorrectUser.Requirements.Add(new SabnzbdRequirement());
|
||||
});
|
||||
|
||||
builder.Services.AddIdentity<IdentityUser, IdentityRole>(options =>
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue