diff --git a/src/NzbDrone.Core.Test/IndexerTests/FileListTests/FileListRequestGeneratorFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/FileListTests/FileListRequestGeneratorFixture.cs index 1e31f4e48..19016cf57 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/FileListTests/FileListRequestGeneratorFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/FileListTests/FileListRequestGeneratorFixture.cs @@ -17,7 +17,6 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests { Subject.Settings = new FileListSettings() { - BaseUrl = "http://127.0.0.1:1234/", Passkey = "abcd", Username = "somename" }; diff --git a/src/NzbDrone.Core.Test/IndexerTests/HDBitsTests/HDBitsRequestGeneratorFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/HDBitsTests/HDBitsRequestGeneratorFixture.cs index ed8b2d486..db8aa7512 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/HDBitsTests/HDBitsRequestGeneratorFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/HDBitsTests/HDBitsRequestGeneratorFixture.cs @@ -18,7 +18,6 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests { Subject.Settings = new HDBitsSettings() { - BaseUrl = "http://127.0.0.1:1234/", ApiKey = "abcd", Username = "somename" }; diff --git a/src/NzbDrone.Core.Test/IndexerTests/IPTorrentsTests/IPTorrentsFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/IPTorrentsTests/IPTorrentsFixture.cs deleted file mode 100644 index 84a758eae..000000000 --- a/src/NzbDrone.Core.Test/IndexerTests/IPTorrentsTests/IPTorrentsFixture.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Common.Http; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Indexers.IPTorrents; -using NzbDrone.Core.IndexerSearch.Definitions; -using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Test.Framework; - -namespace NzbDrone.Core.Test.IndexerTests.IPTorrentsTests -{ - [TestFixture] - public class IPTorrentsFixture : CoreTest - { - [SetUp] - public void Setup() - { - Subject.Definition = new IndexerDefinition() - { - Name = "IPTorrents", - Settings = new IPTorrentsSettings() { BaseUrl = "http://fake.com/" } - }; - } - - private void GivenOldFeedFormat() - { - Subject.Definition = new IndexerDefinition() - { - Name = "IPTorrents", - Settings = new IPTorrentsSettings() { BaseUrl = "https://iptorrents.com/torrents/rss?u=snip;tp=snip;3;80;93;37;download" } - }; - } - - private void GivenNewFeedFormat() - { - Subject.Definition = new IndexerDefinition() - { - Name = "IPTorrents", - Settings = new IPTorrentsSettings() { BaseUrl = "https://iptorrents.com/t.rss?u=USERID;tp=APIKEY;3;80;93;37;download" } - }; - } - - private void GivenFeedNoDownloadFormat() - { - Subject.Definition = new IndexerDefinition() - { - Name = "IPTorrents", - Settings = new IPTorrentsSettings() { BaseUrl = "https://iptorrents.com/t.rss?u=USERID;tp=APIKEY;3;80;93;37" } - }; - } - - [Test] - public void should_validate_old_feed_format() - { - GivenOldFeedFormat(); - var validationResult = Subject.Definition.Settings.Validate(); - validationResult.IsValid.Should().BeTrue(); - } - - [Test] - public void should_validate_new_feed_format() - { - GivenNewFeedFormat(); - var validationResult = Subject.Definition.Settings.Validate(); - validationResult.IsValid.Should().BeTrue(); - } - - [Test] - public void should_not_validate_bad_format() - { - var validationResult = Subject.Definition.Settings.Validate(); - validationResult.IsValid.Should().BeFalse(); - } - - [Test] - public void should_not_validate_no_download_format() - { - GivenFeedNoDownloadFormat(); - var validationResult = Subject.Definition.Settings.Validate(); - validationResult.IsValid.Should().BeFalse(); - } - - [Test] - public void should_parse_recent_feed_from_IPTorrents() - { - var recentFeed = ReadAllText(@"Files/Indexers/IPTorrents/IPTorrents.xml"); - - Mocker.GetMock() - .Setup(o => o.Execute(It.Is(v => v.Method == HttpMethod.GET))) - .Returns(r => new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed)); - - var releases = Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } }).Releases; - - releases.Should().HaveCount(5); - releases.First().Should().BeOfType(); - - var torrentInfo = releases.First() as TorrentInfo; - - torrentInfo.Title.Should().Be("24 S03E12 720p WEBRip h264-DRAWER"); - torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); - torrentInfo.DownloadUrl.Should().Be("http://iptorrents.com/download.php/1234/24.S03E12.720p.WEBRip.h264-DRAWER.torrent?torrent_pass=abcd"); - torrentInfo.InfoUrl.Should().BeNullOrEmpty(); - torrentInfo.CommentUrl.Should().BeNullOrEmpty(); - torrentInfo.Indexer.Should().Be(Subject.Definition.Name); - torrentInfo.PublishDate.Should().Be(DateTime.Parse("2014/05/12 19:06:34")); - torrentInfo.Size.Should().Be(1471026299); - torrentInfo.InfoHash.Should().Be(null); - torrentInfo.MagnetUrl.Should().Be(null); - torrentInfo.Peers.Should().Be(null); - torrentInfo.Seeders.Should().Be(null); - } - } -} diff --git a/src/NzbDrone.Core.Test/IndexerTests/NyaaTests/NyaaFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/NyaaTests/NyaaFixture.cs deleted file mode 100644 index 9ef21a3ac..000000000 --- a/src/NzbDrone.Core.Test/IndexerTests/NyaaTests/NyaaFixture.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Common.Http; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Indexers.Nyaa; -using NzbDrone.Core.IndexerSearch.Definitions; -using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Test.Framework; - -namespace NzbDrone.Core.Test.IndexerTests.NyaaTests -{ - [TestFixture] - public class NyaaFixture : CoreTest - { - [SetUp] - public void Setup() - { - Subject.Definition = new IndexerDefinition() - { - Name = "Nyaa", - Settings = new NyaaSettings() - }; - } - - [Test] - public void should_parse_recent_feed_from_Nyaa() - { - var recentFeed = ReadAllText(@"Files/Indexers/Nyaa/Nyaa.xml"); - - Mocker.GetMock() - .Setup(o => o.Execute(It.Is(v => v.Method == HttpMethod.GET))) - .Returns(r => new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed)); - - var releases = Subject.Fetch(new MovieSearchCriteria()).Releases; - - releases.Should().HaveCount(4); - releases.First().Should().BeOfType(); - - var torrentInfo = releases.First() as TorrentInfo; - - torrentInfo.Title.Should().Be("[TSRaws] Futsuu no Joshikousei ga [Locodol] Yattemita. #07 (TBS).ts"); - torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); - torrentInfo.DownloadUrl.Should().Be("http://www.nyaa.si/?page=download&tid=587750"); - torrentInfo.InfoUrl.Should().Be("http://www.nyaa.si/?page=view&tid=587750"); - torrentInfo.CommentUrl.Should().BeNullOrEmpty(); - torrentInfo.Indexer.Should().Be(Subject.Definition.Name); - torrentInfo.PublishDate.Should().Be(DateTime.Parse("2014/08/14 18:10:36")); - torrentInfo.Size.Should().Be(2523293286); //2.35 GiB - torrentInfo.InfoHash.Should().Be(null); - torrentInfo.MagnetUrl.Should().Be(null); - torrentInfo.Peers.Should().Be(2 + 1); - torrentInfo.Seeders.Should().Be(1); - } - } -} diff --git a/src/NzbDrone.Core.Test/IndexerTests/RarbgTests/RarbgFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/RarbgTests/RarbgFixture.cs index 8348572a8..b9bea4352 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/RarbgTests/RarbgFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/RarbgTests/RarbgFixture.cs @@ -27,7 +27,7 @@ namespace NzbDrone.Core.Test.IndexerTests.RarbgTests }; Mocker.GetMock() - .Setup(v => v.GetToken(It.IsAny())) + .Setup(v => v.GetToken(It.IsAny(), It.IsAny())) .Returns("validtoken"); } diff --git a/src/NzbDrone.Core.Test/IndexerTests/TestIndexer.cs b/src/NzbDrone.Core.Test/IndexerTests/TestIndexer.cs index 4654a6f6d..6246380bc 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/TestIndexer.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/TestIndexer.cs @@ -8,6 +8,7 @@ namespace NzbDrone.Core.Test.IndexerTests public class TestIndexer : HttpIndexerBase { public override string Name => "Test Indexer"; + public override string BaseUrl => "http://testindexer.com"; public override DownloadProtocol Protocol => DownloadProtocol.Usenet; diff --git a/src/NzbDrone.Core.Test/IndexerTests/TestIndexerSettings.cs b/src/NzbDrone.Core.Test/IndexerTests/TestIndexerSettings.cs index 948867108..53bf192b8 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/TestIndexerSettings.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/TestIndexerSettings.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; using NzbDrone.Core.Indexers; +using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Test.IndexerTests { - public class TestIndexerSettings : IIndexerSettings + public class TestIndexerSettings : IProviderConfig { public NzbDroneValidationResult Validate() { @@ -13,7 +14,5 @@ namespace NzbDrone.Core.Test.IndexerTests } public string BaseUrl { get; set; } - - public IEnumerable MultiLanguages { get; set; } } } diff --git a/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TestTorrentRssIndexer.cs b/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TestTorrentRssIndexer.cs deleted file mode 100644 index 19d08ccd6..000000000 --- a/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TestTorrentRssIndexer.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Collections.Generic; -using FluentValidation.Results; -using NLog; -using NLog.Config; -using NLog.Targets; -using NzbDrone.Common.Http; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Indexers.TorrentRss; -using NzbDrone.Core.Parser; - -namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests -{ - public class TestTorrentRssIndexer : TorrentRssIndexer - { - public TestTorrentRssIndexer(ITorrentRssParserFactory torrentRssParserFactory, IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) - : base(torrentRssParserFactory, httpClient, indexerStatusService, configService, logger) - { - } - - public List TestPublic() - { - var result = new List(); - SetupNLog(); // Enable this to enable trace logging with nlog for debugging purposes - Test(result); - return result; - } - - /// - /// Code to quickly debug unit tests - /// - private void SetupNLog() - { - // Step 1. Create configuration object - var config = new LoggingConfiguration(); - - var fileTarget = new FileTarget(); - config.AddTarget("file", fileTarget); - - // Step 3. Set target properties - fileTarget.FileName = "${basedir}/log.txt"; - fileTarget.Layout = GetStandardLayout(); - - // Step 4. Define rules - var rule1 = new LoggingRule("*", LogLevel.Trace, fileTarget); - config.LoggingRules.Add(rule1); - - // Step 5. Activate the configuration - LogManager.Configuration = config; - } - - private static string GetStandardLayout() - { - return @"${date:universalTime=true:format=u}|" + "${processid:fixedLength=True:padding=4:padCharacter= }|" - + "${threadid:fixedLength=True:padding=3:padCharacter= }|" + "${level:fixedLength=True:padding=5:padCharacter= :upperCase=True}|" - + "${callsite:fileName=True:className=False:methodName=True:includeSourcePath=False:padding=50:padCharacter= }|" + "${message}" - + " ${exception:maxInnerExceptionLevel=3:format=Method,Message,StackTrace:innerFormat=Method,Message,StackTrace:separator=\r\n:innerExceptionSeparator=\r\n}"; - } - } -} diff --git a/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TorrentRssIndexerFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TorrentRssIndexerFixture.cs deleted file mode 100644 index c237a2898..000000000 --- a/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TorrentRssIndexerFixture.cs +++ /dev/null @@ -1,304 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Common.Http; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Indexers.TorrentRss; -using NzbDrone.Core.IndexerSearch.Definitions; -using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Test.Common; - -namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests -{ - [TestFixture] - public class TorrentRssIndexerFixture : CoreTest - { - private const string _indexerUrl = "http://my.indexer.tv/recent"; - - [SetUp] - public void Setup() - { - Mocker.SetConstant(Mocker.Resolve()); - Mocker.SetConstant(Mocker.Resolve()); - - Subject.Definition = new IndexerDefinition() - { - Name = "TorrentRssIndexer", - Settings = new TorrentRssIndexerSettings() { BaseUrl = _indexerUrl }, - }; - } - - private void GivenRecentFeedResponse(string rssXmlFile) - { - var recentFeed = ReadAllText(@"Files/Indexers/" + rssXmlFile); - - Mocker.GetMock() - .Setup(o => o.Execute(It.IsAny())) - .Returns(r => new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed)); - } - - [Test] - public void should_parse_recent_feed_from_ImmortalSeed() - { - GivenRecentFeedResponse("TorrentRss/ImmortalSeed.xml"); - - var releases = Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } }).Releases; - - releases.Should().HaveCount(50); - releases.First().Should().BeOfType(); - - var torrentInfo = (TorrentInfo)releases.First(); - - torrentInfo.Title.Should().Be("Conan.2015.02.05.Jeff.Bridges.720p.HDTV.X264-CROOKS"); - torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); - torrentInfo.DownloadUrl.Should().Be("https://immortalseed.me/download.php?type=rss&secret_key=12345678910&id=374534"); - torrentInfo.InfoUrl.Should().BeNullOrEmpty(); - torrentInfo.CommentUrl.Should().BeNullOrEmpty(); - torrentInfo.Indexer.Should().Be(Subject.Definition.Name); - torrentInfo.PublishDate.Should().Be(DateTime.Parse("2015-02-06 13:32:26")); - torrentInfo.Size.Should().Be(984078090); - torrentInfo.InfoHash.Should().BeNullOrEmpty(); - torrentInfo.MagnetUrl.Should().BeNullOrEmpty(); - torrentInfo.Peers.Should().Be(8); - torrentInfo.Seeders.Should().Be(6); - } - - [Test] - public void should_parse_recent_feed_from_Ezrss() - { - GivenRecentFeedResponse("TorrentRss/Ezrss.xml"); - - var releases = Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } }).Releases; - - releases.Should().HaveCount(3); - releases.First().Should().BeOfType(); - - var torrentInfo = releases.First() as TorrentInfo; - - torrentInfo.Title.Should().Be("S4C I Grombil Cyfandir Pell American Interior [PDTV - MVGROUP]"); - torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); - torrentInfo.DownloadUrl.Should().Be("http://re.zoink.it/20a4ed4eFC"); - torrentInfo.InfoUrl.Should().Be("http://eztv.it/ep/58439/s4c-i-grombil-cyfandir-pell-american-interior-pdtv-x264-mvgroup/"); - torrentInfo.CommentUrl.Should().Be("http://eztv.it/forum/discuss/58439/"); - torrentInfo.Indexer.Should().Be(Subject.Definition.Name); - torrentInfo.PublishDate.Should().Be(DateTime.Parse("2014/09/15 18:39:00")); - torrentInfo.Size.Should().Be(796606175); - torrentInfo.InfoHash.Should().Be("20FC4FBFA88272274AC671F857CC15144E9AA83E"); - torrentInfo.MagnetUrl.Should().Be("magnet:?xt=urn:btih:ED6E7P5IQJZCOSWGOH4FPTAVCRHJVKB6&dn=S4C.I.Grombil.Cyfandir.Pell.American.Interior.PDTV.x264-MVGroup"); - torrentInfo.Peers.Should().NotHaveValue(); - torrentInfo.Seeders.Should().NotHaveValue(); - } - - [Test] - public void should_parse_recent_feed_from_ShowRSS_info() - { - Subject.Definition.Settings.As().AllowZeroSize = true; - - GivenRecentFeedResponse("TorrentRss/ShowRSS.info.xml"); - - var releases = Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } }).Releases; - - releases.Should().HaveCount(5); - releases.First().Should().BeOfType(); - - var torrentInfo = releases.First() as TorrentInfo; - - torrentInfo.Title.Should().Be("The Voice 8x25"); - torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); - torrentInfo.DownloadUrl.Should().Be("magnet:?xt=urn:btih:96CD620BEDA3EFD7C4D7746EF94549D03A2EB13B&dn=The+Voice+S08E25+WEBRip+x264+WNN&tr=udp://tracker.coppersurfer.tk:6969/announce&tr=udp://tracker.leechers-paradise.org:6969&tr=udp://open.demonii.com:1337"); - torrentInfo.InfoUrl.Should().BeNullOrEmpty(); - torrentInfo.CommentUrl.Should().BeNullOrEmpty(); - torrentInfo.Indexer.Should().Be(Subject.Definition.Name); - torrentInfo.PublishDate.Should().Be(DateTime.Parse("2015/05/15 08:30:01")); - torrentInfo.Size.Should().Be(0); - torrentInfo.InfoHash.Should().Be("96CD620BEDA3EFD7C4D7746EF94549D03A2EB13B"); - torrentInfo.MagnetUrl.Should().Be("magnet:?xt=urn:btih:96CD620BEDA3EFD7C4D7746EF94549D03A2EB13B&dn=The+Voice+S08E25+WEBRip+x264+WNN&tr=udp://tracker.coppersurfer.tk:6969/announce&tr=udp://tracker.leechers-paradise.org:6969&tr=udp://open.demonii.com:1337"); - torrentInfo.Peers.Should().NotHaveValue(); - torrentInfo.Seeders.Should().NotHaveValue(); - } - - [Test] - public void should_parse_recent_feed_from_Doki() - { - Subject.Definition.Settings.As().AllowZeroSize = true; - - GivenRecentFeedResponse("TorrentRss/Doki.xml"); - - var releases = Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } }).Releases; - - releases.Should().HaveCount(5); - releases.First().Should().BeOfType(); - - var torrentInfo = releases.First() as TorrentInfo; - - torrentInfo.Title.Should().Be("[Doki] PriPara 50 (848x480 h264 AAC) [6F0B49FD] mkv"); - torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); - torrentInfo.DownloadUrl.Should().Be("http://tracker.anime-index.org/download.php?id=82d8ad84403e01a7786130905ca169a3429e657f&f=%5BDoki%5D+PriPara+-+50+%28848x480+h264+AAC%29+%5B6F0B49FD%5D.mkv.torrent"); - torrentInfo.InfoUrl.Should().BeNullOrEmpty(); - torrentInfo.CommentUrl.Should().BeNullOrEmpty(); - torrentInfo.Indexer.Should().Be(Subject.Definition.Name); - torrentInfo.PublishDate.Should().Be(DateTime.Parse("Thu, 02 Jul 2015 08:18:29 GMT").ToUniversalTime()); - torrentInfo.Size.Should().Be(0); - torrentInfo.InfoHash.Should().BeNull(); - torrentInfo.MagnetUrl.Should().BeNull(); - torrentInfo.Peers.Should().NotHaveValue(); - torrentInfo.Seeders.Should().NotHaveValue(); - } - - [Test] - public void should_parse_recent_feed_from_ExtraTorrents() - { - GivenRecentFeedResponse("TorrentRss/ExtraTorrents.xml"); - - var releases = Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } }).Releases; - - releases.Should().HaveCount(5); - releases.First().Should().BeOfType(); - - var torrentInfo = releases.First() as TorrentInfo; - - torrentInfo.Title.Should().Be("One.Piece.E334.D ED.720p.HDTV.x264-W4F-={SPARROW}=-"); - torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); - torrentInfo.DownloadUrl.Should().Be("http://ac.me/download/4722030/One.Piece.E334.D+ED.720p.HDTV.x264-W4F-%3D%7BSPARROW%7D%3D-.torrent"); - torrentInfo.InfoUrl.Should().BeNullOrEmpty(); - torrentInfo.CommentUrl.Should().BeNullOrEmpty(); - torrentInfo.Indexer.Should().Be(Subject.Definition.Name); - torrentInfo.PublishDate.Should().Be(DateTime.Parse("Sun, 21 Feb 2016 09:51:54 +0000").ToUniversalTime()); - torrentInfo.Size.Should().Be(562386947); - torrentInfo.InfoHash.Should().BeNull(); - torrentInfo.MagnetUrl.Should().BeNull(); - torrentInfo.Peers.Should().NotHaveValue(); - torrentInfo.Seeders.Should().NotHaveValue(); - } - - [Test] - public void should_parse_recent_feed_from_LimeTorrents() - { - GivenRecentFeedResponse("TorrentRss/LimeTorrents.xml"); - - var releases = Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } }).Releases; - - releases.Should().HaveCount(5); - releases.First().Should().BeOfType(); - - var torrentInfo = releases.First() as TorrentInfo; - - torrentInfo.Title.Should().Be("The Expanse 2x04 (720p-HDTV-x264-SVA)[VTV]"); - torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); - torrentInfo.DownloadUrl.Should().Be("http://itorrents.org/torrent/51C578C9823DD58F6EEA287C368ED935843D63AB.torrent?title=The-Expanse-2x04-(720p-HDTV-x264-SVA)[VTV]"); - torrentInfo.InfoUrl.Should().BeNullOrEmpty(); - torrentInfo.CommentUrl.Should().Be("http://www.limetorrents.cc/The-Expanse-2x04-(720p-HDTV-x264-SVA)[VTV]-torrent-8643587.html"); - torrentInfo.Indexer.Should().Be(Subject.Definition.Name); - torrentInfo.PublishDate.Should().Be(DateTime.Parse("16 Feb 2017 05:24:26 +0300").ToUniversalTime()); - torrentInfo.Size.Should().Be(880496711); - torrentInfo.InfoHash.Should().BeNull(); - torrentInfo.MagnetUrl.Should().BeNull(); - torrentInfo.Peers.Should().NotHaveValue(); - torrentInfo.Seeders.Should().NotHaveValue(); - } - - [Test] - public void should_parse_recent_feed_from_AnimeTosho_without_size() - { - GivenRecentFeedResponse("TorrentRss/AnimeTosho_NoSize.xml"); - - var releases = Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } }).Releases; - - releases.Should().HaveCount(2); - releases.First().Should().BeOfType(); - - var torrentInfo = releases.First() as TorrentInfo; - - torrentInfo.Title.Should().Be("[FFF] Ore Monogatari!! - Vol.01 [BD][720p-AAC]"); - torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); - torrentInfo.DownloadUrl.Should().Be("http://storage.animetosho.org/torrents/85a570f25067f69b3c83b901ce6c00c491345288/%5BFFF%5D%20Ore%20Monogatari%21%21%20-%20Vol.01%20%5BBD%5D%5B720p-AAC%5D.torrent"); - torrentInfo.InfoUrl.Should().BeNullOrEmpty(); - torrentInfo.CommentUrl.Should().Be("https://animetosho.org/view/fff-ore-monogatari-vol-01-bd-720p-aac.1009077"); - torrentInfo.Indexer.Should().Be(Subject.Definition.Name); - torrentInfo.PublishDate.Should().Be(DateTime.Parse("Tue, 02 Aug 2016 13:48:04 +0000").ToUniversalTime()); - torrentInfo.Size.Should().Be((long)Math.Round(1.366D * 1024L * 1024L * 1024L)); - torrentInfo.InfoHash.Should().BeNull(); - torrentInfo.MagnetUrl.Should().BeNull(); - torrentInfo.Peers.Should().NotHaveValue(); - torrentInfo.Seeders.Should().NotHaveValue(); - } - - [Test] - public void should_parse_multi_enclosure_from_AnimeTosho() - { - GivenRecentFeedResponse("TorrentRss/AnimeTosho_NoSize.xml"); - - var releases = Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } }).Releases; - - releases.Should().HaveCount(2); - releases.Last().Should().BeOfType(); - - var torrentInfo = releases.Last() as TorrentInfo; - - torrentInfo.Title.Should().Be("DAYS - 05 (1280x720 HEVC2 AAC).mkv"); - torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); - torrentInfo.DownloadUrl.Should().Be("http://storage.animetosho.org/torrents/4b58360143d59a55cbd922397a3eaa378165f3ff/DAYS%20-%2005%20%281280x720%20HEVC2%20AAC%29.torrent"); - } - - [Test] - public void should_parse_recent_feed_from_AlphaRatio() - { - GivenRecentFeedResponse("TorrentRss/AlphaRatio.xml"); - - var releases = Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } }).Releases; - - releases.Should().HaveCount(2); - releases.Last().Should().BeOfType(); - - var torrentInfo = releases.Last() as TorrentInfo; - - torrentInfo.Title.Should().Be("TvHD 465860 465831 WWE.RAW.2016.11.28.720p.HDTV.x264-KYR"); - torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); - torrentInfo.DownloadUrl.Should().Be("https://alpharatio.cc/torrents.php?action=download&authkey=private_auth_key&torrent_pass=private_torrent_pass&id=465831"); - } - - [Test] - public void should_parse_recent_feed_from_EveolutionWorld_without_size() - { - Subject.Definition.Settings.As().AllowZeroSize = true; - GivenRecentFeedResponse("TorrentRss/EvolutionWorld.xml"); - - var releases = Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } }).Releases; - - releases.Should().HaveCount(2); - releases.First().Should().BeOfType(); - - var torrentInfo = releases.First() as TorrentInfo; - - torrentInfo.Title.Should().Be("[TVShow --> TVShow Bluray 720p] Fargo S01 Complete Season 1 720p BRRip DD5.1 x264-PSYPHER [SEEDERS (3)/LEECHERS (0)]"); - torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); - torrentInfo.DownloadUrl.Should().Be("http://ew.pw/download.php?id=dea071a7a62a0d662538d46402fb112f30b8c9fa&f=Fargo%20S01%20Complete%20Season%201%20720p%20BRRip%20DD5.1%20x264-PSYPHER.torrent&auth=secret"); - torrentInfo.InfoUrl.Should().BeNullOrEmpty(); - torrentInfo.CommentUrl.Should().BeNullOrEmpty(); - torrentInfo.Indexer.Should().Be(Subject.Definition.Name); - torrentInfo.PublishDate.Should().Be(DateTime.Parse("2017-08-13T22:21:43Z").ToUniversalTime()); - torrentInfo.Size.Should().Be(0); - torrentInfo.InfoHash.Should().BeNull(); - torrentInfo.MagnetUrl.Should().BeNull(); - torrentInfo.Peers.Should().NotHaveValue(); - torrentInfo.Seeders.Should().NotHaveValue(); - } - - [Test] - public void should_record_indexer_failure_if_unsupported_feed() - { - GivenRecentFeedResponse("TorrentRss/invalid/TorrentDay_NoPubDate.xml"); - - Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } }).Releases.Should().BeEmpty(); - - Mocker.GetMock() - .Verify(v => v.RecordFailure(It.IsAny(), TimeSpan.Zero), Times.Once()); - - ExceptionVerification.ExpectedErrors(1); - } - } -} diff --git a/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TorrentRssParserFactoryFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TorrentRssParserFactoryFixture.cs deleted file mode 100644 index e506c2978..000000000 --- a/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TorrentRssParserFactoryFixture.cs +++ /dev/null @@ -1,164 +0,0 @@ -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Common.Cache; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Indexers.Exceptions; -using NzbDrone.Core.Indexers.TorrentRss; -using NzbDrone.Core.Test.Framework; - -namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests -{ - [TestFixture] - public class TorrentRssParserFactoryFixture : CoreTest - { - private TorrentRssIndexerSettings _indexerSettings1; - private TorrentRssIndexerSettings _indexerSettings2; - private TorrentRssIndexerSettings _indexerSettings3; - - [SetUp] - public void SetUp() - { - Mocker.SetConstant(Mocker.Resolve()); - - _indexerSettings1 = new TorrentRssIndexerSettings - { - BaseUrl = "http://my.indexer.com/" - }; - - _indexerSettings2 = new TorrentRssIndexerSettings - { - BaseUrl = "http://my.other.indexer.com/" - }; - - _indexerSettings3 = new TorrentRssIndexerSettings - { - BaseUrl = "http://my.indexer.com/", - AllowZeroSize = true - }; - } - - private void GivenSuccessful(TorrentRssIndexerParserSettings parserSettings = null) - { - if (parserSettings == null) - { - parserSettings = new TorrentRssIndexerParserSettings - { - UseEnclosureLength = true, - ParseSizeInDescription = false - }; - } - - Mocker.GetMock() - .Setup(v => v.Detect(It.IsAny())) - .Returns(parserSettings); - } - - private void GivenFailed() - { - Mocker.GetMock() - .Setup(v => v.Detect(It.IsAny())) - .Returns((TorrentRssIndexerParserSettings)null); - } - - private void VerifyDetectionCount(int count) - { - Mocker.GetMock() - .Verify(v => v.Detect(It.IsAny()), Times.Exactly(count)); - } - - [Test] - public void should_return_ezrssparser() - { - GivenSuccessful(new TorrentRssIndexerParserSettings - { - UseEZTVFormat = true - }); - - var parser = Subject.GetParser(_indexerSettings1); - - parser.Should().BeOfType(); - } - - [Test] - public void should_return_generic_torrentrssparser() - { - GivenSuccessful(new TorrentRssIndexerParserSettings - { - ParseSeedersInDescription = true, - ParseSizeInDescription = true, - SizeElementName = "Hello" - }); - - var parser = Subject.GetParser(_indexerSettings1); - - parser.Should().BeOfType(); - - var rssParser = parser as TorrentRssParser; - - rssParser.ParseSeedersInDescription.Should().BeTrue(); - rssParser.ParseSizeInDescription.Should().BeTrue(); - rssParser.SizeElementName.Should().Be("Hello"); - } - - [Test] - public void should_throw_on_failure() - { - GivenFailed(); - - Assert.Throws(() => Subject.GetParser(_indexerSettings1)); - } - - [Test] - public void should_cache_settings_for_same_baseurl() - { - GivenSuccessful(); - - var detection1 = Subject.GetParser(_indexerSettings1); - - var detection2 = Subject.GetParser(_indexerSettings1); - - detection1.Should().BeEquivalentTo(detection2); - - VerifyDetectionCount(1); - } - - [Test] - public void should_not_cache_failure() - { - GivenFailed(); - - Assert.Throws(() => Subject.GetParser(_indexerSettings1)); - - GivenSuccessful(); - - Subject.GetParser(_indexerSettings1); - - VerifyDetectionCount(2); - } - - [Test] - public void should_not_cache_settings_for_different_baseurl() - { - GivenSuccessful(); - - var detection1 = Subject.GetParser(_indexerSettings1); - - var detection2 = Subject.GetParser(_indexerSettings2); - - VerifyDetectionCount(2); - } - - [Test] - public void should_not_cache_settings_for_different_settings() - { - GivenSuccessful(); - - var detection1 = Subject.GetParser(_indexerSettings1); - - var detection2 = Subject.GetParser(_indexerSettings3); - - VerifyDetectionCount(2); - } - } -} diff --git a/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TorrentRssSettingsDetectorFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TorrentRssSettingsDetectorFixture.cs deleted file mode 100644 index 79fb7fdfb..000000000 --- a/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TorrentRssSettingsDetectorFixture.cs +++ /dev/null @@ -1,320 +0,0 @@ -using System.Net; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Common.Http; -using NzbDrone.Core.Indexers.Exceptions; -using NzbDrone.Core.Indexers.TorrentRss; -using NzbDrone.Core.Test.Framework; - -namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests -{ - [TestFixture] - public class TorrentRssSettingsDetectorFixture : CoreTest - { - private const string _indexerUrl = "http://my.indexer.tv/recent"; - private TorrentRssIndexerSettings _indexerSettings; - - [SetUp] - public void SetUp() - { - _indexerSettings = new TorrentRssIndexerSettings { BaseUrl = _indexerUrl }; - } - - private void GivenRecentFeedResponse(string rssXmlFile) - { - var recentFeed = ReadAllText(@"Files/Indexers/" + rssXmlFile); - - Mocker.GetMock() - .Setup(o => o.Execute(It.IsAny())) - .Returns(r => new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed)); - } - - [Test] - public void should_detect_rss_settings_for_ezrss() - { - GivenRecentFeedResponse("TorrentRss/Ezrss.xml"); - - var settings = Subject.Detect(_indexerSettings); - - settings.Should().BeEquivalentTo(new TorrentRssIndexerParserSettings - { - UseEZTVFormat = true, - UseEnclosureUrl = false, - UseEnclosureLength = false, - ParseSizeInDescription = false, - ParseSeedersInDescription = false, - SizeElementName = null - }); - } - - [Test] - public void should_detect_rss_settings_for_speed_cd() - { - GivenRecentFeedResponse("TorrentRss/speed.cd.xml"); - - var settings = Subject.Detect(_indexerSettings); - - settings.Should().BeEquivalentTo(new TorrentRssIndexerParserSettings - { - UseEZTVFormat = false, - UseEnclosureUrl = false, - UseEnclosureLength = false, - ParseSizeInDescription = true, - ParseSeedersInDescription = false, - SizeElementName = null - }); - } - - [Test] - public void should_detect_rss_settings_for_ImmortalSeed() - { - GivenRecentFeedResponse("TorrentRss/ImmortalSeed.xml"); - - var settings = Subject.Detect(_indexerSettings); - - settings.Should().BeEquivalentTo(new TorrentRssIndexerParserSettings - { - UseEZTVFormat = false, - UseEnclosureUrl = false, - UseEnclosureLength = false, - ParseSizeInDescription = true, - ParseSeedersInDescription = true, - SizeElementName = null - }); - } - - [Test] - public void should_detect_rss_settings_for_ShowRSS_info() - { - _indexerSettings.AllowZeroSize = true; - - GivenRecentFeedResponse("TorrentRss/ShowRSS.info.xml"); - - var settings = Subject.Detect(_indexerSettings); - - settings.Should().BeEquivalentTo(new TorrentRssIndexerParserSettings - { - UseEZTVFormat = false, - UseEnclosureUrl = true, - UseEnclosureLength = false, - ParseSizeInDescription = false, - ParseSeedersInDescription = false, - SizeElementName = null - }); - } - - [Test] - public void should_detect_rss_settings_for_TransmitTheNet() - { - GivenRecentFeedResponse("TorrentRss/TransmitTheNet.xml"); - - var settings = Subject.Detect(_indexerSettings); - - settings.Should().BeEquivalentTo(new TorrentRssIndexerParserSettings - { - UseEZTVFormat = false, - UseEnclosureUrl = true, - UseEnclosureLength = false, - ParseSizeInDescription = true, - ParseSeedersInDescription = false, - SizeElementName = null - }); - } - - [Test] - public void should_detect_rss_settings_for_BitHdtv() - { - GivenRecentFeedResponse("TorrentRss/BitHdtv.xml"); - - var settings = Subject.Detect(_indexerSettings); - - settings.Should().BeEquivalentTo(new TorrentRssIndexerParserSettings - { - UseEZTVFormat = false, - UseEnclosureUrl = false, - UseEnclosureLength = false, - ParseSizeInDescription = false, - ParseSeedersInDescription = false, - SizeElementName = "size" - }); - } - - [Test] - public void should_detect_rss_settings_for_Doki() - { - _indexerSettings.AllowZeroSize = true; - - GivenRecentFeedResponse("TorrentRss/Doki.xml"); - - var settings = Subject.Detect(_indexerSettings); - - settings.Should().BeEquivalentTo(new TorrentRssIndexerParserSettings - { - UseEZTVFormat = false, - UseEnclosureUrl = true, - UseEnclosureLength = false, - ParseSizeInDescription = false, - ParseSeedersInDescription = false, - SizeElementName = null - }); - } - - [Test] - public void should_detect_rss_settings_for_ExtraTorrents() - { - _indexerSettings.AllowZeroSize = true; - - GivenRecentFeedResponse("TorrentRss/ExtraTorrents.xml"); - - var settings = Subject.Detect(_indexerSettings); - - settings.Should().BeEquivalentTo(new TorrentRssIndexerParserSettings - { - UseEZTVFormat = false, - UseEnclosureUrl = true, - UseEnclosureLength = true, - ParseSizeInDescription = false, - ParseSeedersInDescription = false, - SizeElementName = null - }); - } - - [Test] - public void should_detect_rss_settings_for_LimeTorrents() - { - _indexerSettings.AllowZeroSize = true; - - GivenRecentFeedResponse("TorrentRss/LimeTorrents.xml"); - - var settings = Subject.Detect(_indexerSettings); - - settings.Should().BeEquivalentTo(new TorrentRssIndexerParserSettings - { - UseEZTVFormat = false, - UseEnclosureUrl = true, - UseEnclosureLength = true, - ParseSizeInDescription = false, - ParseSeedersInDescription = false, - SizeElementName = null - }); - } - - [Test] - public void should_detect_rss_settings_for_AlphaRatio() - { - _indexerSettings.AllowZeroSize = true; - - GivenRecentFeedResponse("TorrentRss/AlphaRatio.xml"); - - var settings = Subject.Detect(_indexerSettings); - - settings.Should().BeEquivalentTo(new TorrentRssIndexerParserSettings - { - UseEZTVFormat = false, - UseEnclosureUrl = false, - UseEnclosureLength = false, - ParseSizeInDescription = true, - ParseSeedersInDescription = false, - SizeElementName = null - }); - } - - [Test] - [Ignore("Cannot reliably reject unparseable titles")] - public void should_reject_rss_settings_for_AwesomeHD() - { - _indexerSettings.AllowZeroSize = true; - - GivenRecentFeedResponse("TorrentRss/AwesomeHD.xml"); - - var settings = Subject.Detect(_indexerSettings); - - settings.Should().BeNull(); - } - - [Test] - public void should_detect_rss_settings_for_AnimeTosho_without_size() - { - _indexerSettings.AllowZeroSize = true; - - GivenRecentFeedResponse("TorrentRss/AnimeTosho_NoSize.xml"); - - var settings = Subject.Detect(_indexerSettings); - - settings.Should().BeEquivalentTo(new TorrentRssIndexerParserSettings - { - UseEZTVFormat = false, - UseEnclosureUrl = true, - UseEnclosureLength = false, - ParseSizeInDescription = true, - ParseSeedersInDescription = false, - SizeElementName = null - }); - } - - [TestCase("IPTorrents/IPTorrents.xml")] - [TestCase("Nyaa/Nyaa.xml")] - [TestCase("Torznab/torznab_hdaccess_net.xml")] - [TestCase("Torznab/torznab_tpb.xml")] - [TestCase("Torznab/torznab_animetosho.xml")] - public void should_detect_recent_feed(string rssXmlFile) - { - GivenRecentFeedResponse(rssXmlFile); - - var settings = Subject.Detect(_indexerSettings); - - settings.Should().NotBeNull(); - } - - [TestCase("TorrentRss/invalid/ImmortalSeed_InvalidDownloadUrl.xml")] - public void should_reject_recent_feed_with_invalid_downloadurl(string rssXmlFile) - { - GivenRecentFeedResponse(rssXmlFile); - - var ex = Assert.Throws(() => Subject.Detect(_indexerSettings)); - - ex.Message.Should().Contain("download url"); - } - - [TestCase("TorrentRss/invalid/TorrentDay_NoPubDate.xml")] - public void should_reject_recent_feed_without_pubDate(string rssXmlFile) - { - GivenRecentFeedResponse(rssXmlFile); - - var ex = Assert.Throws(() => Subject.Detect(_indexerSettings)); - - ex.Message.Should().Contain("Rss feed must have a pubDate"); - } - - [TestCase("Torrentleech/Torrentleech.xml")] - [TestCase("TorrentRss/invalid/Eztv_InvalidSize.xml")] - [TestCase("TorrentRss/invalid/ImmortalSeed_InvalidSize.xml")] - [TestCase("TorrentRss/Doki.xml")] - public void should_detect_feed_without_size(string rssXmlFile) - { - _indexerSettings.AllowZeroSize = true; - - GivenRecentFeedResponse(rssXmlFile); - - var settings = Subject.Detect(_indexerSettings); - - settings.Should().NotBeNull(); - - settings.UseEnclosureLength.Should().BeFalse(); - settings.ParseSizeInDescription.Should().BeFalse(); - settings.SizeElementName.Should().BeNull(); - } - - [TestCase("TorrentRss/invalid/Eztv_InvalidSize.xml")] - [TestCase("TorrentRss/invalid/ImmortalSeed_InvalidSize.xml")] - public void should_reject_feed_without_size(string rssXmlFile) - { - GivenRecentFeedResponse(rssXmlFile); - - var ex = Assert.Throws(() => Subject.Detect(_indexerSettings)); - - ex.Message.Should().Contain("content size"); - } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/AlphaRatio.cs b/src/NzbDrone.Core/Indexers/Definitions/AlphaRatio.cs new file mode 100644 index 000000000..f451ef385 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/AlphaRatio.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using NLog; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; + +namespace NzbDrone.Core.Indexers.Definitions +{ + public class AlphaRatio : Gazelle.Gazelle + { + public override string Name => "AlphaRatio"; + public override string BaseUrl => "https://alpharatio.cc/"; + public override IndexerPrivacy Privacy => IndexerPrivacy.Private; + + public AlphaRatio(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + : base(httpClient, indexerStatusService, configService, logger) + { + } + + public override IIndexerRequestGenerator GetRequestGenerator() + { + return new AlphaRatioRequestGenerator() + { + Settings = Settings, + HttpClient = _httpClient, + Logger = _logger, + Capabilities = Capabilities, + BaseUrl = BaseUrl + }; + } + + protected override IndexerCapabilities SetCapabilities() + { + var caps = new IndexerCapabilities + { + TvSearchParams = new List + { + TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep + }, + MovieSearchParams = new List + { + MovieSearchParam.Q, MovieSearchParam.ImdbId + } + }; + + caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.TVSD, "TvSD"); + caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TVHD, "TvHD"); + caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.TVUHD, "TvUHD"); + caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.TVSD, "TvDVDRip"); + caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.TVSD, "TvPackSD"); + caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.TVHD, "TvPackHD"); + caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.TVUHD, "TvPackUHD"); + caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.MoviesSD, "MovieSD"); + caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.MoviesHD, "MovieHD"); + caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.MoviesUHD, "MovieUHD"); + caps.Categories.AddCategoryMapping(11, NewznabStandardCategory.MoviesSD, "MoviePackSD"); + caps.Categories.AddCategoryMapping(12, NewznabStandardCategory.MoviesHD, "MoviePackHD"); + caps.Categories.AddCategoryMapping(13, NewznabStandardCategory.MoviesUHD, "MoviePackUHD"); + caps.Categories.AddCategoryMapping(14, NewznabStandardCategory.XXX, "MovieXXX"); + caps.Categories.AddCategoryMapping(15, NewznabStandardCategory.MoviesBluRay, "Bluray"); + caps.Categories.AddCategoryMapping(16, NewznabStandardCategory.TVAnime, "AnimeSD"); + caps.Categories.AddCategoryMapping(17, NewznabStandardCategory.TVAnime, "AnimeHD"); + caps.Categories.AddCategoryMapping(18, NewznabStandardCategory.PCGames, "GamesPC"); + caps.Categories.AddCategoryMapping(19, NewznabStandardCategory.ConsoleXBox, "GamesxBox"); + caps.Categories.AddCategoryMapping(20, NewznabStandardCategory.ConsolePS4, "GamesPS"); + caps.Categories.AddCategoryMapping(21, NewznabStandardCategory.ConsoleWii, "GamesNin"); + caps.Categories.AddCategoryMapping(22, NewznabStandardCategory.PC0day, "AppsWindows"); + caps.Categories.AddCategoryMapping(23, NewznabStandardCategory.PCMac, "AppsMAC"); + caps.Categories.AddCategoryMapping(24, NewznabStandardCategory.PC0day, "AppsLinux"); + caps.Categories.AddCategoryMapping(25, NewznabStandardCategory.PCMobileOther, "AppsMobile"); + caps.Categories.AddCategoryMapping(26, NewznabStandardCategory.XXX, "0dayXXX"); + caps.Categories.AddCategoryMapping(27, NewznabStandardCategory.Books, "eBook"); + caps.Categories.AddCategoryMapping(28, NewznabStandardCategory.AudioAudiobook, "AudioBook"); + caps.Categories.AddCategoryMapping(29, NewznabStandardCategory.AudioOther, "Music"); + caps.Categories.AddCategoryMapping(30, NewznabStandardCategory.Other, "Misc"); + + return caps; + } + } + + public class AlphaRatioRequestGenerator : Gazelle.GazelleRequestGenerator + { + protected override bool ImdbInTags => true; + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHD.cs b/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHD.cs index 4adb30e95..4273c74d8 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHD.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHD.cs @@ -8,6 +8,7 @@ namespace NzbDrone.Core.Indexers.AwesomeHD public class AwesomeHD : HttpIndexerBase { public override string Name => "AwesomeHD"; + public override string BaseUrl => "https://awesome-hd.club"; public override DownloadProtocol Protocol => DownloadProtocol.Torrent; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override bool SupportsRss => true; @@ -24,12 +25,12 @@ namespace NzbDrone.Core.Indexers.AwesomeHD public override IIndexerRequestGenerator GetRequestGenerator() { - return new AwesomeHDRequestGenerator() { Settings = Settings }; + return new AwesomeHDRequestGenerator() { Settings = Settings, BaseUrl = BaseUrl }; } public override IParseIndexerResponse GetParser() { - return new AwesomeHDRssParser(Settings); + return new AwesomeHDRssParser(Settings, BaseUrl); } private IndexerCapabilities SetCapabilities() diff --git a/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDRequestGenerator.cs index a941c182a..f2202c57c 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDRequestGenerator.cs @@ -8,6 +8,7 @@ namespace NzbDrone.Core.Indexers.AwesomeHD { public class AwesomeHDRequestGenerator : IIndexerRequestGenerator { + public string BaseUrl { get; set; } public AwesomeHDSettings Settings { get; set; } public virtual IndexerPageableRequestChain GetRecentRequests() @@ -66,7 +67,7 @@ namespace NzbDrone.Core.Indexers.AwesomeHD { if (searchParameters != null) { - yield return new IndexerRequest($"{Settings.BaseUrl.Trim().TrimEnd('/')}/searchapi.php?passkey={Settings.Passkey.Trim()}{searchParameters}", HttpAccept.Rss); + yield return new IndexerRequest($"{BaseUrl.Trim().TrimEnd('/')}/searchapi.php?passkey={Settings.Passkey.Trim()}{searchParameters}", HttpAccept.Rss); } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDRssParser.cs b/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDRssParser.cs index f63ec0829..498cc5bd8 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDRssParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDRssParser.cs @@ -12,11 +12,13 @@ namespace NzbDrone.Core.Indexers.AwesomeHD { public class AwesomeHDRssParser : IParseIndexerResponse { + private readonly string _baseUrl; private readonly AwesomeHDSettings _settings; - public AwesomeHDRssParser(AwesomeHDSettings settings) + public AwesomeHDRssParser(AwesomeHDSettings settings, string baseUrl) { _settings = settings; + _baseUrl = baseUrl; } public IList ParseResponse(IndexerResponse indexerResponse) @@ -145,7 +147,7 @@ namespace NzbDrone.Core.Indexers.AwesomeHD private string GetDownloadUrl(string torrentId, string authKey, string passKey) { - var url = new HttpUri(_settings.BaseUrl) + var url = new HttpUri(_baseUrl) .CombinePath("/torrents.php") .AddQueryParam("action", "download") .AddQueryParam("id", torrentId) @@ -157,7 +159,7 @@ namespace NzbDrone.Core.Indexers.AwesomeHD private string GetInfoUrl(string groupId, string torrentId) { - var url = new HttpUri(_settings.BaseUrl) + var url = new HttpUri(_baseUrl) .CombinePath("/torrents.php") .AddQueryParam("id", groupId) .AddQueryParam("torrentid", torrentId); diff --git a/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDSettings.cs index badea92b9..ed78ee6b4 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDSettings.cs @@ -1,5 +1,6 @@ using FluentValidation; using NzbDrone.Core.Annotations; +using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.AwesomeHD @@ -8,22 +9,18 @@ namespace NzbDrone.Core.Indexers.AwesomeHD { public AwesomeHDSettingsValidator() { - RuleFor(c => c.BaseUrl).ValidRootUrl(); RuleFor(c => c.Passkey).NotEmpty(); } } - public class AwesomeHDSettings : IIndexerSettings + public class AwesomeHDSettings : IProviderConfig { private static readonly AwesomeHDSettingsValidator Validator = new AwesomeHDSettingsValidator(); public AwesomeHDSettings() { - BaseUrl = "https://awesome-hd.club"; } - public string BaseUrl { get; set; } - [FieldDefinition(1, Label = "Passkey", Privacy = PrivacyLevel.ApiKey)] public string Passkey { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNet.cs b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNet.cs new file mode 100644 index 000000000..bbfb9a38a --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNet.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using NLog; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; + +namespace NzbDrone.Core.Indexers.BroadcastheNet +{ + public class BroadcastheNet : HttpIndexerBase + { + public override string Name => "BroadcasTheNet"; + + public override IndexerPrivacy Privacy => IndexerPrivacy.Private; + public override DownloadProtocol Protocol => DownloadProtocol.Torrent; + public override bool SupportsRss => true; + public override bool SupportsSearch => true; + public override int PageSize => 100; + public override IndexerCapabilities Capabilities => SetCapabilities(); + + public override string BaseUrl => "http://api.broadcasthe.net/"; + + public BroadcastheNet(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + : base(httpClient, indexerStatusService, configService, logger) + { + } + + public override IIndexerRequestGenerator GetRequestGenerator() + { + var requestGenerator = new BroadcastheNetRequestGenerator() { Settings = Settings, PageSize = PageSize, BaseUrl = BaseUrl }; + + var releaseInfo = _indexerStatusService.GetLastRssSyncReleaseInfo(Definition.Id); + if (releaseInfo != null) + { + int torrentID; + if (int.TryParse(releaseInfo.Guid.Replace("BTN-", string.Empty), out torrentID)) + { + requestGenerator.LastRecentTorrentID = torrentID; + } + } + + return requestGenerator; + } + + public override IParseIndexerResponse GetParser() + { + return new BroadcastheNetParser(); + } + + private IndexerCapabilities SetCapabilities() + { + var caps = new IndexerCapabilities + { + LimitsDefault = 100, + LimitsMax = 1000, + TvSearchParams = new List + { + TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep + } + }; + + caps.Categories.AddCategoryMapping("SD", NewznabStandardCategory.TVSD, "SD"); + caps.Categories.AddCategoryMapping("720p", NewznabStandardCategory.TVHD, "720p"); + caps.Categories.AddCategoryMapping("1080p", NewznabStandardCategory.TVHD, "1080p"); + caps.Categories.AddCategoryMapping("1080i", NewznabStandardCategory.TVHD, "1080i"); + caps.Categories.AddCategoryMapping("2160p", NewznabStandardCategory.TVHD, "2160p"); + caps.Categories.AddCategoryMapping("Portable Device", NewznabStandardCategory.TVSD, "Portable Device"); + + return caps; + } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetParser.cs b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetParser.cs new file mode 100644 index 000000000..6468de241 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetParser.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text.RegularExpressions; +using NzbDrone.Common.Http; +using NzbDrone.Core.Indexers.Exceptions; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Indexers.BroadcastheNet +{ + public class BroadcastheNetParser : IParseIndexerResponse + { + private static readonly Regex RegexProtocol = new Regex("^https?:", RegexOptions.Compiled); + + public Action, DateTime?> CookiesUpdater { get; set; } + + public IList ParseResponse(IndexerResponse indexerResponse) + { + var results = new List(); + + switch (indexerResponse.HttpResponse.StatusCode) + { + case HttpStatusCode.Unauthorized: + throw new ApiKeyException("API Key invalid or not authorized"); + case HttpStatusCode.NotFound: + throw new IndexerException(indexerResponse, "Indexer API call returned NotFound, the Indexer API may have changed."); + case HttpStatusCode.ServiceUnavailable: + throw new RequestLimitReachedException("Cannot do more than 150 API requests per hour."); + default: + if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) + { + throw new IndexerException(indexerResponse, "Indexer API call returned an unexpected StatusCode [{0}]", indexerResponse.HttpResponse.StatusCode); + } + + break; + } + + if (indexerResponse.HttpResponse.Headers.ContentType != null && indexerResponse.HttpResponse.Headers.ContentType.Contains("text/html")) + { + throw new IndexerException(indexerResponse, "Indexer responded with html content. Site is likely blocked or unavailable."); + } + + if (indexerResponse.Content == "Query execution was interrupted") + { + throw new IndexerException(indexerResponse, "Indexer API returned an internal server error"); + } + + JsonRpcResponse jsonResponse = new HttpResponse>(indexerResponse.HttpResponse).Resource; + + if (jsonResponse.Error != null || jsonResponse.Result == null) + { + throw new IndexerException(indexerResponse, "Indexer API call returned an error [{0}]", jsonResponse.Error); + } + + if (jsonResponse.Result.Results == 0) + { + return results; + } + + var protocol = indexerResponse.HttpRequest.Url.Scheme + ":"; + + foreach (var torrent in jsonResponse.Result.Torrents.Values) + { + var torrentInfo = new TorrentInfo(); + + torrentInfo.Guid = string.Format("BTN-{0}", torrent.TorrentID); + torrentInfo.Title = CleanReleaseName(torrent.ReleaseName); + torrentInfo.Size = torrent.Size; + torrentInfo.DownloadUrl = RegexProtocol.Replace(torrent.DownloadURL, protocol); + torrentInfo.InfoUrl = string.Format("{0}//broadcasthe.net/torrents.php?id={1}&torrentid={2}", protocol, torrent.GroupID, torrent.TorrentID); + + //torrentInfo.CommentUrl = + if (torrent.TvdbID.HasValue) + { + torrentInfo.TvdbId = torrent.TvdbID.Value; + } + + if (torrent.TvrageID.HasValue) + { + torrentInfo.TvRageId = torrent.TvrageID.Value; + } + + torrentInfo.PublishDate = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).ToUniversalTime().AddSeconds(torrent.Time); + + //torrentInfo.MagnetUrl = + torrentInfo.InfoHash = torrent.InfoHash; + torrentInfo.Seeders = torrent.Seeders; + torrentInfo.Peers = torrent.Leechers + torrent.Seeders; + + torrentInfo.Origin = torrent.Origin; + torrentInfo.Source = torrent.Source; + torrentInfo.Container = torrent.Container; + torrentInfo.Codec = torrent.Codec; + torrentInfo.Resolution = torrent.Resolution; + + results.Add(torrentInfo); + } + + return results; + } + + private string CleanReleaseName(string releaseName) + { + releaseName = releaseName.Replace("\\", ""); + + return releaseName; + } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetRequestGenerator.cs new file mode 100644 index 000000000..f6ad7f0e0 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetRequestGenerator.cs @@ -0,0 +1,118 @@ +using System.Collections.Generic; +using System.Linq; +using NzbDrone.Common.Http; +using NzbDrone.Core.IndexerSearch.Definitions; + +namespace NzbDrone.Core.Indexers.BroadcastheNet +{ + public class BroadcastheNetRequestGenerator : IIndexerRequestGenerator + { + public int MaxPages { get; set; } + public int PageSize { get; set; } + public BroadcastheNetSettings Settings { get; set; } + public IndexerCapabilities Capabilities { get; set; } + + public int? LastRecentTorrentID { get; set; } + public System.Func> GetCookies { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } + public System.Action, System.DateTime?> CookiesUpdater { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } + public string BaseUrl { get; set; } + + public BroadcastheNetRequestGenerator() + { + MaxPages = 10; + PageSize = 100; + } + + private IEnumerable GetPagedRequests(BroadcastheNetTorrentQuery parameters, int results, int offset) + { + var builder = new JsonRpcRequestBuilder(BaseUrl) + .Call("getTorrents", Settings.ApiKey, parameters, results, offset); + builder.SuppressHttpError = true; + + yield return new IndexerRequest(builder.Build()); + } + + public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + var parameters = new BroadcastheNetTorrentQuery(); + + var searchString = searchCriteria.SearchTerm != null ? searchCriteria.SearchTerm : ""; + + var btnResults = searchCriteria.Limit.GetValueOrDefault(); + if (btnResults == 0) + { + btnResults = (int)Capabilities.LimitsDefault; + } + + var btnOffset = searchCriteria.Offset.GetValueOrDefault(); + + if (searchCriteria.TvdbId != 0) + { + parameters.Tvdb = string.Format("{0}", searchCriteria.TvdbId); + } + + if (searchCriteria.RId != 0) + { + parameters.Tvrage = string.Format("{0}", searchCriteria.RId); + } + + // If only the season/episode is searched for then change format to match expected format + if (searchCriteria.Season > 0 && searchCriteria.Ep == null) + { + parameters.Name = string.Format("Season {0}%", searchCriteria.Season.Value); + parameters.Category = "Season"; + } + else if (searchCriteria.Season > 0 && searchCriteria.Ep.Value > 0) + { + parameters.Name = string.Format("S{0:00}E{1:00}", searchCriteria.Season.Value, searchCriteria.Ep.Value); + parameters.Category = "Episode"; + } + + parameters.Search = searchString.Replace(" ", "%"); + + pageableRequests.Add(GetPagedRequests(parameters, btnResults, btnOffset)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + var parameters = new BroadcastheNetTorrentQuery(); + + var searchString = searchCriteria.SearchTerm != null ? searchCriteria.SearchTerm : ""; + + var btnResults = searchCriteria.Limit.GetValueOrDefault(); + if (btnResults == 0) + { + btnResults = (int)Capabilities.LimitsDefault; + } + + parameters.Search = searchString.Replace(" ", "%"); + + var btnOffset = searchCriteria.Offset.GetValueOrDefault(); + + pageableRequests.Add(GetPagedRequests(parameters, btnResults, btnOffset)); + + return pageableRequests; + } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetSettings.cs new file mode 100644 index 000000000..24e090f1f --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetSettings.cs @@ -0,0 +1,32 @@ +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.Indexers.BroadcastheNet +{ + public class BroadcastheNetSettingsValidator : AbstractValidator + { + public BroadcastheNetSettingsValidator() + { + RuleFor(c => c.ApiKey).NotEmpty(); + } + } + + public class BroadcastheNetSettings : IProviderConfig + { + private static readonly BroadcastheNetSettingsValidator Validator = new BroadcastheNetSettingsValidator(); + + public BroadcastheNetSettings() + { + } + + [FieldDefinition(1, Label = "API Key", Privacy = PrivacyLevel.ApiKey)] + public string ApiKey { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetTorrent.cs b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetTorrent.cs new file mode 100644 index 000000000..fd33c3bac --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetTorrent.cs @@ -0,0 +1,31 @@ +namespace NzbDrone.Core.Indexers.BroadcastheNet +{ + public class BroadcastheNetTorrent + { + public string GroupName { get; set; } + public int GroupID { get; set; } + public int TorrentID { get; set; } + public int SeriesID { get; set; } + public string Series { get; set; } + public string SeriesBanner { get; set; } + public string SeriesPoster { get; set; } + public string YoutubeTrailer { get; set; } + public string Category { get; set; } + public int? Snatched { get; set; } + public int? Seeders { get; set; } + public int? Leechers { get; set; } + public string Source { get; set; } + public string Container { get; set; } + public string Codec { get; set; } + public string Resolution { get; set; } + public string Origin { get; set; } + public string ReleaseName { get; set; } + public long Size { get; set; } + public long Time { get; set; } + public int? TvdbID { get; set; } + public int? TvrageID { get; set; } + public string ImdbID { get; set; } + public string InfoHash { get; set; } + public string DownloadURL { get; set; } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetTorrentQuery.cs b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetTorrentQuery.cs new file mode 100644 index 000000000..1180f9b63 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetTorrentQuery.cs @@ -0,0 +1,39 @@ +using Newtonsoft.Json; + +namespace NzbDrone.Core.Indexers.BroadcastheNet +{ + public class BroadcastheNetTorrentQuery + { + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Id { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Category { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Name { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Search { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Codec { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Container { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Source { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Resolution { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Origin { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Hash { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Tvdb { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Tvrage { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Age { get; set; } + + public BroadcastheNetTorrentQuery Clone() + { + return MemberwiseClone() as BroadcastheNetTorrentQuery; + } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetTorrents.cs b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetTorrents.cs new file mode 100644 index 000000000..f9329e7ea --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetTorrents.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace NzbDrone.Core.Indexers.BroadcastheNet +{ + public class BroadcastheNetTorrents + { + public Dictionary Torrents { get; set; } + public int Results { get; set; } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs index 6ab922804..57cdfd6ab 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs @@ -15,6 +15,7 @@ namespace NzbDrone.Core.Indexers.Cardigann private readonly IIndexerDefinitionUpdateService _definitionService; public override string Name => "Cardigann"; + public override string BaseUrl => throw new System.NotImplementedException(); public override DownloadProtocol Protocol => DownloadProtocol.Torrent; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannSettings.cs index db8e9861c..299c90f52 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannSettings.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using FluentValidation; using NzbDrone.Common.Extensions; using NzbDrone.Core.Annotations; -using NzbDrone.Core.Languages; +using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.Cardigann @@ -14,7 +14,7 @@ namespace NzbDrone.Core.Indexers.Cardigann } } - public class CardigannSettings : IIndexerSettings + public class CardigannSettings : IProviderConfig { private static readonly CardigannSettingsValidator Validator = new CardigannSettingsValidator(); @@ -28,8 +28,6 @@ namespace NzbDrone.Core.Indexers.Cardigann public Dictionary ExtraFieldData { get; set; } - public string BaseUrl { get; set; } - // Field 8 is used by TorznabSettings MinimumSeeders // If you need to add another field here, update TorznabSettings as well and this comment public virtual NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Indexers/Definitions/DigitalCore.cs b/src/NzbDrone.Core/Indexers/Definitions/DigitalCore.cs index ba189f92d..df0f21c71 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/DigitalCore.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/DigitalCore.cs @@ -12,6 +12,7 @@ using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.Definitions @@ -19,6 +20,7 @@ namespace NzbDrone.Core.Indexers.Definitions public class DigitalCore : HttpIndexerBase { public override string Name => "DigitalCore"; + public override string BaseUrl => "https://digitalcore.club/"; public override DownloadProtocol Protocol => DownloadProtocol.Torrent; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); @@ -35,7 +37,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IParseIndexerResponse GetParser() { - return new DigitalCoreParser(Settings, Capabilities.Categories); + return new DigitalCoreParser(Settings, Capabilities.Categories, BaseUrl); } private IndexerCapabilities SetCapabilities() @@ -107,6 +109,7 @@ namespace NzbDrone.Core.Indexers.Definitions public class DigitalCoreRequestGenerator : IIndexerRequestGenerator { + public string BaseUrl { get; set; } public DigitalCoreSettings Settings { get; set; } public IndexerCapabilities Capabilities { get; set; } @@ -121,7 +124,7 @@ namespace NzbDrone.Core.Indexers.Definitions private IEnumerable GetPagedRequests(string term, int[] categories) { - var baseUrl = string.Format("{0}/api/v1/torrents?extendedSearch=false", Settings.BaseUrl.TrimEnd('/')); + var baseUrl = string.Format("{0}/api/v1/torrents?extendedSearch=false", BaseUrl.TrimEnd('/')); var parameters = string.Empty; @@ -190,13 +193,15 @@ namespace NzbDrone.Core.Indexers.Definitions public class DigitalCoreParser : IParseIndexerResponse { + private readonly string _baseUrl; private readonly DigitalCoreSettings _settings; private readonly IndexerCapabilitiesCategories _categories; - public DigitalCoreParser(DigitalCoreSettings settings, IndexerCapabilitiesCategories categories) + public DigitalCoreParser(DigitalCoreSettings settings, IndexerCapabilitiesCategories categories, string baseUrl) { _settings = settings; _categories = categories; + _baseUrl = baseUrl; } public IList ParseResponse(IndexerResponse indexerResponse) @@ -230,8 +235,8 @@ namespace NzbDrone.Core.Indexers.Definitions release.Files = row.numfiles; release.Grabs = row.times_completed; - release.Guid = new Uri(_settings.BaseUrl + "torrent/" + row.id.ToString() + "/").ToString(); - release.DownloadUrl = _settings.BaseUrl + "api/v1/torrents/download/" + row.id.ToString(); + release.Guid = new Uri(_baseUrl + "torrent/" + row.id.ToString() + "/").ToString(); + release.DownloadUrl = _baseUrl + "api/v1/torrents/download/" + row.id.ToString(); if (row.imdbid2 != null && row.imdbid2.ToString().StartsWith("tt")) { @@ -266,19 +271,16 @@ namespace NzbDrone.Core.Indexers.Definitions } } - public class DigitalCoreSettings : IIndexerSettings + public class DigitalCoreSettings : IProviderConfig { private static readonly DigitalCoreSettingsValidator Validator = new DigitalCoreSettingsValidator(); public DigitalCoreSettings() { - BaseUrl = "https://digitalcore.club/"; UId = ""; Passphrase = ""; } - public string BaseUrl { get; set; } - [FieldDefinition(1, Label = "UID", Advanced = true, HelpText = "Uid from login cookie")] public string UId { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs index 29da07be4..26f79a38f 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs @@ -7,6 +7,7 @@ namespace NzbDrone.Core.Indexers.FileList public class FileList : HttpIndexerBase { public override string Name => "FileList.io"; + public override string BaseUrl => "https://filelist.io"; public override DownloadProtocol Protocol => DownloadProtocol.Torrent; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override bool SupportsRss => true; @@ -19,12 +20,12 @@ namespace NzbDrone.Core.Indexers.FileList public override IIndexerRequestGenerator GetRequestGenerator() { - return new FileListRequestGenerator() { Settings = Settings }; + return new FileListRequestGenerator() { Settings = Settings, BaseUrl = BaseUrl }; } public override IParseIndexerResponse GetParser() { - return new FileListParser(Settings); + return new FileListParser(Settings, BaseUrl); } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListParser.cs b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListParser.cs index 0936eba62..518597bab 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListParser.cs @@ -10,11 +10,13 @@ namespace NzbDrone.Core.Indexers.FileList { public class FileListParser : IParseIndexerResponse { + private readonly string _baseUrl; private readonly FileListSettings _settings; - public FileListParser(FileListSettings settings) + public FileListParser(FileListSettings settings, string baseUrl) { _settings = settings; + _baseUrl = baseUrl; } public IList ParseResponse(IndexerResponse indexerResponse) @@ -69,7 +71,7 @@ namespace NzbDrone.Core.Indexers.FileList private string GetDownloadUrl(string torrentId) { - var url = new HttpUri(_settings.BaseUrl) + var url = new HttpUri(_baseUrl) .CombinePath("/download.php") .AddQueryParam("id", torrentId) .AddQueryParam("passkey", _settings.Passkey); @@ -79,7 +81,7 @@ namespace NzbDrone.Core.Indexers.FileList private string GetInfoUrl(string torrentId) { - var url = new HttpUri(_settings.BaseUrl) + var url = new HttpUri(_baseUrl) .CombinePath("/details.php") .AddQueryParam("id", torrentId); diff --git a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListRequestGenerator.cs index 1fb0a4908..c2e0ece8a 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListRequestGenerator.cs @@ -9,6 +9,7 @@ namespace NzbDrone.Core.Indexers.FileList { public class FileListRequestGenerator : IIndexerRequestGenerator { + public string BaseUrl { get; set; } public FileListSettings Settings { get; set; } public Func> GetCookies { get; set; } public Action, DateTime?> CookiesUpdater { get; set; } @@ -58,7 +59,7 @@ namespace NzbDrone.Core.Indexers.FileList { var categoriesQuery = string.Join(",", categories.Distinct()); - var baseUrl = string.Format("{0}/api.php?action={1}&category={2}&username={3}&passkey={4}{5}", Settings.BaseUrl.TrimEnd('/'), searchType, categoriesQuery, Settings.Username.Trim(), Settings.Passkey.Trim(), parameters); + var baseUrl = string.Format("{0}/api.php?action={1}&category={2}&username={3}&passkey={4}{5}", BaseUrl.TrimEnd('/'), searchType, categoriesQuery, Settings.Username.Trim(), Settings.Passkey.Trim(), parameters); yield return new IndexerRequest(baseUrl, HttpAccept.Json); } diff --git a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListSettings.cs index abedbe878..48d278ced 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListSettings.cs @@ -3,6 +3,7 @@ using FluentValidation; using NzbDrone.Core.Annotations; using NzbDrone.Core.Languages; using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.FileList @@ -11,23 +12,19 @@ namespace NzbDrone.Core.Indexers.FileList { public FileListSettingsValidator() { - RuleFor(c => c.BaseUrl).ValidRootUrl(); RuleFor(c => c.Username).NotEmpty(); RuleFor(c => c.Passkey).NotEmpty(); } } - public class FileListSettings : IIndexerSettings + public class FileListSettings : IProviderConfig { private static readonly FileListSettingsValidator Validator = new FileListSettingsValidator(); public FileListSettings() { - BaseUrl = "https://filelist.io"; } - public string BaseUrl { get; set; } - [FieldDefinition(0, Label = "Username", Privacy = PrivacyLevel.UserName)] public string Username { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/Gazelle.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/Gazelle.cs new file mode 100644 index 000000000..1417ce836 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/Gazelle.cs @@ -0,0 +1,48 @@ +using NLog; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; + +namespace NzbDrone.Core.Indexers.Gazelle +{ + public abstract class Gazelle : HttpIndexerBase + { + public override DownloadProtocol Protocol => DownloadProtocol.Torrent; + public override string BaseUrl => ""; + public override bool SupportsRss => true; + public override bool SupportsSearch => true; + public override int PageSize => 50; + public override IndexerCapabilities Capabilities => SetCapabilities(); + + public Gazelle(IHttpClient httpClient, + IIndexerStatusService indexerStatusService, + IConfigService configService, + Logger logger) + : base(httpClient, indexerStatusService, configService, logger) + { + } + + public override IIndexerRequestGenerator GetRequestGenerator() + { + return new GazelleRequestGenerator() + { + Settings = Settings, + HttpClient = _httpClient, + Logger = _logger, + Capabilities = Capabilities, + BaseUrl = BaseUrl + }; + } + + public override IParseIndexerResponse GetParser() + { + return new GazelleParser(Settings, Capabilities, BaseUrl); + } + + protected virtual IndexerCapabilities SetCapabilities() + { + var caps = new IndexerCapabilities(); + + return caps; + } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleApi.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleApi.cs new file mode 100644 index 000000000..7da0595a0 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleApi.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Core.Indexers.Gazelle +{ + public class GazelleArtist + { + public string Name { get; set; } + public string Id { get; set; } + public string Aliasid { get; set; } + } + + public class GazelleTorrent + { + public int TorrentId { get; set; } + public int EditionId { get; set; } + public List Artists { get; set; } + public bool Remastered { get; set; } + public string RemasterYear { get; set; } + public string RemasterTitle { get; set; } + public string Media { get; set; } + public string Encoding { get; set; } + public string Format { get; set; } + public bool HasLog { get; set; } + public int LogScore { get; set; } + public bool HasCue { get; set; } + public bool Scene { get; set; } + public bool VanityHouse { get; set; } + public int FileCount { get; set; } + public DateTime Time { get; set; } + public string Size { get; set; } + public string Snatches { get; set; } + public string Seeders { get; set; } + public string Leechers { get; set; } + public string Category { get; set; } + public bool IsFreeLeech { get; set; } + public bool IsNeutralLeech { get; set; } + public bool IsPersonalFreeLeech { get; set; } + public bool CanUseToken { get; set; } + } + + public class GazelleRelease + { + public string GroupId { get; set; } + public string GroupName { get; set; } + public string Artist { get; set; } + public string GroupYear { get; set; } + public string Cover { get; set; } + public List Tags { get; set; } + public string ReleaseType { get; set; } + public int TotalLeechers { get; set; } + public int TotalSeeders { get; set; } + public int TotalSnatched { get; set; } + public long MaxSize { get; set; } + public string GroupTime { get; set; } + public List Torrents { get; set; } + } + + public class GazelleResponse + { + public string Status { get; set; } + public GazelleBrowseResponse Response { get; set; } + } + + public class GazelleBrowseResponse + { + public List Results { get; set; } + public string CurrentPage { get; set; } + public string Pages { get; set; } + } + + public class GazelleAuthResponse + { + public string Status { get; set; } + public GazelleIndexResponse Response { get; set; } + } + + public class GazelleIndexResponse + { + public string Username { get; set; } + public string Id { get; set; } + public string Authkey { get; set; } + public string Passkey { get; set; } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleInfo.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleInfo.cs new file mode 100644 index 000000000..2f694c47f --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleInfo.cs @@ -0,0 +1,9 @@ +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Indexers.Gazelle +{ + public class GazelleInfo : TorrentInfo + { + public bool? Scene { get; set; } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleParser.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleParser.cs new file mode 100644 index 000000000..86a632ab9 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleParser.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; +using NzbDrone.Core.Indexers.Exceptions; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Indexers.Gazelle +{ + public class GazelleParser : IParseIndexerResponse + { + private readonly GazelleSettings _settings; + private readonly IndexerCapabilities _capabilities; + private readonly string _baseUrl; + + public GazelleParser(GazelleSettings settings, IndexerCapabilities capabilities, string baseUrl) + { + _settings = settings; + _capabilities = capabilities; + _baseUrl = baseUrl; + } + + public Action, DateTime?> CookiesUpdater { get; set; } + + public IList ParseResponse(IndexerResponse indexerResponse) + { + var torrentInfos = new List(); + + if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) + { + // Remove cookie cache + CookiesUpdater(null, null); + + throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from API request"); + } + + if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value)) + { + // Remove cookie cache + CookiesUpdater(null, null); + + throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}"); + } + + var jsonResponse = new HttpResponse(indexerResponse.HttpResponse); + if (jsonResponse.Resource.Status != "success" || + jsonResponse.Resource.Status.IsNullOrWhiteSpace() || + jsonResponse.Resource.Response == null) + { + return torrentInfos; + } + + foreach (var result in jsonResponse.Resource.Response.Results) + { + if (result.Torrents != null) + { + foreach (var torrent in result.Torrents) + { + var id = torrent.TorrentId; + var artist = WebUtility.HtmlDecode(result.Artist); + var album = WebUtility.HtmlDecode(result.GroupName); + + var title = $"{result.Artist} - {result.GroupName} ({result.GroupYear}) [{torrent.Format} {torrent.Encoding}] [{torrent.Media}]"; + if (torrent.HasCue) + { + title += " [Cue]"; + } + + var release = new GazelleInfo() + { + Guid = string.Format("Gazelle-{0}", id), + Title = WebUtility.HtmlDecode(title), + Container = torrent.Encoding, + Codec = torrent.Format, + Size = long.Parse(torrent.Size), + DownloadUrl = GetDownloadUrl(id), + InfoUrl = GetInfoUrl(result.GroupId, id), + Seeders = int.Parse(torrent.Seeders), + Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders), + PublishDate = torrent.Time.ToUniversalTime(), + Scene = torrent.Scene, + }; + + var category = torrent.Category; + if (category == null || category.Contains("Select Category")) + { + release.Category = _capabilities.Categories.MapTrackerCatToNewznab("1"); + } + else + { + release.Category = _capabilities.Categories.MapTrackerCatDescToNewznab(category); + } + + torrentInfos.Add(release); + } + } + } + + // order by date + return + torrentInfos + .OrderByDescending(o => o.PublishDate) + .ToArray(); + } + + private string GetDownloadUrl(int torrentId) + { + var url = new HttpUri(_baseUrl) + .CombinePath("/torrents.php") + .AddQueryParam("action", "download") + .AddQueryParam("id", torrentId) + .AddQueryParam("authkey", _settings.AuthKey) + .AddQueryParam("torrent_pass", _settings.PassKey); + + return url.FullUri; + } + + private string GetInfoUrl(string groupId, int torrentId) + { + var url = new HttpUri(_baseUrl) + .CombinePath("/torrents.php") + .AddQueryParam("id", groupId) + .AddQueryParam("torrentid", torrentId); + + return url.FullUri; + } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs new file mode 100644 index 000000000..d74cb7152 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using NLog; +using NzbDrone.Common.Cache; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; +using NzbDrone.Common.Serializer; +using NzbDrone.Core.IndexerSearch.Definitions; + +namespace NzbDrone.Core.Indexers.Gazelle +{ + public class GazelleRequestGenerator : IIndexerRequestGenerator + { + public GazelleSettings Settings { get; set; } + public string BaseUrl { get; set; } + + public IDictionary AuthCookieCache { get; set; } + public IHttpClient HttpClient { get; set; } + public IndexerCapabilities Capabilities { get; set; } + public Logger Logger { get; set; } + + protected virtual string LoginUrl => BaseUrl + "login.php"; + protected virtual string APIUrl => BaseUrl + "ajax.php"; + protected virtual string DownloadUrl => BaseUrl + "torrents.php?action=download&usetoken=" + (Settings.UseFreeleechToken ? "1" : "0") + "&id="; + protected virtual string DetailsUrl => BaseUrl + "torrents.php?torrentid="; + protected virtual bool ImdbInTags => false; + + public Func> GetCookies { get; set; } + public Action, DateTime?> CookiesUpdater { get; set; } + + public virtual IndexerPageableRequestChain GetRecentRequests() + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(GetRequest(null)); + + return pageableRequests; + } + + private IEnumerable GetRequest(string searchParameters) + { + AuthCookieCache = GetCookies(); + + Authenticate(); + + var filter = ""; + if (searchParameters == null) + { + } + + var request = + new IndexerRequest( + $"{APIUrl}?action=browse{searchParameters}{filter}", + HttpAccept.Json); + + var cookies = AuthCookieCache; + foreach (var cookie in cookies) + { + request.HttpRequest.Cookies[cookie.Key] = cookie.Value; + } + + yield return request; + } + + private GazelleAuthResponse GetIndex(IDictionary cookies) + { + var indexRequestBuilder = new HttpRequestBuilder($"{APIUrl}?action=index") + { + LogResponseContent = true + }; + + indexRequestBuilder.SetCookies(cookies); + indexRequestBuilder.Method = HttpMethod.POST; + + var authIndexRequest = indexRequestBuilder + .Accept(HttpAccept.Json) + .Build(); + + var indexResponse = HttpClient.Execute(authIndexRequest); + + var result = Json.Deserialize(indexResponse.Content); + + return result; + } + + private void Authenticate() + { + var requestBuilder = new HttpRequestBuilder(LoginUrl) + { + LogResponseContent = true + }; + + requestBuilder.Method = HttpMethod.POST; + requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15); + + var cookies = AuthCookieCache; + + if (cookies == null) + { + AuthCookieCache = null; + var authLoginRequest = requestBuilder + .AddFormParameter("username", Settings.Username) + .AddFormParameter("password", Settings.Password) + .AddFormParameter("keeplogged", "1") + .SetHeader("Content-Type", "multipart/form-data") + .Accept(HttpAccept.Json) + .Build(); + + var response = HttpClient.Execute(authLoginRequest); + + cookies = response.GetCookies(); + + AuthCookieCache = cookies; + CookiesUpdater(cookies, DateTime.Now + TimeSpan.FromDays(30)); + } + + var index = GetIndex(cookies); + + if (index == null || index.Status.IsNullOrWhiteSpace() || index.Status != "success") + { + Logger.Debug("Gazelle authentication failed."); + AuthCookieCache = null; + CookiesUpdater(null, null); + throw new Exception("Failed to authenticate with Gazelle."); + } + + Logger.Debug("Gazelle authentication succeeded."); + + Settings.AuthKey = index.Response.Authkey; + Settings.PassKey = index.Response.Passkey; + } + + private string GetBasicSearchParameters(string searchTerm, int[] categories) + { + var searchString = GetSearchTerm(searchTerm); + + var parameters = "&action=browse&order_by=time&order_way=desc"; + + if (!string.IsNullOrWhiteSpace(searchString)) + { + parameters += string.Format("&searchstr={0}", searchString); + } + + if (categories != null) + { + foreach (var cat in Capabilities.Categories.MapTorznabCapsToTrackers(categories)) + { + parameters += string.Format("&filter_cat[{0}]=1", cat); + } + } + + return parameters; + } + + public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) + { + var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories); + + if (searchCriteria.ImdbId != null) + { + if (ImdbInTags) + { + parameters += string.Format("&taglist={0}", searchCriteria.ImdbId); + } + else + { + parameters += string.Format("&cataloguenumber={0}", searchCriteria.ImdbId); + } + } + + var pageableRequests = new IndexerPageableRequestChain(); + pageableRequests.Add(GetRequest(parameters)); + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories); + + if (searchCriteria.Artist != null) + { + parameters += string.Format("&artistname={0}", searchCriteria.Artist); + } + + if (searchCriteria.Label != null) + { + parameters += string.Format("&recordlabel={0}", searchCriteria.Label); + } + + if (searchCriteria.Album != null) + { + parameters += string.Format("&groupname={0}", searchCriteria.Album); + } + + var pageableRequests = new IndexerPageableRequestChain(); + pageableRequests.Add(GetRequest(parameters)); + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories); + + if (searchCriteria.ImdbId != null) + { + if (ImdbInTags) + { + parameters += string.Format("&taglist={0}", searchCriteria.ImdbId); + } + else + { + parameters += string.Format("&cataloguenumber={0}", searchCriteria.ImdbId); + } + } + + var pageableRequests = new IndexerPageableRequestChain(); + pageableRequests.Add(GetRequest(parameters)); + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + throw new NotImplementedException(); + } + + // hook to adjust the search term + protected virtual string GetSearchTerm(string term) => term; + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories); + + var pageableRequests = new IndexerPageableRequestChain(); + pageableRequests.Add(GetRequest(parameters)); + return pageableRequests; + } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleSettings.cs new file mode 100644 index 000000000..8c4658452 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleSettings.cs @@ -0,0 +1,42 @@ +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.Indexers.Gazelle +{ + public class GazelleSettingsValidator : AbstractValidator + { + public GazelleSettingsValidator() + { + RuleFor(c => c.Username).NotEmpty(); + RuleFor(c => c.Password).NotEmpty(); + } + } + + public class GazelleSettings : IProviderConfig + { + private static readonly GazelleSettingsValidator Validator = new GazelleSettingsValidator(); + + public GazelleSettings() + { + } + + public string AuthKey; + public string PassKey; + + [FieldDefinition(1, Label = "Username", HelpText = "Username", Privacy = PrivacyLevel.UserName)] + public string Username { get; set; } + + [FieldDefinition(2, Label = "Password", Type = FieldType.Password, HelpText = "Password", Privacy = PrivacyLevel.Password)] + public string Password { get; set; } + + [FieldDefinition(3, Type = FieldType.Checkbox, Label = "Use Freeleech Token", HelpText = "Use Freeleach Token")] + public bool UseFreeleechToken { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBits.cs b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBits.cs index bfbf82d68..f498f0018 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBits.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBits.cs @@ -7,6 +7,7 @@ namespace NzbDrone.Core.Indexers.HDBits public class HDBits : HttpIndexerBase { public override string Name => "HDBits"; + public override string BaseUrl => "https://hdbits.org"; public override DownloadProtocol Protocol => DownloadProtocol.Torrent; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; @@ -19,12 +20,12 @@ namespace NzbDrone.Core.Indexers.HDBits public override IIndexerRequestGenerator GetRequestGenerator() { - return new HDBitsRequestGenerator() { Settings = Settings }; + return new HDBitsRequestGenerator() { Settings = Settings, BaseUrl = BaseUrl }; } public override IParseIndexerResponse GetParser() { - return new HDBitsParser(Settings); + return new HDBitsParser(Settings, BaseUrl); } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsParser.cs b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsParser.cs index a772e9f74..5bba237a9 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsParser.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Net; using Newtonsoft.Json; @@ -11,11 +11,13 @@ namespace NzbDrone.Core.Indexers.HDBits { public class HDBitsParser : IParseIndexerResponse { + private readonly string _baseUrl; private readonly HDBitsSettings _settings; - public HDBitsParser(HDBitsSettings settings) + public HDBitsParser(HDBitsSettings settings, string baseUrl) { _settings = settings; + _baseUrl = baseUrl; } public IList ParseResponse(IndexerResponse indexerResponse) @@ -89,7 +91,7 @@ namespace NzbDrone.Core.Indexers.HDBits private string GetDownloadUrl(string torrentId) { - var url = new HttpUri(_settings.BaseUrl) + var url = new HttpUri(_baseUrl) .CombinePath("/download.php") .AddQueryParam("id", torrentId) .AddQueryParam("passkey", _settings.ApiKey); @@ -99,7 +101,7 @@ namespace NzbDrone.Core.Indexers.HDBits private string GetInfoUrl(string torrentId) { - var url = new HttpUri(_settings.BaseUrl) + var url = new HttpUri(_baseUrl) .CombinePath("/details.php") .AddQueryParam("id", torrentId); diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsRequestGenerator.cs index 5fb60237a..1c194ef8c 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsRequestGenerator.cs @@ -11,6 +11,7 @@ namespace NzbDrone.Core.Indexers.HDBits public class HDBitsRequestGenerator : IIndexerRequestGenerator { public HDBitsSettings Settings { get; set; } + public string BaseUrl { get; set; } public virtual IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) { @@ -52,7 +53,7 @@ namespace NzbDrone.Core.Indexers.HDBits private IEnumerable GetRequest(TorrentQuery query) { - var request = new HttpRequestBuilder(Settings.BaseUrl) + var request = new HttpRequestBuilder(BaseUrl) .Resource("/api/torrents") .Build(); diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsSettings.cs index 487ebe65d..36b739b19 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsSettings.cs @@ -1,8 +1,7 @@ using System.Collections.Generic; using FluentValidation; using NzbDrone.Core.Annotations; -using NzbDrone.Core.Languages; -using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.HDBits @@ -11,18 +10,16 @@ namespace NzbDrone.Core.Indexers.HDBits { public HDBitsSettingsValidator() { - RuleFor(c => c.BaseUrl).ValidRootUrl(); RuleFor(c => c.ApiKey).NotEmpty(); } } - public class HDBitsSettings : IIndexerSettings + public class HDBitsSettings : IProviderConfig { private static readonly HDBitsSettingsValidator Validator = new HDBitsSettingsValidator(); public HDBitsSettings() { - BaseUrl = "https://hdbits.org"; Codecs = System.Array.Empty(); Mediums = System.Array.Empty(); } @@ -33,9 +30,6 @@ namespace NzbDrone.Core.Indexers.HDBits [FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey)] public string ApiKey { get; set; } - [FieldDefinition(3, Label = "API URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since your API key will be sent to that host.")] - public string BaseUrl { get; set; } - [FieldDefinition(5, Label = "Codecs", Type = FieldType.TagSelect, SelectOptions = typeof(HdBitsCodec), Advanced = true, HelpText = "Options: h264, Mpeg2, VC1, Xvid. If unspecified, all options are used.")] public IEnumerable Codecs { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/IPTorrents/IPTorrents.cs b/src/NzbDrone.Core/Indexers/Definitions/IPTorrents/IPTorrents.cs deleted file mode 100644 index cd5dfa28c..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/IPTorrents/IPTorrents.cs +++ /dev/null @@ -1,32 +0,0 @@ -using NLog; -using NzbDrone.Common.Http; -using NzbDrone.Core.Configuration; - -namespace NzbDrone.Core.Indexers.IPTorrents -{ - public class IPTorrents : HttpIndexerBase - { - public override string Name => "IP Torrents"; - - public override DownloadProtocol Protocol => DownloadProtocol.Torrent; - public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public override bool SupportsSearch => false; - - public override int PageSize => 0; - - public IPTorrents(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) - : base(httpClient, indexerStatusService, configService, logger) - { - } - - public override IIndexerRequestGenerator GetRequestGenerator() - { - return new IPTorrentsRequestGenerator() { Settings = Settings }; - } - - public override IParseIndexerResponse GetParser() - { - return new TorrentRssParser() { ParseSizeInDescription = true }; - } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/IPTorrents/IPTorrentsRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/IPTorrents/IPTorrentsRequestGenerator.cs deleted file mode 100644 index 5789c8e58..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/IPTorrents/IPTorrentsRequestGenerator.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using NzbDrone.Common.Extensions; -using NzbDrone.Common.Http; -using NzbDrone.Core.IndexerSearch.Definitions; - -namespace NzbDrone.Core.Indexers.IPTorrents -{ - public class IPTorrentsRequestGenerator : IIndexerRequestGenerator - { - public IPTorrentsSettings Settings { get; set; } - - public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) - { - var pageableRequests = new IndexerPageableRequestChain(); - - if (searchCriteria.SearchTerm.IsNullOrWhiteSpace()) - { - pageableRequests.Add(GetRssRequests()); - } - - return pageableRequests; - } - - private IEnumerable GetRssRequests() - { - yield return new IndexerRequest(Settings.BaseUrl, HttpAccept.Rss); - } - - public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public Func> GetCookies { get; set; } - public Action, DateTime?> CookiesUpdater { get; set; } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/IPTorrents/IPTorrentsSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/IPTorrents/IPTorrentsSettings.cs deleted file mode 100644 index 08980f5f6..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/IPTorrents/IPTorrentsSettings.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; -using FluentValidation; -using NzbDrone.Common.Extensions; -using NzbDrone.Core.Annotations; -using NzbDrone.Core.Languages; -using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Validation; - -namespace NzbDrone.Core.Indexers.IPTorrents -{ - public class IPTorrentsSettingsValidator : AbstractValidator - { - public IPTorrentsSettingsValidator() - { - RuleFor(c => c.BaseUrl).ValidRootUrl(); - - RuleFor(c => c.BaseUrl).Matches(@"(?:/|t\.)rss\?.+$"); - - RuleFor(c => c.BaseUrl).Matches(@"(?:/|t\.)rss\?.+;download(?:;|$)") - .WithMessage("Use Direct Download Url (;download)") - .When(v => v.BaseUrl.IsNotNullOrWhiteSpace() && Regex.IsMatch(v.BaseUrl, @"(?:/|t\.)rss\?.+$")); - } - } - - public class IPTorrentsSettings : IIndexerSettings - { - private static readonly IPTorrentsSettingsValidator Validator = new IPTorrentsSettingsValidator(); - - public IPTorrentsSettings() - { - BaseUrl = string.Empty; - } - - [FieldDefinition(0, Label = "Feed URL", HelpText = "The full RSS feed url generated by IPTorrents, using only the categories you selected (HD, SD, x264, etc ...)")] - public string BaseUrl { get; set; } - - public NzbDroneValidationResult Validate() - { - return new NzbDroneValidationResult(Validator.Validate(this)); - } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/Newznab/Newznab.cs b/src/NzbDrone.Core/Indexers/Definitions/Newznab/Newznab.cs index 0ae2ea61d..0edbf3f49 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Newznab/Newznab.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Newznab/Newznab.cs @@ -16,6 +16,7 @@ namespace NzbDrone.Core.Indexers.Newznab private readonly INewznabCapabilitiesProvider _capabilitiesProvider; public override string Name => "Newznab"; + public override string BaseUrl => Settings.BaseUrl; public override DownloadProtocol Protocol => DownloadProtocol.Usenet; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; diff --git a/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabSettings.cs index 41a56b471..db3c74bca 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabSettings.cs @@ -5,6 +5,7 @@ using FluentValidation; using NzbDrone.Common.Extensions; using NzbDrone.Core.Annotations; using NzbDrone.Core.Languages; +using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.Newznab @@ -44,7 +45,7 @@ namespace NzbDrone.Core.Indexers.Newznab } } - public class NewznabSettings : IIndexerSettings + public class NewznabSettings : IProviderConfig { private static readonly NewznabSettingsValidator Validator = new NewznabSettingsValidator(); diff --git a/src/NzbDrone.Core/Indexers/Definitions/Nyaa/Nyaa.cs b/src/NzbDrone.Core/Indexers/Definitions/Nyaa/Nyaa.cs deleted file mode 100644 index 1b93fc616..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/Nyaa/Nyaa.cs +++ /dev/null @@ -1,31 +0,0 @@ -using NLog; -using NzbDrone.Common.Http; -using NzbDrone.Core.Configuration; - -namespace NzbDrone.Core.Indexers.Nyaa -{ - public class Nyaa : HttpIndexerBase - { - public override string Name => "Nyaa"; - - public override DownloadProtocol Protocol => DownloadProtocol.Torrent; - - public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public override int PageSize => 100; - - public Nyaa(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) - : base(httpClient, indexerStatusService, configService, logger) - { - } - - public override IIndexerRequestGenerator GetRequestGenerator() - { - return new NyaaRequestGenerator() { Settings = Settings, PageSize = PageSize }; - } - - public override IParseIndexerResponse GetParser() - { - return new TorrentRssParser() { UseGuidInfoUrl = true, ParseSizeInDescription = true, ParseSeedersInDescription = true }; - } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/Nyaa/NyaaRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Nyaa/NyaaRequestGenerator.cs deleted file mode 100644 index 0d9e03bf5..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/Nyaa/NyaaRequestGenerator.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Collections.Generic; -using NzbDrone.Common.Http; -using NzbDrone.Core.IndexerSearch.Definitions; - -namespace NzbDrone.Core.Indexers.Nyaa -{ - public class NyaaRequestGenerator : IIndexerRequestGenerator - { - public NyaaSettings Settings { get; set; } - - public int MaxPages { get; set; } - public int PageSize { get; set; } - - public NyaaRequestGenerator() - { - MaxPages = 30; - PageSize = 100; - } - - private IEnumerable GetPagedRequests(int maxPages, string term) - { - var baseUrl = string.Format("{0}/?page=rss{1}", Settings.BaseUrl.TrimEnd('/'), Settings.AdditionalParameters); - - if (term != null) - { - baseUrl += "&term=" + term; - } - - if (PageSize == 0) - { - yield return new IndexerRequest(baseUrl, HttpAccept.Rss); - } - else - { - yield return new IndexerRequest(baseUrl, HttpAccept.Rss); - } - } - - private string PrepareQuery(string query) - { - return query.Replace(' ', '+'); - } - - public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) - { - var pageableRequests = new IndexerPageableRequestChain(); - - pageableRequests.Add(GetPagedRequests(MaxPages, PrepareQuery(string.Format("{0}", searchCriteria.SearchTerm)))); - - return pageableRequests; - } - - public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public Func> GetCookies { get; set; } - public Action, DateTime?> CookiesUpdater { get; set; } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/Nyaa/NyaaSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/Nyaa/NyaaSettings.cs deleted file mode 100644 index a3d76ac15..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/Nyaa/NyaaSettings.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Text.RegularExpressions; -using FluentValidation; -using NzbDrone.Core.Annotations; -using NzbDrone.Core.Validation; - -namespace NzbDrone.Core.Indexers.Nyaa -{ - public class NyaaSettingsValidator : AbstractValidator - { - public NyaaSettingsValidator() - { - RuleFor(c => c.BaseUrl).ValidRootUrl(); - RuleFor(c => c.AdditionalParameters).Matches("(&[a-z]+=[a-z0-9_]+)*", RegexOptions.IgnoreCase); - } - } - - public class NyaaSettings : IIndexerSettings - { - private static readonly NyaaSettingsValidator Validator = new NyaaSettingsValidator(); - - public NyaaSettings() - { - BaseUrl = ""; - AdditionalParameters = ""; - } - - [FieldDefinition(0, Label = "Website URL")] - public string BaseUrl { get; set; } - - [FieldDefinition(1, Label = "Additional Parameters", Advanced = true, HelpText = "Please note if you change the category you will have to add required/restricted rules about the subgroups to avoid foreign language releases.")] - public string AdditionalParameters { get; set; } - - public NzbDroneValidationResult Validate() - { - return new NzbDroneValidationResult(Validator.Validate(this)); - } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs index 617ebc45b..246aee811 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs @@ -9,6 +9,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn public class PassThePopcorn : HttpIndexerBase { public override string Name => "PassThePopcorn"; + public override string BaseUrl => "https://passthepopcorn.me"; public override DownloadProtocol Protocol => DownloadProtocol.Torrent; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override bool SupportsRss => true; @@ -34,6 +35,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn Settings = Settings, HttpClient = _httpClient, Logger = _logger, + BaseUrl = BaseUrl }; } @@ -70,7 +72,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn public override IParseIndexerResponse GetParser() { - return new PassThePopcornParser(Settings, _logger); + return new PassThePopcornParser(BaseUrl, _logger); } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornParser.cs b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornParser.cs index 13860ce22..0f1160d6c 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornParser.cs @@ -12,11 +12,11 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn { public class PassThePopcornParser : IParseIndexerResponse { - private readonly PassThePopcornSettings _settings; + private readonly string _baseUrl; private readonly Logger _logger; - public PassThePopcornParser(PassThePopcornSettings settings, Logger logger) + public PassThePopcornParser(string baseUrl, Logger logger) { - _settings = settings; + _baseUrl = baseUrl; _logger = logger; } @@ -129,7 +129,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn private string GetDownloadUrl(int torrentId, string authKey, string passKey) { - var url = new HttpUri(_settings.BaseUrl) + var url = new HttpUri(_baseUrl) .CombinePath("/torrents.php") .AddQueryParam("action", "download") .AddQueryParam("id", torrentId) @@ -141,7 +141,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn private string GetInfoUrl(string groupId, int torrentId) { - var url = new HttpUri(_settings.BaseUrl) + var url = new HttpUri(_baseUrl) .CombinePath("/torrents.php") .AddQueryParam("id", groupId) .AddQueryParam("torrentid", torrentId); diff --git a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs index 77fb4c6ee..99228224f 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs @@ -9,6 +9,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn { public class PassThePopcornRequestGenerator : IIndexerRequestGenerator { + public string BaseUrl { get; set; } public PassThePopcornSettings Settings { get; set; } public IDictionary Cookies { get; set; } @@ -39,7 +40,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn { var request = new IndexerRequest( - $"{Settings.BaseUrl.Trim().TrimEnd('/')}/torrents.php?action=advanced&json=noredirect&searchstr={searchParameters}", + $"{BaseUrl.Trim().TrimEnd('/')}/torrents.php?action=advanced&json=noredirect&searchstr={searchParameters}", HttpAccept.Json); request.HttpRequest.Headers["ApiUser"] = Settings.APIUser; diff --git a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornSettings.cs index b550a662b..0d532dae4 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornSettings.cs @@ -3,6 +3,7 @@ using FluentValidation; using NzbDrone.Core.Annotations; using NzbDrone.Core.Languages; using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.PassThePopcorn @@ -11,23 +12,19 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn { public PassThePopcornSettingsValidator() { - RuleFor(c => c.BaseUrl).ValidRootUrl(); RuleFor(c => c.APIUser).NotEmpty(); RuleFor(c => c.APIKey).NotEmpty(); } } - public class PassThePopcornSettings : IIndexerSettings + public class PassThePopcornSettings : IProviderConfig { private static readonly PassThePopcornSettingsValidator Validator = new PassThePopcornSettingsValidator(); public PassThePopcornSettings() { - BaseUrl = "https://passthepopcorn.me"; } - public string BaseUrl { get; set; } - [FieldDefinition(0, Label = "APIUser", HelpText = "These settings are found in your PassThePopcorn security settings (Edit Profile > Security).", Privacy = PrivacyLevel.UserName)] public string APIUser { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Rarbg/Rarbg.cs b/src/NzbDrone.Core/Indexers/Definitions/Rarbg/Rarbg.cs index 78533536a..fe17da465 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Rarbg/Rarbg.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Rarbg/Rarbg.cs @@ -15,6 +15,7 @@ namespace NzbDrone.Core.Indexers.Rarbg private readonly IRarbgTokenProvider _tokenProvider; public override string Name => "Rarbg"; + public override string BaseUrl => "https://torrentapi.org"; public override DownloadProtocol Protocol => DownloadProtocol.Torrent; @@ -50,7 +51,7 @@ namespace NzbDrone.Core.Indexers.Rarbg public override IIndexerRequestGenerator GetRequestGenerator() { - return new RarbgRequestGenerator(_tokenProvider) { Settings = Settings }; + return new RarbgRequestGenerator(_tokenProvider) { Settings = Settings, BaseUrl = BaseUrl }; } public override IParseIndexerResponse GetParser() @@ -66,7 +67,7 @@ namespace NzbDrone.Core.Indexers.Rarbg try { - var request = new HttpRequestBuilder(Settings.BaseUrl.Trim('/')) + var request = new HttpRequestBuilder(BaseUrl.Trim('/')) .Resource("/pubapi_v2.php?get_token=get_token") .Accept(HttpAccept.Json) .Build(); diff --git a/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgRequestGenerator.cs index 228246f66..5088498fe 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgRequestGenerator.cs @@ -12,6 +12,7 @@ namespace NzbDrone.Core.Indexers.Rarbg { private readonly IRarbgTokenProvider _tokenProvider; + public string BaseUrl { get; set; } public RarbgSettings Settings { get; set; } public RarbgRequestGenerator(IRarbgTokenProvider tokenProvider) @@ -28,7 +29,7 @@ namespace NzbDrone.Core.Indexers.Rarbg private IEnumerable GetMovieRequest(MovieSearchCriteria searchCriteria) { - var requestBuilder = new HttpRequestBuilder(Settings.BaseUrl) + var requestBuilder = new HttpRequestBuilder(BaseUrl) .Resource("/pubapi_v2.php") .Accept(HttpAccept.Json); @@ -62,7 +63,7 @@ namespace NzbDrone.Core.Indexers.Rarbg requestBuilder.AddQueryParam("category", categoryParam); requestBuilder.AddQueryParam("limit", "100"); - requestBuilder.AddQueryParam("token", _tokenProvider.GetToken(Settings)); + requestBuilder.AddQueryParam("token", _tokenProvider.GetToken(Settings, BaseUrl)); requestBuilder.AddQueryParam("format", "json_extended"); requestBuilder.AddQueryParam("app_id", BuildInfo.AppName); diff --git a/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgSettings.cs index 10fa4655f..796e1bee2 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgSettings.cs @@ -3,6 +3,7 @@ using FluentValidation; using NzbDrone.Core.Annotations; using NzbDrone.Core.Languages; using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.Rarbg @@ -11,22 +12,18 @@ namespace NzbDrone.Core.Indexers.Rarbg { public RarbgSettingsValidator() { - RuleFor(c => c.BaseUrl).ValidRootUrl(); } } - public class RarbgSettings : IIndexerSettings + public class RarbgSettings : IProviderConfig { private static readonly RarbgSettingsValidator Validator = new RarbgSettingsValidator(); public RarbgSettings() { - BaseUrl = "https://torrentapi.org"; RankedOnly = false; } - public string BaseUrl { get; set; } - [FieldDefinition(1, Type = FieldType.Checkbox, Label = "Ranked Only", HelpText = "Only include ranked results.")] public bool RankedOnly { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgTokenProvider.cs b/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgTokenProvider.cs index 151ad6fe1..a4686ee82 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgTokenProvider.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgTokenProvider.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Core.Indexers.Rarbg { public interface IRarbgTokenProvider { - string GetToken(RarbgSettings settings); + string GetToken(RarbgSettings settings, string baseUrl); } public class RarbgTokenProvider : IRarbgTokenProvider @@ -25,12 +25,12 @@ namespace NzbDrone.Core.Indexers.Rarbg _logger = logger; } - public string GetToken(RarbgSettings settings) + public string GetToken(RarbgSettings settings, string baseUrl) { - return _tokenCache.Get(settings.BaseUrl, + return _tokenCache.Get(baseUrl, () => { - var requestBuilder = new HttpRequestBuilder(settings.BaseUrl.Trim('/')) + var requestBuilder = new HttpRequestBuilder(baseUrl.Trim('/')) .WithRateLimit(3.0) .Resource("/pubapi_v2.php?get_token=get_token&app_id=Prowlarr") .Accept(HttpAccept.Json); diff --git a/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs b/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs new file mode 100644 index 000000000..992dd40e4 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using NLog; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; + +namespace NzbDrone.Core.Indexers.Definitions +{ + public class Redacted : Gazelle.Gazelle + { + public override string Name => "Redacted"; + public override string BaseUrl => "https://redacted.ch/"; + public override IndexerPrivacy Privacy => IndexerPrivacy.Private; + + public Redacted(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + : base(httpClient, indexerStatusService, configService, logger) + { + } + + protected override IndexerCapabilities SetCapabilities() + { + var caps = new IndexerCapabilities + { + TvSearchParams = new List + { + TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep + }, + MovieSearchParams = new List + { + MovieSearchParam.Q + }, + MusicSearchParams = new List + { + MusicSearchParam.Q, MusicSearchParam.Album, MusicSearchParam.Artist, MusicSearchParam.Label, MusicSearchParam.Year + }, + BookSearchParams = new List + { + BookSearchParam.Q + } + }; + + caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Audio, "Music"); + caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.PC, "Applications"); + caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Books, "E-Books"); + caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.AudioAudiobook, "Audiobooks"); + caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.Movies, "E-Learning Videos"); + caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.TV, "Comedy"); + caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.Books, "Comics"); + + return caps; + } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotato.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotato.cs index cbbcc99f2..eef8f181b 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotato.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotato.cs @@ -8,6 +8,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato public class TorrentPotato : HttpIndexerBase { public override string Name => "TorrentPotato"; + public override string BaseUrl => "http://127.0.0.1"; public override DownloadProtocol Protocol => DownloadProtocol.Torrent; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; @@ -36,7 +37,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato public override IIndexerRequestGenerator GetRequestGenerator() { - return new TorrentPotatoRequestGenerator() { Settings = Settings }; + return new TorrentPotatoRequestGenerator() { Settings = Settings, BaseUrl = BaseUrl }; } public override IParseIndexerResponse GetParser() diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoRequestGenerator.cs index 5e45956fd..d29d42fb6 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoRequestGenerator.cs @@ -9,6 +9,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato { public class TorrentPotatoRequestGenerator : IIndexerRequestGenerator { + public string BaseUrl { get; set; } public TorrentPotatoSettings Settings { get; set; } public TorrentPotatoRequestGenerator() @@ -26,7 +27,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato private IEnumerable GetPagedRequests(string mode, int? tvdbId, string query, params object[] args) { - var requestBuilder = new HttpRequestBuilder(Settings.BaseUrl) + var requestBuilder = new HttpRequestBuilder(BaseUrl) .Accept(HttpAccept.Json); requestBuilder.AddQueryParam("passkey", Settings.Passkey); @@ -46,7 +47,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato private IEnumerable GetMovieRequest(MovieSearchCriteria searchCriteria) { - var requestBuilder = new HttpRequestBuilder(Settings.BaseUrl) + var requestBuilder = new HttpRequestBuilder(BaseUrl) .Accept(HttpAccept.Json); requestBuilder.AddQueryParam("passkey", Settings.Passkey); diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoSettings.cs index 67c120686..25cf09f38 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoSettings.cs @@ -1,8 +1,6 @@ -using System.Collections.Generic; using FluentValidation; using NzbDrone.Core.Annotations; -using NzbDrone.Core.Languages; -using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.TorrentPotato @@ -11,22 +9,17 @@ namespace NzbDrone.Core.Indexers.TorrentPotato { public TorrentPotatoSettingsValidator() { - RuleFor(c => c.BaseUrl).ValidRootUrl(); } } - public class TorrentPotatoSettings : IIndexerSettings + public class TorrentPotatoSettings : IProviderConfig { private static readonly TorrentPotatoSettingsValidator Validator = new TorrentPotatoSettingsValidator(); public TorrentPotatoSettings() { - BaseUrl = "http://127.0.0.1"; } - [FieldDefinition(0, Label = "API URL", HelpText = "URL to TorrentPotato api.")] - public string BaseUrl { get; set; } - [FieldDefinition(1, Label = "Username", HelpText = "The username you use at your indexer.", Privacy = PrivacyLevel.UserName)] public string User { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexer.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexer.cs deleted file mode 100644 index 01bf6ba7b..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexer.cs +++ /dev/null @@ -1,34 +0,0 @@ -using NLog; -using NzbDrone.Common.Http; -using NzbDrone.Core.Configuration; - -namespace NzbDrone.Core.Indexers.TorrentRss -{ - public class TorrentRssIndexer : HttpIndexerBase - { - public override string Name => "Torrent RSS Feed"; - - public override DownloadProtocol Protocol => DownloadProtocol.Torrent; - public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public override bool SupportsSearch => false; - public override int PageSize => 0; - - private readonly ITorrentRssParserFactory _torrentRssParserFactory; - - public TorrentRssIndexer(ITorrentRssParserFactory torrentRssParserFactory, IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) - : base(httpClient, indexerStatusService, configService, logger) - { - _torrentRssParserFactory = torrentRssParserFactory; - } - - public override IIndexerRequestGenerator GetRequestGenerator() - { - return new TorrentRssIndexerRequestGenerator { Settings = Settings }; - } - - public override IParseIndexerResponse GetParser() - { - return _torrentRssParserFactory.GetParser(Settings); - } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexerParserSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexerParserSettings.cs deleted file mode 100644 index 0167a1a3b..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexerParserSettings.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace NzbDrone.Core.Indexers.TorrentRss -{ - public class TorrentRssIndexerParserSettings - { - public bool UseEZTVFormat { get; set; } - public bool ParseSeedersInDescription { get; set; } - public bool UseEnclosureUrl { get; set; } - public bool UseEnclosureLength { get; set; } - public bool ParseSizeInDescription { get; set; } - public string SizeElementName { get; set; } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexerRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexerRequestGenerator.cs deleted file mode 100644 index 0d01abf28..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexerRequestGenerator.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using NzbDrone.Common.Extensions; -using NzbDrone.Common.Http; -using NzbDrone.Core.IndexerSearch.Definitions; - -namespace NzbDrone.Core.Indexers.TorrentRss -{ - public class TorrentRssIndexerRequestGenerator : IIndexerRequestGenerator - { - public TorrentRssIndexerSettings Settings { get; set; } - - public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) - { - var pageableRequests = new IndexerPageableRequestChain(); - - if (searchCriteria.SearchTerm.IsNullOrWhiteSpace()) - { - pageableRequests.Add(GetRssRequests(null)); - } - - return pageableRequests; - } - - private IEnumerable GetRssRequests(string searchParameters) - { - var request = new IndexerRequest(Settings.BaseUrl.Trim().TrimEnd('/'), HttpAccept.Rss); - - if (Settings.Cookie.IsNotNullOrWhiteSpace()) - { - foreach (var cookie in HttpHeader.ParseCookies(Settings.Cookie)) - { - request.HttpRequest.Cookies[cookie.Key] = cookie.Value; - } - } - - yield return request; - } - - public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public Func> GetCookies { get; set; } - public Action, DateTime?> CookiesUpdater { get; set; } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexerSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexerSettings.cs deleted file mode 100644 index 48a0f0d06..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexerSettings.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Collections.Generic; -using FluentValidation; -using NzbDrone.Core.Annotations; -using NzbDrone.Core.Languages; -using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Validation; - -namespace NzbDrone.Core.Indexers.TorrentRss -{ - public class TorrentRssIndexerSettingsValidator : AbstractValidator - { - public TorrentRssIndexerSettingsValidator() - { - RuleFor(c => c.BaseUrl).ValidRootUrl(); - } - } - - public class TorrentRssIndexerSettings : IIndexerSettings - { - private static readonly TorrentRssIndexerSettingsValidator Validator = new TorrentRssIndexerSettingsValidator(); - - public TorrentRssIndexerSettings() - { - BaseUrl = string.Empty; - AllowZeroSize = false; - } - - [FieldDefinition(0, Label = "Full RSS Feed URL")] - public string BaseUrl { get; set; } - - [FieldDefinition(1, Label = "Cookie", HelpText = "If you site requires a login cookie to access the rss, you'll have to retrieve it via a browser.")] - public string Cookie { get; set; } - - [FieldDefinition(2, Type = FieldType.Checkbox, Label = "Allow Zero Size", HelpText = "Enabling this will allow you to use feeds that don't specify release size, but be careful, size related checks will not be performed.")] - public bool AllowZeroSize { get; set; } - - public NzbDroneValidationResult Validate() - { - return new NzbDroneValidationResult(Validator.Validate(this)); - } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssParserFactory.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssParserFactory.cs deleted file mode 100644 index d7b58cda7..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssParserFactory.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using NLog; -using NzbDrone.Common.Cache; -using NzbDrone.Common.Serializer; -using NzbDrone.Core.Indexers.Exceptions; - -namespace NzbDrone.Core.Indexers.TorrentRss -{ - public interface ITorrentRssParserFactory - { - TorrentRssParser GetParser(TorrentRssIndexerSettings settings); - } - - public class TorrentRssParserFactory : ITorrentRssParserFactory - { - protected readonly Logger _logger; - - private readonly ICached _settingsCache; - - private readonly ITorrentRssSettingsDetector _torrentRssSettingsDetector; - - public TorrentRssParserFactory(ICacheManager cacheManager, ITorrentRssSettingsDetector torrentRssSettingsDetector, Logger logger) - { - _settingsCache = cacheManager.GetCache(GetType()); - _torrentRssSettingsDetector = torrentRssSettingsDetector; - _logger = logger; - } - - public TorrentRssParser GetParser(TorrentRssIndexerSettings indexerSettings) - { - var key = indexerSettings.ToJson(); - var parserSettings = _settingsCache.Get(key, () => DetectParserSettings(indexerSettings), TimeSpan.FromDays(7)); - - if (parserSettings.UseEZTVFormat) - { - return new EzrssTorrentRssParser(); - } - else - { - return new TorrentRssParser - { - UseGuidInfoUrl = false, - ParseSeedersInDescription = parserSettings.ParseSeedersInDescription, - - UseEnclosureUrl = parserSettings.UseEnclosureUrl, - UseEnclosureLength = parserSettings.UseEnclosureLength, - ParseSizeInDescription = parserSettings.ParseSizeInDescription, - SizeElementName = parserSettings.SizeElementName - }; - } - } - - private TorrentRssIndexerParserSettings DetectParserSettings(TorrentRssIndexerSettings indexerSettings) - { - var settings = _torrentRssSettingsDetector.Detect(indexerSettings); - - if (settings == null) - { - throw new UnsupportedFeedException("Could not parse feed from {0}", indexerSettings.BaseUrl); - } - - return settings; - } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssSettingsDetector.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssSettingsDetector.cs deleted file mode 100644 index 34e1bfece..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssSettingsDetector.cs +++ /dev/null @@ -1,307 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Xml; -using System.Xml.Linq; -using NLog; -using NzbDrone.Common.Extensions; -using NzbDrone.Common.Http; -using NzbDrone.Core.Indexers.Exceptions; -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.Indexers.TorrentRss -{ - public interface ITorrentRssSettingsDetector - { - TorrentRssIndexerParserSettings Detect(TorrentRssIndexerSettings settings); - } - - public class TorrentRssSettingsDetector : ITorrentRssSettingsDetector - { - private const long ValidSizeThreshold = 2 * 1024 * 1024; - - protected readonly Logger _logger; - - private readonly IHttpClient _httpClient; - - public TorrentRssSettingsDetector(IHttpClient httpClient, Logger logger) - { - _httpClient = httpClient; - _logger = logger; - } - - /// - /// Detect settings for Parser, based on URL - /// - /// Indexer Settings to use for Parser - /// Parsed Settings or null - public TorrentRssIndexerParserSettings Detect(TorrentRssIndexerSettings indexerSettings) - { - _logger.Debug("Evaluating TorrentRss feed '{0}'", indexerSettings.BaseUrl); - - var requestGenerator = new TorrentRssIndexerRequestGenerator { Settings = indexerSettings }; - var request = requestGenerator.GetSearchRequests(new IndexerSearch.Definitions.MovieSearchCriteria()).GetAllTiers().First().First(); - - HttpResponse httpResponse = null; - try - { - httpResponse = _httpClient.Execute(request.HttpRequest); - } - catch (Exception ex) - { - _logger.Warn(ex, string.Format("Unable to connect to indexer {0}: {1}", request.Url, ex.Message)); - return null; - } - - var indexerResponse = new IndexerResponse(request, httpResponse); - return GetParserSettings(indexerResponse, indexerSettings); - } - - private TorrentRssIndexerParserSettings GetParserSettings(IndexerResponse response, TorrentRssIndexerSettings indexerSettings) - { - var settings = GetEzrssParserSettings(response, indexerSettings); - if (settings != null) - { - return settings; - } - - settings = GetGenericTorrentRssParserSettings(response, indexerSettings); - if (settings != null) - { - return settings; - } - - return null; - } - - private TorrentRssIndexerParserSettings GetEzrssParserSettings(IndexerResponse response, TorrentRssIndexerSettings indexerSettings) - { - if (!IsEZTVFeed(response)) - { - return null; - } - - _logger.Trace("Feed has Ezrss schema"); - - var parser = new EzrssTorrentRssParser(); - var releases = ParseResponse(parser, response); - - try - { - ValidateReleases(releases, indexerSettings); - ValidateReleaseSize(releases, indexerSettings); - - _logger.Debug("Feed was parseable by Ezrss Parser"); - return new TorrentRssIndexerParserSettings - { - UseEZTVFormat = true - }; - } - catch (Exception ex) - { - _logger.Trace(ex, "Feed wasn't parsable by Ezrss Parser"); - return null; - } - } - - private TorrentRssIndexerParserSettings GetGenericTorrentRssParserSettings(IndexerResponse response, TorrentRssIndexerSettings indexerSettings) - { - var parser = new TorrentRssParser - { - UseEnclosureUrl = true, - UseEnclosureLength = true, - ParseSeedersInDescription = true - }; - - var item = parser.GetItems(response).FirstOrDefault(); - if (item == null) - { - throw new UnsupportedFeedException("Empty feed, cannot check if feed is parsable."); - } - - var settings = new TorrentRssIndexerParserSettings() - { - UseEnclosureUrl = true, - UseEnclosureLength = true, - ParseSeedersInDescription = true - }; - - if (item.Element("enclosure") == null) - { - parser.UseEnclosureUrl = settings.UseEnclosureUrl = false; - } - - var releases = ParseResponse(parser, response); - ValidateReleases(releases, indexerSettings); - - if (!releases.Any(v => v.Seeders.HasValue)) - { - _logger.Trace("Feed doesn't have Seeders in Description, disabling option."); - parser.ParseSeedersInDescription = settings.ParseSeedersInDescription = false; - } - - if (!releases.Any(r => r.Size < ValidSizeThreshold)) - { - _logger.Trace("Feed has valid size in enclosure."); - return settings; - } - - parser.UseEnclosureLength = settings.UseEnclosureLength = false; - - foreach (var sizeElementName in new[] { "size", "Size" }) - { - parser.SizeElementName = settings.SizeElementName = sizeElementName; - - releases = ParseResponse(parser, response); - ValidateReleases(releases, indexerSettings); - - if (!releases.Any(r => r.Size < ValidSizeThreshold)) - { - _logger.Trace("Feed has valid size in Size element."); - return settings; - } - } - - parser.SizeElementName = settings.SizeElementName = null; - parser.ParseSizeInDescription = settings.ParseSizeInDescription = true; - - releases = ParseResponse(parser, response); - ValidateReleases(releases, indexerSettings); - - if (releases.Count(r => r.Size >= ValidSizeThreshold) > releases.Length / 2) - { - if (releases.Any(r => r.Size < ValidSizeThreshold)) - { - _logger.Debug("Feed {0} contains very small releases.", response.Request.Url); - } - - _logger.Trace("Feed has valid size in description."); - return settings; - } - - parser.ParseSizeInDescription = settings.ParseSizeInDescription = false; - - _logger.Debug("Feed doesn't have release size."); - - releases = ParseResponse(parser, response); - ValidateReleases(releases, indexerSettings); - ValidateReleaseSize(releases, indexerSettings); - - return settings; - } - - private bool IsEZTVFeed(IndexerResponse response) - { - var content = XmlCleaner.ReplaceEntities(response.Content); - content = XmlCleaner.ReplaceUnicode(content); - - using (var xmlTextReader = XmlReader.Create(new StringReader(content), new XmlReaderSettings { DtdProcessing = DtdProcessing.Parse, ValidationType = ValidationType.None, IgnoreComments = true, XmlResolver = null })) - { - var document = XDocument.Load(xmlTextReader); - - // Check Namespace - if (document.Root == null) - { - throw new InvalidDataException("Could not parse IndexerResponse into XML."); - } - - var ns = document.Root.GetNamespaceOfPrefix("torrent"); - if (ns == "http://xmlns.ezrss.it/0.1/") - { - _logger.Trace("Identified feed as EZTV compatible by EZTV Namespace"); - return true; - } - - // Check DTD in DocType - if (document.DocumentType != null && document.DocumentType.SystemId == "http://xmlns.ezrss.it/0.1/dtd/") - { - _logger.Trace("Identified feed as EZTV compatible by EZTV DTD"); - return true; - } - - // Check namespaces - if (document.Descendants().Any(v => v.GetDefaultNamespace().NamespaceName == "http://xmlns.ezrss.it/0.1/")) - { - _logger.Trace("Identified feed as EZTV compatible by EZTV Namespace"); - return true; - } - - return false; - } - } - - private TorrentInfo[] ParseResponse(IParseIndexerResponse parser, IndexerResponse response) - { - try - { - var releases = parser.ParseResponse(response).Cast().ToArray(); - return releases; - } - catch (Exception ex) - { - _logger.Debug(ex, "Unable to parse indexer feed: " + ex.Message); - throw new UnsupportedFeedException("Unable to parse indexer: " + ex.Message); - } - } - - private void ValidateReleases(TorrentInfo[] releases, TorrentRssIndexerSettings indexerSettings) - { - if (releases == null || releases.Empty()) - { - throw new UnsupportedFeedException("Empty feed, cannot check if feed is parsable."); - } - - var torrentInfo = releases.First(); - - _logger.Trace("TorrentInfo: \n{0}", torrentInfo.ToString("L")); - - if (releases.Any(r => r.Title.IsNullOrWhiteSpace())) - { - throw new UnsupportedFeedException("Feed contains releases without title."); - } - - if (releases.Any(r => !IsValidDownloadUrl(r.DownloadUrl))) - { - throw new UnsupportedFeedException("Failed to find a valid download url in the feed."); - } - - var total = releases.Where(v => v.Guid != null).Select(v => v.Guid).ToArray(); - var distinct = total.Distinct().ToArray(); - - if (distinct.Length != total.Length) - { - throw new UnsupportedFeedException("Feed contains releases with same guid, rejecting malformed rss feed."); - } - } - - private void ValidateReleaseSize(TorrentInfo[] releases, TorrentRssIndexerSettings indexerSettings) - { - if (!indexerSettings.AllowZeroSize && releases.Any(r => r.Size == 0)) - { - throw new UnsupportedFeedException("Feed doesn't contain the release content size."); - } - - if (releases.Any(r => r.Size != 0 && r.Size < ValidSizeThreshold)) - { - throw new UnsupportedFeedException("Size of one more releases lower than {0}, feed must contain release content size.", ValidSizeThreshold.SizeSuffix()); - } - } - - private static bool IsValidDownloadUrl(string url) - { - if (url.IsNullOrWhiteSpace()) - { - return false; - } - - if (url.StartsWith("magnet:") || - url.StartsWith("http:") || - url.StartsWith("https:")) - { - return true; - } - - return false; - } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/Torznab/Torznab.cs b/src/NzbDrone.Core/Indexers/Definitions/Torznab/Torznab.cs index 87f560a22..167658f9a 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Torznab/Torznab.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Torznab/Torznab.cs @@ -17,6 +17,7 @@ namespace NzbDrone.Core.Indexers.Torznab private readonly INewznabCapabilitiesProvider _capabilitiesProvider; public override string Name => "Torznab"; + public override string BaseUrl => Settings.BaseUrl; public override DownloadProtocol Protocol => DownloadProtocol.Torrent; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; diff --git a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs index d8a6a51e3..f84c508cb 100644 --- a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs @@ -12,11 +12,12 @@ using NzbDrone.Core.Http.CloudFlare; using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.ThingiProvider; namespace NzbDrone.Core.Indexers { public abstract class HttpIndexerBase : IndexerBase - where TSettings : IIndexerSettings, new() + where TSettings : IProviderConfig, new() { protected const int MaxNumResultsPerQuery = 1000; @@ -25,9 +26,6 @@ namespace NzbDrone.Core.Indexers public override bool SupportsRss => true; public override bool SupportsSearch => true; public override IndexerCapabilities Capabilities { get; protected set; } - - public bool SupportsPaging => PageSize > 0; - public virtual int PageSize => 0; public virtual TimeSpan RateLimit => TimeSpan.FromSeconds(2); diff --git a/src/NzbDrone.Core/Indexers/IIndexerSettings.cs b/src/NzbDrone.Core/Indexers/IIndexerSettings.cs deleted file mode 100644 index 0a02f9313..000000000 --- a/src/NzbDrone.Core/Indexers/IIndexerSettings.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using NzbDrone.Core.ThingiProvider; - -namespace NzbDrone.Core.Indexers -{ - public interface IIndexerSettings : IProviderConfig - { - string BaseUrl { get; set; } - } -} diff --git a/src/NzbDrone.Core/Indexers/IndexerBase.cs b/src/NzbDrone.Core/Indexers/IndexerBase.cs index 602bec47a..63fb54508 100644 --- a/src/NzbDrone.Core/Indexers/IndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/IndexerBase.cs @@ -12,13 +12,14 @@ using NzbDrone.Core.ThingiProvider; namespace NzbDrone.Core.Indexers { public abstract class IndexerBase : IIndexer - where TSettings : IIndexerSettings, new() + where TSettings : IProviderConfig, new() { protected readonly IIndexerStatusService _indexerStatusService; protected readonly IConfigService _configService; protected readonly Logger _logger; public abstract string Name { get; } + public abstract string BaseUrl { get; } public abstract DownloadProtocol Protocol { get; } public abstract IndexerPrivacy Privacy { get; } public int Priority { get; set; } diff --git a/src/NzbDrone.Core/Indexers/IndexerCapabilitiesCategories.cs b/src/NzbDrone.Core/Indexers/IndexerCapabilitiesCategories.cs index 96e3fed3a..f001b792c 100644 --- a/src/NzbDrone.Core/Indexers/IndexerCapabilitiesCategories.cs +++ b/src/NzbDrone.Core/Indexers/IndexerCapabilitiesCategories.cs @@ -105,18 +105,19 @@ namespace NzbDrone.Core.Indexers return cats; } - public ICollection MapTrackerCatDescToNewznab(string trackerCategoryDesc) + public ICollection MapTrackerCatDescToNewznab(string trackerCategoryDesc) { if (string.IsNullOrWhiteSpace(trackerCategoryDesc)) { - return new List(); + return new List(); } var cats = _categoryMapping .Where(m => !string.IsNullOrWhiteSpace(m.TrackerCategoryDesc) && string.Equals(m.TrackerCategoryDesc, trackerCategoryDesc, StringComparison.InvariantCultureIgnoreCase)) - .Select(c => c.NewzNabCategory).ToList(); + .Select(c => NewznabStandardCategory.AllCats.FirstOrDefault(n => n.Id == c.NewzNabCategory) ?? new IndexerCategory { Id = c.NewzNabCategory }) + .ToList(); return cats; }