diff --git a/server/RdtClient.Data/Enums/MagnetTrackerList.cs b/server/RdtClient.Data/Enums/MagnetTrackerList.cs
deleted file mode 100644
index 5a0dab2..0000000
--- a/server/RdtClient.Data/Enums/MagnetTrackerList.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.ComponentModel;
-
-namespace RdtClient.Data.Enums;
-
-public enum MagnetTrackerEnrichment
-{
- [Description("None (do not modify magnet links)")]
- None,
-
- [Description("Best trackers")]
- trackers_best,
-
- [Description("All trackers")]
- trackers_all,
-
- [Description("All UDP trackers")]
- trackers_all_udp,
-
- [Description("All HTTP trackers")]
- trackers_all_http,
-
- [Description("All HTTPS trackers")]
- trackers_all_https,
-
- [Description("All WebSocket (WS) trackers")]
- trackers_all_ws,
-
- [Description("Best IP-only trackers")]
- trackers_best_ip,
-
- [Description("All I2P trackers")]
- trackers_all_i2p,
-
- [Description("All IP-only trackers")]
- trackers_all_ip
-}
diff --git a/server/RdtClient.Data/Models/Internal/DbSettings.cs b/server/RdtClient.Data/Models/Internal/DbSettings.cs
index 136b6b3..0d6e693 100644
--- a/server/RdtClient.Data/Models/Internal/DbSettings.cs
+++ b/server/RdtClient.Data/Models/Internal/DbSettings.cs
@@ -75,9 +75,9 @@ Supports the following parameters:
[Description("When a torrent file or magnet is added, create a copy in this directory.")]
public String? CopyAddedTorrents { get; set; } = null;
- [DisplayName("Magnet tracker enrichment")]
- [Description(@"Choose which tracker list to append to magnet links, see this README for more info.")]
- public MagnetTrackerEnrichment MagnetTrackerEnrichment { get; set; } = MagnetTrackerEnrichment.None;
+ [DisplayName("Magnet enrichment list")]
+ [Description("Optional. Specify the URL of a tracker list file to be appended to magnet links.")]
+ public String? MagnetTrackerEnrichment { get; set; } = null;
[DisplayName("Disable update notifications")]
[Description("Ignore update notifications. You will still be notified if the version you are running has a security vulnerability.")]
diff --git a/server/RdtClient.Service.Test/Services/TorrentsTest.cs b/server/RdtClient.Service.Test/Services/TorrentsTest.cs
index addd45d..90fe94d 100644
--- a/server/RdtClient.Service.Test/Services/TorrentsTest.cs
+++ b/server/RdtClient.Service.Test/Services/TorrentsTest.cs
@@ -20,11 +20,13 @@ class Mocks
public readonly Mock> TorrentsLoggerMock;
public readonly Mock DownloadsMock;
public readonly Mock TorrentDataMock;
+ public readonly Mock TrackerListGrabberMock;
public Mocks()
{
TorrentDataMock = new();
DownloadsMock = new();
+ TrackerListGrabberMock = new();
TorrentsLoggerMock = new();
@@ -58,7 +60,7 @@ public class TorrentsTest
}
];
- return new ()
+ return new()
{
{
torrent,
@@ -102,6 +104,7 @@ public class TorrentsTest
mocks.DownloadsMock.Object,
mocks.ProcessFactoryMock.Object,
fileSystemMock,
+ mocks.TrackerListGrabberMock.Object,
null!, // Torrent Clients are not used by `RunTorrentComplete`, this is fine
null!,
null!,
@@ -167,6 +170,7 @@ public class TorrentsTest
mocks.DownloadsMock.Object,
mocks.ProcessFactoryMock.Object,
fileSystemMock,
+ mocks.TrackerListGrabberMock.Object,
null!, // Torrent Clients are not used by `RunTorrentComplete`, this is fine
null!,
null!,
@@ -214,6 +218,7 @@ public class TorrentsTest
mocks.DownloadsMock.Object,
mocks.ProcessFactoryMock.Object,
fileSystemMock,
+ mocks.TrackerListGrabberMock.Object,
null!, // Torrent Clients are not used by `RunTorrentComplete`, this is fine
null!,
null!,
@@ -280,6 +285,7 @@ public class TorrentsTest
mocks.DownloadsMock.Object,
mocks.ProcessFactoryMock.Object,
fileSystemMock,
+ mocks.TrackerListGrabberMock.Object,
null!, // Torrent Clients are not used by `RunTorrentComplete`, this is fine
null!,
null!,
diff --git a/server/RdtClient.Service/DiConfig.cs b/server/RdtClient.Service/DiConfig.cs
index fd944e6..a62986d 100644
--- a/server/RdtClient.Service/DiConfig.cs
+++ b/server/RdtClient.Service/DiConfig.cs
@@ -38,6 +38,7 @@ public static class DiConfig
services.AddScoped();
services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton();
diff --git a/server/RdtClient.Service/Services/ITrackerListGrabber.cs b/server/RdtClient.Service/Services/ITrackerListGrabber.cs
new file mode 100644
index 0000000..d3ccc08
--- /dev/null
+++ b/server/RdtClient.Service/Services/ITrackerListGrabber.cs
@@ -0,0 +1,6 @@
+namespace RdtClient.Service.Services;
+
+public interface ITrackerListGrabber
+{
+ Task GetTrackers();
+}
\ No newline at end of file
diff --git a/server/RdtClient.Service/Services/Torrents.cs b/server/RdtClient.Service/Services/Torrents.cs
index ec18b0e..7fe79ad 100644
--- a/server/RdtClient.Service/Services/Torrents.cs
+++ b/server/RdtClient.Service/Services/Torrents.cs
@@ -25,6 +25,7 @@ public class Torrents(
IDownloads downloads,
IProcessFactory processFactory,
IFileSystem fileSystem,
+ ITrackerListGrabber trackerListGrabber,
AllDebridTorrentClient allDebridTorrentClient,
PremiumizeTorrentClient premiumizeTorrentClient,
RealDebridTorrentClient realDebridTorrentClient,
@@ -32,6 +33,7 @@ public class Torrents(
TorBoxTorrentClient torBoxTorrentClient)
{
private static readonly SemaphoreSlim RealDebridUpdateLock = new(1, 1);
+ private readonly ITrackerListGrabber _trackerListGrabber = trackerListGrabber;
private static readonly JsonSerializerOptions JsonSerializerOptions = new()
{
ReferenceHandler = ReferenceHandler.IgnoreCycles
@@ -107,45 +109,37 @@ public class Torrents(
await torrentData.UpdateCategory(torrent.TorrentId, category);
}
- public static async Task EnrichMagnetLink(string magnetLink)
+ private async Task EnrichMagnetLink(String magnetLink)
{
- var enrichment = Settings.Get.General.MagnetTrackerEnrichment;
- if (enrichment == MagnetTrackerEnrichment.None) { return magnetLink; }
+ if (string.IsNullOrWhiteSpace(Settings.Get.General.MagnetTrackerEnrichment)) return magnetLink;
- var url = $"https://github.com/ngosang/trackerslist/raw/refs/heads/master/{enrichment}.txt";
- var newTrackers = (await new HttpClient().GetStringAsync(url)).Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
-
- var originalUri = new Uri(magnetLink);
- var decodedParams = HttpUtility.ParseQueryString(originalUri.Query);
- var rawXt = decodedParams["xt"]!;
- decodedParams.Remove("xt");
-
- var existingTrackers = decodedParams.GetValues("tr") ?? Array.Empty();
- decodedParams.Remove("tr");
- var allTrackers = existingTrackers.Concat(newTrackers).Distinct(StringComparer.OrdinalIgnoreCase);
-
- var pieces = new List { $"xt={rawXt}" };
- var allKeys = decodedParams.AllKeys;
- if (allKeys != null)
+ try
{
- foreach (string? key in allKeys)
+ var newTrackers = await _trackerListGrabber.GetTrackers();
+
+ var uri = new Uri(magnetLink);
+ var query = HttpUtility.ParseQueryString(uri.Query);
+ var existingTrackers = query.GetValues("tr") ?? [];
+ var allTrackers = existingTrackers.Concat(newTrackers).Distinct(StringComparer.OrdinalIgnoreCase);
+
+ var trackerQuery = string.Join("&tr=", allTrackers.Select(Uri.EscapeDataString));
+ if (!string.IsNullOrEmpty(trackerQuery))
{
- if (key == null) continue;
- foreach (var val in decodedParams.GetValues(key)!)
- {
- pieces.Add($"{key}={HttpUtility.UrlEncode(val)}");
- }
+ trackerQuery = "&tr=" + trackerQuery;
}
+
+ var baseWithoutTrackers = magnetLink.Split(["&tr="], StringSplitOptions.None)[0];
+ var separator = baseWithoutTrackers.Contains('?') ? "&" : "?";
+ return baseWithoutTrackers + separator + trackerQuery.TrimStart('&');
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "{Message}, trying to enrich {Magnet}", ex.Message, magnetLink);
+ return magnetLink;
}
- foreach (var tr in allTrackers) { pieces.Add($"tr={HttpUtility.UrlEncode(tr)}"); }
-
- var basePart = originalUri.GetLeftPart(UriPartial.Path);
- var enriched = $"{basePart}?{string.Join("&", pieces)}";
-
- return enriched;
}
- public async Task AddMagnetToDebridQueue(string magnetLink, Torrent torrent)
+ public async Task AddMagnetToDebridQueue(String magnetLink, Torrent torrent)
{
var enriched = await EnrichMagnetLink(magnetLink);
MagnetLink magnet;
diff --git a/server/RdtClient.Service/Services/TrackerListGrabber.cs b/server/RdtClient.Service/Services/TrackerListGrabber.cs
new file mode 100644
index 0000000..5e034a9
--- /dev/null
+++ b/server/RdtClient.Service/Services/TrackerListGrabber.cs
@@ -0,0 +1,34 @@
+namespace RdtClient.Service.Services;
+
+public class TrackerListGrabber : ITrackerListGrabber
+{
+ private readonly IHttpClientFactory _httpClientFactory;
+
+ public TrackerListGrabber(IHttpClientFactory httpClientFactory)
+ {
+ _httpClientFactory = httpClientFactory;
+ }
+
+ public async Task GetTrackers()
+ {
+ var trackerUrlList = Settings.Get.General.MagnetTrackerEnrichment;
+ if (string.IsNullOrWhiteSpace(trackerUrlList))
+ {
+ return Array.Empty();
+ }
+
+ try
+ {
+ var httpClient = _httpClientFactory.CreateClient();
+ var result = await httpClient.GetStringAsync(trackerUrlList);
+ return result
+ .Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToArray();
+ }
+ catch
+ {
+ return Array.Empty();
+ }
+ }
+}
\ No newline at end of file