diff --git a/src/NzbDrone.Core/Datastore/Migration/038_indexers_freeleech_only_config_contract.cs b/src/NzbDrone.Core/Datastore/Migration/038_indexers_freeleech_only_config_contract.cs new file mode 100644 index 000000000..7832dbf71 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/038_indexers_freeleech_only_config_contract.cs @@ -0,0 +1,16 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(038)] + public class indexers_freeleech_only_config_contract : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Update.Table("Indexers").Set(new { ConfigContract = "HDSpaceSettings" }).Where(new { Implementation = "HDSpace" }); + Update.Table("Indexers").Set(new { ConfigContract = "ImmortalSeedSettings" }).Where(new { Implementation = "ImmortalSeed" }); + Update.Table("Indexers").Set(new { ConfigContract = "XSpeedsSettings" }).Where(new { Implementation = "XSpeeds" }); + } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDSpace.cs b/src/NzbDrone.Core/Indexers/Definitions/HDSpace.cs index e2ec566e1..486f69448 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDSpace.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDSpace.cs @@ -9,6 +9,7 @@ using AngleSharp.Html.Parser; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; +using NzbDrone.Core.Annotations; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.Indexers.Settings; @@ -19,7 +20,7 @@ using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.Definitions { - public class HDSpace : TorrentIndexerBase + public class HDSpace : TorrentIndexerBase { public override string Name => "HD-Space"; public override string[] IndexerUrls => new[] { "https://hd-space.org/" }; @@ -225,10 +226,10 @@ namespace NzbDrone.Core.Indexers.Definitions public class HDSpaceParser : IParseIndexerResponse { - private readonly UserPassTorrentBaseSettings _settings; + private readonly HDSpaceSettings _settings; private readonly IndexerCapabilitiesCategories _categories; - public HDSpaceParser(UserPassTorrentBaseSettings settings, IndexerCapabilitiesCategories categories) + public HDSpaceParser(HDSpaceSettings settings, IndexerCapabilitiesCategories categories) { _settings = settings; _categories = categories; @@ -251,9 +252,34 @@ namespace NzbDrone.Core.Indexers.Definitions continue; } - var release = new TorrentInfo(); - release.MinimumRatio = 1; - release.MinimumSeedTime = 86400; // 24 hours + var downloadVolumeFactor = 1.0; + + if (row.QuerySelector("img[title=\"FreeLeech\"]") != null) + { + downloadVolumeFactor = 0; + } + else if (row.QuerySelector("img[src=\"images/sf.png\"]") != null) + { + downloadVolumeFactor = 0; + } + else if (row.QuerySelector("img[title=\"Half FreeLeech\"]") != null) + { + downloadVolumeFactor = 0.5; + } + + // Skip non-freeleech results when freeleech only is set + if (_settings.FreeleechOnly && downloadVolumeFactor != 0.0) + { + continue; + } + + var release = new TorrentInfo + { + DownloadVolumeFactor = downloadVolumeFactor, + UploadVolumeFactor = 1, + MinimumRatio = 1, + MinimumSeedTime = 86400 // 24 hours + }; var qLink = row.QuerySelector("td:nth-child(2) a[href^=\"index.php?page=torrent-details&id=\"]"); release.Title = qLink?.TextContent.Trim(); @@ -292,25 +318,6 @@ namespace NzbDrone.Core.Indexers.Definitions var grabs = row.QuerySelector("td:nth-child(10)")?.TextContent.Trim().Replace("---", "0"); release.Grabs = ParseUtil.CoerceInt(grabs); - if (row.QuerySelector("img[title=\"FreeLeech\"]") != null) - { - release.DownloadVolumeFactor = 0; - } - else if (row.QuerySelector("img[src=\"images/sf.png\"]") != null) - { - release.DownloadVolumeFactor = 0; - } - else if (row.QuerySelector("img[title=\"Half FreeLeech\"]") != null) - { - release.DownloadVolumeFactor = 0.5; - } - else - { - release.DownloadVolumeFactor = 1; - } - - release.UploadVolumeFactor = 1; - var categoryLink = row.QuerySelector("a[href^=\"index.php?page=torrents&category=\"]").GetAttribute("href"); var cat = ParseUtil.GetArgumentFromQueryString(categoryLink, "category"); release.Categories = _categories.MapTrackerCatToNewznab(cat); @@ -323,4 +330,10 @@ namespace NzbDrone.Core.Indexers.Definitions public Action, DateTime?> CookiesUpdater { get; set; } } + + public class HDSpaceSettings : UserPassTorrentBaseSettings + { + [FieldDefinition(4, Label = "Freeleech Only", Type = FieldType.Checkbox, HelpText = "Show freeleech releases only")] + public bool FreeleechOnly { get; set; } = false; + } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/ImmortalSeed.cs b/src/NzbDrone.Core/Indexers/Definitions/ImmortalSeed.cs index 7803fff78..d904c4d05 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/ImmortalSeed.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/ImmortalSeed.cs @@ -10,6 +10,7 @@ using AngleSharp.Html.Parser; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; +using NzbDrone.Core.Annotations; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.Indexers.Settings; @@ -20,7 +21,7 @@ using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.Definitions { - public class ImmortalSeed : TorrentIndexerBase + public class ImmortalSeed : TorrentIndexerBase { public override string Name => "ImmortalSeed"; public override string[] IndexerUrls => new[] { "https://immortalseed.me/" }; @@ -46,7 +47,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IParseIndexerResponse GetParser() { - return new ImmortalSeedParser(Capabilities.Categories); + return new ImmortalSeedParser(Settings, Capabilities.Categories); } protected override async Task DoLogin() @@ -253,11 +254,14 @@ namespace NzbDrone.Core.Indexers.Definitions public class ImmortalSeedParser : IParseIndexerResponse { + private readonly ImmortalSeedSettings _settings; private readonly IndexerCapabilitiesCategories _categories; + private readonly Regex _dateAddedRegex = new (@"\d{4}-\d{2}-\d{2} \d{2}:\d{2} [AaPp][Mm]", RegexOptions.Compiled); - public ImmortalSeedParser(IndexerCapabilitiesCategories categories) + public ImmortalSeedParser(ImmortalSeedSettings settings, IndexerCapabilitiesCategories categories) { + _settings = settings; _categories = categories; } @@ -271,6 +275,23 @@ namespace NzbDrone.Core.Indexers.Definitions var rows = dom.QuerySelectorAll("table#sortabletable > tbody > tr:has(a[href*=\"details.php?id=\"])"); foreach (var row in rows) { + var downloadVolumeFactor = 1.0; + + if (row.QuerySelector("img[title^=\"Free Torrent\"], img[title^=\"Sitewide Free Torrent\"]") != null) + { + downloadVolumeFactor = 0.0; + } + else if (row.QuerySelector("img[title^=\"Silver Torrent\"]") != null) + { + downloadVolumeFactor = 0.5; + } + + // Skip non-freeleech results when freeleech only is set + if (_settings.FreeleechOnly && downloadVolumeFactor != 0.0) + { + continue; + } + // details link, release name gets shortened if it's to long var qDetails = row.QuerySelector("div > a[href*=\"details.php?id=\"]"); @@ -300,6 +321,7 @@ namespace NzbDrone.Core.Indexers.Definitions Peers = peers, Size = ParseUtil.GetBytes(row.QuerySelector("td:nth-of-type(5)")?.TextContent.Trim()), Grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(6)")?.TextContent), + DownloadVolumeFactor = downloadVolumeFactor, UploadVolumeFactor = row.QuerySelector("img[title^=\"x2 Torrent\"]") != null ? 2 : 1, MinimumRatio = 1, MinimumSeedTime = 86400 // 24 hours @@ -311,19 +333,6 @@ namespace NzbDrone.Core.Indexers.Definitions release.PublishDate = DateTime.ParseExact(dateAddedMatch.Value, "yyyy-MM-dd hh:mm tt", CultureInfo.InvariantCulture); } - if (row.QuerySelector("img[title^=\"Free Torrent\"], img[title^=\"Sitewide Free Torrent\"]") != null) - { - release.DownloadVolumeFactor = 0; - } - else if (row.QuerySelector("img[title^=\"Silver Torrent\"]") != null) - { - release.DownloadVolumeFactor = 0.5; - } - else - { - release.DownloadVolumeFactor = 1; - } - releaseInfos.Add(release); } @@ -332,4 +341,10 @@ namespace NzbDrone.Core.Indexers.Definitions public Action, DateTime?> CookiesUpdater { get; set; } } + + public class ImmortalSeedSettings : UserPassTorrentBaseSettings + { + [FieldDefinition(4, Label = "Freeleech Only", Type = FieldType.Checkbox, HelpText = "Show freeleech releases only")] + public bool FreeleechOnly { get; set; } = false; + } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/XSpeeds.cs b/src/NzbDrone.Core/Indexers/Definitions/XSpeeds.cs index fabfd9533..99efc2ecc 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/XSpeeds.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/XSpeeds.cs @@ -9,6 +9,7 @@ using AngleSharp.Html.Parser; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; +using NzbDrone.Core.Annotations; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.Indexers.Settings; @@ -19,7 +20,7 @@ using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.Definitions; -public class XSpeeds : TorrentIndexerBase +public class XSpeeds : TorrentIndexerBase { public override string Name => "XSpeeds"; public override string[] IndexerUrls => new[] { "https://www.xspeeds.eu/" }; @@ -45,7 +46,7 @@ public class XSpeeds : TorrentIndexerBase public override IParseIndexerResponse GetParser() { - return new XSpeedsParser(Capabilities.Categories); + return new XSpeedsParser(Settings, Capabilities.Categories); } protected override async Task DoLogin() @@ -276,11 +277,14 @@ public class XSpeedsRequestGenerator : IIndexerRequestGenerator public class XSpeedsParser : IParseIndexerResponse { + private readonly XSpeedsSettings _settings; private readonly IndexerCapabilitiesCategories _categories; + private readonly Regex _dateAddedRegex = new (@"\d{2}-\d{2}-\d{4} \d{2}:\d{2}", RegexOptions.Compiled); - public XSpeedsParser(IndexerCapabilitiesCategories categories) + public XSpeedsParser(XSpeedsSettings settings, IndexerCapabilitiesCategories categories) { + _settings = settings; _categories = categories; } @@ -294,6 +298,23 @@ public class XSpeedsParser : IParseIndexerResponse var rows = dom.QuerySelectorAll("table#sortabletable > tbody > tr:has(a[href*=\"details.php?id=\"])"); foreach (var row in rows) { + var downloadVolumeFactor = 1.0; + + if (row.QuerySelector("img[title^=\"Free Torrent\"], img[title^=\"Sitewide Free Torrent\"]") != null) + { + downloadVolumeFactor = 0.0; + } + else if (row.QuerySelector("img[title^=\"Silver Torrent\"]") != null) + { + downloadVolumeFactor = 0.5; + } + + // Skip non-freeleech results when freeleech only is set + if (_settings.FreeleechOnly && downloadVolumeFactor != 0.0) + { + continue; + } + var qDetails = row.QuerySelector("div > a[href*=\"details.php?id=\"]"); var title = qDetails?.TextContent.Trim(); @@ -320,6 +341,7 @@ public class XSpeedsParser : IParseIndexerResponse Peers = peers, Size = ParseUtil.GetBytes(row.QuerySelector("td:nth-of-type(5)")?.TextContent.Trim()), Grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(6)")?.TextContent), + DownloadVolumeFactor = downloadVolumeFactor, UploadVolumeFactor = row.QuerySelector("img[title^=\"x2 Torrent\"]") != null ? 2 : 1, MinimumRatio = 0.8 }; @@ -330,19 +352,6 @@ public class XSpeedsParser : IParseIndexerResponse release.PublishDate = DateTime.ParseExact(dateAddedMatch.Value, "dd-MM-yyyy HH:mm", CultureInfo.InvariantCulture); } - if (row.QuerySelector("img[title^=\"Free Torrent\"], img[title^=\"Sitewide Free Torrent\"]") != null) - { - release.DownloadVolumeFactor = 0; - } - else if (row.QuerySelector("img[title^=\"Silver Torrent\"]") != null) - { - release.DownloadVolumeFactor = 0.5; - } - else - { - release.DownloadVolumeFactor = 1; - } - releaseInfos.Add(release); } @@ -351,3 +360,9 @@ public class XSpeedsParser : IParseIndexerResponse public Action, DateTime?> CookiesUpdater { get; set; } } + +public class XSpeedsSettings : UserPassTorrentBaseSettings +{ + [FieldDefinition(4, Label = "Freeleech Only", Type = FieldType.Checkbox, HelpText = "Show freeleech releases only")] + public bool FreeleechOnly { get; set; } = false; +}