diff --git a/NzbDrone.Common/HttpProvider.cs b/NzbDrone.Common/HttpProvider.cs index 22b12dcef..99fef0b1a 100644 --- a/NzbDrone.Common/HttpProvider.cs +++ b/NzbDrone.Common/HttpProvider.cs @@ -49,7 +49,7 @@ namespace NzbDrone.Common } } - public virtual Stream DownloadStream(string url, NetworkCredential credential) + public virtual Stream DownloadStream(string url, NetworkCredential credential = null) { var request = (HttpWebRequest)WebRequest.Create(url); request.UserAgent = _userAgent; diff --git a/NzbDrone.Core.Test/Datastore/BaiscRepositoryFixture.cs b/NzbDrone.Core.Test/Datastore/BaiscRepositoryFixture.cs deleted file mode 100644 index a32dd9c08..000000000 --- a/NzbDrone.Core.Test/Datastore/BaiscRepositoryFixture.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Linq; -using FizzWare.NBuilder; -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.Datastore; -using NzbDrone.Core.Test.Framework; - -namespace NzbDrone.Core.Test.Datastore -{ - - public class SampleType : ModelBase - { - public string Name { get; set; } - public string Tilte { get; set; } - public string Address { get; set; } - } - - [TestFixture] - public class BaiscRepositoryFixture : ObjectDbTest,SampleType> - { - private SampleType sampleType; - - - [SetUp] - public void Setup() - { - sampleType = Builder - .CreateNew() - .With(c => c.Id = 0) - .Build(); - - } - - [Test] - public void should_be_able_to_add() - { - Subject.Insert(sampleType); - Subject.All().Should().HaveCount(1); - } - - - - [Test] - public void should_be_able_to_delete_model() - { - Subject.Insert(sampleType); - Subject.All().Should().HaveCount(1); - - Subject.Delete(sampleType.Id); - Subject.All().Should().BeEmpty(); - } - - [Test] - public void should_be_able_to_find_by_id() - { - Subject.Insert(sampleType); - Subject.Get(sampleType.Id) - .ShouldHave() - .AllProperties() - .EqualTo(sampleType); - } - - [Test] - public void should_be_able_to_update_existing_model() - { - Subject.Insert(sampleType); - - sampleType.Address = "newAddress"; - - Subject.Update(sampleType); - - Subject.Get(sampleType.Id).Address.Should().Be(sampleType.Address); - - } - - [Test] - public void getting_model_with_invalid_id_should_throw() - { - Assert.Throws(() => Subject.Get(12)); - } - - - [Test] - public void get_all_with_empty_db_should_return_empty_list() - { - Subject.All().Should().BeEmpty(); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/DecisionEngineTests/AllowedDownloadSpecificationFixture.cs b/NzbDrone.Core.Test/DecisionEngineTests/AllowedDownloadSpecificationFixture.cs index 285669d3c..96949311e 100644 --- a/NzbDrone.Core.Test/DecisionEngineTests/AllowedDownloadSpecificationFixture.cs +++ b/NzbDrone.Core.Test/DecisionEngineTests/AllowedDownloadSpecificationFixture.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using FluentAssertions; using Moq; using NUnit.Framework; @@ -9,28 +10,28 @@ using NzbDrone.Core.Test.Framework; namespace NzbDrone.Core.Test.DecisionEngineTests { [TestFixture] - public class AllowedDownloadSpecificationFixture : CoreTest + public class AllowedDownloadSpecificationFixture : CoreTest { - private EpisodeParseResult _parseResult; + private List _parseResults; - private Mock _pass1; - private Mock _pass2; - private Mock _pass3; + private Mock _pass1; + private Mock _pass2; + private Mock _pass3; - private Mock _fail1; - private Mock _fail2; - private Mock _fail3; + private Mock _fail1; + private Mock _fail2; + private Mock _fail3; [SetUp] public void Setup() { - _pass1 = new Mock(); - _pass2 = new Mock(); - _pass3 = new Mock(); + _pass1 = new Mock(); + _pass2 = new Mock(); + _pass3 = new Mock(); - _fail1 = new Mock(); - _fail2 = new Mock(); - _fail3 = new Mock(); + _fail1 = new Mock(); + _fail2 = new Mock(); + _fail3 = new Mock(); _pass1.Setup(c => c.IsSatisfiedBy(It.IsAny())).Returns(true); _pass2.Setup(c => c.IsSatisfiedBy(It.IsAny())).Returns(true); @@ -40,11 +41,11 @@ namespace NzbDrone.Core.Test.DecisionEngineTests _fail2.Setup(c => c.IsSatisfiedBy(It.IsAny())).Returns(false); _fail3.Setup(c => c.IsSatisfiedBy(It.IsAny())).Returns(false); - _parseResult = new EpisodeParseResult(); + _parseResults = new List() { new EpisodeParseResult() }; } - private void GivenSpecifications(params Mock[] mocks) + private void GivenSpecifications(params Mock[] mocks) { Mocker.SetConstant(mocks.Select(c => c.Object)); } @@ -54,14 +55,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { GivenSpecifications(_pass1, _pass2, _pass3, _fail1, _fail2, _fail3); - Subject.GetDownloadDecision(_parseResult); + Subject.GetRssDecision(_parseResults); - _fail1.Verify(c => c.IsSatisfiedBy(_parseResult), Times.Once()); - _fail2.Verify(c => c.IsSatisfiedBy(_parseResult), Times.Once()); - _fail3.Verify(c => c.IsSatisfiedBy(_parseResult), Times.Once()); - _pass1.Verify(c => c.IsSatisfiedBy(_parseResult), Times.Once()); - _pass2.Verify(c => c.IsSatisfiedBy(_parseResult), Times.Once()); - _pass3.Verify(c => c.IsSatisfiedBy(_parseResult), Times.Once()); + _fail1.Verify(c => c.IsSatisfiedBy(_parseResults[0]), Times.Once()); + _fail2.Verify(c => c.IsSatisfiedBy(_parseResults[0]), Times.Once()); + _fail3.Verify(c => c.IsSatisfiedBy(_parseResults[0]), Times.Once()); + _pass1.Verify(c => c.IsSatisfiedBy(_parseResults[0]), Times.Once()); + _pass2.Verify(c => c.IsSatisfiedBy(_parseResults[0]), Times.Once()); + _pass3.Verify(c => c.IsSatisfiedBy(_parseResults[0]), Times.Once()); } [Test] @@ -69,9 +70,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { GivenSpecifications(_pass1, _fail1, _pass2, _pass3); - var result = Subject.GetDownloadDecision(_parseResult); + var result = Subject.GetRssDecision(_parseResults); - result.Approved.Should().BeFalse(); + result.Single().Approved.Should().BeFalse(); } [Test] @@ -79,9 +80,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { GivenSpecifications(_pass1, _pass2, _pass3); - var result = Subject.GetDownloadDecision(_parseResult); + var result = Subject.GetRssDecision(_parseResults); - result.Approved.Should().BeTrue(); + result.Single().Approved.Should().BeTrue(); } [Test] @@ -89,8 +90,18 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { GivenSpecifications(_pass1, _pass2, _pass3, _fail1, _fail2, _fail3); - var result = Subject.GetDownloadDecision(_parseResult); - result.Rejections.Should().HaveCount(3); + var result = Subject.GetRssDecision(_parseResults); + result.Single().Rejections.Should().HaveCount(3); + } + + + [Test] + public void parse_result_should_be_attached_to_decision() + { + GivenSpecifications(_pass1, _pass2, _pass3, _fail1, _fail2, _fail3); + + var result = Subject.GetRssDecision(_parseResults); + result.Single().ParseResult.Should().Be(_parseResults.Single()); } } diff --git a/NzbDrone.Core.Test/IndexerSearchTests/DailyEpisodeSearchTests/IndexerDailyEpisodeSearchFixture.cs b/NzbDrone.Core.Test/IndexerSearchTests/DailyEpisodeSearchTests/IndexerDailyEpisodeSearchFixture.cs deleted file mode 100644 index b1a834484..000000000 --- a/NzbDrone.Core.Test/IndexerSearchTests/DailyEpisodeSearchTests/IndexerDailyEpisodeSearchFixture.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Collections.Generic; -using FizzWare.NBuilder; -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.IndexerSearch; -using NzbDrone.Core.Model; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Test.IndexerSearchTests.DailyEpisodeSearchTests -{ - [TestFixture] - public class IndexerDailyEpisodeSearchFixture : CoreTest - { - private Series _series; - private Episode _episode; - private EpisodeParseResult _episodeParseResult; - - [SetUp] - public void Setup() - { - _series = Builder - .CreateNew() - .Build(); - - _episode = Builder - .CreateNew() - .With(e => e.SeriesId = _series.Id) - .With(e => e.Series = _series) - .Build(); - - _episodeParseResult = Builder - .CreateNew() - .With(p => p.AirDate = _episode.AirDate) - .With(p => p.Episodes = new List { _episode }) - .With(p => p.Series = _series) - .Build(); - } - - [Test] - public void should_return_WrongEpisode_is_parseResult_doesnt_have_airdate() - { - _episodeParseResult.AirDate = null; - - Subject.IsEpisodeMatch(_series, new { Episode = _episode }, _episodeParseResult).Should().BeFalse(); - } - - [Test] - public void should_return_WrongEpisode_is_parseResult_airdate_doesnt_match_episode() - { - _episodeParseResult.AirDate = _episode.AirDate.Value.AddDays(-10); - - Subject.IsEpisodeMatch(_series, new { Episode = _episode }, _episodeParseResult) - .Should() - .BeFalse(); - } - - [Test] - public void should_not_return_error_when_airDates_match() - { - Subject.IsEpisodeMatch(_series, new {Episode = _episode}, _episodeParseResult) - .Should().BeTrue(); - } - } -} diff --git a/NzbDrone.Core.Test/IndexerSearchTests/DailyEpisodeSearchTests/IndexerDailyEpisodeSearch_EpisodeMatch.cs b/NzbDrone.Core.Test/IndexerSearchTests/DailyEpisodeSearchTests/IndexerDailyEpisodeSearch_EpisodeMatch.cs deleted file mode 100644 index fe4e1b41b..000000000 --- a/NzbDrone.Core.Test/IndexerSearchTests/DailyEpisodeSearchTests/IndexerDailyEpisodeSearch_EpisodeMatch.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.IndexerSearch; -using NzbDrone.Core.Tv; -using NzbDrone.Test.Common; - -namespace NzbDrone.Core.Test.IndexerSearchTests.DailyEpisodeSearchTests -{ - [TestFixture] - public class IndexerDailyEpisodeSearch_EpisodeMatch : IndexerSearchTestBase - { - [Test] - public void should_fetch_results_from_indexers() - { - WithValidIndexers(); - - - Subject.PerformSearch(_series, new List { _episode }, notification) - .Should() - .HaveCount(20); - } - - [Test] - public void should_log_error_when_fetching_from_indexer_fails() - { - WithBrokenIndexers(); - - Mocker.Resolve() - .PerformSearch(_series, new List { _episode }, notification) - .Should() - .HaveCount(0); - - ExceptionVerification.ExpectedErrors(2); - } - } -} diff --git a/NzbDrone.Core.Test/IndexerSearchTests/EpisodeSearchTests/IndexerEpisodeSearchFixture.cs b/NzbDrone.Core.Test/IndexerSearchTests/EpisodeSearchTests/IndexerEpisodeSearchFixture.cs deleted file mode 100644 index 23d836142..000000000 --- a/NzbDrone.Core.Test/IndexerSearchTests/EpisodeSearchTests/IndexerEpisodeSearchFixture.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System.Collections.Generic; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.IndexerSearch; -using NzbDrone.Core.Tv; -using NzbDrone.Test.Common; - -namespace NzbDrone.Core.Test.IndexerSearchTests.EpisodeSearchTests -{ - [TestFixture] - public class IndexerEpisodeSearchFixture : IndexerSearchTestBase - { - - [Test] - public void should_fetch_results_from_indexers() - { - WithValidIndexers(); - - Subject - .PerformSearch(_series, new List { _episode }, notification) - .Should() - .HaveCount(20); - } - - [Test] - public void should_log_error_when_fetching_from_indexer_fails() - { - WithBrokenIndexers(); - - Subject - .PerformSearch(_series, new List { _episode }, notification) - .Should() - .HaveCount(0); - - ExceptionVerification.ExpectedErrors(2); - } - - [Test] - public void should_use_scene_numbering_when_available() - { - _series.UseSceneNumbering = true; - _episode.SceneEpisodeNumber = 5; - _episode.SceneSeasonNumber = 10; - - WithValidIndexers(); - - Subject - .PerformSearch(_series, new List { _episode }, notification) - .Should() - .HaveCount(20); - - _indexer1.Verify(v => v.FetchEpisode(_series.Title, 10, 5), Times.Once()); - _indexer2.Verify(v => v.FetchEpisode(_series.Title, 10, 5), Times.Once()); - } - - [Test] - public void should_use_standard_numbering_when_scene_series_set_but_info_is_not_available() - { - _series.UseSceneNumbering = true; - _episode.SceneEpisodeNumber = 0; - _episode.SceneSeasonNumber = 0; - - WithValidIndexers(); - - Subject - .PerformSearch(_series, new List { _episode }, notification) - .Should() - .HaveCount(20); - - _indexer1.Verify(v => v.FetchEpisode(_series.Title, _episode.SeasonNumber, _episode.EpisodeNumber), Times.Once()); - _indexer2.Verify(v => v.FetchEpisode(_series.Title, _episode.SeasonNumber, _episode.EpisodeNumber), Times.Once()); - } - - [Test] - public void should_use_standard_numbering_when_not_scene_series() - { - _series.UseSceneNumbering = false; - - WithValidIndexers(); - - Subject - .PerformSearch(_series, new List { _episode }, notification) - .Should() - .HaveCount(20); - - _indexer1.Verify(v => v.FetchEpisode(_series.Title, _episode.SeasonNumber, _episode.EpisodeNumber), Times.Once()); - _indexer2.Verify(v => v.FetchEpisode(_series.Title, _episode.SeasonNumber, _episode.EpisodeNumber), Times.Once()); - } - } -} diff --git a/NzbDrone.Core.Test/IndexerSearchTests/EpisodeSearchTests/IndexerEpisodeSearch_EpisodeMatch.cs b/NzbDrone.Core.Test/IndexerSearchTests/EpisodeSearchTests/IndexerEpisodeSearch_EpisodeMatch.cs deleted file mode 100644 index 6b3c4a289..000000000 --- a/NzbDrone.Core.Test/IndexerSearchTests/EpisodeSearchTests/IndexerEpisodeSearch_EpisodeMatch.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System.Collections.Generic; -using FizzWare.NBuilder; -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.IndexerSearch; -using NzbDrone.Core.Model; -using NzbDrone.Core.Tv; -using NzbDrone.Test.Common; - -namespace NzbDrone.Core.Test.IndexerSearchTests.EpisodeSearchTests -{ - [TestFixture] - public class IndexerEpisodeSearch_EpisodeMatch : TestBase - { - private Series _series; - private Episode _episode; - private EpisodeParseResult _episodeParseResult; - - [SetUp] - public void Setup() - { - _series = Builder - .CreateNew() - .Build(); - - _episode = Builder - .CreateNew() - .With(e => e.SeriesId = _series.Id) - .With(e => e.Series = _series) - .Build(); - - _episodeParseResult = Builder - .CreateNew() - .With(p => p.SeasonNumber = 1) - .With(p => p.EpisodeNumbers = new List{ _episode.EpisodeNumber }) - .With(p => p.Episodes = new List { _episode }) - .With(p => p.Series = _series) - .Build(); - - } - - [Test] - public void should_return_WrongSeason_when_season_doesnt_match() - { - _episode.SeasonNumber = 10; - - Mocker.Resolve() - .IsEpisodeMatch(_series, new {Episode = _episode}, _episodeParseResult) - .Should() - .BeFalse(); - } - - [Test] - public void should_return_WrongEpisode_when_episode_doesnt_match() - { - _episode.EpisodeNumber = 10; - - Mocker.Resolve() - .IsEpisodeMatch(_series, new { Episode = _episode }, _episodeParseResult) - .Should() - .BeFalse(); - } - - [Test] - public void should_not_return_error_when_season_and_episode_match() - { - Mocker.Resolve() - .IsEpisodeMatch(_series, new { Episode = _episode }, _episodeParseResult) - .Should() - .BeTrue(); - } - - [Test] - public void should_return_WrongSeason_when_season_doesnt_match_for_scene_series() - { - _series.UseSceneNumbering = true; - _episode.SceneSeasonNumber = 10; - _episode.SeasonNumber = 10; - _episode.EpisodeNumber = 10; - - Mocker.Resolve() - .IsEpisodeMatch(_series, new { Episode = _episode }, _episodeParseResult) - .Should() - .BeFalse(); - } - - [Test] - public void should_return_WrongEpisode_when_episode_doesnt_match_for_scene_series() - { - _series.UseSceneNumbering = true; - _episode.SceneEpisodeNumber = 10; - _episode.SeasonNumber = 10; - _episode.EpisodeNumber = 10; - - Mocker.Resolve() - .IsEpisodeMatch(_series, new { Episode = _episode }, _episodeParseResult) - .Should() - .BeFalse(); - } - - [Test] - public void should_not_return_error_when_season_and_episode_match_for_scene_series() - { - _series.UseSceneNumbering = true; - _episode.SceneSeasonNumber = _episode.SeasonNumber; - _episode.SceneEpisodeNumber = _episode.EpisodeNumber; - _episode.SeasonNumber = 10; - _episode.EpisodeNumber = 10; - - Mocker.Resolve() - .IsEpisodeMatch(_series, new { Episode = _episode }, _episodeParseResult) - .Should() - .BeTrue(); - } - } -} diff --git a/NzbDrone.Core.Test/IndexerSearchTests/GetSearchTitleFixture.cs b/NzbDrone.Core.Test/IndexerSearchTests/GetSearchTitleFixture.cs deleted file mode 100644 index 76f0e2f70..000000000 --- a/NzbDrone.Core.Test/IndexerSearchTests/GetSearchTitleFixture.cs +++ /dev/null @@ -1,85 +0,0 @@ -using FizzWare.NBuilder; -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.DataAugmentation; -using NzbDrone.Core.DataAugmentation.Scene; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Test.IndexerSearchTests -{ - public class GetSearchTitleFixture : CoreTest - { - private Series _series; - - [SetUp] - public void Setup() - { - _series = Builder - .CreateNew() - .With(s => s.Title = "Hawaii Five-0") - .Build(); - } - - private void WithSceneMapping() - { - Mocker.GetMock() - .Setup(s => s.GetSceneName(_series.Id, -1)) - .Returns("Hawaii Five 0 2010"); - } - - [Test] - public void should_return_series_title_when_there_is_no_scene_mapping() - { - Subject.GetSearchTitle(_series, 5).Should().Be(_series.Title); - } - - [Test] - public void should_return_scene_mapping_when_one_exists() - { - WithSceneMapping(); - - Subject.GetSearchTitle(_series, 5).Should().Be("Hawaii Five 0 2010"); - } - - [Test] - public void should_return_season_scene_name_when_one_exists() - { - Mocker.GetMock() - .Setup(s => s.GetSceneName(_series.Id, 5)) - .Returns("Hawaii Five 0 2010 - Season 5"); - - Subject.GetSearchTitle(_series, 5).Should().Be("Hawaii Five 0 2010 - Season 5"); - } - - [Test] - public void should_return_series_scene_name_when_one_for_season_does_not_exist() - { - WithSceneMapping(); - - Subject.GetSearchTitle(_series, 5).Should().Be("Hawaii Five 0 2010"); - } - - [Test] - public void should_replace_ampersand_with_and() - { - _series.Title = "Franklin & Bash"; - - Subject.GetSearchTitle(_series, 5).Should().Be("Franklin and Bash"); - } - - [TestCase("Betty White's Off Their Rockers", "Betty Whites Off Their Rockers")] - [TestCase("Star Wars: The Clone Wars", "Star Wars The Clone Wars")] - [TestCase("Hawaii Five-0", "Hawaii Five-0")] - public void should_replace_some_special_characters(string input, string expected) - { - _series.Title = input; - - Mocker.GetMock() - .Setup(s => s.GetSceneName(_series.Id, -1)) - .Returns(""); - - Subject.GetSearchTitle(_series, 5).Should().Be(expected); - } - } -} diff --git a/NzbDrone.Core.Test/IndexerSearchTests/IndexerSearchTestBase.cs b/NzbDrone.Core.Test/IndexerSearchTests/IndexerSearchTestBase.cs deleted file mode 100644 index 81d8fa71f..000000000 --- a/NzbDrone.Core.Test/IndexerSearchTests/IndexerSearchTestBase.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Collections.Generic; -using FizzWare.NBuilder; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.IndexerSearch; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Model; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Test.IndexerSearchTests -{ - public abstract class IndexerSearchTestBase : CoreTest - where TSearch : IndexerSearchBase - { - protected Series _series; - protected Episode _episode; - protected ProgressNotification notification = new ProgressNotification("Testing"); - - protected Mock _indexer1; - protected Mock _indexer2; - protected List _indexers; - protected IList _parseResults; - - [SetUp] - public void Setup() - { - _series = Builder - .CreateNew() - .Build(); - - _episode = Builder - .CreateNew() - .With(e => e.SeriesId = _series.Id) - .With(e => e.Series = _series) - .Build(); - - - _parseResults = Builder - .CreateListOfSize(10) - .Build(); - - _indexer1 = new Mock(); - _indexer2 = new Mock(); - _indexers = new List { _indexer1.Object, _indexer2.Object }; - - Mocker.GetMock() - .Setup(c => c.GetEnabledIndexers()) - .Returns(_indexers); - } - - protected void WithValidIndexers() - { - _indexer1.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_parseResults); - _indexer1.Setup(c => c.FetchDailyEpisode(It.IsAny(), It.IsAny())) - .Returns(_parseResults); - _indexer1.Setup(c => c.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_parseResults); - - _indexer2.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_parseResults); - _indexer2.Setup(c => c.FetchDailyEpisode(It.IsAny(), It.IsAny())) - .Returns(_parseResults); - _indexer2.Setup(c => c.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_parseResults); - } - - protected void WithBrokenIndexers() - { - _indexer1.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) - .Throws(new Exception()); - _indexer1.Setup(c => c.FetchDailyEpisode(It.IsAny(), It.IsAny())) - .Throws(new Exception()); - _indexer1.Setup(c => c.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny())) - .Throws(new Exception()); - - _indexer2.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) - .Throws(new Exception()); - _indexer2.Setup(c => c.FetchDailyEpisode(It.IsAny(), It.IsAny())) - .Throws(new Exception()); - _indexer2.Setup(c => c.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny())) - .Throws(new Exception()); - - _indexer1.SetupGet(c => c.Name).Returns("Indexer1"); - _indexer1.SetupGet(c => c.Name).Returns("Indexer2"); - } - } -} diff --git a/NzbDrone.Core.Test/IndexerSearchTests/PartialSeasonSearchTests/PartialSeasonSearchFixture.cs b/NzbDrone.Core.Test/IndexerSearchTests/PartialSeasonSearchTests/PartialSeasonSearchFixture.cs deleted file mode 100644 index c02f4e275..000000000 --- a/NzbDrone.Core.Test/IndexerSearchTests/PartialSeasonSearchTests/PartialSeasonSearchFixture.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.Collections.Generic; -using FizzWare.NBuilder; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.IndexerSearch; -using NzbDrone.Core.Tv; -using NzbDrone.Test.Common; -using System.Linq; - -namespace NzbDrone.Core.Test.IndexerSearchTests.PartialSeasonSearchTests -{ - [TestFixture] - public class PartialSeasonSearchFixture : IndexerSearchTestBase - { - - [Test] - public void should_fetch_results_from_indexers() - { - WithValidIndexers(); - - Subject.PerformSearch(_series, new List { _episode }, notification) - .Should() - .HaveCount(20); - } - - [Test] - public void should_log_error_when_fetching_from_indexer_fails() - { - WithBrokenIndexers(); - - Subject.PerformSearch(_series, new List { _episode }, notification) - .Should() - .HaveCount(0); - - ExceptionVerification.ExpectedErrors(2); - } - - [Test] - public void should_hit_each_indexer_once_for_each_prefix() - { - WithValidIndexers(); - - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.SeasonNumber = 1) - .Build(); - - episodes[0].EpisodeNumber = 1; - episodes[1].EpisodeNumber = 5; - episodes[2].EpisodeNumber = 10; - episodes[3].EpisodeNumber = 15; - - - Subject.PerformSearch(_series, episodes.ToList(), notification) - .Should() - .HaveCount(40); - - _indexer1.Verify(v => v.FetchPartialSeason(_series.Title, 1, 0), Times.Once()); - _indexer1.Verify(v => v.FetchPartialSeason(_series.Title, 1, 1), Times.Once()); - _indexer2.Verify(v => v.FetchPartialSeason(_series.Title, 1, 0), Times.Once()); - _indexer2.Verify(v => v.FetchPartialSeason(_series.Title, 1, 1), Times.Once()); - - _indexer1.Verify(v => v.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(2)); - _indexer2.Verify(v => v.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(2)); - } - } -} diff --git a/NzbDrone.Core.Test/IndexerSearchTests/PartialSeasonSearchTests/PartialSeasonSearch_EpisodeMatch.cs b/NzbDrone.Core.Test/IndexerSearchTests/PartialSeasonSearchTests/PartialSeasonSearch_EpisodeMatch.cs deleted file mode 100644 index 99c36e221..000000000 --- a/NzbDrone.Core.Test/IndexerSearchTests/PartialSeasonSearchTests/PartialSeasonSearch_EpisodeMatch.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using FizzWare.NBuilder; -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.IndexerSearch; -using NzbDrone.Core.Model; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Tv; -using NzbDrone.Test.Common; - -namespace NzbDrone.Core.Test.IndexerSearchTests.PartialSeasonSearchTests -{ - [TestFixture] - public class PartialSeasonSearch_EpisodeMatch : CoreTest - { - private Series _series; - private List _episodes; - private EpisodeParseResult _episodeParseResult; - - [SetUp] - public void Setup() - { - _series = Builder - .CreateNew() - .Build(); - - _episodes = Builder - .CreateListOfSize(10) - .All() - .With(e => e.SeriesId = _series.Id) - .With(e => e.Series = _series) - .Build() - .ToList(); - - _episodeParseResult = Builder - .CreateNew() - .With(p => p.SeasonNumber = 1) - .Build(); - - } - - [Test] - public void should_return_wrongSeason_when_season_does_not_match() - { - Subject.IsEpisodeMatch(_series, new { SeasonNumber = 2, Episodes = _episodes }, _episodeParseResult) - .Should().BeFalse(); - } - - [Test] - public void should_not_return_error_when_season_matches() - { - Subject.IsEpisodeMatch(_series, new { SeasonNumber = 1, Episodes = _episodes }, _episodeParseResult) - .Should().BeTrue(); - } - } -} diff --git a/NzbDrone.Core.Test/IndexerSearchTests/ProcessResultsFixture.cs b/NzbDrone.Core.Test/IndexerSearchTests/ProcessResultsFixture.cs deleted file mode 100644 index 4e9a388a5..000000000 --- a/NzbDrone.Core.Test/IndexerSearchTests/ProcessResultsFixture.cs +++ /dev/null @@ -1,236 +0,0 @@ -/* -using System.Collections.Generic; -using System.Linq; -using FizzWare.NBuilder; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.Download; -using NzbDrone.Core.Qualities; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Model; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Repository.Search; -using NzbDrone.Test.Common; - -namespace NzbDrone.Core.Test.ProviderTests.SearchTests -{ - [TestFixture] - public class ProcessResultsFixture : CoreTest - { - private Series _matchingSeries; - private Series _mismatchedSeries; - private Series _nullSeries = null; - - private EpisodeSearchResult _episodeSearchResult; - private ProgressNotification _notification; - - private List _episodes; - - [SetUp] - public void Setup() - { - _matchingSeries = Builder.CreateNew() - .With(s => s.Id = 79488) - .With(s => s.Title = "30 Rock") - .Build(); - - _mismatchedSeries = Builder.CreateNew() - .With(s => s.Id = 12345) - .With(s => s.Title = "Not 30 Rock") - .Build(); - - _episodeSearchResult = new EpisodeSearchResult(); - _notification = new ProgressNotification("Test"); - - _episodes = Builder - .CreateListOfSize(1) - .Build().ToList(); - - Mocker.GetMock() - .Setup(s => s.GetEpisodesByParseResult(It.IsAny())) - .Returns(_episodes); - } - - private void WithMatchingSeries() - { - Mocker.GetMock() - .Setup(s => s.GetByTitle(It.IsAny())).Returns(_matchingSeries); - } - - private void WithMisMatchedSeries() - { - Mocker.GetMock() - .Setup(s => s.GetByTitle(It.IsAny())).Returns(_mismatchedSeries); - } - - private void WithNullSeries() - { - Mocker.GetMock() - .Setup(s => s.GetByTitle(It.IsAny())).Returns(_nullSeries); - } - - private void WithSuccessfulDownload() - { - Mocker.GetMock() - .Setup(s => s.DownloadReport(It.IsAny())) - .Returns(true); - } - - private void WithFailingDownload() - { - Mocker.GetMock() - .Setup(s => s.DownloadReport(It.IsAny())) - .Returns(false); - } - - private void WithApprovedDecisions() - { - Mocker.GetMock() - .Setup(s => s.GetDownloadDecision(It.IsAny())) - .Returns(new DownloadDecision(new string[0])); - } - - private void WithDeclinedDecisions() - { - Mocker.GetMock() - .Setup(s => s.GetDownloadDecision(It.IsAny())) - .Returns(new DownloadDecision(new[] { "Rejection reason" })); - } - - Times.Once()); - } - - - - - - [Test] - public void should_skip_if_series_does_not_match_searched_series() - { - var parseResults = Builder.CreateListOfSize(5) - .All() - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumbers = new List { 1 }) - .With(e => e.Quality = new QualityModel(Quality.HDTV720p, false)) - .Build() - .ToList(); - - WithMisMatchedSeries(); - - - var result = Subject.ProcessReports(_matchingSeries, new { }, parseResults, _episodeSearchResult, _notification); - - - result.SearchHistoryItems.Should().HaveCount(parseResults.Count); - result.SearchHistoryItems.Should().NotContain(s => s.Success); - - Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), - Times.Never()); - } - - [Test] - public void should_skip_if_episode_was_already_downloaded() - { - var parseResults = Builder.CreateListOfSize(2) - .All() - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumbers = new List { 5 }) - .With(c => c.Quality = new QualityModel(Quality.DVD, true)) - .TheLast(1) - .With(e => e.EpisodeNumbers = new List { 1, 2, 3, 4, 5 }) - .Build() - .ToList(); - - WithMatchingSeries(); - WithQualityNeeded(); - WithSuccessfulDownload(); - - - var result = Subject.ProcessReports(_matchingSeries, new { }, parseResults, _episodeSearchResult, _notification); - - - result.SearchHistoryItems.Should().HaveCount(parseResults.Count); - result.SearchHistoryItems.Should().Contain(s => s.Success); - - Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), - Times.Once()); - } - - [Test] - public void should_try_next_report_if_download_fails() - { - var parseResults = Builder.CreateListOfSize(2) - .All() - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumbers = new List { 1 }) - .With(c => c.Quality = new QualityModel(Quality.DVD, true)) - .TheLast(1) - .With(c => c.Quality = new QualityModel(Quality.SDTV, true)) - .Build() - .ToList(); - - WithMatchingSeries(); - WithQualityNeeded(); - - Mocker.GetMock() - .Setup(s => s.DownloadReport(It.Is(d => d.Quality.Quality == Quality.DVD))) - .Returns(false); - - Mocker.GetMock() - .Setup(s => s.DownloadReport(It.Is(d => d.Quality.Quality == Quality.SDTV))) - .Returns(true); - - - var result = Subject.ProcessReports(_matchingSeries, new { }, parseResults, _episodeSearchResult, _notification); - - - result.SearchHistoryItems.Should().HaveCount(parseResults.Count); - result.SearchHistoryItems.Should().Contain(s => s.Success); - - Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), - Times.Exactly(2)); - } - - [Test] - public void should_return_valid_successes_when_one_or_more_downloaded() - { - var parseResults = Builder.CreateListOfSize(5) - .All() - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumbers = new List { 1 }) - .With(c => c.Quality = new QualityModel(Quality.DVD, true)) - .With(c => c.Age = 10) - .Random(1) - .With(c => c.Quality = new QualityModel(Quality.Bluray1080p, true)) - .With(c => c.Age = 100) - .Build() - .ToList(); - - WithMatchingSeries(); - WithSuccessfulDownload(); - - Mocker.GetMock() - .Setup(s => s.IsDownloadPermitted(It.Is(d => d.Quality.Quality == Quality.Bluray1080p))) - .Returns(ReportRejectionReasons.None); - - - var result = Subject.ProcessReports(_matchingSeries, new { }, parseResults, _episodeSearchResult, _notification); - - - result.Successes.Should().NotBeNull(); - result.Successes.Should().NotBeEmpty(); - - Mocker.GetMock().Verify(c => c.IsDownloadPermitted(It.IsAny()), - Times.Once()); - Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), - Times.Once()); - } - } -} -*/ - -namespace NzbDrone.Core.Test.IndexerSearchTests -{ -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/IndexerSearchTests/SearchDefinitionFixture.cs b/NzbDrone.Core.Test/IndexerSearchTests/SearchDefinitionFixture.cs new file mode 100644 index 000000000..b497ddf0e --- /dev/null +++ b/NzbDrone.Core.Test/IndexerSearchTests/SearchDefinitionFixture.cs @@ -0,0 +1,19 @@ +using NUnit.Framework; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.IndexerSearchTests +{ + public class SearchDefinitionFixture : CoreTest + { + [TestCase("Betty White's Off Their Rockers", Result = "Betty+Whites+Off+Their+Rockers")] + [TestCase("Star Wars: The Clone Wars", Result = "Star+Wars+The+Clone+Wars")] + [TestCase("Hawaii Five-0", Result = "Hawaii+Five+0")] + [TestCase("Franklin & Bash", Result = "Franklin+and+Bash")] + public string should_replace_some_special_characters(string input) + { + Subject.SceneTitle = input; + return Subject.QueryTitle; + } + } +} diff --git a/NzbDrone.Core.Test/IndexerSearchTests/TestSearch.cs b/NzbDrone.Core.Test/IndexerSearchTests/TestSearch.cs deleted file mode 100644 index 5948c88c6..000000000 --- a/NzbDrone.Core.Test/IndexerSearchTests/TestSearch.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using NLog; -using NzbDrone.Core.DataAugmentation; -using NzbDrone.Core.DataAugmentation.Scene; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Download; -using NzbDrone.Core.IndexerSearch; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Model; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Test.IndexerSearchTests -{ - public class TestSearch : IndexerSearchBase - { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - public TestSearch(IEpisodeService episodeService, IDownloadService downloadService, - IIndexerService indexerService, ISceneMappingService sceneMappingService, - IDownloadDirector downloadDirector, ISeriesRepository seriesRepository) - : base(seriesRepository, episodeService, downloadService, indexerService, sceneMappingService, - downloadDirector) - { - } - - public override List PerformSearch(Series series, List episodes, Model.Notification.ProgressNotification notification) - { - var episode = episodes.Single(); - - var reports = new List(); - var title = GetSearchTitle(series); - - var seasonNumber = episode.SeasonNumber; - var episodeNumber = episode.EpisodeNumber; - - Parallel.ForEach(_indexerService.GetEnabledIndexers(), indexer => - { - try - { - reports.AddRange(indexer.FetchEpisode(title, seasonNumber, episodeNumber)); - } - - catch (Exception e) - { - logger.ErrorException(String.Format("An error has occurred while searching for {0}-S{1:00}E{2:00} from: {3}", - series.Title, episode.SeasonNumber, episode.EpisodeNumber, indexer.Name), e); - } - }); - - return reports; - } - - public override bool IsEpisodeMatch(Series series, dynamic options, EpisodeParseResult episodeParseResult) - { - return true; - } - - } -} diff --git a/NzbDrone.Core.Test/IndexerTests/IndexerFixture.cs b/NzbDrone.Core.Test/IndexerTests/IndexerFixture.cs deleted file mode 100644 index 4d8f0eea0..000000000 --- a/NzbDrone.Core.Test/IndexerTests/IndexerFixture.cs +++ /dev/null @@ -1,490 +0,0 @@ -using System; -using System.Globalization; -using System.Linq; -using System.Net; -using System.ServiceModel.Syndication; -using System.Threading; -using FizzWare.NBuilder; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Common; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Model; -using NzbDrone.Core.Qualities; - -using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Test.Indexers; -using NzbDrone.Test.Common; - -namespace NzbDrone.Core.Test.IndexerTests -{ - [TestFixture] - - public class IndexerFixture : CoreTest - { - private void WithConfiguredIndexers() - { - Mocker.GetMock().SetupGet(c => c.NzbsOrgHash).Returns("MockedConfigValue"); - Mocker.GetMock().SetupGet(c => c.NzbsOrgUId).Returns("MockedConfigValue"); - - Mocker.GetMock().SetupGet(c => c.NzbsrusHash).Returns("MockedConfigValue"); - Mocker.GetMock().SetupGet(c => c.NzbsrusUId).Returns("MockedConfigValue"); - - Mocker.GetMock().SetupGet(c => c.FileSharingTalkUid).Returns("MockedConfigValue"); - Mocker.GetMock().SetupGet(c => c.FileSharingTalkSecret).Returns("MockedConfigValue"); - - Mocker.GetMock().SetupGet(c => c.OmgwtfnzbsUsername).Returns("MockedConfigValue"); - Mocker.GetMock().SetupGet(c => c.OmgwtfnzbsApiKey).Returns("MockedConfigValue"); - } - - [TestCase("nzbsrus.xml")] - [TestCase("newznab.xml")] - [TestCase("wombles.xml")] - [TestCase("filesharingtalk.xml")] - [TestCase("nzbindex.xml")] - [TestCase("nzbclub.xml")] - [TestCase("omgwtfnzbs.xml")] - public void parse_feed_xml(string fileName) - { - Mocker.GetMock() - .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) - .Returns(OpenRead("Files", "Rss", fileName)); - - var fakeSettings = Builder.CreateNew().Build(); - Mocker.GetMock() - .Setup(c => c.GetSettings(It.IsAny())) - .Returns(fakeSettings); - - var mockIndexer = Mocker.Resolve(); - var parseResults = mockIndexer.FetchRss(); - - foreach (var episodeParseResult in parseResults) - { - var Uri = new Uri(episodeParseResult.NzbUrl); - Uri.PathAndQuery.Should().NotContain("//"); - } - - parseResults.Should().NotBeEmpty(); - parseResults.Should().OnlyContain(s => s.Indexer == mockIndexer.Name); - parseResults.Should().OnlyContain(s => !String.IsNullOrEmpty(s.OriginalString)); - parseResults.Should().OnlyContain(s => s.Age >= 0); - } - - [Test] - public void custom_parser_partial_success() - { - const string title = "Adventure.Inc.S03E19.DVDRip.XviD-OSiTV"; - const int season = 3; - const int episode = 19; - var quality = Quality.DVD; - - const string summary = "My fake summary"; - - var fakeSettings = Builder.CreateNew().Build(); - Mocker.GetMock() - .Setup(c => c.GetSettings(It.IsAny())) - .Returns(fakeSettings); - - var fakeRssItem = Builder.CreateNew() - .With(c => c.Title = new TextSyndicationContent(title)) - .With(c => c.Summary = new TextSyndicationContent(summary)) - .Build(); - - var result = Mocker.Resolve().ParseFeed(fakeRssItem); - - Assert.IsNotNull(result); - Assert.AreEqual(LanguageType.Finnish, result.Language); - Assert.AreEqual(season, result.SeasonNumber); - Assert.AreEqual(episode, result.EpisodeNumbers[0]); - Assert.AreEqual(quality, result.Quality.Quality); - } - - [TestCase("Adventure.Inc.DVDRip.XviD-OSiTV")] - public void custom_parser_full_parse(string title) - { - const string summary = "My fake summary"; - - var fakeSettings = Builder.CreateNew().Build(); - Mocker.GetMock() - .Setup(c => c.GetSettings(It.IsAny())) - .Returns(fakeSettings); - - var fakeRssItem = Builder.CreateNew() - .With(c => c.Title = new TextSyndicationContent(title)) - .With(c => c.Summary = new TextSyndicationContent(summary)) - .Build(); - - var result = Mocker.Resolve().ParseFeed(fakeRssItem); - - Assert.IsNotNull(result); - Assert.AreEqual(LanguageType.Finnish, result.Language); - } - - [TestCase("hawaii five-0 (2010)", "hawaii+five+0+2010")] - [TestCase("this& that", "this+that")] - [TestCase("this& that", "this+that")] - [TestCase("grey's anatomy", "grey+s+anatomy")] - public void get_query_title(string raw, string clean) - { - var mock = new Mock(); - mock.CallBase = true; - var result = mock.Object.GetQueryTitle(raw); - result.Should().Be(clean); - } - - [Test] - public void size_nzbsrus() - { - WithConfiguredIndexers(); - - Mocker.GetMock() - .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) - .Returns(OpenRead("Files", "Rss", "SizeParsing", "nzbsrus.xml")); - - - var parseResults = Mocker.Resolve().FetchRss(); - - parseResults.Should().HaveCount(1); - parseResults[0].Size.Should().Be(1793148846); - } - - [Test] - public void size_newznab() - { - WithConfiguredIndexers(); - - var newznabDefs = Builder.CreateListOfSize(1) - .All() - .With(n => n.ApiKey = String.Empty) - .Build(); - - Mocker.GetMock().Setup(s => s.Enabled()).Returns(newznabDefs.ToList()); - - Mocker.GetMock() - .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) - .Returns(OpenRead("Files", "Rss", "SizeParsing", "newznab.xml")); - - - var parseResults = Mocker.Resolve().FetchRss(); - - parseResults[0].Size.Should().Be(1183105773); - } - - [Test] - public void size_nzbindex() - { - WithConfiguredIndexers(); - - Mocker.GetMock() - .Setup(h => h.DownloadStream("http://www.nzbindex.nl/rss/alt.binaries.teevee/?sort=agedesc&minsize=100&complete=1&max=50&more=1&q=%23a.b.teevee", It.IsAny())) - .Returns(OpenRead("Files", "Rss", "SizeParsing", "nzbindex.xml")); - - Mocker.GetMock() - .Setup(h => h.DownloadStream("http://www.nzbindex.nl/rss/alt.binaries.hdtv/?sort=agedesc&minsize=100&complete=1&max=50&more=1&q=", It.IsAny())) - .Returns(OpenRead("Files", "Rss", "SizeParsing", "nzbindex.xml")); - - - var parseResults = Mocker.Resolve().FetchRss(); - - parseResults[0].Size.Should().Be(587328389); - } - - [Test] - public void size_nzbclub() - { - WithConfiguredIndexers(); - - Mocker.GetMock() - .Setup(h => h.DownloadStream("http://www.nzbclub.com/nzbfeed.aspx?ig=2&gid=102952&st=1&ns=1&q=%23a.b.teevee", It.IsAny())) - .Returns(OpenRead("Files", "Rss", "SizeParsing", "nzbclub.xml")); - - Mocker.GetMock() - .Setup(h => h.DownloadStream("http://www.nzbclub.com/nzbfeed.aspx?ig=2&gid=5542&st=1&ns=1&q=", It.IsAny())) - .Returns(OpenRead("Files", "Rss", "SizeParsing", "nzbclub.xml")); - - - var parseResults = Mocker.Resolve().FetchRss(); - - parseResults.Should().HaveCount(2); - parseResults[0].Size.Should().Be(2652142305); - } - - [Test] - public void size_omgwtfnzbs() - { - WithConfiguredIndexers(); - - Mocker.GetMock() - .Setup(h => h.DownloadStream("http://rss.omgwtfnzbs.org/rss-search.php?catid=19,20&user=MockedConfigValue&api=MockedConfigValue&eng=1", It.IsAny())) - .Returns(OpenRead("Files", "Rss", "SizeParsing", "omgwtfnzbs.xml")); - - - var parseResults = Mocker.Resolve().FetchRss(); - - parseResults.Should().HaveCount(1); - parseResults[0].Size.Should().Be(236820890); - } - - [Test] - public void Server_Unavailable_503_should_not_log_exception() - { - Mocker.GetMock() - .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) - .Throws(new WebException("503")); - - Mocker.Resolve().FetchRss(); - - ExceptionVerification.ExpectedErrors(0); - ExceptionVerification.ExpectedWarns(1); - } - - [Test] - public void none_503_server_error_should_still_log_error() - { - WithConfiguredIndexers(); - - Mocker.GetMock() - .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) - .Throws(new WebException("some other server error")); - - Mocker.Resolve().FetchRss(); - - ExceptionVerification.ExpectedErrors(1); - ExceptionVerification.ExpectedWarns(0); - } - - [Test] - public void indexer_that_isnt_configured_shouldnt_make_an_http_call() - { - Mocker.Resolve().FetchRss(); - - Mocker.GetMock() - .Verify(c => c.DownloadFile(It.IsAny(), It.IsAny()), Times.Never()); - - ExceptionVerification.ExpectedWarns(1); - } - - [Test] - public void newznab_link_should_be_link_to_nzb_not_details() - { - Mocker.GetMock() - .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) - .Returns(OpenRead("Files", "Rss", "newznab.xml")); - - var fakeSettings = Builder.CreateNew().Build(); - Mocker.GetMock() - .Setup(c => c.GetSettings(It.IsAny())) - .Returns(fakeSettings); - - var mockIndexer = Mocker.Resolve(); - var parseResults = mockIndexer.FetchRss(); - - parseResults.Should().NotBeEmpty(); - parseResults.Should().OnlyContain(s => s.NzbUrl.Contains("getnzb")); - parseResults.Should().NotContain(s => s.NzbUrl.Contains("details")); - } - - private static void Mark500Inconclusive() - { - ExceptionVerification.MarkInconclusive(typeof(WebException)); - ExceptionVerification.MarkInconclusive("System.Net.WebException"); - ExceptionVerification.MarkInconclusive("(503) Server Unavailable."); - ExceptionVerification.MarkInconclusive("(500) Internal Server Error."); - } - - [TestCase("wombles.xml", "de-de")] - public void dateTime_should_parse_when_using_other_cultures(string fileName, string culture) - { - var currentCulture = Thread.CurrentThread.CurrentCulture; - Thread.CurrentThread.CurrentCulture = new CultureInfo(culture); - - Mocker.GetMock() - .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) - .Returns(OpenRead("Files", "Rss", "" + fileName)); - - var fakeSettings = Builder.CreateNew().Build(); - Mocker.GetMock() - .Setup(c => c.GetSettings(It.IsAny())) - .Returns(fakeSettings); - - var mockIndexer = Mocker.Resolve(); - var parseResults = mockIndexer.FetchRss(); - - foreach (var episodeParseResult in parseResults) - { - var Uri = new Uri(episodeParseResult.NzbUrl); - Uri.PathAndQuery.Should().NotContain("//"); - } - - parseResults.Should().NotBeEmpty(); - parseResults.Should().OnlyContain(s => s.Indexer == mockIndexer.Name); - parseResults.Should().OnlyContain(s => !String.IsNullOrEmpty(s.OriginalString)); - parseResults.Should().OnlyContain(s => s.Age >= 0); - - Thread.CurrentThread.CurrentCulture = currentCulture; - } - - [Test] - public void NzbsRus_NzbInfoUrl_should_contain_information_string() - { - WithConfiguredIndexers(); - - const string fileName = "nzbsrus.xml"; - const string expectedString = "nzbdetails"; - - Mocker.GetMock() - .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) - .Returns(OpenRead("Files", "Rss", "" + fileName)); - - var parseResults = Mocker.Resolve().FetchRss(); - - foreach (var episodeParseResult in parseResults) - { - episodeParseResult.NzbInfoUrl.Should().Contain(expectedString); - } - } - - [Test] - public void Newznab_NzbInfoUrl_should_contain_information_string() - { - WithConfiguredIndexers(); - - const string fileName = "newznab.xml"; - const string expectedString = "/details/"; - - var newznabDefs = Builder.CreateListOfSize(1) - .All() - .With(n => n.ApiKey = String.Empty) - .Build(); - - Mocker.GetMock().Setup(s => s.Enabled()).Returns(newznabDefs.ToList()); - - Mocker.GetMock() - .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) - .Returns(OpenRead("Files", "Rss", "" + fileName)); - - var parseResults = Mocker.Resolve().FetchRss(); - - foreach (var episodeParseResult in parseResults) - { - episodeParseResult.NzbInfoUrl.Should().Contain(expectedString); - } - } - - [Test] - public void Wombles_NzbInfoUrl_should_contain_information_string() - { - WithConfiguredIndexers(); - - const string fileName = "wombles.xml"; - - Mocker.GetMock() - .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) - .Returns(OpenRead("Files", "Rss", "" + fileName)); - - var parseResults = Mocker.Resolve().FetchRss(); - - foreach (var episodeParseResult in parseResults) - { - episodeParseResult.NzbInfoUrl.Should().BeNull(); - } - } - - [Test] - public void FileSharingTalk_NzbInfoUrl_should_contain_information_string() - { - WithConfiguredIndexers(); - - const string fileName = "filesharingtalk.xml"; - const string expectedString = "/nzbs/tv"; - - Mocker.GetMock() - .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) - .Returns(OpenRead("Files", "Rss", "" + fileName)); - - var parseResults = Mocker.Resolve().FetchRss(); - - foreach (var episodeParseResult in parseResults) - { - episodeParseResult.NzbInfoUrl.Should().Contain(expectedString); - } - } - - [Test] - public void NzbIndex_NzbInfoUrl_should_contain_information_string() - { - WithConfiguredIndexers(); - - const string expectedString = "release"; - - Mocker.GetMock() - .Setup(h => h.DownloadStream("http://www.nzbindex.nl/rss/alt.binaries.teevee/?sort=agedesc&minsize=100&complete=1&max=50&more=1&q=%23a.b.teevee", It.IsAny())) - .Returns(OpenRead("Files", "Rss", "nzbindex.xml")); - - Mocker.GetMock() - .Setup(h => h.DownloadStream("http://www.nzbindex.nl/rss/alt.binaries.hdtv/?sort=agedesc&minsize=100&complete=1&max=50&more=1&q=", It.IsAny())) - .Returns(OpenRead("Files", "Rss", "nzbindex.xml")); - - var parseResults = Mocker.Resolve().FetchRss(); - - foreach (var episodeParseResult in parseResults) - { - episodeParseResult.NzbInfoUrl.Should().Contain(expectedString); - } - } - - [Test] - public void NzbClub_NzbInfoUrl_should_contain_information_string() - { - WithConfiguredIndexers(); - - const string fileName = "nzbclub.xml"; - const string expectedString = "nzb_view"; - - Mocker.GetMock() - .Setup(h => h.DownloadStream("http://www.nzbclub.com/nzbfeed.aspx?ig=2&gid=102952&st=1&ns=1&q=%23a.b.teevee", It.IsAny())) - .Returns(OpenRead("Files", "Rss", "" + fileName)); - - Mocker.GetMock() - .Setup(h => h.DownloadStream("http://www.nzbclub.com/nzbfeed.aspx?ig=2&gid=5542&st=1&ns=1&q=", It.IsAny())) - .Returns(OpenRead("Files", "Rss", "" + fileName)); - - var parseResults = Mocker.Resolve().FetchRss(); - - foreach (var episodeParseResult in parseResults) - { - episodeParseResult.NzbInfoUrl.Should().Contain(expectedString); - } - } - - [TestCase("30 Rock", "30%20Rock")] - [TestCase("The Office (US)", "Office%20US")] - [TestCase("Revenge", "Revenge")] - [TestCase(" Top Gear ", "Top%20Gear")] - [TestCase("Breaking Bad", "Breaking%20Bad")] - [TestCase("Top Chef (US)", "Top%20Chef%20US")] - [TestCase("Castle (2009)", "Castle%202009")] - public void newznab_GetQueryTitle_should_return_expected_result(string seriesTitle, string expected) - { - Mocker.Resolve().GetQueryTitle(seriesTitle).Should().Be(expected); - } - - [Test] - public void should_get_nzbInfoUrl_for_omgwtfnzbs() - { - WithConfiguredIndexers(); - - Mocker.GetMock() - .Setup(h => h.DownloadStream("http://rss.omgwtfnzbs.org/rss-search.php?catid=19,20&user=MockedConfigValue&api=MockedConfigValue&eng=1", It.IsAny())) - .Returns(OpenRead("Files", "Rss", "SizeParsing", "omgwtfnzbs.xml")); - - - var parseResults = Mocker.Resolve().FetchRss(); - - parseResults.Should().HaveCount(1); - parseResults[0].NzbInfoUrl.Should().Be("http://omgwtfnzbs.org/details.php?id=OAl4g"); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/IndexerTests/NzbxFixture.cs b/NzbDrone.Core.Test/IndexerTests/NzbxFixture.cs deleted file mode 100644 index 7cb417ee3..000000000 --- a/NzbDrone.Core.Test/IndexerTests/NzbxFixture.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Net; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Common; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Test.Framework; - -namespace NzbDrone.Core.Test.IndexerTests -{ - [TestFixture] - - public class NzbxFixture : CoreTest - { - [Test] - public void should_get_size_when_parsing_recent_feed() - { - Mocker.GetMock() - .Setup(h => h.DownloadString("https://nzbx.co/api/recent?category=tv", It.IsAny())) - .Returns(ReadAllText("Files", "Rss", "SizeParsing", "nzbx_recent.json")); - - - var parseResults = Mocker.Resolve().FetchRss(); - - parseResults.Should().HaveCount(1); - parseResults[0].Size.Should().Be(890190951); - } - - [Test] - public void should_get_size_when_parsing_search_results() - { - Mocker.GetMock() - .Setup(h => h.DownloadString("https://nzbx.co/api/search?q=30+Rock+S01E01", It.IsAny())) - .Returns(ReadAllText("Files", "Rss", "SizeParsing", "nzbx_search.json")); - - - var parseResults = Mocker.Resolve().FetchEpisode("30 Rock", 1, 1); - - parseResults.Should().HaveCount(1); - parseResults[0].Size.Should().Be(418067671); - } - - [Test] - public void should_be_able_parse_results_from_recent_feed() - { - Mocker.GetMock() - .Setup(h => h.DownloadString(It.IsAny(), It.IsAny())) - .Returns(ReadAllText("Files", "Rss", "nzbx_recent.json")); - - var parseResults = Mocker.Resolve().FetchRss(); - - parseResults.Should().NotBeEmpty(); - parseResults.Should().OnlyContain(s => s.Indexer == "nzbx"); - parseResults.Should().OnlyContain(s => !String.IsNullOrEmpty(s.OriginalString)); - parseResults.Should().OnlyContain(s => s.Age >= 0); - } - - [Test] - public void should_be_able_to_parse_results_from_search_results() - { - Mocker.GetMock() - .Setup(h => h.DownloadString(It.IsAny(), It.IsAny())) - .Returns(ReadAllText("Files", "Rss", "nzbx_search.json")); - - var parseResults = Mocker.Resolve().FetchEpisode("30 Rock", 1, 1); - - parseResults.Should().NotBeEmpty(); - parseResults.Should().OnlyContain(s => s.Indexer == "nzbx"); - parseResults.Should().OnlyContain(s => !String.IsNullOrEmpty(s.OriginalString)); - parseResults.Should().OnlyContain(s => s.Age >= 0); - } - - [Test] - public void should_get_postedDate_when_parsing_recent_feed() - { - var expectedAge = DateTime.Today.Subtract(new DateTime(2012, 12, 21)).Days; - - Mocker.GetMock() - .Setup(h => h.DownloadString("https://nzbx.co/api/recent?category=tv", It.IsAny())) - .Returns(ReadAllText("Files", "Rss", "SizeParsing", "nzbx_recent.json")); - - - var parseResults = Mocker.Resolve().FetchRss(); - - parseResults.Should().HaveCount(1); - parseResults[0].Age.Should().Be(expectedAge); - } - - [Test] - public void should_get_postedDate_when_parsing_search_results() - { - var expectedAge = DateTime.Today.Subtract(new DateTime(2012, 2, 11)).Days; - - Mocker.GetMock() - .Setup(h => h.DownloadString("https://nzbx.co/api/search?q=30+Rock+S01E01", It.IsAny())) - .Returns(ReadAllText("Files", "Rss", "SizeParsing", "nzbx_search.json")); - - - var parseResults = Mocker.Resolve().FetchEpisode("30 Rock", 1, 1); - - parseResults.Should().HaveCount(1); - parseResults[0].Age.Should().Be(expectedAge); - } - - [Test] - public void should_name_nzb_properly() - { - Mocker.GetMock() - .Setup(h => h.DownloadString("https://nzbx.co/api/recent?category=tv", It.IsAny())) - .Returns(ReadAllText("Files", "Rss", "SizeParsing", "nzbx_recent.json")); - - - var parseResults = Mocker.Resolve().FetchRss(); - - parseResults.Should().HaveCount(1); - parseResults[0].NzbUrl.Should().EndWith(parseResults[0].OriginalString); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/Indexers/IndexerServiceTest.cs b/NzbDrone.Core.Test/Indexers/IndexerServiceTest.cs deleted file mode 100644 index 600a963d9..000000000 --- a/NzbDrone.Core.Test/Indexers/IndexerServiceTest.cs +++ /dev/null @@ -1,362 +0,0 @@ - - -using System; -using System.Collections.Generic; -using System.Net; -using System.ServiceModel.Syndication; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Common; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Model; -using NzbDrone.Core.Providers; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Test.Common.AutoMoq; - -namespace NzbDrone.Core.Test.Indexers -{ - [TestFixture] - - public class IndexerServiceTest : CoreTest - { - [Test] - public void should_insert_indexer_in_repository_when_it_doesnt_exist() - { - Mocker.SetConstant>(new List { Mocker.Resolve() }); - - Subject.Init(); - - Mocker.GetMock() - .Verify(v => v.Insert(It.IsAny()), Times.Once()); - } - - [Test] - public void getEnabled_should_not_return_any_when_no_indexers_are_enabled() - { - Mocker.SetConstant>(new List { Mocker.Resolve() }); - - Mocker.GetMock() - .Setup(s => s.All()) - .Returns(new List {new Indexer {Id = 1, Type = "", Enable = false, Name = "Fake Indexer"}}); - - Subject.GetEnabledIndexers().Should().BeEmpty(); - } - - [Test] - public void Init_indexer_should_enable_indexer_that_is_enabled_by_default() - { - Mocker.SetConstant>(new List { Mocker.Resolve() }); - - Subject.Init(); - - Mocker.GetMock() - .Verify(v => v.Insert(It.Is(indexer => indexer.Enable)), Times.Once()); - - Mocker.GetMock() - .Verify(v => v.Insert(It.Is(indexer => !indexer.Enable)), Times.Never()); - } - - [Test] - public void Init_indexer_should_not_enable_indexer_that_is_not_enabled_by_default() - { - Mocker.SetConstant>(new List { Mocker.Resolve() }); - - Subject.Init(); - - Mocker.GetMock() - .Verify(v => v.Insert(It.Is(indexer => indexer.Enable)), Times.Never()); - - Mocker.GetMock() - .Verify(v => v.Insert(It.Is(indexer => !indexer.Enable)), Times.Once()); - } - } - - public class MockIndexer : IndexerBase - { - public MockIndexer(HttpProvider httpProvider, IConfigService configService) - : base(httpProvider, configService) - { - } - - protected override string[] Urls - { - get { return new[] { "www.google.com" }; } - } - - public override bool IsConfigured - { - get { return true; } - } - - protected override NetworkCredential Credentials - { - get { return null; } - } - - protected override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) - { - throw new NotImplementedException(); - } - - public override string Name - { - get { return "Mocked Indexer"; } - } - - protected override string NzbDownloadUrl(SyndicationItem item) - { - return item.Links[0].Uri.ToString(); - } - - protected override string NzbInfoUrl(SyndicationItem item) - { - return item.Links[0].Uri.ToString(); - } - } - - public class TestUrlIndexer : IndexerBase - { - public TestUrlIndexer(HttpProvider httpProvider, IConfigService configService) - : base(httpProvider, configService) - { - } - - public override string Name - { - get { return "All Urls"; } - } - - protected override string[] Urls - { - get { return new[] { "http://rss.nzbs.com/rss.php?cat=TV" }; } - } - - public override bool IsConfigured - { - get { return true; } - } - - protected override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) - { - throw new NotImplementedException(); - } - - protected override string NzbDownloadUrl(SyndicationItem item) - { - return "http://google.com"; - } - - protected override string NzbInfoUrl(SyndicationItem item) - { - return "http://google.com"; - } - } - - public class CustomParserIndexer : IndexerBase - { - public CustomParserIndexer(HttpProvider httpProvider, IConfigService configService) - : base(httpProvider, configService) - { - } - - public override string Name - { - get { return "Custom parser"; } - } - - protected override string[] Urls - { - get { return new[] { "http://www.google.com" }; } - } - - public override bool IsConfigured - { - get { return true; } - } - - protected override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) - { - throw new NotImplementedException(); - } - - protected override string NzbDownloadUrl(SyndicationItem item) - { - return "http://www.google.com"; - } - - protected override string NzbInfoUrl(SyndicationItem item) - { - return "http://www.google.com"; - } - - protected override EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult) - { - if (currentResult == null) currentResult = new EpisodeParseResult(); - currentResult.Language = LanguageType.Finnish; - return currentResult; - } - } - - public class NotConfiguredIndexer : IndexerBase - { - public NotConfiguredIndexer(HttpProvider httpProvider, IConfigService configService) - : base(httpProvider, configService) - { - } - - public override string Name - { - get { return "NotConfiguredIndexer"; } - } - - protected override string[] Urls - { - get { return new[] { "http://rss.nzbs.com/rss.php?cat=TV" }; } - } - - public override bool IsConfigured - { - get { return false; } - } - - protected override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) - { - throw new NotImplementedException(); - } - - protected override string NzbDownloadUrl(SyndicationItem item) - { - throw new NotImplementedException(); - } - - protected override string NzbInfoUrl(SyndicationItem item) - { - throw new NotImplementedException(); - } - } - - public class DefaultEnabledIndexer : IndexerBase - { - public DefaultEnabledIndexer(HttpProvider httpProvider, IConfigService configService) - : base(httpProvider, configService) - { - } - - protected override string[] Urls - { - get { return new[] { "www.google.com" }; } - } - - public override bool IsConfigured - { - get { return true; } - } - - protected override NetworkCredential Credentials - { - get { return null; } - } - - protected override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) - { - throw new NotImplementedException(); - } - - protected override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) - { - throw new NotImplementedException(); - } - - public override string Name - { - get { return "Mocked Indexer"; } - } - - protected override string NzbDownloadUrl(SyndicationItem item) - { - return item.Links[0].Uri.ToString(); - } - - protected override string NzbInfoUrl(SyndicationItem item) - { - return item.Links[1].Uri.ToString(); - } - - public override bool EnabledByDefault - { - get { return true; } - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/Indexers/NewznabServiceTest.cs b/NzbDrone.Core.Test/Indexers/NewznabServiceTest.cs deleted file mode 100644 index 8dca0af3e..000000000 --- a/NzbDrone.Core.Test/Indexers/NewznabServiceTest.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Sockets; -using System.Security.Policy; -using FizzWare.NBuilder; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Test.Common; - -namespace NzbDrone.Core.Test.Indexers -{ - [TestFixture] - - public class NewznabProviderTest : CoreTest - { - private void WithInvalidName() - { - Mocker.GetMock() - .Setup(s => s.All()) - .Returns(new List{new NewznabDefinition { Id = 1, Name = "", Url = "http://www.nzbdrone.com" }}); - } - - private void WithExisting() - { - Mocker.GetMock() - .Setup(s => s.All()) - .Returns(new List { new NewznabDefinition { Id = 1, Name = "Nzbs.org", Url = "http://nzbs.org" } }); - } - - [Test] - public void InitializeNewznabIndexers_should_initialize_build_in_indexers() - { - Subject.Init(); - - Mocker.GetMock() - .Verify(s => s.Insert(It.Is(n => n.BuiltIn)), Times.Exactly(3)); - } - - [Test] - public void should_delete_indexers_without_names() - { - WithInvalidName(); - - Subject.Init(); - - Mocker.GetMock() - .Verify(s => s.Delete(1), Times.Once()); - } - - [Test] - public void should_add_new_indexers() - { - WithExisting(); - - Subject.Init(); - - Mocker.GetMock() - .Verify(s => s.Insert(It.IsAny()), Times.Exactly(2)); - } - - [Test] - public void should_update_existing() - { - WithExisting(); - - Subject.Init(); - - Mocker.GetMock() - .Verify(s => s.Update(It.IsAny()), Times.Once()); - } - - [Test] - public void CheckHostname_should_do_nothing_if_hostname_is_valid() - { - Subject.CheckHostname("http://www.google.com"); - } - - [Test] - public void CheckHostname_should_log_error_and_throw_exception_if_dnsHostname_is_invalid() - { - Assert.Throws(() => Subject.CheckHostname("http://BadName")); - - ExceptionVerification.ExpectedErrors(1); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/JobTests/BacklogSearchJobTest.cs b/NzbDrone.Core.Test/JobTests/BacklogSearchJobTest.cs deleted file mode 100644 index 8dc93b826..000000000 --- a/NzbDrone.Core.Test/JobTests/BacklogSearchJobTest.cs +++ /dev/null @@ -1,342 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using FizzWare.NBuilder; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Jobs.Implementations; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Jobs; -using NzbDrone.Core.Model; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Providers; - -using NzbDrone.Core.Test.Framework; -using NzbDrone.Test.Common; - -namespace NzbDrone.Core.Test.JobTests -{ - [TestFixture] - public class BacklogSearchJobTest : CoreTest - { - private void WithEnableBacklogSearching() - { - Mocker.GetMock().SetupGet(s => s.EnableBacklogSearching).Returns(true); - } - - [Test] - public void no_missing_epsiodes_should_not_trigger_any_search() - { - - var notification = new ProgressNotification("Backlog Search Job Test"); - - var episodes = new List(); - - WithStrictMocker(); - WithEnableBacklogSearching(); - - Mocker.GetMock() - .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); - - - Subject.Start(notification, null); - - - Mocker.GetMock().Verify(c => c.Start(notification, new { SeriesId = It.IsAny(), SeasonNumber = It.IsAny() }), - Times.Never()); - - Mocker.GetMock().Verify(c => c.Start(notification, new { SeriesId = It.IsAny(), SeasonNumber = 0 }), - Times.Never()); - } - - [Test] - public void individual_missing_episode() - { - - var notification = new ProgressNotification("Backlog Search Job Test"); - - var series = Builder.CreateNew() - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .Build(); - - var episodes = Builder.CreateListOfSize(1) - .All() - .With(e => e.Series = series) - .Build().ToList(); - - WithEnableBacklogSearching(); - - Mocker.GetMock() - .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); - - Mocker.GetMock() - .Setup(s => s.Start(notification, It.Is(d => d.GetPropertyValue("EpisodeId") == 1))); - - - Subject.Start(notification, null); - - - Mocker.GetMock().Verify(c => c.Start(notification, It.Is(d => d.GetPropertyValue("EpisodeId") >= 0)), - Times.Once()); - } - - [Test] - public void individual_missing_episodes_only() - { - - var notification = new ProgressNotification("Backlog Search Job Test"); - - var series = Builder.CreateNew() - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .Build(); - - var episodes = Builder.CreateListOfSize(5) - .All() - .With(e => e.Series = series) - .Build().ToList(); - - WithEnableBacklogSearching(); - - Mocker.GetMock() - .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); - - - Subject.Start(notification, null); - - - Mocker.GetMock().Verify(c => c.Start(notification, It.Is(d => d.GetPropertyValue("EpisodeId") >= 0)), - Times.Exactly(episodes.Count)); - } - - [Test] - public void series_season_missing_episodes_only_mismatch_count() - { - - var notification = new ProgressNotification("Backlog Search Job Test"); - - var series = Builder.CreateNew() - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .Build(); - - var episodes = Builder.CreateListOfSize(5) - .All() - .With(e => e.Series = series) - .With(e => e.SeasonNumber = 1) - .Build().ToList(); - - WithEnableBacklogSearching(); - - Mocker.GetMock() - .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); - - Mocker.GetMock() - .Setup(s => s.GetEpisodeNumbersBySeason(1, 1)).Returns(new List { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); - - - Subject.Start(notification, null); - - - Mocker.GetMock().Verify(c => c.Start(notification, It.Is(d => d.GetPropertyValue("EpisodeId") >= 0)), - Times.Exactly(episodes.Count)); - } - - [Test] - public void series_season_missing_episodes_only() - { - - var notification = new ProgressNotification("Backlog Search Job Test"); - - var series = Builder.CreateNew() - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .Build(); - - var episodes = Builder.CreateListOfSize(5) - .All() - .With(e => e.Series = series) - .With(e => e.SeriesId = series.Id) - .With(e => e.SeasonNumber = 1) - .Build().ToList(); - - WithEnableBacklogSearching(); - - Mocker.GetMock() - .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); - - Mocker.GetMock() - .Setup(s => s.GetEpisodeNumbersBySeason(1, 1)).Returns(episodes.Select(e => e.EpisodeNumber).ToList()); - - - Subject.Start(notification, null); - - - Mocker.GetMock().Verify(c => c.Start(notification, It.Is(d => d.GetPropertyValue("SeriesId") >= 0 && - d.GetPropertyValue("SeasonNumber") >= 0)), - Times.Once()); - } - - [Test] - public void multiple_missing_episodes() - { - - var notification = new ProgressNotification("Backlog Search Job Test"); - - var series = Builder.CreateNew() - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .Build(); - - var series2 = Builder.CreateNew() - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .Build(); - - var episodes = Builder.CreateListOfSize(10) - .TheFirst(5) - .With(e => e.Series = series) - .With(e => e.SeriesId = series.Id) - .With(e => e.SeasonNumber = 1) - .TheNext(5) - .With(e => e.Series = series2) - .Build().ToList(); - - WithEnableBacklogSearching(); - - Mocker.GetMock() - .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); - - Mocker.GetMock() - .Setup(s => s.GetEpisodeNumbersBySeason(1, 1)).Returns(new List { 1, 2, 3, 4, 5 }); - - - Subject.Start(notification, null); - - - Mocker.GetMock().Verify(c => c.Start(notification, It.Is(d => d.GetPropertyValue("SeriesId") >= 0 && - d.GetPropertyValue("SeasonNumber") >= 0)), - Times.Once()); - - Mocker.GetMock().Verify(c => c.Start(notification, It.Is(d => d.GetPropertyValue("EpisodeId") >= 0)), - Times.Exactly(5)); - } - - [Test] - public void GetMissingForEnabledSeries_should_only_return_episodes_for_monitored_series() - { - - var series = Builder.CreateListOfSize(2) - .TheFirst(1) - .With(s => s.Monitored = false) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .TheNext(1) - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .Build(); - - var episodes = Builder.CreateListOfSize(11) - .TheFirst(5) - .With(e => e.Series = series[0]) - .With(e => e.SeasonNumber = 1) - .TheLast(6) - .With(e => e.Series = series[1]) - .Build().ToList(); - - WithEnableBacklogSearching(); - - Mocker.GetMock() - .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); - - - var result = Subject.GetMissingForEnabledSeries(); - - - result.Should().NotBeEmpty(); - result.Should().Contain(s => s.Series.Monitored); - result.Should().NotContain(s => !s.Series.Monitored); - } - - [Test] - public void GetMissingForEnabledSeries_should_only_return_explicity_enabled_series_when_backlog_searching_is_ignored() - { - - var series = Builder.CreateListOfSize(3) - .TheFirst(1) - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Disable) - .TheNext(1) - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .TheNext(1) - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Inherit) - .Build(); - - var episodes = Builder.CreateListOfSize(12) - .TheFirst(3) - .With(e => e.Series = series[0]) - .TheNext(4) - .With(e => e.Series = series[1]) - .TheNext(5) - .With(e => e.Series = series[2]) - .Build().ToList(); - - //WithEnableBacklogSearching(); - - Mocker.GetMock() - .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); - - - var result = Subject.GetMissingForEnabledSeries(); - - - result.Should().NotBeEmpty(); - result.Should().Contain(s => s.Series.BacklogSetting == BacklogSettingType.Enable); - result.Should().NotContain(s => s.Series.BacklogSetting == BacklogSettingType.Disable); - result.Should().NotContain(s => s.Series.BacklogSetting == BacklogSettingType.Inherit); - } - - [Test] - public void GetMissingForEnabledSeries_should_return_explicity_enabled_and_inherit_series_when_backlog_searching_is_enabled() - { - - var series = Builder.CreateListOfSize(3) - .TheFirst(1) - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Disable) - .TheNext(1) - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .TheNext(1) - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Inherit) - .Build(); - - var episodes = Builder.CreateListOfSize(12) - .TheFirst(3) - .With(e => e.Series = series[0]) - .TheNext(4) - .With(e => e.Series = series[1]) - .TheNext(5) - .With(e => e.Series = series[2]) - .Build().ToList(); - - WithEnableBacklogSearching(); - - Mocker.GetMock() - .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); - - - var result = Subject.GetMissingForEnabledSeries(); - - - result.Should().NotBeEmpty(); - result.Should().Contain(s => s.Series.BacklogSetting == BacklogSettingType.Enable); - result.Should().NotContain(s => s.Series.BacklogSetting == BacklogSettingType.Disable); - result.Should().Contain(s => s.Series.BacklogSetting == BacklogSettingType.Inherit); - } - } -} diff --git a/NzbDrone.Core.Test/JobTests/RecentBacklogSearchJobTest.cs b/NzbDrone.Core.Test/JobTests/RecentBacklogSearchJobTest.cs deleted file mode 100644 index 33a191eaa..000000000 --- a/NzbDrone.Core.Test/JobTests/RecentBacklogSearchJobTest.cs +++ /dev/null @@ -1,219 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -using FizzWare.NBuilder; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Jobs.Implementations; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Jobs; -using NzbDrone.Core.Model; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Providers; - -using NzbDrone.Core.Test.Framework; -using NzbDrone.Test.Common; -using NzbDrone.Test.Common.AutoMoq; - -namespace NzbDrone.Core.Test.JobTests -{ - [TestFixture] - public class RecentBacklogSearchJobTest : CoreTest - { - private void WithEnableBacklogSearching() - { - Mocker.GetMock().SetupGet(s => s.EnableBacklogSearching).Returns(true); - } - - [SetUp] - public void Setup() - { - - } - - [Test] - public void no_missing_epsiodes_should_not_trigger_any_search() - { - - var episodes = new List(); - - Mocker.GetMock() - .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); - - - Mocker.Resolve().Start(MockNotification, null); - - - Mocker.GetMock().Verify(c => c.Start(MockNotification, new { EpisodeId = It.IsAny() }), - Times.Never()); - } - - [Test] - public void should_only_process_missing_episodes_from_the_last_30_days() - { - WithEnableBacklogSearching(); - - var series = Builder.CreateNew() - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .Build(); - - - var episodes = Builder.CreateListOfSize(50) - .All() - .With(e => e.Series = series) - .TheFirst(5) - .With(e => e.AirDate = DateTime.Today) - .TheNext(5) - .With(e => e.AirDate = DateTime.Today.AddDays(-1)) //Today - .TheNext(5) - .With(e => e.AirDate = DateTime.Today.AddDays(-5)) //Yeserday - .TheNext(5) - .With(e => e.AirDate = DateTime.Today.AddDays(-10)) - .TheNext(5) - .With(e => e.AirDate = DateTime.Today.AddDays(-15)) - .TheNext(5) - .With(e => e.AirDate = DateTime.Today.AddDays(-20)) - .TheNext(5) - .With(e => e.AirDate = DateTime.Today.AddDays(-25)) - .TheNext(5) - .With(e => e.AirDate = DateTime.Today.AddDays(-30)) - .TheNext(5) - .With(e => e.AirDate = DateTime.Today.AddDays(-31)) //31 Days - .TheNext(5) - .With(e => e.AirDate = DateTime.Today.AddDays(-35)) - .Build().ToList(); - - Mocker.GetMock() - .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); - - Mocker.GetMock().Setup(c => c.Start(It.IsAny(), It.Is(d => d.GetPropertyValue("EpisodeId") >= 0))); - - - Mocker.Resolve().Start(MockNotification, null); - - - Mocker.GetMock().Verify(c => c.Start(It.IsAny(), It.Is(d => d.GetPropertyValue("EpisodeId") >= 0)), - Times.Exactly(40)); - } - - [Test] - public void GetMissingForEnabledSeries_should_only_return_episodes_for_monitored_series() - { - - var series = Builder.CreateListOfSize(2) - .TheFirst(1) - .With(s => s.Monitored = false) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .TheNext(1) - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .Build(); - - var episodes = Builder.CreateListOfSize(11) - .TheFirst(5) - .With(e => e.Series = series[0]) - .With(e => e.SeasonNumber = 1) - .TheLast(6) - .With(e => e.Series = series[1]) - .Build().ToList(); - - WithEnableBacklogSearching(); - - Mocker.GetMock() - .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); - - - var result = Mocker.Resolve().GetMissingForEnabledSeries(); - - - result.Should().NotBeEmpty(); - result.Should().Contain(s => s.Series.Monitored); - result.Should().NotContain(s => !s.Series.Monitored); - } - - [Test] - public void GetMissingForEnabledSeries_should_only_return_explicity_enabled_series_when_backlog_searching_is_ignored() - { - - var series = Builder.CreateListOfSize(3) - .TheFirst(1) - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Disable) - .TheNext(1) - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .TheNext(1) - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Inherit) - .Build(); - - var episodes = Builder.CreateListOfSize(12) - .TheFirst(3) - .With(e => e.Series = series[0]) - .TheNext(4) - .With(e => e.Series = series[1]) - .TheNext(5) - .With(e => e.Series = series[2]) - .Build().ToList(); - - //WithEnableBacklogSearching(); - - Mocker.GetMock() - .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); - - - var result = Mocker.Resolve().GetMissingForEnabledSeries(); - - - result.Should().NotBeEmpty(); - result.Should().Contain(s => s.Series.BacklogSetting == BacklogSettingType.Enable); - result.Should().NotContain(s => s.Series.BacklogSetting == BacklogSettingType.Disable); - result.Should().NotContain(s => s.Series.BacklogSetting == BacklogSettingType.Inherit); - } - - [Test] - public void GetMissingForEnabledSeries_should_return_explicity_enabled_and_inherit_series_when_backlog_searching_is_enabled() - { - - var series = Builder.CreateListOfSize(3) - .TheFirst(1) - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Disable) - .TheNext(1) - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Enable) - .TheNext(1) - .With(s => s.Monitored = true) - .With(s => s.BacklogSetting = BacklogSettingType.Inherit) - .Build(); - - var episodes = Builder.CreateListOfSize(12) - .TheFirst(3) - .With(e => e.Series = series[0]) - .TheNext(4) - .With(e => e.Series = series[1]) - .TheNext(5) - .With(e => e.Series = series[2]) - .Build().ToList(); - - WithEnableBacklogSearching(); - - Mocker.GetMock() - .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); - - - var result = Mocker.Resolve().GetMissingForEnabledSeries(); - - - result.Should().NotBeEmpty(); - result.Should().Contain(s => s.Series.BacklogSetting == BacklogSettingType.Enable); - result.Should().NotContain(s => s.Series.BacklogSetting == BacklogSettingType.Disable); - result.Should().Contain(s => s.Series.BacklogSetting == BacklogSettingType.Inherit); - } - - } -} diff --git a/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs b/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs deleted file mode 100644 index 51f4732ed..000000000 --- a/NzbDrone.Core.Test/JobTests/SeasonSearchJobTest.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -using FizzWare.NBuilder; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Jobs.Implementations; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Providers; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Test.Common; - -namespace NzbDrone.Core.Test.JobTests -{ - [TestFixture] - - public class SeasonSearchJobTest : CoreTest - { - private List _episodes; - - private ProgressNotification notification; - - [SetUp] - public void Setup() - { - notification = new ProgressNotification("Search"); - - _episodes = Builder.CreateListOfSize(5) - .All() - .With(e => e.SeriesId = 1) - .With(e => e.SeasonNumber = 1) - .With(e => e.Ignored = false) - .With(e => e.AirDate = DateTime.Today.AddDays(-1)) - .Build().ToList(); - - Mocker.GetMock() - .Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(_episodes); - } - - [Test] - public void SeasonSearch_partial_season_success() - { - Mocker.GetMock() - .Setup(c => c.PartialSeasonSearch(notification, 1, 1)) - .Returns(_episodes.Select(e => e.EpisodeNumber).ToList()); - - - Mocker.Resolve().Start(notification, new { SeriesId = 1, SeasonNumber = 1 }); - - - Mocker.VerifyAllMocks(); - Mocker.GetMock().Verify(c => c.PartialSeasonSearch(notification, 1, 1), Times.Once()); - Mocker.GetMock().Verify(c => c.Start(notification, new { EpisodeId = It.IsAny() }), Times.Never()); - } - - [Test] - public void SeasonSearch_partial_season_failure() - { - Mocker.GetMock() - .Setup(c => c.PartialSeasonSearch(notification, 1, 1)) - .Returns(new List()); - - - Mocker.Resolve().Start(notification, new { SeriesId = 1, SeasonNumber = 1 }); - - - Mocker.GetMock().Verify(c => c.PartialSeasonSearch(notification, 1, 1), Times.Once()); - } - - [Test] - public void SeasonSearch_should_not_search_for_episodes_that_havent_aired_yet_or_air_tomorrow() - { - var episodes = Builder.CreateListOfSize(5) - .All() - .With(e => e.SeriesId = 1) - .With(e => e.SeasonNumber = 1) - .With(e => e.Ignored = false) - .With(e => e.AirDate = DateTime.Today.AddDays(-1)) - .TheLast(2) - .With(e => e.AirDate = DateTime.Today.AddDays(2)) - .Build().ToList(); - - Mocker.GetMock() - .Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(episodes); - - Mocker.GetMock() - .Setup(c => c.PartialSeasonSearch(notification, 1, 1)) - .Returns(new List{1}); - - - - Mocker.Resolve().Start(notification, new { SeriesId = 1, SeasonNumber = 1 }); - - - Mocker.VerifyAllMocks(); - Mocker.GetMock().Verify(c => c.PartialSeasonSearch(notification, 1, 1), Times.Once()); - } - - [Test] - public void SeasonSearch_should_allow_searching_of_season_zero() - { - Mocker.GetMock() - .Setup(c => c.PartialSeasonSearch(notification, 1, 0)).Returns(new List()); - - - Mocker.Resolve().Start(notification, new { SeriesId = 1, SeasonNumber = 0 }); - - - Mocker.GetMock().Verify(c => c.PartialSeasonSearch(notification, 1, 1), Times.Never()); - Mocker.GetMock().Verify(c => c.Start(notification, new { EpisodeId = It.IsAny() }), Times.Never()); - - ExceptionVerification.ExpectedWarns(1); - } - - [Test] - public void should_search_for_individual_episodes_when_no_partial_results_are_returned() - { - Mocker.GetMock() - .Setup(c => c.PartialSeasonSearch(notification, 1, 1)).Returns(new List()); - - Mocker.Resolve().Start(notification, new { SeriesId = 1, SeasonNumber = 1 }); - - Mocker.GetMock().Verify(v => v.Start(notification, It.Is(o => o.GetPropertyValue("EpisodeId") > 0)), Times.Exactly(_episodes.Count)); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/JobTests/SeriesSearchJobTest.cs b/NzbDrone.Core.Test/JobTests/SeriesSearchJobTest.cs deleted file mode 100644 index 53978bf70..000000000 --- a/NzbDrone.Core.Test/JobTests/SeriesSearchJobTest.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Collections.Generic; - -using Moq; -using NUnit.Framework; -using NzbDrone.Core.Jobs.Implementations; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Jobs; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Providers; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Test.Common; -using NzbDrone.Test.Common.AutoMoq; - -namespace NzbDrone.Core.Test.JobTests -{ - [TestFixture] - - public class SeriesSearchJobTest : CoreTest - { - [Test] - public void SeriesSearch_success() - { - var seasons = new List { 1, 2, 3, 4, 5 }; - - WithStrictMocker(); - - var notification = new ProgressNotification("Series Search"); - - Mocker.GetMock() - .Setup(c => c.GetSeasonNumbers(1)).Returns(seasons); - - Mocker.GetMock() - .Setup(c => c.IsIgnored(It.IsAny(), It.IsAny())).Returns(false); - - Mocker.GetMock() - .Setup(c => c.Start(notification, It.Is(d => d.GetPropertyValue("SeriesId") == 1 && d.GetPropertyValue("SeasonNumber") >= 0))).Verifiable(); - - - Mocker.Resolve().Start(notification, new { SeriesId = 1 }); - - - Mocker.VerifyAllMocks(); - Mocker.GetMock().Verify(c => c.Start(notification, It.Is(d => d.GetPropertyValue("SeriesId") == 1 && d.GetPropertyValue("SeasonNumber") >= 0)), - Times.Exactly(seasons.Count)); - } - - [Test] - public void SeriesSearch_no_seasons() - { - var seasons = new List(); - - WithStrictMocker(); - - var notification = new ProgressNotification("Series Search"); - - Mocker.GetMock() - .Setup(c => c.GetSeasonNumbers(1)).Returns(seasons); - - - Mocker.Resolve().Start(notification, new { SeriesId = 1 }); - - - Mocker.VerifyAllMocks(); - Mocker.GetMock().Verify(c => c.Start(notification, new { SeriesId = 1, SeasonNumber = It.IsAny() }), - Times.Never()); - } - - [Test] - public void SeriesSearch_should_not_search_for_season_0() - { - Mocker.GetMock() - .Setup(c => c.GetSeasonNumbers(It.IsAny())) - .Returns(new List { 0, 1, 2 }); - - Mocker.Resolve().Start(MockNotification, new { SeriesId = 12 }); - - - Mocker.GetMock() - .Verify(c => c.Start(It.IsAny(), new { SeriesId = It.IsAny(), SeasonNumber = 0 }), Times.Never()); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/License.txt b/NzbDrone.Core.Test/License.txt deleted file mode 100644 index 5ead6991a..000000000 --- a/NzbDrone.Core.Test/License.txt +++ /dev/null @@ -1,22 +0,0 @@ - Copyright (c) 2010 Darren Cauthon - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 5affffc91..f7c03b625 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -134,17 +134,7 @@ - - - - - - - - - - - + @@ -157,9 +147,7 @@ - - @@ -181,7 +169,6 @@ - @@ -199,8 +186,6 @@ - - @@ -211,12 +196,10 @@ - - diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.ncrunchproject b/NzbDrone.Core.Test/NzbDrone.Core.Test.ncrunchproject deleted file mode 100644 index 41ce94d3a..000000000 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.ncrunchproject +++ /dev/null @@ -1,24 +0,0 @@ - - false - false - false - true - false - false - true - true - true - true - false - true - true - 60000 - - - - AutoDetect - STA - x86 - ..\Libraries\Sqlite\sqlite3.dll - PostBuildEventDisabled;PreBuildEventDisabled - \ No newline at end of file diff --git a/NzbDrone.Core.Test/test-results/NzbDrone.Core.Test.csproj-Debug-2013-02-16.xml b/NzbDrone.Core.Test/test-results/NzbDrone.Core.Test.csproj-Debug-2013-02-16.xml deleted file mode 100644 index 0102fcced..000000000 --- a/NzbDrone.Core.Test/test-results/NzbDrone.Core.Test.csproj-Debug-2013-02-16.xml +++ /dev/null @@ -1,230 +0,0 @@ - - - - - - - - - - - - - - - 2013-02-16T21:37:42 - 0 - 0 - 0 - 0 - 0 - 0 - 9 - - - - - - - 2013-02-16T21:37:42 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - - - - - - - 2013-02-16T21:37:42 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - - - - - - - 2013-02-16T21:37:42 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - - - - - - - 2013-02-16T21:37:42 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - - - - - - - 2013-02-16T21:37:42 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - - - - - - - 2013-02-16T21:37:42 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - - - - - - - 2013-02-16T21:37:42 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - - - - - - - 2013-02-16T21:37:42 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - - - - - - - 2013-02-16T21:37:42 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/NzbDrone.Core/DataAugmentation/Scene/SceneMappingProvider.cs b/NzbDrone.Core/DataAugmentation/Scene/SceneMappingProvider.cs index ca987fc5c..24de07b36 100644 --- a/NzbDrone.Core/DataAugmentation/Scene/SceneMappingProvider.cs +++ b/NzbDrone.Core/DataAugmentation/Scene/SceneMappingProvider.cs @@ -2,13 +2,14 @@ using System; using System.Linq; using NLog; using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Tv; namespace NzbDrone.Core.DataAugmentation.Scene { public interface ISceneMappingService { void UpdateMappings(); - string GetSceneName(int tvdbId, int seasonNumber = -1); + string GetSceneName(int seriesId, int seasonNumber = -1); Nullable GetTvDbId(string cleanName); string GetCleanName(int tvdbId); } @@ -17,12 +18,14 @@ namespace NzbDrone.Core.DataAugmentation.Scene { private readonly ISceneMappingRepository _repository; private readonly ISceneMappingProxy _sceneMappingProxy; + private readonly ISeriesService _seriesService; private readonly Logger _logger; - public SceneMappingService(ISceneMappingRepository repository, ISceneMappingProxy sceneMappingProxy, Logger logger) + public SceneMappingService(ISceneMappingRepository repository, ISceneMappingProxy sceneMappingProxy, ISeriesService seriesService, Logger logger) { _repository = repository; _sceneMappingProxy = sceneMappingProxy; + _seriesService = seriesService; _logger = logger; } @@ -48,16 +51,20 @@ namespace NzbDrone.Core.DataAugmentation.Scene } } - public virtual string GetSceneName(int tvdbId, int seasonNumber = -1) + public string GetSceneName(int seriesId, int seasonNumber = -1) { - var mapping = _repository.FindByTvdbId(tvdbId); + var tvDbId = _seriesService.FindByTvdbId(seriesId).TvDbId; + + var mapping = _repository.FindByTvdbId(tvDbId); if (mapping == null) return null; return mapping.SceneName; } - public virtual Nullable GetTvDbId(string cleanName) + + + public Nullable GetTvDbId(string cleanName) { var mapping = _repository.FindByCleanTitle(cleanName); @@ -68,7 +75,7 @@ namespace NzbDrone.Core.DataAugmentation.Scene } - public virtual string GetCleanName(int tvdbId) + public string GetCleanName(int tvdbId) { var mapping = _repository.FindByTvdbId(tvdbId); diff --git a/NzbDrone.Core/Datastore/Migration/Migration20130324.cs b/NzbDrone.Core/Datastore/Migration/Migration20130324.cs index 27d37e943..0bc6d1cb4 100644 --- a/NzbDrone.Core/Datastore/Migration/Migration20130324.cs +++ b/NzbDrone.Core/Datastore/Migration/Migration20130324.cs @@ -100,8 +100,8 @@ namespace NzbDrone.Core.Datastore.Migration Create.TableForModel("IndexerDefinitions") .WithColumn("Enable").AsBoolean() - .WithColumn("Type").AsString().Unique() - .WithColumn("Name").AsString().Unique(); + .WithColumn("Name").AsString().Unique() + .WithColumn("Settings").AsString(); Create.TableForModel("NewznabDefinitions") .WithColumn("Enable").AsBoolean() diff --git a/NzbDrone.Core/Datastore/TableMapping.cs b/NzbDrone.Core/Datastore/TableMapping.cs index e3bbfe373..6406bbeea 100644 --- a/NzbDrone.Core/Datastore/TableMapping.cs +++ b/NzbDrone.Core/Datastore/TableMapping.cs @@ -9,6 +9,7 @@ using NzbDrone.Core.DataAugmentation.Scene; using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.ExternalNotification; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Indexers.Newznab; using NzbDrone.Core.Instrumentation; using NzbDrone.Core.Jobs; using NzbDrone.Core.MediaFiles; @@ -30,7 +31,7 @@ namespace NzbDrone.Core.Datastore Mapper.Entity().RegisterModel("Config"); Mapper.Entity().RegisterModel("RootFolders").Ignore(r => r.FreeSpace); - Mapper.Entity().RegisterModel("IndexerDefinitions"); + Mapper.Entity().RegisterModel("IndexerDefinitions"); Mapper.Entity().RegisterModel("NewznabDefinitions"); Mapper.Entity().RegisterModel("JobDefinitions"); Mapper.Entity().RegisterModel("ExternalNotificationDefinitions"); diff --git a/NzbDrone.Core/DecisionEngine/DownloadDecision.cs b/NzbDrone.Core/DecisionEngine/DownloadDecision.cs index cd2295207..cbc3a4373 100644 --- a/NzbDrone.Core/DecisionEngine/DownloadDecision.cs +++ b/NzbDrone.Core/DecisionEngine/DownloadDecision.cs @@ -1,11 +1,14 @@ using System.Collections.Generic; using System.Linq; +using NzbDrone.Core.Model; namespace NzbDrone.Core.DecisionEngine { public class DownloadDecision { + public EpisodeParseResult ParseResult { get; private set; } public IEnumerable Rejections { get; private set; } + public bool Approved { get @@ -14,9 +17,23 @@ namespace NzbDrone.Core.DecisionEngine } } - public DownloadDecision(params string[] rejections) + public DownloadDecision(EpisodeParseResult parseResult, params string[] rejections) { + ParseResult = parseResult; Rejections = rejections.ToList(); } + + + public static EpisodeParseResult PickBestReport(IEnumerable downloadDecisions) + { + var reports = downloadDecisions + .Where(c => c.Approved) + .Select(c => c.ParseResult) + .OrderByDescending(c => c.Quality) + .ThenBy(c => c.EpisodeNumbers.MinOrDefault()) + .ThenBy(c => c.Age); + + return reports.SingleOrDefault(); + } } } \ No newline at end of file diff --git a/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs new file mode 100644 index 000000000..450e66e5a --- /dev/null +++ b/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.Linq; +using NzbDrone.Core.DecisionEngine.Specifications.Search; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.DecisionEngine +{ + public interface IMakeDownloadDecision + { + IEnumerable GetRssDecision(IEnumerable episodeParseResults); + IEnumerable GetSearchDecision(IEnumerable episodeParseResult, SearchDefinitionBase searchDefinitionBase); + } + + public class DownloadDecisionMaker : IMakeDownloadDecision + { + private readonly IEnumerable _specifications; + + public DownloadDecisionMaker(IEnumerable specifications) + { + _specifications = specifications; + } + + public IEnumerable GetRssDecision(IEnumerable episodeParseResults) + { + foreach (var parseResult in episodeParseResults) + { + parseResult.Decision = new DownloadDecision(parseResult, GetGeneralRejectionReasons(parseResult).ToArray()); + yield return parseResult.Decision; + } + + } + + public IEnumerable GetSearchDecision(IEnumerable episodeParseResults, SearchDefinitionBase searchDefinitionBase) + { + foreach (var parseResult in episodeParseResults) + { + var generalReasons = GetGeneralRejectionReasons(parseResult); + var searchReasons = GetSearchRejectionReasons(parseResult, searchDefinitionBase); + + parseResult.Decision = new DownloadDecision(parseResult, generalReasons.Union(searchReasons).ToArray()); + + yield return parseResult.Decision; + } + } + + + private IEnumerable GetGeneralRejectionReasons(EpisodeParseResult episodeParseResult) + { + return _specifications + .OfType() + .Where(spec => !spec.IsSatisfiedBy(episodeParseResult)) + .Select(spec => spec.RejectionReason); + } + + private IEnumerable GetSearchRejectionReasons(EpisodeParseResult episodeParseResult, SearchDefinitionBase searchDefinitionBase) + { + return _specifications + .OfType() + .Where(spec => !spec.IsSatisfiedBy(episodeParseResult, searchDefinitionBase)) + .Select(spec => spec.RejectionReason); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/DecisionEngine/DownloadDirector.cs b/NzbDrone.Core/DecisionEngine/DownloadDirector.cs deleted file mode 100644 index cb94a00fa..000000000 --- a/NzbDrone.Core/DecisionEngine/DownloadDirector.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using NzbDrone.Core.Model; - -namespace NzbDrone.Core.DecisionEngine -{ - public interface IDownloadDirector - { - DownloadDecision GetDownloadDecision(EpisodeParseResult episodeParseResult); - } - - public class DownloadDirector : IDownloadDirector - { - private readonly IEnumerable _specifications; - - public DownloadDirector(IEnumerable specifications) - { - _specifications = specifications; - } - - public DownloadDecision GetDownloadDecision(EpisodeParseResult episodeParseResult) - { - var rejections = _specifications - .Where(spec => !spec.IsSatisfiedBy(episodeParseResult)) - .Select(spec => spec.RejectionReason).ToArray(); - - episodeParseResult.Decision = new DownloadDecision(rejections); - - return episodeParseResult.Decision; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/DecisionEngine/IFetchableSpecification.cs b/NzbDrone.Core/DecisionEngine/IDecisionEngineSpecification.cs similarity index 61% rename from NzbDrone.Core/DecisionEngine/IFetchableSpecification.cs rename to NzbDrone.Core/DecisionEngine/IDecisionEngineSpecification.cs index efb3b6c15..b51de99ee 100644 --- a/NzbDrone.Core/DecisionEngine/IFetchableSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/IDecisionEngineSpecification.cs @@ -2,9 +2,8 @@ using NzbDrone.Core.Model; namespace NzbDrone.Core.DecisionEngine { - public interface IFetchableSpecification + public interface IDecisionEngineSpecification : IRejectWithReason { - string RejectionReason { get; } bool IsSatisfiedBy(EpisodeParseResult subject); } } \ No newline at end of file diff --git a/NzbDrone.Core/DecisionEngine/IRejectWithReason.cs b/NzbDrone.Core/DecisionEngine/IRejectWithReason.cs new file mode 100644 index 000000000..503071e02 --- /dev/null +++ b/NzbDrone.Core/DecisionEngine/IRejectWithReason.cs @@ -0,0 +1,7 @@ +namespace NzbDrone.Core.DecisionEngine +{ + public interface IRejectWithReason + { + string RejectionReason { get; } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs index 525b35a68..916669f01 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs @@ -5,7 +5,7 @@ using NzbDrone.Core.Tv; namespace NzbDrone.Core.DecisionEngine.Specifications { - public class AcceptableSizeSpecification : IFetchableSpecification + public class AcceptableSizeSpecification : IDecisionEngineSpecification { private readonly IQualitySizeService _qualityTypeProvider; private readonly IEpisodeService _episodeService; diff --git a/NzbDrone.Core/DecisionEngine/Specifications/AllowedReleaseGroupSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/AllowedReleaseGroupSpecification.cs index 644d6ee6f..ac01f135c 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/AllowedReleaseGroupSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/AllowedReleaseGroupSpecification.cs @@ -5,7 +5,7 @@ using NzbDrone.Core.Model; namespace NzbDrone.Core.DecisionEngine.Specifications { - public class AllowedReleaseGroupSpecification : IFetchableSpecification + public class AllowedReleaseGroupSpecification : IDecisionEngineSpecification { private readonly IConfigService _configService; private readonly Logger _logger; diff --git a/NzbDrone.Core/DecisionEngine/Specifications/CustomStartDateSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/CustomStartDateSpecification.cs index 47def975a..da5a25e62 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/CustomStartDateSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/CustomStartDateSpecification.cs @@ -4,7 +4,7 @@ using NzbDrone.Core.Model; namespace NzbDrone.Core.DecisionEngine.Specifications { - public class CustomStartDateSpecification : IFetchableSpecification + public class CustomStartDateSpecification : IDecisionEngineSpecification { private readonly Logger _logger; diff --git a/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs index 47c793937..35c5a2659 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs @@ -3,7 +3,7 @@ using NzbDrone.Core.Model; namespace NzbDrone.Core.DecisionEngine.Specifications { - public class LanguageSpecification : IFetchableSpecification + public class LanguageSpecification : IDecisionEngineSpecification { private readonly Logger _logger; diff --git a/NzbDrone.Core/DecisionEngine/Specifications/MonitoredEpisodeSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/MonitoredEpisodeSpecification.cs index af769d63e..b457ac05b 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/MonitoredEpisodeSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/MonitoredEpisodeSpecification.cs @@ -5,7 +5,7 @@ using NzbDrone.Core.Tv; namespace NzbDrone.Core.DecisionEngine.Specifications { - public class MonitoredEpisodeSpecification : IFetchableSpecification + public class MonitoredEpisodeSpecification : IDecisionEngineSpecification { private readonly IEpisodeService _episodeService; private readonly ISeriesRepository _seriesRepository; diff --git a/NzbDrone.Core/DecisionEngine/Specifications/NotInQueueSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/NotInQueueSpecification.cs index ea4e6b4a4..90344a8fd 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/NotInQueueSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/NotInQueueSpecification.cs @@ -4,7 +4,7 @@ using NzbDrone.Core.Model; namespace NzbDrone.Core.DecisionEngine.Specifications { - public class NotInQueueSpecification : IFetchableSpecification + public class NotInQueueSpecification : IDecisionEngineSpecification { private readonly IProvideDownloadClient _downloadClientProvider; diff --git a/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs index 435cb8dd9..c134f1727 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs @@ -3,7 +3,7 @@ using NzbDrone.Core.Model; namespace NzbDrone.Core.DecisionEngine.Specifications { - public class QualityAllowedByProfileSpecification : IFetchableSpecification + public class QualityAllowedByProfileSpecification : IDecisionEngineSpecification { private readonly Logger _logger; diff --git a/NzbDrone.Core/DecisionEngine/Specifications/RetentionSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/RetentionSpecification.cs index 4b9bef567..a0ca08d9b 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/RetentionSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/RetentionSpecification.cs @@ -4,7 +4,7 @@ using NzbDrone.Core.Model; namespace NzbDrone.Core.DecisionEngine.Specifications { - public class RetentionSpecification : IFetchableSpecification + public class RetentionSpecification : IDecisionEngineSpecification { private readonly IConfigService _configService; private readonly Logger _logger; diff --git a/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs new file mode 100644 index 000000000..a6784a42f --- /dev/null +++ b/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs @@ -0,0 +1,43 @@ +using NLog; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Model; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.DecisionEngine.Specifications.Search +{ + public class DailyEpisodeMatchSpecification : IDecisionEngineSearchSpecification + { + private readonly Logger _logger; + private readonly IEpisodeService _episodeService; + + public DailyEpisodeMatchSpecification(Logger logger, IEpisodeService episodeService) + { + _logger = logger; + _episodeService = episodeService; + } + + public string RejectionReason + { + get + { + return "Episode doesn't match"; + } + } + public bool IsSatisfiedBy(EpisodeParseResult episodeParseResult, SearchDefinitionBase searchDefinitionBase) + { + var dailySearchSpec = searchDefinitionBase as DailyEpisodeSearchDefinition; + + if (dailySearchSpec == null) return true; + + var episode = _episodeService.GetEpisode(dailySearchSpec.SeriesId, dailySearchSpec.Airtime); + + if (!episodeParseResult.AirDate.HasValue || episodeParseResult.AirDate.Value != episode.AirDate.Value) + { + _logger.Trace("Episode AirDate does not match searched episode number, skipping."); + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/DecisionEngine/Specifications/Search/IDecisionEngineSearchSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/Search/IDecisionEngineSearchSpecification.cs new file mode 100644 index 000000000..909a3f21f --- /dev/null +++ b/NzbDrone.Core/DecisionEngine/Specifications/Search/IDecisionEngineSearchSpecification.cs @@ -0,0 +1,11 @@ +using NzbDrone.Core.IndexerSearch; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.DecisionEngine.Specifications.Search +{ + public interface IDecisionEngineSearchSpecification : IRejectWithReason + { + bool IsSatisfiedBy(EpisodeParseResult subject, SearchDefinitionBase searchDefinitionBase); + } +} \ No newline at end of file diff --git a/NzbDrone.Core/DecisionEngine/Specifications/Search/SeasonMatchSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/Search/SeasonMatchSpecification.cs new file mode 100644 index 000000000..e2d9300ec --- /dev/null +++ b/NzbDrone.Core/DecisionEngine/Specifications/Search/SeasonMatchSpecification.cs @@ -0,0 +1,38 @@ +using NLog; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.DecisionEngine.Specifications.Search +{ + public class SeasonMatchSpecification : IDecisionEngineSearchSpecification + { + private readonly Logger _logger; + + public SeasonMatchSpecification(Logger logger) + { + _logger = logger; + } + + public string RejectionReason + { + get + { + return "Episode doesn't match"; + } + } + + public bool IsSatisfiedBy(EpisodeParseResult episodeParseResult, SearchDefinitionBase searchDefinitionBase) + { + var singleEpisodeSpec = searchDefinitionBase as SeasonSearchDefinition; + if (singleEpisodeSpec == null) return true; + + if (singleEpisodeSpec.SeasonNumber != episodeParseResult.SeasonNumber) + { + _logger.Trace("Season number does not match searched season number, skipping."); + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/DecisionEngine/Specifications/Search/SingleEpisodeMatchSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/Search/SingleEpisodeMatchSpecification.cs new file mode 100644 index 000000000..66b5512d3 --- /dev/null +++ b/NzbDrone.Core/DecisionEngine/Specifications/Search/SingleEpisodeMatchSpecification.cs @@ -0,0 +1,44 @@ +using NLog; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.DecisionEngine.Specifications.Search +{ + public class SingleEpisodeMatchSpecification : IDecisionEngineSearchSpecification + { + private readonly Logger _logger; + + public SingleEpisodeMatchSpecification(Logger logger) + { + _logger = logger; + } + + public string RejectionReason + { + get + { + return "Episode doesn't match"; + } + } + + public bool IsSatisfiedBy(EpisodeParseResult episodeParseResult, SearchDefinitionBase searchDefinitionBase) + { + var singleEpisodeSpec = searchDefinitionBase as SingleEpisodeSearchDefinition; + if (singleEpisodeSpec == null) return true; + + if (singleEpisodeSpec.SeasonNumber != episodeParseResult.SeasonNumber) + { + _logger.Trace("Season number does not match searched season number, skipping."); + return false; + } + + if (!episodeParseResult.EpisodeNumbers.Contains(singleEpisodeSpec.EpisodeNumber)) + { + _logger.Trace("Episode number does not match searched episode number, skipping."); + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs index 064910baa..86d42a600 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs @@ -6,7 +6,7 @@ using NzbDrone.Core.Tv; namespace NzbDrone.Core.DecisionEngine.Specifications { - public class UpgradeDiskSpecification : IFetchableSpecification + public class UpgradeDiskSpecification : IDecisionEngineSpecification { private readonly QualityUpgradableSpecification _qualityUpgradableSpecification; private readonly Logger _logger; diff --git a/NzbDrone.Core/DecisionEngine/Specifications/UpgradeHistorySpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/UpgradeHistorySpecification.cs index da65f6bc1..fec243a36 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/UpgradeHistorySpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/UpgradeHistorySpecification.cs @@ -4,7 +4,7 @@ using NzbDrone.Core.Model; namespace NzbDrone.Core.DecisionEngine.Specifications { - public class UpgradeHistorySpecification : IFetchableSpecification + public class UpgradeHistorySpecification : IDecisionEngineSpecification { private readonly IHistoryService _historyService; private readonly QualityUpgradableSpecification _qualityUpgradableSpecification; diff --git a/NzbDrone.Core/Download/DownloadService.cs b/NzbDrone.Core/Download/DownloadService.cs index b5ad09c63..dad046613 100644 --- a/NzbDrone.Core/Download/DownloadService.cs +++ b/NzbDrone.Core/Download/DownloadService.cs @@ -1,9 +1,12 @@ using System; +using System.Collections.Generic; using System.Linq; using NLog; using NzbDrone.Common.Eventing; using NzbDrone.Core.Configuration; +using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Model; +using NzbDrone.Core.Tv; namespace NzbDrone.Core.Download { @@ -29,7 +32,6 @@ namespace NzbDrone.Core.Download _logger = logger; } - public bool DownloadReport(EpisodeParseResult parseResult) { var downloadTitle = parseResult.OriginalString; @@ -45,7 +47,7 @@ namespace NzbDrone.Core.Download if (success) { - _logger.Trace("Download added to Queue: {0}", downloadTitle); + _logger.Info("Report sent to download client. {0}", downloadTitle); _eventAggregator.Publish(new EpisodeGrabbedEvent(parseResult)); } diff --git a/NzbDrone.Core/IndexerSearch/DailyEpisodeSearch.cs b/NzbDrone.Core/IndexerSearch/DailyEpisodeSearch.cs deleted file mode 100644 index bccbecd25..000000000 --- a/NzbDrone.Core/IndexerSearch/DailyEpisodeSearch.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using NLog; -using NzbDrone.Common.EnsureThat; -using NzbDrone.Core.DataAugmentation; -using NzbDrone.Core.DataAugmentation.Scene; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Download; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Model; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.IndexerSearch -{ - public class DailyEpisodeSearch : IndexerSearchBase - { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - public DailyEpisodeSearch(IEpisodeService episodeService, IDownloadService downloadService, IIndexerService indexerService, - ISceneMappingService sceneMappingService, IDownloadDirector downloadDirector, - ISeriesRepository seriesRepository) - : base(seriesRepository, episodeService, downloadService, indexerService, sceneMappingService, - downloadDirector) - { - } - - public DailyEpisodeSearch() - { - } - - public override List PerformSearch(Series series, List episodes, ProgressNotification notification) - { - var episode = episodes.Single(); - - notification.CurrentMessage = "Looking for " + episode; - - var reports = new List(); - var title = GetSearchTitle(series); - - Parallel.ForEach(_indexerService.GetEnabledIndexers(), indexer => - { - try - { - reports.AddRange(indexer.FetchDailyEpisode(title, episode.AirDate.Value)); - } - - catch (Exception e) - { - logger.ErrorException(String.Format("An error has occurred while searching for {0} - {1:yyyy-MM-dd} from: {2}", - series.Title, episode.AirDate, indexer.Name), e); - } - }); - - return reports; - } - - public override bool IsEpisodeMatch(Series series, dynamic options, EpisodeParseResult episodeParseResult) - { - Episode episode = options.Episode; - - if (!episodeParseResult.AirDate.HasValue || episodeParseResult.AirDate.Value != episode.AirDate.Value) - { - logger.Trace("Episode AirDate does not match searched episode number, skipping."); - return false; - } - - return true; - } - - } -} \ No newline at end of file diff --git a/NzbDrone.Core/IndexerSearch/Definitions/DailyEpisodeSearchDefinition.cs b/NzbDrone.Core/IndexerSearch/Definitions/DailyEpisodeSearchDefinition.cs new file mode 100644 index 000000000..6f7478b91 --- /dev/null +++ b/NzbDrone.Core/IndexerSearch/Definitions/DailyEpisodeSearchDefinition.cs @@ -0,0 +1,14 @@ +using System; + +namespace NzbDrone.Core.IndexerSearch.Definitions +{ + public class DailyEpisodeSearchDefinition : SearchDefinitionBase + { + public DateTime Airtime { get; set; } + + public override string ToString() + { + return string.Format("[{0} : {1}", SceneTitle, Airtime); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/IndexerSearch/Definitions/PartialSeasonSearchDefinition.cs b/NzbDrone.Core/IndexerSearch/Definitions/PartialSeasonSearchDefinition.cs new file mode 100644 index 000000000..e3ef7d0b7 --- /dev/null +++ b/NzbDrone.Core/IndexerSearch/Definitions/PartialSeasonSearchDefinition.cs @@ -0,0 +1,19 @@ +namespace NzbDrone.Core.IndexerSearch.Definitions +{ + public class PartialSeasonSearchDefinition : SeasonSearchDefinition + { + public int Prefix { get; set; } + + public PartialSeasonSearchDefinition(SeasonSearchDefinition seasonSearch, int prefix) + { + Prefix = prefix; + SceneTitle = seasonSearch.SceneTitle; + SeasonNumber = seasonSearch.SeasonNumber; + } + + public override string ToString() + { + return string.Format("[{0} : S{1:00}E{1:0}*]", SceneTitle, SeasonNumber, Prefix); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/IndexerSearch/Definitions/SearchDefinitionBase.cs b/NzbDrone.Core/IndexerSearch/Definitions/SearchDefinitionBase.cs new file mode 100644 index 000000000..e3ac90147 --- /dev/null +++ b/NzbDrone.Core/IndexerSearch/Definitions/SearchDefinitionBase.cs @@ -0,0 +1,38 @@ +using System; +using System.Text.RegularExpressions; + +namespace NzbDrone.Core.IndexerSearch.Definitions +{ + public abstract class SearchDefinitionBase + { + private static readonly Regex NoneWord = new Regex(@"[\W]", RegexOptions.IgnoreCase | RegexOptions.Compiled); + private static readonly Regex BeginningThe = new Regex(@"^the\s", RegexOptions.IgnoreCase | RegexOptions.Compiled); + + public int SeriesId { get; set; } + public string SceneTitle { get; set; } + + public string QueryTitle + { + get + { + return GetQueryTitle(SceneTitle); + } + } + + private static string GetQueryTitle(string title) + { + var cleanTitle = BeginningThe.Replace(title, String.Empty); + + cleanTitle = cleanTitle + .Replace("&", "and") + .Replace("`", "") + .Replace("'", ""); + + cleanTitle = NoneWord.Replace(cleanTitle, "+"); + + //remove any repeating +s + cleanTitle = Regex.Replace(cleanTitle, @"\+{2,}", "+"); + return cleanTitle.Trim('+', ' '); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/IndexerSearch/Definitions/SeasonSearchDefinition.cs b/NzbDrone.Core/IndexerSearch/Definitions/SeasonSearchDefinition.cs new file mode 100644 index 000000000..ec0031e94 --- /dev/null +++ b/NzbDrone.Core/IndexerSearch/Definitions/SeasonSearchDefinition.cs @@ -0,0 +1,12 @@ +namespace NzbDrone.Core.IndexerSearch.Definitions +{ + public class SeasonSearchDefinition : SearchDefinitionBase + { + public int SeasonNumber { get; set; } + + public override string ToString() + { + return string.Format("[{0} : S{1:00}]", SceneTitle, SeasonNumber); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/IndexerSearch/Definitions/SingleEpisodeSearchDefinition.cs b/NzbDrone.Core/IndexerSearch/Definitions/SingleEpisodeSearchDefinition.cs new file mode 100644 index 000000000..aaa392a4f --- /dev/null +++ b/NzbDrone.Core/IndexerSearch/Definitions/SingleEpisodeSearchDefinition.cs @@ -0,0 +1,15 @@ +namespace NzbDrone.Core.IndexerSearch.Definitions +{ + public class SingleEpisodeSearchDefinition : SearchDefinitionBase + { + + //TODO make sure these are populated with scene if required + public int EpisodeNumber { get; set; } + public int SeasonNumber { get; set; } + + public override string ToString() + { + return string.Format("[{0} : S{1:00}E{2:00} ]", SceneTitle, SeasonNumber, EpisodeNumber); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/IndexerSearch/EpisodeSearch.cs b/NzbDrone.Core/IndexerSearch/EpisodeSearch.cs deleted file mode 100644 index 7ebb616f8..000000000 --- a/NzbDrone.Core/IndexerSearch/EpisodeSearch.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using NLog; -using NzbDrone.Core.DataAugmentation; -using NzbDrone.Core.DataAugmentation.Scene; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Download; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Model; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.IndexerSearch -{ - public class EpisodeSearch : IndexerSearchBase - { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - public EpisodeSearch(IEpisodeService episodeService, IDownloadService downloadService, IIndexerService indexerService, - ISceneMappingService sceneMappingService, IDownloadDirector downloadDirector, - ISeriesRepository seriesRepository) - : base(seriesRepository, episodeService, downloadService, indexerService, sceneMappingService, - downloadDirector) - { - } - - public EpisodeSearch() - { - } - - public override List PerformSearch(Series series, List episodes, ProgressNotification notification) - { - //Todo: Daily and Anime or separate them out? - //Todo: Epsiodes that use scene numbering - - var episode = episodes.Single(); - - - var reports = new List(); - var title = GetSearchTitle(series); - - var seasonNumber = episode.SeasonNumber; - var episodeNumber = episode.EpisodeNumber; - - if (series.UseSceneNumbering) - { - if (episode.SceneSeasonNumber > 0 && episode.SceneEpisodeNumber > 0) - { - logger.Trace("Using Scene Numbering for: {0}", episode); - seasonNumber = episode.SceneSeasonNumber; - episodeNumber = episode.SceneEpisodeNumber; - } - } - - Parallel.ForEach(_indexerService.GetEnabledIndexers(), indexer => - { - try - { - reports.AddRange(indexer.FetchEpisode(title, seasonNumber, episodeNumber)); - } - - catch (Exception e) - { - logger.ErrorException(String.Format("An error has occurred while searching for {0}-S{1:00}E{2:00} from: {3}", - series.Title, episode.SeasonNumber, episode.EpisodeNumber, indexer.Name), e); - } - }); - - return reports; - } - - public override bool IsEpisodeMatch(Series series, dynamic options, EpisodeParseResult episodeParseResult) - { - if (series.UseSceneNumbering && options.Episode.SeasonNumber > 0 && options.Episode.EpisodeNumber > 0) - { - if (options.Episode.SceneSeasonNumber != episodeParseResult.SeasonNumber) - { - logger.Trace("Season number does not match searched season number, skipping."); - return false; - } - - if (!episodeParseResult.EpisodeNumbers.Contains(options.Episode.SceneEpisodeNumber)) - { - logger.Trace("Episode number does not match searched episode number, skipping."); - return false; - } - - return true; - } - - if (options.Episode.SeasonNumber != episodeParseResult.SeasonNumber) - { - logger.Trace("Season number does not match searched season number, skipping."); - return false; - } - - if (!episodeParseResult.EpisodeNumbers.Contains(options.Episode.EpisodeNumber)) - { - logger.Trace("Episode number does not match searched episode number, skipping."); - return false; - } - - return true; - } - - } -} diff --git a/NzbDrone.Core/IndexerSearch/IndexerSearchBase.cs b/NzbDrone.Core/IndexerSearch/IndexerSearchBase.cs deleted file mode 100644 index f21b73483..000000000 --- a/NzbDrone.Core/IndexerSearch/IndexerSearchBase.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using NLog; -using NzbDrone.Core.DataAugmentation; -using NzbDrone.Core.DataAugmentation.Scene; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Download; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Model; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.IndexerSearch -{ - public abstract class IndexerSearchBase - { - private readonly ISeriesRepository _seriesRepository; - private readonly IEpisodeService _episodeService; - private readonly IDownloadService _downloadService; - private readonly ISceneMappingService _sceneMappingService; - private readonly IDownloadDirector DownloadDirector; - - protected readonly IIndexerService _indexerService; - - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - protected IndexerSearchBase(ISeriesRepository seriesRepository, IEpisodeService episodeService, IDownloadService downloadService, - IIndexerService indexerService, ISceneMappingService sceneMappingService, - IDownloadDirector downloadDirector) - { - _seriesRepository = seriesRepository; - _episodeService = episodeService; - _downloadService = downloadService; - _indexerService = indexerService; - _sceneMappingService = sceneMappingService; - DownloadDirector = downloadDirector; - } - - protected IndexerSearchBase() - { - } - - public abstract List PerformSearch(Series series, List episodes, ProgressNotification notification); - public abstract bool IsEpisodeMatch(Series series, dynamic options, EpisodeParseResult episodeParseResult); - - public virtual List Search(Series series, dynamic options, ProgressNotification notification) - { - if (options == null) - throw new ArgumentNullException(options); - - - List reports = PerformSearch(series, options, notification); - - logger.Debug("Finished searching all indexers. Total {0}", reports.Count); - notification.CurrentMessage = "Processing search results"; - - var result = ProcessReports(series, options, reports); - - if (!result.Grabbed.Any()) - { - logger.Warn("Unable to find {0} in any of indexers.", options.Episode); - - notification.CurrentMessage = reports.Any() ? String.Format("Sorry, couldn't find {0}, that matches your preferences.", options.Episode) - : String.Format("Sorry, couldn't find {0} in any of indexers.", options.Episode); - } - - return result.Grabbed; - } - - public void ProcessReports(Series series, dynamic options, List episodeParseResults) - { - - var sortedResults = episodeParseResults.OrderByDescending(c => c.Quality) - .ThenBy(c => c.EpisodeNumbers.MinOrDefault()) - .ThenBy(c => c.Age); - - foreach (var episodeParseResult in sortedResults) - { - try - { - - logger.Trace("Analyzing report " + episodeParseResult); - episodeParseResult.Series = _seriesRepository.GetByTitle(episodeParseResult.CleanTitle); - - if (episodeParseResult.Series == null || episodeParseResult.Series.Id != series.Id) - { - episodeParseResult.Decision = new DownloadDecision("Invalid Series"); - continue; - } - - episodeParseResult.Episodes = _episodeService.GetEpisodesByParseResult(episodeParseResult); - - - if (!IsEpisodeMatch(series, options, episodeParseResult)) - { - episodeParseResult.Decision = new DownloadDecision("Incorrect Episode/Season"); - } - - var downloadDecision = DownloadDirector.GetDownloadDecision(episodeParseResult); - - if (downloadDecision.Approved) - { - DownloadReport(episodeParseResult); - } - } - catch (Exception e) - { - logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e); - } - } - } - - public virtual Boolean DownloadReport(EpisodeParseResult episodeParseResult) - { - logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); - try - { - if (_downloadService.DownloadReport(episodeParseResult)) - { - return true; - } - } - catch (Exception e) - { - logger.ErrorException("Unable to add report to download queue." + episodeParseResult, e); - } - - return false; - } - - public virtual string GetSearchTitle(Series series, int seasonNumber = -1) - { - var seasonTitle = _sceneMappingService.GetSceneName(series.Id, seasonNumber); - - if (!String.IsNullOrWhiteSpace(seasonTitle)) - return seasonTitle; - - var title = _sceneMappingService.GetSceneName(series.Id); - - if (String.IsNullOrWhiteSpace(title)) - { - title = series.Title; - title = title.Replace("&", "and"); - title = Regex.Replace(title, @"[^\w\d\s\-]", ""); - } - - return title; - } - } -} diff --git a/NzbDrone.Core/IndexerSearch/NzbSearchService.cs b/NzbDrone.Core/IndexerSearch/NzbSearchService.cs new file mode 100644 index 000000000..883bc76ae --- /dev/null +++ b/NzbDrone.Core/IndexerSearch/NzbSearchService.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NLog; +using NzbDrone.Core.DataAugmentation.Scene; +using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Indexers; +using NzbDrone.Core.Model; +using NzbDrone.Core.Tv; +using System.Linq; + +namespace NzbDrone.Core.IndexerSearch +{ + public interface ISearchForNzb + { + List SearchSingle(int seriesId, int seasonNumber, int episodeNumber); + List SearchDaily(int seriesId, DateTime airDate); + List SearchSeason(int seriesId, int seasonNumber); + } + + public class NzbSearchService : ISearchForNzb + { + private readonly IIndexerService _indexerService; + private readonly IFetchFeedFromIndexers _feedFetcher; + private readonly ISceneMappingService _sceneMapping; + private readonly ISeriesService _seriesService; + private readonly IEpisodeService _episodeService; + private readonly IMakeDownloadDecision _makeDownloadDecision; + private readonly Logger _logger; + + public NzbSearchService(IIndexerService indexerService, IFetchFeedFromIndexers feedFetcher, ISceneMappingService sceneMapping, ISeriesService seriesService, IEpisodeService episodeService, IMakeDownloadDecision makeDownloadDecision, Logger logger) + { + _indexerService = indexerService; + _feedFetcher = feedFetcher; + _sceneMapping = sceneMapping; + _seriesService = seriesService; + _episodeService = episodeService; + _makeDownloadDecision = makeDownloadDecision; + _logger = logger; + } + + public List SearchSingle(int seriesId, int seasonNumber, int episodeNumber) + { + var searchSpec = Get(seriesId, seasonNumber); + + if (_seriesService.GetSeries(seriesId).UseSceneNumbering) + { + var episode = _episodeService.GetEpisode(seriesId, seasonNumber, episodeNumber); + searchSpec.EpisodeNumber = episode.SceneEpisodeNumber; + searchSpec.SeasonNumber = episode.SceneSeasonNumber; + } + else + { + searchSpec.EpisodeNumber = episodeNumber; + searchSpec.SeasonNumber = seasonNumber; + } + + return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); + } + + public List SearchDaily(int seriesId, DateTime airDate) + { + var searchSpec = Get(seriesId); + searchSpec.Airtime = airDate; + + return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); + } + + public List SearchSeason(int seriesId, int seasonNumber) + { + var searchSpec = Get(seriesId, seasonNumber); + searchSpec.SeasonNumber = seasonNumber; + + return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); + } + + private List PartialSeasonSearch(SeasonSearchDefinition search) + { + var episodesNumbers = _episodeService.GetEpisodesBySeason(search.SeriesId, search.SeasonNumber).Select(c => c.EpisodeNumber); + var prefixes = episodesNumbers + .Select(i => i / 10) + .Distinct() + .Select(prefix => new PartialSeasonSearchDefinition(search, prefix)); + + var result = new List(); + + foreach (var partialSeasonSearchSpec in prefixes) + { + var spec = partialSeasonSearchSpec; + result.AddRange(Dispatch(indexer => _feedFetcher.Fetch(indexer, spec), partialSeasonSearchSpec)); + } + + + return result; + } + + private TSpec Get(int seriesId, int seasonNumber = -1) where TSpec : SearchDefinitionBase, new() + { + var spec = new TSpec(); + + spec.SeriesId = seriesId; + spec.SceneTitle = _sceneMapping.GetSceneName(seriesId, seasonNumber); + + return spec; + } + + private List Dispatch(Func> searchAction, SearchDefinitionBase definitionBase) + { + var indexers = _indexerService.GetAvailableIndexers(); + var parseResults = new List(); + + Parallel.ForEach(indexers, indexer => + { + try + { + var indexerReports = searchAction(indexer); + lock (indexer) + { + parseResults.AddRange(indexerReports); + } + } + catch (Exception e) + { + _logger.ErrorException(String.Format("An error has occurred while searching for {0} from: {1}", definitionBase, indexer.Name), e); + } + }); + + _logger.Debug("Total of {0} reports were found for {1} in {2} indexers", parseResults.Count, definitionBase, indexers.Count); + + return _makeDownloadDecision.GetSearchDecision(parseResults, definitionBase).ToList(); + } + + + } +} \ No newline at end of file diff --git a/NzbDrone.Core/IndexerSearch/PartialSeasonSearch.cs b/NzbDrone.Core/IndexerSearch/PartialSeasonSearch.cs deleted file mode 100644 index 931381881..000000000 --- a/NzbDrone.Core/IndexerSearch/PartialSeasonSearch.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using NLog; -using NzbDrone.Core.DataAugmentation; -using NzbDrone.Core.DataAugmentation.Scene; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Download; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Model; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.IndexerSearch -{ - public class PartialSeasonSearch : IndexerSearchBase - { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - public PartialSeasonSearch(IEpisodeService episodeService, IDownloadService downloadService, IIndexerService indexerService, - ISceneMappingService sceneMappingService, IDownloadDirector downloadDirector, - ISeriesRepository seriesRepository) - : base(seriesRepository, episodeService, downloadService, indexerService, sceneMappingService, - downloadDirector) - { - } - - public PartialSeasonSearch() - { - } - - public override List PerformSearch(Series series, List episodes, ProgressNotification notification) - { - var seasons = episodes.Select(c => c.SeasonNumber).Distinct().ToList(); - - if (seasons.Count > 1) - { - throw new ArgumentOutOfRangeException("episodes", "episode list contains episodes from more than one season"); - } - - var seasonNumber = seasons[0]; - notification.CurrentMessage = String.Format("Looking for {0} - Season {1}", series.Title, seasonNumber); - - var reports = new List(); - object reportsLock = new object(); - - var title = GetSearchTitle(series); - var prefixes = GetEpisodeNumberPrefixes(episodes.Select(e => e.EpisodeNumber)); - - foreach (var p in prefixes) - { - var prefix = p; - - Parallel.ForEach(_indexerService.GetEnabledIndexers(), indexer => - { - try - { - lock (reportsLock) - { - reports.AddRange(indexer.FetchPartialSeason(title, seasonNumber, prefix)); - } - } - - catch (Exception e) - { - logger.ErrorException( - String.Format( - "An error has occurred while searching for {0} Season {1:00} Prefix: {2} from: {3}", - series.Title, seasonNumber, prefix, indexer.Name), - e); - } - }); - } - - return reports; - } - - public override bool IsEpisodeMatch(Series series, dynamic options, EpisodeParseResult episodeParseResult) - { - if (options.SeasonNumber != episodeParseResult.SeasonNumber) - { - logger.Trace("Season number does not match searched season number, skipping."); - return false; - } - - return true; - } - - - private List GetEpisodeNumberPrefixes(IEnumerable episodeNumbers) - { - var results = new List(); - - foreach (var i in episodeNumbers) - { - results.Add(i / 10); - } - - return results.Distinct().ToList(); - } - } -} diff --git a/NzbDrone.Core/IndexerSearch/SearchAndDownloadService.cs b/NzbDrone.Core/IndexerSearch/SearchAndDownloadService.cs new file mode 100644 index 000000000..23f4383a7 --- /dev/null +++ b/NzbDrone.Core/IndexerSearch/SearchAndDownloadService.cs @@ -0,0 +1,41 @@ +using System; +using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.Download; + +namespace NzbDrone.Core.IndexerSearch +{ + + interface ISearchAndDownload + { + void SearchSingle(int seriesId, int seasonNumber, int episodeNumber); + void SearchDaily(int seriesId, DateTime airDate); + void SearchSeason(int seriesId, int seasonNumber); + } + + public class SearchAndDownloadService : ISearchAndDownload + { + private readonly ISearchForNzb _searchService; + private readonly IMakeDownloadDecision _downloadDecisionMaker; + + public SearchAndDownloadService(ISearchForNzb searchService, IMakeDownloadDecision downloadDecisionMaker) + { + _searchService = searchService; + _downloadDecisionMaker = downloadDecisionMaker; + } + + public void SearchSingle(int seriesId, int seasonNumber, int episodeNumber) + { + var result = _searchService.SearchSingle(seriesId, seasonNumber, episodeNumber); + } + + public void SearchDaily(int seriesId, DateTime airDate) + { + throw new NotImplementedException(); + } + + public void SearchSeason(int seriesId, int seasonNumber) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/BaseIndexer.cs b/NzbDrone.Core/Indexers/BaseIndexer.cs new file mode 100644 index 000000000..32a5abcc1 --- /dev/null +++ b/NzbDrone.Core/Indexers/BaseIndexer.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Core.Indexers +{ + public interface IIndexerBase + { + string Name { get; } + bool EnabledByDefault { get; } + + IEnumerable RecentFeed { get; } + + IParseFeed Parser { get; } + + IIndexerSetting Settings { get; } + + IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber); + IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date); + IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber); + IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard); + } + + public abstract class BaseIndexer : IIndexerBase + { + public abstract string Name { get; } + + public virtual bool EnabledByDefault + { + get + { + return false; + } + } + + public virtual IParseFeed Parser + { + get + { + return new BasicRssParser(); + } + } + + public virtual IIndexerSetting Settings + { + get + { + return new NullSetting(); + } + } + + public abstract IEnumerable RecentFeed { get; } + + public abstract IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber); + public abstract IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date); + public abstract IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber); + public abstract IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard); + } + + +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/BasicRssParser.cs b/NzbDrone.Core/Indexers/BasicRssParser.cs new file mode 100644 index 000000000..12bf6d2c4 --- /dev/null +++ b/NzbDrone.Core/Indexers/BasicRssParser.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.ServiceModel.Syndication; +using NLog; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.Indexers +{ + public interface IParseFeed + { + IEnumerable Process(Stream source); + } + + public class BasicRssParser : IParseFeed + { + private readonly Logger _logger; + + public BasicRssParser() + { + _logger = LogManager.GetCurrentClassLogger(); + } + + public IEnumerable Process(Stream source) + { + var reader = new SyndicationFeedXmlReader(source); + var feed = SyndicationFeed.Load(reader).Items; + + var result = new List(); + + foreach (var syndicationItem in feed) + { + try + { + var parsedEpisode = ParseFeed(syndicationItem); + if (parsedEpisode != null) + { + parsedEpisode.NzbUrl = GetNzbUrl(syndicationItem); + parsedEpisode.NzbInfoUrl = GetNzbUrl(syndicationItem); + result.Add(parsedEpisode); + } + } + catch (Exception itemEx) + { + itemEx.Data.Add("Item", syndicationItem.Title); + _logger.ErrorException("An error occurred while processing feed item", itemEx); + } + } + + return result; + } + + + protected virtual string GetTitle(SyndicationItem syndicationItem) + { + return syndicationItem.Title.Text; + } + + protected virtual string GetNzbUrl(SyndicationItem item) + { + return item.Links[0].Uri.ToString(); + } + + protected virtual string GetNzbInfoUrl(SyndicationItem item) + { + return string.Empty; + } + + protected virtual EpisodeParseResult PostProcessor(SyndicationItem item, EpisodeParseResult currentResult) + { + return currentResult; + } + + private EpisodeParseResult ParseFeed(SyndicationItem item) + { + var title = GetTitle(item); + + var episodeParseResult = Parser.ParseTitle(title); + if (episodeParseResult != null) + { + episodeParseResult.Age = DateTime.Now.Date.Subtract(item.PublishDate.Date).Days; + episodeParseResult.OriginalString = title; + episodeParseResult.SceneSource = true; + } + + _logger.Trace("Parsed: {0} from: {1}", episodeParseResult, item.Title.Text); + + return PostProcessor(item, episodeParseResult); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/FileSharingTalk.cs b/NzbDrone.Core/Indexers/FileSharingTalk.cs deleted file mode 100644 index ba9e8ba32..000000000 --- a/NzbDrone.Core/Indexers/FileSharingTalk.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Linq; -using System; -using System.Collections.Generic; -using System.ServiceModel.Syndication; -using NzbDrone.Common; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Model; - -namespace NzbDrone.Core.Indexers -{ - public class FileSharingTalk : IndexerBase - { - public FileSharingTalk(HttpProvider httpProvider, IConfigService configService) - : base(httpProvider, configService) - { - } - - protected override string[] Urls - { - get - { - return new[] - { - string.Format("http://filesharingtalk.com/ng_rss.php?uid={0}&ps={1}&category=tv&subcategory=x264sd,x264720,xvid,webdl720,x2641080", - _configService.FileSharingTalkUid, _configService.FileSharingTalkSecret) - }; - } - } - - public override bool IsConfigured - { - get - { - return !string.IsNullOrWhiteSpace(_configService.FileSharingTalkUid) && - !string.IsNullOrWhiteSpace(_configService.FileSharingTalkSecret); - } - } - - protected override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) - { - return new List(); - } - - protected override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) - { - return new List(); - } - - protected override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) - { - return new List(); - } - - protected override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) - { - return new List(); - } - - public override string Name - { - get { return "FileSharingTalk"; } - } - - protected override string NzbDownloadUrl(SyndicationItem item) - { - return item.Links[0].Uri.ToString(); - } - - protected override string NzbInfoUrl(SyndicationItem item) - { - return item.Id; - } - - protected override EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult) - { - if (currentResult != null) - { - currentResult.Size = 0; - currentResult.Age = 0; - } - - return currentResult; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/FileSharingTalk/FileSharingTalk.cs b/NzbDrone.Core/Indexers/FileSharingTalk/FileSharingTalk.cs new file mode 100644 index 000000000..a8dc63a61 --- /dev/null +++ b/NzbDrone.Core/Indexers/FileSharingTalk/FileSharingTalk.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Core.Indexers.FileSharingTalk +{ + public class FileSharingTalk : BaseIndexer + { + private readonly FileSharingTalkSetting _settings; + + public FileSharingTalk(IProviderIndexerSetting settingProvider) + { + _settings = settingProvider.Get(this); + } + + public override IEnumerable RecentFeed + { + get + { + yield return + string.Format( + "http://filesharingtalk.com/ng_rss.php?uid={0}&ps={1}&category=tv&subcategory=x264sd,x264720,xvid,webdl720,x2641080", + _settings.Uid, _settings.Secret); + } + } + + + public override IParseFeed Parser + { + get + { + return new FileSharingTalkParser(); + } + } + + public override IIndexerSetting Settings + { + get { return _settings; } + } + + public override string Name + { + get { return "FileSharingTalk"; } + } + + public override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) + { + return new List(); + } + + public override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) + { + return new List(); + } + + public override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) + { + return new List(); + } + + public override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) + { + return new List(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/FileSharingTalk/FileSharingTalkParser.cs b/NzbDrone.Core/Indexers/FileSharingTalk/FileSharingTalkParser.cs new file mode 100644 index 000000000..e851cac26 --- /dev/null +++ b/NzbDrone.Core/Indexers/FileSharingTalk/FileSharingTalkParser.cs @@ -0,0 +1,24 @@ +using System.ServiceModel.Syndication; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.Indexers.FileSharingTalk +{ + public class FileSharingTalkParser : BasicRssParser + { + protected override string GetNzbInfoUrl(SyndicationItem item) + { + return item.Id; + } + + protected override EpisodeParseResult PostProcessor(SyndicationItem item, EpisodeParseResult currentResult) + { + if (currentResult != null) + { + currentResult.Size = 0; + currentResult.Age = 0; + } + + return currentResult; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/FileSharingTalk/FileSharingTalkSettings.cs b/NzbDrone.Core/Indexers/FileSharingTalk/FileSharingTalkSettings.cs new file mode 100644 index 000000000..b279e94a3 --- /dev/null +++ b/NzbDrone.Core/Indexers/FileSharingTalk/FileSharingTalkSettings.cs @@ -0,0 +1,18 @@ +using System; + +namespace NzbDrone.Core.Indexers.FileSharingTalk +{ + public class FileSharingTalkSetting : IIndexerSetting + { + public String Uid { get; set; } + public String Secret { get; set; } + + public bool IsValid + { + get + { + return !string.IsNullOrWhiteSpace(Uid) && !string.IsNullOrWhiteSpace(Secret); + } + } + } +} diff --git a/NzbDrone.Core/Indexers/IIndexerSettings.cs b/NzbDrone.Core/Indexers/IIndexerSettings.cs new file mode 100644 index 000000000..c68ecf0ad --- /dev/null +++ b/NzbDrone.Core/Indexers/IIndexerSettings.cs @@ -0,0 +1,19 @@ +namespace NzbDrone.Core.Indexers +{ + public interface IIndexerSetting + { + bool IsValid { get; } + } + + + public class NullSetting : IIndexerSetting + { + public bool IsValid + { + get + { + return true; + } + } + } +} diff --git a/NzbDrone.Core/Indexers/IndexerBase.cs b/NzbDrone.Core/Indexers/IndexerBase.cs deleted file mode 100644 index f2bba6768..000000000 --- a/NzbDrone.Core/Indexers/IndexerBase.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.ServiceModel.Syndication; -using System.Text.RegularExpressions; -using NLog; -using NzbDrone.Common; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Model; - -namespace NzbDrone.Core.Indexers -{ - public abstract class IndexerBase - { - protected readonly Logger _logger; - protected readonly HttpProvider _httpProvider; - protected readonly IConfigService _configService; - - protected static readonly Regex TitleSearchRegex = new Regex(@"[\W]", RegexOptions.IgnoreCase | RegexOptions.Compiled); - protected static readonly Regex RemoveThe = new Regex(@"^the\s", RegexOptions.IgnoreCase | RegexOptions.Compiled); - - protected IndexerBase(HttpProvider httpProvider, IConfigService configService) - { - _httpProvider = httpProvider; - _configService = configService; - - _logger = LogManager.GetLogger(GetType().ToString()); - } - - public IndexerBase() - { - - } - - /// - /// Gets the name for the feed - /// - public abstract string Name { get; } - - /// - /// Gets the source URL for the feed - /// - protected abstract string[] Urls { get; } - - public abstract bool IsConfigured { get; } - - /// - /// Should the indexer be enabled by default? - /// - public virtual bool EnabledByDefault - { - get { return false; } - } - - /// - /// Gets the credential. - /// - protected virtual NetworkCredential Credentials - { - get { return null; } - } - - protected abstract IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber); - protected abstract IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date); - protected abstract IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber); - protected abstract IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard); - - protected virtual EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult) - { - return currentResult; - } - - protected virtual string TitlePreParser(SyndicationItem item) - { - return item.Title.Text; - } - - protected abstract string NzbDownloadUrl(SyndicationItem item); - - protected abstract string NzbInfoUrl(SyndicationItem item); - - public virtual IList FetchRss() - { - _logger.Debug("Fetching feeds from " + Name); - - var result = new List(); - - - result = Fetch(Urls); - - - _logger.Debug("Finished processing feeds from " + Name); - return result; - } - - public virtual IList FetchSeason(string seriesTitle, int seasonNumber) - { - _logger.Debug("Searching {0} for {1} Season {2}", Name, seriesTitle, seasonNumber); - - var searchUrls = GetSeasonSearchUrls(GetQueryTitle(seriesTitle), seasonNumber); - var result = Fetch(searchUrls); - - _logger.Info("Finished searching {0} for {1} Season {2}, Found {3}", Name, seriesTitle, seasonNumber, result.Count); - return result; - } - - public virtual IList FetchPartialSeason(string seriesTitle, int seasonNumber, int episodePrefix) - { - _logger.Debug("Searching {0} for {1} Season {2}, Prefix: {3}", Name, seriesTitle, seasonNumber, episodePrefix); - - - var searchUrls = GetPartialSeasonSearchUrls(GetQueryTitle(seriesTitle), seasonNumber, episodePrefix); - - var result = Fetch(searchUrls); - - _logger.Info("Finished searching {0} for {1} Season {2}, Found {3}", Name, seriesTitle, seasonNumber, result.Count); - return result; - } - - public virtual IList FetchEpisode(string seriesTitle, int seasonNumber, int episodeNumber) - { - _logger.Debug("Searching {0} for {1}-S{2:00}E{3:00}", Name, seriesTitle, seasonNumber, episodeNumber); - - var searchUrls = GetEpisodeSearchUrls(GetQueryTitle(seriesTitle), seasonNumber, episodeNumber); - - var result = Fetch(searchUrls); - - _logger.Info("Finished searching {0} for {1} S{2:00}E{3:00}, Found {4}", Name, seriesTitle, seasonNumber, episodeNumber, result.Count); - return result; - - } - - public virtual IList FetchDailyEpisode(string seriesTitle, DateTime airDate) - { - _logger.Debug("Searching {0} for {1}-{2}", Name, seriesTitle, airDate.ToShortDateString()); - - var searchUrls = GetDailyEpisodeSearchUrls(GetQueryTitle(seriesTitle), airDate); - - var result = Fetch(searchUrls); - - _logger.Info("Finished searching {0} for {1}-{2}, Found {3}", Name, seriesTitle, airDate.ToShortDateString(), result.Count); - return result; - - } - - private List Fetch(IEnumerable urls) - { - var result = new List(); - - if (!IsConfigured) - { - _logger.Warn("Indexer '{0}' isn't configured correctly. please reconfigure the indexer in settings page.", Name); - return result; - } - - foreach (var url in urls) - { - try - { - _logger.Trace("Downloading RSS " + url); - - var reader = new SyndicationFeedXmlReader(_httpProvider.DownloadStream(url, Credentials)); - var feed = SyndicationFeed.Load(reader).Items; - - foreach (var item in feed) - { - try - { - var parsedEpisode = ParseFeed(item); - if (parsedEpisode != null) - { - parsedEpisode.NzbUrl = NzbDownloadUrl(item); - parsedEpisode.NzbInfoUrl = NzbInfoUrl(item); - parsedEpisode.Indexer = String.IsNullOrWhiteSpace(parsedEpisode.Indexer) ? Name : parsedEpisode.Indexer; - result.Add(parsedEpisode); - } - } - catch (Exception itemEx) - { - itemEx.Data.Add("FeedUrl", url); - itemEx.Data.Add("Item", item.Title); - _logger.ErrorException("An error occurred while processing feed item", itemEx); - } - - } - } - catch (WebException webException) - { - if (webException.Message.Contains("503")) - { - _logger.Warn("{0} server is currently unavailable.{1} {2}", Name, url, webException.Message); - } - else - { - webException.Data.Add("FeedUrl", url); - _logger.ErrorException("An error occurred while processing feed. " + url, webException); - } - } - catch (Exception feedEx) - { - feedEx.Data.Add("FeedUrl", url); - _logger.ErrorException("An error occurred while processing feed. " + url, feedEx); - } - } - - return result; - } - - public EpisodeParseResult ParseFeed(SyndicationItem item) - { - var title = TitlePreParser(item); - - var episodeParseResult = Parser.ParseTitle(title); - if (episodeParseResult != null) - { - episodeParseResult.Age = DateTime.Now.Date.Subtract(item.PublishDate.Date).Days; - episodeParseResult.OriginalString = title; - episodeParseResult.SceneSource = true; - } - - _logger.Trace("Parsed: {0} from: {1}", episodeParseResult, item.Title.Text); - - return CustomParser(item, episodeParseResult); - } - - public virtual string GetQueryTitle(string title) - { - title = RemoveThe.Replace(title, string.Empty); - - var cleanTitle = TitleSearchRegex.Replace(title, "+").Trim('+', ' '); - - //remove any repeating +s - cleanTitle = Regex.Replace(cleanTitle, @"\+{1,100}", "+"); - return cleanTitle; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/Indexer.cs b/NzbDrone.Core/Indexers/IndexerDefinition.cs similarity index 66% rename from NzbDrone.Core/Indexers/Indexer.cs rename to NzbDrone.Core/Indexers/IndexerDefinition.cs index 18a8e0fd5..398dd4bf1 100644 --- a/NzbDrone.Core/Indexers/Indexer.cs +++ b/NzbDrone.Core/Indexers/IndexerDefinition.cs @@ -3,10 +3,10 @@ using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Indexers { - public class Indexer : ModelBase + public class IndexerDefinition : ModelBase { public Boolean Enable { get; set; } - public String Type { get; set; } public String Name { get; set; } + public String Settings { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/IndexerFetchService.cs b/NzbDrone.Core/Indexers/IndexerFetchService.cs new file mode 100644 index 000000000..b2ef0f676 --- /dev/null +++ b/NzbDrone.Core/Indexers/IndexerFetchService.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Net; +using NLog; +using NzbDrone.Common; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.Indexers +{ + public interface IFetchFeedFromIndexers + { + IList FetchRss(IIndexerBase indexer); + + IList Fetch(IIndexerBase indexer, SeasonSearchDefinition searchDefinition); + IList Fetch(IIndexerBase indexer, SingleEpisodeSearchDefinition searchDefinition); + IList Fetch(IIndexerBase indexer, PartialSeasonSearchDefinition searchDefinition); + IList Fetch(IIndexerBase indexer, DailyEpisodeSearchDefinition searchDefinition); + } + + public class FetchFeedService : IFetchFeedFromIndexers + { + private readonly Logger _logger; + private readonly HttpProvider _httpProvider; + + + protected FetchFeedService(HttpProvider httpProvider, Logger logger) + { + _httpProvider = httpProvider; + _logger = logger; + } + + + public virtual IList FetchRss(IIndexerBase indexer) + { + _logger.Debug("Fetching feeds from " + indexer.Name); + + var result = Fetch(indexer, indexer.RecentFeed); + + _logger.Debug("Finished processing feeds from " + indexer.Name); + + return result; + } + + public IList Fetch(IIndexerBase indexer, SeasonSearchDefinition searchDefinition) + { + _logger.Debug("Searching for {0}", searchDefinition); + + var searchUrls = indexer.GetSeasonSearchUrls(searchDefinition.SceneTitle, searchDefinition.SeasonNumber); + var result = Fetch(indexer, searchUrls); + + + _logger.Info("Finished searching {0} on {1}. Found {2}", indexer.Name, searchDefinition, result.Count); + return result; + } + + public IList Fetch(IIndexerBase indexer, SingleEpisodeSearchDefinition searchDefinition) + { + _logger.Debug("Searching for {0}", searchDefinition); + + var searchUrls = indexer.GetEpisodeSearchUrls(searchDefinition.SceneTitle, searchDefinition.SeasonNumber, searchDefinition.EpisodeNumber); + var result = Fetch(indexer, searchUrls); + + + _logger.Info("Finished searching {0} on {1}. Found {2}", indexer.Name, searchDefinition, result.Count); + return result; + + } + + public IList Fetch(IIndexerBase indexer, PartialSeasonSearchDefinition searchDefinition) + { + _logger.Debug("Searching for {0}", searchDefinition); + + var searchUrls = indexer.GetSeasonSearchUrls(searchDefinition.SceneTitle, searchDefinition.SeasonNumber); + var result = Fetch(indexer, searchUrls); + + + _logger.Info("Finished searching {0} on {1}. Found {2}", indexer.Name, searchDefinition, result.Count); + return result; + } + + public IList Fetch(IIndexerBase indexer, DailyEpisodeSearchDefinition searchDefinition) + { + _logger.Debug("Searching for {0}", searchDefinition); + + var searchUrls = indexer.GetDailyEpisodeSearchUrls(searchDefinition.SceneTitle, searchDefinition.Airtime); + var result = Fetch(indexer, searchUrls); + + _logger.Info("Finished searching {0} on {1}. Found {2}", indexer.Name, searchDefinition, result.Count); + return result; + } + + private List Fetch(IIndexerBase indexer, IEnumerable urls) + { + var result = new List(); + + foreach (var url in urls) + { + try + { + _logger.Trace("Downloading Feed " + url); + var stream = _httpProvider.DownloadStream(url); + result.AddRange(indexer.Parser.Process(stream)); + } + catch (WebException webException) + { + if (webException.Message.Contains("503")) + { + _logger.Warn("{0} server is currently unavailable.{1} {2}", indexer.Name, url, webException.Message); + } + else + { + webException.Data.Add("FeedUrl", url); + _logger.ErrorException("An error occurred while processing feed. " + url, webException); + } + } + catch (Exception feedEx) + { + feedEx.Data.Add("FeedUrl", url); + _logger.ErrorException("An error occurred while processing feed. " + url, feedEx); + } + } + + result.ForEach(c => c.Indexer = indexer.Name); + + return result; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/IndexerRepository.cs b/NzbDrone.Core/Indexers/IndexerRepository.cs index c527fa3a8..f2572d812 100644 --- a/NzbDrone.Core/Indexers/IndexerRepository.cs +++ b/NzbDrone.Core/Indexers/IndexerRepository.cs @@ -1,25 +1,23 @@ -using System; -using System.Data; -using System.Linq; +using System.Linq; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Indexers { - public interface IIndexerRepository : IBasicRepository + public interface IIndexerRepository : IBasicRepository { - Indexer Find(Type type); + IndexerDefinition Get(string name); } - public class IndexerRepository : BasicRepository, IIndexerRepository + public class IndexerRepository : BasicRepository, IIndexerRepository { public IndexerRepository(IDatabase database) : base(database) { } - public Indexer Find(Type type) + public IndexerDefinition Get(string name) { - return Query.Single(i => i.Type == type.ToString()); + return Query.Single(i => i.Name.ToLower() == name.ToLower()); } } } diff --git a/NzbDrone.Core/Indexers/IndexerService.cs b/NzbDrone.Core/Indexers/IndexerService.cs index 3c0a1c352..2d86e6201 100644 --- a/NzbDrone.Core/Indexers/IndexerService.cs +++ b/NzbDrone.Core/Indexers/IndexerService.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using NLog; using NzbDrone.Core.Lifecycle; @@ -9,10 +8,10 @@ namespace NzbDrone.Core.Indexers { public interface IIndexerService { - List All(); - List GetEnabledIndexers(); - void SaveSettings(Indexer indexer); - Indexer GetSettings(Type type); + List All(); + List GetAvailableIndexers(); + void Save(IndexerDefinition indexer); + IndexerDefinition Get(string name); } public class IndexerService : IIndexerService, IInitializable @@ -20,9 +19,9 @@ namespace NzbDrone.Core.Indexers private readonly IIndexerRepository _indexerRepository; private readonly Logger _logger; - private IList _indexers; + private readonly IList _indexers; - public IndexerService(IIndexerRepository indexerRepository, IEnumerable indexers, Logger logger) + public IndexerService(IIndexerRepository indexerRepository, IEnumerable indexers, Logger logger) { _indexerRepository = indexerRepository; _logger = logger; @@ -37,14 +36,13 @@ namespace NzbDrone.Core.Indexers foreach (var feedProvider in _indexers) { - IndexerBase indexerLocal = feedProvider; - if (!currentIndexers.Exists(c => c.Type == indexerLocal.GetType().ToString())) + IIndexerBase indexerLocal = feedProvider; + if (!currentIndexers.Exists(c => c.Name == indexerLocal.Name)) { - var settings = new Indexer + var settings = new IndexerDefinition { Enable = indexerLocal.EnabledByDefault, - Type = indexerLocal.GetType().ToString(), - Name = indexerLocal.Name + Name = indexerLocal.Name.ToLower() }; _indexerRepository.Insert(settings); @@ -52,27 +50,27 @@ namespace NzbDrone.Core.Indexers } } - public List All() + public List All() { return _indexerRepository.All().ToList(); } - public List GetEnabledIndexers() + public List GetAvailableIndexers() { - var all = All(); - return _indexers.Where(i => all.Exists(c => c.Type == i.GetType().ToString() && c.Enable)).ToList(); + var enabled = All().Where(c => c.Enable).Select(c => c.Name); + var configureIndexers = _indexers.Where(c => c.Settings.IsValid); + + return configureIndexers.Where(c => enabled.Contains(c.Name)).ToList(); } - public void SaveSettings(Indexer indexer) + public void Save(IndexerDefinition indexer) { - //Todo: This will be used in the API - _logger.Debug("Upserting Indexer definitions for {0}", indexer.Name); - _indexerRepository.Upsert(indexer); + _indexerRepository.Update(indexer); } - public Indexer GetSettings(Type type) + public IndexerDefinition Get(string name) { - return _indexerRepository.Find(type); + return _indexerRepository.Get(name); } } } \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/IndexerSettingProvider.cs b/NzbDrone.Core/Indexers/IndexerSettingProvider.cs new file mode 100644 index 000000000..7c6356d29 --- /dev/null +++ b/NzbDrone.Core/Indexers/IndexerSettingProvider.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; + +namespace NzbDrone.Core.Indexers +{ + public interface IProviderIndexerSetting + { + TSetting Get(IIndexerBase indexer) where TSetting : IIndexerSetting, new(); + } + + public class IndexerSettingProvider : IProviderIndexerSetting + { + private readonly IIndexerRepository _indexerRepository; + + public IndexerSettingProvider(IIndexerRepository indexerRepository) + { + _indexerRepository = indexerRepository; + } + + public TSetting Get(IIndexerBase indexer) where TSetting : IIndexerSetting, new() + { + var json = _indexerRepository.Get(indexer.Name).Settings; + return JsonConvert.DeserializeObject(json); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/Newznab.cs b/NzbDrone.Core/Indexers/Newznab.cs deleted file mode 100644 index 2f822dcc8..000000000 --- a/NzbDrone.Core/Indexers/Newznab.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System.Linq; -using System; -using System.Collections.Generic; -using System.ServiceModel.Syndication; -using System.Text.RegularExpressions; -using NzbDrone.Common; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Model; -using NzbDrone.Core.Providers; - -namespace NzbDrone.Core.Indexers -{ - public class Newznab : IndexerBase - { - private readonly INewznabService _newznabProvider; - - public Newznab(HttpProvider httpProvider, IConfigService configService, INewznabService newznabProvider) - : base(httpProvider, configService) - { - _newznabProvider = newznabProvider; - } - - protected override string[] Urls - { - get { return GetUrls(); } - } - - public override bool IsConfigured - { - get { return true; } - } - - protected override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}&limit=100&q={1}&season={2}&ep={3}", url, seriesTitle, seasonNumber, episodeNumber)); - } - - return searchUrls; - } - - protected override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}&limit=100&q={1}&season={2:yyyy}&ep={2:MM/dd}", url, seriesTitle, date)); - } - - return searchUrls; - } - - protected override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}&limit=100&q={1}&season={2}", url, seriesTitle, seasonNumber)); - } - - return searchUrls; - } - - protected override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}&limit=100&q={1}+S{2:00}E{3}", url, seriesTitle, seasonNumber, episodeWildcard)); - } - - return searchUrls; - } - - public override string Name - { - get { return "Newznab"; } - } - - protected override string NzbDownloadUrl(SyndicationItem item) - { - return item.Links[0].Uri.ToString(); - } - - protected override string NzbInfoUrl(SyndicationItem item) - { - return item.Id; - } - - protected override EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult) - { - if (currentResult != null) - { - if (item.Links.Count > 1) - currentResult.Size = item.Links[1].Length; - - currentResult.Indexer = GetName(item); - } - - return currentResult; - } - - private string[] GetUrls() - { - var urls = new List(); - var newznabIndexers = _newznabProvider.Enabled(); - - foreach (var newznabDefinition in newznabIndexers) - { - if (!String.IsNullOrWhiteSpace(newznabDefinition.ApiKey)) - urls.Add(String.Format("{0}/api?t=tvsearch&cat=5030,5040,5070,5090&apikey={1}", newznabDefinition.Url, - newznabDefinition.ApiKey)); - - else - urls.Add(String.Format("{0}/api?t=tvsearch&cat=5030,5040,5070,5090s", newznabDefinition.Url)); - } - - return urls.ToArray(); - } - - private string GetName(SyndicationItem item) - { - var hostname = item.Links[0].Uri.DnsSafeHost.ToLower(); - return String.Format("{0}_{1}", Name, hostname); - } - - public override string GetQueryTitle(string title) - { - title = RemoveThe.Replace(title, string.Empty); - - //remove any repeating whitespace - var cleanTitle = TitleSearchRegex.Replace(title, "%20"); - - cleanTitle = Regex.Replace(cleanTitle, @"(%20){1,100}", "%20"); - - //Trim %20 from start then then the end - cleanTitle = Regex.Replace(cleanTitle, "^(%20)", ""); - cleanTitle = Regex.Replace(cleanTitle, "(%20)$", ""); - - return cleanTitle; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/Newznab/Newznab.cs b/NzbDrone.Core/Indexers/Newznab/Newznab.cs new file mode 100644 index 000000000..23e43ea40 --- /dev/null +++ b/NzbDrone.Core/Indexers/Newznab/Newznab.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Core.Indexers.Newznab +{ + public class Newznab : BaseIndexer + { + private readonly INewznabService _newznabProvider; + + public Newznab(INewznabService newznabProvider) + { + _newznabProvider = newznabProvider; + } + + + public override IEnumerable RecentFeed + { + get { return GetUrls(); } + } + + public override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) + { + foreach (var url in RecentFeed) + { + yield return String.Format("{0}&limit=100&q={1}&season={2}&ep={3}", url, NewsnabifyTitle(seriesTitle), seasonNumber, episodeNumber); + } + } + + public override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) + { + var searchUrls = new List(); + + foreach (var url in RecentFeed) + { + searchUrls.Add(String.Format("{0}&limit=100&q={1}&season={2:yyyy}&ep={2:MM/dd}", url, NewsnabifyTitle(seriesTitle), date)); + } + + return searchUrls; + } + + public override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) + { + foreach (var url in RecentFeed) + { + yield return String.Format("{0}&limit=100&q={1}&season={2}", url, NewsnabifyTitle(seriesTitle), seasonNumber); + } + } + + public override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) + { + foreach (var url in RecentFeed) + { + yield return + String.Format("{0}&limit=100&q={1}+S{2:00}E{3}", url, NewsnabifyTitle(seriesTitle), seasonNumber, episodeWildcard); + } + } + + public override string Name + { + get { return "Newznab"; } + } + + + private IEnumerable GetUrls() + { + var urls = new List(); + var newznabIndexers = _newznabProvider.Enabled(); + + foreach (var newznabDefinition in newznabIndexers) + { + var url = String.Format("{0}/api?t=tvsearch&cat=5030,5040,5070,5090s", newznabDefinition.Url); + + if (String.IsNullOrWhiteSpace(newznabDefinition.ApiKey)) + { + url += "&apikey=" + newznabDefinition.ApiKey; + } + + urls.Add(url); + } + + return urls; + } + + + private static string NewsnabifyTitle(string title) + { + return title.Replace("+", "%20"); + } + + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/NewznabDefinition.cs b/NzbDrone.Core/Indexers/Newznab/NewznabDefinition.cs similarity index 73% rename from NzbDrone.Core/Indexers/NewznabDefinition.cs rename to NzbDrone.Core/Indexers/Newznab/NewznabDefinition.cs index 9a8c373d1..2fad208af 100644 --- a/NzbDrone.Core/Indexers/NewznabDefinition.cs +++ b/NzbDrone.Core/Indexers/Newznab/NewznabDefinition.cs @@ -1,8 +1,7 @@ -using System; +using System; using NzbDrone.Core.Datastore; - -namespace NzbDrone.Core.Indexers +namespace NzbDrone.Core.Indexers.Newznab { public class NewznabDefinition : ModelBase { @@ -10,6 +9,5 @@ namespace NzbDrone.Core.Indexers public String Name { get; set; } public String Url { get; set; } public String ApiKey { get; set; } - public bool BuiltIn { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs b/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs new file mode 100644 index 000000000..f9e9f194f --- /dev/null +++ b/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs @@ -0,0 +1,41 @@ +using System; +using System.ServiceModel.Syndication; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.Indexers.Newznab +{ + public class NewznabParser : BasicRssParser + { + private readonly Newznab _newznabIndexer; + + public NewznabParser(Newznab newznabIndexer) + { + _newznabIndexer = newznabIndexer; + } + + protected override string GetNzbInfoUrl(SyndicationItem item) + { + return item.Id; + } + + protected override EpisodeParseResult PostProcessor(SyndicationItem item, EpisodeParseResult currentResult) + { + if (currentResult != null) + { + if (item.Links.Count > 1) + currentResult.Size = item.Links[1].Length; + + currentResult.Indexer = GetName(item); + } + + return currentResult; + } + + + private string GetName(SyndicationItem item) + { + var hostname = item.Links[0].Uri.DnsSafeHost.ToLower(); + return String.Format("{0}_{1}", _newznabIndexer.Name, hostname); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/NewznabRepository.cs b/NzbDrone.Core/Indexers/Newznab/NewznabRepository.cs similarity index 87% rename from NzbDrone.Core/Indexers/NewznabRepository.cs rename to NzbDrone.Core/Indexers/Newznab/NewznabRepository.cs index 38a7099e5..16a304104 100644 --- a/NzbDrone.Core/Indexers/NewznabRepository.cs +++ b/NzbDrone.Core/Indexers/Newznab/NewznabRepository.cs @@ -1,7 +1,7 @@ -using System.Collections.Generic; +using System.Collections.Generic; using NzbDrone.Core.Datastore; -namespace NzbDrone.Core.Indexers +namespace NzbDrone.Core.Indexers.Newznab { public interface INewznabRepository : IBasicRepository { diff --git a/NzbDrone.Core/Indexers/NewznabService.cs b/NzbDrone.Core/Indexers/Newznab/NewznabService.cs similarity index 94% rename from NzbDrone.Core/Indexers/NewznabService.cs rename to NzbDrone.Core/Indexers/Newznab/NewznabService.cs index d491f67e0..5f387ca25 100644 --- a/NzbDrone.Core/Indexers/NewznabService.cs +++ b/NzbDrone.Core/Indexers/Newznab/NewznabService.cs @@ -1,12 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; using NLog; using NzbDrone.Core.Lifecycle; - -namespace NzbDrone.Core.Indexers +namespace NzbDrone.Core.Indexers.Newznab { public interface INewznabService { @@ -53,7 +52,7 @@ namespace NzbDrone.Core.Indexers public void Delete(int id) { - + _newznabRepository.Delete(id); } @@ -79,9 +78,9 @@ namespace NzbDrone.Core.Indexers { var newznabIndexers = new List { - new NewznabDefinition { Enable = false, Name = "Nzbs.org", Url = "http://nzbs.org", BuiltIn = true }, - new NewznabDefinition { Enable = false, Name = "Nzb.su", Url = "https://nzb.su", BuiltIn = true }, - new NewznabDefinition { Enable = false, Name = "Dognzb.cr", Url = "https://dognzb.cr", BuiltIn = true } + new NewznabDefinition { Enable = false, Name = "Nzbs.org", Url = "http://nzbs.org" }, + new NewznabDefinition { Enable = false, Name = "Nzb.su", Url = "https://nzb.su" }, + new NewznabDefinition { Enable = false, Name = "Dognzb.cr", Url = "https://dognzb.cr" } }; _logger.Debug("Initializing Newznab indexers. Count {0}", newznabIndexers); @@ -112,7 +111,6 @@ namespace NzbDrone.Core.Indexers Name = indexerLocal.Name, Url = indexerLocal.Url, ApiKey = indexerLocal.ApiKey, - BuiltIn = true }; Insert(definition); @@ -121,7 +119,6 @@ namespace NzbDrone.Core.Indexers else { currentIndexer.Url = indexerLocal.Url; - currentIndexer.BuiltIn = true; Update(currentIndexer); } } diff --git a/NzbDrone.Core/Indexers/NzbClub.cs b/NzbDrone.Core/Indexers/NzbClub.cs deleted file mode 100644 index 7724e568e..000000000 --- a/NzbDrone.Core/Indexers/NzbClub.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System.Linq; -using System; -using System.Collections.Generic; -using System.ServiceModel.Syndication; -using System.Text.RegularExpressions; -using NzbDrone.Common; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Model; - -namespace NzbDrone.Core.Indexers -{ - public class NzbClub : IndexerBase - { - public NzbClub(HttpProvider httpProvider, IConfigService configService) - : base(httpProvider, configService) - { - } - - protected override string[] Urls - { - get - { - return new[] - { - String.Format("http://www.nzbclub.com/nzbfeed.aspx?ig=2&gid=102952&st=1&ns=1&q=%23a.b.teevee"), - String.Format("http://www.nzbclub.com/nzbfeed.aspx?ig=2&gid=5542&st=1&ns=1&q=") - }; - } - } - - public override bool IsConfigured - { - get - { - return true; - } - } - - public override string Name - { - get { return "NzbClub"; } - } - - protected override string NzbDownloadUrl(SyndicationItem item) - { - return item.Links[0].Uri.ToString(); - } - - protected override string NzbInfoUrl(SyndicationItem item) - { - return item.Links[1].Uri.ToString(); - } - - protected override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}+{1}+s{2:00}e{3:00}", url, seriesTitle, seasonNumber, episodeNumber)); - } - - return searchUrls; - } - - protected override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}+{1}+s{2:00}", url, seriesTitle, seasonNumber)); - } - - return searchUrls; - } - - protected override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}+{1}+{2:yyyy MM dd}", url, seriesTitle, date)); - } - - return searchUrls; - } - - protected override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}+{1}+S{2:00}E{3}", url, seriesTitle, seasonNumber, episodeWildcard)); - } - - return searchUrls; - } - - protected override EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult) - { - if (currentResult != null) - { - var sizeString = Regex.Match(item.Summary.Text, @"Size:\s\d+\.\d{1,2}\s\w{2}\s", RegexOptions.IgnoreCase | RegexOptions.Compiled).Value; - currentResult.Size = Parser.GetReportSize(sizeString); - } - - return currentResult; - } - - public override bool EnabledByDefault - { - get { return false; } - } - - protected override string TitlePreParser(SyndicationItem item) - { - var title = Parser.ParseHeader(item.Title.Text); - - if (String.IsNullOrWhiteSpace(title)) - return item.Title.Text; - - return title; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/NzbClub/NzbClub.cs b/NzbDrone.Core/Indexers/NzbClub/NzbClub.cs new file mode 100644 index 000000000..efa70e9ce --- /dev/null +++ b/NzbDrone.Core/Indexers/NzbClub/NzbClub.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using NzbDrone.Common; +using NzbDrone.Core.Configuration; + +namespace NzbDrone.Core.Indexers.NzbClub +{ + public class NzbClub : BaseIndexer + { + public NzbClub(HttpProvider httpProvider, IConfigService configService) + { + } + + public override IEnumerable RecentFeed + { + get + { + return new[] + { + String.Format("http://www.nzbclub.com/nzbfeed.aspx?ig=2&gid=102952&st=1&ns=1&q=%23a.b.teevee"), + String.Format("http://www.nzbclub.com/nzbfeed.aspx?ig=2&gid=5542&st=1&ns=1&q=") + }; + } + } + + public override string Name + { + get { return "NzbClub"; } + } + + + + public override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) + { + var searchUrls = new List(); + + foreach (var url in RecentFeed) + { + searchUrls.Add(String.Format("{0}+{1}+s{2:00}e{3:00}", url, seriesTitle, seasonNumber, episodeNumber)); + } + + return searchUrls; + } + + public override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) + { + var searchUrls = new List(); + + foreach (var url in RecentFeed) + { + searchUrls.Add(String.Format("{0}+{1}+s{2:00}", url, seriesTitle, seasonNumber)); + } + + return searchUrls; + } + + public override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) + { + var searchUrls = new List(); + + foreach (var url in RecentFeed) + { + searchUrls.Add(String.Format("{0}+{1}+{2:yyyy MM dd}", url, seriesTitle, date)); + } + + return searchUrls; + } + + public override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) + { + var searchUrls = new List(); + + foreach (var url in RecentFeed) + { + searchUrls.Add(String.Format("{0}+{1}+S{2:00}E{3}", url, seriesTitle, seasonNumber, episodeWildcard)); + } + + return searchUrls; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/NzbClub/NzbClubParser.cs b/NzbDrone.Core/Indexers/NzbClub/NzbClubParser.cs new file mode 100644 index 000000000..4333dfcda --- /dev/null +++ b/NzbDrone.Core/Indexers/NzbClub/NzbClubParser.cs @@ -0,0 +1,36 @@ +using System; +using System.ServiceModel.Syndication; +using System.Text.RegularExpressions; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.Indexers.NzbClub +{ + public class NzbClubParser : BasicRssParser + { + protected override EpisodeParseResult PostProcessor(SyndicationItem item, EpisodeParseResult currentResult) + { + if (currentResult != null) + { + var sizeString = Regex.Match(item.Summary.Text, @"Size:\s\d+\.\d{1,2}\s\w{2}\s", RegexOptions.IgnoreCase | RegexOptions.Compiled).Value; + currentResult.Size = Parser.GetReportSize(sizeString); + } + + return currentResult; + } + + protected override string GetTitle(SyndicationItem syndicationItem) + { + var title = Parser.ParseHeader(syndicationItem.Title.Text); + + if (String.IsNullOrWhiteSpace(title)) + return syndicationItem.Title.Text; + + return title; + } + + protected override string GetNzbInfoUrl(SyndicationItem item) + { + return item.Links[1].Uri.ToString(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/NzbIndex.cs b/NzbDrone.Core/Indexers/NzbIndex.cs deleted file mode 100644 index c716d49d0..000000000 --- a/NzbDrone.Core/Indexers/NzbIndex.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System.Linq; -using System; -using System.Collections.Generic; -using System.ServiceModel.Syndication; -using System.Text.RegularExpressions; -using NzbDrone.Common; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Model; - -namespace NzbDrone.Core.Indexers -{ - public class NzbIndex : IndexerBase - { - public NzbIndex(HttpProvider httpProvider, IConfigService configService) - : base(httpProvider, configService) - { - } - - protected override string[] Urls - { - get - { - return new[] - { - String.Format("http://www.nzbindex.nl/rss/alt.binaries.teevee/?sort=agedesc&minsize=100&complete=1&max=50&more=1&q=%23a.b.teevee"), - String.Format("http://www.nzbindex.nl/rss/alt.binaries.hdtv/?sort=agedesc&minsize=100&complete=1&max=50&more=1&q=") - }; - } - } - - public override bool IsConfigured - { - get - { - return true; - } - } - - public override string Name - { - get { return "NzbIndex"; } - } - - protected override string NzbDownloadUrl(SyndicationItem item) - { - return item.Links[1].Uri.ToString(); - } - - protected override string NzbInfoUrl(SyndicationItem item) - { - return item.Links[0].Uri.ToString(); - } - - protected override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}+{1}+s{2:00}e{3:00}", url, seriesTitle, seasonNumber, episodeNumber)); - } - - return searchUrls; - } - - protected override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}+{1}+s{2:00}", url, seriesTitle, seasonNumber)); - } - - return searchUrls; - } - - protected override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}+{1}+{2:yyyy MM dd}", url, seriesTitle, date)); - } - - return searchUrls; - } - - protected override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}+{1}+S{2:00}E{3}", url, seriesTitle, seasonNumber, episodeWildcard)); - } - - return searchUrls; - } - - protected override EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult) - { - if (currentResult != null) - { - var sizeString = Regex.Match(item.Summary.Text, @"\d+\.\d{1,2}\s\w{2}", RegexOptions.IgnoreCase | RegexOptions.Compiled).Value; - currentResult.Size = Parser.GetReportSize(sizeString); - } - - return currentResult; - } - - public override bool EnabledByDefault - { - get { return true; } - } - - protected override string TitlePreParser(SyndicationItem item) - { - var title = Parser.ParseHeader(item.Title.Text); - - if (String.IsNullOrWhiteSpace(title)) - return item.Title.Text; - - return title; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/NzbIndex/NzbIndex.cs b/NzbDrone.Core/Indexers/NzbIndex/NzbIndex.cs new file mode 100644 index 000000000..395772719 --- /dev/null +++ b/NzbDrone.Core/Indexers/NzbIndex/NzbIndex.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Core.Indexers.NzbIndex +{ + public class NzbIndex : BaseIndexer + { + public override IEnumerable RecentFeed + { + get + { + return new[] + { + String.Format("http://www.nzbindex.nl/rss/alt.binaries.teevee/?sort=agedesc&minsize=100&complete=1&max=50&more=1&q=%23a.b.teevee"), + String.Format("http://www.nzbindex.nl/rss/alt.binaries.hdtv/?sort=agedesc&minsize=100&complete=1&max=50&more=1&q=") + }; + } + } + + + public override string Name + { + get { return "NzbIndex"; } + } + + + + + public override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) + { + var searchUrls = new List(); + + foreach (var url in RecentFeed) + { + searchUrls.Add(String.Format("{0}+{1}+s{2:00}e{3:00}", url, seriesTitle, seasonNumber, episodeNumber)); + } + + return searchUrls; + } + + public override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) + { + var searchUrls = new List(); + + foreach (var url in RecentFeed) + { + searchUrls.Add(String.Format("{0}+{1}+s{2:00}", url, seriesTitle, seasonNumber)); + } + + return searchUrls; + } + + public override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) + { + var searchUrls = new List(); + + foreach (var url in RecentFeed) + { + searchUrls.Add(String.Format("{0}+{1}+{2:yyyy MM dd}", url, seriesTitle, date)); + } + + return searchUrls; + } + + public override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) + { + var searchUrls = new List(); + + foreach (var url in RecentFeed) + { + searchUrls.Add(String.Format("{0}+{1}+S{2:00}E{3}", url, seriesTitle, seasonNumber, episodeWildcard)); + } + + return searchUrls; + } + + + + + + + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/NzbIndex/NzbIndexParser.cs b/NzbDrone.Core/Indexers/NzbIndex/NzbIndexParser.cs new file mode 100644 index 000000000..890e40de8 --- /dev/null +++ b/NzbDrone.Core/Indexers/NzbIndex/NzbIndexParser.cs @@ -0,0 +1,42 @@ +using System; +using System.ServiceModel.Syndication; +using System.Text.RegularExpressions; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.Indexers.NzbIndex +{ + public class NzbIndexParser : BasicRssParser + { + + protected override string GetNzbUrl(SyndicationItem item) + { + return item.Links[1].Uri.ToString(); + } + + protected override string GetNzbInfoUrl(SyndicationItem item) + { + return item.Links[0].Uri.ToString(); + } + + protected override EpisodeParseResult PostProcessor(SyndicationItem item, EpisodeParseResult currentResult) + { + if (currentResult != null) + { + var sizeString = Regex.Match(item.Summary.Text, @"\d+\.\d{1,2}\s\w{2}", RegexOptions.IgnoreCase | RegexOptions.Compiled).Value; + currentResult.Size = Parser.GetReportSize(sizeString); + } + + return currentResult; + } + + protected override string GetTitle(SyndicationItem item) + { + var title = Parser.ParseHeader(item.Title.Text); + + if (String.IsNullOrWhiteSpace(title)) + return item.Title.Text; + + return title; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/NzbsRUs.cs b/NzbDrone.Core/Indexers/NzbsRUs.cs deleted file mode 100644 index 4a571c7da..000000000 --- a/NzbDrone.Core/Indexers/NzbsRUs.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Linq; -using System; -using System.Collections.Generic; -using System.ServiceModel.Syndication; -using System.Text.RegularExpressions; -using NzbDrone.Common; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Model; - -namespace NzbDrone.Core.Indexers -{ - public class NzbsRUs : IndexerBase - { - public NzbsRUs(HttpProvider httpProvider, IConfigService configService) : base(httpProvider, configService) - { - } - - protected override string[] Urls - { - get - { - return new[] - { - string.Format( - "https://www.nzbsrus.com/rssfeed.php?cat=91,75&i={0}&h={1}", - _configService.NzbsrusUId, - _configService.NzbsrusHash) - }; - } - } - - public override bool IsConfigured - { - get - { - return !string.IsNullOrWhiteSpace(_configService.NzbsrusUId) && - !string.IsNullOrWhiteSpace(_configService.NzbsrusHash); - } - } - - public override string Name - { - get { return "NzbsRUs"; } - } - - protected override string NzbDownloadUrl(SyndicationItem item) - { - return item.Links[0].Uri.ToString(); - } - - protected override string NzbInfoUrl(SyndicationItem item) - { - return item.Links[0].Uri.ToString(); - } - - protected override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) - { - return new List(); - } - - protected override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) - { - return new List(); - } - - protected override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) - { - return new List(); - } - - protected override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) - { - return new List(); - } - - protected override EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult) - { - if (currentResult != null) - { - var sizeString = Regex.Match(item.Summary.Text, @"\d+\.\d{1,2} \w{3}", RegexOptions.IgnoreCase).Value; - currentResult.Size = Parser.GetReportSize(sizeString); - } - - return currentResult; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/NzbsRUs/NzbsRUs.cs b/NzbDrone.Core/Indexers/NzbsRUs/NzbsRUs.cs new file mode 100644 index 000000000..3cf988a45 --- /dev/null +++ b/NzbDrone.Core/Indexers/NzbsRUs/NzbsRUs.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Core.Indexers.NzbsRUs +{ + public class Nzbsrus : BaseIndexer + { + private readonly NzbsrusSetting _setting; + + public Nzbsrus(IProviderIndexerSetting settingProvider) + { + _setting = settingProvider.Get(this); + } + + public override IEnumerable RecentFeed + { + get + { + yield return string.Format("https://www.nzbsrus.com/rssfeed.php?cat=91,75&i={0}&h={1}", + _setting.Uid, + _setting.Hash); + + } + } + + + public override IIndexerSetting Settings + { + get { return _setting; } + } + + public override string Name + { + get { return "NzbsRUs"; } + } + + public override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) + { + return new List(); + } + + public override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) + { + return new List(); + } + + public override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) + { + return new List(); + } + + public override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) + { + return new List(); + } + + + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/NzbsRUs/NzbsrusParser.cs b/NzbDrone.Core/Indexers/NzbsRUs/NzbsrusParser.cs new file mode 100644 index 000000000..92342f590 --- /dev/null +++ b/NzbDrone.Core/Indexers/NzbsRUs/NzbsrusParser.cs @@ -0,0 +1,20 @@ +using System.ServiceModel.Syndication; +using System.Text.RegularExpressions; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.Indexers.NzbsRUs +{ + public class NzbsrusParser : BasicRssParser + { + protected override EpisodeParseResult PostProcessor(SyndicationItem item, EpisodeParseResult currentResult) + { + if (currentResult != null) + { + var sizeString = Regex.Match(item.Summary.Text, @"\d+\.\d{1,2} \w{3}", RegexOptions.IgnoreCase).Value; + currentResult.Size = Parser.GetReportSize(sizeString); + } + + return currentResult; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/NzbsRUs/NzbsrusSettings.cs b/NzbDrone.Core/Indexers/NzbsRUs/NzbsrusSettings.cs new file mode 100644 index 000000000..70e217ab4 --- /dev/null +++ b/NzbDrone.Core/Indexers/NzbsRUs/NzbsrusSettings.cs @@ -0,0 +1,18 @@ +using System; + +namespace NzbDrone.Core.Indexers.NzbsRUs +{ + public class NzbsrusSetting : IIndexerSetting + { + public String Uid { get; set; } + public String Hash { get; set; } + + public bool IsValid + { + get + { + return !string.IsNullOrWhiteSpace(Uid) && !string.IsNullOrWhiteSpace(Hash); + } + } + } +} diff --git a/NzbDrone.Core/Indexers/Nzbx.cs b/NzbDrone.Core/Indexers/Nzbx.cs deleted file mode 100644 index ec340e56b..000000000 --- a/NzbDrone.Core/Indexers/Nzbx.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.ServiceModel.Syndication; -using Newtonsoft.Json; -using NzbDrone.Common; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Model; -using NzbDrone.Core.Model.Nzbx; - -namespace NzbDrone.Core.Indexers -{ - class Nzbx : IndexerBase - { - public Nzbx(HttpProvider httpProvider, IConfigService configService) - : base(httpProvider, configService) - { - } - - public override string Name - { - get { return "nzbx"; } - } - - protected override string[] Urls - { - get - { - return new string[] - { - String.Format("https://nzbx.co/api/recent?category=tv") - }; - } - } - - public override bool IsConfigured - { - get - { - return true; - //return !string.IsNullOrWhiteSpace(_configProvider.OmgwtfnzbsUsername) && - // !string.IsNullOrWhiteSpace(_configProvider.OmgwtfnzbsApiKey); - } - } - - protected override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) - { - var searchUrls = new List(); - - searchUrls.Add(String.Format("https://nzbx.co/api/search?q={0}+S{1:00}E{2:00}", seriesTitle, seasonNumber, episodeNumber)); - - return searchUrls; - } - - protected override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) - { - var searchUrls = new List(); - - searchUrls.Add(String.Format("https://nzbx.co/api/search?q={0}+{1:yyyy MM dd}", seriesTitle, date)); - - return searchUrls; - } - - protected override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) - { - var searchUrls = new List(); - - searchUrls.Add(String.Format("https://nzbx.co/api/search?q={0}+S{1:00}", seriesTitle, seasonNumber)); - - return searchUrls; - } - - protected override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) - { - var searchUrls = new List(); - - searchUrls.Add(String.Format("https://nzbx.co/api/search?q={0}+S{1:00}E{2}", seriesTitle, seasonNumber, episodeWildcard)); - - return searchUrls; - } - - protected override string NzbDownloadUrl(SyndicationItem item) - { - throw new NotImplementedException(); - } - - protected override string NzbInfoUrl(SyndicationItem item) - { - throw new NotImplementedException(); - } - - protected override EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult) - { - throw new NotImplementedException(); - } - - public override IList FetchRss() - { - _logger.Debug("Fetching feeds from " + Name); - - var result = new List(); - - if (!IsConfigured) - { - _logger.Warn("Indexer '{0}' isn't configured correctly. please reconfigure the indexer in settings page.", Name); - return result; - } - - foreach (var url in Urls) - { - var response = Download(url); - - if (response != null) - { - var feed = JsonConvert.DeserializeObject>(response); - - foreach (var item in feed) - { - try - { - var episodeParseResult = Parser.ParseTitle(item.Name); - if (episodeParseResult != null) - { - episodeParseResult.Age = DateTime.Now.Date.Subtract(item.PostDate).Days; - episodeParseResult.OriginalString = item.Name; - episodeParseResult.SceneSource = true; - episodeParseResult.NzbUrl = String.Format("http://nzbx.co/nzb?{0}*|*{1}", item.Guid, item.Name); - episodeParseResult.NzbInfoUrl = String.Format("http://nzbx.co/d?{0}", item.Guid); - episodeParseResult.Indexer = Name; - episodeParseResult.Size = item.Size; - - result.Add(episodeParseResult); - } - } - catch (Exception itemEx) - { - itemEx.Data.Add("FeedUrl", url); - itemEx.Data.Add("Item", item.Name); - _logger.ErrorException("An error occurred while processing feed item", itemEx); - } - - } - } - } - - _logger.Debug("Finished processing feeds from " + Name); - return result; - } - - protected List Fetch(IEnumerable urls) - { - var result = new List(); - - if (!IsConfigured) - { - _logger.Warn("Indexer '{0}' isn't configured correctly. please reconfigure the indexer in settings page.", Name); - return result; - } - - foreach (var url in urls) - { - var response = Download(url); - - if(response != null) - { - var feed = JsonConvert.DeserializeObject>(response); - - foreach (var item in feed) - { - try - { - var episodeParseResult = Parser.ParseTitle(item.Name); - if (episodeParseResult != null) - { - episodeParseResult.Age = DateTime.Now.Date.Subtract(item.PostDate).Days; - episodeParseResult.OriginalString = item.Name; - episodeParseResult.SceneSource = true; - episodeParseResult.NzbUrl = item.Nzb; - episodeParseResult.NzbInfoUrl = String.Format("http://nzbx.co/d?{0}", item.Guid); - episodeParseResult.Indexer = Name; - episodeParseResult.Size = item.Size; - - result.Add(episodeParseResult); - } - } - catch (Exception itemEx) - { - itemEx.Data.Add("FeedUrl", url); - itemEx.Data.Add("Item", item.Name); - _logger.ErrorException("An error occurred while processing feed item", itemEx); - } - - } - } - } - - return result; - } - - private string Download(string url) - { - try - { - _logger.Trace("Downloading RSS " + url); - - return _httpProvider.DownloadString(url, Credentials); - } - catch (WebException webException) - { - if (webException.Message.Contains("503")) - { - _logger.Warn("{0} server is currently unavailable.{1} {2}", Name, url, webException.Message); - } - else - { - webException.Data.Add("FeedUrl", url); - _logger.ErrorException("An error occurred while processing feed. " + url, webException); - } - } - catch (Exception feedEx) - { - feedEx.Data.Add("FeedUrl", url); - _logger.ErrorException("An error occurred while processing feed. " + url, feedEx); - } - - return null; - } - } -} diff --git a/NzbDrone.Core/Indexers/Nzbx/Nzbx.cs b/NzbDrone.Core/Indexers/Nzbx/Nzbx.cs new file mode 100644 index 000000000..fe8880e02 --- /dev/null +++ b/NzbDrone.Core/Indexers/Nzbx/Nzbx.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Core.Indexers.Nzbx +{ + public class Nzbx : BaseIndexer + { + public override string Name + { + get { return "nzbx"; } + } + + public override IEnumerable RecentFeed + { + get + { + return new[] { String.Format("https://nzbx.co/api/recent?category=tv") }; + } + } + + + public override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) + { + yield return String.Format("https://nzbx.co/api/search?q={0}+S{1:00}E{2:00}", seriesTitle, seasonNumber, episodeNumber); + } + + public override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) + { + yield return String.Format("https://nzbx.co/api/search?q={0}+{1:yyyy MM dd}", seriesTitle, date); + } + + public override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) + { + yield return String.Format("https://nzbx.co/api/search?q={0}+S{1:00}", seriesTitle, seasonNumber); + } + + public override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) + { + yield return String.Format("https://nzbx.co/api/search?q={0}+S{1:00}E{2}", seriesTitle, seasonNumber, episodeWildcard); + } + } +} diff --git a/NzbDrone.Core/Indexers/Nzbx/NzbxParser.cs b/NzbDrone.Core/Indexers/Nzbx/NzbxParser.cs new file mode 100644 index 000000000..7fe3e620f --- /dev/null +++ b/NzbDrone.Core/Indexers/Nzbx/NzbxParser.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.IO; +using NLog; +using Newtonsoft.Json; +using NzbDrone.Core.Model; +using NzbDrone.Core.Model.Nzbx; + +namespace NzbDrone.Core.Indexers.Nzbx +{ + public class NzbxParser : IParseFeed + { + private readonly Logger _logger; + private readonly JsonSerializer _serializer; + + public NzbxParser(Logger logger) + { + _logger = logger; + _serializer = new JsonSerializer(); + } + + public IEnumerable Process(Stream source) + { + var result = new List(); + var jsonReader = new JsonTextReader(new StreamReader(source)); + var feed = _serializer.Deserialize>(jsonReader); + + foreach (var item in feed) + { + try + { + var episodeParseResult = Parser.ParseTitle(item.Name); + if (episodeParseResult != null) + { + episodeParseResult.Age = DateTime.Now.Date.Subtract(item.PostDate).Days; + episodeParseResult.OriginalString = item.Name; + episodeParseResult.SceneSource = true; + episodeParseResult.NzbUrl = String.Format("http://nzbx.co/nzb?{0}*|*{1}", item.Guid, item.Name); + episodeParseResult.NzbInfoUrl = String.Format("http://nzbx.co/d?{0}", item.Guid); + episodeParseResult.Size = item.Size; + + result.Add(episodeParseResult); + } + } + catch (Exception itemEx) + { + itemEx.Data.Add("Item", item.Name); + _logger.ErrorException("An error occurred while processing feed item", itemEx); + } + } + + return result; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/Omgwtfnzbs.cs b/NzbDrone.Core/Indexers/Omgwtfnzbs.cs deleted file mode 100644 index 60dc131f6..000000000 --- a/NzbDrone.Core/Indexers/Omgwtfnzbs.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.ServiceModel.Syndication; -using System.Text.RegularExpressions; -using NzbDrone.Common; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Model; - -namespace NzbDrone.Core.Indexers -{ - class Omgwtfnzbs : IndexerBase - { - public Omgwtfnzbs(HttpProvider httpProvider, IConfigService configService) - : base(httpProvider, configService) - { - } - - public override string Name - { - get { return "omgwtfnzbs"; } - } - - protected override string[] Urls - { - get - { - return new string[] - { - String.Format("http://rss.omgwtfnzbs.org/rss-search.php?catid=19,20&user={0}&api={1}&eng=1", - _configService.OmgwtfnzbsUsername, _configService.OmgwtfnzbsApiKey) - }; - } - } - - public override bool IsConfigured - { - get - { - return !string.IsNullOrWhiteSpace(_configService.OmgwtfnzbsUsername) && - !string.IsNullOrWhiteSpace(_configService.OmgwtfnzbsApiKey); - } - } - - protected override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}&search={1}+S{2:00}E{3:00}", url, seriesTitle, seasonNumber, episodeNumber)); - } - - return searchUrls; - } - - protected override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}&search={1}+{2:yyyy MM dd}", url, seriesTitle, date)); - } - - return searchUrls; - } - - protected override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}&search={1}+S{2:00}", url, seriesTitle, seasonNumber)); - } - - return searchUrls; - } - - protected override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) - { - var searchUrls = new List(); - - foreach (var url in Urls) - { - searchUrls.Add(String.Format("{0}&search={1}+S{2:00}E{3}", url, seriesTitle, seasonNumber, episodeWildcard)); - } - - return searchUrls; - } - - protected override string NzbDownloadUrl(SyndicationItem item) - { - return item.Links[0].Uri.ToString(); - } - - protected override string NzbInfoUrl(SyndicationItem item) - { - //Todo: Me thinks I need to parse details to get this... - var match = Regex.Match(item.Summary.Text, @"(?:\View NZB\:\<\/b\>\s\.+)(?:\""\starget)", - RegexOptions.IgnoreCase | RegexOptions.Compiled); - - if(match.Success) - { - return match.Groups["URL"].Value; - } - - return String.Empty; - } - - protected override EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult) - { - if (currentResult != null) - { - var sizeString = Regex.Match(item.Summary.Text, @"Size:\<\/b\>\s\d+\.\d{1,2}\s\w{2}\
", RegexOptions.IgnoreCase | RegexOptions.Compiled).Value; - currentResult.Size = Parser.GetReportSize(sizeString); - } - - return currentResult; - } - } -} diff --git a/NzbDrone.Core/Indexers/Omgwtfnzbs/Omgwtfnzbs.cs b/NzbDrone.Core/Indexers/Omgwtfnzbs/Omgwtfnzbs.cs new file mode 100644 index 000000000..8dc1d8703 --- /dev/null +++ b/NzbDrone.Core/Indexers/Omgwtfnzbs/Omgwtfnzbs.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Core.Indexers.Omgwtfnzbs +{ + public class Omgwtfnzbs : BaseIndexer + { + private readonly OmgwtfnzbsSetting _settings; + + public Omgwtfnzbs(IProviderIndexerSetting settingProvider) + { + _settings = settingProvider.Get(this); + } + + public override string Name + { + get { return "omgwtfnzbs"; } + } + + public override IEnumerable RecentFeed + { + get + { + + yield return + String.Format("http://rss.omgwtfnzbs.org/rss-search.php?catid=19,20&user={0}&api={1}&eng=1", + _settings.Username, _settings.ApiKey); + } + } + + public override IIndexerSetting Settings + { + get { return _settings; } + } + + public override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) + { + var searchUrls = new List(); + + foreach (var url in RecentFeed) + { + searchUrls.Add(String.Format("{0}&search={1}+S{2:00}E{3:00}", url, seriesTitle, seasonNumber, episodeNumber)); + } + + return searchUrls; + } + + public override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) + { + var searchUrls = new List(); + + foreach (var url in RecentFeed) + { + searchUrls.Add(String.Format("{0}&search={1}+{2:yyyy MM dd}", url, seriesTitle, date)); + } + + return searchUrls; + } + + public override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) + { + var searchUrls = new List(); + + foreach (var url in RecentFeed) + { + searchUrls.Add(String.Format("{0}&search={1}+S{2:00}", url, seriesTitle, seasonNumber)); + } + + return searchUrls; + } + + public override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) + { + var searchUrls = new List(); + + foreach (var url in RecentFeed) + { + searchUrls.Add(String.Format("{0}&search={1}+S{2:00}E{3}", url, seriesTitle, seasonNumber, episodeWildcard)); + } + + return searchUrls; + } + + + } +} diff --git a/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsParser.cs b/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsParser.cs new file mode 100644 index 000000000..099ec37a4 --- /dev/null +++ b/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsParser.cs @@ -0,0 +1,40 @@ +using System; +using System.ServiceModel.Syndication; +using System.Text.RegularExpressions; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.Indexers.Omgwtfnzbs +{ + public class OmgwtfnzbsParser : BasicRssParser + { + protected override string GetNzbUrl(SyndicationItem item) + { + return item.Links[0].Uri.ToString(); + } + + protected override string GetNzbInfoUrl(SyndicationItem item) + { + //Todo: Me thinks I need to parse details to get this... + var match = Regex.Match(item.Summary.Text, @"(?:\View NZB\:\<\/b\>\s\.+)(?:\""\starget)", + RegexOptions.IgnoreCase | RegexOptions.Compiled); + + if (match.Success) + { + return match.Groups["URL"].Value; + } + + return String.Empty; + } + + protected override EpisodeParseResult PostProcessor(SyndicationItem item, EpisodeParseResult currentResult) + { + if (currentResult != null) + { + var sizeString = Regex.Match(item.Summary.Text, @"Size:\<\/b\>\s\d+\.\d{1,2}\s\w{2}\
", RegexOptions.IgnoreCase | RegexOptions.Compiled).Value; + currentResult.Size = Parser.GetReportSize(sizeString); + } + + return currentResult; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsSettings.cs b/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsSettings.cs new file mode 100644 index 000000000..429b1de06 --- /dev/null +++ b/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsSettings.cs @@ -0,0 +1,18 @@ +using System; + +namespace NzbDrone.Core.Indexers.Omgwtfnzbs +{ + public class OmgwtfnzbsSetting : IIndexerSetting + { + public String Username { get; set; } + public String ApiKey { get; set; } + + public bool IsValid + { + get + { + return !string.IsNullOrWhiteSpace(Username) && !string.IsNullOrWhiteSpace(ApiKey); + } + } + } +} diff --git a/NzbDrone.Core/Indexers/RssSyncService.cs b/NzbDrone.Core/Indexers/RssSyncService.cs new file mode 100644 index 000000000..492f0bcde --- /dev/null +++ b/NzbDrone.Core/Indexers/RssSyncService.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using NLog; +using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.Download; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.Indexers +{ + + public interface ISyncRss + { + void Sync(); + } + + public class RssSyncService : ISyncRss + { + private readonly IFetchAndParseRss _rssFetcherAndParser; + private readonly IMakeDownloadDecision _downloadDecisionMaker; + private readonly IDownloadService _downloadService; + private readonly Logger _logger; + + public RssSyncService(IFetchAndParseRss rssFetcherAndParser, IMakeDownloadDecision downloadDecisionMaker, IDownloadService downloadService, Logger logger) + { + _rssFetcherAndParser = rssFetcherAndParser; + _downloadDecisionMaker = downloadDecisionMaker; + _downloadService = downloadService; + _logger = logger; + } + + + public void Sync() + { + _logger.Info("Starting RSS Sync"); + + var parseResults = _rssFetcherAndParser.Fetch(); + var decisions = _downloadDecisionMaker.GetRssDecision(parseResults); + + var qualifiedReports = decisions + .Where(c => c.Approved) + .Select(c => c.ParseResult) + .OrderByDescending(c => c.Quality) + .ThenBy(c => c.EpisodeNumbers.MinOrDefault()) + .ThenBy(c => c.Age); + + + foreach (var episodeParseResult in qualifiedReports) + { + try + { + _downloadService.DownloadReport(episodeParseResult); + } + catch (Exception e) + { + _logger.WarnException("Couldn't add report to download queue. " + episodeParseResult, e); + } + } + + _logger.Info("RSS Sync Completed. Reports found {0}, Fetches attempted {1}", parseResults.Count, qualifiedReports); + } + } + + + public interface IFetchAndParseRss + { + List Fetch(); + } + + public class FetchAndParseRssService : IFetchAndParseRss + { + private readonly IIndexerService _indexerService; + private readonly IFetchFeedFromIndexers _feedFetcher; + + public FetchAndParseRssService(IIndexerService indexerService, IFetchFeedFromIndexers feedFetcher) + { + _indexerService = indexerService; + _feedFetcher = feedFetcher; + } + + public List Fetch() + { + var result = new List(); + + var indexers = _indexerService.GetAvailableIndexers(); + + Parallel.ForEach(indexers, indexer => + { + var indexerFeed = _feedFetcher.FetchRss(indexer); + + lock (result) + { + result.AddRange(indexerFeed); + } + }); + + return result; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/Wombles.cs b/NzbDrone.Core/Indexers/Wombles.cs deleted file mode 100644 index 1e26803c0..000000000 --- a/NzbDrone.Core/Indexers/Wombles.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.Linq; -using System; -using System.Collections.Generic; -using System.ServiceModel.Syndication; -using NzbDrone.Common; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Model; - -namespace NzbDrone.Core.Indexers -{ - public class Wombles : IndexerBase - { - public Wombles(HttpProvider httpProvider, IConfigService configService) : base(httpProvider, configService) - { - } - - protected override string[] Urls - { - get - { - return new[] - { - string.Format("http://nzb.isasecret.com/rss") - }; - } - } - - public override bool IsConfigured - { - get - { - return true; - } - } - - public override string Name - { - get { return "WomblesIndex"; } - } - - protected override string NzbDownloadUrl(SyndicationItem item) - { - return item.Links[0].Uri.ToString(); - } - - protected override string NzbInfoUrl(SyndicationItem item) - { - return null; - } - - protected override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) - { - return new List(); - } - - protected override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) - { - return new List(); - } - - protected override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) - { - return new List(); - } - - protected override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) - { - return new List(); - } - - protected override EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult) - { - if (currentResult != null) - { - currentResult.Size = 0; - } - - return currentResult; - } - - public override bool EnabledByDefault - { - get { return true; } - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/Wombles/Wombles.cs b/NzbDrone.Core/Indexers/Wombles/Wombles.cs new file mode 100644 index 000000000..d27df1a6c --- /dev/null +++ b/NzbDrone.Core/Indexers/Wombles/Wombles.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Core.Indexers.Wombles +{ + public class Wombles : BaseIndexer + { + public override IEnumerable RecentFeed + { + get + { + return new[] + { + string.Format("http://nzb.isasecret.com/rss") + }; + } + } + + public override string Name + { + get { return "WomblesIndex"; } + } + + + + public override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) + { + return new List(); + } + + public override IEnumerable GetSeasonSearchUrls(string seriesTitle, int seasonNumber) + { + return new List(); + } + + public override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) + { + return new List(); + } + + public override IEnumerable GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) + { + return new List(); + } + + + public bool EnabledByDefault + { + get { return true; } + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/Wombles/WomblesParser.cs b/NzbDrone.Core/Indexers/Wombles/WomblesParser.cs new file mode 100644 index 000000000..a6912ba9c --- /dev/null +++ b/NzbDrone.Core/Indexers/Wombles/WomblesParser.cs @@ -0,0 +1,28 @@ +using System.ServiceModel.Syndication; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.Indexers.Wombles +{ + public class WomblesParser : BasicRssParser + { + protected override string GetNzbUrl(SyndicationItem item) + { + return item.Links[0].Uri.ToString(); + } + + protected override string GetNzbInfoUrl(SyndicationItem item) + { + return null; + } + + protected override EpisodeParseResult PostProcessor(SyndicationItem item, EpisodeParseResult currentResult) + { + if (currentResult != null) + { + currentResult.Size = 0; + } + + return currentResult; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/Implementations/BacklogSearchJob.cs b/NzbDrone.Core/Jobs/Implementations/BacklogSearchJob.cs deleted file mode 100644 index 575c9b254..000000000 --- a/NzbDrone.Core/Jobs/Implementations/BacklogSearchJob.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using NLog; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Model; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Jobs.Implementations -{ - public class BacklogSearchJob : IJob - { - private readonly IEpisodeService _episodeService; - private readonly EpisodeSearchJob _episodeSearchJob; - private readonly SeasonSearchJob _seasonSearchJob; - private readonly IConfigService _configService; - - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - public BacklogSearchJob(IEpisodeService episodeService, EpisodeSearchJob episodeSearchJob, - SeasonSearchJob seasonSearchJob, IConfigService configService) - { - _episodeService = episodeService; - _episodeSearchJob = episodeSearchJob; - _seasonSearchJob = seasonSearchJob; - _configService = configService; - } - - public string Name - { - get { return "Backlog Search"; } - } - - public TimeSpan DefaultInterval - { - get { return TimeSpan.FromDays(30); } - } - - public void Start(ProgressNotification notification, dynamic options) - { - var missingEpisodes = GetMissingForEnabledSeries().GroupBy(e => new { e.SeriesId, e.SeasonNumber }); - - var individualEpisodes = new List(); - - Logger.Trace("Processing missing episodes list"); - foreach (var group in missingEpisodes) - { - var count = group.Count(); - - if (count == 1) - individualEpisodes.Add(group.First()); - - else - { - //Get count and compare to the actual number of episodes for this season - //If numbers don't match then add to individual episodes, else process as full season... - var seriesId = group.Key.SeriesId; - var seasonNumber = group.Key.SeasonNumber; - - var countInDb = _episodeService.GetEpisodeNumbersBySeason(seriesId, seasonNumber).Count; - - //Todo: Download a full season if more than n% is missing? - - if (count != countInDb) - { - //Add the episodes to be processed manually - individualEpisodes.AddRange(group); - } - - else - { - //Process as a full season - Logger.Debug("Processing Full Season: {0} Season {1}", seriesId, seasonNumber); - _seasonSearchJob.Start(notification, new { SeriesId = seriesId, SeasonNumber = seasonNumber }); - } - } - } - - Logger.Debug("Processing standalone episodes"); - //Process the list of remaining episodes, 1 by 1 - foreach (var episode in individualEpisodes) - { - _episodeSearchJob.Start(notification, new { EpisodeId = episode.Id}); - } - } - - public List GetMissingForEnabledSeries() - { - if (!_configService.EnableBacklogSearching) - { - Logger.Trace("Backlog searching is not enabled, only running for explicitly enabled series."); - return _episodeService.EpisodesWithoutFiles(true).Where(e => - e.Series.BacklogSetting == BacklogSettingType.Enable && - e.Series.Monitored - ).ToList(); - } - - else - { - Logger.Trace("Backlog searching is enabled, skipping explicity disabled series."); - return _episodeService.EpisodesWithoutFiles(true).Where(e => - e.Series.BacklogSetting != BacklogSettingType.Disable && - e.Series.Monitored - ).ToList(); - } - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/Implementations/EpisodeSearchJob.cs b/NzbDrone.Core/Jobs/Implementations/EpisodeSearchJob.cs deleted file mode 100644 index 38a85ef3e..000000000 --- a/NzbDrone.Core/Jobs/Implementations/EpisodeSearchJob.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using NLog; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.IndexerSearch; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Jobs.Implementations -{ - public class EpisodeSearchJob : IJob - { - private readonly IEpisodeService _episodeService; - private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification; - private readonly EpisodeSearch _episodeSearch; - private readonly DailyEpisodeSearch _dailyEpisodeSearch; - - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - public EpisodeSearchJob(IEpisodeService episodeService, IQualityUpgradableSpecification qualityUpgradableSpecification, - EpisodeSearch episodeSearch, DailyEpisodeSearch dailyEpisodeSearch) - { - if (dailyEpisodeSearch == null) throw new ArgumentNullException("dailyEpisodeSearch"); - _episodeService = episodeService; - _qualityUpgradableSpecification = qualityUpgradableSpecification; - _episodeSearch = episodeSearch; - _dailyEpisodeSearch = dailyEpisodeSearch; - } - - public EpisodeSearchJob() - { - - } - - public string Name - { - get { return "Episode Search"; } - } - - public TimeSpan DefaultInterval - { - get { return TimeSpan.FromTicks(0); } - } - - public virtual void Start(ProgressNotification notification, dynamic options) - { - if (options == null || options.EpisodeId <= 0) - throw new ArgumentException("options"); - - Episode episode = _episodeService.GetEpisode((int)options.EpisodeId); - - if (episode == null) - { - logger.Error("Unable to find an episode {0} in database", options.EpisodeId); - return; - } - - if (!_qualityUpgradableSpecification.IsUpgradable(episode.Series.QualityProfile, episode.EpisodeFile.Quality)) - { - logger.Info("Search for {0} was aborted, file in disk meets or exceeds Profile's Cutoff", episode); - notification.CurrentMessage = String.Format("Skipping search for {0}, the file you have is already at cutoff", episode); - return; - } - - if (episode.Series.SeriesType == SeriesTypes.Daily) - { - if (!episode.AirDate.HasValue) - { - logger.Warn("AirDate is not Valid for: {0}", episode); - notification.CurrentMessage = String.Format("Search for {0} Failed, AirDate is invalid", episode); - return; - } - - _dailyEpisodeSearch.Search(episode.Series, new { Episode = episode }, notification); - } - - else - { - _episodeSearch.Search(episode.Series, new { Episode = episode }, notification); - } - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/Implementations/PastWeekBacklogSearchJob.cs b/NzbDrone.Core/Jobs/Implementations/PastWeekBacklogSearchJob.cs deleted file mode 100644 index 39bd09600..000000000 --- a/NzbDrone.Core/Jobs/Implementations/PastWeekBacklogSearchJob.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using NLog; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Jobs.Implementations -{ - public class PastWeekBacklogSearchJob : IJob - { - private readonly IEpisodeService _episodeService; - private readonly EpisodeSearchJob _episodeSearchJob; - - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - public PastWeekBacklogSearchJob(IEpisodeService episodeService, EpisodeSearchJob episodeSearchJob) - { - _episodeService = episodeService; - _episodeSearchJob = episodeSearchJob; - } - - public string Name - { - get { return "Past Week Backlog Search"; } - } - - public TimeSpan DefaultInterval - { - get { return TimeSpan.FromTicks(0); } - } - - public void Start(ProgressNotification notification, dynamic options) - { - var missingEpisodes = GetMissingForEnabledSeries(); - - Logger.Debug("Processing missing episodes from the past week, count: {0}", missingEpisodes.Count); - foreach (var episode in missingEpisodes) - { - _episodeSearchJob.Start(notification, new { EpisodeId = episode.Id }); - } - } - - public List GetMissingForEnabledSeries() - { - return _episodeService.EpisodesWithoutFiles(true).Where(e => - e.AirDate >= DateTime.Today.AddDays(-7) && - e.Series.Monitored - ).ToList(); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/Implementations/RecentBacklogSearchJob.cs b/NzbDrone.Core/Jobs/Implementations/RecentBacklogSearchJob.cs deleted file mode 100644 index 1b7a1584f..000000000 --- a/NzbDrone.Core/Jobs/Implementations/RecentBacklogSearchJob.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using NLog; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Model; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Jobs.Implementations -{ - public class RecentBacklogSearchJob : IJob - { - private readonly IEpisodeService _episodeService; - private readonly EpisodeSearchJob _episodeSearchJob; - private readonly IConfigService _configService; - - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - public RecentBacklogSearchJob(IEpisodeService episodeService, EpisodeSearchJob episodeSearchJob, - IConfigService configService) - { - _episodeService = episodeService; - _episodeSearchJob = episodeSearchJob; - _configService = configService; - } - - public string Name - { - get { return "Recent Backlog Search"; } - } - - public TimeSpan DefaultInterval - { - get { return TimeSpan.FromDays(1); } - } - - public void Start(ProgressNotification notification, dynamic options) - { - var missingEpisodes = GetMissingForEnabledSeries(); - - Logger.Debug("Processing missing episodes from the last 30 days, count: {0}", missingEpisodes.Count); - foreach (var episode in missingEpisodes) - { - _episodeSearchJob.Start(notification, new { EpisodeId = episode.Id }); - } - } - - public List GetMissingForEnabledSeries() - { - if (!_configService.EnableBacklogSearching) - { - Logger.Trace("Backlog searching is not enabled, only running for explicitly enabled series."); - return _episodeService.EpisodesWithoutFiles(true).Where(e => - e.AirDate >= DateTime.Today.AddDays(-30) && - e.Series.BacklogSetting == BacklogSettingType.Enable && - e.Series.Monitored - ).ToList(); - } - - else - { - Logger.Trace("Backlog searching is enabled, skipping explicitly disabled series."); - return _episodeService.EpisodesWithoutFiles(true).Where(e => - e.AirDate >= DateTime.Today.AddDays(-30) && - e.Series.BacklogSetting != BacklogSettingType.Disable && - e.Series.Monitored - ).ToList(); - } - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/Implementations/RssSyncJob.cs b/NzbDrone.Core/Jobs/Implementations/RssSyncJob.cs index 4d6d99fa9..2b11a82a8 100644 --- a/NzbDrone.Core/Jobs/Implementations/RssSyncJob.cs +++ b/NzbDrone.Core/Jobs/Implementations/RssSyncJob.cs @@ -1,31 +1,19 @@ using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using NLog; using NzbDrone.Core.Configuration; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Download; using NzbDrone.Core.Indexers; -using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; namespace NzbDrone.Core.Jobs.Implementations { public class RssSyncJob : IJob { - private readonly DownloadService _downloadService; - private readonly IIndexerService _indexerService; - private readonly IDownloadDirector DownloadDirector; + private readonly ISyncRss _syncRssService; private readonly IConfigService _configService; - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - public RssSyncJob(DownloadService downloadService, IIndexerService indexerService, IDownloadDirector downloadDirector, IConfigService configService) + public RssSyncJob(ISyncRss syncRssService, IConfigService configService) { - _downloadService = downloadService; - _indexerService = indexerService; - DownloadDirector = downloadDirector; + _syncRssService = syncRssService; _configService = configService; } @@ -41,49 +29,7 @@ namespace NzbDrone.Core.Jobs.Implementations public void Start(ProgressNotification notification, dynamic options) { - var reports = new List(); - - notification.CurrentMessage = "Fetching RSS"; - - Parallel.ForEach(_indexerService.GetEnabledIndexers(), indexer => - { - try - { - var parseResults = indexer.FetchRss(); - lock (reports) - { - reports.AddRange(parseResults); - } - } - catch (Exception e) - { - Logger.ErrorException("An error has occurred while fetching items from " + indexer.Name, e); - } - }); - - Logger.Debug("Finished fetching reports from all indexers. Total {0}", reports.Count); - - notification.CurrentMessage = "Processing downloaded RSS"; - - foreach (var episodeParseResult in reports) - { - try - { - if (DownloadDirector.GetDownloadDecision(episodeParseResult).Approved) - { - _downloadService.DownloadReport(episodeParseResult); - } - } - catch (Exception e) - { - Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e); - } - } - - notification.CurrentMessage = "RSS Sync Completed"; - - Logger.Info("RSS Sync completed"); - + _syncRssService.Sync(); } } } \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/Implementations/SeasonSearchJob.cs b/NzbDrone.Core/Jobs/Implementations/SeasonSearchJob.cs deleted file mode 100644 index 575a7af6f..000000000 --- a/NzbDrone.Core/Jobs/Implementations/SeasonSearchJob.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using NLog; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Providers; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Jobs.Implementations -{ - public class SeasonSearchJob : IJob - { - private readonly SearchProvider _searchProvider; - private readonly EpisodeSearchJob _episodeSearchJob; - private readonly IEpisodeService _episodeService; - - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - public SeasonSearchJob(SearchProvider searchProvider, EpisodeSearchJob episodeSearchJob, - IEpisodeService episodeService) - { - _searchProvider = searchProvider; - _episodeSearchJob = episodeSearchJob; - _episodeService = episodeService; - } - - public SeasonSearchJob() - { - - } - - public string Name - { - get { return "Season Search"; } - } - - public TimeSpan DefaultInterval - { - get { return TimeSpan.FromTicks(0); } - } - - public virtual void Start(ProgressNotification notification, dynamic options) - { - if (options == null || options.SeriesId <= 0) - throw new ArgumentException("options"); - - if (options.SeasonNumber < 0) - throw new ArgumentException("options.SeasonNumber"); - - //Perform a Partial Season Search - Because a full season search is a waste - //3 searches should guarentee results, (24 eps) versus, a potential 4 to get the same eps. - List successes = _searchProvider.PartialSeasonSearch(notification, options.SeriesId, options.SeasonNumber); - - //This causes issues with Newznab - //if (successes.Count == 0) - // return; - - Logger.Debug("Getting episodes from database for series: {0} and season: {1}", options.SeriesId, options.SeasonNumber); - IList episodes = _episodeService.GetEpisodesBySeason((int)options.SeriesId, (int)options.SeasonNumber); - - if (episodes == null || episodes.Count == 0) - { - Logger.Warn("No episodes in database found for series: {0} and season: {1}.", options.SeriesId, options.SeasonNumber); - return; - } - - if (episodes.Count == successes.Count) - return; - - var missingEpisodes = episodes.Select(e => e.Id).Except(successes).ToList(); - - foreach (var episode in episodes.Where(e => !e.Ignored && missingEpisodes.Contains(e.Id)).OrderBy(o => o.EpisodeNumber)) - { - _episodeSearchJob.Start(notification, new { EpisodeId = episode.Id }); - } - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/Implementations/SeriesSearchJob.cs b/NzbDrone.Core/Jobs/Implementations/SeriesSearchJob.cs deleted file mode 100644 index 95be1bbc6..000000000 --- a/NzbDrone.Core/Jobs/Implementations/SeriesSearchJob.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using NLog; -using NzbDrone.Core.Model.Notification; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Jobs.Implementations -{ - public class SeriesSearchJob : IJob - { - private readonly SeasonSearchJob _seasonSearchJob; - private readonly ISeasonRepository _seasonRepository; - - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - public SeriesSearchJob(SeasonSearchJob seasonSearchJob, ISeasonRepository seasonRepository) - { - _seasonSearchJob = seasonSearchJob; - _seasonRepository = seasonRepository; - } - - public string Name - { - get { return "Series Search"; } - } - - public TimeSpan DefaultInterval - { - get { return TimeSpan.FromTicks(0); } - } - - public void Start(ProgressNotification notification, dynamic options) - { - if (options == null || options.SeriesId <= 0) - throw new ArgumentException("options.SeriesId"); - - logger.Debug("Getting seasons from database for series: {0}", options.SeriesId); - IList seasons = _seasonRepository.GetSeasonNumbers((int)options.SeriesId); - - foreach (var season in seasons.Where(s => s > 0)) - { - if (!_seasonRepository.IsIgnored((int)options.SeriesId, season)) - { - _seasonSearchJob.Start(notification, new { SeriesId = options.SeriesId, SeasonNumber = season }); - } - } - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index da63de0f1..a867b202c 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -213,9 +213,13 @@ - + + + + + - + @@ -223,6 +227,7 @@ + @@ -238,29 +243,53 @@ - - - - + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + - - - - - - @@ -353,12 +382,6 @@ - - - - - - @@ -433,13 +456,7 @@ Code - - Code - - - Code - - + Code @@ -451,9 +468,6 @@ Code - - Code - Code @@ -474,9 +488,6 @@ Code - - Code - Code @@ -498,9 +509,7 @@ Code - - diff --git a/NzbDrone.Core/Providers/SearchProvider.cs b/NzbDrone.Core/Providers/SearchProvider.cs deleted file mode 100644 index a232b96dc..000000000 --- a/NzbDrone.Core/Providers/SearchProvider.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using NLog; -using NzbDrone.Core.IndexerSearch; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Model.Notification; - -namespace NzbDrone.Core.Providers -{ - public class SearchProvider - { - private readonly IEpisodeService _episodeService; - private readonly PartialSeasonSearch _partialSeasonSearch; - private readonly ISeriesRepository _seriesRepository; - - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - public SearchProvider(IEpisodeService episodeService, PartialSeasonSearch partialSeasonSearch, ISeriesRepository seriesRepository) - { - _episodeService = episodeService; - _partialSeasonSearch = partialSeasonSearch; - _seriesRepository = seriesRepository; - } - - public SearchProvider() - { - } - - public virtual List SeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber) - { - var series = _seriesRepository.Get(seriesId); - - if (series == null) - { - logger.Error("Unable to find an series {0} in database", seriesId); - return new List(); - } - - if (series.SeriesType == SeriesTypes.Daily) - { - logger.Trace("Daily series detected, skipping season search: {0}", series.Title); - return new List(); - } - - logger.Debug("Getting episodes from database for series: {0} and season: {1}", seriesId, seasonNumber); - var episodes = _episodeService.GetEpisodesBySeason(seriesId, seasonNumber); - - if (episodes == null || episodes.Count == 0) - { - logger.Warn("No episodes in database found for series: {0} and season: {1}.", seriesId, seasonNumber); - return new List(); - } - - //Todo: Support full season searching - return new List(); - } - - public virtual List PartialSeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber) - { - var series = _seriesRepository.Get(seriesId); - - if (series == null) - { - logger.Error("Unable to find an series {0} in database", seriesId); - return new List(); - } - - if (series.SeriesType == SeriesTypes.Daily) - { - logger.Trace("Daily series detected, skipping season search: {0}", series.Title); - return new List(); - } - - var episodes = _episodeService.GetEpisodesBySeason(seriesId, seasonNumber); - - if (episodes == null || episodes.Count == 0) - { - logger.Warn("No episodes in database found for series: {0} Season: {1}.", seriesId, seasonNumber); - return new List(); - } - - return _partialSeasonSearch.Search(series, new { SeasonNumber = seasonNumber, Episodes = episodes }, notification); - } - } -} diff --git a/NzbDrone.Core/Tv/SeriesService.cs b/NzbDrone.Core/Tv/SeriesService.cs index debff39c5..cefe50207 100644 --- a/NzbDrone.Core/Tv/SeriesService.cs +++ b/NzbDrone.Core/Tv/SeriesService.cs @@ -20,6 +20,7 @@ namespace NzbDrone.Core.Tv bool IsMonitored(int id); Series UpdateSeriesInfo(int seriesId); Series FindSeries(string title); + Series GetSeries(int seriesId); void AddSeries(Series newSeries); void UpdateFromSeriesEditor(IList editedSeries); Series FindByTvdbId(int tvdbId); @@ -95,6 +96,11 @@ namespace NzbDrone.Core.Tv return _seriesRepository.GetByTitle(normalizeTitle); } + public Series GetSeries(int seriesId) + { + return _seriesRepository.Get(seriesId); + } + public void AddSeries(Series newSeries) { Ensure.That(() => newSeries).IsNotNull(); diff --git a/NzbDrone.ncrunchsolution b/NzbDrone.ncrunchsolution index 969da6dd4..444b34b1d 100644 --- a/NzbDrone.ncrunchsolution +++ b/NzbDrone.ncrunchsolution @@ -1,6 +1,6 @@ 1 - True + False true true UseDynamicAnalysis diff --git a/NzbDrone.sln.DotSettings b/NzbDrone.sln.DotSettings index b161673b3..a85dee74c 100644 --- a/NzbDrone.sln.DotSettings +++ b/NzbDrone.sln.DotSettings @@ -5,6 +5,7 @@ DO_NOT_SHOW ERROR ERROR + HINT HINT <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb" /></Policy> diff --git a/NzbDrone/CommonContainerExtentions.cs b/NzbDrone/CommonContainerExtentions.cs index 48a1a3e2b..ae46a3a78 100644 --- a/NzbDrone/CommonContainerExtentions.cs +++ b/NzbDrone/CommonContainerExtentions.cs @@ -34,8 +34,7 @@ namespace NzbDrone container.AutoRegisterInterfaces("NzbDrone.Core"); container.AutoRegisterInterfaces("NzbDrone.Api"); - container.AutoRegisterImplementations(); - container.AutoRegisterImplementations(); + container.AutoRegisterImplementations(); container.AutoRegisterImplementations(); container.AutoRegisterMultipleImplementations(); diff --git a/tests_mono.ps1 b/tests_mono.ps1 new file mode 100644 index 000000000..894c6faf8 --- /dev/null +++ b/tests_mono.ps1 @@ -0,0 +1,32 @@ +$msBuild = 'C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe' +$outputFolder = '.\_output' + +Function Build() +{ + + + $clean = $msbuild + " nzbdrone.sln /t:Clean /m" + $build = $msbuild + " nzbdrone.sln /p:Configuration=Release /p:Platform=x86 /t:Build" + + if(Test-Path $outputFolder) + { + Remove-Item -Recurse -Force $outputFolder -ErrorAction Continue + } + + + Invoke-Expression $clean + Invoke-Expression $build +} + +Function Package() +{ + Write-Host Removing XMLDoc files + get-childitem $outputFolder -include *.xml -recurse | foreach ($_) {remove-item $_.fullname} + + Write-Host Removing FluentValidation.resources + get-childitem $outputFolder -include FluentValidation.resources.dll -recurse | foreach ($_) {remove-item $_.fullname} +} + + +Build +Package \ No newline at end of file