Added RSS support for torrents

pull/3113/head
Keivan Beigi 11 years ago
parent 89d603d71c
commit 82b06bab7a

@ -28,8 +28,8 @@ namespace NzbDrone.Api.Test.ClientSchemaTests
var schema = SchemaBuilder.GenerateSchema(model); var schema = SchemaBuilder.GenerateSchema(model);
schema.Should().Contain(c => c.Order == 1 && c.Name == "LastName" && c.Label == "Last Name" && c.HelpText == "Your Last Name" && c.Value == "Poop"); schema.Should().Contain(c => c.Order == 1 && c.Name == "LastName" && c.Label == "Last Name" && c.HelpText == "Your Last Name" && (string) c.Value == "Poop");
schema.Should().Contain(c => c.Order == 0 && c.Name == "FirstName" && c.Label == "First Name" && c.HelpText == "Your First Name" && c.Value == "Bob"); schema.Should().Contain(c => c.Order == 0 && c.Name == "FirstName" && c.Label == "First Name" && c.HelpText == "Your First Name" && (string) c.Value == "Bob");
} }
} }

@ -37,7 +37,7 @@ 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(Indexer), typeof(IndexerResource))] [TestCase(typeof(Indexer), typeof(IndexerResource))]
[TestCase(typeof(ReportInfo), typeof(ReleaseResource))] [TestCase(typeof(ReleaseInfo), typeof(ReleaseResource))]
[TestCase(typeof(ParsedEpisodeInfo), typeof(ReleaseResource))] [TestCase(typeof(ParsedEpisodeInfo), typeof(ReleaseResource))]
[TestCase(typeof(DownloadDecision), typeof(ReleaseResource))] [TestCase(typeof(DownloadDecision), typeof(ReleaseResource))]
[TestCase(typeof(Core.History.History), typeof(HistoryResource))] [TestCase(typeof(Core.History.History), typeof(HistoryResource))]

@ -42,7 +42,7 @@ namespace NzbDrone.Api.Indexers
private Response DownloadRelease(ReleaseResource release) private Response DownloadRelease(ReleaseResource release)
{ {
var remoteEpisode = _parsingService.Map(release.InjectTo<ParsedEpisodeInfo>(), 0); var remoteEpisode = _parsingService.Map(release.InjectTo<ParsedEpisodeInfo>(), 0);
remoteEpisode.Report = release.InjectTo<ReportInfo>(); remoteEpisode.Release = release.InjectTo<ReleaseInfo>();
_downloadService.DownloadReport(remoteEpisode); _downloadService.DownloadReport(remoteEpisode);
@ -82,7 +82,7 @@ namespace NzbDrone.Api.Indexers
{ {
var release = new ReleaseResource(); var release = new ReleaseResource();
release.InjectFrom(downloadDecision.RemoteEpisode.Report); release.InjectFrom(downloadDecision.RemoteEpisode.Release);
release.InjectFrom(downloadDecision.RemoteEpisode.ParsedEpisodeInfo); release.InjectFrom(downloadDecision.RemoteEpisode.ParsedEpisodeInfo);
release.InjectFrom(downloadDecision); release.InjectFrom(downloadDecision);
release.Rejections = downloadDecision.Rejections.ToList(); release.Rejections = downloadDecision.Rejections.ToList();

@ -12,8 +12,6 @@ namespace NzbDrone.Api.Indexers
public Int32 Age { get; set; } public Int32 Age { get; set; }
public Int64 Size { get; set; } public Int64 Size { get; set; }
public String Indexer { get; set; } public String Indexer { get; set; }
public String NzbInfoUrl { get; set; }
public String NzbUrl { get; set; }
public String ReleaseGroup { get; set; } public String ReleaseGroup { get; set; }
public String Title { get; set; } public String Title { get; set; }
public Boolean FullSeason { get; set; } public Boolean FullSeason { get; set; }
@ -26,5 +24,9 @@ namespace NzbDrone.Api.Indexers
public Boolean Approved { get; set; } public Boolean Approved { get; set; }
public Int32 TvRageId { get; set; } public Int32 TvRageId { get; set; }
public List<string> Rejections { get; set; } public List<string> Rejections { get; set; }
public DateTime PublishDate { get; set; }
public String CommentUrl { get; set; }
public String DownloadUrl { get; set; }
public String InfoUrl { get; set; }
} }
} }

@ -26,14 +26,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
parseResultMulti = new RemoteEpisode parseResultMulti = new RemoteEpisode
{ {
Report = new ReportInfo(), Release = new ReleaseInfo(),
ParsedEpisodeInfo = new ParsedEpisodeInfo { 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(), Release = new ReleaseInfo(),
ParsedEpisodeInfo = new ParsedEpisodeInfo { 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() }
@ -59,7 +59,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void IsAcceptableSize_true_single_episode_not_first_or_last_30_minute() public void IsAcceptableSize_true_single_episode_not_first_or_last_30_minute()
{ {
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Report.Size = 184572800; parseResultSingle.Release.Size = 184572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -78,7 +78,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void IsAcceptableSize_true_single_episode_not_first_or_last_60_minute() public void IsAcceptableSize_true_single_episode_not_first_or_last_60_minute()
{ {
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Report.Size = 368572800; parseResultSingle.Release.Size = 368572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -97,7 +97,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void IsAcceptableSize_false_single_episode_not_first_or_last_30_minute() public void IsAcceptableSize_false_single_episode_not_first_or_last_30_minute()
{ {
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Report.Size = 1.Gigabytes(); parseResultSingle.Release.Size = 1.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -116,7 +116,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
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.Release.Size = 1.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -133,7 +133,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
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.Release.Size = 184572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -152,7 +152,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
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.Release.Size = 368572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -171,7 +171,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
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.Release.Size = 1.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -190,7 +190,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
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.Release.Size = 10.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -209,7 +209,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
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.Release.Size = 184572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -228,7 +228,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
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.Release.Size = 368572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -247,7 +247,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
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.Release.Size = 1.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -268,7 +268,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Report.Size = 10.Gigabytes(); parseResultSingle.Release.Size = 10.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -289,7 +289,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Report.Size = 18457280000; parseResultSingle.Release.Size = 18457280000;
qualityType.MaxSize = 0; qualityType.MaxSize = 0;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -311,7 +311,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Report.Size = 36857280000; parseResultSingle.Release.Size = 36857280000;
qualityType.MaxSize = 0; qualityType.MaxSize = 0;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -334,7 +334,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Series.SeriesType = SeriesTypes.Daily; parseResultSingle.Series.SeriesType = SeriesTypes.Daily;
parseResultSingle.Report.Size = 300.Megabytes(); parseResultSingle.Release.Size = 300.Megabytes();
qualityType.MaxSize = (int)600.Megabytes(); qualityType.MaxSize = (int)600.Megabytes();

@ -15,7 +15,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[TestFixture] [TestFixture]
public class DownloadDecisionMakerFixture : CoreTest<DownloadDecisionMaker> public class DownloadDecisionMakerFixture : CoreTest<DownloadDecisionMaker>
{ {
private List<ReportInfo> _reports; private List<ReleaseInfo> _reports;
private RemoteEpisode _remoteEpisode; private RemoteEpisode _remoteEpisode;
private Mock<IDecisionEngineSpecification> _pass1; private Mock<IDecisionEngineSpecification> _pass1;
@ -56,7 +56,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_fail3.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null)).Returns(false); _fail3.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null)).Returns(false);
_fail3.Setup(c => c.RejectionReason).Returns("_fail3"); _fail3.Setup(c => c.RejectionReason).Returns("_fail3");
_reports = new List<ReportInfo> { new ReportInfo { Title = "The.Office.S03E115.DVDRip.XviD-OSiTV" } }; _reports = new List<ReleaseInfo> { new ReleaseInfo { Title = "The.Office.S03E115.DVDRip.XviD-OSiTV" } };
_remoteEpisode = new RemoteEpisode { Series = new Series() }; _remoteEpisode = new RemoteEpisode { Series = new Series() };
Mocker.GetMock<IParsingService>().Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>())) Mocker.GetMock<IParsingService>().Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>()))
@ -177,11 +177,11 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Mocker.GetMock<IParsingService>().Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>())) Mocker.GetMock<IParsingService>().Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>()))
.Throws<TestException>(); .Throws<TestException>();
_reports = new List<ReportInfo> _reports = new List<ReleaseInfo>
{ {
new ReportInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"}, new ReleaseInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"},
new ReportInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"}, new ReleaseInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"},
new ReportInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"} new ReleaseInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"}
}; };
Subject.GetRssDecision(_reports); Subject.GetRssDecision(_reports);

@ -17,7 +17,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
_parseResult = new RemoteEpisode _parseResult = new RemoteEpisode
{ {
Report = new ReportInfo Release = new ReleaseInfo
{ {
Title = "Dexter.S08E01.EDITED.WEBRip.x264-KYR" Title = "Dexter.S08E01.EDITED.WEBRip.x264-KYR"
} }

@ -1,4 +1,5 @@
using FluentAssertions; using System;
using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
@ -19,9 +20,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
parseResult = new RemoteEpisode parseResult = new RemoteEpisode
{ {
Report = new ReportInfo Release = new ReleaseInfo
{ {
Age = 100 PublishDate = DateTime.Now.AddDays(-100)
} }
}; };
} }

@ -34,8 +34,8 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
remoteEpisode.Episodes = new List<Episode>(); remoteEpisode.Episodes = new List<Episode>();
remoteEpisode.Episodes.AddRange(episodes); remoteEpisode.Episodes.AddRange(episodes);
remoteEpisode.Report = new ReportInfo(); remoteEpisode.Release = new ReleaseInfo();
remoteEpisode.Report.Age = 0; remoteEpisode.Release.PublishDate = DateTime.UtcNow;
return remoteEpisode; return remoteEpisode;
} }

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
@ -32,9 +33,9 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
remoteEpisode.Episodes = new List<Episode>(); remoteEpisode.Episodes = new List<Episode>();
remoteEpisode.Episodes.AddRange(episodes); remoteEpisode.Episodes.AddRange(episodes);
remoteEpisode.Report = new ReportInfo(); remoteEpisode.Release = new ReleaseInfo();
remoteEpisode.Report.Age = Age; remoteEpisode.Release.PublishDate = DateTime.Now.AddDays(-Age);
remoteEpisode.Report.Size = size; remoteEpisode.Release.Size = size;
return remoteEpisode; return remoteEpisode;
} }

@ -30,9 +30,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
Mocker.GetMock<IConfigService>().SetupGet(c => c.BlackholeFolder).Returns(_blackHoleFolder); Mocker.GetMock<IConfigService>().SetupGet(c => c.BlackholeFolder).Returns(_blackHoleFolder);
_remoteEpisode = new RemoteEpisode(); _remoteEpisode = new RemoteEpisode();
_remoteEpisode.Report = new ReportInfo(); _remoteEpisode.Release = new ReleaseInfo();
_remoteEpisode.Report.Title = _title; _remoteEpisode.Release.Title = _title;
_remoteEpisode.Report.NzbUrl = _nzbUrl; _remoteEpisode.Release.DownloadUrl = _nzbUrl;
} }
private void WithExistingFile() private void WithExistingFile()
@ -58,7 +58,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
{ {
var illegalTitle = "Saturday Night Live - S38E08 - Jeremy Renner/Maroon 5 [SDTV]"; var illegalTitle = "Saturday Night Live - S38E08 - Jeremy Renner/Maroon 5 [SDTV]";
var expectedFilename = Path.Combine(_blackHoleFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV].nzb"); var expectedFilename = Path.Combine(_blackHoleFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV].nzb");
_remoteEpisode.Report.Title = illegalTitle; _remoteEpisode.Release.Title = illegalTitle;
Subject.DownloadNzb(_remoteEpisode); Subject.DownloadNzb(_remoteEpisode);

@ -30,9 +30,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetProviderTests
fakeConfig.SetupGet(c => c.NzbgetRecentTvPriority).Returns(PriorityType.High); fakeConfig.SetupGet(c => c.NzbgetRecentTvPriority).Returns(PriorityType.High);
_remoteEpisode = new RemoteEpisode(); _remoteEpisode = new RemoteEpisode();
_remoteEpisode.Report = new ReportInfo(); _remoteEpisode.Release = new ReleaseInfo();
_remoteEpisode.Report.Title = _title; _remoteEpisode.Release.Title = _title;
_remoteEpisode.Report.NzbUrl = _url; _remoteEpisode.Release.DownloadUrl = _url;
_remoteEpisode.Episodes = Builder<Episode>.CreateListOfSize(1) _remoteEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
.All() .All()

@ -34,9 +34,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
Mocker.GetMock<IConfigService>().SetupGet(c => c.DownloadedEpisodesFolder).Returns(_sabDrop); Mocker.GetMock<IConfigService>().SetupGet(c => c.DownloadedEpisodesFolder).Returns(_sabDrop);
_remoteEpisode = new RemoteEpisode(); _remoteEpisode = new RemoteEpisode();
_remoteEpisode.Report = new ReportInfo(); _remoteEpisode.Release = new ReleaseInfo();
_remoteEpisode.Report.Title = _title; _remoteEpisode.Release.Title = _title;
_remoteEpisode.Report.NzbUrl = _nzbUrl; _remoteEpisode.Release.DownloadUrl = _nzbUrl;
_remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo(); _remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo();
_remoteEpisode.ParsedEpisodeInfo.FullSeason = false; _remoteEpisode.ParsedEpisodeInfo.FullSeason = false;
@ -72,7 +72,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
[Test] [Test]
public void should_throw_if_full_season_download() public void should_throw_if_full_season_download()
{ {
_remoteEpisode.Report.Title = "30 Rock - Season 1"; _remoteEpisode.Release.Title = "30 Rock - Season 1";
_remoteEpisode.ParsedEpisodeInfo.FullSeason = true; _remoteEpisode.ParsedEpisodeInfo.FullSeason = true;
Assert.Throws<NotImplementedException>(() => Subject.DownloadNzb(_remoteEpisode)); Assert.Throws<NotImplementedException>(() => Subject.DownloadNzb(_remoteEpisode));
@ -83,7 +83,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
{ {
var illegalTitle = "Saturday Night Live - S38E08 - Jeremy Renner/Maroon 5 [SDTV]"; var illegalTitle = "Saturday Night Live - S38E08 - Jeremy Renner/Maroon 5 [SDTV]";
var expectedFilename = Path.Combine(_pneumaticFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV].nzb"); var expectedFilename = Path.Combine(_pneumaticFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV].nzb");
_remoteEpisode.Report.Title = illegalTitle; _remoteEpisode.Release.Title = illegalTitle;
Subject.DownloadNzb(_remoteEpisode); Subject.DownloadNzb(_remoteEpisode);

@ -35,9 +35,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests
fakeConfig.SetupGet(c => c.SabTvCategory).Returns("tv"); fakeConfig.SetupGet(c => c.SabTvCategory).Returns("tv");
_remoteEpisode = new RemoteEpisode(); _remoteEpisode = new RemoteEpisode();
_remoteEpisode.Report = new ReportInfo(); _remoteEpisode.Release = new ReleaseInfo();
_remoteEpisode.Report.Title = TITLE; _remoteEpisode.Release.Title = TITLE;
_remoteEpisode.Report.NzbUrl = URL; _remoteEpisode.Release.DownloadUrl = URL;
_remoteEpisode.Episodes = Builder<Episode>.CreateListOfSize(1) _remoteEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
.All() .All()

@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.Download
_parseResult = Builder<RemoteEpisode>.CreateNew() _parseResult = Builder<RemoteEpisode>.CreateNew()
.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.Release = Builder<ReleaseInfo>.CreateNew().Build())
.With(c => c.Episodes = episodes) .With(c => c.Episodes = episodes)
.Build(); .Build();

@ -5,7 +5,7 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.IndexerTests namespace NzbDrone.Core.Test.IndexerTests
{ {
public class BasicRssParserFixture : CoreTest<BasicRssParser> public class BasicRssParserFixture : CoreTest<RssParserBase>
{ {
[TestCase("Castle.2009.S01E14.English.HDTV.XviD-LOL", "LOL")] [TestCase("Castle.2009.S01E14.English.HDTV.XviD-LOL", "LOL")]
@ -16,7 +16,7 @@ namespace NzbDrone.Core.Test.IndexerTests
[TestCase("The.Office.S03E115.DVDRip.XviD-OSiTV", "OSiTV")] [TestCase("The.Office.S03E115.DVDRip.XviD-OSiTV", "OSiTV")]
public void parse_releaseGroup(string title, string expected) public void parse_releaseGroup(string title, string expected)
{ {
BasicRssParser.ParseReleaseGroup(title).Should().Be(expected); RssParserBase.ParseReleaseGroup(title).Should().Be(expected);
} }
@ -29,7 +29,7 @@ namespace NzbDrone.Core.Test.IndexerTests
[TestCase("845 MB", 886046720)] [TestCase("845 MB", 886046720)]
public void parse_size(string sizeString, long expectedSize) public void parse_size(string sizeString, long expectedSize)
{ {
var result = BasicRssParser.GetReportSize(sizeString); var result = RssParserBase.ParseSize(sizeString);
result.Should().Be(expectedSize); result.Should().Be(expectedSize);
} }

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using FluentAssertions; using FluentAssertions;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Eztv;
using NzbDrone.Core.Indexers.Newznab; using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.Indexers.NzbClub; using NzbDrone.Core.Indexers.NzbClub;
using NzbDrone.Core.Indexers.Wombles; using NzbDrone.Core.Indexers.Wombles;
@ -8,6 +9,7 @@ using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Test.Common.Categories; using NzbDrone.Test.Common.Categories;
using System.Linq;
namespace NzbDrone.Core.Test.IndexerTests.IntegrationTests namespace NzbDrone.Core.Test.IndexerTests.IntegrationTests
{ {
@ -43,6 +45,17 @@ namespace NzbDrone.Core.Test.IndexerTests.IntegrationTests
} }
[Test]
public void extv_rss()
{
var indexer = new Eztv();
var result = Subject.FetchRss(indexer);
ValidateTorrentResult(result, skipSize: false, skipInfo: true);
}
[Test] [Test]
public void nzbsorg_rss() public void nzbsorg_rss()
{ {
@ -63,15 +76,17 @@ namespace NzbDrone.Core.Test.IndexerTests.IntegrationTests
private void ValidateResult(IList<ReportInfo> reports, bool skipSize = false, bool skipInfo = false) private void ValidateResult(IList<ReleaseInfo> reports, bool skipSize = false, bool skipInfo = false)
{ {
reports.Should().NotBeEmpty(); reports.Should().NotBeEmpty();
reports.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.Title)); reports.Should().NotContain(c => string.IsNullOrWhiteSpace(c.Title));
reports.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.NzbUrl)); reports.Should().NotContain(c => string.IsNullOrWhiteSpace(c.DownloadUrl));
reports.Should().OnlyContain(c => c.PublishDate.Year > 2000);
reports.Should().OnlyContain(c => c.DownloadUrl.StartsWith("http"));
if (!skipInfo) if (!skipInfo)
{ {
reports.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.NzbInfoUrl)); reports.Should().NotContain(c => string.IsNullOrWhiteSpace(c.InfoUrl));
} }
if (!skipSize) if (!skipSize)
@ -80,5 +95,18 @@ namespace NzbDrone.Core.Test.IndexerTests.IntegrationTests
} }
} }
private void ValidateTorrentResult(IList<ReleaseInfo> reports, bool skipSize = false, bool skipInfo = false)
{
reports.Should().OnlyContain(c => c.GetType() == typeof(TorrentInfo));
ValidateResult(reports, skipSize, skipInfo);
reports.Should().OnlyContain(c => c.DownloadUrl.EndsWith(".torrent"));
reports.Cast<TorrentInfo>().Should().OnlyContain(c => c.MagnetUrl.StartsWith("magnet:"));
reports.Cast<TorrentInfo>().Should().NotContain(c => string.IsNullOrWhiteSpace(c.InfoHash));
}
} }
} }

@ -400,14 +400,6 @@ namespace NzbDrone.Core.Test.ParserTests
result.Should().BeNull(); result.Should().BeNull();
} }
[TestCase("[112461]-[FULL]-[#a.b.teevee@EFNet]-[ 666.Park.Avenue.S01E03.720p.HDTV.X264-DIMENSION ]-[02/31] - \"the.devils.address.103.720p-dimension.par2\" yEnc", "666.Park.Avenue.S01E03.720p.HDTV.X264-DIMENSION")]
[TestCase("[112438]-[FULL]-[#a.b.teevee@EFNet]-[ Downton_Abbey.3x05.HDTV_x264-FoV ]-[01/26] - \"downton_abbey.3x05.hdtv_x264-fov.nfo\" yEnc", "Downton_Abbey.3x05.HDTV_x264-FoV")]
[TestCase("[ 21154 ] - [ TrollHD ] - [ 00/73 ] - \"MythBusters S03E20 Escape Slide Parachute 1080i HDTV-UPSCALE DD5.1 MPEG2-TrollHD.nzb\" yEnc", "MythBusters S03E20 Escape Slide Parachute 1080i HDTV-UPSCALE DD5.1 MPEG2-TrollHD.nzb")]
public void parse_header(string title, string expected)
{
BasicRssParser.ParseHeader(title).Should().Be(expected);
}
[TestCase("76El6LcgLzqb426WoVFg1vVVVGx4uCYopQkfjmLe")] [TestCase("76El6LcgLzqb426WoVFg1vVVVGx4uCYopQkfjmLe")]
[TestCase("Vrq6e1Aba3U amCjuEgV5R2QvdsLEGYF3YQAQkw8")] [TestCase("Vrq6e1Aba3U amCjuEgV5R2QvdsLEGYF3YQAQkw8")]
[TestCase("TDAsqTea7k4o6iofVx3MQGuDK116FSjPobMuh8oB")] [TestCase("TDAsqTea7k4o6iofVx3MQGuDK116FSjPobMuh8oB")]

@ -66,7 +66,7 @@ namespace NzbDrone.Core.DecisionEngine
if (parsedEpisodeInfo != null && !string.IsNullOrWhiteSpace(parsedEpisodeInfo.SeriesTitle)) if (parsedEpisodeInfo != null && !string.IsNullOrWhiteSpace(parsedEpisodeInfo.SeriesTitle))
{ {
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, report.TvRageId); var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, report.TvRageId);
remoteEpisode.Report = report; remoteEpisode.Release = report;
if (remoteEpisode.Series != null) if (remoteEpisode.Series != null)
{ {
@ -118,9 +118,9 @@ namespace NzbDrone.Core.DecisionEngine
} }
catch (Exception e) catch (Exception e)
{ {
e.Data.Add("report", remoteEpisode.Report.ToJson()); e.Data.Add("report", remoteEpisode.Release.ToJson());
e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson()); e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson());
_logger.ErrorException("Couldn't evaluate decision on " + remoteEpisode.Report.Title, e); _logger.ErrorException("Couldn't evaluate decision on " + remoteEpisode.Release.Title, e);
return string.Format("{0}: {1}", spec.GetType().Name, e.Message); return string.Format("{0}: {1}", spec.GetType().Name, e.Message);
} }

@ -65,9 +65,9 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
} }
//If the parsed size is greater than maxSize we don't want it //If the parsed size is greater than maxSize we don't want it
if (subject.Report.Size > maxSize) if (subject.Release.Size > maxSize)
{ {
_logger.Trace("Item: {0}, Size: {1} is greater than maximum allowed size ({2}), rejecting.", subject, subject.Report.Size, maxSize); _logger.Trace("Item: {0}, Size: {1} is greater than maximum allowed size ({2}), rejecting.", subject, subject.Release.Size, maxSize);
return false; return false;
} }

@ -41,7 +41,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
foreach (var restriction in restrictions) foreach (var restriction in restrictions)
{ {
if (subject.Report.Title.ToLowerInvariant().Contains(restriction.ToLowerInvariant())) if (subject.Release.Title.ToLowerInvariant().Contains(restriction.ToLowerInvariant()))
{ {
_logger.Trace("{0} is restricted: {1}", subject, restriction); _logger.Trace("{0} is restricted: {1}", subject, restriction);
return false; return false;

@ -27,7 +27,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{ {
var age = subject.Report.Age; var age = subject.Release.Age;
_logger.Trace("Checking if report meets retention requirements. {0}", age); _logger.Trace("Checking if report meets retention requirements. {0}", age);
if (_configService.Retention > 0 && age > _configService.Retention) if (_configService.Retention > 0 && age > _configService.Retention)

@ -24,8 +24,8 @@ namespace NzbDrone.Core.Download.Clients
public void DownloadNzb(RemoteEpisode remoteEpisode) public void DownloadNzb(RemoteEpisode remoteEpisode)
{ {
var url = remoteEpisode.Report.NzbUrl; var url = remoteEpisode.Release.DownloadUrl;
var title = remoteEpisode.Report.Title; var title = remoteEpisode.Release.Title;
title = FileNameBuilder.CleanFilename(title); title = FileNameBuilder.CleanFilename(title);

@ -23,8 +23,8 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
public void DownloadNzb(RemoteEpisode remoteEpisode) public void DownloadNzb(RemoteEpisode remoteEpisode)
{ {
var url = remoteEpisode.Report.NzbUrl; var url = remoteEpisode.Release.DownloadUrl;
var title = remoteEpisode.Report.Title + ".nzb"; var title = remoteEpisode.Release.Title + ".nzb";
string cat = _configService.NzbgetTvCategory; string cat = _configService.NzbgetTvCategory;
int priority = remoteEpisode.IsRecentEpisode() ? (int)_configService.NzbgetRecentTvPriority : (int)_configService.NzbgetOlderTvPriority; int priority = remoteEpisode.IsRecentEpisode() ? (int)_configService.NzbgetRecentTvPriority : (int)_configService.NzbgetOlderTvPriority;

@ -28,8 +28,8 @@ namespace NzbDrone.Core.Download.Clients
public void DownloadNzb(RemoteEpisode remoteEpisode) public void DownloadNzb(RemoteEpisode remoteEpisode)
{ {
var url = remoteEpisode.Report.NzbUrl; var url = remoteEpisode.Release.DownloadUrl;
var title = remoteEpisode.Report.Title; var title = remoteEpisode.Release.Title;
if (remoteEpisode.ParsedEpisodeInfo.FullSeason) if (remoteEpisode.ParsedEpisodeInfo.FullSeason)
{ {

@ -26,8 +26,8 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
string cat = _configService.SabTvCategory; string cat = _configService.SabTvCategory;
int priority = (int)_configService.SabRecentTvPriority; int priority = (int)_configService.SabRecentTvPriority;
string name = remoteEpisode.Report.NzbUrl.Replace("&", "%26"); string name = remoteEpisode.Release.DownloadUrl.Replace("&", "%26");
string nzbName = HttpUtility.UrlEncode(remoteEpisode.Report.Title); string nzbName = HttpUtility.UrlEncode(remoteEpisode.Release.Title);
string action = string.Format("mode=addurl&name={0}&priority={1}&pp=3&cat={2}&nzbname={3}&output=json", string action = string.Format("mode=addurl&name={0}&priority={1}&pp=3&cat={2}&nzbname={3}&output=json",
name, priority, cat, nzbName); name, priority, cat, nzbName);
@ -66,8 +66,8 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
public void DownloadNzb(RemoteEpisode remoteEpisode) public void DownloadNzb(RemoteEpisode remoteEpisode)
{ {
var url = remoteEpisode.Report.NzbUrl; var url = remoteEpisode.Release.DownloadUrl;
var title = remoteEpisode.Report.Title; var title = remoteEpisode.Release.Title;
string cat = _configService.SabTvCategory; string cat = _configService.SabTvCategory;
int priority = remoteEpisode.IsRecentEpisode() ? (int)_configService.SabRecentTvPriority : (int)_configService.SabOlderTvPriority; int priority = remoteEpisode.IsRecentEpisode() ? (int)_configService.SabRecentTvPriority : (int)_configService.SabOlderTvPriority;

@ -59,8 +59,8 @@ namespace NzbDrone.Core.Download
return decisions.Where(c => c.Approved && c.RemoteEpisode.Episodes.Any()) return decisions.Where(c => c.Approved && c.RemoteEpisode.Episodes.Any())
.OrderByDescending(c => c.RemoteEpisode.ParsedEpisodeInfo.Quality) .OrderByDescending(c => c.RemoteEpisode.ParsedEpisodeInfo.Quality)
.ThenBy(c => c.RemoteEpisode.Episodes.Select(e => e.EpisodeNumber).MinOrDefault()) .ThenBy(c => c.RemoteEpisode.Episodes.Select(e => e.EpisodeNumber).MinOrDefault())
.ThenBy(c => c.RemoteEpisode.Report.Size.Round(200.Megabytes()) / c.RemoteEpisode.Episodes.Count) .ThenBy(c => c.RemoteEpisode.Release.Size.Round(200.Megabytes()) / c.RemoteEpisode.Episodes.Count)
.ThenBy(c => c.RemoteEpisode.Report.Age) .ThenBy(c => c.RemoteEpisode.Release.Age)
.ToList(); .ToList();
} }
} }

@ -27,7 +27,7 @@ namespace NzbDrone.Core.Download
public void DownloadReport(RemoteEpisode remoteEpisode) public void DownloadReport(RemoteEpisode remoteEpisode)
{ {
var downloadTitle = remoteEpisode.Report.Title; var downloadTitle = remoteEpisode.Release.Title;
var downloadClient = _downloadClientProvider.GetDownloadClient(); var downloadClient = _downloadClientProvider.GetDownloadClient();
if (!downloadClient.IsConfigured) if (!downloadClient.IsConfigured)

@ -64,15 +64,15 @@ namespace NzbDrone.Core.History
EventType = HistoryEventType.Grabbed, EventType = HistoryEventType.Grabbed,
Date = DateTime.UtcNow, Date = DateTime.UtcNow,
Quality = message.Episode.ParsedEpisodeInfo.Quality, Quality = message.Episode.ParsedEpisodeInfo.Quality,
SourceTitle = message.Episode.Report.Title, SourceTitle = message.Episode.Release.Title,
SeriesId = episode.SeriesId, SeriesId = episode.SeriesId,
EpisodeId = episode.Id, EpisodeId = episode.Id,
}; };
history.Data.Add("Indexer", message.Episode.Report.Indexer); history.Data.Add("Indexer", message.Episode.Release.Indexer);
history.Data.Add("NzbInfoUrl", message.Episode.Report.NzbInfoUrl); history.Data.Add("NzbInfoUrl", message.Episode.Release.InfoUrl);
history.Data.Add("ReleaseGroup", message.Episode.Report.ReleaseGroup); history.Data.Add("ReleaseGroup", message.Episode.Release.ReleaseGroup);
history.Data.Add("Age", message.Episode.Report.Age.ToString()); history.Data.Add("Age", message.Episode.Release.Age.ToString());
_historyRepository.Insert(history); _historyRepository.Insert(history);
} }

@ -128,9 +128,11 @@ namespace NzbDrone.Core.IndexerSearch
return spec; return spec;
} }
private List<DownloadDecision> Dispatch(Func<IIndexer, IEnumerable<ReportInfo>> searchAction, SearchCriteriaBase criteriaBase) private List<DownloadDecision> Dispatch(Func<IIndexer, IEnumerable<ReleaseInfo>> searchAction, SearchCriteriaBase criteriaBase)
{ {
var indexers = _indexerService.GetAvailableIndexers().ToList(); var indexers = _indexerService.GetAvailableIndexers().ToList();
var reports = new List<ReportInfo>();
var reports = new List<ReleaseInfo>();
_logger.ProgressInfo("Searching {0} indexers for {1}", indexers.Count, criteriaBase); _logger.ProgressInfo("Searching {0} indexers for {1}", indexers.Count, criteriaBase);

@ -0,0 +1,48 @@
using System;
using System.Xml.Linq;
using NLog;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers
{
public class BasicTorrentRssParser : RssParserBase
{
protected override ReleaseInfo CreateNewReleaseInfo()
{
return new TorrentInfo();
}
protected override ReleaseInfo PostProcessor(XElement item, ReleaseInfo currentResult)
{
var torrentInfo = (TorrentInfo)currentResult;
torrentInfo.MagnetUrl = MagnetUrl(item);
torrentInfo.InfoHash = InfoHash(item);
return torrentInfo;
}
protected override long GetSize(XElement item)
{
var elementLength = GetTorrentElement(item).Element("contentLength");
return Convert.ToInt64(elementLength.Value);
}
protected virtual string MagnetUrl(XElement item)
{
var elementLength = GetTorrentElement(item).Element("magnetURI");
return elementLength.Value;
}
protected virtual string InfoHash(XElement item)
{
var elementLength = GetTorrentElement(item).Element("infoHash");
return elementLength.Value;
}
private static XElement GetTorrentElement(XElement item)
{
return item.Element("torrent");
}
}
}

@ -0,0 +1,8 @@
namespace NzbDrone.Core.Indexers
{
public enum DownloadProtocols
{
Nzb = 0,
Torrent =1
}
}

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
namespace NzbDrone.Core.Indexers.Eztv
{
public class Eztv : IndexerBase
{
public override string Name
{
get { return "Eztv"; }
}
public override bool EnableByDefault
{
get { return false; }
}
public override IParseFeed Parser
{
get
{
return new BasicTorrentRssParser();
}
}
public override IEnumerable<string> RecentFeed
{
get
{
return new[]
{
"http://www.ezrss.it/feed/"
};
}
}
public override IEnumerable<string> GetEpisodeSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int episodeNumber)
{
yield return string.Format("http://www.ezrss.it/search/index.php?show_name={0}&season={1}&episode={2}&mode=rss", seriesTitle, seasonNumber, episodeNumber);
}
public override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int offset)
{
yield return string.Format("http://www.ezrss.it/search/index.php?show_name={0}&season={1}&mode=rss", seriesTitle, seasonNumber);
}
public override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, int tvRageId, DateTime date)
{
//EZTV doesn't support searching based on actual epidose airdate. they only support release date.
return new string[0];
}
}
}

@ -9,7 +9,7 @@ namespace NzbDrone.Core.Indexers
{ {
public interface IFetchAndParseRss public interface IFetchAndParseRss
{ {
List<ReportInfo> Fetch(); List<ReleaseInfo> Fetch();
} }
public class FetchAndParseRssService : IFetchAndParseRss public class FetchAndParseRssService : IFetchAndParseRss
@ -25,9 +25,9 @@ namespace NzbDrone.Core.Indexers
_logger = logger; _logger = logger;
} }
public List<ReportInfo> Fetch() public List<ReleaseInfo> Fetch()
{ {
var result = new List<ReportInfo>(); var result = new List<ReleaseInfo>();
var indexers = _indexerService.GetAvailableIndexers().ToList(); var indexers = _indexerService.GetAvailableIndexers().ToList();

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.IO;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers
{
public interface IParseFeed
{
IEnumerable<ReleaseInfo> Process(string source, string url);
}
}

@ -25,13 +25,7 @@ namespace NzbDrone.Core.Indexers
} }
} }
public virtual IParseFeed Parser public virtual IParseFeed Parser { get; private set; }
{
get
{
return new BasicRssParser();
}
}
public abstract IEnumerable<string> RecentFeed { get; } public abstract IEnumerable<string> RecentFeed { get; }
public abstract IEnumerable<string> GetEpisodeSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int episodeNumber); public abstract IEnumerable<string> GetEpisodeSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int episodeNumber);

@ -11,11 +11,11 @@ namespace NzbDrone.Core.Indexers
{ {
public interface IFetchFeedFromIndexers public interface IFetchFeedFromIndexers
{ {
IList<ReportInfo> FetchRss(IIndexer indexer); IList<ReleaseInfo> FetchRss(IIndexer indexer);
IList<ReportInfo> Fetch(IIndexer indexer, SeasonSearchCriteria searchCriteria); IList<ReleaseInfo> Fetch(IIndexer indexer, SeasonSearchCriteria searchCriteria);
IList<ReportInfo> Fetch(IIndexer indexer, SingleEpisodeSearchCriteria searchCriteria); IList<ReleaseInfo> Fetch(IIndexer indexer, SingleEpisodeSearchCriteria searchCriteria);
IList<ReportInfo> Fetch(IIndexer indexer, DailyEpisodeSearchCriteria searchCriteria); IList<ReleaseInfo> Fetch(IIndexer indexer, DailyEpisodeSearchCriteria searchCriteria);
} }
public class FetchFeedService : IFetchFeedFromIndexers public class FetchFeedService : IFetchFeedFromIndexers
@ -31,7 +31,7 @@ namespace NzbDrone.Core.Indexers
} }
public virtual IList<ReportInfo> FetchRss(IIndexer indexer) public virtual IList<ReleaseInfo> FetchRss(IIndexer indexer)
{ {
_logger.Debug("Fetching feeds from " + indexer.Name); _logger.Debug("Fetching feeds from " + indexer.Name);
@ -42,11 +42,11 @@ namespace NzbDrone.Core.Indexers
return result; return result;
} }
public IList<ReportInfo> Fetch(IIndexer indexer, SeasonSearchCriteria searchCriteria) public IList<ReleaseInfo> Fetch(IIndexer indexer, SeasonSearchCriteria searchCriteria)
{ {
_logger.Debug("Searching for {0}", searchCriteria); _logger.Debug("Searching for {0}", searchCriteria);
var result = Fetch(indexer, searchCriteria, 0).DistinctBy(c => c.NzbUrl).ToList(); var result = Fetch(indexer, searchCriteria, 0).DistinctBy(c => c.DownloadUrl).ToList();
_logger.Info("Finished searching {0} for {1}. Found {2}", indexer.Name, searchCriteria, result.Count); _logger.Info("Finished searching {0} for {1}. Found {2}", indexer.Name, searchCriteria, result.Count);
@ -54,7 +54,7 @@ namespace NzbDrone.Core.Indexers
} }
private IList<ReportInfo> Fetch(IIndexer indexer, SeasonSearchCriteria searchCriteria, int offset) private IList<ReleaseInfo> Fetch(IIndexer indexer, SeasonSearchCriteria searchCriteria, int offset)
{ {
_logger.Debug("Searching for {0} offset: {1}", searchCriteria, offset); _logger.Debug("Searching for {0} offset: {1}", searchCriteria, offset);
@ -72,7 +72,7 @@ namespace NzbDrone.Core.Indexers
return result; return result;
} }
public IList<ReportInfo> Fetch(IIndexer indexer, SingleEpisodeSearchCriteria searchCriteria) public IList<ReleaseInfo> Fetch(IIndexer indexer, SingleEpisodeSearchCriteria searchCriteria)
{ {
_logger.Debug("Searching for {0}", searchCriteria); _logger.Debug("Searching for {0}", searchCriteria);
@ -84,7 +84,7 @@ namespace NzbDrone.Core.Indexers
return result; return result;
} }
public IList<ReportInfo> Fetch(IIndexer indexer, DailyEpisodeSearchCriteria searchCriteria) public IList<ReleaseInfo> Fetch(IIndexer indexer, DailyEpisodeSearchCriteria searchCriteria)
{ {
_logger.Debug("Searching for {0}", searchCriteria); _logger.Debug("Searching for {0}", searchCriteria);
@ -95,9 +95,9 @@ namespace NzbDrone.Core.Indexers
return result; return result;
} }
private List<ReportInfo> Fetch(IIndexer indexer, IEnumerable<string> urls) private List<ReleaseInfo> Fetch(IIndexer indexer, IEnumerable<string> urls)
{ {
var result = new List<ReportInfo>(); var result = new List<ReleaseInfo>();
foreach (var url in urls) foreach (var url in urls)
{ {

@ -153,10 +153,16 @@ namespace NzbDrone.Core.Indexers
RemoveMissingImplementations(); RemoveMissingImplementations();
if (!All().Any())
{
var definitions = _indexers.SelectMany(indexer => indexer.DefaultDefinitions); var definitions = _indexers.SelectMany(indexer => indexer.DefaultDefinitions);
_indexerRepository.InsertMany(definitions.ToList());
var currentIndexer = All();
var newIndexers = definitions.Where(def => currentIndexer.All(c => c.Implementation != def.Implementation)).ToList();
if (newIndexers.Any())
{
_indexerRepository.InsertMany(newIndexers);
} }
} }

@ -123,6 +123,7 @@ namespace NzbDrone.Core.Indexers.Newznab
} }
} }
private static string NewsnabifyTitle(string title) private static string NewsnabifyTitle(string title)
{ {
return title.Replace("+", "%20"); return title.Replace("+", "%20");

@ -5,29 +5,33 @@ using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.Newznab namespace NzbDrone.Core.Indexers.Newznab
{ {
public class NewznabParser : BasicRssParser public class NewznabParser : RssParserBase
{ {
private static readonly XNamespace NewznabNamespace = "http://www.newznab.com/DTD/2010/feeds/attributes/";
protected override string GetNzbInfoUrl(XElement item) protected override string GetNzbInfoUrl(XElement item)
{ {
return item.Comments().Replace("#comments", ""); return item.Comments().Replace("#comments", "");
} }
protected override ReportInfo PostProcessor(XElement item, ReportInfo currentResult) protected override long GetSize(XElement item)
{
if (currentResult != null)
{ {
var attributes = item.Elements(NewznabNamespace + "attr").ToList(); var attributes = item.Elements("attr").ToList();
var sizeElement = attributes.SingleOrDefault(e => e.Attribute("name").Value.Equals("size", StringComparison.CurrentCultureIgnoreCase)); var sizeElement = attributes.SingleOrDefault(e => e.Attribute("name").Value.Equals("size", StringComparison.CurrentCultureIgnoreCase));
var rageIdElement = attributes.SingleOrDefault(e => e.Attribute("name").Value.Equals("rageid", StringComparison.CurrentCultureIgnoreCase));
if (sizeElement == null) if (sizeElement == null)
{ {
throw new SizeParsingException("Unable to parse size from: {0} [{1}]", currentResult.Title, currentResult.Indexer);
}
return Convert.ToInt64(sizeElement.Attribute("value").Value);
} }
currentResult.Size = Convert.ToInt64(sizeElement.Attribute("value").Value); protected override ReleaseInfo PostProcessor(XElement item, ReleaseInfo currentResult)
{
if (currentResult != null)
{
var attributes = item.Elements("attr").ToList();
var rageIdElement = attributes.SingleOrDefault(e => e.Attribute("name").Value.Equals("rageid", StringComparison.CurrentCultureIgnoreCase));
if (rageIdElement != null) if (rageIdElement != null)
{ {

@ -3,40 +3,26 @@ using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml.Linq; using System.Xml.Linq;
using NLog; using NLog;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.NzbClub namespace NzbDrone.Core.Indexers.NzbClub
{ {
public class NzbClubParser : BasicRssParser public class NzbClubParser : RssParserBase
{ {
private static readonly Regex SizeRegex = new Regex(@"(?:Size:)\s(?<size>\d+.\d+\s[g|m]i?[b])", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex SizeRegex = new Regex(@"(?:Size:)\s(?<size>\d+.\d+\s[g|m]i?[b])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Logger logger;
public NzbClubParser()
{
logger = NzbDroneLogger.GetLogger();
}
protected override ReportInfo PostProcessor(XElement item, ReportInfo currentResult) protected override long GetSize(XElement item)
{ {
if (currentResult != null) logger = LogManager.GetCurrentClassLogger();
{
var match = SizeRegex.Match(item.Description());
if (match.Success && match.Groups["size"].Success) if (match.Success && match.Groups["size"].Success)
{ {
currentResult.Size = GetReportSize(match.Groups["size"].Value); return ParseSize(match.Groups["size"].Value);
}
else
{
logger.Warn("Couldn't parse size from {0}", item.Description());
}
} }
return currentResult; return 0;
} }
protected override string GetTitle(XElement item) protected override string GetTitle(XElement item)
@ -49,6 +35,31 @@ namespace NzbDrone.Core.Indexers.NzbClub
return title; return title;
} }
private static readonly Regex[] HeaderRegex = new[]
{
new Regex(@"(?:\[.+\]\-\[.+\]\-\[.+\]\-\[)(?<nzbTitle>.+)(?:\]\-.+)",
RegexOptions.IgnoreCase),
new Regex(@"(?:\[.+\]\W+\[.+\]\W+\[.+\]\W+\"")(?<nzbTitle>.+)(?:\"".+)",
RegexOptions.IgnoreCase),
new Regex(@"(?:\[)(?<nzbTitle>.+)(?:\]\-.+)",
RegexOptions.IgnoreCase),
};
private static string ParseHeader(string header)
{
foreach (var regex in HeaderRegex)
{
var match = regex.Matches(header);
if (match.Count != 0)
return match[0].Groups["nzbTitle"].Value.Trim();
}
return header;
}
protected override string GetNzbInfoUrl(XElement item) protected override string GetNzbInfoUrl(XElement item)
{ {
return item.Links().First(); return item.Links().First();

@ -1,11 +1,10 @@
using System; using System;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml.Linq; using System.Xml.Linq;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.Omgwtfnzbs namespace NzbDrone.Core.Indexers.Omgwtfnzbs
{ {
public class OmgwtfnzbsParser : BasicRssParser public class OmgwtfnzbsParser : RssParserBase
{ {
protected override string GetNzbInfoUrl(XElement item) protected override string GetNzbInfoUrl(XElement item)
{ {
@ -21,15 +20,10 @@ namespace NzbDrone.Core.Indexers.Omgwtfnzbs
return String.Empty; return String.Empty;
} }
protected override ReportInfo PostProcessor(XElement item, ReportInfo currentResult) protected override long GetSize(XElement item)
{
if (currentResult != null)
{ {
var sizeString = Regex.Match(item.Description(), @"(?:Size:\<\/b\>\s\d+\.)\d{1,2}\s\w{2}(?:\<br \/\>)", RegexOptions.IgnoreCase | RegexOptions.Compiled).Value; var sizeString = Regex.Match(item.Description(), @"(?:Size:\<\/b\>\s\d+\.)\d{1,2}\s\w{2}(?:\<br \/\>)", RegexOptions.IgnoreCase | RegexOptions.Compiled).Value;
currentResult.Size = GetReportSize(sizeString); return ParseSize(sizeString);
}
return currentResult;
} }
} }
} }

@ -8,43 +8,44 @@ using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
using NLog; using NLog;
using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers namespace NzbDrone.Core.Indexers
{ {
public interface IParseFeed public abstract class RssParserBase : IParseFeed
{ {
IEnumerable<ReportInfo> Process(string xml, string url); private Logger _logger;
}
public class BasicRssParser : IParseFeed protected virtual ReleaseInfo CreateNewReleaseInfo()
{ {
private readonly Logger _logger; return new ReleaseInfo();
}
public BasicRssParser() protected RssParserBase()
{ {
_logger = NzbDroneLogger.GetLogger(this); _logger = NzbDroneLogger.GetLogger(this);
} }
public IEnumerable<ReportInfo> Process(string xml, string url) public IEnumerable<ReleaseInfo> Process(string xml, string url)
{ {
using (var xmlTextReader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings { ProhibitDtd = false, IgnoreComments = true })) using (var xmlTextReader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings { ProhibitDtd = false, IgnoreComments = true }))
{ {
var document = XDocument.Load(xmlTextReader); var document = XDocument.Load(xmlTextReader);
var items = document.Descendants("item"); var items = document.Descendants("item");
var result = new List<ReportInfo>(); var result = new List<ReleaseInfo>();
foreach (var item in items) foreach (var item in items)
{ {
try try
{ {
var reportInfo = ParseFeedItem(item); var reportInfo = ParseFeedItem(item.StripNameSpace(), url);
if (reportInfo != null) if (reportInfo != null)
{ {
reportInfo.NzbUrl = GetNzbUrl(item); reportInfo.DownloadUrl = GetNzbUrl(item);
reportInfo.NzbInfoUrl = GetNzbInfoUrl(item); reportInfo.InfoUrl = GetNzbInfoUrl(item);
result.Add(reportInfo); result.Add(reportInfo);
} }
} }
@ -59,6 +60,31 @@ namespace NzbDrone.Core.Indexers
} }
} }
private ReleaseInfo ParseFeedItem(XElement item, string url)
{
var title = GetTitle(item);
var reportInfo = CreateNewReleaseInfo();
reportInfo.Title = title;
reportInfo.PublishDate = item.PublishDate();
reportInfo.ReleaseGroup = ParseReleaseGroup(title);
reportInfo.DownloadUrl = GetNzbUrl(item);
reportInfo.InfoUrl = GetNzbInfoUrl(item);
try
{
reportInfo.Size = GetSize(item);
}
catch (Exception)
{
throw new SizeParsingException("Unable to parse size from: {0} [{1}]", reportInfo.Title, url);
}
_logger.Trace("Parsed: {0} from: {1}", reportInfo, item.Title());
return PostProcessor(item, reportInfo);
}
protected virtual string GetTitle(XElement item) protected virtual string GetTitle(XElement item)
{ {
@ -75,25 +101,14 @@ namespace NzbDrone.Core.Indexers
return String.Empty; return String.Empty;
} }
protected virtual ReportInfo PostProcessor(XElement item, ReportInfo currentResult) protected abstract long GetSize(XElement item);
protected virtual ReleaseInfo PostProcessor(XElement item, ReleaseInfo currentResult)
{ {
return currentResult; return currentResult;
} }
private ReportInfo ParseFeedItem(XElement item)
{
var title = GetTitle(item);
var reportInfo = new ReportInfo();
reportInfo.Title = title;
reportInfo.Age = DateTime.Now.Date.Subtract(item.PublishDate().Date).Days;
reportInfo.ReleaseGroup = ParseReleaseGroup(title);
_logger.Trace("Parsed: {0} from: {1}", reportInfo, item.Title());
return PostProcessor(item, reportInfo);
}
public static string ParseReleaseGroup(string title) public static string ParseReleaseGroup(string title)
{ {
@ -111,39 +126,15 @@ namespace NzbDrone.Core.Indexers
if (@group.Length == title.Length) if (@group.Length == title.Length)
return String.Empty; return String.Empty;
return @group; return @group.Trim('-', ' ', '[', ']');
}
private static readonly Regex[] HeaderRegex = new[]
{
new Regex(@"(?:\[.+\]\-\[.+\]\-\[.+\]\-\[)(?<nzbTitle>.+)(?:\]\-.+)",
RegexOptions.IgnoreCase),
new Regex(@"(?:\[.+\]\W+\[.+\]\W+\[.+\]\W+\"")(?<nzbTitle>.+)(?:\"".+)",
RegexOptions.IgnoreCase),
new Regex(@"(?:\[)(?<nzbTitle>.+)(?:\]\-.+)",
RegexOptions.IgnoreCase),
};
public static string ParseHeader(string header)
{
foreach (var regex in HeaderRegex)
{
var match = regex.Matches(header);
if (match.Count != 0)
return match[0].Groups["nzbTitle"].Value.Trim();
}
return header;
} }
private static readonly Regex ReportSizeRegex = new Regex(@"(?<value>\d+\.\d{1,2}|\d+\,\d+\.\d{1,2}|\d+)\W?(?<unit>GB|MB|GiB|MiB)", private static readonly Regex ReportSizeRegex = new Regex(@"(?<value>\d+\.\d{1,2}|\d+\,\d+\.\d{1,2}|\d+)\W?(?<unit>GB|MB|GiB|MiB)",
RegexOptions.IgnoreCase | RegexOptions.Compiled); RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static long GetReportSize(string sizeString)
public static long ParseSize(string sizeString)
{ {
var match = ReportSizeRegex.Matches(sizeString); var match = ReportSizeRegex.Matches(sizeString);

@ -1,23 +1,17 @@
using System.Xml.Linq; using System.Xml.Linq;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.Wombles namespace NzbDrone.Core.Indexers.Wombles
{ {
public class WomblesParser : BasicRssParser public class WomblesParser : RssParserBase
{ {
protected override string GetNzbInfoUrl(XElement item) protected override string GetNzbInfoUrl(XElement item)
{ {
return null; return null;
} }
protected override ReportInfo PostProcessor(XElement item, ReportInfo currentResult) protected override long GetSize(XElement item)
{ {
if (currentResult != null) return 0;
{
currentResult.Size = 0;
}
return currentResult;
} }
} }
} }

@ -11,7 +11,6 @@ namespace NzbDrone.Core.Indexers
{ {
public static class XElementExtensions public static class XElementExtensions
{ {
private static readonly Logger Logger = NzbDroneLogger.GetLogger(); private static readonly Logger Logger = NzbDroneLogger.GetLogger();
private static readonly Regex RemoveTimeZoneRegex = new Regex(@"\s[A-Z]{2,4}$", RegexOptions.Compiled); private static readonly Regex RemoveTimeZoneRegex = new Regex(@"\s[A-Z]{2,4}$", RegexOptions.Compiled);
@ -21,6 +20,21 @@ namespace NzbDrone.Core.Indexers
return item.TryGetValue("title", "Unknown"); return item.TryGetValue("title", "Unknown");
} }
public static XElement StripNameSpace(this XElement root)
{
var res = new XElement(
root.Name.LocalName,
root.HasElements ?
root.Elements().Select(StripNameSpace) :
(object)root.Value
);
res.ReplaceAttributes(
root.Attributes().Where(attr => (!attr.IsNamespaceDeclaration)));
return res;
}
public static DateTime PublishDate(this XElement item) public static DateTime PublishDate(this XElement item)
{ {
string dateString = item.TryGetValue("pubDate"); string dateString = item.TryGetValue("pubDate");
@ -33,7 +47,7 @@ namespace NzbDrone.Core.Indexers
dateString = RemoveTimeZoneRegex.Replace(dateString, ""); dateString = RemoveTimeZoneRegex.Replace(dateString, "");
result = DateTime.Parse(dateString, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AdjustToUniversal); result = DateTime.Parse(dateString, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AdjustToUniversal);
} }
return result.ToUniversalTime(); return result.ToUniversalTime().Date;
} }
catch (FormatException e) catch (FormatException e)
{ {
@ -59,6 +73,11 @@ namespace NzbDrone.Core.Indexers
return item.TryGetValue("comments"); return item.TryGetValue("comments");
} }
public static long Length(this XElement item)
{
return long.Parse(item.TryGetValue("length"));
}
private static string TryGetValue(this XElement item, string elementName, string defaultValue = "") private static string TryGetValue(this XElement item, string elementName, string defaultValue = "")
{ {
var element = item.Element(elementName); var element = item.Element(elementName);

@ -215,10 +215,14 @@
<Compile Include="IndexerSearch\EpisodeSearchCommand.cs" /> <Compile Include="IndexerSearch\EpisodeSearchCommand.cs" />
<Compile Include="IndexerSearch\SeasonSearchCommand.cs" /> <Compile Include="IndexerSearch\SeasonSearchCommand.cs" />
<Compile Include="IndexerSearch\SeasonSearchService.cs" /> <Compile Include="IndexerSearch\SeasonSearchService.cs" />
<Compile Include="Indexers\BasicTorrentRssParser.cs" />
<Compile Include="Indexers\DownloadProtocols.cs" />
<Compile Include="Indexers\Eztv\Eztv.cs" />
<Compile Include="Indexers\FetchAndParseRssService.cs" /> <Compile Include="Indexers\FetchAndParseRssService.cs" />
<Compile Include="Indexers\IIndexer.cs" /> <Compile Include="Indexers\IIndexer.cs" />
<Compile Include="Indexers\IndexerSettingUpdatedEvent.cs" /> <Compile Include="Indexers\IndexerSettingUpdatedEvent.cs" />
<Compile Include="Indexers\IndexerWithSetting.cs" /> <Compile Include="Indexers\IndexerWithSetting.cs" />
<Compile Include="Indexers\IParseFeed.cs" />
<Compile Include="Indexers\Newznab\SizeParsingException.cs" /> <Compile Include="Indexers\Newznab\SizeParsingException.cs" />
<Compile Include="Indexers\NullSetting.cs" /> <Compile Include="Indexers\NullSetting.cs" />
<Compile Include="Indexers\RssSyncCommand.cs" /> <Compile Include="Indexers\RssSyncCommand.cs" />
@ -294,7 +298,7 @@
<Compile Include="IndexerSearch\Definitions\SingleEpisodeSearchCriteria.cs" /> <Compile Include="IndexerSearch\Definitions\SingleEpisodeSearchCriteria.cs" />
<Compile Include="IndexerSearch\NzbSearchService.cs" /> <Compile Include="IndexerSearch\NzbSearchService.cs" />
<Compile Include="IndexerSearch\SearchAndDownloadService.cs" /> <Compile Include="IndexerSearch\SearchAndDownloadService.cs" />
<Compile Include="Indexers\BasicRssParser.cs" /> <Compile Include="Indexers\RssParserBase.cs" />
<Compile Include="Indexers\RssSyncService.cs" /> <Compile Include="Indexers\RssSyncService.cs" />
<Compile Include="Indexers\IndexerBase.cs" /> <Compile Include="Indexers\IndexerBase.cs" />
<Compile Include="Indexers\IndexerDefinition.cs" /> <Compile Include="Indexers\IndexerDefinition.cs" />
@ -373,7 +377,8 @@
<Compile Include="Parser\Model\LocalEpisode.cs" /> <Compile Include="Parser\Model\LocalEpisode.cs" />
<Compile Include="Parser\Model\ParsedEpisodeInfo.cs" /> <Compile Include="Parser\Model\ParsedEpisodeInfo.cs" />
<Compile Include="Parser\Model\RemoteEpisode.cs" /> <Compile Include="Parser\Model\RemoteEpisode.cs" />
<Compile Include="Parser\Model\ReportInfo.cs" /> <Compile Include="Parser\Model\ReleaseInfo.cs" />
<Compile Include="Parser\Model\TorrentInfo.cs" />
<Compile Include="Parser\Parser.cs" /> <Compile Include="Parser\Parser.cs" />
<Compile Include="Parser\ParsingService.cs" /> <Compile Include="Parser\ParsingService.cs" />
<Compile Include="Parser\QualityParser.cs" /> <Compile Include="Parser\QualityParser.cs" />

@ -0,0 +1,27 @@
using System;
namespace NzbDrone.Core.Parser.Model
{
public class ReleaseInfo
{
public string Title { get; set; }
public long Size { get; set; }
public string DownloadUrl { get; set; }
public string InfoUrl { get; set; }
public string CommentUrl { get; set; }
public String Indexer { get; set; }
public DateTime PublishDate { get; set; }
public int Age
{
get
{
return DateTime.UtcNow.Subtract(PublishDate).Days;
}
}
public string ReleaseGroup { get; set; }
public int TvRageId { get; set; }
}
}

@ -7,7 +7,7 @@ namespace NzbDrone.Core.Parser.Model
{ {
public class RemoteEpisode public class RemoteEpisode
{ {
public ReportInfo Report { get; set; } public ReleaseInfo Release { get; set; }
public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; } public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; }
@ -22,7 +22,7 @@ namespace NzbDrone.Core.Parser.Model
public override string ToString() public override string ToString()
{ {
return Report.Title; return Release.Title;
} }
} }
} }

@ -1,16 +0,0 @@
using System;
namespace NzbDrone.Core.Parser.Model
{
public class ReportInfo
{
public string Title { get; set; }
public long Size { get; set; }
public string NzbUrl { get; set; }
public string NzbInfoUrl { get; set; }
public String Indexer { get; set; }
public int Age { get; set; }
public string ReleaseGroup { get; set; }
public int TvRageId { get; set; }
}
}

@ -0,0 +1,8 @@
namespace NzbDrone.Core.Parser.Model
{
public class TorrentInfo : ReleaseInfo
{
public string MagnetUrl { get; set; }
public string InfoHash { get; set; }
}
}

@ -22,7 +22,7 @@ namespace NzbDrone.Integration.Test
{ {
releaseResource.Age.Should().BeGreaterOrEqualTo(-1); releaseResource.Age.Should().BeGreaterOrEqualTo(-1);
releaseResource.Title.Should().NotBeBlank(); releaseResource.Title.Should().NotBeBlank();
releaseResource.NzbUrl.Should().NotBeBlank(); releaseResource.DownloadUrl.Should().NotBeBlank();
releaseResource.SeriesTitle.Should().NotBeBlank(); releaseResource.SeriesTitle.Should().NotBeBlank();
//TODO: uncomment these after moving to restsharp for rss //TODO: uncomment these after moving to restsharp for rss
//releaseResource.NzbInfoUrl.Should().NotBeBlank(); //releaseResource.NzbInfoUrl.Should().NotBeBlank();

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<configurations lcid_type="UserExe" show_language_selector="False" language_selector_title="" language_selector_ok="OK" language_selector_cancel="Cancel" configuration_no_match_message="" ui_level="basic" fileversion="" productversion="" log_enabled="True" log_file="#TEMPPATH\dotNetInstallerLog.txt">
<schema version="2.2.824.0" generator="dotNetInstaller InstallerEditor" />
<configuration dialog_caption="SyntikX Installer" dialog_message="Following components need to be installed" dialog_message_uninstall="" dialog_bitmap="#APPPATH\banner.bmp" skip_caption="Skip" install_caption="Install" uninstall_caption="Uninstall" cancel_caption="Close" status_installed=" (Already Installed)" status_notinstalled="" failed_exec_command_continue="Failed to install %s." installation_completed="SyntikX installed successfully!" uninstallation_completed="SyntikX uninstalled successfully!" installation_none="SyntikX is not compatible with your machine. Please contact support for more information." uninstallation_none="SyntikX is not installed!" installing_component_wait="Installing %s. Wait, this operation could take some time ..." uninstalling_component_wait="Uninstalling %s. Wait, this operation could take some time ..." reboot_required="To continue the installation you must restart your computer. Restart now?" must_reboot_required="False" dialog_otherinfo_caption="" dialog_otherinfo_link="" complete_command="" complete_command_silent="" complete_command_basic="" wait_for_complete_command="True" prompt_for_optional_components="False" auto_close_if_installed="False" auto_close_on_error="False" reload_on_error="True" dialog_show_installed="True" dialog_show_uninstalled="False" dialog_show_required="True" cab_dialog_message="%s" cab_cancelled_message="" cab_dialog_caption="" cab_path="#TEMPPATH\#GUID" cab_path_autodelete="True" dialog_default_button="install" dialog_position="" dialog_components_list_position="" dialog_message_position="" dialog_bitmap_position="" dialog_otherinfo_link_position="" dialog_osinfo_position="" dialog_install_button_position="" dialog_cancel_button_position="" dialog_skip_button_position="" auto_start="True" auto_continue_on_reboot="True" reboot_cmd="" show_progress_dialog="False" show_cab_dialog="True" disable_wow64_fs_redirection="False" administrator_required="False" administrator_required_message="SyntikX installation requires administration rights." type="install" lcid_filter="" language_id="" language="" os_filter="" os_filter_min="" os_filter_max="" processor_architecture_filter="" supports_install="True" supports_uninstall="False">
<component executable="#TEMPPATH\syntik_bootstrap\WindowsXP-KB936929-SP3-x86-ENU.exe" executable_silent="" executable_basic="" install_directory="" responsefile_source="" responsefile_target="" responsefile_format="none" uninstall_executable="" uninstall_executable_silent="" uninstall_executable_basic="" uninstall_responsefile_source="" uninstall_responsefile_target="" returncodes_success="" returncodes_reboot="" exeparameters="" exeparameters_basic="" exeparameters_silent="" uninstall_exeparameters="" uninstall_exeparameters_basic="" uninstall_exeparameters_silent="" disable_wow64_fs_redirection="False" id="XPSP3" display_name="Windows XP Service Pack 3" uninstall_display_name="" os_filter="" os_filter_min="winXPsp1" os_filter_max="winXPsp2" os_filter_lcid="" type="exe" installcompletemessage="" uninstallcompletemessage="" mustreboot="False" reboot_required="" must_reboot_required="False" failed_exec_command_continue="" allow_continue_on_error="False" default_continue_on_error="False" required_install="True" required_uninstall="False" selected_install="True" selected_uninstall="True" note="Windows XP Service Pack 3" processor_architecture_filter="" status_installed="" status_notinstalled="" supports_install="True" supports_uninstall="False" show_progress_dialog="True" show_cab_dialog="True">
<downloaddialog dialog_caption="Download Windows XP Service Pack 3" dialog_message="SyntikX requires Windows XP Service Pack 3 or latter. Press start to automatically download and install this update." dialog_message_downloading="Downloading ..." dialog_message_copying="Copying ..." dialog_message_connecting="Connecting ..." dialog_message_sendingrequest="Sending request ..." autostartdownload="False" buttonstart_caption="Start" buttoncancel_caption="Cancel">
<download componentname="Windows XP Service Pack 3" sourceurl="http://download.microsoft.com/download/d/3/0/d30e32d8-418a-469d-b600-f32ce3edf42d/WindowsXP-KB936929-SP3-x86-ENU.exe" sourcepath="" destinationpath="#TEMPPATH\syntik_bootstrap\" destinationfilename="" alwaysdownload="False" clear_cache="False" />
</downloaddialog>
</component>
<component command="#TEMPPATH\syntik_bootstrap\dotNetFx40_Full_setup.exe /passive" command_silent="" command_basic="" uninstall_command="" uninstall_command_silent="" uninstall_command_basic="" returncodes_success="" returncodes_reboot="3010" disable_wow64_fs_redirection="False" id="Microsoft .NET Framework 4.0 - Full" display_name="Microsoft .NET Framework 4.0 " uninstall_display_name="" os_filter="" os_filter_min="winXPsp3" os_filter_max="" os_filter_lcid="" type="cmd" installcompletemessage="" uninstallcompletemessage="" mustreboot="False" reboot_required="" must_reboot_required="False" failed_exec_command_continue="" allow_continue_on_error="False" default_continue_on_error="False" required_install="True" required_uninstall="False" selected_install="True" selected_uninstall="False" note="English - WebSetup - .NET Framework 4.0 - Full for all operating system since Windows XP SP3 (Install check)" processor_architecture_filter="" status_installed="" status_notinstalled="" supports_install="True" supports_uninstall="False" show_progress_dialog="True" show_cab_dialog="True">
<installedcheck path="SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" fieldname="Install" fieldvalue="1" defaultvalue="False" fieldtype="REG_DWORD" comparison="match" rootkey="HKEY_LOCAL_MACHINE" wowoption="NONE" type="check_registry_value" description="Installed Check" />
<downloaddialog dialog_caption="Microsoft .NET Framework 4.0" dialog_message="Press 'Start' to download and install Microsoft .NET Framework 4.0 - Full" dialog_message_downloading="Download in progress. Please wait..." dialog_message_copying="Files are downloaded. Please wait ..." dialog_message_connecting="Connecting ..." dialog_message_sendingrequest="Sending request ..." autostartdownload="True" buttonstart_caption="Start" buttoncancel_caption="Cancel">
<download componentname="Microsoft .NET Framework 4.0" sourceurl="http://download.microsoft.com/download/1/B/E/1BE39E79-7E39-46A3-96FF-047F95396215/dotNetFx40_Full_setup.exe" sourcepath="" destinationpath="#TEMPPATH\syntik_bootstrap\" destinationfilename="" alwaysdownload="True" clear_cache="True" />
</downloaddialog>
</component>
<component package="#TEMPPATH\syntik_update\syntikx.msi" cmdparameters="" cmdparameters_silent="" cmdparameters_basic="" uninstall_package="" uninstall_cmdparameters="/qb-" uninstall_cmdparameters_silent="/qn" uninstall_cmdparameters_basic="/qb-" disable_wow64_fs_redirection="False" id="SyntikX" display_name="SyntikX" uninstall_display_name="" os_filter="" os_filter_min="winXPsp3" os_filter_max="" os_filter_lcid="" type="msi" installcompletemessage="" uninstallcompletemessage="" mustreboot="False" reboot_required="" must_reboot_required="False" failed_exec_command_continue="" allow_continue_on_error="False" default_continue_on_error="False" required_install="True" required_uninstall="True" selected_install="True" selected_uninstall="True" note="" processor_architecture_filter="" status_installed="" status_notinstalled="" supports_install="True" supports_uninstall="True" show_progress_dialog="True" show_cab_dialog="True">
<downloaddialog dialog_caption="Downloading latest version of SyntikX" dialog_message="Press 'Start' to install latest version of SyntikX" dialog_message_downloading="Downloading ..." dialog_message_copying="Copying ..." dialog_message_connecting="Connecting ..." dialog_message_sendingrequest="Sending request ..." autostartdownload="True" buttonstart_caption="Start" buttoncancel_caption="Cancel">
<download componentname="Download Setup" sourceurl="http://localhost:59330/v1/update" sourcepath="" destinationpath="#TEMPPATH\syntik_update\" destinationfilename="syntikx.msi" alwaysdownload="True" clear_cache="True" />
</downloaddialog>
</component>
</configuration>
</configurations>

Binary file not shown.

@ -0,0 +1,8 @@
rd _raw /s /q
rd _setup /s /q
xcopy ..\SyntikX.Client\bin\release\*.* _raw\ /S /V /I /F /R
"C:\Program Files (x86)\WiX Toolset v3.6\bin\candle.exe" -nologo "syntik.wix.build.wxs" -out "_setup\SyntikX.Wix.wixobj" -ext WixNetFxExtension -ext WixUIExtension
"C:\Program Files (x86)\WiX Toolset v3.6\bin\light.exe" -nologo "_setup\SyntikX.Wix.wixobj" -out "_setup\SyntikX.msi" -ext WixNetFxExtension -ext WixUIExtension
pause

@ -0,0 +1,10 @@
rd _raw /s /q
rd _setup /s /q
xcopy ..\SyntikX.Client\bin\debug\*.* _raw\ /S /V /I /F /R
SET BUILD_NUMBER=1.9.9.9
"C:\Program Files (x86)\WiX Toolset v3.6\bin\candle.exe" -nologo "syntik.wix.build.wxs" -out "_setup\SyntikX.Wix.wixobj" -ext WixNetFxExtension -ext WixUIExtension
"C:\Program Files (x86)\WiX Toolset v3.6\bin\light.exe" -nologo "_setup\SyntikX.Wix.wixobj" -out "_setup\SyntikX.Wix.msi" -ext WixNetFxExtension -ext WixUIExtension
pause

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="NzbDrone" Language="1033" Version="$(env.BUILD_NUMBER)" Manufacturer="NzbDrone Team" UpgradeCode="56833D74-A480-4CA2-B562-5A018B3A0F99">
<Package Description="NzbDrone"
Comments="NzbDrone"
InstallerVersion="200"
Compressed="yes"
InstallPrivileges="limited"
InstallScope="perUser"
Platform="x86"
Manufacturer="NzbDrone Team"
/>
<Media Id="1" Cabinet="NzbDrone.cab" EmbedCab="yes" CompressionLevel="high"/>
<Property Id="ARPPRODUCTICON" Value="ND_ICON" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="LocalAppDataFolder" Name="AppDataFolder">
<Directory Name="NzbDrone" Id="SX_ROOT">
<Component Id="SX_APP_COMP" DiskId="1" Guid="9c3ac309-cde4-4338-be75-a914cbce2601">
<File Id="SX_EXE_FILE" Name="NzbDrone.Client.exe" Source="_raw\NzbDrone.Client.exe" />
<File Id="AUTOFAC.CONFIGURATION.DLL" Name="Autofac.Configuration.dll" Source="_raw\Autofac.Configuration.dll" />
<File Id="AUTOFAC.DLL" Name="Autofac.dll" Source="_raw\Autofac.dll" />
<File Id="BCRYPT.NET.DLL" Name="BCrypt.Net.dll" Source="_raw\BCrypt.Net.dll" />
<File Id="CALIBURN.MICRO.DLL" Name="Caliburn.Micro.dll" Source="_raw\Caliburn.Micro.dll" />
<File Id="ICSHARPCODE.SHARPZIPLIB.DLL" Name="ICSharpCode.SharpZipLib.dll" Source="_raw\ICSharpCode.SharpZipLib.dll" />
<File Id="MAHAPPS.METRO.DLL" Name="MahApps.Metro.dll" Source="_raw\MahApps.Metro.dll" />
<File Id="NEWTONSOFT.JSON.DLL" Name="Newtonsoft.Json.dll" Source="_raw\Newtonsoft.Json.dll" />
<File Id="NLOG.DLL" Name="NLog.dll" Source="_raw\NLog.dll" />
<File Id="RESTSHARP.DLL" Name="RestSharp.dll" Source="_raw\RestSharp.dll" />
<File Id="EXCEPTRON.CLIENT.DLL" Name="exceptron.client.dll" Source="_raw\exceptron.client.dll" />
<File Id="EXCEPTRON.NLOG.DLL" Name="exceptron.nlog.dll" Source="_raw\exceptron.nlog.dll" />
<File Id="NzbDrone.CLIENT.CORE.DLL" Name="NzbDrone.Client.Core.dll" Source="_raw\NzbDrone.Client.Core.dll" />
<File Id="NzbDrone.CLIENT.CORE.PDB" Name="NzbDrone.Client.Core.pdb" Source="_raw\NzbDrone.Client.Core.pdb" />
<File Id="NzbDrone.CLIENT.EXE.CONFIG" Name="NzbDrone.Client.exe.config" Source="_raw\NzbDrone.Client.exe.config" />
<File Id="NzbDrone.CLIENT.PDB" Name="NzbDrone.Client.pdb" Source="_raw\NzbDrone.Client.pdb" />
<File Id="NzbDrone.SHARED.DLL" Name="NzbDrone.Shared.dll" Source="_raw\NzbDrone.Shared.dll" />
<File Id="NzbDrone.SHARED.PDB" Name="NzbDrone.Shared.pdb" Source="_raw\NzbDrone.Shared.pdb" />
<File Id="SYSTEM.WINDOWS.INTERACTIVITY.DLL" Name="System.Windows.Interactivity.dll" Source="_raw\System.Windows.Interactivity.dll" />
<RegistryValue Root="HKCU" Key="Software\Microsoft\NzbDrone" Name="installed" Type="integer" Value="1" KeyPath="yes" />
<RemoveFolder Id="SX_ROOT" On="uninstall" />
</Component>
</Directory>
</Directory>
<Directory Id="DesktopFolder">
<Component Id="SX_DESKTOP_SHORTCUT_COMP" Guid="1f395635-7a9d-454d-aab4-95a5a4e70be4">
<Shortcut Id="SX_DESKTOP_SHORTCUT" Name="NzbDrone" Description="NzbDrone Backup Client" Target="[SX_ROOT]NzbDrone.Client.exe" WorkingDirectory="SX_ROOT" />
<RegistryValue Root="HKCU" Key="Software\Microsoft\NzbDrone" Name="installed" Type="integer" Value="1" KeyPath="yes" />
</Component>
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="SX_START_DIR" Name="NzbDrone">
<Component Id="SX_START_SHORTCUT_COMP" Guid="8b3d54c6-712b-4bc2-b1e9-7cf40bcc1344">
<Shortcut Id="SX_START_SHORTCUT" Name="NzbDrone" Description="NzbDrone Backup Client" Target="[SX_ROOT]NzbDrone.Client.exe" WorkingDirectory="SX_ROOT" />
<RemoveFolder Id="SX_START_DIR" On="uninstall" />
<RegistryValue Root="HKCU" Key="Software\Microsoft\NzbDrone" Name="installed" Type="integer" Value="1" KeyPath="yes" />
</Component>
</Directory>
<Directory Id="StartupFolder" Name="Startup">
<Component Id="SX_STARTUP_SHORTCUT_COMP" Guid="0fdfe510-621e-4925-a0d4-395617fb7cbc">
<Shortcut Id="SX_STARTUP_SHORTCUT" Name="NzbDrone" Description="NzbDrone Backup Client" Target="[SX_ROOT]NzbDrone.Client.exe" Arguments="/startup" WorkingDirectory="SX_ROOT" />
<RegistryValue Root="HKCU" Key="Software\Microsoft\NzbDrone" Name="installed" Type="integer" Value="1" KeyPath="yes" />
</Component>
</Directory>
</Directory>
</Directory>
<!--<UIRef Id="WixUI_Minimal" />-->
<UI />
<MajorUpgrade AllowDowngrades="no" AllowSameVersionUpgrades="yes" MigrateFeatures="yes" Schedule="afterInstallInitialize" DowngradeErrorMessage="Newer version of NzbDrone is already installed." />
<Feature Id="DefaultFeature" Title="Main Feature" Level="1">
<ComponentRef Id="SX_APP_COMP" />
<ComponentRef Id="SX_START_SHORTCUT_COMP" />
<ComponentRef Id="SX_STARTUP_SHORTCUT_COMP" />
<ComponentRef Id="SX_DESKTOP_SHORTCUT_COMP" />
</Feature>
<PropertyRef Id="NETFRAMEWORK40FULL" />
<Condition Message="This application requires .NET Framework 4.0 or later. Please install the .NET Framework then run this installer again.">NETFRAMEWORK40FULL</Condition>
<Icon Id="SX_ICON" SourceFile="_raw\NzbDrone.Client.exe" />
<InstallExecuteSequence>
<InstallInitialize/>
<Custom Action="SX_START_ACTION" After="InstallFiles" />
<InstallFinalize/>
</InstallExecuteSequence>
<CustomAction Id="SX_START_ACTION" FileKey="SX_EXE_FILE" ExeCommand="" Execute="deferred" Impersonate="no" Return="asyncNoWait" />
</Product>
</Wix>

@ -17,11 +17,11 @@ define(
var seasonField = this.column.get('seasonNumber') || 'seasonNumber'; var seasonField = this.column.get('seasonNumber') || 'seasonNumber';
var episodeField = this.column.get('episodes') || 'episodeNumber'; var episodeField = this.column.get('episodes') || 'episodeNumber';
if (this.cellValue) { if (this.model) {
var airDate = this.cellValue.get(airDateField); var airDate = this.model.get(airDateField);
var seasonNumber = this.cellValue.get(seasonField); var seasonNumber = this.model.get(seasonField);
var episodes = this.cellValue.get(episodeField); var episodes = this.model.get(episodeField);
var result = 'Unknown'; var result = 'Unknown';

@ -27,7 +27,7 @@
{{#if nzbInfoUrl}} {{#if nzbInfoUrl}}
<dt>Info</dt> <dt>Info</dt>
<dd><a href="{{nzbInfoUrl}}">{{nzbInfoUrl}}o</a></dd> <dd><a href="{{infoUrl}}">{{infoUrl}}o</a></dd>
{{/if}} {{/if}}
{{/with}} {{/with}}
</dl> </dl>

Loading…
Cancel
Save