mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-04 10:02:40 +01:00
rewrite of indexer/episode search
This commit is contained in:
parent
9ae21cf7a1
commit
a6a4932b44
@ -49,7 +49,7 @@ public virtual string DownloadString(string address, ICredentials identity)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Stream DownloadStream(string url, NetworkCredential credential)
|
public virtual Stream DownloadStream(string url, NetworkCredential credential = null)
|
||||||
{
|
{
|
||||||
var request = (HttpWebRequest)WebRequest.Create(url);
|
var request = (HttpWebRequest)WebRequest.Create(url);
|
||||||
request.UserAgent = _userAgent;
|
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 FluentAssertions;
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -9,28 +10,28 @@
|
|||||||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class AllowedDownloadSpecificationFixture : CoreTest<DownloadDirector>
|
public class AllowedDownloadSpecificationFixture : CoreTest<DownloadDecisionMaker>
|
||||||
{
|
{
|
||||||
private EpisodeParseResult _parseResult;
|
private List<EpisodeParseResult> _parseResults;
|
||||||
|
|
||||||
private Mock<IFetchableSpecification> _pass1;
|
private Mock<IDecisionEngineSpecification> _pass1;
|
||||||
private Mock<IFetchableSpecification> _pass2;
|
private Mock<IDecisionEngineSpecification> _pass2;
|
||||||
private Mock<IFetchableSpecification> _pass3;
|
private Mock<IDecisionEngineSpecification> _pass3;
|
||||||
|
|
||||||
private Mock<IFetchableSpecification> _fail1;
|
private Mock<IDecisionEngineSpecification> _fail1;
|
||||||
private Mock<IFetchableSpecification> _fail2;
|
private Mock<IDecisionEngineSpecification> _fail2;
|
||||||
private Mock<IFetchableSpecification> _fail3;
|
private Mock<IDecisionEngineSpecification> _fail3;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
_pass1 = new Mock<IFetchableSpecification>();
|
_pass1 = new Mock<IDecisionEngineSpecification>();
|
||||||
_pass2 = new Mock<IFetchableSpecification>();
|
_pass2 = new Mock<IDecisionEngineSpecification>();
|
||||||
_pass3 = new Mock<IFetchableSpecification>();
|
_pass3 = new Mock<IDecisionEngineSpecification>();
|
||||||
|
|
||||||
_fail1 = new Mock<IFetchableSpecification>();
|
_fail1 = new Mock<IDecisionEngineSpecification>();
|
||||||
_fail2 = new Mock<IFetchableSpecification>();
|
_fail2 = new Mock<IDecisionEngineSpecification>();
|
||||||
_fail3 = new Mock<IFetchableSpecification>();
|
_fail3 = new Mock<IDecisionEngineSpecification>();
|
||||||
|
|
||||||
_pass1.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(true);
|
_pass1.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(true);
|
||||||
_pass2.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(true);
|
_pass2.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(true);
|
||||||
@ -40,11 +41,11 @@ public void Setup()
|
|||||||
_fail2.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(false);
|
_fail2.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(false);
|
||||||
_fail3.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));
|
Mocker.SetConstant(mocks.Select(c => c.Object));
|
||||||
}
|
}
|
||||||
@ -54,14 +55,14 @@ public void should_call_all_specifications()
|
|||||||
{
|
{
|
||||||
GivenSpecifications(_pass1, _pass2, _pass3, _fail1, _fail2, _fail3);
|
GivenSpecifications(_pass1, _pass2, _pass3, _fail1, _fail2, _fail3);
|
||||||
|
|
||||||
Subject.GetDownloadDecision(_parseResult);
|
Subject.GetRssDecision(_parseResults);
|
||||||
|
|
||||||
_fail1.Verify(c => c.IsSatisfiedBy(_parseResult), Times.Once());
|
_fail1.Verify(c => c.IsSatisfiedBy(_parseResults[0]), Times.Once());
|
||||||
_fail2.Verify(c => c.IsSatisfiedBy(_parseResult), Times.Once());
|
_fail2.Verify(c => c.IsSatisfiedBy(_parseResults[0]), Times.Once());
|
||||||
_fail3.Verify(c => c.IsSatisfiedBy(_parseResult), Times.Once());
|
_fail3.Verify(c => c.IsSatisfiedBy(_parseResults[0]), Times.Once());
|
||||||
_pass1.Verify(c => c.IsSatisfiedBy(_parseResult), Times.Once());
|
_pass1.Verify(c => c.IsSatisfiedBy(_parseResults[0]), Times.Once());
|
||||||
_pass2.Verify(c => c.IsSatisfiedBy(_parseResult), Times.Once());
|
_pass2.Verify(c => c.IsSatisfiedBy(_parseResults[0]), Times.Once());
|
||||||
_pass3.Verify(c => c.IsSatisfiedBy(_parseResult), Times.Once());
|
_pass3.Verify(c => c.IsSatisfiedBy(_parseResults[0]), Times.Once());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -69,9 +70,9 @@ public void should_return_rejected_if_one_of_specs_fail()
|
|||||||
{
|
{
|
||||||
GivenSpecifications(_pass1, _fail1, _pass2, _pass3);
|
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]
|
[Test]
|
||||||
@ -79,9 +80,9 @@ public void should_return_pass_if_all_specs_pass()
|
|||||||
{
|
{
|
||||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||||
|
|
||||||
var result = Subject.GetDownloadDecision(_parseResult);
|
var result = Subject.GetRssDecision(_parseResults);
|
||||||
|
|
||||||
result.Approved.Should().BeTrue();
|
result.Single().Approved.Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -89,8 +90,18 @@ public void should_have_same_number_of_rejections_as_specs_that_failed()
|
|||||||
{
|
{
|
||||||
GivenSpecifications(_pass1, _pass2, _pass3, _fail1, _fail2, _fail3);
|
GivenSpecifications(_pass1, _pass2, _pass3, _fail1, _fail2, _fail3);
|
||||||
|
|
||||||
var result = Subject.GetDownloadDecision(_parseResult);
|
var result = Subject.GetRssDecision(_parseResults);
|
||||||
result.Rejections.Should().HaveCount(3);
|
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\DbTest.cs" />
|
||||||
<Compile Include="Framework\NBuilderExtensions.cs" />
|
<Compile Include="Framework\NBuilderExtensions.cs" />
|
||||||
<Compile Include="HelperTests\XElementHelperTests\ConvertToTFixture.cs" />
|
<Compile Include="HelperTests\XElementHelperTests\ConvertToTFixture.cs" />
|
||||||
<Compile Include="IndexerSearchTests\DailyEpisodeSearchTests\IndexerDailyEpisodeSearchFixture.cs" />
|
<Compile Include="IndexerSearchTests\SearchDefinitionFixture.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="JobTests\JobRepositoryFixture.cs" />
|
<Compile Include="JobTests\JobRepositoryFixture.cs" />
|
||||||
<Compile Include="JobTests\RenameSeasonJobFixture.cs" />
|
<Compile Include="JobTests\RenameSeasonJobFixture.cs" />
|
||||||
<Compile Include="DecisionEngineTests\LanguageSpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\LanguageSpecificationFixture.cs" />
|
||||||
@ -157,9 +147,7 @@
|
|||||||
<Compile Include="HelperTests\XElementHelperTests\ConvertToDayOfWeekFixture.cs" />
|
<Compile Include="HelperTests\XElementHelperTests\ConvertToDayOfWeekFixture.cs" />
|
||||||
<Compile Include="Qualities\QualityFixture.cs" />
|
<Compile Include="Qualities\QualityFixture.cs" />
|
||||||
<Compile Include="EpisodeParseResultTest.cs" />
|
<Compile Include="EpisodeParseResultTest.cs" />
|
||||||
<Compile Include="JobTests\BacklogSearchJobTest.cs" />
|
|
||||||
<Compile Include="JobTests\PostDownloadScanJobFixture.cs" />
|
<Compile Include="JobTests\PostDownloadScanJobFixture.cs" />
|
||||||
<Compile Include="JobTests\RecentBacklogSearchJobTest.cs" />
|
|
||||||
<Compile Include="ParserTests\QualityParserFixture.cs" />
|
<Compile Include="ParserTests\QualityParserFixture.cs" />
|
||||||
<Compile Include="Configuration\ConfigCachingFixture.cs" />
|
<Compile Include="Configuration\ConfigCachingFixture.cs" />
|
||||||
<Compile Include="DecisionEngineTests\AllowedReleaseGroupSpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\AllowedReleaseGroupSpecificationFixture.cs" />
|
||||||
@ -181,7 +169,6 @@
|
|||||||
<Compile Include="DecisionEngineTests\QualityUpgradeSpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\QualityUpgradeSpecificationFixture.cs" />
|
||||||
<Compile Include="DecisionEngineTests\QualityUpgradableSpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\QualityUpgradableSpecificationFixture.cs" />
|
||||||
<Compile Include="ProviderTests\NotificationProviderTests\NotificationProviderFixture.cs" />
|
<Compile Include="ProviderTests\NotificationProviderTests\NotificationProviderFixture.cs" />
|
||||||
<Compile Include="Indexers\NewznabServiceTest.cs" />
|
|
||||||
<Compile Include="ProviderTests\DiskProviderTests\FreeDiskSpaceTest.cs" />
|
<Compile Include="ProviderTests\DiskProviderTests\FreeDiskSpaceTest.cs" />
|
||||||
<Compile Include="ProviderTests\ProwlProviderTest.cs" />
|
<Compile Include="ProviderTests\ProwlProviderTest.cs" />
|
||||||
<Compile Include="ProviderTests\GrowlProviderTest.cs" />
|
<Compile Include="ProviderTests\GrowlProviderTest.cs" />
|
||||||
@ -199,8 +186,6 @@
|
|||||||
<Compile Include="DecisionEngineTests\AcceptableSizeSpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\AcceptableSizeSpecificationFixture.cs" />
|
||||||
<Compile Include="Qualities\QualitySizeServiceFixture.cs" />
|
<Compile Include="Qualities\QualitySizeServiceFixture.cs" />
|
||||||
<Compile Include="ProviderTests\MisnamedProviderTest.cs" />
|
<Compile Include="ProviderTests\MisnamedProviderTest.cs" />
|
||||||
<Compile Include="JobTests\SeasonSearchJobTest.cs" />
|
|
||||||
<Compile Include="JobTests\SeriesSearchJobTest.cs" />
|
|
||||||
<Compile Include="ProviderTests\EventClientProviderTest.cs" />
|
<Compile Include="ProviderTests\EventClientProviderTest.cs" />
|
||||||
<Compile Include="ProviderTests\XbmcProviderTest.cs" />
|
<Compile Include="ProviderTests\XbmcProviderTest.cs" />
|
||||||
<Compile Include="TvTests\EpisodeProviderTests\EpisodeProviderTest_GetEpisodesByParseResult.cs" />
|
<Compile Include="TvTests\EpisodeProviderTests\EpisodeProviderTest_GetEpisodesByParseResult.cs" />
|
||||||
@ -211,12 +196,10 @@
|
|||||||
<Compile Include="DecisionEngineTests\MonitoredEpisodeSpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\MonitoredEpisodeSpecificationFixture.cs" />
|
||||||
<Compile Include="EpisodeStatusTest.cs" />
|
<Compile Include="EpisodeStatusTest.cs" />
|
||||||
<Compile Include="JobTests\DiskScanJobTest.cs" />
|
<Compile Include="JobTests\DiskScanJobTest.cs" />
|
||||||
<Compile Include="IndexerTests\IndexerFixture.cs" />
|
|
||||||
<Compile Include="DecisionEngineTests\AllowedDownloadSpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\AllowedDownloadSpecificationFixture.cs" />
|
||||||
<Compile Include="JobTests\JobControllerFixture.cs" />
|
<Compile Include="JobTests\JobControllerFixture.cs" />
|
||||||
<Compile Include="TvTests\QualityModelFixture.cs" />
|
<Compile Include="TvTests\QualityModelFixture.cs" />
|
||||||
<Compile Include="RootFolderTests\RootFolderServiceFixture.cs" />
|
<Compile Include="RootFolderTests\RootFolderServiceFixture.cs" />
|
||||||
<Compile Include="Indexers\IndexerServiceTest.cs" />
|
|
||||||
<Compile Include="HistoryTests\HistoryRepositoryFixture.cs" />
|
<Compile Include="HistoryTests\HistoryRepositoryFixture.cs" />
|
||||||
<Compile Include="MediaFileTests\MediaFileServiceTest.cs" />
|
<Compile Include="MediaFileTests\MediaFileServiceTest.cs" />
|
||||||
<Compile Include="Configuration\ConfigServiceFixture.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.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.Lifecycle;
|
using NzbDrone.Core.Lifecycle;
|
||||||
|
using NzbDrone.Core.Tv;
|
||||||
|
|
||||||
namespace NzbDrone.Core.DataAugmentation.Scene
|
namespace NzbDrone.Core.DataAugmentation.Scene
|
||||||
{
|
{
|
||||||
public interface ISceneMappingService
|
public interface ISceneMappingService
|
||||||
{
|
{
|
||||||
void UpdateMappings();
|
void UpdateMappings();
|
||||||
string GetSceneName(int tvdbId, int seasonNumber = -1);
|
string GetSceneName(int seriesId, int seasonNumber = -1);
|
||||||
Nullable<int> GetTvDbId(string cleanName);
|
Nullable<int> GetTvDbId(string cleanName);
|
||||||
string GetCleanName(int tvdbId);
|
string GetCleanName(int tvdbId);
|
||||||
}
|
}
|
||||||
@ -17,12 +18,14 @@ public class SceneMappingService : IInitializable, ISceneMappingService
|
|||||||
{
|
{
|
||||||
private readonly ISceneMappingRepository _repository;
|
private readonly ISceneMappingRepository _repository;
|
||||||
private readonly ISceneMappingProxy _sceneMappingProxy;
|
private readonly ISceneMappingProxy _sceneMappingProxy;
|
||||||
|
private readonly ISeriesService _seriesService;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public SceneMappingService(ISceneMappingRepository repository, ISceneMappingProxy sceneMappingProxy, Logger logger)
|
public SceneMappingService(ISceneMappingRepository repository, ISceneMappingProxy sceneMappingProxy, ISeriesService seriesService, Logger logger)
|
||||||
{
|
{
|
||||||
_repository = repository;
|
_repository = repository;
|
||||||
_sceneMappingProxy = sceneMappingProxy;
|
_sceneMappingProxy = sceneMappingProxy;
|
||||||
|
_seriesService = seriesService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,16 +51,20 @@ public void UpdateMappings()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
if (mapping == null) return null;
|
||||||
|
|
||||||
return mapping.SceneName;
|
return mapping.SceneName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Nullable<Int32> GetTvDbId(string cleanName)
|
|
||||||
|
|
||||||
|
public Nullable<Int32> GetTvDbId(string cleanName)
|
||||||
{
|
{
|
||||||
var mapping = _repository.FindByCleanTitle(cleanName);
|
var mapping = _repository.FindByCleanTitle(cleanName);
|
||||||
|
|
||||||
@ -68,7 +75,7 @@ public virtual Nullable<Int32> GetTvDbId(string cleanName)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public virtual string GetCleanName(int tvdbId)
|
public string GetCleanName(int tvdbId)
|
||||||
{
|
{
|
||||||
var mapping = _repository.FindByTvdbId(tvdbId);
|
var mapping = _repository.FindByTvdbId(tvdbId);
|
||||||
|
|
||||||
|
@ -100,8 +100,8 @@ protected override void MainDbUpgrade()
|
|||||||
|
|
||||||
Create.TableForModel("IndexerDefinitions")
|
Create.TableForModel("IndexerDefinitions")
|
||||||
.WithColumn("Enable").AsBoolean()
|
.WithColumn("Enable").AsBoolean()
|
||||||
.WithColumn("Type").AsString().Unique()
|
.WithColumn("Name").AsString().Unique()
|
||||||
.WithColumn("Name").AsString().Unique();
|
.WithColumn("Settings").AsString();
|
||||||
|
|
||||||
Create.TableForModel("NewznabDefinitions")
|
Create.TableForModel("NewznabDefinitions")
|
||||||
.WithColumn("Enable").AsBoolean()
|
.WithColumn("Enable").AsBoolean()
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
using NzbDrone.Core.Datastore.Converters;
|
using NzbDrone.Core.Datastore.Converters;
|
||||||
using NzbDrone.Core.ExternalNotification;
|
using NzbDrone.Core.ExternalNotification;
|
||||||
using NzbDrone.Core.Indexers;
|
using NzbDrone.Core.Indexers;
|
||||||
|
using NzbDrone.Core.Indexers.Newznab;
|
||||||
using NzbDrone.Core.Instrumentation;
|
using NzbDrone.Core.Instrumentation;
|
||||||
using NzbDrone.Core.Jobs;
|
using NzbDrone.Core.Jobs;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
@ -30,7 +31,7 @@ public static void Map()
|
|||||||
Mapper.Entity<Config>().RegisterModel("Config");
|
Mapper.Entity<Config>().RegisterModel("Config");
|
||||||
Mapper.Entity<RootFolder>().RegisterModel("RootFolders").Ignore(r => r.FreeSpace);
|
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<NewznabDefinition>().RegisterModel("NewznabDefinitions");
|
||||||
Mapper.Entity<JobDefinition>().RegisterModel("JobDefinitions");
|
Mapper.Entity<JobDefinition>().RegisterModel("JobDefinitions");
|
||||||
Mapper.Entity<ExternalNotificationDefinition>().RegisterModel("ExternalNotificationDefinitions");
|
Mapper.Entity<ExternalNotificationDefinition>().RegisterModel("ExternalNotificationDefinitions");
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using NzbDrone.Core.Model;
|
||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine
|
namespace NzbDrone.Core.DecisionEngine
|
||||||
{
|
{
|
||||||
public class DownloadDecision
|
public class DownloadDecision
|
||||||
{
|
{
|
||||||
|
public EpisodeParseResult ParseResult { get; private set; }
|
||||||
public IEnumerable<string> Rejections { get; private set; }
|
public IEnumerable<string> Rejections { get; private set; }
|
||||||
|
|
||||||
public bool Approved
|
public bool Approved
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -14,9 +17,23 @@ public bool Approved
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownloadDecision(params string[] rejections)
|
public DownloadDecision(EpisodeParseResult parseResult, params string[] rejections)
|
||||||
{
|
{
|
||||||
|
ParseResult = parseResult;
|
||||||
Rejections = rejections.ToList();
|
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 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine
|
namespace NzbDrone.Core.DecisionEngine
|
||||||
{
|
{
|
||||||
public interface IFetchableSpecification
|
public interface IDecisionEngineSpecification : IRejectWithReason
|
||||||
{
|
{
|
||||||
string RejectionReason { get; }
|
|
||||||
bool IsSatisfiedBy(EpisodeParseResult subject);
|
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 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
{
|
{
|
||||||
public class AcceptableSizeSpecification : IFetchableSpecification
|
public class AcceptableSizeSpecification : IDecisionEngineSpecification
|
||||||
{
|
{
|
||||||
private readonly IQualitySizeService _qualityTypeProvider;
|
private readonly IQualitySizeService _qualityTypeProvider;
|
||||||
private readonly IEpisodeService _episodeService;
|
private readonly IEpisodeService _episodeService;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
{
|
{
|
||||||
public class AllowedReleaseGroupSpecification : IFetchableSpecification
|
public class AllowedReleaseGroupSpecification : IDecisionEngineSpecification
|
||||||
{
|
{
|
||||||
private readonly IConfigService _configService;
|
private readonly IConfigService _configService;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
{
|
{
|
||||||
public class CustomStartDateSpecification : IFetchableSpecification
|
public class CustomStartDateSpecification : IDecisionEngineSpecification
|
||||||
{
|
{
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
{
|
{
|
||||||
public class LanguageSpecification : IFetchableSpecification
|
public class LanguageSpecification : IDecisionEngineSpecification
|
||||||
{
|
{
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
{
|
{
|
||||||
public class MonitoredEpisodeSpecification : IFetchableSpecification
|
public class MonitoredEpisodeSpecification : IDecisionEngineSpecification
|
||||||
{
|
{
|
||||||
private readonly IEpisodeService _episodeService;
|
private readonly IEpisodeService _episodeService;
|
||||||
private readonly ISeriesRepository _seriesRepository;
|
private readonly ISeriesRepository _seriesRepository;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
{
|
{
|
||||||
public class NotInQueueSpecification : IFetchableSpecification
|
public class NotInQueueSpecification : IDecisionEngineSpecification
|
||||||
{
|
{
|
||||||
private readonly IProvideDownloadClient _downloadClientProvider;
|
private readonly IProvideDownloadClient _downloadClientProvider;
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
{
|
{
|
||||||
public class QualityAllowedByProfileSpecification : IFetchableSpecification
|
public class QualityAllowedByProfileSpecification : IDecisionEngineSpecification
|
||||||
{
|
{
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
{
|
{
|
||||||
public class RetentionSpecification : IFetchableSpecification
|
public class RetentionSpecification : IDecisionEngineSpecification
|
||||||
{
|
{
|
||||||
private readonly IConfigService _configService;
|
private readonly IConfigService _configService;
|
||||||
private readonly Logger _logger;
|
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 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
{
|
{
|
||||||
public class UpgradeDiskSpecification : IFetchableSpecification
|
public class UpgradeDiskSpecification : IDecisionEngineSpecification
|
||||||
{
|
{
|
||||||
private readonly QualityUpgradableSpecification _qualityUpgradableSpecification;
|
private readonly QualityUpgradableSpecification _qualityUpgradableSpecification;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
{
|
{
|
||||||
public class UpgradeHistorySpecification : IFetchableSpecification
|
public class UpgradeHistorySpecification : IDecisionEngineSpecification
|
||||||
{
|
{
|
||||||
private readonly IHistoryService _historyService;
|
private readonly IHistoryService _historyService;
|
||||||
private readonly QualityUpgradableSpecification _qualityUpgradableSpecification;
|
private readonly QualityUpgradableSpecification _qualityUpgradableSpecification;
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Eventing;
|
using NzbDrone.Common.Eventing;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.DecisionEngine;
|
||||||
using NzbDrone.Core.Model;
|
using NzbDrone.Core.Model;
|
||||||
|
using NzbDrone.Core.Tv;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download
|
namespace NzbDrone.Core.Download
|
||||||
{
|
{
|
||||||
@ -29,7 +32,6 @@ public DownloadService(IProvideDownloadClient downloadClientProvider, IConfigSer
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public bool DownloadReport(EpisodeParseResult parseResult)
|
public bool DownloadReport(EpisodeParseResult parseResult)
|
||||||
{
|
{
|
||||||
var downloadTitle = parseResult.OriginalString;
|
var downloadTitle = parseResult.OriginalString;
|
||||||
@ -45,7 +47,7 @@ public bool DownloadReport(EpisodeParseResult parseResult)
|
|||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
_logger.Trace("Download added to Queue: {0}", downloadTitle);
|
_logger.Info("Report sent to download client. {0}", downloadTitle);
|
||||||
_eventAggregator.Publish(new EpisodeGrabbedEvent(parseResult));
|
_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 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.Indexers
|
namespace NzbDrone.Core.Indexers
|
||||||
{
|
{
|
||||||
public class Indexer : ModelBase
|
public class IndexerDefinition : ModelBase
|
||||||
{
|
{
|
||||||
public Boolean Enable { get; set; }
|
public Boolean Enable { get; set; }
|
||||||
public String Type { get; set; }
|
|
||||||
public String Name { 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.Linq;
|
||||||
using System.Data;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Indexers
|
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)
|
public IndexerRepository(IDatabase database)
|
||||||
: base(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 System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.Lifecycle;
|
using NzbDrone.Core.Lifecycle;
|
||||||
@ -9,10 +8,10 @@ namespace NzbDrone.Core.Indexers
|
|||||||
{
|
{
|
||||||
public interface IIndexerService
|
public interface IIndexerService
|
||||||
{
|
{
|
||||||
List<Indexer> All();
|
List<IndexerDefinition> All();
|
||||||
List<IndexerBase> GetEnabledIndexers();
|
List<IIndexerBase> GetAvailableIndexers();
|
||||||
void SaveSettings(Indexer indexer);
|
void Save(IndexerDefinition indexer);
|
||||||
Indexer GetSettings(Type type);
|
IndexerDefinition Get(string name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IndexerService : IIndexerService, IInitializable
|
public class IndexerService : IIndexerService, IInitializable
|
||||||
@ -20,9 +19,9 @@ public class IndexerService : IIndexerService, IInitializable
|
|||||||
private readonly IIndexerRepository _indexerRepository;
|
private readonly IIndexerRepository _indexerRepository;
|
||||||
private readonly Logger _logger;
|
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;
|
_indexerRepository = indexerRepository;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@ -37,14 +36,13 @@ public void Init()
|
|||||||
|
|
||||||
foreach (var feedProvider in _indexers)
|
foreach (var feedProvider in _indexers)
|
||||||
{
|
{
|
||||||
IndexerBase indexerLocal = feedProvider;
|
IIndexerBase indexerLocal = feedProvider;
|
||||||
if (!currentIndexers.Exists(c => c.Type == indexerLocal.GetType().ToString()))
|
if (!currentIndexers.Exists(c => c.Name == indexerLocal.Name))
|
||||||
{
|
{
|
||||||
var settings = new Indexer
|
var settings = new IndexerDefinition
|
||||||
{
|
{
|
||||||
Enable = indexerLocal.EnabledByDefault,
|
Enable = indexerLocal.EnabledByDefault,
|
||||||
Type = indexerLocal.GetType().ToString(),
|
Name = indexerLocal.Name.ToLower()
|
||||||
Name = indexerLocal.Name
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_indexerRepository.Insert(settings);
|
_indexerRepository.Insert(settings);
|
||||||
@ -52,27 +50,27 @@ public void Init()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Indexer> All()
|
public List<IndexerDefinition> All()
|
||||||
{
|
{
|
||||||
return _indexerRepository.All().ToList();
|
return _indexerRepository.All().ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<IndexerBase> GetEnabledIndexers()
|
public List<IIndexerBase> GetAvailableIndexers()
|
||||||
{
|
{
|
||||||
var all = All();
|
var enabled = All().Where(c => c.Enable).Select(c => c.Name);
|
||||||
return _indexers.Where(i => all.Exists(c => c.Type == i.GetType().ToString() && c.Enable)).ToList();
|
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
|
_indexerRepository.Update(indexer);
|
||||||
_logger.Debug("Upserting Indexer definitions for {0}", indexer.Name);
|
|
||||||
_indexerRepository.Upsert(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;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Indexers.Newznab
|
||||||
namespace NzbDrone.Core.Indexers
|
|
||||||
{
|
{
|
||||||
public class NewznabDefinition : ModelBase
|
public class NewznabDefinition : ModelBase
|
||||||
{
|
{
|
||||||
@ -10,6 +9,5 @@ public class NewznabDefinition : ModelBase
|
|||||||
public String Name { get; set; }
|
public String Name { get; set; }
|
||||||
public String Url { get; set; }
|
public String Url { get; set; }
|
||||||
public String ApiKey { 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;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Indexers
|
namespace NzbDrone.Core.Indexers.Newznab
|
||||||
{
|
{
|
||||||
public interface INewznabRepository : IBasicRepository<NewznabDefinition>
|
public interface INewznabRepository : IBasicRepository<NewznabDefinition>
|
||||||
{
|
{
|
@ -1,12 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.Lifecycle;
|
using NzbDrone.Core.Lifecycle;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Indexers.Newznab
|
||||||
namespace NzbDrone.Core.Indexers
|
|
||||||
{
|
{
|
||||||
public interface INewznabService
|
public interface INewznabService
|
||||||
{
|
{
|
||||||
@ -79,9 +78,9 @@ public void Init()
|
|||||||
{
|
{
|
||||||
var newznabIndexers = new List<NewznabDefinition>
|
var newznabIndexers = new List<NewznabDefinition>
|
||||||
{
|
{
|
||||||
new NewznabDefinition { Enable = false, Name = "Nzbs.org", Url = "http://nzbs.org", BuiltIn = true },
|
new NewznabDefinition { Enable = false, Name = "Nzbs.org", Url = "http://nzbs.org" },
|
||||||
new NewznabDefinition { Enable = false, Name = "Nzb.su", Url = "https://nzb.su", BuiltIn = true },
|
new NewznabDefinition { Enable = false, Name = "Nzb.su", Url = "https://nzb.su" },
|
||||||
new NewznabDefinition { Enable = false, Name = "Dognzb.cr", Url = "https://dognzb.cr", BuiltIn = true }
|
new NewznabDefinition { Enable = false, Name = "Dognzb.cr", Url = "https://dognzb.cr" }
|
||||||
};
|
};
|
||||||
|
|
||||||
_logger.Debug("Initializing Newznab indexers. Count {0}", newznabIndexers);
|
_logger.Debug("Initializing Newznab indexers. Count {0}", newznabIndexers);
|
||||||
@ -112,7 +111,6 @@ public void Init()
|
|||||||
Name = indexerLocal.Name,
|
Name = indexerLocal.Name,
|
||||||
Url = indexerLocal.Url,
|
Url = indexerLocal.Url,
|
||||||
ApiKey = indexerLocal.ApiKey,
|
ApiKey = indexerLocal.ApiKey,
|
||||||
BuiltIn = true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Insert(definition);
|
Insert(definition);
|
||||||
@ -121,7 +119,6 @@ public void Init()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
currentIndexer.Url = indexerLocal.Url;
|
currentIndexer.Url = indexerLocal.Url;
|
||||||
currentIndexer.BuiltIn = true;
|
|
||||||
Update(currentIndexer);
|
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
Loading…
Reference in New Issue
Block a user