Merge pull request #729 from Cucumberrbob/refactor/download-filtering
refactor!: DRY Download Filtering
This commit is contained in:
commit
13bb353644
10 changed files with 463 additions and 465 deletions
|
|
@ -0,0 +1,261 @@
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Moq;
|
||||||
|
using RdtClient.Data.Models.Data;
|
||||||
|
using RdtClient.Service.Services;
|
||||||
|
|
||||||
|
namespace RdtClient.Service.Test.Services;
|
||||||
|
|
||||||
|
public class DownloadableFileFilterTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void IsDownloadable_WhenNoFilterSpecified_ReturnsTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mocks = new Mocks();
|
||||||
|
|
||||||
|
var torrent = new Torrent
|
||||||
|
{
|
||||||
|
RdId = "1"
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileFilter = new DownloadableFileFilter(mocks.LoggerMock.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = fileFilter.IsDownloadable(torrent, "file.txt", 10000);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
// downloadMinSize is in MB, fileSize is in B
|
||||||
|
[InlineData(100, 20 * 1024 * 1024)]
|
||||||
|
[InlineData(2, 2 * 1024 * 1024)]
|
||||||
|
[InlineData(2, 2 * (1000 * 1000 + 1))] // mostly to show we use 1024 not 1000 for conversion
|
||||||
|
public void IsDownloadable_WhenDownloadMinSizeSpecified_AndDownloadBelowSize_ReturnsFalse(Int32 downloadMinSize, Int64 fileSize)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mocks = new Mocks();
|
||||||
|
|
||||||
|
var torrent = new Torrent
|
||||||
|
{
|
||||||
|
RdId = "1",
|
||||||
|
DownloadMinSize = downloadMinSize
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileFilter = new DownloadableFileFilter(mocks.LoggerMock.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = fileFilter.IsDownloadable(torrent, "file.txt", fileSize);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(100, 110 * 1024 * 1024)]
|
||||||
|
[InlineData(2, 2 * 1024 * 1024 + 1)]
|
||||||
|
public void IsDownloadable_WhenDownloadMinSizeSpecified_AndDownloadAboveSize_ReturnsTrue(Int32 downloadMinSize, Int64 fileSize)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mocks = new Mocks();
|
||||||
|
|
||||||
|
var torrent = new Torrent
|
||||||
|
{
|
||||||
|
RdId = "1",
|
||||||
|
DownloadMinSize = downloadMinSize
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileFilter = new DownloadableFileFilter(mocks.LoggerMock.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = fileFilter.IsDownloadable(torrent, "file.txt", fileSize);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("file", "no-match")]
|
||||||
|
[InlineData("file", "even/in/a/subdirectory.txt")]
|
||||||
|
[InlineData("ch[aA]racter c[lL]asses", "nope.txt")]
|
||||||
|
[InlineData("digits\\d+", "123 not matching.txt")]
|
||||||
|
public void IsDownloadable_WhenIncludeRegexSpecified_AndPathDoesNotMatchRegex_ReturnsFalse(String includeRegex, String filePath)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mocks = new Mocks();
|
||||||
|
|
||||||
|
var torrent = new Torrent
|
||||||
|
{
|
||||||
|
RdId = "1",
|
||||||
|
IncludeRegex = includeRegex
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileFilter = new DownloadableFileFilter(mocks.LoggerMock.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = fileFilter.IsDownloadable(torrent, filePath, Int64.MaxValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("file", "file.txt")]
|
||||||
|
[InlineData("file", "file/in/a/subdirectory.txt")]
|
||||||
|
[InlineData("ch[aA]racter c[lL]asses", "character cLasses")]
|
||||||
|
[InlineData("digits\\d+", "digits123456.txt")]
|
||||||
|
public void IsDownloadable_WhenIncludeRegexSpecified_AndPathMatchesRegex_ReturnsTrue(String includeRegex, String filePath)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mocks = new Mocks();
|
||||||
|
|
||||||
|
var torrent = new Torrent
|
||||||
|
{
|
||||||
|
RdId = "1",
|
||||||
|
IncludeRegex = includeRegex
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileFilter = new DownloadableFileFilter(mocks.LoggerMock.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = fileFilter.IsDownloadable(torrent, filePath, Int64.MaxValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("file", "no-match")]
|
||||||
|
[InlineData("file", "even/in/a/subdirectory.txt")]
|
||||||
|
[InlineData("ch[aA]racter c[lL]asses", "nope.txt")]
|
||||||
|
[InlineData("digits\\d+", "123 not matching.txt")]
|
||||||
|
public void IsDownloadable_WhenExcludeRegexSpecified_AndPathDoesNotMatchRegex_ReturnsTrue(String excludeRegex, String filePath)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mocks = new Mocks();
|
||||||
|
|
||||||
|
var torrent = new Torrent
|
||||||
|
{
|
||||||
|
RdId = "1",
|
||||||
|
ExcludeRegex = excludeRegex
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileFilter = new DownloadableFileFilter(mocks.LoggerMock.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = fileFilter.IsDownloadable(torrent, filePath, Int64.MaxValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("file", "file.txt")]
|
||||||
|
[InlineData("file", "file/in/a/subdirectory.txt")]
|
||||||
|
[InlineData("ch[aA]racter c[lL]asses", "character cLasses")]
|
||||||
|
[InlineData("digits\\d+", "digits123456.txt")]
|
||||||
|
public void IsDownloadable_WhenExcludeRegexSpecified_AndPathMatchesRegex_ReturnsFalse(String excludeRegex, String filePath)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mocks = new Mocks();
|
||||||
|
|
||||||
|
var torrent = new Torrent
|
||||||
|
{
|
||||||
|
RdId = "1",
|
||||||
|
ExcludeRegex = excludeRegex
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileFilter = new DownloadableFileFilter(mocks.LoggerMock.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = fileFilter.IsDownloadable(torrent, filePath, Int64.MaxValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("file", "file", "file.txt")]
|
||||||
|
[InlineData("file", "in/a", "file/in/a/subdirectory.txt")]
|
||||||
|
[InlineData("ch[aA]racter c[lL]asses", "character", "character cLasses")]
|
||||||
|
[InlineData("digits\\d+", "123456", "digits123456.txt")]
|
||||||
|
public void IsDownloadable_WhenBothIncludeAndExcludeRegexSpecified_AndPathMatchesIncludeAndExcludeRegex_ReturnsTrue(String includeRegex, String excludeRegex, String filePath)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mocks = new Mocks();
|
||||||
|
|
||||||
|
var torrent = new Torrent
|
||||||
|
{
|
||||||
|
RdId = "1",
|
||||||
|
IncludeRegex = includeRegex,
|
||||||
|
ExcludeRegex = excludeRegex
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileFilter = new DownloadableFileFilter(mocks.LoggerMock.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = fileFilter.IsDownloadable(torrent, filePath, Int64.MaxValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(10, "file", 10 * 1024 * 1024 + 1, "no-match.txt")]
|
||||||
|
public void IsDownloadable_WhenBothDownloadMinSizeAndIncludeRegexSpecified_AndDownloadAboveSizeAndDoesNotMatchRegex_ReturnsFalse(
|
||||||
|
Int32 minSize,
|
||||||
|
String includeRegex,
|
||||||
|
Int64 fileSize,
|
||||||
|
String filePath)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mocks = new Mocks();
|
||||||
|
|
||||||
|
var torrent = new Torrent
|
||||||
|
{
|
||||||
|
RdId = "1",
|
||||||
|
IncludeRegex = includeRegex,
|
||||||
|
DownloadMinSize = minSize
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileFilter = new DownloadableFileFilter(mocks.LoggerMock.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = fileFilter.IsDownloadable(torrent, filePath, fileSize);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(10, "file", 10 * 1024 * 1024 - 1, "file.txt")]
|
||||||
|
public void IsDownloadable_WhenBothDownloadMinSizeAndIncludeRegexSpecified_AndDownloadBelowSizeAndMatchesRegex_ReturnsFalse(
|
||||||
|
Int32 minSize,
|
||||||
|
String includeRegex,
|
||||||
|
Int64 fileSize,
|
||||||
|
String filePath)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mocks = new Mocks();
|
||||||
|
|
||||||
|
var torrent = new Torrent
|
||||||
|
{
|
||||||
|
RdId = "1",
|
||||||
|
IncludeRegex = includeRegex,
|
||||||
|
DownloadMinSize = minSize
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileFilter = new DownloadableFileFilter(mocks.LoggerMock.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = fileFilter.IsDownloadable(torrent, filePath, fileSize);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
private class Mocks
|
||||||
|
{
|
||||||
|
public readonly Mock<ILogger<DownloadableFileFilter>> LoggerMock = new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ using Moq;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using RdtClient.Data.Enums;
|
using RdtClient.Data.Enums;
|
||||||
using RdtClient.Data.Models.Data;
|
using RdtClient.Data.Models.Data;
|
||||||
|
using RdtClient.Service.Services;
|
||||||
using RdtClient.Service.Services.TorrentClients;
|
using RdtClient.Service.Services.TorrentClients;
|
||||||
using File = AllDebridNET.File;
|
using File = AllDebridNET.File;
|
||||||
|
|
||||||
|
|
@ -69,60 +70,6 @@ public class AllDebridTorrentClientTest
|
||||||
CompletionDate = new DateTimeOffset(2020, 1, 1, 1, 5, 0, TimeSpan.Zero).Second
|
CompletionDate = new DateTimeOffset(2020, 1, 1, 1, 5, 0, TimeSpan.Zero).Second
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly List<File> IncludeExcludeFiles =
|
|
||||||
[
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
FolderOrFileName = "include.txt",
|
|
||||||
Size = 1,
|
|
||||||
DownloadLink = "https://fake.url/include.txt"
|
|
||||||
},
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
FolderOrFileName = "exclude.txt",
|
|
||||||
Size = 1,
|
|
||||||
DownloadLink = "https://fake.url/exclude.txt"
|
|
||||||
},
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
FolderOrFileName = "include-folder",
|
|
||||||
SubNodes =
|
|
||||||
[
|
|
||||||
new File
|
|
||||||
{
|
|
||||||
FolderOrFileName = "include-folder-exclude-file.txt",
|
|
||||||
Size = 1,
|
|
||||||
DownloadLink = "https://fake.url/include-folder-exclude-file.txt"
|
|
||||||
},
|
|
||||||
new File
|
|
||||||
{
|
|
||||||
FolderOrFileName = "include-folder-include-file.txt",
|
|
||||||
Size = 1,
|
|
||||||
DownloadLink = "https://fake.url/include-folder-include-file.txt"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
FolderOrFileName = "exclude-folder",
|
|
||||||
SubNodes =
|
|
||||||
[
|
|
||||||
new File
|
|
||||||
{
|
|
||||||
FolderOrFileName = "exclude-folder-exclude-file.txt",
|
|
||||||
Size = 1,
|
|
||||||
DownloadLink = "https://fake.url/exclude-folder-exclude-file.txt"
|
|
||||||
},
|
|
||||||
new File
|
|
||||||
{
|
|
||||||
FolderOrFileName = "exclude-folder-include-file.txt",
|
|
||||||
Size = 1,
|
|
||||||
DownloadLink = "https://fake.url/exclude-folder-include-file.txt"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetTorrents_WhenFullSyncNoTorrents_ReturnsEmptyList()
|
public async Task GetTorrents_WhenFullSyncNoTorrents_ReturnsEmptyList()
|
||||||
{
|
{
|
||||||
|
|
@ -137,7 +84,7 @@ public class AllDebridTorrentClientTest
|
||||||
Fullsync = true
|
Fullsync = true
|
||||||
});
|
});
|
||||||
|
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await allDebridTorrentClient.GetTorrents();
|
var result = await allDebridTorrentClient.GetTorrents();
|
||||||
|
|
@ -167,7 +114,7 @@ public class AllDebridTorrentClientTest
|
||||||
Fullsync = false
|
Fullsync = false
|
||||||
});
|
});
|
||||||
|
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await allDebridTorrentClient.GetTorrents();
|
var result = await allDebridTorrentClient.GetTorrents();
|
||||||
|
|
@ -197,7 +144,7 @@ public class AllDebridTorrentClientTest
|
||||||
Fullsync = true
|
Fullsync = true
|
||||||
});
|
});
|
||||||
|
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
||||||
|
|
@ -233,7 +180,7 @@ public class AllDebridTorrentClientTest
|
||||||
Fullsync = false
|
Fullsync = false
|
||||||
});
|
});
|
||||||
|
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
||||||
|
|
@ -275,7 +222,7 @@ public class AllDebridTorrentClientTest
|
||||||
Fullsync = false
|
Fullsync = false
|
||||||
});
|
});
|
||||||
|
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
||||||
|
|
@ -322,7 +269,7 @@ public class AllDebridTorrentClientTest
|
||||||
Fullsync = true
|
Fullsync = true
|
||||||
});
|
});
|
||||||
|
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
||||||
|
|
@ -347,7 +294,7 @@ public class AllDebridTorrentClientTest
|
||||||
};
|
};
|
||||||
|
|
||||||
var serializedOriginal = JsonConvert.SerializeObject(torrent);
|
var serializedOriginal = JsonConvert.SerializeObject(torrent);
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await allDebridTorrentClient.UpdateData(torrent, null);
|
var result = await allDebridTorrentClient.UpdateData(torrent, null);
|
||||||
|
|
@ -373,7 +320,7 @@ public class AllDebridTorrentClientTest
|
||||||
};
|
};
|
||||||
|
|
||||||
mocks.AllDebridClientMock.Setup(c => c.Magnet.StatusAsync(torrent.RdId, It.IsAny<CancellationToken>())).ReturnsAsync(Magnet1Finished);
|
mocks.AllDebridClientMock.Setup(c => c.Magnet.StatusAsync(torrent.RdId, It.IsAny<CancellationToken>())).ReturnsAsync(Magnet1Finished);
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await allDebridTorrentClient.UpdateData(torrent, null);
|
var result = await allDebridTorrentClient.UpdateData(torrent, null);
|
||||||
|
|
@ -406,7 +353,7 @@ public class AllDebridTorrentClientTest
|
||||||
mocks.AllDebridClientMock.Setup(c => c.Magnet.StatusAsync(torrent.RdId, It.IsAny<CancellationToken>()))
|
mocks.AllDebridClientMock.Setup(c => c.Magnet.StatusAsync(torrent.RdId, It.IsAny<CancellationToken>()))
|
||||||
.ThrowsAsync(new AllDebridException("Magnet not found", "MAGNET_INVALID_ID"));
|
.ThrowsAsync(new AllDebridException("Magnet not found", "MAGNET_INVALID_ID"));
|
||||||
|
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await allDebridTorrentClient.UpdateData(torrent, null);
|
var result = await allDebridTorrentClient.UpdateData(torrent, null);
|
||||||
|
|
@ -441,7 +388,7 @@ public class AllDebridTorrentClientTest
|
||||||
Fullsync = true
|
Fullsync = true
|
||||||
});
|
});
|
||||||
|
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
|
||||||
var torrentClientTorrent = (await allDebridTorrentClient.GetTorrents()).First();
|
var torrentClientTorrent = (await allDebridTorrentClient.GetTorrents()).First();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -468,7 +415,7 @@ public class AllDebridTorrentClientTest
|
||||||
RdId = null
|
RdId = null
|
||||||
};
|
};
|
||||||
|
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await allDebridTorrentClient.GetDownloadLinks(torrent);
|
var result = await allDebridTorrentClient.GetDownloadLinks(torrent);
|
||||||
|
|
@ -479,7 +426,57 @@ public class AllDebridTorrentClientTest
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetDownloadLinks_ByDefault_GetsLinksForAllFiles()
|
public async Task GetDownloadLinks_UsesFileFilter()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mocks = new Mocks();
|
||||||
|
|
||||||
|
var torrent = new Torrent
|
||||||
|
{
|
||||||
|
RdId = "1"
|
||||||
|
};
|
||||||
|
|
||||||
|
List<File> files =
|
||||||
|
[
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
FolderOrFileName = "file1.txt",
|
||||||
|
Size = 123,
|
||||||
|
DownloadLink = "https://fake.url/file1.txt"
|
||||||
|
},
|
||||||
|
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
FolderOrFileName = "folder",
|
||||||
|
SubNodes =
|
||||||
|
[
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
FolderOrFileName = "file2.txt",
|
||||||
|
Size = 180,
|
||||||
|
DownloadLink = "https://fake.url/file2.txt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
mocks.AllDebridClientMock.Setup(c => c.Magnet.FilesAsync(Int64.Parse(torrent.RdId), It.IsAny<CancellationToken>())).ReturnsAsync(files);
|
||||||
|
mocks.FileFilterMock.Setup(f => f.IsDownloadable(torrent, "file1.txt", 123)).Returns(true);
|
||||||
|
mocks.FileFilterMock.Setup(f => f.IsDownloadable(torrent, "folder/file2.txt", 180)).Returns(false);
|
||||||
|
|
||||||
|
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await allDebridTorrentClient.GetDownloadLinks(torrent);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Single(result);
|
||||||
|
Assert.Contains("https://fake.url/file1.txt", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetDownloadLinks_WhenAllFilesExcluded_ReturnsAllFiles()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mocks = new Mocks();
|
var mocks = new Mocks();
|
||||||
|
|
@ -494,20 +491,22 @@ public class AllDebridTorrentClientTest
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
FolderOrFileName = "file-1.txt",
|
FolderOrFileName = "file-1.txt",
|
||||||
Size = 50,
|
Size = 100,
|
||||||
DownloadLink = "https://fake.url/file-1.txt"
|
DownloadLink = "https://fake.url/file-1.txt"
|
||||||
},
|
},
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
FolderOrFileName = "file-2.txt",
|
FolderOrFileName = "file-2.txt",
|
||||||
Size = 150,
|
Size = 100,
|
||||||
DownloadLink = "https://fake.url/file-2.txt"
|
DownloadLink = "https://fake.url/file-2.txt"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
var expectedLinksSet = new HashSet<String>(files.Select(n => n.DownloadLink)!);
|
var expectedLinksSet = new HashSet<String>(files.Select(n => n.DownloadLink)!);
|
||||||
mocks.AllDebridClientMock.Setup(c => c.Magnet.FilesAsync(Int64.Parse(torrent.RdId), It.IsAny<CancellationToken>())).ReturnsAsync(files);
|
mocks.AllDebridClientMock.Setup(c => c.Magnet.FilesAsync(Int64.Parse(torrent.RdId), It.IsAny<CancellationToken>())).ReturnsAsync(files);
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
mocks.FileFilterMock.Setup(f => f.IsDownloadable(torrent, It.IsAny<String>(), It.IsAny<Int64>())).Returns(false);
|
||||||
|
|
||||||
|
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await allDebridTorrentClient.GetDownloadLinks(torrent);
|
var result = await allDebridTorrentClient.GetDownloadLinks(torrent);
|
||||||
|
|
@ -516,184 +515,9 @@ public class AllDebridTorrentClientTest
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
var linksSet = new HashSet<String>(result);
|
var linksSet = new HashSet<String>(result);
|
||||||
Assert.Equal(expectedLinksSet, linksSet);
|
Assert.Equal(expectedLinksSet, linksSet);
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
mocks.FileFilterMock.Verify(f => f.IsDownloadable(torrent, "file-1.txt", 100));
|
||||||
public async Task GetDownloadLinks_WhenDownloadMinSizeSet_GetsLinksForOnlyFilesAboveThatSize()
|
mocks.FileFilterMock.Verify(f => f.IsDownloadable(torrent, "file-2.txt", 100));
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mocks = new Mocks();
|
|
||||||
|
|
||||||
var torrent = new Torrent
|
|
||||||
{
|
|
||||||
RdId = "1",
|
|
||||||
|
|
||||||
// NB: this is in MB, the file sizes below are in B
|
|
||||||
DownloadMinSize = 100
|
|
||||||
};
|
|
||||||
|
|
||||||
List<File> files =
|
|
||||||
[
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
FolderOrFileName = "too-small.txt",
|
|
||||||
Size = (torrent.DownloadMinSize - 1) * 1024 * 1024,
|
|
||||||
DownloadLink = "https://fake.url/too-small.txt"
|
|
||||||
},
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
FolderOrFileName = "bigger-than-min.txt",
|
|
||||||
Size = (torrent.DownloadMinSize + 1) * 1024 * 1024,
|
|
||||||
DownloadLink = "https://fake.url/bigger-than-min.txt"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
var expectedLinksSet = new HashSet<String>(files.Where(m => m.Size > torrent.DownloadMinSize * 1024 * 1024).Select(n => n.DownloadLink)!);
|
|
||||||
mocks.AllDebridClientMock.Setup(c => c.Magnet.FilesAsync(Int64.Parse(torrent.RdId), It.IsAny<CancellationToken>())).ReturnsAsync(files);
|
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await allDebridTorrentClient.GetDownloadLinks(torrent);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(result);
|
|
||||||
var linksSet = new HashSet<String>(result);
|
|
||||||
Assert.Equal(expectedLinksSet, linksSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetDownloadLinks_WhenIncludeRegexSet_GetsLinksForOnlyFilesMatchingRegex()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mocks = new Mocks();
|
|
||||||
|
|
||||||
var torrent = new Torrent
|
|
||||||
{
|
|
||||||
RdId = "1",
|
|
||||||
IncludeRegex = "include"
|
|
||||||
};
|
|
||||||
|
|
||||||
var expectedLinksSet = new HashSet<String>([
|
|
||||||
"https://fake.url/include.txt", "https://fake.url/include-folder-exclude-file.txt", "https://fake.url/include-folder-include-file.txt",
|
|
||||||
"https://fake.url/exclude-folder-include-file.txt"
|
|
||||||
]);
|
|
||||||
|
|
||||||
mocks.AllDebridClientMock.Setup(c => c.Magnet.FilesAsync(Int64.Parse(torrent.RdId), It.IsAny<CancellationToken>())).ReturnsAsync(IncludeExcludeFiles);
|
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await allDebridTorrentClient.GetDownloadLinks(torrent);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(result);
|
|
||||||
var linksSet = new HashSet<String>(result);
|
|
||||||
Assert.Equal(expectedLinksSet, linksSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetDownloadLinks_WhenExcludeRegexSet_GetsLinksForOnlyFilesNotMatchingRegex()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mocks = new Mocks();
|
|
||||||
|
|
||||||
var torrent = new Torrent
|
|
||||||
{
|
|
||||||
RdId = "1",
|
|
||||||
ExcludeRegex = "exclude"
|
|
||||||
};
|
|
||||||
|
|
||||||
var expectedLinksSet = new HashSet<String>([
|
|
||||||
"https://fake.url/include.txt", "https://fake.url/include-folder-include-file.txt"
|
|
||||||
]);
|
|
||||||
|
|
||||||
mocks.AllDebridClientMock.Setup(c => c.Magnet.FilesAsync(Int64.Parse(torrent.RdId), It.IsAny<CancellationToken>())).ReturnsAsync(IncludeExcludeFiles);
|
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await allDebridTorrentClient.GetDownloadLinks(torrent);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(result);
|
|
||||||
var linksSet = new HashSet<String>(result);
|
|
||||||
Assert.Equal(expectedLinksSet, linksSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetDownloadLinks_WhenIncludeAndExcludeRegexSet_GetsLinksForOnlyFilesMatchingIncludeRegexEvenIfNotMatchingExcludeRegex()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mocks = new Mocks();
|
|
||||||
|
|
||||||
var torrentInclude = new Torrent
|
|
||||||
{
|
|
||||||
RdId = "1",
|
|
||||||
IncludeRegex = "include"
|
|
||||||
};
|
|
||||||
|
|
||||||
var torrentIncludeExclude = new Torrent
|
|
||||||
{
|
|
||||||
RdId = "2",
|
|
||||||
IncludeRegex = "include",
|
|
||||||
ExcludeRegex = "exclude"
|
|
||||||
};
|
|
||||||
|
|
||||||
mocks.AllDebridClientMock.Setup(c => c.Magnet.FilesAsync(It.IsAny<Int64>(), It.IsAny<CancellationToken>())).ReturnsAsync(IncludeExcludeFiles);
|
|
||||||
|
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var includeOnlyResult = await allDebridTorrentClient.GetDownloadLinks(torrentInclude);
|
|
||||||
var includeExcludeResult = await allDebridTorrentClient.GetDownloadLinks(torrentIncludeExclude);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(includeOnlyResult);
|
|
||||||
Assert.NotNull(includeExcludeResult);
|
|
||||||
var includeOnlyLinksSet = new HashSet<String>(includeOnlyResult);
|
|
||||||
var includeExcludeLinksSet = new HashSet<String>(includeExcludeResult);
|
|
||||||
Assert.Equal(includeOnlyLinksSet, includeExcludeLinksSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetDownloadLinks_WhenAllFilesExcluded_ReturnsAllFiles()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mocks = new Mocks();
|
|
||||||
|
|
||||||
var torrent = new Torrent
|
|
||||||
{
|
|
||||||
RdId = "1",
|
|
||||||
|
|
||||||
// NB: this is in MB, the file sizes below are in B
|
|
||||||
DownloadMinSize = 100
|
|
||||||
};
|
|
||||||
|
|
||||||
List<File> files =
|
|
||||||
[
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
FolderOrFileName = "too-small-1.txt",
|
|
||||||
Size = (torrent.DownloadMinSize - 1) * 1024 * 1024,
|
|
||||||
DownloadLink = "https://fake.url/too-small-1.txt"
|
|
||||||
},
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
FolderOrFileName = "too-small-2.txt",
|
|
||||||
Size = (torrent.DownloadMinSize - 1) * 1024 * 1024,
|
|
||||||
DownloadLink = "https://fake.url/too-small-2.txt"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
var expectedLinksSet = new HashSet<String>(files.Select(n => n.DownloadLink)!);
|
|
||||||
mocks.AllDebridClientMock.Setup(c => c.Magnet.FilesAsync(Int64.Parse(torrent.RdId), It.IsAny<CancellationToken>())).ReturnsAsync(files);
|
|
||||||
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await allDebridTorrentClient.GetDownloadLinks(torrent);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(result);
|
|
||||||
var linksSet = new HashSet<String>(result);
|
|
||||||
Assert.Equal(expectedLinksSet, linksSet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Mocks
|
private class Mocks
|
||||||
|
|
@ -701,10 +525,12 @@ public class AllDebridTorrentClientTest
|
||||||
public readonly Mock<IAllDebridNetClientFactory> AllDebridClientFactoryMock;
|
public readonly Mock<IAllDebridNetClientFactory> AllDebridClientFactoryMock;
|
||||||
public readonly Mock<IAllDebridNETClient> AllDebridClientMock;
|
public readonly Mock<IAllDebridNETClient> AllDebridClientMock;
|
||||||
public readonly Mock<ILogger<AllDebridTorrentClient>> LoggerMock;
|
public readonly Mock<ILogger<AllDebridTorrentClient>> LoggerMock;
|
||||||
|
public readonly Mock<IDownloadableFileFilter> FileFilterMock;
|
||||||
|
|
||||||
public Mocks()
|
public Mocks()
|
||||||
{
|
{
|
||||||
LoggerMock = new();
|
LoggerMock = new();
|
||||||
|
FileFilterMock = new();
|
||||||
AllDebridClientMock = new();
|
AllDebridClientMock = new();
|
||||||
AllDebridClientFactoryMock = new();
|
AllDebridClientFactoryMock = new();
|
||||||
AllDebridClientFactoryMock.Setup(f => f.GetClient()).Returns(AllDebridClientMock.Object);
|
AllDebridClientFactoryMock.Setup(f => f.GetClient()).Returns(AllDebridClientMock.Object);
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ public static class DiConfig
|
||||||
services.AddScoped<TorrentRunner>();
|
services.AddScoped<TorrentRunner>();
|
||||||
services.AddScoped<DebridLinkClient>();
|
services.AddScoped<DebridLinkClient>();
|
||||||
|
|
||||||
|
services.AddSingleton<IDownloadableFileFilter, DownloadableFileFilter>();
|
||||||
|
|
||||||
services.AddSingleton<IAuthorizationHandler, AuthSettingHandler>();
|
services.AddSingleton<IAuthorizationHandler, AuthSettingHandler>();
|
||||||
|
|
||||||
services.AddHostedService<ProviderUpdater>();
|
services.AddHostedService<ProviderUpdater>();
|
||||||
|
|
|
||||||
79
server/RdtClient.Service/Services/DownloadableFileFilter.cs
Normal file
79
server/RdtClient.Service/Services/DownloadableFileFilter.cs
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using RdtClient.Data.Enums;
|
||||||
|
using RdtClient.Data.Models.Data;
|
||||||
|
|
||||||
|
namespace RdtClient.Service.Services;
|
||||||
|
|
||||||
|
public interface IDownloadableFileFilter
|
||||||
|
{
|
||||||
|
public Boolean IsDownloadable(Torrent torrent, String filePath, Int64 fileSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DownloadableFileFilter(ILogger<DownloadableFileFilter> logger) : IDownloadableFileFilter
|
||||||
|
{
|
||||||
|
public Boolean IsDownloadable(Torrent torrent, String filePath, Int64 fileSize)
|
||||||
|
{
|
||||||
|
var isDownloadable = PassesSizeFilter(torrent, filePath, fileSize) &&
|
||||||
|
PassesFilePathFilter(torrent, filePath);
|
||||||
|
|
||||||
|
if (isDownloadable)
|
||||||
|
{
|
||||||
|
logger.LogDebug("File {filePath} was included after filtering", filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isDownloadable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean PassesSizeFilter(Torrent torrent, String filePath, Int64 fileSize)
|
||||||
|
{
|
||||||
|
if (torrent is { ClientKind: Provider.RealDebrid, DownloadAction: TorrentDownloadAction.DownloadManual })
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (torrent.DownloadMinSize <= 0 || fileSize > torrent.DownloadMinSize * 1024 * 1024)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogDebug("Not downloading file {filePath} file size {fileSize} smaller than minimum {downloadMinSize}", filePath, fileSize, torrent.DownloadMinSize);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean PassesFilePathFilter(Torrent torrent, String filePath)
|
||||||
|
{
|
||||||
|
return PassesIncludeRegexFilter(torrent, filePath) && PassesExcludeRegexFilter(torrent, filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean PassesIncludeRegexFilter(Torrent torrent, String filePath)
|
||||||
|
{
|
||||||
|
if (String.IsNullOrWhiteSpace(torrent.IncludeRegex) || Regex.IsMatch(filePath, torrent.IncludeRegex))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogDebug("Not downloading file {filePath} does not match regex {includeRegex}", filePath, torrent.IncludeRegex);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean PassesExcludeRegexFilter(Torrent torrent, String filePath)
|
||||||
|
{
|
||||||
|
// If the IncludeRegex is set, ignore the ExcludeRegex
|
||||||
|
if (!String.IsNullOrWhiteSpace(torrent.IncludeRegex))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (String.IsNullOrWhiteSpace(torrent.ExcludeRegex) || !Regex.IsMatch(filePath, torrent.ExcludeRegex))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogDebug("Not downloading file {filePath} matches regex {excludeRegex}", filePath, torrent.ExcludeRegex);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Text.RegularExpressions;
|
using AllDebridNET;
|
||||||
using AllDebridNET;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using RdtClient.Data.Enums;
|
using RdtClient.Data.Enums;
|
||||||
|
|
@ -52,7 +51,7 @@ public class AllDebridNetClientFactory(ILogger<AllDebridNetClientFactory> logger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AllDebridTorrentClient(ILogger<AllDebridTorrentClient> logger, IAllDebridNetClientFactory allDebridNetClientFactory) : ITorrentClient
|
public class AllDebridTorrentClient(ILogger<AllDebridTorrentClient> logger, IAllDebridNetClientFactory allDebridNetClientFactory, IDownloadableFileFilter fileFilter) : ITorrentClient
|
||||||
{
|
{
|
||||||
private static readonly Int64 SessionId = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
private static readonly Int64 SessionId = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
private static List<TorrentClientTorrent> _cache = [];
|
private static List<TorrentClientTorrent> _cache = [];
|
||||||
|
|
@ -257,67 +256,10 @@ public class AllDebridTorrentClient(ILogger<AllDebridTorrentClient> logger, IAll
|
||||||
|
|
||||||
var files = GetFiles(allFiles);
|
var files = GetFiles(allFiles);
|
||||||
|
|
||||||
|
files = files.Where(f => fileFilter.IsDownloadable(torrent, f.Path, f.Bytes)).ToList();
|
||||||
|
|
||||||
Log($"Getting download links", torrent);
|
Log($"Getting download links", torrent);
|
||||||
|
|
||||||
if (torrent.DownloadMinSize > 0)
|
|
||||||
{
|
|
||||||
var minFileSize = torrent.DownloadMinSize * 1024 * 1024;
|
|
||||||
|
|
||||||
Log($"Determining which files are over {minFileSize} bytes", torrent);
|
|
||||||
|
|
||||||
files = files.Where(m => m.Bytes > minFileSize)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
Log($"Found {files.Count} files that match the minimum file size criterea", torrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!String.IsNullOrWhiteSpace(torrent.IncludeRegex))
|
|
||||||
{
|
|
||||||
Log($"Using regular expression {torrent.IncludeRegex} to include only files matching this regex", torrent);
|
|
||||||
|
|
||||||
var newFiles = new List<TorrentClientFile>();
|
|
||||||
|
|
||||||
foreach (var file in files)
|
|
||||||
{
|
|
||||||
if (Regex.IsMatch(file.Path, torrent.IncludeRegex))
|
|
||||||
{
|
|
||||||
Log($"* Including {file.Path}", torrent);
|
|
||||||
newFiles.Add(file);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log($"* Excluding {file.Path}", torrent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
files = newFiles;
|
|
||||||
|
|
||||||
Log($"Found {newFiles.Count} files that match the regex", torrent);
|
|
||||||
}
|
|
||||||
else if (!String.IsNullOrWhiteSpace(torrent.ExcludeRegex))
|
|
||||||
{
|
|
||||||
Log($"Using regular expression {torrent.IncludeRegex} to ignore files matching this regex", torrent);
|
|
||||||
|
|
||||||
var newLinks = new List<TorrentClientFile>();
|
|
||||||
|
|
||||||
foreach (var link in files)
|
|
||||||
{
|
|
||||||
if (!Regex.IsMatch(link.Path, torrent.ExcludeRegex))
|
|
||||||
{
|
|
||||||
Log($"* Including {link.Path}", torrent);
|
|
||||||
newLinks.Add(link);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log($"* Excluding {link.Path}", torrent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
files = newLinks;
|
|
||||||
|
|
||||||
Log($"Found {newLinks.Count} files that match the regex", torrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.Count == 0)
|
if (files.Count == 0)
|
||||||
{
|
{
|
||||||
Log($"Filtered all files out! Downloading ALL files instead!", torrent);
|
Log($"Filtered all files out! Downloading ALL files instead!", torrent);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ using Download = RdtClient.Data.Models.Data.Download;
|
||||||
|
|
||||||
namespace RdtClient.Service.Services.TorrentClients;
|
namespace RdtClient.Service.Services.TorrentClients;
|
||||||
|
|
||||||
public class DebridLinkClient(ILogger<DebridLinkClient> logger, IHttpClientFactory httpClientFactory) : ITorrentClient
|
public class DebridLinkClient(ILogger<DebridLinkClient> logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter) : ITorrentClient
|
||||||
{
|
{
|
||||||
private DebridLinkFrNETClient GetClient()
|
private DebridLinkFrNETClient GetClient()
|
||||||
{
|
{
|
||||||
|
|
@ -69,12 +69,14 @@ public class DebridLinkClient(ILogger<DebridLinkClient> logger, IHttpClientFacto
|
||||||
Status = torrent.Status.ToString(),
|
Status = torrent.Status.ToString(),
|
||||||
Added = DateTimeOffset.FromUnixTimeSeconds(torrent.Created),
|
Added = DateTimeOffset.FromUnixTimeSeconds(torrent.Created),
|
||||||
Files = (torrent.Files ?? []).Select((m, i) => new TorrentClientFile
|
Files = (torrent.Files ?? []).Select((m, i) => new TorrentClientFile
|
||||||
{
|
{
|
||||||
Path = m.Name ?? "",
|
Path = m.Name ?? "",
|
||||||
Bytes = m.Size,
|
Bytes = m.Size,
|
||||||
Id = i,
|
Id = i,
|
||||||
Selected = true
|
Selected = true,
|
||||||
}).ToList(),
|
DownloadLink = m.DownloadUrl
|
||||||
|
})
|
||||||
|
.ToList(),
|
||||||
Links = torrent.Files?.Select(m => m.DownloadUrl.ToString()).ToList(),
|
Links = torrent.Files?.Select(m => m.DownloadUrl.ToString()).ToList(),
|
||||||
Ended = null,
|
Ended = null,
|
||||||
Speed = torrent.UploadSpeed,
|
Speed = torrent.UploadSpeed,
|
||||||
|
|
@ -246,7 +248,10 @@ public class DebridLinkClient(ILogger<DebridLinkClient> logger, IHttpClientFacto
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var downloadLinks = rdTorrent.Links.Where(m => !String.IsNullOrWhiteSpace(m)).ToList();
|
var downloadLinks = rdTorrent.Files?
|
||||||
|
.Where(m => fileFilter.IsDownloadable(torrent, m.Path, m.Bytes) && m.DownloadLink != null)
|
||||||
|
.Select(m => m.DownloadLink!)
|
||||||
|
.ToList() ?? [];
|
||||||
|
|
||||||
Log($"Found {downloadLinks.Count} links", torrent);
|
Log($"Found {downloadLinks.Count} links", torrent);
|
||||||
|
|
||||||
|
|
@ -255,25 +260,7 @@ public class DebridLinkClient(ILogger<DebridLinkClient> logger, IHttpClientFacto
|
||||||
Log($"{link}", torrent);
|
Log($"{link}", torrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if all the links are set that have been selected
|
return downloadLinks;
|
||||||
if (torrent.Files.Count(m => m.Selected) == downloadLinks.Count)
|
|
||||||
{
|
|
||||||
return downloadLinks;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if all all the links are set for manual selection
|
|
||||||
if (torrent.ManualFiles.Count == downloadLinks.Count)
|
|
||||||
{
|
|
||||||
return downloadLinks;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is only 1 link, delay for 1 minute to see if more links pop up.
|
|
||||||
if (downloadLinks.Count == 1 && torrent.RdEnded.HasValue && DateTime.UtcNow > torrent.RdEnded.Value.ToUniversalTime().AddMinutes(1))
|
|
||||||
{
|
|
||||||
return downloadLinks;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<TorrentClientTorrent> GetInfo(String torrentId)
|
private async Task<TorrentClientTorrent> GetInfo(String torrentId)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Text.RegularExpressions;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using PremiumizeNET;
|
using PremiumizeNET;
|
||||||
using RdtClient.Data.Enums;
|
using RdtClient.Data.Enums;
|
||||||
|
|
@ -10,7 +9,7 @@ using Torrent = RdtClient.Data.Models.Data.Torrent;
|
||||||
|
|
||||||
namespace RdtClient.Service.Services.TorrentClients;
|
namespace RdtClient.Service.Services.TorrentClients;
|
||||||
|
|
||||||
public class PremiumizeTorrentClient(ILogger<PremiumizeTorrentClient> logger, IHttpClientFactory httpClientFactory) : ITorrentClient
|
public class PremiumizeTorrentClient(ILogger<PremiumizeTorrentClient> logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter) : ITorrentClient
|
||||||
{
|
{
|
||||||
private PremiumizeNETClient GetClient()
|
private PremiumizeNETClient GetClient()
|
||||||
{
|
{
|
||||||
|
|
@ -294,33 +293,9 @@ public class PremiumizeTorrentClient(ILogger<PremiumizeTorrentClient> logger, IH
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (torrent.DownloadMinSize > 0)
|
if (!fileFilter.IsDownloadable(torrent, item.Name, item.Size))
|
||||||
{
|
{
|
||||||
if (item.Link.Length < torrent.DownloadMinSize * 1024 * 1024)
|
continue;
|
||||||
{
|
|
||||||
Log($"Not downloading {item.Name}, its size of {item.Link.Length} bytes is smaller than the minimum size of {torrent.DownloadMinSize} bytes", torrent);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!String.IsNullOrWhiteSpace(torrent.IncludeRegex))
|
|
||||||
{
|
|
||||||
if (!Regex.IsMatch(item.Name, torrent.IncludeRegex))
|
|
||||||
{
|
|
||||||
Log($"Not downloading {item.Name}, it does not match regex {torrent.IncludeRegex}", torrent);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!String.IsNullOrWhiteSpace(torrent.ExcludeRegex))
|
|
||||||
{
|
|
||||||
if (Regex.IsMatch(item.Name, torrent.ExcludeRegex))
|
|
||||||
{
|
|
||||||
Log($"Not downloading {item.Name}, it matches regex {torrent.ExcludeRegex}", torrent);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Log($"Found item {item.Name} in folder {folder.Name} ({folderId})", torrent);
|
Log($"Found item {item.Name} in folder {folder.Name} ({folderId})", torrent);
|
||||||
|
|
@ -329,6 +304,12 @@ public class PremiumizeTorrentClient(ILogger<PremiumizeTorrentClient> logger, IH
|
||||||
}
|
}
|
||||||
else if (item.Type == "folder")
|
else if (item.Type == "folder")
|
||||||
{
|
{
|
||||||
|
// Folders don't have Size, use maximum Int64 so it always passes min size check
|
||||||
|
if (!fileFilter.IsDownloadable(torrent, item.Name, Int64.MaxValue))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Log($"Found subfolder {item.Name} in {folder.Name} ({folderId}), searching subfolder for files", torrent);
|
Log($"Found subfolder {item.Name} in {folder.Name} ({folderId}), searching subfolder for files", torrent);
|
||||||
var subDownloadLinks = await GetAllDownloadLinks(torrent, item.Id);
|
var subDownloadLinks = await GetAllDownloadLinks(torrent, item.Id);
|
||||||
downloadLinks.AddRange(subDownloadLinks);
|
downloadLinks.AddRange(subDownloadLinks);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Text.RegularExpressions;
|
using System.Web;
|
||||||
using System.Web;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using RDNET;
|
using RDNET;
|
||||||
|
|
@ -9,7 +8,7 @@ using RdtClient.Service.Helpers;
|
||||||
|
|
||||||
namespace RdtClient.Service.Services.TorrentClients;
|
namespace RdtClient.Service.Services.TorrentClients;
|
||||||
|
|
||||||
public class RealDebridTorrentClient(ILogger<RealDebridTorrentClient> logger, IHttpClientFactory httpClientFactory) : ITorrentClient
|
public class RealDebridTorrentClient(ILogger<RealDebridTorrentClient> logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter) : ITorrentClient
|
||||||
{
|
{
|
||||||
private TimeSpan? _offset;
|
private TimeSpan? _offset;
|
||||||
|
|
||||||
|
|
@ -162,62 +161,7 @@ public class RealDebridTorrentClient(ILogger<RealDebridTorrentClient> logger, IH
|
||||||
|
|
||||||
Log($"Selecting {files.Count}/{torrent.Files.Count} files", torrent);
|
Log($"Selecting {files.Count}/{torrent.Files.Count} files", torrent);
|
||||||
|
|
||||||
if (torrent.DownloadAction != TorrentDownloadAction.DownloadManual && torrent.DownloadMinSize > 0)
|
files = files.Where(f => fileFilter.IsDownloadable(torrent, f.Path, f.Bytes)).ToList();
|
||||||
{
|
|
||||||
var minFileSize = torrent.DownloadMinSize * 1024 * 1024;
|
|
||||||
|
|
||||||
Log($"Determining which files are over {minFileSize} bytes", torrent);
|
|
||||||
|
|
||||||
files = files.Where(m => m.Bytes > minFileSize)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
Log($"Found {files.Count} files that match the minimum file size criterea", torrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!String.IsNullOrWhiteSpace(torrent.IncludeRegex))
|
|
||||||
{
|
|
||||||
Log($"Using regular expression {torrent.IncludeRegex} to include only files matching this regex", torrent);
|
|
||||||
|
|
||||||
var newFiles = new List<TorrentClientFile>();
|
|
||||||
foreach (var file in files)
|
|
||||||
{
|
|
||||||
if (Regex.IsMatch(file.Path, torrent.IncludeRegex))
|
|
||||||
{
|
|
||||||
Log($"* Including {file.Path}", torrent);
|
|
||||||
newFiles.Add(file);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log($"* Excluding {file.Path}", torrent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
files = newFiles;
|
|
||||||
|
|
||||||
Log($"Found {files.Count} files that match the regex", torrent);
|
|
||||||
}
|
|
||||||
else if (!String.IsNullOrWhiteSpace(torrent.ExcludeRegex))
|
|
||||||
{
|
|
||||||
Log($"Using regular expression {torrent.IncludeRegex} to ignore files matching this regex", torrent);
|
|
||||||
|
|
||||||
var newFiles = new List<TorrentClientFile>();
|
|
||||||
foreach (var file in files)
|
|
||||||
{
|
|
||||||
if (!Regex.IsMatch(file.Path, torrent.ExcludeRegex))
|
|
||||||
{
|
|
||||||
Log($"* Including {file.Path}", torrent);
|
|
||||||
newFiles.Add(file);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log($"* Excluding {file.Path}", torrent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
files = newFiles;
|
|
||||||
|
|
||||||
Log($"Found {files.Count} files that match the regex", torrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.Count == 0)
|
if (files.Count == 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using TorBoxNET;
|
using TorBoxNET;
|
||||||
|
|
@ -10,7 +9,7 @@ using RdtClient.Service.Helpers;
|
||||||
|
|
||||||
namespace RdtClient.Service.Services.TorrentClients;
|
namespace RdtClient.Service.Services.TorrentClients;
|
||||||
|
|
||||||
public class TorBoxTorrentClient(ILogger<TorBoxTorrentClient> logger, IHttpClientFactory httpClientFactory) : ITorrentClient
|
public class TorBoxTorrentClient(ILogger<TorBoxTorrentClient> logger, IHttpClientFactory httpClientFactory, IDownloadableFileFilter fileFilter) : ITorrentClient
|
||||||
{
|
{
|
||||||
private TimeSpan? _offset;
|
private TimeSpan? _offset;
|
||||||
private TorBoxNetClient GetClient()
|
private TorBoxNetClient GetClient()
|
||||||
|
|
@ -288,46 +287,9 @@ public class TorBoxTorrentClient(ILogger<TorBoxTorrentClient> logger, IHttpClien
|
||||||
{
|
{
|
||||||
var torrentId = await GetClient().Torrents.GetHashInfoAsync(torrent.Hash, skipCache: true);
|
var torrentId = await GetClient().Torrents.GetHashInfoAsync(torrent.Hash, skipCache: true);
|
||||||
|
|
||||||
var selectedFiles = torrent.Files.Where(file =>
|
return torrent.Files.Where(file => fileFilter.IsDownloadable(torrent, file.Path, file.Bytes))
|
||||||
{
|
.Select(file => $"https://torbox.app/fakedl/{torrentId?.Id}/{file.Id}")
|
||||||
var fileName = Path.GetFileName(file.Path);
|
.ToList();
|
||||||
|
|
||||||
if (torrent.DownloadMinSize > 0 && file.Bytes < torrent.DownloadMinSize * 1024 * 1024)
|
|
||||||
{
|
|
||||||
Log($"Not downloading {fileName}, its size of {file.Bytes} bytes is smaller than the minimum size of {torrent.DownloadMinSize} bytes", torrent);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!String.IsNullOrWhiteSpace(torrent.IncludeRegex))
|
|
||||||
{
|
|
||||||
if (!Regex.IsMatch(fileName, torrent.IncludeRegex))
|
|
||||||
{
|
|
||||||
Log($"Not downloading {fileName}, it does not match regex {torrent.IncludeRegex}", torrent);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If IncludeRegex is set, don't look at ExcludeRegex
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!String.IsNullOrWhiteSpace(torrent.ExcludeRegex))
|
|
||||||
{
|
|
||||||
if (Regex.IsMatch(fileName, torrent.ExcludeRegex))
|
|
||||||
|
|
||||||
{
|
|
||||||
Log($"Not downloading {fileName}, it matches regex {torrent.ExcludeRegex}", torrent);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return selectedFiles.Select(file => $"https://torbox.app/fakedl/{torrentId?.Id}/{file.Id}")
|
|
||||||
.ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<String> GetFileName(String downloadUrl)
|
public async Task<String> GetFileName(String downloadUrl)
|
||||||
|
|
|
||||||
|
|
@ -242,6 +242,20 @@ public class Torrents(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (downloadLinks.Count == 0)
|
||||||
|
{
|
||||||
|
logger.LogInformation("All files excluded by filters (IncludeRegex: {includeRegex}, ExcludeRegex: {excludeRegex}, DownloadMinSize: {downloadMinSize} {torrentInfo}",
|
||||||
|
torrent.IncludeRegex,
|
||||||
|
torrent.ExcludeRegex,
|
||||||
|
torrent.DownloadMinSize,
|
||||||
|
torrent.ToLog());
|
||||||
|
|
||||||
|
await torrentData.UpdateRetry(torrentId, null, torrent.TorrentRetryAttempts);
|
||||||
|
await torrentData.UpdateComplete(torrentId, "All files excluded", DateTimeOffset.Now, false);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var downloadLink in downloadLinks)
|
foreach (var downloadLink in downloadLinks)
|
||||||
{
|
{
|
||||||
// Make sure downloads don't get added multiple times
|
// Make sure downloads don't get added multiple times
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue