You've already forked Sonarr
							
							
				mirror of
				https://github.com/Sonarr/Sonarr.git
				synced 2025-10-31 00:07:55 +02:00 
			
		
		
		
	rewrite of indexer/episode search
This commit is contained in:
		| @@ -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; | ||||
|   | ||||
| @@ -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<BasicRepository<SampleType>,SampleType> | ||||
|     { | ||||
|         private SampleType sampleType; | ||||
|  | ||||
|  | ||||
|         [SetUp] | ||||
|         public void Setup() | ||||
|         { | ||||
|             sampleType = Builder<SampleType> | ||||
|                 .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<InvalidOperationException>(() => Subject.Get(12)); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         [Test] | ||||
|         public void get_all_with_empty_db_should_return_empty_list() | ||||
|         { | ||||
|             Subject.All().Should().BeEmpty(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<DownloadDirector> | ||||
|     public class AllowedDownloadSpecificationFixture : CoreTest<DownloadDecisionMaker> | ||||
|     { | ||||
|         private EpisodeParseResult _parseResult; | ||||
|         private List<EpisodeParseResult> _parseResults; | ||||
|  | ||||
|         private Mock<IFetchableSpecification> _pass1; | ||||
|         private Mock<IFetchableSpecification> _pass2; | ||||
|         private Mock<IFetchableSpecification> _pass3; | ||||
|         private Mock<IDecisionEngineSpecification> _pass1; | ||||
|         private Mock<IDecisionEngineSpecification> _pass2; | ||||
|         private Mock<IDecisionEngineSpecification> _pass3; | ||||
|  | ||||
|         private Mock<IFetchableSpecification> _fail1; | ||||
|         private Mock<IFetchableSpecification> _fail2; | ||||
|         private Mock<IFetchableSpecification> _fail3; | ||||
|         private Mock<IDecisionEngineSpecification> _fail1; | ||||
|         private Mock<IDecisionEngineSpecification> _fail2; | ||||
|         private Mock<IDecisionEngineSpecification> _fail3; | ||||
|  | ||||
|         [SetUp] | ||||
|         public void Setup() | ||||
|         { | ||||
|             _pass1 = new Mock<IFetchableSpecification>(); | ||||
|             _pass2 = new Mock<IFetchableSpecification>(); | ||||
|             _pass3 = new Mock<IFetchableSpecification>(); | ||||
|             _pass1 = new Mock<IDecisionEngineSpecification>(); | ||||
|             _pass2 = new Mock<IDecisionEngineSpecification>(); | ||||
|             _pass3 = new Mock<IDecisionEngineSpecification>(); | ||||
|  | ||||
|             _fail1 = new Mock<IFetchableSpecification>(); | ||||
|             _fail2 = new Mock<IFetchableSpecification>(); | ||||
|             _fail3 = new Mock<IFetchableSpecification>(); | ||||
|             _fail1 = new Mock<IDecisionEngineSpecification>(); | ||||
|             _fail2 = new Mock<IDecisionEngineSpecification>(); | ||||
|             _fail3 = new Mock<IDecisionEngineSpecification>(); | ||||
|  | ||||
|             _pass1.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(true); | ||||
|             _pass2.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(true); | ||||
| @@ -40,11 +41,11 @@ namespace NzbDrone.Core.Test.DecisionEngineTests | ||||
|             _fail2.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(false); | ||||
|             _fail3.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(false); | ||||
|  | ||||
|             _parseResult = new EpisodeParseResult(); | ||||
|             _parseResults = new List<EpisodeParseResult>() { new EpisodeParseResult() }; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         private void GivenSpecifications(params Mock<IFetchableSpecification>[] mocks) | ||||
|         private void GivenSpecifications(params Mock<IDecisionEngineSpecification>[] 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()); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|   | ||||
| @@ -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<DailyEpisodeSearch> | ||||
|     { | ||||
|         private Series _series; | ||||
|         private Episode _episode; | ||||
|         private EpisodeParseResult _episodeParseResult; | ||||
|  | ||||
|         [SetUp] | ||||
|         public void Setup() | ||||
|         { | ||||
|             _series = Builder<Series> | ||||
|                     .CreateNew() | ||||
|                     .Build(); | ||||
|  | ||||
|             _episode = Builder<Episode> | ||||
|                     .CreateNew() | ||||
|                     .With(e => e.SeriesId = _series.Id) | ||||
|                     .With(e => e.Series = _series) | ||||
|                     .Build(); | ||||
|  | ||||
|             _episodeParseResult = Builder<EpisodeParseResult> | ||||
|                     .CreateNew() | ||||
|                     .With(p => p.AirDate = _episode.AirDate) | ||||
|                     .With(p => p.Episodes = new List<Episode> { _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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<DailyEpisodeSearch> | ||||
|     { | ||||
|         [Test] | ||||
|         public void should_fetch_results_from_indexers() | ||||
|         { | ||||
|             WithValidIndexers(); | ||||
|  | ||||
|  | ||||
|             Subject.PerformSearch(_series, new List<Episode> { _episode }, notification) | ||||
|             .Should() | ||||
|             .HaveCount(20); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_log_error_when_fetching_from_indexer_fails() | ||||
|         { | ||||
|             WithBrokenIndexers(); | ||||
|  | ||||
|             Mocker.Resolve<DailyEpisodeSearch>() | ||||
|                   .PerformSearch(_series, new List<Episode> { _episode }, notification) | ||||
|                   .Should() | ||||
|                   .HaveCount(0); | ||||
|  | ||||
|             ExceptionVerification.ExpectedErrors(2); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<EpisodeSearch> | ||||
|     { | ||||
|  | ||||
|         [Test] | ||||
|         public void should_fetch_results_from_indexers() | ||||
|         { | ||||
|             WithValidIndexers(); | ||||
|  | ||||
|             Subject | ||||
|                    .PerformSearch(_series, new List<Episode> { _episode }, notification) | ||||
|                    .Should() | ||||
|                    .HaveCount(20); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_log_error_when_fetching_from_indexer_fails() | ||||
|         { | ||||
|             WithBrokenIndexers(); | ||||
|  | ||||
|             Subject | ||||
|                    .PerformSearch(_series, new List<Episode> { _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> { _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> { _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> { _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()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<Series> | ||||
|                     .CreateNew() | ||||
|                     .Build(); | ||||
|  | ||||
|             _episode = Builder<Episode> | ||||
|                     .CreateNew() | ||||
|                     .With(e => e.SeriesId = _series.Id) | ||||
|                     .With(e => e.Series = _series) | ||||
|                     .Build(); | ||||
|  | ||||
|             _episodeParseResult = Builder<EpisodeParseResult> | ||||
|                     .CreateNew() | ||||
|                     .With(p => p.SeasonNumber = 1) | ||||
|                     .With(p => p.EpisodeNumbers = new List<int>{ _episode.EpisodeNumber }) | ||||
|                     .With(p => p.Episodes = new List<Episode> { _episode }) | ||||
|                     .With(p => p.Series = _series) | ||||
|                     .Build(); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_return_WrongSeason_when_season_doesnt_match() | ||||
|         { | ||||
|             _episode.SeasonNumber = 10; | ||||
|  | ||||
|             Mocker.Resolve<EpisodeSearch>() | ||||
|                   .IsEpisodeMatch(_series, new {Episode = _episode}, _episodeParseResult) | ||||
|                   .Should() | ||||
|                   .BeFalse(); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_return_WrongEpisode_when_episode_doesnt_match() | ||||
|         { | ||||
|             _episode.EpisodeNumber = 10; | ||||
|  | ||||
|             Mocker.Resolve<EpisodeSearch>() | ||||
|                   .IsEpisodeMatch(_series, new { Episode = _episode }, _episodeParseResult) | ||||
|                   .Should() | ||||
|                   .BeFalse(); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_not_return_error_when_season_and_episode_match() | ||||
|         { | ||||
|             Mocker.Resolve<EpisodeSearch>() | ||||
|                   .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<EpisodeSearch>() | ||||
|                   .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<EpisodeSearch>() | ||||
|                   .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<EpisodeSearch>() | ||||
|                   .IsEpisodeMatch(_series, new { Episode = _episode }, _episodeParseResult) | ||||
|                   .Should() | ||||
|                   .BeTrue(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<TestSearch> | ||||
|     { | ||||
|         private Series _series; | ||||
|  | ||||
|         [SetUp] | ||||
|         public void Setup() | ||||
|         { | ||||
|             _series = Builder<Series> | ||||
|                     .CreateNew() | ||||
|                     .With(s => s.Title = "Hawaii Five-0") | ||||
|                     .Build(); | ||||
|         } | ||||
|  | ||||
|         private void WithSceneMapping() | ||||
|         { | ||||
|             Mocker.GetMock<ISceneMappingService>() | ||||
|                   .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<ISceneMappingService>() | ||||
|                   .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<ISceneMappingService>() | ||||
|                   .Setup(s => s.GetSceneName(_series.Id, -1)) | ||||
|                   .Returns(""); | ||||
|  | ||||
|             Subject.GetSearchTitle(_series, 5).Should().Be(expected); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<TSearch> : CoreTest<TSearch> | ||||
|         where TSearch : IndexerSearchBase | ||||
|     { | ||||
|         protected Series _series; | ||||
|         protected Episode _episode; | ||||
|         protected ProgressNotification notification = new ProgressNotification("Testing"); | ||||
|  | ||||
|         protected Mock<IndexerBase> _indexer1; | ||||
|         protected Mock<IndexerBase> _indexer2; | ||||
|         protected List<IndexerBase> _indexers; | ||||
|         protected IList<EpisodeParseResult> _parseResults; | ||||
|  | ||||
|         [SetUp] | ||||
|         public void Setup() | ||||
|         { | ||||
|             _series = Builder<Series> | ||||
|                     .CreateNew() | ||||
|                     .Build(); | ||||
|  | ||||
|             _episode = Builder<Episode> | ||||
|                     .CreateNew() | ||||
|                     .With(e => e.SeriesId = _series.Id) | ||||
|                     .With(e => e.Series = _series) | ||||
|                     .Build(); | ||||
|  | ||||
|  | ||||
|             _parseResults = Builder<EpisodeParseResult> | ||||
|                     .CreateListOfSize(10) | ||||
|                     .Build(); | ||||
|  | ||||
|             _indexer1 = new Mock<IndexerBase>(); | ||||
|             _indexer2 = new Mock<IndexerBase>(); | ||||
|             _indexers = new List<IndexerBase> { _indexer1.Object, _indexer2.Object }; | ||||
|  | ||||
|             Mocker.GetMock<IIndexerService>() | ||||
|                   .Setup(c => c.GetEnabledIndexers()) | ||||
|                   .Returns(_indexers); | ||||
|         } | ||||
|  | ||||
|         protected void WithValidIndexers() | ||||
|         { | ||||
|             _indexer1.Setup(c => c.FetchEpisode(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>())) | ||||
|                 .Returns(_parseResults); | ||||
|             _indexer1.Setup(c => c.FetchDailyEpisode(It.IsAny<string>(), It.IsAny<DateTime>())) | ||||
|                 .Returns(_parseResults); | ||||
|             _indexer1.Setup(c => c.FetchPartialSeason(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>())) | ||||
|                 .Returns(_parseResults); | ||||
|  | ||||
|             _indexer2.Setup(c => c.FetchEpisode(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>())) | ||||
|                 .Returns(_parseResults); | ||||
|             _indexer2.Setup(c => c.FetchDailyEpisode(It.IsAny<string>(), It.IsAny<DateTime>())) | ||||
|                 .Returns(_parseResults); | ||||
|             _indexer2.Setup(c => c.FetchPartialSeason(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>())) | ||||
|                 .Returns(_parseResults); | ||||
|         } | ||||
|  | ||||
|         protected void WithBrokenIndexers() | ||||
|         { | ||||
|             _indexer1.Setup(c => c.FetchEpisode(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>())) | ||||
|                 .Throws(new Exception()); | ||||
|             _indexer1.Setup(c => c.FetchDailyEpisode(It.IsAny<string>(), It.IsAny<DateTime>())) | ||||
|                 .Throws(new Exception()); | ||||
|             _indexer1.Setup(c => c.FetchPartialSeason(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>())) | ||||
|                 .Throws(new Exception()); | ||||
|  | ||||
|             _indexer2.Setup(c => c.FetchEpisode(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>())) | ||||
|                 .Throws(new Exception()); | ||||
|             _indexer2.Setup(c => c.FetchDailyEpisode(It.IsAny<string>(), It.IsAny<DateTime>())) | ||||
|                 .Throws(new Exception()); | ||||
|             _indexer2.Setup(c => c.FetchPartialSeason(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>())) | ||||
|                 .Throws(new Exception()); | ||||
|  | ||||
|             _indexer1.SetupGet(c => c.Name).Returns("Indexer1"); | ||||
|             _indexer1.SetupGet(c => c.Name).Returns("Indexer2"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<PartialSeasonSearch> | ||||
|     { | ||||
|  | ||||
|         [Test] | ||||
|         public void should_fetch_results_from_indexers() | ||||
|         { | ||||
|             WithValidIndexers(); | ||||
|  | ||||
|             Subject.PerformSearch(_series, new List<Episode> { _episode }, notification) | ||||
|                    .Should() | ||||
|                    .HaveCount(20); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_log_error_when_fetching_from_indexer_fails() | ||||
|         { | ||||
|             WithBrokenIndexers(); | ||||
|  | ||||
|             Subject.PerformSearch(_series, new List<Episode> { _episode }, notification) | ||||
|                   .Should() | ||||
|                   .HaveCount(0); | ||||
|  | ||||
|             ExceptionVerification.ExpectedErrors(2); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_hit_each_indexer_once_for_each_prefix() | ||||
|         { | ||||
|             WithValidIndexers(); | ||||
|  | ||||
|  | ||||
|             var episodes = Builder<Episode>.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<string>(), It.IsAny<int>(), It.IsAny<int>()), Times.Exactly(2)); | ||||
|             _indexer2.Verify(v => v.FetchPartialSeason(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()), Times.Exactly(2)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<PartialSeasonSearch> | ||||
|     { | ||||
|         private Series _series; | ||||
|         private List<Episode> _episodes; | ||||
|         private EpisodeParseResult _episodeParseResult; | ||||
|  | ||||
|         [SetUp] | ||||
|         public void Setup() | ||||
|         { | ||||
|             _series = Builder<Series> | ||||
|                     .CreateNew() | ||||
|                     .Build(); | ||||
|  | ||||
|             _episodes = Builder<Episode> | ||||
|                     .CreateListOfSize(10) | ||||
|                     .All() | ||||
|                     .With(e => e.SeriesId = _series.Id) | ||||
|                     .With(e => e.Series = _series) | ||||
|                     .Build() | ||||
|                     .ToList(); | ||||
|  | ||||
|             _episodeParseResult = Builder<EpisodeParseResult> | ||||
|                     .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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<TestSearch> | ||||
|     { | ||||
|         private Series _matchingSeries; | ||||
|         private Series _mismatchedSeries; | ||||
|         private Series _nullSeries = null; | ||||
|  | ||||
|         private EpisodeSearchResult _episodeSearchResult; | ||||
|         private ProgressNotification _notification; | ||||
|  | ||||
|         private List<Episode> _episodes; | ||||
|  | ||||
|         [SetUp] | ||||
|         public void Setup() | ||||
|         { | ||||
|             _matchingSeries = Builder<Series>.CreateNew() | ||||
|                 .With(s => s.Id = 79488) | ||||
|                 .With(s => s.Title = "30 Rock") | ||||
|                 .Build(); | ||||
|  | ||||
|             _mismatchedSeries = Builder<Series>.CreateNew() | ||||
|                 .With(s => s.Id = 12345) | ||||
|                 .With(s => s.Title = "Not 30 Rock") | ||||
|                 .Build(); | ||||
|  | ||||
|             _episodeSearchResult = new EpisodeSearchResult(); | ||||
|             _notification = new ProgressNotification("Test"); | ||||
|  | ||||
|             _episodes = Builder<Episode> | ||||
|                     .CreateListOfSize(1) | ||||
|                     .Build().ToList(); | ||||
|  | ||||
|             Mocker.GetMock<IEpisodeService>() | ||||
|                   .Setup(s => s.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>())) | ||||
|                   .Returns(_episodes); | ||||
|         } | ||||
|  | ||||
|         private void WithMatchingSeries() | ||||
|         { | ||||
|             Mocker.GetMock<ISeriesRepository>() | ||||
|                 .Setup(s => s.GetByTitle(It.IsAny<string>())).Returns(_matchingSeries); | ||||
|         } | ||||
|  | ||||
|         private void WithMisMatchedSeries() | ||||
|         { | ||||
|             Mocker.GetMock<ISeriesRepository>() | ||||
|                 .Setup(s => s.GetByTitle(It.IsAny<string>())).Returns(_mismatchedSeries); | ||||
|         } | ||||
|  | ||||
|         private void WithNullSeries() | ||||
|         { | ||||
|             Mocker.GetMock<ISeriesRepository>() | ||||
|                 .Setup(s => s.GetByTitle(It.IsAny<string>())).Returns(_nullSeries); | ||||
|         } | ||||
|  | ||||
|         private void WithSuccessfulDownload() | ||||
|         { | ||||
|             Mocker.GetMock<DownloadProvider>() | ||||
|                 .Setup(s => s.DownloadReport(It.IsAny<EpisodeParseResult>())) | ||||
|                 .Returns(true); | ||||
|         } | ||||
|  | ||||
|         private void WithFailingDownload() | ||||
|         { | ||||
|             Mocker.GetMock<DownloadProvider>() | ||||
|                 .Setup(s => s.DownloadReport(It.IsAny<EpisodeParseResult>())) | ||||
|                 .Returns(false); | ||||
|         } | ||||
|  | ||||
|         private void WithApprovedDecisions() | ||||
|         { | ||||
|             Mocker.GetMock<IDownloadDirector>() | ||||
|                 .Setup(s => s.GetDownloadDecision(It.IsAny<EpisodeParseResult>())) | ||||
|                 .Returns(new DownloadDecision(new string[0])); | ||||
|         } | ||||
|  | ||||
|         private void WithDeclinedDecisions() | ||||
|         { | ||||
|             Mocker.GetMock<IDownloadDirector>() | ||||
|                 .Setup(s => s.GetDownloadDecision(It.IsAny<EpisodeParseResult>())) | ||||
|                 .Returns(new DownloadDecision(new[] { "Rejection reason" })); | ||||
|         } | ||||
|  | ||||
|                                  Times.Once()); | ||||
|         } | ||||
|  | ||||
|       | ||||
|  | ||||
|         | ||||
|  | ||||
|         [Test] | ||||
|         public void should_skip_if_series_does_not_match_searched_series() | ||||
|         { | ||||
|             var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(5) | ||||
|                 .All() | ||||
|                 .With(e => e.SeasonNumber = 1) | ||||
|                 .With(e => e.EpisodeNumbers = new List<int> { 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<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), | ||||
|                                                       Times.Never()); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_skip_if_episode_was_already_downloaded() | ||||
|         { | ||||
|             var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(2) | ||||
|                 .All() | ||||
|                 .With(e => e.SeasonNumber = 1) | ||||
|                 .With(e => e.EpisodeNumbers = new List<int> { 5 }) | ||||
|                 .With(c => c.Quality = new QualityModel(Quality.DVD, true)) | ||||
|                 .TheLast(1) | ||||
|                 .With(e => e.EpisodeNumbers = new List<int> { 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<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), | ||||
|                                                       Times.Once()); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_try_next_report_if_download_fails() | ||||
|         { | ||||
|             var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(2) | ||||
|                 .All() | ||||
|                 .With(e => e.SeasonNumber = 1) | ||||
|                 .With(e => e.EpisodeNumbers = new List<int> { 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<DownloadProvider>() | ||||
|                 .Setup(s => s.DownloadReport(It.Is<EpisodeParseResult>(d => d.Quality.Quality == Quality.DVD))) | ||||
|                 .Returns(false); | ||||
|  | ||||
|             Mocker.GetMock<DownloadProvider>() | ||||
|                 .Setup(s => s.DownloadReport(It.Is<EpisodeParseResult>(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<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), | ||||
|                                                       Times.Exactly(2)); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_return_valid_successes_when_one_or_more_downloaded() | ||||
|         { | ||||
|             var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(5) | ||||
|                 .All() | ||||
|                 .With(e => e.SeasonNumber = 1) | ||||
|                 .With(e => e.EpisodeNumbers = new List<int> { 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<DownloadDirector>() | ||||
|                 .Setup(s => s.IsDownloadPermitted(It.Is<EpisodeParseResult>(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<DownloadDirector>().Verify(c => c.IsDownloadPermitted(It.IsAny<EpisodeParseResult>()), | ||||
|                                                        Times.Once()); | ||||
|             Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), | ||||
|                                                       Times.Once()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| */ | ||||
|  | ||||
| namespace NzbDrone.Core.Test.IndexerSearchTests | ||||
| { | ||||
| } | ||||
| @@ -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<SingleEpisodeSearchDefinition> | ||||
|     { | ||||
|         [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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<EpisodeParseResult> PerformSearch(Series series, List<Episode> episodes, Model.Notification.ProgressNotification notification) | ||||
|         { | ||||
|             var episode = episodes.Single(); | ||||
|  | ||||
|             var reports = new List<EpisodeParseResult>(); | ||||
|             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; | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -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<IConfigService>().SetupGet(c => c.NzbsOrgHash).Returns("MockedConfigValue"); | ||||
|             Mocker.GetMock<IConfigService>().SetupGet(c => c.NzbsOrgUId).Returns("MockedConfigValue"); | ||||
|  | ||||
|             Mocker.GetMock<IConfigService>().SetupGet(c => c.NzbsrusHash).Returns("MockedConfigValue"); | ||||
|             Mocker.GetMock<IConfigService>().SetupGet(c => c.NzbsrusUId).Returns("MockedConfigValue"); | ||||
|  | ||||
|             Mocker.GetMock<IConfigService>().SetupGet(c => c.FileSharingTalkUid).Returns("MockedConfigValue"); | ||||
|             Mocker.GetMock<IConfigService>().SetupGet(c => c.FileSharingTalkSecret).Returns("MockedConfigValue"); | ||||
|  | ||||
|             Mocker.GetMock<IConfigService>().SetupGet(c => c.OmgwtfnzbsUsername).Returns("MockedConfigValue"); | ||||
|             Mocker.GetMock<IConfigService>().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<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", fileName)); | ||||
|  | ||||
|             var fakeSettings = Builder<Indexer>.CreateNew().Build(); | ||||
|             Mocker.GetMock<IIndexerService>() | ||||
|                 .Setup(c => c.GetSettings(It.IsAny<Type>())) | ||||
|                 .Returns(fakeSettings); | ||||
|  | ||||
|             var mockIndexer = Mocker.Resolve<MockIndexer>(); | ||||
|             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<Indexer>.CreateNew().Build(); | ||||
|             Mocker.GetMock<IIndexerService>() | ||||
|                 .Setup(c => c.GetSettings(It.IsAny<Type>())) | ||||
|                 .Returns(fakeSettings); | ||||
|  | ||||
|             var fakeRssItem = Builder<SyndicationItem>.CreateNew() | ||||
|                 .With(c => c.Title = new TextSyndicationContent(title)) | ||||
|                 .With(c => c.Summary = new TextSyndicationContent(summary)) | ||||
|                 .Build(); | ||||
|  | ||||
|             var result = Mocker.Resolve<CustomParserIndexer>().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<Indexer>.CreateNew().Build(); | ||||
|             Mocker.GetMock<IIndexerService>() | ||||
|                 .Setup(c => c.GetSettings(It.IsAny<Type>())) | ||||
|                 .Returns(fakeSettings); | ||||
|  | ||||
|             var fakeRssItem = Builder<SyndicationItem>.CreateNew() | ||||
|                 .With(c => c.Title = new TextSyndicationContent(title)) | ||||
|                 .With(c => c.Summary = new TextSyndicationContent(summary)) | ||||
|                 .Build(); | ||||
|  | ||||
|             var result = Mocker.Resolve<CustomParserIndexer>().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<IndexerBase>(); | ||||
|             mock.CallBase = true; | ||||
|             var result = mock.Object.GetQueryTitle(raw); | ||||
|             result.Should().Be(clean); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void size_nzbsrus() | ||||
|         { | ||||
|             WithConfiguredIndexers(); | ||||
|  | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "SizeParsing", "nzbsrus.xml")); | ||||
|  | ||||
|              | ||||
|             var parseResults = Mocker.Resolve<NzbsRUs>().FetchRss(); | ||||
|  | ||||
|             parseResults.Should().HaveCount(1); | ||||
|             parseResults[0].Size.Should().Be(1793148846); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void size_newznab() | ||||
|         { | ||||
|             WithConfiguredIndexers(); | ||||
|  | ||||
|             var newznabDefs = Builder<NewznabDefinition>.CreateListOfSize(1) | ||||
|                     .All() | ||||
|                     .With(n => n.ApiKey = String.Empty) | ||||
|                     .Build(); | ||||
|  | ||||
|             Mocker.GetMock<INewznabService>().Setup(s => s.Enabled()).Returns(newznabDefs.ToList()); | ||||
|  | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "SizeParsing", "newznab.xml")); | ||||
|  | ||||
|              | ||||
|             var parseResults = Mocker.Resolve<Newznab>().FetchRss(); | ||||
|  | ||||
|             parseResults[0].Size.Should().Be(1183105773); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void size_nzbindex() | ||||
|         { | ||||
|             WithConfiguredIndexers(); | ||||
|  | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .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<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "SizeParsing", "nzbindex.xml")); | ||||
|  | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .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<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "SizeParsing", "nzbindex.xml")); | ||||
|  | ||||
|              | ||||
|             var parseResults = Mocker.Resolve<NzbIndex>().FetchRss(); | ||||
|  | ||||
|             parseResults[0].Size.Should().Be(587328389); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void size_nzbclub() | ||||
|         { | ||||
|             WithConfiguredIndexers(); | ||||
|  | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream("http://www.nzbclub.com/nzbfeed.aspx?ig=2&gid=102952&st=1&ns=1&q=%23a.b.teevee", It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "SizeParsing", "nzbclub.xml")); | ||||
|  | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream("http://www.nzbclub.com/nzbfeed.aspx?ig=2&gid=5542&st=1&ns=1&q=", It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "SizeParsing", "nzbclub.xml")); | ||||
|  | ||||
|              | ||||
|             var parseResults = Mocker.Resolve<NzbClub>().FetchRss(); | ||||
|  | ||||
|             parseResults.Should().HaveCount(2); | ||||
|             parseResults[0].Size.Should().Be(2652142305); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void size_omgwtfnzbs() | ||||
|         { | ||||
|             WithConfiguredIndexers(); | ||||
|  | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream("http://rss.omgwtfnzbs.org/rss-search.php?catid=19,20&user=MockedConfigValue&api=MockedConfigValue&eng=1", It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "SizeParsing", "omgwtfnzbs.xml")); | ||||
|  | ||||
|              | ||||
|             var parseResults = Mocker.Resolve<Omgwtfnzbs>().FetchRss(); | ||||
|  | ||||
|             parseResults.Should().HaveCount(1); | ||||
|             parseResults[0].Size.Should().Be(236820890); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void Server_Unavailable_503_should_not_log_exception() | ||||
|         { | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>())) | ||||
|                           .Throws(new WebException("503")); | ||||
|  | ||||
|             Mocker.Resolve<NzbsRUs>().FetchRss(); | ||||
|  | ||||
|             ExceptionVerification.ExpectedErrors(0); | ||||
|             ExceptionVerification.ExpectedWarns(1); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void none_503_server_error_should_still_log_error() | ||||
|         { | ||||
|             WithConfiguredIndexers(); | ||||
|  | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>())) | ||||
|                           .Throws(new WebException("some other server error")); | ||||
|  | ||||
|             Mocker.Resolve<NzbsRUs>().FetchRss(); | ||||
|  | ||||
|             ExceptionVerification.ExpectedErrors(1); | ||||
|             ExceptionVerification.ExpectedWarns(0); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void indexer_that_isnt_configured_shouldnt_make_an_http_call() | ||||
|         { | ||||
|             Mocker.Resolve<NotConfiguredIndexer>().FetchRss(); | ||||
|  | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                 .Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never()); | ||||
|  | ||||
|             ExceptionVerification.ExpectedWarns(1); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void newznab_link_should_be_link_to_nzb_not_details() | ||||
|         { | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "newznab.xml")); | ||||
|  | ||||
|             var fakeSettings = Builder<Indexer>.CreateNew().Build(); | ||||
|             Mocker.GetMock<IIndexerService>() | ||||
|                 .Setup(c => c.GetSettings(It.IsAny<Type>())) | ||||
|                 .Returns(fakeSettings); | ||||
|  | ||||
|             var mockIndexer = Mocker.Resolve<MockIndexer>(); | ||||
|             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<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "" + fileName)); | ||||
|  | ||||
|             var fakeSettings = Builder<Indexer>.CreateNew().Build(); | ||||
|             Mocker.GetMock<IIndexerService>() | ||||
|                 .Setup(c => c.GetSettings(It.IsAny<Type>())) | ||||
|                 .Returns(fakeSettings); | ||||
|  | ||||
|             var mockIndexer = Mocker.Resolve<MockIndexer>(); | ||||
|             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<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "" + fileName)); | ||||
|  | ||||
|             var parseResults = Mocker.Resolve<NzbsRUs>().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<NewznabDefinition>.CreateListOfSize(1) | ||||
|                     .All() | ||||
|                     .With(n => n.ApiKey = String.Empty) | ||||
|                     .Build(); | ||||
|  | ||||
|             Mocker.GetMock<INewznabService>().Setup(s => s.Enabled()).Returns(newznabDefs.ToList()); | ||||
|  | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "" + fileName)); | ||||
|  | ||||
|             var parseResults = Mocker.Resolve<Newznab>().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<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "" + fileName)); | ||||
|  | ||||
|             var parseResults = Mocker.Resolve<Wombles>().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<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "" + fileName)); | ||||
|  | ||||
|             var parseResults = Mocker.Resolve<FileSharingTalk>().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<HttpProvider>() | ||||
|                           .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<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "nzbindex.xml")); | ||||
|  | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .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<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "nzbindex.xml")); | ||||
|  | ||||
|             var parseResults = Mocker.Resolve<NzbIndex>().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<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream("http://www.nzbclub.com/nzbfeed.aspx?ig=2&gid=102952&st=1&ns=1&q=%23a.b.teevee", It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "" + fileName)); | ||||
|  | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream("http://www.nzbclub.com/nzbfeed.aspx?ig=2&gid=5542&st=1&ns=1&q=", It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "" + fileName)); | ||||
|  | ||||
|             var parseResults = Mocker.Resolve<NzbClub>().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<Newznab>().GetQueryTitle(seriesTitle).Should().Be(expected); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_get_nzbInfoUrl_for_omgwtfnzbs() | ||||
|         { | ||||
|             WithConfiguredIndexers(); | ||||
|  | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadStream("http://rss.omgwtfnzbs.org/rss-search.php?catid=19,20&user=MockedConfigValue&api=MockedConfigValue&eng=1", It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(OpenRead("Files", "Rss", "SizeParsing", "omgwtfnzbs.xml")); | ||||
|  | ||||
|              | ||||
|             var parseResults = Mocker.Resolve<Omgwtfnzbs>().FetchRss(); | ||||
|  | ||||
|             parseResults.Should().HaveCount(1); | ||||
|             parseResults[0].NzbInfoUrl.Should().Be("http://omgwtfnzbs.org/details.php?id=OAl4g"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadString("https://nzbx.co/api/recent?category=tv", It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(ReadAllText("Files", "Rss", "SizeParsing", "nzbx_recent.json")); | ||||
|  | ||||
|              | ||||
|             var parseResults = Mocker.Resolve<Nzbx>().FetchRss(); | ||||
|  | ||||
|             parseResults.Should().HaveCount(1); | ||||
|             parseResults[0].Size.Should().Be(890190951); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_get_size_when_parsing_search_results() | ||||
|         { | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadString("https://nzbx.co/api/search?q=30+Rock+S01E01", It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(ReadAllText("Files", "Rss", "SizeParsing", "nzbx_search.json")); | ||||
|  | ||||
|              | ||||
|             var parseResults = Mocker.Resolve<Nzbx>().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<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadString(It.IsAny<String>(), It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(ReadAllText("Files", "Rss", "nzbx_recent.json")); | ||||
|  | ||||
|             var parseResults = Mocker.Resolve<Nzbx>().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<HttpProvider>() | ||||
|                   .Setup(h => h.DownloadString(It.IsAny<String>(), It.IsAny<NetworkCredential>())) | ||||
|                   .Returns(ReadAllText("Files", "Rss", "nzbx_search.json")); | ||||
|  | ||||
|             var parseResults = Mocker.Resolve<Nzbx>().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<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadString("https://nzbx.co/api/recent?category=tv", It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(ReadAllText("Files", "Rss", "SizeParsing", "nzbx_recent.json")); | ||||
|  | ||||
|              | ||||
|             var parseResults = Mocker.Resolve<Nzbx>().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<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadString("https://nzbx.co/api/search?q=30+Rock+S01E01", It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(ReadAllText("Files", "Rss", "SizeParsing", "nzbx_search.json")); | ||||
|  | ||||
|              | ||||
|             var parseResults = Mocker.Resolve<Nzbx>().FetchEpisode("30 Rock", 1, 1); | ||||
|  | ||||
|             parseResults.Should().HaveCount(1); | ||||
|             parseResults[0].Age.Should().Be(expectedAge); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_name_nzb_properly() | ||||
|         { | ||||
|             Mocker.GetMock<HttpProvider>() | ||||
|                           .Setup(h => h.DownloadString("https://nzbx.co/api/recent?category=tv", It.IsAny<NetworkCredential>())) | ||||
|                           .Returns(ReadAllText("Files", "Rss", "SizeParsing", "nzbx_recent.json")); | ||||
|  | ||||
|              | ||||
|             var parseResults = Mocker.Resolve<Nzbx>().FetchRss(); | ||||
|  | ||||
|             parseResults.Should().HaveCount(1); | ||||
|             parseResults[0].NzbUrl.Should().EndWith(parseResults[0].OriginalString); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<IndexerService> | ||||
|     { | ||||
|         [Test] | ||||
|         public void should_insert_indexer_in_repository_when_it_doesnt_exist() | ||||
|         { | ||||
|             Mocker.SetConstant<IEnumerable<IndexerBase>>(new List<IndexerBase> { Mocker.Resolve<MockIndexer>() }); | ||||
|  | ||||
|             Subject.Init(); | ||||
|  | ||||
|             Mocker.GetMock<IIndexerRepository>() | ||||
|                 .Verify(v => v.Insert(It.IsAny<Indexer>()), Times.Once()); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void getEnabled_should_not_return_any_when_no_indexers_are_enabled() | ||||
|         { | ||||
|             Mocker.SetConstant<IEnumerable<IndexerBase>>(new List<IndexerBase> { Mocker.Resolve<MockIndexer>() }); | ||||
|  | ||||
|             Mocker.GetMock<IIndexerRepository>() | ||||
|                   .Setup(s => s.All()) | ||||
|                   .Returns(new List<Indexer> {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<IEnumerable<IndexerBase>>(new List<IndexerBase> { Mocker.Resolve<DefaultEnabledIndexer>() }); | ||||
|  | ||||
|             Subject.Init(); | ||||
|  | ||||
|             Mocker.GetMock<IIndexerRepository>() | ||||
|                 .Verify(v => v.Insert(It.Is<Indexer>(indexer => indexer.Enable)), Times.Once()); | ||||
|  | ||||
|             Mocker.GetMock<IIndexerRepository>() | ||||
|                 .Verify(v => v.Insert(It.Is<Indexer>(indexer => !indexer.Enable)), Times.Never()); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void Init_indexer_should_not_enable_indexer_that_is_not_enabled_by_default() | ||||
|         { | ||||
|             Mocker.SetConstant<IEnumerable<IndexerBase>>(new List<IndexerBase> { Mocker.Resolve<MockIndexer>() }); | ||||
|  | ||||
|             Subject.Init(); | ||||
|  | ||||
|             Mocker.GetMock<IIndexerRepository>() | ||||
|                 .Verify(v => v.Insert(It.Is<Indexer>(indexer => indexer.Enable)), Times.Never()); | ||||
|  | ||||
|             Mocker.GetMock<IIndexerRepository>() | ||||
|                 .Verify(v => v.Insert(It.Is<Indexer>(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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> 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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> 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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> 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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> 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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> 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; } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<NewznabService> | ||||
|     { | ||||
|         private void WithInvalidName() | ||||
|         { | ||||
|             Mocker.GetMock<INewznabRepository>() | ||||
|                   .Setup(s => s.All()) | ||||
|                   .Returns(new List<NewznabDefinition>{new NewznabDefinition { Id = 1, Name = "", Url = "http://www.nzbdrone.com" }}); | ||||
|         } | ||||
|  | ||||
|         private void WithExisting() | ||||
|         { | ||||
|             Mocker.GetMock<INewznabRepository>() | ||||
|                   .Setup(s => s.All()) | ||||
|                   .Returns(new List<NewznabDefinition> { new NewznabDefinition { Id = 1, Name = "Nzbs.org", Url = "http://nzbs.org" } }); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void InitializeNewznabIndexers_should_initialize_build_in_indexers() | ||||
|         { | ||||
|             Subject.Init(); | ||||
|  | ||||
|             Mocker.GetMock<INewznabRepository>() | ||||
|                   .Verify(s => s.Insert(It.Is<NewznabDefinition>(n => n.BuiltIn)), Times.Exactly(3)); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_delete_indexers_without_names() | ||||
|         { | ||||
|             WithInvalidName(); | ||||
|  | ||||
|             Subject.Init(); | ||||
|  | ||||
|             Mocker.GetMock<INewznabRepository>() | ||||
|                   .Verify(s => s.Delete(1), Times.Once()); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_add_new_indexers() | ||||
|         { | ||||
|             WithExisting(); | ||||
|              | ||||
|             Subject.Init(); | ||||
|  | ||||
|             Mocker.GetMock<INewznabRepository>() | ||||
|                   .Verify(s => s.Insert(It.IsAny<NewznabDefinition>()), Times.Exactly(2)); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_update_existing() | ||||
|         { | ||||
|             WithExisting(); | ||||
|  | ||||
|             Subject.Init(); | ||||
|  | ||||
|             Mocker.GetMock<INewznabRepository>() | ||||
|                   .Verify(s => s.Update(It.IsAny<NewznabDefinition>()), 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<SocketException>(() => Subject.CheckHostname("http://BadName")); | ||||
|  | ||||
|             ExceptionVerification.ExpectedErrors(1); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<BacklogSearchJob> | ||||
|     { | ||||
|         private void WithEnableBacklogSearching() | ||||
|         { | ||||
|             Mocker.GetMock<IConfigService>().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<Episode>(); | ||||
|  | ||||
|             WithStrictMocker(); | ||||
|             WithEnableBacklogSearching(); | ||||
|  | ||||
|             Mocker.GetMock<IEpisodeService>() | ||||
|                 .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); | ||||
|  | ||||
|              | ||||
|             Subject.Start(notification, null); | ||||
|  | ||||
|              | ||||
|             Mocker.GetMock<SeasonSearchJob>().Verify(c => c.Start(notification, new { SeriesId = It.IsAny<int>(), SeasonNumber = It.IsAny<int>() }), | ||||
|                                                        Times.Never()); | ||||
|  | ||||
|             Mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, new { SeriesId = It.IsAny<int>(), SeasonNumber = 0 }), | ||||
|                                                        Times.Never()); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void individual_missing_episode() | ||||
|         { | ||||
|              | ||||
|             var notification = new ProgressNotification("Backlog Search Job Test"); | ||||
|  | ||||
|             var series = Builder<Series>.CreateNew() | ||||
|                     .With(s => s.Monitored = true) | ||||
|                     .With(s => s.BacklogSetting = BacklogSettingType.Enable) | ||||
|                     .Build(); | ||||
|  | ||||
|             var episodes = Builder<Episode>.CreateListOfSize(1) | ||||
|                 .All() | ||||
|                 .With(e => e.Series = series) | ||||
|                 .Build().ToList(); | ||||
|  | ||||
|             WithEnableBacklogSearching(); | ||||
|  | ||||
|             Mocker.GetMock<IEpisodeService>() | ||||
|                 .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); | ||||
|  | ||||
|             Mocker.GetMock<EpisodeSearchJob>() | ||||
|                 .Setup(s => s.Start(notification, It.Is<object>(d => d.GetPropertyValue<int>("EpisodeId") == 1))); | ||||
|  | ||||
|              | ||||
|             Subject.Start(notification, null); | ||||
|  | ||||
|              | ||||
|             Mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.Is<object>(d => d.GetPropertyValue<int>("EpisodeId") >= 0)), | ||||
|                                                        Times.Once()); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void individual_missing_episodes_only() | ||||
|         { | ||||
|              | ||||
|             var notification = new ProgressNotification("Backlog Search Job Test"); | ||||
|  | ||||
|             var series = Builder<Series>.CreateNew() | ||||
|                     .With(s => s.Monitored = true) | ||||
|                     .With(s => s.BacklogSetting = BacklogSettingType.Enable) | ||||
|                     .Build(); | ||||
|  | ||||
|             var episodes = Builder<Episode>.CreateListOfSize(5) | ||||
|                 .All() | ||||
|                 .With(e => e.Series = series) | ||||
|                 .Build().ToList(); | ||||
|  | ||||
|             WithEnableBacklogSearching(); | ||||
|  | ||||
|             Mocker.GetMock<IEpisodeService>() | ||||
|                 .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); | ||||
|  | ||||
|              | ||||
|             Subject.Start(notification, null); | ||||
|  | ||||
|              | ||||
|             Mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.Is<object>(d => d.GetPropertyValue<int>("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<Series>.CreateNew() | ||||
|                     .With(s => s.Monitored = true) | ||||
|                     .With(s => s.BacklogSetting = BacklogSettingType.Enable) | ||||
|                     .Build(); | ||||
|  | ||||
|             var episodes = Builder<Episode>.CreateListOfSize(5) | ||||
|                 .All() | ||||
|                 .With(e => e.Series = series) | ||||
|                 .With(e => e.SeasonNumber = 1) | ||||
|                 .Build().ToList(); | ||||
|  | ||||
|             WithEnableBacklogSearching(); | ||||
|  | ||||
|             Mocker.GetMock<IEpisodeService>() | ||||
|                 .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); | ||||
|  | ||||
|             Mocker.GetMock<IEpisodeService>() | ||||
|                 .Setup(s => s.GetEpisodeNumbersBySeason(1, 1)).Returns(new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); | ||||
|  | ||||
|              | ||||
|             Subject.Start(notification, null); | ||||
|  | ||||
|              | ||||
|             Mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.Is<object>(d => d.GetPropertyValue<int>("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<Series>.CreateNew() | ||||
|                     .With(s => s.Monitored = true) | ||||
|                     .With(s => s.BacklogSetting = BacklogSettingType.Enable) | ||||
|                     .Build(); | ||||
|  | ||||
|             var episodes = Builder<Episode>.CreateListOfSize(5) | ||||
|                 .All() | ||||
|                 .With(e => e.Series = series) | ||||
|                 .With(e => e.SeriesId = series.Id) | ||||
|                 .With(e => e.SeasonNumber = 1) | ||||
|                 .Build().ToList(); | ||||
|  | ||||
|             WithEnableBacklogSearching(); | ||||
|  | ||||
|             Mocker.GetMock<IEpisodeService>() | ||||
|                 .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); | ||||
|  | ||||
|             Mocker.GetMock<IEpisodeService>() | ||||
|                 .Setup(s => s.GetEpisodeNumbersBySeason(1, 1)).Returns(episodes.Select(e => e.EpisodeNumber).ToList()); | ||||
|  | ||||
|              | ||||
|             Subject.Start(notification, null); | ||||
|  | ||||
|              | ||||
|             Mocker.GetMock<SeasonSearchJob>().Verify(c => c.Start(notification, It.Is<object>(d => d.GetPropertyValue<int>("SeriesId") >= 0 && | ||||
|                                                                                                    d.GetPropertyValue<int>("SeasonNumber") >= 0)), | ||||
|                                                        Times.Once()); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void multiple_missing_episodes() | ||||
|         { | ||||
|              | ||||
|             var notification = new ProgressNotification("Backlog Search Job Test"); | ||||
|  | ||||
|             var series = Builder<Series>.CreateNew() | ||||
|                     .With(s => s.Monitored = true) | ||||
|                     .With(s => s.BacklogSetting = BacklogSettingType.Enable) | ||||
|                     .Build(); | ||||
|  | ||||
|             var series2 = Builder<Series>.CreateNew() | ||||
|                     .With(s => s.Monitored = true) | ||||
|                     .With(s => s.BacklogSetting = BacklogSettingType.Enable) | ||||
|                     .Build(); | ||||
|  | ||||
|             var episodes = Builder<Episode>.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<IEpisodeService>() | ||||
|                 .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); | ||||
|  | ||||
|             Mocker.GetMock<IEpisodeService>() | ||||
|                 .Setup(s => s.GetEpisodeNumbersBySeason(1, 1)).Returns(new List<int> { 1, 2, 3, 4, 5 }); | ||||
|  | ||||
|              | ||||
|             Subject.Start(notification, null); | ||||
|  | ||||
|              | ||||
|             Mocker.GetMock<SeasonSearchJob>().Verify(c => c.Start(notification, It.Is<object>(d => d.GetPropertyValue<int>("SeriesId") >= 0 && | ||||
|                                                                                                    d.GetPropertyValue<int>("SeasonNumber") >= 0)), | ||||
|                                                        Times.Once()); | ||||
|  | ||||
|             Mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.Is<object>(d => d.GetPropertyValue<int>("EpisodeId") >= 0)), | ||||
|                                        Times.Exactly(5)); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void GetMissingForEnabledSeries_should_only_return_episodes_for_monitored_series() | ||||
|         { | ||||
|              | ||||
|             var series = Builder<Series>.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<Episode>.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<IEpisodeService>() | ||||
|                 .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<Series>.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<Episode>.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<IEpisodeService>() | ||||
|                 .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<Series>.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<Episode>.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<IEpisodeService>() | ||||
|                 .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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<IConfigService>().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<Episode>(); | ||||
|  | ||||
|             Mocker.GetMock<IEpisodeService>() | ||||
|                 .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); | ||||
|  | ||||
|              | ||||
|             Mocker.Resolve<RecentBacklogSearchJob>().Start(MockNotification, null); | ||||
|  | ||||
|              | ||||
|             Mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(MockNotification, new { EpisodeId = It.IsAny<int>() }), | ||||
|                                                        Times.Never()); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_only_process_missing_episodes_from_the_last_30_days() | ||||
|         { | ||||
|             WithEnableBacklogSearching(); | ||||
|  | ||||
|             var series = Builder<Series>.CreateNew() | ||||
|                     .With(s => s.Monitored = true) | ||||
|                     .With(s => s.BacklogSetting = BacklogSettingType.Enable) | ||||
|                     .Build(); | ||||
|  | ||||
|              | ||||
|             var episodes = Builder<Episode>.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<IEpisodeService>() | ||||
|                 .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); | ||||
|  | ||||
|             Mocker.GetMock<EpisodeSearchJob>().Setup(c => c.Start(It.IsAny<ProgressNotification>(), It.Is<object>(d => d.GetPropertyValue<int>("EpisodeId") >= 0))); | ||||
|  | ||||
|              | ||||
|             Mocker.Resolve<RecentBacklogSearchJob>().Start(MockNotification, null); | ||||
|  | ||||
|              | ||||
|             Mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(It.IsAny<ProgressNotification>(), It.Is<object>(d => d.GetPropertyValue<int>("EpisodeId") >= 0)), | ||||
|                                                        Times.Exactly(40)); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void GetMissingForEnabledSeries_should_only_return_episodes_for_monitored_series() | ||||
|         { | ||||
|              | ||||
|             var series = Builder<Series>.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<Episode>.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<IEpisodeService>() | ||||
|                 .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); | ||||
|  | ||||
|              | ||||
|             var result = Mocker.Resolve<RecentBacklogSearchJob>().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<Series>.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<Episode>.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<IEpisodeService>() | ||||
|                 .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); | ||||
|  | ||||
|              | ||||
|             var result = Mocker.Resolve<RecentBacklogSearchJob>().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<Series>.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<Episode>.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<IEpisodeService>() | ||||
|                 .Setup(s => s.EpisodesWithoutFiles(true)).Returns(episodes); | ||||
|  | ||||
|              | ||||
|             var result = Mocker.Resolve<RecentBacklogSearchJob>().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); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -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<Episode> _episodes;  | ||||
|  | ||||
|         private ProgressNotification notification; | ||||
|  | ||||
|         [SetUp] | ||||
|         public void Setup() | ||||
|         { | ||||
|              notification = new ProgressNotification("Search"); | ||||
|  | ||||
|              _episodes = Builder<Episode>.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<IEpisodeService>() | ||||
|                  .Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(_episodes); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void SeasonSearch_partial_season_success() | ||||
|         { | ||||
|             Mocker.GetMock<SearchProvider>() | ||||
|                 .Setup(c => c.PartialSeasonSearch(notification, 1, 1)) | ||||
|                 .Returns(_episodes.Select(e => e.EpisodeNumber).ToList()); | ||||
|  | ||||
|              | ||||
|             Mocker.Resolve<SeasonSearchJob>().Start(notification, new { SeriesId = 1, SeasonNumber = 1 }); | ||||
|  | ||||
|              | ||||
|             Mocker.VerifyAllMocks(); | ||||
|             Mocker.GetMock<SearchProvider>().Verify(c => c.PartialSeasonSearch(notification, 1, 1), Times.Once()); | ||||
|             Mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, new { EpisodeId =  It.IsAny<int>() }), Times.Never()); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void SeasonSearch_partial_season_failure() | ||||
|         { | ||||
|             Mocker.GetMock<SearchProvider>() | ||||
|                 .Setup(c => c.PartialSeasonSearch(notification, 1, 1)) | ||||
|                 .Returns(new List<int>()); | ||||
|  | ||||
|              | ||||
|             Mocker.Resolve<SeasonSearchJob>().Start(notification, new { SeriesId = 1, SeasonNumber = 1 }); | ||||
|  | ||||
|              | ||||
|             Mocker.GetMock<SearchProvider>().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<Episode>.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<IEpisodeService>() | ||||
|                 .Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(episodes); | ||||
|  | ||||
|             Mocker.GetMock<SearchProvider>() | ||||
|                 .Setup(c => c.PartialSeasonSearch(notification, 1, 1)) | ||||
|                 .Returns(new List<int>{1}); | ||||
|  | ||||
|  | ||||
|              | ||||
|             Mocker.Resolve<SeasonSearchJob>().Start(notification, new { SeriesId = 1, SeasonNumber = 1 }); | ||||
|  | ||||
|              | ||||
|             Mocker.VerifyAllMocks(); | ||||
|             Mocker.GetMock<SearchProvider>().Verify(c => c.PartialSeasonSearch(notification, 1, 1), Times.Once()); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void SeasonSearch_should_allow_searching_of_season_zero() | ||||
|         { | ||||
|             Mocker.GetMock<SearchProvider>() | ||||
|                 .Setup(c => c.PartialSeasonSearch(notification, 1, 0)).Returns(new List<int>()); | ||||
|  | ||||
|              | ||||
|             Mocker.Resolve<SeasonSearchJob>().Start(notification, new { SeriesId = 1, SeasonNumber = 0 }); | ||||
|  | ||||
|              | ||||
|             Mocker.GetMock<SearchProvider>().Verify(c => c.PartialSeasonSearch(notification, 1, 1), Times.Never()); | ||||
|             Mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, new { EpisodeId = It.IsAny<int>() }), Times.Never()); | ||||
|  | ||||
|             ExceptionVerification.ExpectedWarns(1); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void should_search_for_individual_episodes_when_no_partial_results_are_returned() | ||||
|         { | ||||
|             Mocker.GetMock<SearchProvider>() | ||||
|                 .Setup(c => c.PartialSeasonSearch(notification, 1, 1)).Returns(new List<int>()); | ||||
|              | ||||
|             Mocker.Resolve<SeasonSearchJob>().Start(notification, new { SeriesId = 1, SeasonNumber = 1 }); | ||||
|  | ||||
|             Mocker.GetMock<EpisodeSearchJob>().Verify(v => v.Start(notification, It.Is<object>(o => o.GetPropertyValue<Int32>("EpisodeId") > 0)), Times.Exactly(_episodes.Count)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<int> { 1, 2, 3, 4, 5 }; | ||||
|  | ||||
|             WithStrictMocker(); | ||||
|  | ||||
|             var notification = new ProgressNotification("Series Search"); | ||||
|  | ||||
|             Mocker.GetMock<ISeasonRepository>() | ||||
|                 .Setup(c => c.GetSeasonNumbers(1)).Returns(seasons); | ||||
|  | ||||
|             Mocker.GetMock<ISeasonRepository>() | ||||
|                 .Setup(c => c.IsIgnored(It.IsAny<int>(), It.IsAny<int>())).Returns(false); | ||||
|  | ||||
|             Mocker.GetMock<SeasonSearchJob>() | ||||
|                 .Setup(c => c.Start(notification, It.Is<object>(d => d.GetPropertyValue<int>("SeriesId") == 1 && d.GetPropertyValue<int>("SeasonNumber") >= 0))).Verifiable(); | ||||
|  | ||||
|              | ||||
|             Mocker.Resolve<SeriesSearchJob>().Start(notification, new { SeriesId = 1 }); | ||||
|  | ||||
|              | ||||
|             Mocker.VerifyAllMocks(); | ||||
|             Mocker.GetMock<SeasonSearchJob>().Verify(c => c.Start(notification, It.Is<object>(d => d.GetPropertyValue<int>("SeriesId") == 1 && d.GetPropertyValue<int>("SeasonNumber") >= 0)), | ||||
|                                                        Times.Exactly(seasons.Count)); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void SeriesSearch_no_seasons() | ||||
|         { | ||||
|             var seasons = new List<int>(); | ||||
|  | ||||
|             WithStrictMocker(); | ||||
|  | ||||
|             var notification = new ProgressNotification("Series Search"); | ||||
|  | ||||
|             Mocker.GetMock<ISeasonRepository>() | ||||
|                 .Setup(c => c.GetSeasonNumbers(1)).Returns(seasons); | ||||
|  | ||||
|              | ||||
|             Mocker.Resolve<SeriesSearchJob>().Start(notification, new { SeriesId = 1 }); | ||||
|  | ||||
|              | ||||
|             Mocker.VerifyAllMocks(); | ||||
|             Mocker.GetMock<SeasonSearchJob>().Verify(c => c.Start(notification, new { SeriesId = 1, SeasonNumber = It.IsAny<int>() }), | ||||
|                                                        Times.Never()); | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void SeriesSearch_should_not_search_for_season_0() | ||||
|         { | ||||
|             Mocker.GetMock<ISeasonRepository>() | ||||
|                 .Setup(c => c.GetSeasonNumbers(It.IsAny<int>())) | ||||
|                 .Returns(new List<int> { 0, 1, 2 }); | ||||
|  | ||||
|             Mocker.Resolve<SeriesSearchJob>().Start(MockNotification, new { SeriesId = 12 }); | ||||
|  | ||||
|  | ||||
|             Mocker.GetMock<SeasonSearchJob>() | ||||
|                 .Verify(c => c.Start(It.IsAny<ProgressNotification>(), new { SeriesId = It.IsAny<int>(), SeasonNumber = 0 }), Times.Never()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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. | ||||
| @@ -134,17 +134,7 @@ | ||||
|     <Compile Include="Framework\DbTest.cs" /> | ||||
|     <Compile Include="Framework\NBuilderExtensions.cs" /> | ||||
|     <Compile Include="HelperTests\XElementHelperTests\ConvertToTFixture.cs" /> | ||||
|     <Compile Include="IndexerSearchTests\DailyEpisodeSearchTests\IndexerDailyEpisodeSearchFixture.cs" /> | ||||
|     <Compile Include="IndexerSearchTests\DailyEpisodeSearchTests\IndexerDailyEpisodeSearch_EpisodeMatch.cs" /> | ||||
|     <Compile Include="IndexerSearchTests\EpisodeSearchTests\IndexerEpisodeSearchFixture.cs" /> | ||||
|     <Compile Include="IndexerSearchTests\EpisodeSearchTests\IndexerEpisodeSearch_EpisodeMatch.cs" /> | ||||
|     <Compile Include="IndexerSearchTests\GetSearchTitleFixture.cs" /> | ||||
|     <Compile Include="IndexerSearchTests\IndexerSearchTestBase.cs" /> | ||||
|     <Compile Include="IndexerSearchTests\PartialSeasonSearchTests\PartialSeasonSearchFixture.cs" /> | ||||
|     <Compile Include="IndexerSearchTests\PartialSeasonSearchTests\PartialSeasonSearch_EpisodeMatch.cs" /> | ||||
|     <Compile Include="IndexerSearchTests\ProcessResultsFixture.cs" /> | ||||
|     <Compile Include="IndexerSearchTests\TestSearch.cs" /> | ||||
|     <Compile Include="IndexerTests\NzbxFixture.cs" /> | ||||
|     <Compile Include="IndexerSearchTests\SearchDefinitionFixture.cs" /> | ||||
|     <Compile Include="JobTests\JobRepositoryFixture.cs" /> | ||||
|     <Compile Include="JobTests\RenameSeasonJobFixture.cs" /> | ||||
|     <Compile Include="DecisionEngineTests\LanguageSpecificationFixture.cs" /> | ||||
| @@ -157,9 +147,7 @@ | ||||
|     <Compile Include="HelperTests\XElementHelperTests\ConvertToDayOfWeekFixture.cs" /> | ||||
|     <Compile Include="Qualities\QualityFixture.cs" /> | ||||
|     <Compile Include="EpisodeParseResultTest.cs" /> | ||||
|     <Compile Include="JobTests\BacklogSearchJobTest.cs" /> | ||||
|     <Compile Include="JobTests\PostDownloadScanJobFixture.cs" /> | ||||
|     <Compile Include="JobTests\RecentBacklogSearchJobTest.cs" /> | ||||
|     <Compile Include="ParserTests\QualityParserFixture.cs" /> | ||||
|     <Compile Include="Configuration\ConfigCachingFixture.cs" /> | ||||
|     <Compile Include="DecisionEngineTests\AllowedReleaseGroupSpecificationFixture.cs" /> | ||||
| @@ -181,7 +169,6 @@ | ||||
|     <Compile Include="DecisionEngineTests\QualityUpgradeSpecificationFixture.cs" /> | ||||
|     <Compile Include="DecisionEngineTests\QualityUpgradableSpecificationFixture.cs" /> | ||||
|     <Compile Include="ProviderTests\NotificationProviderTests\NotificationProviderFixture.cs" /> | ||||
|     <Compile Include="Indexers\NewznabServiceTest.cs" /> | ||||
|     <Compile Include="ProviderTests\DiskProviderTests\FreeDiskSpaceTest.cs" /> | ||||
|     <Compile Include="ProviderTests\ProwlProviderTest.cs" /> | ||||
|     <Compile Include="ProviderTests\GrowlProviderTest.cs" /> | ||||
| @@ -199,8 +186,6 @@ | ||||
|     <Compile Include="DecisionEngineTests\AcceptableSizeSpecificationFixture.cs" /> | ||||
|     <Compile Include="Qualities\QualitySizeServiceFixture.cs" /> | ||||
|     <Compile Include="ProviderTests\MisnamedProviderTest.cs" /> | ||||
|     <Compile Include="JobTests\SeasonSearchJobTest.cs" /> | ||||
|     <Compile Include="JobTests\SeriesSearchJobTest.cs" /> | ||||
|     <Compile Include="ProviderTests\EventClientProviderTest.cs" /> | ||||
|     <Compile Include="ProviderTests\XbmcProviderTest.cs" /> | ||||
|     <Compile Include="TvTests\EpisodeProviderTests\EpisodeProviderTest_GetEpisodesByParseResult.cs" /> | ||||
| @@ -211,12 +196,10 @@ | ||||
|     <Compile Include="DecisionEngineTests\MonitoredEpisodeSpecificationFixture.cs" /> | ||||
|     <Compile Include="EpisodeStatusTest.cs" /> | ||||
|     <Compile Include="JobTests\DiskScanJobTest.cs" /> | ||||
|     <Compile Include="IndexerTests\IndexerFixture.cs" /> | ||||
|     <Compile Include="DecisionEngineTests\AllowedDownloadSpecificationFixture.cs" /> | ||||
|     <Compile Include="JobTests\JobControllerFixture.cs" /> | ||||
|     <Compile Include="TvTests\QualityModelFixture.cs" /> | ||||
|     <Compile Include="RootFolderTests\RootFolderServiceFixture.cs" /> | ||||
|     <Compile Include="Indexers\IndexerServiceTest.cs" /> | ||||
|     <Compile Include="HistoryTests\HistoryRepositoryFixture.cs" /> | ||||
|     <Compile Include="MediaFileTests\MediaFileServiceTest.cs" /> | ||||
|     <Compile Include="Configuration\ConfigServiceFixture.cs" /> | ||||
|   | ||||
| @@ -1,24 +0,0 @@ | ||||
| <ProjectConfiguration> | ||||
|   <CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace> | ||||
|   <ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing> | ||||
|   <PreloadReferencedAssemblies>false</PreloadReferencedAssemblies> | ||||
|   <AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking> | ||||
|   <AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking> | ||||
|   <IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely> | ||||
|   <RunPreBuildEvents>true</RunPreBuildEvents> | ||||
|   <RunPostBuildEvents>true</RunPostBuildEvents> | ||||
|   <PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully> | ||||
|   <InstrumentAssembly>true</InstrumentAssembly> | ||||
|   <PreventSigningOfAssembly>false</PreventSigningOfAssembly> | ||||
|   <AnalyseExecutionTimes>true</AnalyseExecutionTimes> | ||||
|   <IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace> | ||||
|   <DefaultTestTimeout>60000</DefaultTestTimeout> | ||||
|   <UseBuildConfiguration></UseBuildConfiguration> | ||||
|   <UseBuildPlatform></UseBuildPlatform> | ||||
|   <ProxyProcessPath></ProxyProcessPath> | ||||
|   <UseCPUArchitecture>AutoDetect</UseCPUArchitecture> | ||||
|   <MSTestThreadApartmentState>STA</MSTestThreadApartmentState> | ||||
|   <BuildProcessArchitecture>x86</BuildProcessArchitecture> | ||||
|   <AdditionalFilesToInclude>..\Libraries\Sqlite\sqlite3.dll</AdditionalFilesToInclude> | ||||
|   <HiddenWarnings>PostBuildEventDisabled;PreBuildEventDisabled</HiddenWarnings> | ||||
| </ProjectConfiguration> | ||||
| @@ -1,230 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <TestRecord xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> | ||||
|   <Tests> | ||||
|     <TestRecord Name="NzbDrone"> | ||||
|       <Tests> | ||||
|         <TestRecord Name="Core"> | ||||
|           <Tests> | ||||
|             <TestRecord Name="Test"> | ||||
|               <Tests> | ||||
|                 <TestRecord Name="ProviderTests"> | ||||
|                   <Tests> | ||||
|                     <TestRecord Name="ReferenceDataProviderTest"> | ||||
|                       <Results> | ||||
|                         <UnitTestResult> | ||||
|                           <TestDate>2013-02-16T21:37:42</TestDate> | ||||
|                           <Passed>0</Passed> | ||||
|                           <Errors>0</Errors> | ||||
|                           <Failures>0</Failures> | ||||
|                           <Inconclusive>0</Inconclusive> | ||||
|                           <NotRunnable>0</NotRunnable> | ||||
|                           <Skipped>0</Skipped> | ||||
|                           <Ignored>9</Ignored> | ||||
|                           <Time /> | ||||
|                           <Message>Test successful | ||||
|  | ||||
| Execution time: 0.47ms</Message> | ||||
|                         </UnitTestResult> | ||||
|                       </Results> | ||||
|                       <Tests> | ||||
|                         <TestRecord Name="broken_service_should_not_cause_this_call_to_fail"> | ||||
|                           <Results> | ||||
|                             <UnitTestResult> | ||||
|                               <TestDate>2013-02-16T21:37:42</TestDate> | ||||
|                               <Passed>0</Passed> | ||||
|                               <Errors>0</Errors> | ||||
|                               <Failures>0</Failures> | ||||
|                               <Inconclusive>0</Inconclusive> | ||||
|                               <NotRunnable>0</NotRunnable> | ||||
|                               <Skipped>0</Skipped> | ||||
|                               <Ignored>1</Ignored> | ||||
|                               <Time /> | ||||
|                               <Message>SetUp : SqlCe is not supported in mono.</Message> | ||||
|                               <StackTrace>  at NzbDrone.Core.Test.Framework.SqlCeTest.CoreTestSetup () [0x00000] in <filename unknown>:0  | ||||
|   at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) | ||||
|   at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d5] in C:\cygwin\tmp\monobuild\build\BUILD\mono-2.10.9\mcs\class\corlib\System.Reflection\MonoMethod.cs:226 </StackTrace> | ||||
|                               <ConsoleOutput /> | ||||
|                               <ConsoleError /> | ||||
|                             </UnitTestResult> | ||||
|                           </Results> | ||||
|                         </TestRecord> | ||||
|                         <TestRecord Name="GetDailySeriesIds_should_return_empty_list_of_int_when_server_is_unavailable"> | ||||
|                           <Results> | ||||
|                             <UnitTestResult> | ||||
|                               <TestDate>2013-02-16T21:37:42</TestDate> | ||||
|                               <Passed>0</Passed> | ||||
|                               <Errors>0</Errors> | ||||
|                               <Failures>0</Failures> | ||||
|                               <Inconclusive>0</Inconclusive> | ||||
|                               <NotRunnable>0</NotRunnable> | ||||
|                               <Skipped>0</Skipped> | ||||
|                               <Ignored>1</Ignored> | ||||
|                               <Time /> | ||||
|                               <Message>SetUp : SqlCe is not supported in mono.</Message> | ||||
|                               <StackTrace>  at NzbDrone.Core.Test.Framework.SqlCeTest.CoreTestSetup () [0x00000] in <filename unknown>:0  | ||||
|   at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) | ||||
|   at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d5] in C:\cygwin\tmp\monobuild\build\BUILD\mono-2.10.9\mcs\class\corlib\System.Reflection\MonoMethod.cs:226 </StackTrace> | ||||
|                               <ConsoleOutput /> | ||||
|                               <ConsoleError /> | ||||
|                             </UnitTestResult> | ||||
|                           </Results> | ||||
|                         </TestRecord> | ||||
|                         <TestRecord Name="GetDailySeriesIds_should_return_empty_list_when_unable_to_parse"> | ||||
|                           <Results> | ||||
|                             <UnitTestResult> | ||||
|                               <TestDate>2013-02-16T21:37:42</TestDate> | ||||
|                               <Passed>0</Passed> | ||||
|                               <Errors>0</Errors> | ||||
|                               <Failures>0</Failures> | ||||
|                               <Inconclusive>0</Inconclusive> | ||||
|                               <NotRunnable>0</NotRunnable> | ||||
|                               <Skipped>0</Skipped> | ||||
|                               <Ignored>1</Ignored> | ||||
|                               <Time /> | ||||
|                               <Message>SetUp : SqlCe is not supported in mono.</Message> | ||||
|                               <StackTrace>  at NzbDrone.Core.Test.Framework.SqlCeTest.CoreTestSetup () [0x00000] in <filename unknown>:0  | ||||
|   at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) | ||||
|   at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d5] in C:\cygwin\tmp\monobuild\build\BUILD\mono-2.10.9\mcs\class\corlib\System.Reflection\MonoMethod.cs:226 </StackTrace> | ||||
|                               <ConsoleOutput /> | ||||
|                               <ConsoleError /> | ||||
|                             </UnitTestResult> | ||||
|                           </Results> | ||||
|                         </TestRecord> | ||||
|                         <TestRecord Name="GetDailySeriesIds_should_return_list_of_int_when_all_are_valid"> | ||||
|                           <Results> | ||||
|                             <UnitTestResult> | ||||
|                               <TestDate>2013-02-16T21:37:42</TestDate> | ||||
|                               <Passed>0</Passed> | ||||
|                               <Errors>0</Errors> | ||||
|                               <Failures>0</Failures> | ||||
|                               <Inconclusive>0</Inconclusive> | ||||
|                               <NotRunnable>0</NotRunnable> | ||||
|                               <Skipped>0</Skipped> | ||||
|                               <Ignored>1</Ignored> | ||||
|                               <Time /> | ||||
|                               <Message>SetUp : SqlCe is not supported in mono.</Message> | ||||
|                               <StackTrace>  at NzbDrone.Core.Test.Framework.SqlCeTest.CoreTestSetup () [0x00000] in <filename unknown>:0  | ||||
|   at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) | ||||
|   at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d5] in C:\cygwin\tmp\monobuild\build\BUILD\mono-2.10.9\mcs\class\corlib\System.Reflection\MonoMethod.cs:226 </StackTrace> | ||||
|                               <ConsoleOutput /> | ||||
|                               <ConsoleError /> | ||||
|                             </UnitTestResult> | ||||
|                           </Results> | ||||
|                         </TestRecord> | ||||
|                         <TestRecord Name="IsDailySeries_should_return_false"> | ||||
|                           <Results> | ||||
|                             <UnitTestResult> | ||||
|                               <TestDate>2013-02-16T21:37:42</TestDate> | ||||
|                               <Passed>0</Passed> | ||||
|                               <Errors>0</Errors> | ||||
|                               <Failures>0</Failures> | ||||
|                               <Inconclusive>0</Inconclusive> | ||||
|                               <NotRunnable>0</NotRunnable> | ||||
|                               <Skipped>0</Skipped> | ||||
|                               <Ignored>1</Ignored> | ||||
|                               <Time /> | ||||
|                               <Message>SetUp : SqlCe is not supported in mono.</Message> | ||||
|                               <StackTrace>  at NzbDrone.Core.Test.Framework.SqlCeTest.CoreTestSetup () [0x00000] in <filename unknown>:0  | ||||
|   at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) | ||||
|   at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d5] in C:\cygwin\tmp\monobuild\build\BUILD\mono-2.10.9\mcs\class\corlib\System.Reflection\MonoMethod.cs:226 </StackTrace> | ||||
|                               <ConsoleOutput /> | ||||
|                               <ConsoleError /> | ||||
|                             </UnitTestResult> | ||||
|                           </Results> | ||||
|                         </TestRecord> | ||||
|                         <TestRecord Name="IsDailySeries_should_return_true"> | ||||
|                           <Results> | ||||
|                             <UnitTestResult> | ||||
|                               <TestDate>2013-02-16T21:37:42</TestDate> | ||||
|                               <Passed>0</Passed> | ||||
|                               <Errors>0</Errors> | ||||
|                               <Failures>0</Failures> | ||||
|                               <Inconclusive>0</Inconclusive> | ||||
|                               <NotRunnable>0</NotRunnable> | ||||
|                               <Skipped>0</Skipped> | ||||
|                               <Ignored>1</Ignored> | ||||
|                               <Time /> | ||||
|                               <Message>SetUp : SqlCe is not supported in mono.</Message> | ||||
|                               <StackTrace>  at NzbDrone.Core.Test.Framework.SqlCeTest.CoreTestSetup () [0x00000] in <filename unknown>:0  | ||||
|   at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) | ||||
|   at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d5] in C:\cygwin\tmp\monobuild\build\BUILD\mono-2.10.9\mcs\class\corlib\System.Reflection\MonoMethod.cs:226 </StackTrace> | ||||
|                               <ConsoleOutput /> | ||||
|                               <ConsoleError /> | ||||
|                             </UnitTestResult> | ||||
|                           </Results> | ||||
|                         </TestRecord> | ||||
|                         <TestRecord Name="UpdateDailySeries_should_update_series_should_not_overwrite_existing_isDaily"> | ||||
|                           <Results> | ||||
|                             <UnitTestResult> | ||||
|                               <TestDate>2013-02-16T21:37:42</TestDate> | ||||
|                               <Passed>0</Passed> | ||||
|                               <Errors>0</Errors> | ||||
|                               <Failures>0</Failures> | ||||
|                               <Inconclusive>0</Inconclusive> | ||||
|                               <NotRunnable>0</NotRunnable> | ||||
|                               <Skipped>0</Skipped> | ||||
|                               <Ignored>1</Ignored> | ||||
|                               <Time /> | ||||
|                               <Message>SetUp : SqlCe is not supported in mono.</Message> | ||||
|                               <StackTrace>  at NzbDrone.Core.Test.Framework.SqlCeTest.CoreTestSetup () [0x00000] in <filename unknown>:0  | ||||
|   at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) | ||||
|   at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d5] in C:\cygwin\tmp\monobuild\build\BUILD\mono-2.10.9\mcs\class\corlib\System.Reflection\MonoMethod.cs:226 </StackTrace> | ||||
|                               <ConsoleOutput /> | ||||
|                               <ConsoleError /> | ||||
|                             </UnitTestResult> | ||||
|                           </Results> | ||||
|                         </TestRecord> | ||||
|                         <TestRecord Name="UpdateDailySeries_should_update_series_should_skip_series_that_dont_match"> | ||||
|                           <Results> | ||||
|                             <UnitTestResult> | ||||
|                               <TestDate>2013-02-16T21:37:42</TestDate> | ||||
|                               <Passed>0</Passed> | ||||
|                               <Errors>0</Errors> | ||||
|                               <Failures>0</Failures> | ||||
|                               <Inconclusive>0</Inconclusive> | ||||
|                               <NotRunnable>0</NotRunnable> | ||||
|                               <Skipped>0</Skipped> | ||||
|                               <Ignored>1</Ignored> | ||||
|                               <Time /> | ||||
|                               <Message>SetUp : SqlCe is not supported in mono.</Message> | ||||
|                               <StackTrace>  at NzbDrone.Core.Test.Framework.SqlCeTest.CoreTestSetup () [0x00000] in <filename unknown>:0  | ||||
|   at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) | ||||
|   at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d5] in C:\cygwin\tmp\monobuild\build\BUILD\mono-2.10.9\mcs\class\corlib\System.Reflection\MonoMethod.cs:226 </StackTrace> | ||||
|                               <ConsoleOutput /> | ||||
|                               <ConsoleError /> | ||||
|                             </UnitTestResult> | ||||
|                           </Results> | ||||
|                         </TestRecord> | ||||
|                         <TestRecord Name="UpdateDailySeries_should_update_series_that_match_daily_series_list"> | ||||
|                           <Results> | ||||
|                             <UnitTestResult> | ||||
|                               <TestDate>2013-02-16T21:37:42</TestDate> | ||||
|                               <Passed>0</Passed> | ||||
|                               <Errors>0</Errors> | ||||
|                               <Failures>0</Failures> | ||||
|                               <Inconclusive>0</Inconclusive> | ||||
|                               <NotRunnable>0</NotRunnable> | ||||
|                               <Skipped>0</Skipped> | ||||
|                               <Ignored>1</Ignored> | ||||
|                               <Time /> | ||||
|                               <Message>SetUp : SqlCe is not supported in mono.</Message> | ||||
|                               <StackTrace>  at NzbDrone.Core.Test.Framework.SqlCeTest.CoreTestSetup () [0x00000] in <filename unknown>:0  | ||||
|   at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) | ||||
|   at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d5] in C:\cygwin\tmp\monobuild\build\BUILD\mono-2.10.9\mcs\class\corlib\System.Reflection\MonoMethod.cs:226 </StackTrace> | ||||
|                               <ConsoleOutput /> | ||||
|                               <ConsoleError /> | ||||
|                             </UnitTestResult> | ||||
|                           </Results> | ||||
|                         </TestRecord> | ||||
|                       </Tests> | ||||
|                     </TestRecord> | ||||
|                   </Tests> | ||||
|                 </TestRecord> | ||||
|               </Tests> | ||||
|             </TestRecord> | ||||
|           </Tests> | ||||
|         </TestRecord> | ||||
|       </Tests> | ||||
|     </TestRecord> | ||||
|   </Tests> | ||||
| </TestRecord> | ||||
| @@ -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<int> 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<Int32> GetTvDbId(string cleanName) | ||||
|  | ||||
|  | ||||
|         public Nullable<Int32> 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); | ||||
|  | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
| @@ -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<Config>().RegisterModel("Config"); | ||||
|             Mapper.Entity<RootFolder>().RegisterModel("RootFolders").Ignore(r => r.FreeSpace); | ||||
|  | ||||
|             Mapper.Entity<Indexer>().RegisterModel("IndexerDefinitions"); | ||||
|             Mapper.Entity<IndexerDefinition>().RegisterModel("IndexerDefinitions"); | ||||
|             Mapper.Entity<NewznabDefinition>().RegisterModel("NewznabDefinitions"); | ||||
|             Mapper.Entity<JobDefinition>().RegisterModel("JobDefinitions"); | ||||
|             Mapper.Entity<ExternalNotificationDefinition>().RegisterModel("ExternalNotificationDefinitions"); | ||||
|   | ||||
| @@ -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<string> 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<DownloadDecision> 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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										64
									
								
								NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<DownloadDecision> GetRssDecision(IEnumerable<EpisodeParseResult> episodeParseResults); | ||||
|         IEnumerable<DownloadDecision> GetSearchDecision(IEnumerable<EpisodeParseResult> episodeParseResult, SearchDefinitionBase searchDefinitionBase); | ||||
|     } | ||||
|  | ||||
|     public class DownloadDecisionMaker : IMakeDownloadDecision | ||||
|     { | ||||
|         private readonly IEnumerable<IRejectWithReason> _specifications; | ||||
|  | ||||
|         public DownloadDecisionMaker(IEnumerable<IRejectWithReason> specifications) | ||||
|         { | ||||
|             _specifications = specifications; | ||||
|         } | ||||
|  | ||||
|         public IEnumerable<DownloadDecision> GetRssDecision(IEnumerable<EpisodeParseResult> episodeParseResults) | ||||
|         { | ||||
|             foreach (var parseResult in episodeParseResults) | ||||
|             { | ||||
|                 parseResult.Decision = new DownloadDecision(parseResult, GetGeneralRejectionReasons(parseResult).ToArray()); | ||||
|                 yield return parseResult.Decision; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public IEnumerable<DownloadDecision> GetSearchDecision(IEnumerable<EpisodeParseResult> 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<string> GetGeneralRejectionReasons(EpisodeParseResult episodeParseResult) | ||||
|         { | ||||
|             return _specifications | ||||
|                 .OfType<IDecisionEngineSpecification>() | ||||
|                 .Where(spec => !spec.IsSatisfiedBy(episodeParseResult)) | ||||
|                 .Select(spec => spec.RejectionReason); | ||||
|         } | ||||
|  | ||||
|         private IEnumerable<string> GetSearchRejectionReasons(EpisodeParseResult episodeParseResult, SearchDefinitionBase searchDefinitionBase) | ||||
|         { | ||||
|             return _specifications | ||||
|                 .OfType<IDecisionEngineSearchSpecification>() | ||||
|                 .Where(spec => !spec.IsSatisfiedBy(episodeParseResult, searchDefinitionBase)) | ||||
|                 .Select(spec => spec.RejectionReason); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<IFetchableSpecification> _specifications; | ||||
|  | ||||
|         public DownloadDirector(IEnumerable<IFetchableSpecification> 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										7
									
								
								NzbDrone.Core/DecisionEngine/IRejectWithReason.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								NzbDrone.Core/DecisionEngine/IRejectWithReason.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| namespace NzbDrone.Core.DecisionEngine | ||||
| { | ||||
|     public interface IRejectWithReason | ||||
|     { | ||||
|         string RejectionReason { get; } | ||||
|     } | ||||
| } | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
| @@ -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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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)); | ||||
|             } | ||||
|  | ||||
|   | ||||
| @@ -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<EpisodeParseResult> PerformSearch(Series series, List<Episode> episodes, ProgressNotification notification) | ||||
|         { | ||||
|             var episode = episodes.Single(); | ||||
|  | ||||
|             notification.CurrentMessage = "Looking for " + episode; | ||||
|  | ||||
|             var reports = new List<EpisodeParseResult>(); | ||||
|             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; | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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('+', ' '); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<EpisodeParseResult> PerformSearch(Series series, List<Episode> 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<EpisodeParseResult>(); | ||||
|             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; | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -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<EpisodeParseResult> PerformSearch(Series series, List<Episode> episodes, ProgressNotification notification); | ||||
|         public abstract bool IsEpisodeMatch(Series series, dynamic options, EpisodeParseResult episodeParseResult); | ||||
|  | ||||
|         public virtual List<int> Search(Series series, dynamic options, ProgressNotification notification) | ||||
|         { | ||||
|             if (options == null) | ||||
|                 throw new ArgumentNullException(options); | ||||
|  | ||||
|  | ||||
|             List<EpisodeParseResult> 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<EpisodeParseResult> 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										136
									
								
								NzbDrone.Core/IndexerSearch/NzbSearchService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								NzbDrone.Core/IndexerSearch/NzbSearchService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<DownloadDecision> SearchSingle(int seriesId, int seasonNumber, int episodeNumber); | ||||
|         List<DownloadDecision> SearchDaily(int seriesId, DateTime airDate); | ||||
|         List<DownloadDecision> 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<DownloadDecision> SearchSingle(int seriesId, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             var searchSpec = Get<SingleEpisodeSearchDefinition>(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<DownloadDecision> SearchDaily(int seriesId, DateTime airDate) | ||||
|         { | ||||
|             var searchSpec = Get<DailyEpisodeSearchDefinition>(seriesId); | ||||
|             searchSpec.Airtime = airDate; | ||||
|  | ||||
|             return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); | ||||
|         } | ||||
|  | ||||
|         public List<DownloadDecision> SearchSeason(int seriesId, int seasonNumber) | ||||
|         { | ||||
|             var searchSpec = Get<SeasonSearchDefinition>(seriesId, seasonNumber); | ||||
|             searchSpec.SeasonNumber = seasonNumber; | ||||
|  | ||||
|             return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); | ||||
|         } | ||||
|  | ||||
|         private List<DownloadDecision> 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<DownloadDecision>(); | ||||
|  | ||||
|             foreach (var partialSeasonSearchSpec in prefixes) | ||||
|             { | ||||
|                 var spec = partialSeasonSearchSpec; | ||||
|                 result.AddRange(Dispatch(indexer => _feedFetcher.Fetch(indexer, spec), partialSeasonSearchSpec)); | ||||
|             } | ||||
|  | ||||
|  | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         private TSpec Get<TSpec>(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<DownloadDecision> Dispatch(Func<IIndexerBase, IEnumerable<EpisodeParseResult>> searchAction, SearchDefinitionBase definitionBase) | ||||
|         { | ||||
|             var indexers = _indexerService.GetAvailableIndexers(); | ||||
|             var parseResults = new List<EpisodeParseResult>(); | ||||
|  | ||||
|             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(); | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -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<EpisodeParseResult> PerformSearch(Series series, List<Episode> 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<EpisodeParseResult>(); | ||||
|             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<int> GetEpisodeNumberPrefixes(IEnumerable<int> episodeNumbers) | ||||
|         { | ||||
|             var results = new List<int>(); | ||||
|  | ||||
|             foreach (var i in episodeNumbers) | ||||
|             { | ||||
|                 results.Add(i / 10); | ||||
|             } | ||||
|  | ||||
|             return results.Distinct().ToList(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										41
									
								
								NzbDrone.Core/IndexerSearch/SearchAndDownloadService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								NzbDrone.Core/IndexerSearch/SearchAndDownloadService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										60
									
								
								NzbDrone.Core/Indexers/BaseIndexer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								NzbDrone.Core/Indexers/BaseIndexer.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
|  | ||||
| namespace NzbDrone.Core.Indexers | ||||
| { | ||||
|     public interface IIndexerBase | ||||
|     { | ||||
|         string Name { get; } | ||||
|         bool EnabledByDefault { get; } | ||||
|  | ||||
|         IEnumerable<string> RecentFeed { get; } | ||||
|  | ||||
|         IParseFeed Parser { get; } | ||||
|  | ||||
|         IIndexerSetting Settings { get; } | ||||
|  | ||||
|         IEnumerable<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber); | ||||
|         IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date); | ||||
|         IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber); | ||||
|         IEnumerable<string> 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<string> RecentFeed { get; } | ||||
|  | ||||
|         public abstract IEnumerable<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber); | ||||
|         public abstract IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date); | ||||
|         public abstract IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber); | ||||
|         public abstract IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										91
									
								
								NzbDrone.Core/Indexers/BasicRssParser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								NzbDrone.Core/Indexers/BasicRssParser.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<EpisodeParseResult> Process(Stream source); | ||||
|     } | ||||
|  | ||||
|     public class BasicRssParser : IParseFeed | ||||
|     { | ||||
|         private readonly Logger _logger; | ||||
|  | ||||
|         public BasicRssParser() | ||||
|         { | ||||
|             _logger = LogManager.GetCurrentClassLogger(); | ||||
|         } | ||||
|  | ||||
|         public IEnumerable<EpisodeParseResult> Process(Stream source) | ||||
|         { | ||||
|             var reader = new SyndicationFeedXmlReader(source); | ||||
|             var feed = SyndicationFeed.Load(reader).Items; | ||||
|  | ||||
|             var result = new List<EpisodeParseResult>(); | ||||
|  | ||||
|             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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										65
									
								
								NzbDrone.Core/Indexers/FileSharingTalk/FileSharingTalk.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								NzbDrone.Core/Indexers/FileSharingTalk/FileSharingTalk.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<FileSharingTalkSetting>(this); | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> 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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										19
									
								
								NzbDrone.Core/Indexers/IIndexerSettings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								NzbDrone.Core/Indexers/IIndexerSettings.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| namespace NzbDrone.Core.Indexers | ||||
| { | ||||
|     public interface IIndexerSetting | ||||
|     { | ||||
|         bool IsValid { get; } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public class NullSetting : IIndexerSetting | ||||
|     { | ||||
|         public bool IsValid | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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() | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         ///   Gets the name for the feed | ||||
|         /// </summary> | ||||
|         public abstract string Name { get; } | ||||
|  | ||||
|         /// <summary> | ||||
|         ///   Gets the source URL for the feed | ||||
|         /// </summary> | ||||
|         protected abstract string[] Urls { get; } | ||||
|  | ||||
|         public abstract bool IsConfigured { get; } | ||||
|  | ||||
|         /// <summary> | ||||
|         ///   Should the indexer be enabled by default? | ||||
|         /// </summary> | ||||
|         public virtual bool EnabledByDefault | ||||
|         { | ||||
|             get { return false; } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the credential. | ||||
|         /// </summary> | ||||
|         protected virtual NetworkCredential Credentials | ||||
|         { | ||||
|             get { return null; } | ||||
|         } | ||||
|  | ||||
|         protected abstract IEnumerable<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber); | ||||
|         protected abstract IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date); | ||||
|         protected abstract IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber); | ||||
|         protected abstract IEnumerable<string> 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<EpisodeParseResult> FetchRss() | ||||
|         { | ||||
|             _logger.Debug("Fetching feeds from " + Name); | ||||
|  | ||||
|             var result = new List<EpisodeParseResult>(); | ||||
|  | ||||
|  | ||||
|             result = Fetch(Urls); | ||||
|  | ||||
|  | ||||
|             _logger.Debug("Finished processing feeds from " + Name); | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         public virtual IList<EpisodeParseResult> 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<EpisodeParseResult> 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<EpisodeParseResult> 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<EpisodeParseResult> 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<EpisodeParseResult> Fetch(IEnumerable<string> urls) | ||||
|         { | ||||
|             var result = new List<EpisodeParseResult>(); | ||||
|  | ||||
|             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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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; } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										129
									
								
								NzbDrone.Core/Indexers/IndexerFetchService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								NzbDrone.Core/Indexers/IndexerFetchService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<EpisodeParseResult> FetchRss(IIndexerBase indexer); | ||||
|  | ||||
|         IList<EpisodeParseResult> Fetch(IIndexerBase indexer, SeasonSearchDefinition searchDefinition); | ||||
|         IList<EpisodeParseResult> Fetch(IIndexerBase indexer, SingleEpisodeSearchDefinition searchDefinition); | ||||
|         IList<EpisodeParseResult> Fetch(IIndexerBase indexer, PartialSeasonSearchDefinition searchDefinition); | ||||
|         IList<EpisodeParseResult> 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<EpisodeParseResult> 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<EpisodeParseResult> 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<EpisodeParseResult> 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<EpisodeParseResult> 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<EpisodeParseResult> 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<EpisodeParseResult> Fetch(IIndexerBase indexer, IEnumerable<string> urls) | ||||
|         { | ||||
|             var result = new List<EpisodeParseResult>(); | ||||
|  | ||||
|             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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<Indexer> | ||||
|     public interface IIndexerRepository : IBasicRepository<IndexerDefinition> | ||||
|     { | ||||
|         Indexer Find(Type type); | ||||
|         IndexerDefinition Get(string name); | ||||
|     } | ||||
|  | ||||
|     public class IndexerRepository : BasicRepository<Indexer>, IIndexerRepository | ||||
|     public class IndexerRepository : BasicRepository<IndexerDefinition>, 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()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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<Indexer> All(); | ||||
|         List<IndexerBase> GetEnabledIndexers(); | ||||
|         void SaveSettings(Indexer indexer); | ||||
|         Indexer GetSettings(Type type); | ||||
|         List<IndexerDefinition> All(); | ||||
|         List<IIndexerBase> 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<IndexerBase> _indexers; | ||||
|         private readonly IList<IIndexerBase> _indexers; | ||||
|  | ||||
|         public IndexerService(IIndexerRepository indexerRepository, IEnumerable<IndexerBase> indexers, Logger logger) | ||||
|         public IndexerService(IIndexerRepository indexerRepository, IEnumerable<IIndexerBase> 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<Indexer> All() | ||||
|         public List<IndexerDefinition> All() | ||||
|         { | ||||
|             return _indexerRepository.All().ToList(); | ||||
|         } | ||||
|  | ||||
|         public List<IndexerBase> GetEnabledIndexers() | ||||
|         public List<IIndexerBase> 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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										25
									
								
								NzbDrone.Core/Indexers/IndexerSettingProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								NzbDrone.Core/Indexers/IndexerSettingProvider.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| using Newtonsoft.Json; | ||||
|  | ||||
| namespace NzbDrone.Core.Indexers | ||||
| { | ||||
|     public interface IProviderIndexerSetting | ||||
|     { | ||||
|         TSetting Get<TSetting>(IIndexerBase indexer) where TSetting : IIndexerSetting, new(); | ||||
|     } | ||||
|  | ||||
|     public class IndexerSettingProvider : IProviderIndexerSetting | ||||
|     { | ||||
|         private readonly IIndexerRepository _indexerRepository; | ||||
|  | ||||
|         public IndexerSettingProvider(IIndexerRepository indexerRepository) | ||||
|         { | ||||
|             _indexerRepository = indexerRepository; | ||||
|         } | ||||
|  | ||||
|         public TSetting Get<TSetting>(IIndexerBase indexer) where TSetting : IIndexerSetting, new() | ||||
|         { | ||||
|             var json = _indexerRepository.Get(indexer.Name).Settings; | ||||
|             return JsonConvert.DeserializeObject<TSetting>(json); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             var searchUrls = new List<string>(); | ||||
|  | ||||
|             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<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             var searchUrls = new List<string>(); | ||||
|  | ||||
|             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<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             var searchUrls = new List<string>(); | ||||
|  | ||||
|             foreach (var url in Urls) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}&limit=100&q={1}&season={2}", url, seriesTitle, seasonNumber)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) | ||||
|         { | ||||
|             var searchUrls = new List<string>(); | ||||
|  | ||||
|             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<string>(); | ||||
|             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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										91
									
								
								NzbDrone.Core/Indexers/Newznab/Newznab.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								NzbDrone.Core/Indexers/Newznab/Newznab.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<string> RecentFeed | ||||
|         { | ||||
|             get { return GetUrls(); } | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> 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<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             var searchUrls = new List<string>(); | ||||
|  | ||||
|             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<string> 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<string> 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<string> GetUrls() | ||||
|         { | ||||
|             var urls = new List<string>(); | ||||
|             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"); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -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; } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										41
									
								
								NzbDrone.Core/Indexers/Newznab/NewznabParser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								NzbDrone.Core/Indexers/Newznab/NewznabParser.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<NewznabDefinition> | ||||
|     { | ||||
| @@ -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<NewznabDefinition> | ||||
|                                       { | ||||
|                                               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); | ||||
|                         } | ||||
|                     } | ||||
| @@ -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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             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<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             foreach (var url in Urls) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}+{1}+s{2:00}", url, seriesTitle, seasonNumber)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             foreach (var url in Urls) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}+{1}+{2:yyyy MM dd}", url, seriesTitle, date)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										81
									
								
								NzbDrone.Core/Indexers/NzbClub/NzbClub.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								NzbDrone.Core/Indexers/NzbClub/NzbClub.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<string> 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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             var searchUrls = new List<string>(); | ||||
|  | ||||
|             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<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             var searchUrls = new List<string>(); | ||||
|  | ||||
|             foreach (var url in RecentFeed) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}+{1}+s{2:00}", url, seriesTitle, seasonNumber)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             foreach (var url in RecentFeed) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}+{1}+{2:yyyy MM dd}", url, seriesTitle, date)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             foreach (var url in RecentFeed) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}+{1}+S{2:00}E{3}", url, seriesTitle, seasonNumber, episodeWildcard)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										36
									
								
								NzbDrone.Core/Indexers/NzbClub/NzbClubParser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								NzbDrone.Core/Indexers/NzbClub/NzbClubParser.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             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<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             foreach (var url in Urls) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}+{1}+s{2:00}", url, seriesTitle, seasonNumber)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             foreach (var url in Urls) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}+{1}+{2:yyyy MM dd}", url, seriesTitle, date)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             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, @"<b>\d+\.\d{1,2}\s\w{2}</b><br\s/>", 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										83
									
								
								NzbDrone.Core/Indexers/NzbIndex/NzbIndex.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								NzbDrone.Core/Indexers/NzbIndex/NzbIndex.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
|  | ||||
| namespace NzbDrone.Core.Indexers.NzbIndex | ||||
| { | ||||
|     public class NzbIndex : BaseIndexer | ||||
|     { | ||||
|         public override IEnumerable<string> 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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             var searchUrls = new List<string>(); | ||||
|  | ||||
|             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<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             var searchUrls = new List<string>(); | ||||
|  | ||||
|             foreach (var url in RecentFeed) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}+{1}+s{2:00}", url, seriesTitle, seasonNumber)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             foreach (var url in RecentFeed) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}+{1}+{2:yyyy MM dd}", url, seriesTitle, date)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             foreach (var url in RecentFeed) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}+{1}+S{2:00}E{3}", url, seriesTitle, seasonNumber, episodeWildcard)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|        | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										42
									
								
								NzbDrone.Core/Indexers/NzbIndex/NzbIndexParser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								NzbDrone.Core/Indexers/NzbIndex/NzbIndexParser.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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, @"<b>\d+\.\d{1,2}\s\w{2}</b><br\s/>", 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										59
									
								
								NzbDrone.Core/Indexers/NzbsRUs/NzbsRUs.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								NzbDrone.Core/Indexers/NzbsRUs/NzbsRUs.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<NzbsrusSetting>(this); | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> 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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										20
									
								
								NzbDrone.Core/Indexers/NzbsRUs/NzbsrusParser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								NzbDrone.Core/Indexers/NzbsRUs/NzbsrusParser.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										18
									
								
								NzbDrone.Core/Indexers/NzbsRUs/NzbsrusSettings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								NzbDrone.Core/Indexers/NzbsRUs/NzbsrusSettings.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             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<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             searchUrls.Add(String.Format("https://nzbx.co/api/search?q={0}+{1:yyyy MM dd}", seriesTitle, date)); | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             searchUrls.Add(String.Format("https://nzbx.co/api/search?q={0}+S{1:00}", seriesTitle, seasonNumber)); | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             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<EpisodeParseResult> FetchRss() | ||||
|         { | ||||
|             _logger.Debug("Fetching feeds from " + Name); | ||||
|  | ||||
|             var result = new List<EpisodeParseResult>(); | ||||
|  | ||||
|             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<List<NzbxRecentItem>>(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<EpisodeParseResult> Fetch(IEnumerable<string> urls) | ||||
|         { | ||||
|             var result = new List<EpisodeParseResult>(); | ||||
|              | ||||
|             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<List<NzbxSearchItem>>(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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										42
									
								
								NzbDrone.Core/Indexers/Nzbx/Nzbx.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								NzbDrone.Core/Indexers/Nzbx/Nzbx.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<string> RecentFeed | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return new[] { String.Format("https://nzbx.co/api/recent?category=tv") }; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public override IEnumerable<string> 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<string> 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<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             yield return String.Format("https://nzbx.co/api/search?q={0}+S{1:00}", seriesTitle, seasonNumber); | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> 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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										55
									
								
								NzbDrone.Core/Indexers/Nzbx/NzbxParser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								NzbDrone.Core/Indexers/Nzbx/NzbxParser.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<EpisodeParseResult> Process(Stream source) | ||||
|         { | ||||
|             var result = new List<EpisodeParseResult>(); | ||||
|             var jsonReader = new JsonTextReader(new StreamReader(source)); | ||||
|             var feed = _serializer.Deserialize<List<NzbxRecentItem>>(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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             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<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             foreach (var url in Urls) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}&search={1}+{2:yyyy MM dd}", url, seriesTitle, date)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             foreach (var url in Urls) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}&search={1}+S{2:00}", url, seriesTitle, seasonNumber)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             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, @"(?:\<b\>View NZB\:\<\/b\>\s\<a\shref\=\"")(?<URL>.+)(?:\""\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}\<br \/\>", RegexOptions.IgnoreCase | RegexOptions.Compiled).Value; | ||||
|                 currentResult.Size = Parser.GetReportSize(sizeString); | ||||
|             } | ||||
|  | ||||
|             return currentResult; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										86
									
								
								NzbDrone.Core/Indexers/Omgwtfnzbs/Omgwtfnzbs.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								NzbDrone.Core/Indexers/Omgwtfnzbs/Omgwtfnzbs.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<OmgwtfnzbsSetting>(this); | ||||
|         } | ||||
|  | ||||
|         public override string Name | ||||
|         { | ||||
|             get { return "omgwtfnzbs"; } | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> 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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             var searchUrls = new List<string>(); | ||||
|  | ||||
|             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<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             foreach (var url in RecentFeed) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}&search={1}+{2:yyyy MM dd}", url, seriesTitle, date)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             foreach (var url in RecentFeed) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}&search={1}+S{2:00}", url, seriesTitle, seasonNumber)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) | ||||
|         { | ||||
|             var searchUrls = new List<String>(); | ||||
|  | ||||
|             foreach (var url in RecentFeed) | ||||
|             { | ||||
|                 searchUrls.Add(String.Format("{0}&search={1}+S{2:00}E{3}", url, seriesTitle, seasonNumber, episodeWildcard)); | ||||
|             } | ||||
|  | ||||
|             return searchUrls; | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										40
									
								
								NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsParser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsParser.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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, @"(?:\<b\>View NZB\:\<\/b\>\s\<a\shref\=\"")(?<URL>.+)(?:\""\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}\<br \/\>", RegexOptions.IgnoreCase | RegexOptions.Compiled).Value; | ||||
|                 currentResult.Size = Parser.GetReportSize(sizeString); | ||||
|             } | ||||
|  | ||||
|             return currentResult; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										18
									
								
								NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsSettings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsSettings.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										101
									
								
								NzbDrone.Core/Indexers/RssSyncService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								NzbDrone.Core/Indexers/RssSyncService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<EpisodeParseResult> 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<EpisodeParseResult> Fetch() | ||||
|         { | ||||
|             var result = new List<EpisodeParseResult>(); | ||||
|  | ||||
|             var indexers = _indexerService.GetAvailableIndexers(); | ||||
|  | ||||
|             Parallel.ForEach(indexers, indexer => | ||||
|             { | ||||
|                 var indexerFeed = _feedFetcher.FetchRss(indexer); | ||||
|  | ||||
|                 lock (result) | ||||
|                 { | ||||
|                     result.AddRange(indexerFeed); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             return result; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         protected override IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         protected override EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult) | ||||
|         { | ||||
|             if (currentResult != null) | ||||
|             { | ||||
|                 currentResult.Size = 0; | ||||
|             } | ||||
|  | ||||
|             return currentResult; | ||||
|         } | ||||
|  | ||||
|         public override bool EnabledByDefault | ||||
|         { | ||||
|             get { return true; } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										52
									
								
								NzbDrone.Core/Indexers/Wombles/Wombles.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								NzbDrone.Core/Indexers/Wombles/Wombles.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
|  | ||||
| namespace NzbDrone.Core.Indexers.Wombles | ||||
| { | ||||
|     public class Wombles : BaseIndexer | ||||
|     { | ||||
|         public override IEnumerable<string> RecentFeed | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return new[] | ||||
|                            { | ||||
|                                string.Format("http://nzb.isasecret.com/rss") | ||||
|                            }; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public override string Name | ||||
|         { | ||||
|             get { return "WomblesIndex"; } | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         public override IEnumerable<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|         public override IEnumerable<string> GetPartialSeasonSearchUrls(string seriesTitle, int seasonNumber, int episodeWildcard) | ||||
|         { | ||||
|             return new List<string>(); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public bool EnabledByDefault | ||||
|         { | ||||
|             get { return true; } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										28
									
								
								NzbDrone.Core/Indexers/Wombles/WomblesParser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								NzbDrone.Core/Indexers/Wombles/WomblesParser.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user