release endpoint now returns fully parsed rss info with decisions.

pull/6/head
kay.one 12 years ago
parent 7e473ca78d
commit ca8eba9cf1

@ -6,8 +6,10 @@ using NzbDrone.Api.Indexers;
using NzbDrone.Api.Mapping; using NzbDrone.Api.Mapping;
using NzbDrone.Api.RootFolders; using NzbDrone.Api.RootFolders;
using NzbDrone.Api.Series; using NzbDrone.Api.Series;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Organizer; using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RootFolders; using NzbDrone.Core.RootFolders;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
@ -21,6 +23,9 @@ namespace NzbDrone.Api.Test.MappingTests
[TestCase(typeof(RootFolder), typeof(RootFolderResource))] [TestCase(typeof(RootFolder), typeof(RootFolderResource))]
[TestCase(typeof(NamingConfig), typeof(NamingConfigResource))] [TestCase(typeof(NamingConfig), typeof(NamingConfigResource))]
[TestCase(typeof(IndexerDefinition), typeof(IndexerResource))] [TestCase(typeof(IndexerDefinition), typeof(IndexerResource))]
[TestCase(typeof(ReportInfo), typeof(ReleaseResource))]
[TestCase(typeof(ParsedEpisodeInfo), typeof(ReleaseResource))]
[TestCase(typeof(DownloadDecision), typeof(ReleaseResource))]
public void matching_fields(Type modelType, Type resourceType) public void matching_fields(Type modelType, Type resourceType)
{ {
MappingValidation.ValidateMapping(modelType, resourceType); MappingValidation.ValidateMapping(modelType, resourceType);

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using NzbDrone.Api.Mapping;
using NzbDrone.Api.REST;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser;
using Omu.ValueInjecter;
using System.Linq;
namespace NzbDrone.Api.Indexers
{
public class ReleaseModule : NzbDroneRestModule<ReleaseResource>
{
private readonly IFetchAndParseRss _rssFetcherAndParser;
private readonly IMakeDownloadDecision _downloadDecisionMaker;
public ReleaseModule(IFetchAndParseRss rssFetcherAndParser, IMakeDownloadDecision downloadDecisionMaker)
{
_rssFetcherAndParser = rssFetcherAndParser;
_downloadDecisionMaker = downloadDecisionMaker;
GetResourceAll = GetRss;
}
private List<ReleaseResource> GetRss()
{
var reports = _rssFetcherAndParser.Fetch();
var decisions = _downloadDecisionMaker.GetRssDecision(reports);
var result = new List<ReleaseResource>();
foreach (var downloadDecision in decisions)
{
var release = new ReleaseResource();
release.InjectFrom(downloadDecision.RemoteEpisode.Report);
release.InjectFrom(downloadDecision.RemoteEpisode.ParsedEpisodeInfo);
release.InjectFrom(downloadDecision);
release.Rejections = downloadDecision.Rejections.ToList();
result.Add(release);
}
return result;
}
}
public class ReleaseResource : RestResource
{
public Int32 Age { get; set; }
public Int64 Size { get; set; }
public String Indexer { get; set; }
public String NzbInfoUrl { get; set; }
public String NzbUrl { get; set; }
public String ReleaseGroup { get; set; }
public String Title { get; set; }
public Boolean FullSeason { get; set; }
public Boolean SceneSource { get; set; }
public Int32 SeasonNumber { get; set; }
public Language Language { get; set; }
public DateTime? AirDate { get; set; }
public String OriginalString { get; set; }
public String SeriesTitle { get; set; }
public int[] EpisodeNumbers { get; set; }
public Boolean Approved { get; set; }
public List<string> Rejections { get; set; }
}
}

@ -0,0 +1,71 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Omu.ValueInjecter;
namespace NzbDrone.Api.Mapping
{
public class CloneInjection : ConventionInjection
{
protected override bool Match(ConventionInfo conventionInfo)
{
return conventionInfo.SourceProp.Name == conventionInfo.TargetProp.Name &&
conventionInfo.SourceProp.Value != null;
}
protected override object SetValue(ConventionInfo conventionInfo)
{
//for value types and string just return the value as is
if (conventionInfo.SourceProp.Type.IsValueType || conventionInfo.SourceProp.Type == typeof(string))
return conventionInfo.SourceProp.Value;
//handle arrays
if (conventionInfo.SourceProp.Type.IsArray)
{
var array = (Array)conventionInfo.SourceProp.Value;
var clone = (Array)array.Clone();
for (var index = 0; index < array.Length; index++)
{
var item = array.GetValue(index);
if (!item.GetType().IsValueType && !(item is string))
{
clone.SetValue(Activator.CreateInstance(item.GetType()).InjectFrom<CloneInjection>(item), index);
}
}
return clone;
}
if (conventionInfo.SourceProp.Type.IsGenericType)
{
//handle IEnumerable<> also ICollection<> IList<> List<>
if (conventionInfo.SourceProp.Type.GetGenericTypeDefinition().GetInterfaces().Any(d => d == typeof(IEnumerable)))
{
var t = conventionInfo.SourceProp.Type.GetGenericArguments()[0];
if (t.IsValueType || t == typeof(string)) return conventionInfo.SourceProp.Value;
var tlist = typeof(List<>).MakeGenericType(t);
var list = Activator.CreateInstance(tlist);
var addMethod = tlist.GetMethod("Add");
foreach (var o in (IEnumerable)conventionInfo.SourceProp.Value)
{
var e = Activator.CreateInstance(t).InjectFrom<CloneInjection>(o);
addMethod.Invoke(list, new[] { e }); // in 4.0 you can use dynamic and just do list.Add(e);
}
return list;
}
//unhandled generic type, you could also return null or throw
return conventionInfo.SourceProp.Value;
}
//for simple object types create a new instace and apply the clone injection on it
return Activator.CreateInstance(conventionInfo.SourceProp.Type)
.InjectFrom<CloneInjection>(conventionInfo.SourceProp.Value);
}
}
}

@ -96,6 +96,8 @@
<Compile Include="Frontend\StaticResourceMapper.cs" /> <Compile Include="Frontend\StaticResourceMapper.cs" />
<Compile Include="Indexers\IndexerModule.cs" /> <Compile Include="Indexers\IndexerModule.cs" />
<Compile Include="Indexers\IndexerResource.cs" /> <Compile Include="Indexers\IndexerResource.cs" />
<Compile Include="Indexers\ReleaseModule.cs" />
<Compile Include="Mapping\CloneInjection.cs" />
<Compile Include="Mapping\MappingValidation.cs" /> <Compile Include="Mapping\MappingValidation.cs" />
<Compile Include="Mapping\ResourceMappingException.cs" /> <Compile Include="Mapping\ResourceMappingException.cs" />
<Compile Include="Mapping\ValueInjectorExtensions.cs" /> <Compile Include="Mapping\ValueInjectorExtensions.cs" />

@ -22,11 +22,12 @@ namespace NzbDrone.Common.Reflection
public static bool IsSimpleType(this Type type) public static bool IsSimpleType(this Type type)
{ {
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) if (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(Nullable<>) || type.GetGenericTypeDefinition() == typeof(List<>)))
{ {
type = type.GetGenericArguments()[0]; type = type.GetGenericArguments()[0];
} }
return type.IsPrimitive return type.IsPrimitive
|| type.IsEnum || type.IsEnum
|| type == typeof(string) || type == typeof(string)

@ -32,7 +32,6 @@ namespace NzbDrone.Core.Test.Datastore
public class TypeWithNoMappableProperties public class TypeWithNoMappableProperties
{ {
public Series Series { get; set; } public Series Series { get; set; }
public List<string> ListOfStrings { get; set; }
public int ReadOnly { get; private set; } public int ReadOnly { get; private set; }
public int WriteOnly { private get; set; } public int WriteOnly { private get; set; }

@ -27,14 +27,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultMulti = new RemoteEpisode parseResultMulti = new RemoteEpisode
{ {
Report = new ReportInfo(), Report = new ReportInfo(),
Quality = new QualityModel(Quality.SDTV, true), ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) },
Episodes = new List<Episode> { new Episode(), new Episode() } Episodes = new List<Episode> { new Episode(), new Episode() }
}; };
parseResultSingle = new RemoteEpisode parseResultSingle = new RemoteEpisode
{ {
Report = new ReportInfo(), Report = new ReportInfo(),
Quality = new QualityModel(Quality.SDTV, true), ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) },
Episodes = new List<Episode> { new Episode() } Episodes = new List<Episode> { new Episode() }
}; };
@ -115,7 +115,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void IsAcceptableSize_false_single_episode_not_first_or_last_60_minute() public void IsAcceptableSize_false_single_episode_not_first_or_last_60_minute()
{ {
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Report.Size = 1.Gigabytes(); parseResultSingle.Report.Size = 1.Gigabytes();
@ -136,7 +136,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void IsAcceptableSize_true_multi_episode_not_first_or_last_30_minute() public void IsAcceptableSize_true_multi_episode_not_first_or_last_30_minute()
{ {
parseResultMulti.Series = series30minutes; parseResultMulti.Series = series30minutes;
parseResultMulti.Report.Size = 184572800; parseResultMulti.Report.Size = 184572800;
@ -157,7 +157,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void IsAcceptableSize_true_multi_episode_not_first_or_last_60_minute() public void IsAcceptableSize_true_multi_episode_not_first_or_last_60_minute()
{ {
parseResultMulti.Series = series60minutes; parseResultMulti.Series = series60minutes;
parseResultMulti.Report.Size = 368572800; parseResultMulti.Report.Size = 368572800;
@ -178,7 +178,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void IsAcceptableSize_false_multi_episode_not_first_or_last_30_minute() public void IsAcceptableSize_false_multi_episode_not_first_or_last_30_minute()
{ {
parseResultMulti.Series = series30minutes; parseResultMulti.Series = series30minutes;
parseResultMulti.Report.Size = 1.Gigabytes(); parseResultMulti.Report.Size = 1.Gigabytes();
@ -199,7 +199,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void IsAcceptableSize_false_multi_episode_not_first_or_last_60_minute() public void IsAcceptableSize_false_multi_episode_not_first_or_last_60_minute()
{ {
parseResultMulti.Series = series60minutes; parseResultMulti.Series = series60minutes;
parseResultMulti.Report.Size = 10.Gigabytes(); parseResultMulti.Report.Size = 10.Gigabytes();
@ -220,7 +220,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void IsAcceptableSize_true_single_episode_first_30_minute() public void IsAcceptableSize_true_single_episode_first_30_minute()
{ {
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Report.Size = 184572800; parseResultSingle.Report.Size = 184572800;
@ -241,7 +241,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void IsAcceptableSize_true_single_episode_first_60_minute() public void IsAcceptableSize_true_single_episode_first_60_minute()
{ {
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Report.Size = 368572800; parseResultSingle.Report.Size = 368572800;
@ -262,7 +262,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void IsAcceptableSize_false_single_episode_first_30_minute() public void IsAcceptableSize_false_single_episode_first_30_minute()
{ {
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Report.Size = 1.Gigabytes(); parseResultSingle.Report.Size = 1.Gigabytes();
@ -283,7 +283,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void IsAcceptableSize_false_single_episode_first_60_minute() public void IsAcceptableSize_false_single_episode_first_60_minute()
{ {
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Report.Size = 10.Gigabytes(); parseResultSingle.Report.Size = 10.Gigabytes();
@ -304,7 +304,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void IsAcceptableSize_true_unlimited_30_minute() public void IsAcceptableSize_true_unlimited_30_minute()
{ {
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Report.Size = 18457280000; parseResultSingle.Report.Size = 18457280000;
@ -326,7 +326,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void IsAcceptableSize_true_unlimited_60_minute() public void IsAcceptableSize_true_unlimited_60_minute()
{ {
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Report.Size = 36857280000; parseResultSingle.Report.Size = 36857280000;
@ -374,7 +374,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
var parseResult = new RemoteEpisode var parseResult = new RemoteEpisode
{ {
Quality = new QualityModel(Quality.RAWHD, false) ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.RAWHD, false) },
}; };
Subject.IsSatisfiedBy(parseResult).Should().BeTrue(); Subject.IsSatisfiedBy(parseResult).Should().BeTrue();

@ -7,6 +7,7 @@ using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.DecisionEngineTests namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
@ -43,10 +44,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_fail2.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>())).Returns(false); _fail2.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>())).Returns(false);
_fail3.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>())).Returns(false); _fail3.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>())).Returns(false);
_reports = new List<ReportInfo> { new ReportInfo() }; _reports = new List<ReportInfo> { new ReportInfo { Title = "The.Office.S03E115.DVDRip.XviD-OSiTV" } };
_remoteEpisode = new RemoteEpisode(); _remoteEpisode = new RemoteEpisode { Series = new Series() };
Mocker.GetMock<IParsingService>().Setup(c => c.Map(It.IsAny<ReportInfo>())) Mocker.GetMock<IParsingService>().Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>()))
.Returns(_remoteEpisode); .Returns(_remoteEpisode);
} }
@ -102,21 +103,50 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_not_attempt_to_make_decision_if_remote_episode_is_null() public void should_not_attempt_to_map_episode_if_not_parsable()
{ {
GivenSpecifications(_pass1, _pass2, _pass3); GivenSpecifications(_pass1, _pass2, _pass3);
_reports[0].Title = "Not parsable";
Mocker.GetMock<IParsingService>().Setup(c => c.Map(It.IsAny<ReportInfo>()))
.Returns<RemoteEpisode>(null);
var results = Subject.GetRssDecision(_reports).ToList(); var results = Subject.GetRssDecision(_reports).ToList();
Mocker.GetMock<IParsingService>().Verify(c => c.Map(It.IsAny<ParsedEpisodeInfo>()), Times.Never());
_pass1.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>()), Times.Never()); _pass1.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>()), Times.Never());
_pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>()), Times.Never()); _pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>()), Times.Never());
_pass3.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>()), Times.Never()); _pass3.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>()), Times.Never());
results.Should().BeEmpty(); results.Should().BeEmpty();
}
[Test]
public void should_not_attempt_to_make_decision_if_series_is_unknow()
{
GivenSpecifications(_pass1, _pass2, _pass3);
_remoteEpisode.Series = null;
Subject.GetRssDecision(_reports);
_pass1.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>()), Times.Never());
_pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>()), Times.Never());
_pass3.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>()), Times.Never());
}
[Test]
public void should_return_unknow_series_rejectio_if_series_is_unknow()
{
GivenSpecifications(_pass1, _pass2, _pass3);
_remoteEpisode.Series = null;
var result = Subject.GetRssDecision(_reports);
result.Should().HaveCount(1);
} }

@ -1,43 +1,38 @@
 using FluentAssertions;
using System.Linq;
using System;
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Model;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.DecisionEngineTests namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
[TestFixture] [TestFixture]
public class LanguageSpecificationFixture : CoreTest public class LanguageSpecificationFixture : CoreTest
{ {
private RemoteEpisode parseResult; private RemoteEpisode parseResult;
private void WithEnglishRelease() private void WithEnglishRelease()
{ {
parseResult = Builder<RemoteEpisode> parseResult = new RemoteEpisode
.CreateNew() {
.With(p => p.Language = Language.English) ParsedEpisodeInfo = new ParsedEpisodeInfo
.Build(); {
Language = Language.English
}
};
} }
private void WithGermanRelease() private void WithGermanRelease()
{ {
parseResult = Builder<RemoteEpisode> parseResult = new RemoteEpisode
.CreateNew() {
.With(p => p.Language = Language.German) ParsedEpisodeInfo = new ParsedEpisodeInfo
.Build(); {
Language = Language.German
}
};
} }
[Test] [Test]

@ -1,6 +1,4 @@
 using System.Collections.Generic;
using System.Collections.Generic;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
@ -16,7 +14,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public class QualityAllowedByProfileSpecificationFixture : CoreTest<QualityAllowedByProfileSpecification> public class QualityAllowedByProfileSpecificationFixture : CoreTest<QualityAllowedByProfileSpecification>
{ {
private RemoteEpisode parseResult; private RemoteEpisode remoteEpisode;
public static object[] AllowedTestCases = public static object[] AllowedTestCases =
{ {
@ -39,29 +37,29 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.With(c => c.QualityProfile = new QualityProfile { Cutoff = Quality.Bluray1080p }) .With(c => c.QualityProfile = new QualityProfile { Cutoff = Quality.Bluray1080p })
.Build(); .Build();
parseResult = new RemoteEpisode remoteEpisode = new RemoteEpisode
{ {
Series = fakeSeries, Series = fakeSeries,
Quality = new QualityModel(Quality.DVD, true), ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) },
}; };
} }
[Test, TestCaseSource("AllowedTestCases")] [Test, TestCaseSource("AllowedTestCases")]
public void should_allow_if_quality_is_defined_in_profile(Quality qualityType) public void should_allow_if_quality_is_defined_in_profile(Quality qualityType)
{ {
parseResult.Quality.Quality = qualityType; remoteEpisode.ParsedEpisodeInfo.Quality.Quality = qualityType;
parseResult.Series.QualityProfile.Allowed = new List<Quality> { Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p }; remoteEpisode.Series.QualityProfile.Allowed = new List<Quality> { Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p };
Subject.IsSatisfiedBy(parseResult).Should().BeTrue(); Subject.IsSatisfiedBy(remoteEpisode).Should().BeTrue();
} }
[Test, TestCaseSource("DeniedTestCases")] [Test, TestCaseSource("DeniedTestCases")]
public void should_not_allow_if_quality_is_not_defined_in_profile(Quality qualityType) public void should_not_allow_if_quality_is_not_defined_in_profile(Quality qualityType)
{ {
parseResult.Quality.Quality = qualityType; remoteEpisode.ParsedEpisodeInfo.Quality.Quality = qualityType;
parseResult.Series.QualityProfile.Allowed = new List<Quality> { Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p }; remoteEpisode.Series.QualityProfile.Allowed = new List<Quality> { Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p };
Subject.IsSatisfiedBy(parseResult).Should().BeFalse(); Subject.IsSatisfiedBy(remoteEpisode).Should().BeFalse();
} }
} }
} }

@ -44,14 +44,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultMulti = new RemoteEpisode parseResultMulti = new RemoteEpisode
{ {
Series = fakeSeries, Series = fakeSeries,
Quality = new QualityModel(Quality.DVD, true), ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) },
Episodes = doubleEpisodeList Episodes = doubleEpisodeList
}; };
parseResultSingle = new RemoteEpisode parseResultSingle = new RemoteEpisode
{ {
Series = fakeSeries, Series = fakeSeries,
Quality = new QualityModel(Quality.DVD, true), ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) },
Episodes = singleEpisodeList Episodes = singleEpisodeList
}; };
} }
@ -113,7 +113,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_not_be_upgradable_if_qualities_are_the_same() public void should_not_be_upgradable_if_qualities_are_the_same()
{ {
firstFile.Quality = new QualityModel(Quality.WEBDL1080p); firstFile.Quality = new QualityModel(Quality.WEBDL1080p);
parseResultSingle.Quality = new QualityModel(Quality.WEBDL1080p, false); parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, false);
_upgradeDisk.IsSatisfiedBy(parseResultSingle).Should().BeFalse(); _upgradeDisk.IsSatisfiedBy(parseResultSingle).Should().BeFalse();
} }

@ -44,14 +44,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_parseResultMulti = new RemoteEpisode _parseResultMulti = new RemoteEpisode
{ {
Series = _fakeSeries, Series = _fakeSeries,
Quality = new QualityModel(Quality.DVD, true), ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) },
Episodes = doubleEpisodeList Episodes = doubleEpisodeList
}; };
_parseResultSingle = new RemoteEpisode _parseResultSingle = new RemoteEpisode
{ {
Series = _fakeSeries, Series = _fakeSeries,
Quality = new QualityModel(Quality.DVD, true), ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) },
Episodes = singleEpisodeList Episodes = singleEpisodeList
}; };
@ -115,7 +115,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_not_be_upgradable_if_episode_is_of_same_quality_as_existing() public void should_not_be_upgradable_if_episode_is_of_same_quality_as_existing()
{ {
_fakeSeries.QualityProfile = new QualityProfile { Cutoff = Quality.WEBDL1080p }; _fakeSeries.QualityProfile = new QualityProfile { Cutoff = Quality.WEBDL1080p };
_parseResultSingle.Quality = new QualityModel(Quality.WEBDL1080p, false); _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, false);
_upgradableQuality = new QualityModel(Quality.WEBDL1080p, false); _upgradableQuality = new QualityModel(Quality.WEBDL1080p, false);
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(1)).Returns(_upgradableQuality); Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(1)).Returns(_upgradableQuality);

@ -12,11 +12,11 @@ using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests
{ {
[TestFixture] [TestFixture]
public class SabProviderFixture : CoreTest public class SabProviderFixture : CoreTest<SabnzbdClient>
{ {
private const string url = "http://www.nzbclub.com/nzb_download.aspx?mid=1950232"; private const string URL = "http://www.nzbclub.com/nzb_download.aspx?mid=1950232";
private const string title = "My Series Name - 5x2-5x3 - My title [Bluray720p] [Proper]"; private const string TITLE = "My Series Name - 5x2-5x3 - My title [Bluray720p] [Proper]";
[SetUp] [SetUp]
public void Setup() public void Setup()
@ -44,40 +44,34 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=addurl&name=http://www.nzbclub.com/nzb_download.aspx?mid=1950232&priority=0&pp=3&cat=tv&nzbname=My+Series+Name+-+5x2-5x3+-+My+title+%5bBluray720p%5d+%5bProper%5d&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass")) .Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=addurl&name=http://www.nzbclub.com/nzb_download.aspx?mid=1950232&priority=0&pp=3&cat=tv&nzbname=My+Series+Name+-+5x2-5x3+-+My+title+%5bBluray720p%5d+%5bProper%5d&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns("{ \"status\": true }"); .Returns("{ \"status\": true }");
Mocker.Resolve<SabnzbdClient>().DownloadNzb(url, title, false).Should().BeTrue(); Subject.DownloadNzb(URL, TITLE, false).Should().BeTrue();
} }
[Test] [Test]
public void add_by_url_should_detect_and_handle_sab_errors() public void add_by_url_should_detect_and_handle_sab_errors()
{ {
WithFailResponse(); WithFailResponse();
Assert.Throws<ApplicationException>(() => Subject.DownloadNzb(URL, TITLE, false).Should().BeFalse());
Assert.Throws<ApplicationException>(() => Mocker.Resolve<SabnzbdClient>().DownloadNzb(url, title, false).Should().BeFalse());
//ExceptionVerification.ExpectedErrors(1);
} }
[Test] [Test]
public void should_be_able_to_get_categories_when_config_is_passed_in() public void should_be_able_to_get_categories_when_config_is_passed_in()
{ {
const string host = "192.168.5.22"; const string host = "192.168.5.22";
const int port = 1111; const int port = 1111;
const string apikey = "5c770e3197e4fe763423ee7c392c25d2"; const string apikey = "5c770e3197e4fe763423ee7c392c25d2";
const string username = "admin2"; const string username = "admin2";
const string password = "pass2"; const string password = "pass2";
Mocker.GetMock<IHttpProvider>(MockBehavior.Strict) Mocker.GetMock<IHttpProvider>(MockBehavior.Strict)
.Setup(s => s.DownloadString("http://192.168.5.22:1111/api?mode=get_cats&output=json&apikey=5c770e3197e4fe763423ee7c392c25d2&ma_username=admin2&ma_password=pass2")) .Setup(s => s.DownloadString("http://192.168.5.22:1111/api?mode=get_cats&output=json&apikey=5c770e3197e4fe763423ee7c392c25d2&ma_username=admin2&ma_password=pass2"))
.Returns(ReadAllText("Files","Categories_json.txt")); .Returns(ReadAllText("Files", "Categories_json.txt"));
var result = Subject.GetCategories(host, port, apikey, username, password);
var result = Mocker.Resolve<SabnzbdClient>().GetCategories(host, port, apikey, username, password);
result.Should().NotBeNull(); result.Should().NotBeNull();
result.categories.Should().NotBeEmpty(); result.categories.Should().NotBeEmpty();
} }
@ -87,12 +81,12 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests
{ {
Mocker.GetMock<IHttpProvider>(MockBehavior.Strict) Mocker.GetMock<IHttpProvider>(MockBehavior.Strict)
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=get_cats&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass")) .Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=get_cats&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(ReadAllText("Files","Categories_json.txt")); .Returns(ReadAllText("Files", "Categories_json.txt"));
var result = Subject.GetCategories();
var result = Mocker.Resolve<SabnzbdClient>().GetCategories();
result.Should().NotBeNull(); result.Should().NotBeNull();
result.categories.Should().NotBeEmpty(); result.categories.Should().NotBeEmpty();
} }
@ -104,10 +98,10 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=history&output=json&start=0&limit=0&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass")) .Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=history&output=json&start=0&limit=0&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(ReadAllText("Files", "History.txt")); .Returns(ReadAllText("Files", "History.txt"));
var result = Mocker.Resolve<SabnzbdClient>().GetHistory();
var result = Subject.GetHistory();
result.Should().HaveCount(1); result.Should().HaveCount(1);
} }
@ -116,12 +110,12 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests
{ {
Mocker.GetMock<IHttpProvider>() Mocker.GetMock<IHttpProvider>()
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=history&output=json&start=0&limit=0&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass")) .Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=history&output=json&start=0&limit=0&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(ReadAllText("Files","HistoryEmpty.txt")); .Returns(ReadAllText("Files", "HistoryEmpty.txt"));
var result = Subject.GetHistory();
var result = Mocker.Resolve<SabnzbdClient>().GetHistory();
result.Should().BeEmpty(); result.Should().BeEmpty();
} }
@ -130,10 +124,10 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests
{ {
Mocker.GetMock<IHttpProvider>() Mocker.GetMock<IHttpProvider>()
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=history&output=json&start=0&limit=0&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass")) .Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=history&output=json&start=0&limit=0&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(ReadAllText("Files","JsonError.txt")); .Returns(ReadAllText("Files", "JsonError.txt"));
Assert.Throws<ApplicationException>(() => Subject.GetHistory(), "API Key Incorrect");
Assert.Throws<ApplicationException>(() => Mocker.Resolve<SabnzbdClient>().GetHistory(), "API Key Incorrect");
} }
[Test] [Test]
@ -145,10 +139,10 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=version&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass")) .Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=version&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(response); .Returns(response);
var result = Mocker.Resolve<SabnzbdClient>().GetVersion("192.168.5.55", 2222, "5c770e3197e4fe763423ee7c392c25d1", "admin", "pass");
var result = Subject.GetVersion("192.168.5.55", 2222, "5c770e3197e4fe763423ee7c392c25d1", "admin", "pass");
result.Should().NotBeNull(); result.Should().NotBeNull();
result.Version.Should().Be("0.6.9"); result.Version.Should().Be("0.6.9");
} }
@ -162,10 +156,10 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=version&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass")) .Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=version&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(response); .Returns(response);
var result = Mocker.Resolve<SabnzbdClient>().GetVersion();
var result = Subject.GetVersion();
result.Should().NotBeNull(); result.Should().NotBeNull();
result.Version.Should().Be("0.6.9"); result.Version.Should().Be("0.6.9");
} }
@ -173,16 +167,16 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests
[Test] [Test]
public void Test_should_return_version_as_a_string() public void Test_should_return_version_as_a_string()
{ {
var response = "{ \"version\": \"0.6.9\" }"; const string response = "{ \"version\": \"0.6.9\" }";
Mocker.GetMock<IHttpProvider>() Mocker.GetMock<IHttpProvider>()
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=version&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass")) .Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=version&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(response); .Returns(response);
var result = Mocker.Resolve<SabnzbdClient>().Test("192.168.5.55", 2222, "5c770e3197e4fe763423ee7c392c25d1", "admin", "pass");
var result = Subject.Test("192.168.5.55", 2222, "5c770e3197e4fe763423ee7c392c25d1", "admin", "pass");
result.Should().Be("0.6.9"); result.Should().Be("0.6.9");
} }
@ -192,7 +186,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests
Mocker.GetMock<IHttpProvider>() Mocker.GetMock<IHttpProvider>()
.Setup(s => s.DownloadString(It.IsAny<String>())).Throws(new WebException()); .Setup(s => s.DownloadString(It.IsAny<String>())).Throws(new WebException());
Mocker.Resolve<SabnzbdClient>().DownloadNzb(url, title, false).Should().BeFalse(); Subject.DownloadNzb(URL, TITLE, false).Should().BeFalse();
ExceptionVerification.ExpectedErrors(1); ExceptionVerification.ExpectedErrors(1);
} }
@ -211,8 +205,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=addurl&name=http://www.nzbclub.com/nzb_download.aspx?mid=1950232&priority=1&pp=3&cat=tv&nzbname=My+Series+Name+-+5x2-5x3+-+My+title+%5bBluray720p%5d+%5bProper%5d&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass")) .Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=addurl&name=http://www.nzbclub.com/nzb_download.aspx?mid=1950232&priority=1&pp=3&cat=tv&nzbname=My+Series+Name+-+5x2-5x3+-+My+title+%5bBluray720p%5d+%5bProper%5d&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns("{ \"status\": true }"); .Returns("{ \"status\": true }");
Mocker.Resolve<SabnzbdClient>().DownloadNzb(url, title, true).Should().BeTrue(); Subject.DownloadNzb(URL, TITLE, true).Should().BeTrue();
Mocker.GetMock<IHttpProvider>() Mocker.GetMock<IHttpProvider>()
.Verify(v => v.DownloadString("http://192.168.5.55:2222/api?mode=addurl&name=http://www.nzbclub.com/nzb_download.aspx?mid=1950232&priority=1&pp=3&cat=tv&nzbname=My+Series+Name+-+5x2-5x3+-+My+title+%5bBluray720p%5d+%5bProper%5d&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"), Times.Once()); .Verify(v => v.DownloadString("http://192.168.5.55:2222/api?mode=addurl&name=http://www.nzbclub.com/nzb_download.aspx?mid=1950232&priority=1&pp=3&cat=tv&nzbname=My+Series+Name+-+5x2-5x3+-+My+title+%5bBluray720p%5d+%5bProper%5d&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"), Times.Once());
@ -233,8 +227,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=addurl&name=http://www.nzbclub.com/nzb_download.aspx?mid=1950232&priority=-1&pp=3&cat=tv&nzbname=My+Series+Name+-+5x2-5x3+-+My+title+%5bBluray720p%5d+%5bProper%5d&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass")) .Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=addurl&name=http://www.nzbclub.com/nzb_download.aspx?mid=1950232&priority=-1&pp=3&cat=tv&nzbname=My+Series+Name+-+5x2-5x3+-+My+title+%5bBluray720p%5d+%5bProper%5d&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns("{ \"status\": true }"); .Returns("{ \"status\": true }");
Mocker.Resolve<SabnzbdClient>().DownloadNzb(url, title, false).Should().BeTrue(); Subject.DownloadNzb(URL, TITLE, false).Should().BeTrue();
Mocker.GetMock<IHttpProvider>() Mocker.GetMock<IHttpProvider>()
.Verify(v => v.DownloadString("http://192.168.5.55:2222/api?mode=addurl&name=http://www.nzbclub.com/nzb_download.aspx?mid=1950232&priority=-1&pp=3&cat=tv&nzbname=My+Series+Name+-+5x2-5x3+-+My+title+%5bBluray720p%5d+%5bProper%5d&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"), Times.Once()); .Verify(v => v.DownloadString("http://192.168.5.55:2222/api?mode=addurl&name=http://www.nzbclub.com/nzb_download.aspx?mid=1950232&priority=-1&pp=3&cat=tv&nzbname=My+Series+Name+-+5x2-5x3+-+My+title+%5bBluray720p%5d+%5bProper%5d&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"), Times.Once());

@ -1,13 +1,10 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Model;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -31,7 +28,6 @@ namespace NzbDrone.Core.Test.Download
.Build().ToList(); .Build().ToList();
_parseResult = Builder<RemoteEpisode>.CreateNew() _parseResult = Builder<RemoteEpisode>.CreateNew()
.With(c => c.Quality = new QualityModel(Quality.DVD))
.With(c => c.Series = Builder<Series>.CreateNew().Build()) .With(c => c.Series = Builder<Series>.CreateNew().Build())
.With(c=>c.Report = Builder<ReportInfo>.CreateNew().Build()) .With(c=>c.Report = Builder<ReportInfo>.CreateNew().Build())
.With(c => c.Episodes = episodes) .With(c => c.Episodes = episodes)

@ -88,7 +88,6 @@ namespace NzbDrone.Core.Test.ParserTests
result.SeasonNumber.Should().Be(seasonNumber); result.SeasonNumber.Should().Be(seasonNumber);
result.EpisodeNumbers.First().Should().Be(episodeNumber); result.EpisodeNumbers.First().Should().Be(episodeNumber);
result.SeriesTitle.Should().Be(Parser.Parser.NormalizeTitle(title)); result.SeriesTitle.Should().Be(Parser.Parser.NormalizeTitle(title));
result.OriginalString.Should().Be(postTitle);
} }
[TestCase(@"z:\tv shows\battlestar galactica (2003)\Season 3\S03E05 - Collaborators.mkv", 3, 5)] [TestCase(@"z:\tv shows\battlestar galactica (2003)\Season 3\S03E05 - Collaborators.mkv", 3, 5)]
@ -107,7 +106,6 @@ namespace NzbDrone.Core.Test.ParserTests
result.EpisodeNumbers.Should().HaveCount(1); result.EpisodeNumbers.Should().HaveCount(1);
result.SeasonNumber.Should().Be(season); result.SeasonNumber.Should().Be(season);
result.EpisodeNumbers[0].Should().Be(episode); result.EpisodeNumbers[0].Should().Be(episode);
result.OriginalString.Should().Be(path);
ExceptionVerification.IgnoreWarns(); ExceptionVerification.IgnoreWarns();
} }
@ -159,7 +157,6 @@ namespace NzbDrone.Core.Test.ParserTests
result.SeasonNumber.Should().Be(season); result.SeasonNumber.Should().Be(season);
result.EpisodeNumbers.Should().BeEquivalentTo(episodes); result.EpisodeNumbers.Should().BeEquivalentTo(episodes);
result.SeriesTitle.Should().Be(Parser.Parser.NormalizeTitle(title)); result.SeriesTitle.Should().Be(Parser.Parser.NormalizeTitle(title));
result.OriginalString.Should().Be(postTitle);
} }
@ -181,7 +178,6 @@ namespace NzbDrone.Core.Test.ParserTests
result.SeriesTitle.Should().Be(Parser.Parser.NormalizeTitle(title)); result.SeriesTitle.Should().Be(Parser.Parser.NormalizeTitle(title));
result.AirDate.Should().Be(airDate); result.AirDate.Should().Be(airDate);
result.EpisodeNumbers.Should().BeNull(); result.EpisodeNumbers.Should().BeNull();
result.OriginalString.Should().Be(postTitle);
} }
[Test] [Test]
@ -203,9 +199,8 @@ namespace NzbDrone.Core.Test.ParserTests
var result = Parser.Parser.ParseTitle(postTitle); var result = Parser.Parser.ParseTitle(postTitle);
result.SeasonNumber.Should().Be(season); result.SeasonNumber.Should().Be(season);
result.SeriesTitle.Should().Be(Parser.Parser.NormalizeTitle(title)); result.SeriesTitle.Should().Be(Parser.Parser.NormalizeTitle(title));
result.EpisodeNumbers.Count.Should().Be(0); result.EpisodeNumbers.Length.Should().Be(0);
result.FullSeason.Should().BeTrue(); result.FullSeason.Should().BeTrue();
result.OriginalString.Should().Be(postTitle);
} }
[TestCase("Conan", "conan")] [TestCase("Conan", "conan")]
@ -346,7 +341,6 @@ namespace NzbDrone.Core.Test.ParserTests
result.SeriesTitle.Should().Be(Parser.Parser.NormalizeTitle(seriesName)); result.SeriesTitle.Should().Be(Parser.Parser.NormalizeTitle(seriesName));
result.SeasonNumber.Should().Be(seasonNumber); result.SeasonNumber.Should().Be(seasonNumber);
result.FullSeason.Should().BeTrue(); result.FullSeason.Should().BeTrue();
result.OriginalString.Should().Be(postTitle);
} }
[TestCase("Acropolis Now S05 EXTRAS DVDRip XviD RUNNER")] [TestCase("Acropolis Now S05 EXTRAS DVDRip XviD RUNNER")]

@ -408,13 +408,6 @@ namespace NzbDrone.Core.Configuration
set { SetValue("IgnoreArticlesWhenSortingSeries", value); } set { SetValue("IgnoreArticlesWhenSortingSeries", value); }
} }
public Boolean DownloadClientUseSceneName
{
get { return GetValueBoolean("DownloadClientUseSceneName", false); }
set { SetValue("DownloadClientUseSceneName", value); }
}
public String NzbgetUsername public String NzbgetUsername
{ {
get { return GetValue("NzbgetUsername", "nzbget"); } get { return GetValue("NzbgetUsername", "nzbget"); }

@ -68,7 +68,6 @@ namespace NzbDrone.Core.Configuration
string OmgwtfnzbsUsername { get; set; } string OmgwtfnzbsUsername { get; set; }
string OmgwtfnzbsApiKey { get; set; } string OmgwtfnzbsApiKey { get; set; }
Boolean IgnoreArticlesWhenSortingSeries { get; set; } Boolean IgnoreArticlesWhenSortingSeries { get; set; }
Boolean DownloadClientUseSceneName { get; set; }
String NzbgetUsername { get; set; } String NzbgetUsername { get; set; }
String NzbgetPassword { get; set; } String NzbgetPassword { get; set; }
String NzbgetHost { get; set; } String NzbgetHost { get; set; }

@ -1,14 +1,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.Model;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine namespace NzbDrone.Core.DecisionEngine
{ {
public class DownloadDecision public class DownloadDecision
{ {
public RemoteEpisode Episode { get; private set; } public RemoteEpisode RemoteEpisode { get; private set; }
public IEnumerable<string> Rejections { get; private set; } public IEnumerable<string> Rejections { get; private set; }
public bool Approved public bool Approved
@ -21,7 +19,7 @@ namespace NzbDrone.Core.DecisionEngine
public DownloadDecision(RemoteEpisode episode, params string[] rejections) public DownloadDecision(RemoteEpisode episode, params string[] rejections)
{ {
Episode = episode; RemoteEpisode = episode;
Rejections = rejections.ToList(); Rejections = rejections.ToList();
} }
} }

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.DecisionEngine.Specifications.Search; using NzbDrone.Core.DecisionEngine.Specifications.Search;
@ -9,8 +10,8 @@ namespace NzbDrone.Core.DecisionEngine
{ {
public interface IMakeDownloadDecision public interface IMakeDownloadDecision
{ {
IEnumerable<DownloadDecision> GetRssDecision(IEnumerable<ReportInfo> reports); List<DownloadDecision> GetRssDecision(IEnumerable<ReportInfo> reports);
IEnumerable<DownloadDecision> GetSearchDecision(IEnumerable<ReportInfo> reports, SearchDefinitionBase searchDefinitionBase); List<DownloadDecision> GetSearchDecision(IEnumerable<ReportInfo> reports, SearchDefinitionBase searchDefinitionBase);
} }
public class DownloadDecisionMaker : IMakeDownloadDecision public class DownloadDecisionMaker : IMakeDownloadDecision
@ -24,29 +25,19 @@ namespace NzbDrone.Core.DecisionEngine
_parsingService = parsingService; _parsingService = parsingService;
} }
public IEnumerable<DownloadDecision> GetRssDecision(IEnumerable<ReportInfo> reports) public List<DownloadDecision> GetRssDecision(IEnumerable<ReportInfo> reports)
{ {
foreach (var report in reports) return GetDecisions(reports, GetGeneralRejectionReasons).ToList();
{
var parseResult = _parsingService.Map(report);
if (parseResult != null)
{
yield return new DownloadDecision(parseResult, GetGeneralRejectionReasons(parseResult).ToArray());
}
}
} }
public IEnumerable<DownloadDecision> GetSearchDecision(IEnumerable<ReportInfo> reports, SearchDefinitionBase searchDefinitionBase) public List<DownloadDecision> GetSearchDecision(IEnumerable<ReportInfo> reports, SearchDefinitionBase searchDefinitionBase)
{ {
foreach (var report in reports) return GetDecisions(reports, remoteEpisode =>
{ {
var remoteEpisode = _parsingService.Map(report); var generalReasons = GetGeneralRejectionReasons(remoteEpisode);
var generalReasons = GetGeneralRejectionReasons(remoteEpisode); var searchReasons = GetSearchRejectionReasons(remoteEpisode, searchDefinitionBase);
var searchReasons = GetSearchRejectionReasons(remoteEpisode, searchDefinitionBase); return generalReasons.Union(searchReasons);
}).ToList();
yield return new DownloadDecision(remoteEpisode, generalReasons.Union(searchReasons).ToArray());
}
} }
@ -58,6 +49,29 @@ namespace NzbDrone.Core.DecisionEngine
.Select(spec => spec.RejectionReason); .Select(spec => spec.RejectionReason);
} }
private IEnumerable<DownloadDecision> GetDecisions(IEnumerable<ReportInfo> reports, Func<RemoteEpisode, IEnumerable<string>> decisionCallback)
{
foreach (var report in reports)
{
var parsedEpisodeInfo = Parser.Parser.ParseTitle(report.Title);
if (parsedEpisodeInfo != null)
{
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo);
remoteEpisode.Report = report;
if (remoteEpisode.Series != null)
{
yield return new DownloadDecision(remoteEpisode, decisionCallback(remoteEpisode).ToArray());
}
else
{
yield return new DownloadDecision(remoteEpisode, "Unknown Series");
}
}
}
}
private IEnumerable<string> GetSearchRejectionReasons(RemoteEpisode report, SearchDefinitionBase searchDefinitionBase) private IEnumerable<string> GetSearchRejectionReasons(RemoteEpisode report, SearchDefinitionBase searchDefinitionBase)
{ {
return _specifications return _specifications

@ -30,13 +30,13 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
{ {
_logger.Trace("Beginning size check for: {0}", subject); _logger.Trace("Beginning size check for: {0}", subject);
if (subject.Quality.Quality == Quality.RAWHD) if (subject.ParsedEpisodeInfo.Quality.Quality == Quality.RAWHD)
{ {
_logger.Trace("Raw-HD release found, skipping size check."); _logger.Trace("Raw-HD release found, skipping size check.");
return true; return true;
} }
var qualityType = _qualityTypeProvider.Get((int)subject.Quality.Quality); var qualityType = _qualityTypeProvider.Get((int)subject.ParsedEpisodeInfo.Quality.Quality);
if (qualityType.MaxSize == 0) if (qualityType.MaxSize == 0)
{ {

@ -1,5 +1,4 @@
using NLog; using NLog;
using NzbDrone.Core.Model;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -24,10 +23,10 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public virtual bool IsSatisfiedBy(RemoteEpisode subject) public virtual bool IsSatisfiedBy(RemoteEpisode subject)
{ {
_logger.Trace("Checking if report meets language requirements. {0}", subject.Language); _logger.Trace("Checking if report meets language requirements. {0}", subject.ParsedEpisodeInfo.Language);
if (subject.Language != Language.English) if (subject.ParsedEpisodeInfo.Language != Language.English)
{ {
_logger.Trace("Report Language: {0} rejected because it is not English", subject.Language); _logger.Trace("Report Language: {0} rejected because it is not English", subject.ParsedEpisodeInfo.Language);
return false; return false;
} }

@ -1,10 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NLog;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Model;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -27,7 +24,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
} }
} }
public virtual bool IsSatisfiedBy(RemoteEpisode subject) public bool IsSatisfiedBy(RemoteEpisode subject)
{ {
var downloadClient = _downloadClientProvider.GetDownloadClient(); var downloadClient = _downloadClientProvider.GetDownloadClient();
@ -36,25 +33,25 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
return !IsInQueue(subject, queue); return !IsInQueue(subject, queue);
} }
public virtual bool IsInQueue(RemoteEpisode newEpisode, IEnumerable<ParsedEpisodeInfo> queue) private bool IsInQueue(RemoteEpisode newEpisode, IEnumerable<ParsedEpisodeInfo> queue)
{ {
var matchingTitle = queue.Where(q => String.Equals(q.SeriesTitle, newEpisode.Series.CleanTitle, StringComparison.InvariantCultureIgnoreCase)); var matchingTitle = queue.Where(q => String.Equals(q.SeriesTitle, newEpisode.Series.CleanTitle, StringComparison.InvariantCultureIgnoreCase));
var matchingTitleWithQuality = matchingTitle.Where(q => q.Quality >= newEpisode.Quality); var matchingTitleWithQuality = matchingTitle.Where(q => q.Quality >= newEpisode.ParsedEpisodeInfo.Quality);
if (newEpisode.Series.SeriesType == SeriesTypes.Daily) if (newEpisode.Series.SeriesType == SeriesTypes.Daily)
{ {
return matchingTitleWithQuality.Any(q => q.AirDate.Value.Date == newEpisode.AirDate.Value.Date); return matchingTitleWithQuality.Any(q => q.AirDate.Value.Date == newEpisode.ParsedEpisodeInfo.AirDate.Value.Date);
} }
var matchingSeason = matchingTitleWithQuality.Where(q => q.SeasonNumber == newEpisode.SeasonNumber); var matchingSeason = matchingTitleWithQuality.Where(q => q.SeasonNumber == newEpisode.ParsedEpisodeInfo.SeasonNumber);
if (newEpisode.FullSeason) if (newEpisode.ParsedEpisodeInfo.FullSeason)
{ {
return matchingSeason.Any(); return matchingSeason.Any();
} }
return matchingSeason.Any(q => q.EpisodeNumbers != null && q.EpisodeNumbers.Any(e => newEpisode.EpisodeNumbers.Contains(e))); return matchingSeason.Any(q => q.EpisodeNumbers != null && q.EpisodeNumbers.Any(e => newEpisode.ParsedEpisodeInfo.EpisodeNumbers.Contains(e)));
} }
} }

@ -24,10 +24,10 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public virtual bool IsSatisfiedBy(RemoteEpisode subject) public virtual bool IsSatisfiedBy(RemoteEpisode subject)
{ {
_logger.Trace("Checking if report meets quality requirements. {0}", subject.Quality); _logger.Trace("Checking if report meets quality requirements. {0}", subject.ParsedEpisodeInfo.Quality);
if (!subject.Series.QualityProfile.Allowed.Contains(subject.Quality.Quality)) if (!subject.Series.QualityProfile.Allowed.Contains(subject.ParsedEpisodeInfo.Quality.Quality))
{ {
_logger.Trace("Quality {0} rejected by Series' quality profile", subject.Quality); _logger.Trace("Quality {0} rejected by Series' quality profile", subject.ParsedEpisodeInfo.Quality);
return false; return false;
} }

@ -31,7 +31,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
var episode = _episodeService.GetEpisode(dailySearchSpec.SeriesId, dailySearchSpec.Airtime); var episode = _episodeService.GetEpisode(dailySearchSpec.SeriesId, dailySearchSpec.Airtime);
if (!remoteEpisode.AirDate.HasValue || remoteEpisode.AirDate.Value != episode.AirDate.Value) if (!remoteEpisode.ParsedEpisodeInfo.AirDate.HasValue || remoteEpisode.ParsedEpisodeInfo.AirDate.Value.Date != episode.AirDate.Value.Date)
{ {
_logger.Trace("Episode AirDate does not match searched episode number, skipping."); _logger.Trace("Episode AirDate does not match searched episode number, skipping.");
return false; return false;

@ -1,7 +1,5 @@
using NLog; using NLog;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Model;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search namespace NzbDrone.Core.DecisionEngine.Specifications.Search
@ -28,7 +26,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
var singleEpisodeSpec = searchDefinitionBase as SeasonSearchDefinition; var singleEpisodeSpec = searchDefinitionBase as SeasonSearchDefinition;
if (singleEpisodeSpec == null) return true; if (singleEpisodeSpec == null) return true;
if (singleEpisodeSpec.SeasonNumber != remoteEpisode.SeasonNumber) if (singleEpisodeSpec.SeasonNumber != remoteEpisode.ParsedEpisodeInfo.SeasonNumber)
{ {
_logger.Trace("Season number does not match searched season number, skipping."); _logger.Trace("Season number does not match searched season number, skipping.");
return false; return false;

@ -1,8 +1,6 @@
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Model;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search namespace NzbDrone.Core.DecisionEngine.Specifications.Search
@ -29,7 +27,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
var singleEpisodeSpec = searchDefinitionBase as SingleEpisodeSearchDefinition; var singleEpisodeSpec = searchDefinitionBase as SingleEpisodeSearchDefinition;
if (singleEpisodeSpec == null) return true; if (singleEpisodeSpec == null) return true;
if (singleEpisodeSpec.SeasonNumber != remoteEpisode.SeasonNumber) if (singleEpisodeSpec.SeasonNumber != remoteEpisode.ParsedEpisodeInfo.SeasonNumber)
{ {
_logger.Trace("Season number does not match searched season number, skipping."); _logger.Trace("Season number does not match searched season number, skipping.");
return false; return false;

@ -30,12 +30,12 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
{ {
_logger.Trace("Comparing file quality with report. Existing file is {0}", file.Quality); _logger.Trace("Comparing file quality with report. Existing file is {0}", file.Quality);
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.QualityProfile, file.Quality, subject.Quality)) if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.QualityProfile, file.Quality, subject.ParsedEpisodeInfo.Quality))
{ {
return false; return false;
} }
if (subject.Quality.Proper && file.DateAdded < DateTime.Today.AddDays(-7)) if (subject.ParsedEpisodeInfo.Quality.Proper && file.DateAdded < DateTime.Today.AddDays(-7))
{ {
_logger.Trace("Proper for old file, skipping: {0}", subject); _logger.Trace("Proper for old file, skipping: {0}", subject);
return false; return false;

@ -33,7 +33,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
if (bestQualityInHistory != null) if (bestQualityInHistory != null)
{ {
_logger.Trace("Comparing history quality with report. History is {0}", bestQualityInHistory); _logger.Trace("Comparing history quality with report. History is {0}", bestQualityInHistory);
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.QualityProfile, bestQualityInHistory, subject.Quality)) if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.QualityProfile, bestQualityInHistory, subject.ParsedEpisodeInfo.Quality))
return false; return false;
} }
} }

@ -7,19 +7,61 @@ using Newtonsoft.Json.Linq;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Parser.Model;
using RestSharp;
namespace NzbDrone.Core.Download.Clients.Sabnzbd namespace NzbDrone.Core.Download.Clients.Sabnzbd
{ {
public class SabRequestBuilder
{
private readonly IConfigService _configService;
public SabRequestBuilder(IConfigService configService)
{
_configService = configService;
}
public IRestRequest AddToQueueRequest(RemoteEpisode remoteEpisode)
{
string cat = _configService.SabTvCategory;
int priority = remoteEpisode.IsRecentEpisode() ? (int)_configService.SabRecentTvPriority : (int)_configService.SabBacklogTvPriority;
string name = remoteEpisode.Report.NzbUrl.Replace("&", "%26");
string nzbName = HttpUtility.UrlEncode(remoteEpisode.Report.Title);
string action = string.Format("mode=addurl&name={0}&priority={1}&pp=3&cat={2}&nzbname={3}&output=json",
name, priority, cat, nzbName);
string request = GetSabRequest(action);
return new RestRequest(request);
}
private string GetSabRequest(string action)
{
return string.Format(@"http://{0}:{1}/api?{2}&apikey={3}&ma_username={4}&ma_password={5}",
_configService.SabHost,
_configService.SabPort,
action,
_configService.SabApiKey,
_configService.SabUsername,
_configService.SabPassword);
}
}
public class SabnzbdClient : IDownloadClient public class SabnzbdClient : IDownloadClient
{ {
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
private readonly IConfigService _configService; private readonly IConfigService _configService;
private readonly IHttpProvider _httpProvider; private readonly IHttpProvider _httpProvider;
private readonly Logger _logger;
public SabnzbdClient(IConfigService configService, IHttpProvider httpProvider) public SabnzbdClient(IConfigService configService, IHttpProvider httpProvider, Logger logger)
{ {
_configService = configService; _configService = configService;
_httpProvider = httpProvider; _httpProvider = httpProvider;
_logger = logger;
} }
public virtual bool DownloadNzb(string url, string title, bool recentlyAired) public virtual bool DownloadNzb(string url, string title, bool recentlyAired)
@ -36,11 +78,11 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
name, priority, cat, nzbName); name, priority, cat, nzbName);
string request = GetSabRequest(action); string request = GetSabRequest(action);
logger.Info("Adding report [{0}] to the queue.", title); _logger.Info("Adding report [{0}] to the queue.", title);
var response = _httpProvider.DownloadString(request); var response = _httpProvider.DownloadString(request);
logger.Debug("Queue Response: [{0}]", response); _logger.Debug("Queue Response: [{0}]", response);
CheckForError(response); CheckForError(response);
return true; return true;
@ -48,7 +90,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
catch (WebException ex) catch (WebException ex)
{ {
logger.Error("Error communicating with SAB: " + ex.Message); _logger.Error("Error communicating with SAB: " + ex.Message);
} }
return false; return false;
@ -62,9 +104,9 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
CheckForError(response); CheckForError(response);
var sabQeueu = JsonConvert.DeserializeObject<SabQueue>(JObject.Parse(response).SelectToken("queue").ToString()).Items; var sabQueue = JsonConvert.DeserializeObject<SabQueue>(JObject.Parse(response).SelectToken("queue").ToString()).Items;
foreach (var sabQueueItem in sabQeueu) foreach (var sabQueueItem in sabQueue)
{ {
var queueItem = new QueueItem(); var queueItem = new QueueItem();
queueItem.Id = sabQueueItem.Id; queueItem.Id = sabQueueItem.Id;
@ -163,7 +205,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.DebugException("Failed to Test SABnzbd", ex); _logger.DebugException("Failed to Test SABnzbd", ex);
} }
return String.Empty; return String.Empty;

@ -37,10 +37,6 @@ namespace NzbDrone.Core.Download
public bool DownloadReport(RemoteEpisode episode) public bool DownloadReport(RemoteEpisode episode)
{ {
var downloadTitle = episode.Report.Title; var downloadTitle = episode.Report.Title;
if (!_configService.DownloadClientUseSceneName)
{
downloadTitle = episode.GetDownloadTitle();
}
var provider = _downloadClientProvider.GetDownloadClient(); var provider = _downloadClientProvider.GetDownloadClient();
var recentEpisode = ContainsRecentEpisode(episode); var recentEpisode = ContainsRecentEpisode(episode);

@ -1,8 +1,4 @@
using System; using System;
using Newtonsoft.Json;
using NzbDrone.Core.Download.Clients.Sabnzbd;
using NzbDrone.Core.Download.Clients.Sabnzbd.JsonConverters;
using NzbDrone.Core.Model;
namespace NzbDrone.Core.Download namespace NzbDrone.Core.Download
{ {
@ -14,8 +10,6 @@ namespace NzbDrone.Core.Download
public decimal SizeLeft { get; set; } public decimal SizeLeft { get; set; }
public int Percentage { get; set; }
public string Id { get; set; } public string Id { get; set; }
public TimeSpan Timeleft { get; set; } public TimeSpan Timeleft { get; set; }

@ -83,7 +83,8 @@ namespace NzbDrone.Core.ExternalNotification
try try
{ {
_logger.Trace("Sending grab notification to {0}", Name); _logger.Trace("Sending grab notification to {0}", Name);
OnGrab(message.Episode.GetDownloadTitle()); //todo: pass all the info to grab event and let the handlers deal with it.
OnGrab(message.Episode.ToString());
} }
catch (Exception e) catch (Exception e)

@ -56,7 +56,7 @@ namespace NzbDrone.Core.History
{ {
Date = DateTime.Now, Date = DateTime.Now,
Indexer = message.Episode.Report.Indexer, Indexer = message.Episode.Report.Indexer,
Quality = message.Episode.Quality, Quality = message.Episode.ParsedEpisodeInfo.Quality,
NzbTitle = message.Episode.Report.Title, NzbTitle = message.Episode.Report.Title,
EpisodeId = episode.Id, EpisodeId = episode.Id,
NzbInfoUrl = message.Episode.Report.NzbInfoUrl, NzbInfoUrl = message.Episode.Report.NzbInfoUrl,

@ -40,8 +40,8 @@ namespace NzbDrone.Core.Indexers
var qualifiedReports = decisions var qualifiedReports = decisions
.Where(c => c.Approved) .Where(c => c.Approved)
.Select(c => c.Episode) .Select(c => c.RemoteEpisode)
.OrderByDescending(c => c.Quality) .OrderByDescending(c => c.ParsedEpisodeInfo.Quality)
.ThenBy(c => c.Episodes.Select(e => e.EpisodeNumber).MinOrDefault()) .ThenBy(c => c.Episodes.Select(e => e.EpisodeNumber).MinOrDefault())
.ThenBy(c => c.Report.Age); .ThenBy(c => c.Report.Age);

@ -1,7 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.Model;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Parser.Model namespace NzbDrone.Core.Parser.Model
@ -9,10 +7,9 @@ namespace NzbDrone.Core.Parser.Model
public class ParsedEpisodeInfo public class ParsedEpisodeInfo
{ {
public string SeriesTitle { get; set; } public string SeriesTitle { get; set; }
public string OriginalString { get; set; }
public QualityModel Quality { get; set; } public QualityModel Quality { get; set; }
public int SeasonNumber { get; set; } public int SeasonNumber { get; set; }
public List<int> EpisodeNumbers { get; set; } public int[] EpisodeNumbers { get; set; }
public DateTime? AirDate { get; set; } public DateTime? AirDate { get; set; }
public Language Language { get; set; } public Language Language { get; set; }

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Parser.Model namespace NzbDrone.Core.Parser.Model
@ -9,100 +8,16 @@ namespace NzbDrone.Core.Parser.Model
public class RemoteEpisode public class RemoteEpisode
{ {
public ReportInfo Report { get; set; } public ReportInfo Report { get; set; }
public bool FullSeason { get; set; } public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; }
public Series Series { get; set; } public Series Series { get; set; }
public List<Episode> Episodes { get; set; } public List<Episode> Episodes { get; set; }
public QualityModel Quality { get; set; } public bool IsRecentEpisode()
public Language Language { get; set; }
public int SeasonNumber
{
get { return Episodes.Select(e => e.SeasonNumber).Distinct().SingleOrDefault(); }
}
public DateTime? AirDate
{
get
{
return Episodes.Single().AirDate;
}
}
public IEnumerable<int> EpisodeNumbers
{
get
{
return Episodes.Select(c => c.EpisodeNumber).Distinct();
}
}
public string GetDownloadTitle()
{
var seriesTitle = FileNameBuilder.CleanFilename(Series.Title);
//Handle Full Naming
if (FullSeason)
{
var seasonResult = String.Format("{0} - Season {1} [{2}]", seriesTitle, SeasonNumber, Quality);
if (Quality.Proper)
seasonResult += " [Proper]";
return seasonResult;
}
if (Series.SeriesType == SeriesTypes.Daily)
{
var dailyResult = String.Format("{0} - {1:yyyy-MM-dd} - {2} [{3}]", seriesTitle,
AirDate, Episodes.First().Title, Quality);
if (Quality.Proper)
dailyResult += " [Proper]";
return dailyResult;
}
//Show Name - 1x01-1x02 - Episode Name
//Show Name - 1x01 - Episode Name
var episodeString = new List<string>();
var episodeNames = new List<string>();
foreach (var episode in Episodes)
{
episodeString.Add(String.Format("{0}x{1:00}", episode.SeasonNumber, episode.EpisodeNumber));
episodeNames.Add(Core.Parser.Parser.CleanupEpisodeTitle(episode.Title));
}
var epNumberString = String.Join("-", episodeString);
string episodeName;
if (episodeNames.Distinct().Count() == 1)
episodeName = episodeNames.First();
else
episodeName = String.Join(" + ", episodeNames.Distinct());
var result = String.Format("{0} - {1} - {2} [{3}]", seriesTitle, epNumberString, episodeName, Quality);
if (Quality.Proper)
{
result += " [Proper]";
}
return result;
}
public override string ToString()
{ {
throw new NotImplementedException(); return Episodes.Any(e => e.AirDate >= DateTime.Today.AddDays(-7));
} }
} }
} }

@ -5,7 +5,6 @@ using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.Model;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -90,11 +89,7 @@ namespace NzbDrone.Core.Parser
result = ParseTitle(fileInfo.FullName); result = ParseTitle(fileInfo.FullName);
} }
if (result != null) if (result == null)
{
result.OriginalString = path;
}
else
{ {
Logger.Warn("Unable to parse episode info from path {0}", path); Logger.Warn("Unable to parse episode info from path {0}", path);
} }
@ -124,7 +119,6 @@ namespace NzbDrone.Core.Parser
result.Language = ParseLanguage(title); result.Language = ParseLanguage(title);
result.Quality = ParseQuality(title); result.Quality = ParseQuality(title);
result.OriginalString = title;
return result; return result;
} }
} }
@ -172,7 +166,7 @@ namespace NzbDrone.Core.Parser
result = new ParsedEpisodeInfo result = new ParsedEpisodeInfo
{ {
SeasonNumber = seasons.First(), SeasonNumber = seasons.First(),
EpisodeNumbers = new List<int>() EpisodeNumbers = new int[0],
}; };
foreach (Match matchGroup in matchCollection) foreach (Match matchGroup in matchCollection)
@ -184,7 +178,7 @@ namespace NzbDrone.Core.Parser
{ {
var first = Convert.ToInt32(episodeCaptures.First().Value); var first = Convert.ToInt32(episodeCaptures.First().Value);
var last = Convert.ToInt32(episodeCaptures.Last().Value); var last = Convert.ToInt32(episodeCaptures.Last().Value);
result.EpisodeNumbers = Enumerable.Range(first, last - first + 1).ToList(); result.EpisodeNumbers = Enumerable.Range(first, last - first + 1).ToArray();
} }
else else
{ {

@ -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.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -11,7 +10,7 @@ namespace NzbDrone.Core.Parser
{ {
LocalEpisode GetEpisodes(string fileName, Series series); LocalEpisode GetEpisodes(string fileName, Series series);
Series GetSeries(string title); Series GetSeries(string title);
RemoteEpisode Map(ReportInfo reportInfo); RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo);
} }
public class ParsingService : IParsingService public class ParsingService : IParsingService
@ -65,32 +64,23 @@ namespace NzbDrone.Core.Parser
return _seriesService.FindByTitle(searchTitle); return _seriesService.FindByTitle(searchTitle);
} }
public RemoteEpisode Map(ReportInfo reportInfo) public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo)
{ {
var parsedInfo = Parser.ParseTitle(reportInfo.Title); var remoteEpisode = new RemoteEpisode
{
if (parsedInfo == null) ParsedEpisodeInfo = parsedEpisodeInfo,
{ };
return null;
}
var series = _seriesService.FindByTitle(parsedInfo.SeriesTitle); var series = _seriesService.FindByTitle(parsedEpisodeInfo.SeriesTitle);
if (series == null) if (series == null)
{ {
_logger.Trace("No matching series {0}", parsedInfo.SeriesTitle); _logger.Trace("No matching series {0}", parsedEpisodeInfo.SeriesTitle);
return null; return remoteEpisode;
} }
var remoteEpisode = new RemoteEpisode remoteEpisode.Series = series;
{ remoteEpisode.Episodes = GetEpisodes(parsedEpisodeInfo, series);
Series = series,
Episodes = GetEpisodes(parsedInfo, series),
FullSeason = parsedInfo.FullSeason,
Language = parsedInfo.Language,
Quality = parsedInfo.Quality,
Report = reportInfo
};
return remoteEpisode; return remoteEpisode;
} }
@ -104,7 +94,7 @@ namespace NzbDrone.Core.Parser
if (series.SeriesType == SeriesTypes.Standard) if (series.SeriesType == SeriesTypes.Standard)
{ {
//Todo: Collect this as a Series we want to treat as a daily series, or possible parsing error //Todo: Collect this as a Series we want to treat as a daily series, or possible parsing error
_logger.Warn("Found daily-style episode for non-daily series: {0}. {1}", series.Title, parsedEpisodeInfo.OriginalString); _logger.Warn("Found daily-style episode for non-daily series: {0}.", series.Title);
return new List<Episode>(); return new List<Episode>();
} }

@ -3,6 +3,7 @@ using System.Net;
using FluentAssertions; using FluentAssertions;
using NLog; using NLog;
using NzbDrone.Api.REST; using NzbDrone.Api.REST;
using NzbDrone.Common;
using RestSharp; using RestSharp;
namespace NzbDrone.Integration.Test.Client namespace NzbDrone.Integration.Test.Client
@ -13,6 +14,7 @@ namespace NzbDrone.Integration.Test.Client
private readonly string _resource; private readonly string _resource;
private readonly Logger _logger; private readonly Logger _logger;
private readonly JsonSerializer _jsonSerializer;
public ClientBase(IRestClient restClient, string resource = null) public ClientBase(IRestClient restClient, string resource = null)
{ {
@ -23,6 +25,11 @@ namespace NzbDrone.Integration.Test.Client
_restClient = restClient; _restClient = restClient;
_resource = resource; _resource = resource;
_jsonSerializer = new JsonSerializer();
_logger = LogManager.GetLogger("REST"); _logger = LogManager.GetLogger("REST");
} }
@ -60,13 +67,13 @@ namespace NzbDrone.Integration.Test.Client
}; };
} }
protected T Get<T>(IRestRequest request, HttpStatusCode statusCode = HttpStatusCode.OK) where T : new() protected T Get<T>(IRestRequest request, HttpStatusCode statusCode = HttpStatusCode.OK) where T : class, new()
{ {
request.Method = Method.GET; request.Method = Method.GET;
return Execute<T>(request, statusCode); return Execute<T>(request, statusCode);
} }
public T Post<T>(IRestRequest request, HttpStatusCode statusCode = HttpStatusCode.Created) where T : new() public T Post<T>(IRestRequest request, HttpStatusCode statusCode = HttpStatusCode.Created) where T : class, new()
{ {
request.Method = Method.POST; request.Method = Method.POST;
return Execute<T>(request, statusCode); return Execute<T>(request, statusCode);
@ -78,11 +85,11 @@ namespace NzbDrone.Integration.Test.Client
Execute<object>(request, statusCode); Execute<object>(request, statusCode);
} }
private T Execute<T>(IRestRequest request, HttpStatusCode statusCode) where T : new() private T Execute<T>(IRestRequest request, HttpStatusCode statusCode) where T : class, new()
{ {
_logger.Info("{0}: {1}", request.Method, _restClient.BuildUri(request)); _logger.Info("{0}: {1}", request.Method, _restClient.BuildUri(request));
var response = _restClient.Execute<T>(request); var response = _restClient.Execute(request);
_logger.Info("Response: {0}", response.Content); _logger.Info("Response: {0}", response.Content);
response.StatusCode.Should().Be(statusCode); response.StatusCode.Should().Be(statusCode);
@ -94,7 +101,7 @@ namespace NzbDrone.Integration.Test.Client
response.ErrorMessage.Should().BeBlank(); response.ErrorMessage.Should().BeBlank();
return response.Data; return _jsonSerializer.Deserialize<T>(response.Content);
} }
} }

@ -0,0 +1,16 @@
using NzbDrone.Api.Indexers;
using RestSharp;
namespace NzbDrone.Integration.Test.Client
{
public class ReleaseClient : ClientBase<ReleaseResource>
{
public ReleaseClient(IRestClient restClient)
: base(restClient)
{
}
}
}

@ -31,15 +31,16 @@ namespace NzbDrone.Integration.Test
protected SeriesClient Series; protected SeriesClient Series;
protected ClientBase<RootFolderResource> RootFolders; protected ClientBase<RootFolderResource> RootFolders;
protected ClientBase<CommandResource> Commands; protected ClientBase<CommandResource> Commands;
protected ReleaseClient Releases;
static IntegrationTest() static IntegrationTest()
{ {
if (LogManager.Configuration == null || LogManager.Configuration is XmlLoggingConfiguration) if (LogManager.Configuration == null || LogManager.Configuration is XmlLoggingConfiguration)
{ {
LogManager.Configuration = new LoggingConfiguration(); LogManager.Configuration = new LoggingConfiguration();
var consoleTarget = new ConsoleTarget { Layout = "${logger} - ${message} ${exception}" }; var consoleTarget = new ConsoleTarget { Layout = "${time} - ${logger} - ${message} ${exception}" };
LogManager.Configuration.AddTarget(consoleTarget.GetType().Name, consoleTarget); LogManager.Configuration.AddTarget(consoleTarget.GetType().Name, consoleTarget);
LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Info, consoleTarget)); LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, consoleTarget));
} }
@ -85,6 +86,7 @@ namespace NzbDrone.Integration.Test
RestClient = new RestClient(url + "/api/"); RestClient = new RestClient(url + "/api/");
Series = new SeriesClient(RestClient); Series = new SeriesClient(RestClient);
Releases = new ReleaseClient(RestClient);
RootFolders = new ClientBase<RootFolderResource>(RestClient); RootFolders = new ClientBase<RootFolderResource>(RestClient);
Commands = new ClientBase<CommandResource>(RestClient); Commands = new ClientBase<CommandResource>(RestClient);

@ -75,8 +75,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Client\ClientBase.cs" /> <Compile Include="Client\ClientBase.cs" />
<Compile Include="Client\SeriesClient - Copy.cs" />
<Compile Include="Client\SeriesClient.cs" /> <Compile Include="Client\SeriesClient.cs" />
<Compile Include="CommandIntegerationTests.cs" /> <Compile Include="CommandIntegerationTests.cs" />
<Compile Include="ReleaseIntegrationTest.cs" />
<Compile Include="IntegrationTest.cs" /> <Compile Include="IntegrationTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RootFolderIntegrationTest.cs" /> <Compile Include="RootFolderIntegrationTest.cs" />

@ -0,0 +1,17 @@
using FluentAssertions;
using NUnit.Framework;
namespace NzbDrone.Integration.Test
{
[TestFixture]
public class ReleaseIntegrationTest : IntegrationTest
{
[Test]
public void should_only_have_unknown_series_releases()
{
Releases.All().Should().OnlyContain(c => c.Rejections.Contains("Unknown Series"));
}
}
}
Loading…
Cancel
Save