Merge branch 'master' of git://github.com/kayone/NzbDrone

This commit is contained in:
Mark McDowall 2011-05-26 22:02:55 -07:00
commit fc7d3da540
21 changed files with 761 additions and 333 deletions

View File

@ -0,0 +1,295 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AutoMoq;
using FizzWare.NBuilder;
using MbUnit.Framework;
using Moq;
using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Notification;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Indexer;
using NzbDrone.Core.Providers.Jobs;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class EpisodeSearchJobTest : TestBase
{
[Test]
public void ParseResult_should_return_after_match()
{
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(5)
.Build();
var episode = Builder<Episode>.CreateNew().Build();
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.GetMock<InventoryProvider>()
.Setup(c => c.IsNeeded(It.IsAny<EpisodeParseResult>())).Returns(true);
mocker.GetMock<DownloadProvider>()
.Setup(c => c.DownloadReport(It.IsAny<EpisodeParseResult>())).Returns(true);
//Act
mocker.Resolve<EpisodeSearchJob>().ProcessResults(new ProgressNotification("Test"), episode, parseResults);
//Assert
mocker.VerifyAllMocks();
mocker.GetMock<InventoryProvider>().Verify(c => c.IsNeeded(It.IsAny<EpisodeParseResult>()), Times.Once());
mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), Times.Once());
}
[Test]
public void higher_quality_should_be_called_first()
{
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(2)
.WhereTheFirst(1).Has(c => c.Quality = QualityTypes.Bluray1080p)
.AndTheNext(1).Has(c => c.Quality = QualityTypes.DVD)
.Build();
var episode = Builder<Episode>.CreateNew().Build();
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.GetMock<InventoryProvider>()
.Setup(c => c.IsNeeded(parseResults[0])).Returns(true);
mocker.GetMock<DownloadProvider>()
.Setup(c => c.DownloadReport(parseResults[0])).Returns(true);
//Act
mocker.Resolve<EpisodeSearchJob>().ProcessResults(new ProgressNotification("Test"), episode, parseResults);
//Assert
mocker.VerifyAllMocks();
mocker.GetMock<InventoryProvider>().Verify(c => c.IsNeeded(parseResults[0]), Times.Once());
mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(parseResults[0]), Times.Once());
}
[Test]
public void when_same_quality_proper_should_be_called_first()
{
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(20)
.WhereAll().Have(c => c.Quality = QualityTypes.DVD)
.And(c => c.Proper = false)
.WhereRandom(1).Has(c => c.Proper = true)
.Build();
Assert.Count(1, parseResults.Where(c => c.Proper));
var episode = Builder<Episode>.CreateNew().Build();
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.GetMock<InventoryProvider>()
.Setup(c => c.IsNeeded(It.Is<EpisodeParseResult>(p => p.Proper))).Returns(true);
mocker.GetMock<DownloadProvider>()
.Setup(c => c.DownloadReport(It.Is<EpisodeParseResult>(p => p.Proper))).Returns(true);
//Act
mocker.Resolve<EpisodeSearchJob>().ProcessResults(new ProgressNotification("Test"), episode, parseResults);
//Assert
mocker.VerifyAllMocks();
mocker.GetMock<InventoryProvider>().Verify(c => c.IsNeeded(It.Is<EpisodeParseResult>(p => p.Proper)), Times.Once());
mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.Is<EpisodeParseResult>(p => p.Proper)), Times.Once());
}
[Test]
public void when_not_needed_should_check_the_rest()
{
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(4)
.Build();
var episode = Builder<Episode>.CreateNew().Build();
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.GetMock<InventoryProvider>()
.Setup(c => c.IsNeeded(It.IsAny<EpisodeParseResult>())).Returns(false);
//Act
mocker.Resolve<EpisodeSearchJob>().ProcessResults(new ProgressNotification("Test"), episode, parseResults);
//Assert
mocker.VerifyAllMocks();
mocker.GetMock<InventoryProvider>().Verify(c => c.IsNeeded(It.IsAny<EpisodeParseResult>()), Times.Exactly(4));
ExceptionVerification.ExcpectedWarns(1);
}
[Test]
public void failed_IsNeeded_should_check_the_rest()
{
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(4)
.Build();
var episode = Builder<Episode>.CreateNew().Build();
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.GetMock<InventoryProvider>()
.Setup(c => c.IsNeeded(It.IsAny<EpisodeParseResult>())).Throws(new Exception());
//Act
mocker.Resolve<EpisodeSearchJob>().ProcessResults(new ProgressNotification("Test"), episode, parseResults);
//Assert
mocker.VerifyAllMocks();
mocker.GetMock<InventoryProvider>().Verify(c => c.IsNeeded(It.IsAny<EpisodeParseResult>()), Times.Exactly(4));
ExceptionVerification.ExcpectedErrors(4);
ExceptionVerification.ExcpectedWarns(1);
}
[Test]
[Row(0)]
[Row(-1)]
[Row(-100)]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void target_id_less_than_0_throws_exception(int target)
{
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), target);
}
[Test]
public void should_search_all_providers()
{
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(4)
.Build();
var episode = Builder<Episode>.CreateNew()
.With(c => c.Series = Builder<Series>.CreateNew().Build())
.With(c => c.SeasonNumber = 12)
.Build();
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.GetMock<EpisodeProvider>()
.Setup(c => c.GetEpisode(episode.EpisodeId))
.Returns(episode);
var indexer1 = new Mock<IndexerBase>();
indexer1.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber))
.Returns(parseResults).Verifiable();
var indexer2 = new Mock<IndexerBase>();
indexer2.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber))
.Returns(parseResults).Verifiable();
var indexers = new List<IndexerBase> { indexer1.Object, indexer2.Object };
mocker.GetMock<IndexerProvider>()
.Setup(c => c.GetEnabledIndexers())
.Returns(indexers);
mocker.GetMock<InventoryProvider>()
.Setup(c => c.IsNeeded(It.IsAny<EpisodeParseResult>())).Returns(false);
//Act
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), episode.EpisodeId);
//Assert
mocker.VerifyAllMocks();
mocker.GetMock<InventoryProvider>().Verify(c => c.IsNeeded(It.IsAny<EpisodeParseResult>()), Times.Exactly(8));
ExceptionVerification.ExcpectedWarns(1);
indexer1.VerifyAll();
indexer2.VerifyAll();
}
[Test]
public void failed_indexer_should_not_break_job()
{
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(4)
.Build();
var episode = Builder<Episode>.CreateNew()
.With(c => c.Series = Builder<Series>.CreateNew().Build())
.With(c => c.SeasonNumber = 12)
.Build();
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.GetMock<EpisodeProvider>()
.Setup(c => c.GetEpisode(episode.EpisodeId))
.Returns(episode);
var indexer1 = new Mock<IndexerBase>();
indexer1.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber))
.Returns(parseResults).Verifiable();
var indexer2 = new Mock<IndexerBase>();
indexer2.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber))
.Throws(new Exception()).Verifiable();
var indexer3 = new Mock<IndexerBase>();
indexer2.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber))
.Returns(parseResults).Verifiable();
var indexers = new List<IndexerBase> { indexer1.Object, indexer2.Object, indexer3.Object };
mocker.GetMock<IndexerProvider>()
.Setup(c => c.GetEnabledIndexers())
.Returns(indexers);
mocker.GetMock<InventoryProvider>()
.Setup(c => c.IsNeeded(It.IsAny<EpisodeParseResult>())).Returns(false);
//Act
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), episode.EpisodeId);
//Assert
mocker.VerifyAllMocks();
mocker.GetMock<InventoryProvider>().Verify(c => c.IsNeeded(It.IsAny<EpisodeParseResult>()), Times.Exactly(8));
ExceptionVerification.ExcpectedWarns(1);
ExceptionVerification.ExcpectedErrors(1);
indexer1.VerifyAll();
indexer2.VerifyAll();
indexer3.VerifyAll();
}
[Test]
public void no_episode_found_should_return_with_error_logged()
{
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.GetMock<EpisodeProvider>()
.Setup(c => c.GetEpisode(It.IsAny<long>()))
.Returns<Episode>(null);
//Act
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), 12);
//Assert
mocker.VerifyAllMocks();
ExceptionVerification.ExcpectedErrors(1);
}
}
}

View File

@ -27,17 +27,9 @@ namespace NzbDrone.Core.Test.Framework
internal static void AssertNoError()
{
if (_logs.Count != 0)
{
string errors = GetLogsString(_logs);
var message = String.Format("{0} unexpected Fatal/Error/Warning were logged during execution.\n\r Use ExceptionVerification.Excpected methods if errors are excepted for this test.{1}{2}",
_logs.Count,
Environment.NewLine,
errors);
Assert.Fail(message);
}
ExcpectedFatals(0);
ExcpectedErrors(0);
ExcpectedWarns(0);
}
private static string GetLogsString(IEnumerable<LogEventInfo> logs)
@ -48,9 +40,9 @@ namespace NzbDrone.Core.Test.Framework
string exception = "";
if (log.Exception != null)
{
exception = log.Exception.ToString();
exception = log.Exception.Message;
}
errors += Environment.NewLine + String.Format("[{0}] {1}: {2} {3}", log.Level, log.LoggerName, log.FormattedMessage, exception);
errors += Environment.NewLine + String.Format("[{0}] {1}: {2} [{3}]", log.Level, log.LoggerName, log.FormattedMessage, exception);
}
return errors;
}
@ -86,8 +78,14 @@ namespace NzbDrone.Core.Test.Framework
if (levelLogs.Count != count)
{
var message = String.Format("{0} {1}(s) were expected but {2} were logged.\n\r{3}",
count, level, _logs.Count, GetLogsString(levelLogs));
count, level, levelLogs.Count, GetLogsString(levelLogs));
message =
"********************************************************************************************************************************\n\r"
+ message +
"\n\r********************************************************************************************************************************";
Assert.Fail(message);
}

View File

@ -20,10 +20,8 @@ namespace NzbDrone.Core.Test
public class ImportNewSeriesJobTest : TestBase
{
[Test]
public void series_specific_scan_should_scan_series()
public void import_new_series_succesfull()
{
var series = Builder<Series>.CreateListOfSize(2)
.WhereAll().Have(s => s.Episodes = Builder<Episode>.CreateListOfSize(10).Build())
.WhereAll().Have(s => s.LastInfoSync = null)
@ -37,21 +35,38 @@ namespace NzbDrone.Core.Test
mocker.GetMock<SeriesProvider>()
.Setup(p => p.GetAllSeries())
.Returns(series.AsQueryable())
.Callback(() => series.ToList().ForEach(c => c.LastInfoSync = DateTime.Now));//Set the last scan time on the collection
.Returns(series.AsQueryable());
mocker.GetMock<DiskScanJob>()
.Setup(j => j.Start(notification, series[0].SeriesId));
.Setup(j => j.Start(notification, series[0].SeriesId))
.Callback(() => series[0].LastDiskSync = DateTime.Now)
.AtMostOnce();
mocker.GetMock<DiskScanJob>()
.Setup(j => j.Start(notification, series[1].SeriesId));
.Setup(j => j.Start(notification, series[1].SeriesId))
.Callback(() => series[1].LastDiskSync = DateTime.Now)
.AtMostOnce();
mocker.GetMock<UpdateInfoJob>()
.Setup(j => j.Start(notification, series[0].SeriesId));
.Setup(j => j.Start(notification, series[0].SeriesId))
.Callback(() => series[0].LastInfoSync = DateTime.Now)
.AtMostOnce();
mocker.GetMock<UpdateInfoJob>()
.Setup(j => j.Start(notification, series[1].SeriesId));
.Setup(j => j.Start(notification, series[1].SeriesId))
.Callback(() => series[1].LastInfoSync = DateTime.Now)
.AtMostOnce();
mocker.GetMock<SeriesProvider>()
.Setup(s => s.GetSeries(series[0].SeriesId)).Returns(series[0]);
mocker.GetMock<SeriesProvider>()
.Setup(s => s.GetSeries(series[1].SeriesId)).Returns(series[1]);
mocker.GetMock<MediaFileProvider>()
.Setup(s => s.GetSeriesFiles(It.IsAny<int>())).Returns(new List<EpisodeFile>());
//Act
mocker.Resolve<ImportNewSeriesJob>().Start(notification, 0);
@ -61,81 +76,57 @@ namespace NzbDrone.Core.Test
}
[Test]
public void series_with_no_episodes_should_skip_scan()
[Timeout(3)]
public void failed_import_should_not_be_stuck_in_loop()
{
var series = Builder<Series>.CreateNew()
.With(s => s.SeriesId = 12)
.With(s => s.Episodes = new List<Episode>())
.Build();
var series = Builder<Series>.CreateListOfSize(2)
.WhereAll().Have(s => s.Episodes = Builder<Episode>.CreateListOfSize(10).Build())
.WhereAll().Have(s => s.LastInfoSync = null)
.WhereTheFirst(1).Has(s => s.SeriesId = 12)
.AndTheNext(1).Has(s => s.SeriesId = 15)
.Build();
var notification = new ProgressNotification("Test");
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.GetMock<SeriesProvider>()
.Setup(p => p.GetSeries(series.SeriesId))
.Returns(series);
.Setup(p => p.GetAllSeries())
.Returns(series.AsQueryable());
mocker.GetMock<UpdateInfoJob>()
.Setup(j => j.Start(notification, series[0].SeriesId))
.Callback(() => series[0].LastInfoSync = DateTime.Now)
.AtMostOnce();
mocker.GetMock<UpdateInfoJob>()
.Setup(j => j.Start(notification, series[1].SeriesId))
.Throws(new InvalidOperationException())
.AtMostOnce();
mocker.GetMock<DiskScanJob>()
.Setup(j => j.Start(notification, series[0].SeriesId))
.Callback(() => series[0].LastDiskSync = DateTime.Now)
.AtMostOnce();
mocker.GetMock<SeriesProvider>()
.Setup(s => s.GetSeries(series[0].SeriesId)).Returns(series[0]);
mocker.GetMock<MediaFileProvider>()
.Setup(s => s.GetSeriesFiles(It.IsAny<int>())).Returns(new List<EpisodeFile>());
//Act
mocker.Resolve<DiskScanJob>().Start(new ProgressNotification("Test"), series.SeriesId);
mocker.Resolve<ImportNewSeriesJob>().Start(notification, 0);
//Assert
mocker.VerifyAllMocks();
ExceptionVerification.ExcpectedErrors(1);
}
[Test]
public void job_with_no_target_should_scan_all_series()
{
var series = Builder<Series>.CreateListOfSize(2)
.WhereTheFirst(1).Has(s => s.SeriesId = 12)
.AndTheNext(1).Has(s => s.SeriesId = 15)
.WhereAll().Have(s => s.Episodes = Builder<Episode>.CreateListOfSize(10).Build())
.Build();
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.GetMock<SeriesProvider>()
.Setup(p => p.GetAllSeries())
.Returns(series.AsQueryable());
mocker.GetMock<MediaFileProvider>()
.Setup(s => s.Scan(series[0]))
.Returns(new List<EpisodeFile>());
mocker.GetMock<MediaFileProvider>()
.Setup(s => s.Scan(series[1]))
.Returns(new List<EpisodeFile>());
mocker.Resolve<DiskScanJob>().Start(new ProgressNotification("Test"), 0);
mocker.VerifyAllMocks();
}
[Test]
public void job_with_no_target_should_scan_series_with_episodes()
{
var series = Builder<Series>.CreateListOfSize(2)
.WhereTheFirst(1).Has(s => s.SeriesId = 12)
.And(s => s.Episodes = Builder<Episode>.CreateListOfSize(10).Build())
.AndTheNext(1).Has(s => s.SeriesId = 15)
.And(s => s.Episodes = new List<Episode>())
.Build();
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.GetMock<SeriesProvider>()
.Setup(p => p.GetAllSeries())
.Returns(series.AsQueryable());
mocker.GetMock<MediaFileProvider>()
.Setup(s => s.Scan(series[0]))
.Returns(new List<EpisodeFile>());
mocker.Resolve<DiskScanJob>().Start(new ProgressNotification("Test"), 0);
mocker.VerifyAllMocks();
}
}
}

View File

@ -23,151 +23,6 @@ namespace NzbDrone.Core.Test
// ReSharper disable InconsistentNaming
public class IndexerProviderTest : TestBase
{
[Test]
[Row("nzbsorg.xml", 0)]
[Row("nzbsrus.xml", 6)]
[Row("newzbin.xml", 1)]
[Row("nzbmatrix.xml", 1)]
public void parse_feed_xml(string fileName, int warns)
{
var mocker = new AutoMoqer();
mocker.GetMock<HttpProvider>()
.Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>()))
.Returns(File.OpenRead(".\\Files\\Rss\\" + fileName));
var fakeSettings = Builder<IndexerSetting>.CreateNew().Build();
mocker.GetMock<IndexerProvider>()
.Setup(c => c.GetSettings(It.IsAny<Type>()))
.Returns(fakeSettings);
var mockIndexer = mocker.Resolve<MockIndexer>();
var parseResults = mockIndexer.FetchRss();
foreach (var episodeParseResult in parseResults)
{
var Uri = new Uri(episodeParseResult.NzbUrl);
Assert.DoesNotContain(Uri.PathAndQuery, "//");
}
Assert.IsNotEmpty(parseResults);
Assert.ForAll(parseResults, s => Assert.AreEqual(mockIndexer.Name, s.Indexer));
Assert.ForAll(parseResults, s => Assert.AreNotEqual("", s.NzbTitle));
Assert.ForAll(parseResults, s => Assert.AreNotEqual(null, s.NzbTitle));
ExceptionVerification.ExcpectedWarns(warns);
}
[Test]
public void newzbin()
{
var mocker = new AutoMoqer();
mocker.GetMock<HttpProvider>()
.Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>()))
.Returns(File.OpenRead(".\\Files\\Rss\\newzbin.xml"));
var fakeSettings = Builder<IndexerSetting>.CreateNew().Build();
mocker.GetMock<IndexerProvider>()
.Setup(c => c.GetSettings(It.IsAny<Type>()))
.Returns(fakeSettings);
var newzbinProvider = mocker.Resolve<Newzbin>();
var parseResults = newzbinProvider.FetchRss();
foreach (var episodeParseResult in parseResults)
{
var Uri = new Uri(episodeParseResult.NzbUrl);
Assert.DoesNotContain(Uri.PathAndQuery, "//");
}
Assert.IsNotEmpty(parseResults);
Assert.ForAll(parseResults, s => Assert.AreEqual(newzbinProvider.Name, s.Indexer));
Assert.ForAll(parseResults, s => Assert.AreNotEqual("", s.NzbTitle));
Assert.ForAll(parseResults, s => Assert.AreNotEqual(null, s.NzbTitle));
ExceptionVerification.ExcpectedWarns(1);
}
[Test]
[Row("Adventure.Inc.S03E19.DVDRip.XviD-OSiTV", 3, 19, QualityTypes.DVD)]
public void custome_parser_partial_success(string title, int season, int episode, QualityTypes quality)
{
var mocker = new AutoMoqer();
const string summary = "My fake summary";
var fakeSettings = Builder<IndexerSetting>.CreateNew().Build();
mocker.GetMock<IndexerProvider>()
.Setup(c => c.GetSettings(It.IsAny<Type>()))
.Returns(fakeSettings);
var fakeRssItem = Builder<SyndicationItem>.CreateNew()
.With(c => c.Title = new TextSyndicationContent(title))
.With(c => c.Summary = new TextSyndicationContent(summary))
.Build();
var result = mocker.Resolve<CustomParserIndexer>().ParseFeed(fakeRssItem);
Assert.IsNotNull(result);
Assert.AreEqual(summary, result.EpisodeTitle);
Assert.AreEqual(season, result.SeasonNumber);
Assert.AreEqual(episode, result.EpisodeNumbers[0]);
Assert.AreEqual(quality, result.Quality);
}
[Test]
[Row("Adventure.Inc.DVDRip.XviD-OSiTV")]
public void custome_parser_full_parse(string title)
{
var mocker = new AutoMoqer();
const string summary = "My fake summary";
var fakeSettings = Builder<IndexerSetting>.CreateNew().Build();
mocker.GetMock<IndexerProvider>()
.Setup(c => c.GetSettings(It.IsAny<Type>()))
.Returns(fakeSettings);
var fakeRssItem = Builder<SyndicationItem>.CreateNew()
.With(c => c.Title = new TextSyndicationContent(title))
.With(c => c.Summary = new TextSyndicationContent(summary))
.Build();
var result = mocker.Resolve<CustomParserIndexer>().ParseFeed(fakeRssItem);
Assert.IsNotNull(result);
Assert.AreEqual(summary, result.EpisodeTitle);
ExceptionVerification.ExcpectedWarns(1);
}
[Test]
public void downloadFeed()
{
var mocker = new AutoMoqer();
mocker.SetConstant(new HttpProvider());
var fakeSettings = Builder<IndexerSetting>.CreateNew().Build();
mocker.GetMock<IndexerProvider>()
.Setup(c => c.GetSettings(It.IsAny<Type>()))
.Returns(fakeSettings);
mocker.Resolve<TestUrlIndexer>().FetchRss();
ExceptionVerification.IgnoreWarns();
}
[Test]
public void Init_indexer_test()
{
@ -178,49 +33,32 @@ namespace NzbDrone.Core.Test
//Act
var indexerProvider = mocker.Resolve<IndexerProvider>();
indexerProvider.InitializeIndexers(new List<IndexerBase> { mocker.Resolve<MockIndexer>() });
var indexers = indexerProvider.All();
var settings = indexerProvider.GetSettings(typeof(MockIndexer));
settings.Enable = true;
indexerProvider.SaveSettings(settings);
//Assert
Assert.Count(1, indexers);
Assert.Count(1, indexerProvider.GetAllISettings());
Assert.Count(1, indexerProvider.GetEnabledIndexers());
}
[Test]
public void unmapped_series_shouldnt_call_any_providers()
{
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.GetMock<SeriesProvider>()
.Setup(c => c.FindSeries(It.IsAny<String>()))
.Returns<Series>(null);
var indexer = mocker.Resolve<MockIndexer>();
//indexer.ProcessItem(new SyndicationItem { Title = new TextSyndicationContent("Adventure.Inc.S01E18.DVDRip.XviD-OSiTV") });
}
[Test]
public void nzbsorg_search_returns_valid_results()
public void Init_indexer_with_disabled_job()
{
var mocker = new AutoMoqer();
mocker.GetMock<ConfigProvider>()
.SetupGet(c => c.NzbsOrgUId)
.Returns("43516");
mocker.SetConstant(MockLib.GetEmptyRepository());
mocker.GetMock<ConfigProvider>()
.SetupGet(c => c.NzbsOrgHash)
.Returns("bc8edb4cc49d4ae440775adec5ac001f");
mocker.Resolve<HttpProvider>();
var result = mocker.Resolve<NzbsOrg>().FetchEpisode("Simpsons", 21, 23);
Assert.IsNotEmpty(result);
Assert.ForAll(result, r => r.CleanTitle == "simpsons");
Assert.ForAll(result, r => r.SeasonNumber == 21);
Assert.ForAll(result, r => r.EpisodeNumbers.Contains(23));
//Act
var indexerProvider = mocker.Resolve<IndexerProvider>();
indexerProvider.InitializeIndexers(new List<IndexerBase> { mocker.Resolve<MockIndexer>() });
var settings = indexerProvider.GetSettings(typeof(MockIndexer));
settings.Enable = false;
indexerProvider.SaveSettings(settings);
//Assert
Assert.Count(1, indexerProvider.GetAllISettings());
Assert.IsEmpty(indexerProvider.GetEnabledIndexers());
}
}

View File

@ -0,0 +1,194 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.ServiceModel.Syndication;
using System.Text;
using AutoMoq;
using FizzWare.NBuilder;
using Gallio.Framework;
using MbUnit.Framework;
using MbUnit.Framework.ContractVerifiers;
using Moq;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.Indexer;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class IndexerTests : TestBase
{
[Test]
[Row("nzbsorg.xml", 0)]
[Row("nzbsrus.xml", 6)]
[Row("newzbin.xml", 1)]
[Row("nzbmatrix.xml", 1)]
public void parse_feed_xml(string fileName, int warns)
{
var mocker = new AutoMoqer();
mocker.GetMock<HttpProvider>()
.Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>()))
.Returns(File.OpenRead(".\\Files\\Rss\\" + fileName));
var fakeSettings = Builder<IndexerSetting>.CreateNew().Build();
mocker.GetMock<IndexerProvider>()
.Setup(c => c.GetSettings(It.IsAny<Type>()))
.Returns(fakeSettings);
var mockIndexer = mocker.Resolve<MockIndexer>();
var parseResults = mockIndexer.FetchRss();
foreach (var episodeParseResult in parseResults)
{
var Uri = new Uri(episodeParseResult.NzbUrl);
Assert.DoesNotContain(Uri.PathAndQuery, "//");
}
Assert.IsNotEmpty(parseResults);
Assert.ForAll(parseResults, s => Assert.AreEqual(mockIndexer.Name, s.Indexer));
Assert.ForAll(parseResults, s => Assert.AreNotEqual("", s.NzbTitle));
Assert.ForAll(parseResults, s => Assert.AreNotEqual(null, s.NzbTitle));
ExceptionVerification.ExcpectedWarns(warns);
}
[Test]
public void newzbin()
{
var mocker = new AutoMoqer();
mocker.GetMock<HttpProvider>()
.Setup(h => h.DownloadStream(It.IsAny<String>(), It.IsAny<NetworkCredential>()))
.Returns(File.OpenRead(".\\Files\\Rss\\newzbin.xml"));
var fakeSettings = Builder<IndexerSetting>.CreateNew().Build();
mocker.GetMock<IndexerProvider>()
.Setup(c => c.GetSettings(It.IsAny<Type>()))
.Returns(fakeSettings);
var newzbinProvider = mocker.Resolve<Newzbin>();
var parseResults = newzbinProvider.FetchRss();
foreach (var episodeParseResult in parseResults)
{
var Uri = new Uri(episodeParseResult.NzbUrl);
Assert.DoesNotContain(Uri.PathAndQuery, "//");
}
Assert.IsNotEmpty(parseResults);
Assert.ForAll(parseResults, s => Assert.AreEqual(newzbinProvider.Name, s.Indexer));
Assert.ForAll(parseResults, s => Assert.AreNotEqual("", s.NzbTitle));
Assert.ForAll(parseResults, s => Assert.AreNotEqual(null, s.NzbTitle));
ExceptionVerification.ExcpectedWarns(1);
}
[Test]
[Row("Adventure.Inc.S03E19.DVDRip.XviD-OSiTV", 3, 19, QualityTypes.DVD)]
public void custome_parser_partial_success(string title, int season, int episode, QualityTypes quality)
{
var mocker = new AutoMoqer();
const string summary = "My fake summary";
var fakeSettings = Builder<IndexerSetting>.CreateNew().Build();
mocker.GetMock<IndexerProvider>()
.Setup(c => c.GetSettings(It.IsAny<Type>()))
.Returns(fakeSettings);
var fakeRssItem = Builder<SyndicationItem>.CreateNew()
.With(c => c.Title = new TextSyndicationContent(title))
.With(c => c.Summary = new TextSyndicationContent(summary))
.Build();
var result = mocker.Resolve<CustomParserIndexer>().ParseFeed(fakeRssItem);
Assert.IsNotNull(result);
Assert.AreEqual(summary, result.EpisodeTitle);
Assert.AreEqual(season, result.SeasonNumber);
Assert.AreEqual(episode, result.EpisodeNumbers[0]);
Assert.AreEqual(quality, result.Quality);
}
[Test]
[Row("Adventure.Inc.DVDRip.XviD-OSiTV")]
public void custome_parser_full_parse(string title)
{
var mocker = new AutoMoqer();
const string summary = "My fake summary";
var fakeSettings = Builder<IndexerSetting>.CreateNew().Build();
mocker.GetMock<IndexerProvider>()
.Setup(c => c.GetSettings(It.IsAny<Type>()))
.Returns(fakeSettings);
var fakeRssItem = Builder<SyndicationItem>.CreateNew()
.With(c => c.Title = new TextSyndicationContent(title))
.With(c => c.Summary = new TextSyndicationContent(summary))
.Build();
var result = mocker.Resolve<CustomParserIndexer>().ParseFeed(fakeRssItem);
Assert.IsNotNull(result);
Assert.AreEqual(summary, result.EpisodeTitle);
ExceptionVerification.ExcpectedWarns(1);
}
[Test]
public void downloadFeed()
{
var mocker = new AutoMoqer();
mocker.SetConstant(new HttpProvider());
var fakeSettings = Builder<IndexerSetting>.CreateNew().Build();
mocker.GetMock<IndexerProvider>()
.Setup(c => c.GetSettings(It.IsAny<Type>()))
.Returns(fakeSettings);
mocker.Resolve<TestUrlIndexer>().FetchRss();
ExceptionVerification.IgnoreWarns();
}
[Test]
public void nzbsorg_search_returns_valid_results()
{
var mocker = new AutoMoqer();
mocker.GetMock<ConfigProvider>()
.SetupGet(c => c.NzbsOrgUId)
.Returns("43516");
mocker.GetMock<ConfigProvider>()
.SetupGet(c => c.NzbsOrgHash)
.Returns("bc8edb4cc49d4ae440775adec5ac001f");
mocker.Resolve<HttpProvider>();
var result = mocker.Resolve<NzbsOrg>().FetchEpisode("Simpsons", 21, 23);
Assert.IsNotEmpty(result);
Assert.ForAll(result, r => r.CleanTitle == "simpsons");
Assert.ForAll(result, r => r.SeasonNumber == 21);
Assert.ForAll(result, r => r.EpisodeNumbers.Contains(23));
}
}
}

View File

@ -114,13 +114,13 @@ namespace NzbDrone.Core.Test
var timerProvider = mocker.Resolve<JobProvider>();
timerProvider.Initialize();
var firstRun = timerProvider.QueueJob(typeof(SlowJob),1);
var secondRun = timerProvider.QueueJob(typeof(SlowJob),2);
var third = timerProvider.QueueJob(typeof(SlowJob),3);
var firstRun = timerProvider.QueueJob(typeof(SlowJob), 1);
var secondRun = timerProvider.QueueJob(typeof(SlowJob), 2);
var third = timerProvider.QueueJob(typeof(SlowJob), 3);
Thread.Sleep(10000);
//Asserts are done in ExceptionVerification
}

View File

@ -86,9 +86,11 @@
</ItemGroup>
<ItemGroup>
<Compile Include="DownloadProviderTest.cs" />
<Compile Include="EpisodeSearchJobTest.cs" />
<Compile Include="EpisodeStatusTest.cs" />
<Compile Include="ImportNewSeriesJobTest.cs" />
<Compile Include="DiskScanJobTest.cs" />
<Compile Include="IndexerTests.cs" />
<Compile Include="InventoryProviderTest.cs" />
<Compile Include="Framework\AutoMoq\AutoMoqer.cs" />
<Compile Include="Framework\AutoMoq\AutoMoqerTest.cs" />

View File

@ -63,6 +63,9 @@ namespace NzbDrone.Core.Test
public void enteties_toString()
{
Console.WriteLine(new Episode().ToString());
Console.WriteLine(new Season().ToString());
Console.WriteLine(new Series().ToString());
Console.WriteLine(new EpisodeFile().ToString());
}
[Test]

View File

@ -53,12 +53,12 @@ namespace NzbDrone.Core
private static void InitializeApp()
{
BindKernel();
LogConfiguration.Setup();
Migrations.Run();
ForceMigration(_kernel.Get<IRepository>());
SetupDefaultQualityProfiles(_kernel.Get<IRepository>()); //Setup the default QualityProfiles on start-up
BindIndexers();
@ -110,8 +110,6 @@ namespace NzbDrone.Core
_kernel.Bind<IndexerBase>().To<NzbMatrix>().InSingletonScope();
_kernel.Bind<IndexerBase>().To<NzbsRUs>().InSingletonScope();
_kernel.Bind<IndexerBase>().To<Newzbin>().InSingletonScope();
var indexers = _kernel.GetAll<IndexerBase>();
_kernel.Get<IndexerProvider>().InitializeIndexers(indexers.ToList());
}
private static void BindJobs()

View File

@ -96,7 +96,7 @@
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRuleSet>Properties\AnalysisRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
<CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
<CodeAnalysisIgnoreBuiltInRules>false</CodeAnalysisIgnoreBuiltInRules>
@ -264,6 +264,7 @@
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="Properties\AnalysisRules.ruleset" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Migrator.net\Migrator.Framework\Migrator.Framework.csproj">

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Copy of Microsoft Basic Correctness Rules" Description="These rules focus on logic errors and common mistakes made in the usage of framework APIs. Include this rule set to expand on the list of warnings reported by the minimum recommended rules." ToolsVersion="10.0">
<Include Path="minimumrecommendedrules.ruleset" Action="Default" />
<Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
<Rule Id="CA1008" Action="Warning" />
<Rule Id="CA1013" Action="Warning" />
<Rule Id="CA1301" Action="None" />
<Rule Id="CA1806" Action="Warning" />
<Rule Id="CA1816" Action="Warning" />
<Rule Id="CA1819" Action="Warning" />
<Rule Id="CA1820" Action="Warning" />
<Rule Id="CA1903" Action="Warning" />
<Rule Id="CA2004" Action="Warning" />
<Rule Id="CA2006" Action="Warning" />
<Rule Id="CA2101" Action="None" />
<Rule Id="CA2102" Action="Warning" />
<Rule Id="CA2105" Action="Warning" />
<Rule Id="CA2106" Action="Warning" />
<Rule Id="CA2115" Action="Warning" />
<Rule Id="CA2119" Action="Warning" />
<Rule Id="CA2120" Action="Warning" />
<Rule Id="CA2121" Action="Warning" />
<Rule Id="CA2122" Action="None" />
<Rule Id="CA2130" Action="Warning" />
<Rule Id="CA2205" Action="Warning" />
<Rule Id="CA2215" Action="Warning" />
<Rule Id="CA2221" Action="Warning" />
<Rule Id="CA2222" Action="Warning" />
<Rule Id="CA2223" Action="Warning" />
<Rule Id="CA2224" Action="Warning" />
<Rule Id="CA2226" Action="Warning" />
<Rule Id="CA2231" Action="Warning" />
<Rule Id="CA2239" Action="Warning" />
</Rules>
</RuleSet>

View File

@ -6,7 +6,6 @@ using System.Web;
using NLog;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers.Indexer
{
@ -24,6 +23,11 @@ namespace NzbDrone.Core.Providers.Indexer
_logger = LogManager.GetLogger(GetType().ToString());
}
public IndexerBase()
{
}
/// <summary>
/// Gets the name for the feed
/// </summary>
@ -35,15 +39,6 @@ namespace NzbDrone.Core.Providers.Indexer
protected abstract string[] Urls { get; }
/// <summary>
/// Gets the rss url for specific episode search
/// </summary>
/// <param name="seriesTitle">The series title.</param>
/// <param name="seasonNumber">The season number.</param>
/// <param name="episodeNumber">The episode number.</param>
/// <returns></returns>
protected abstract IList<String> GetSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber);
/// <summary>
/// Gets the credential.
/// </summary>
@ -53,10 +48,37 @@ namespace NzbDrone.Core.Providers.Indexer
}
/// <summary>
/// Gets the rss url for specific episode search
/// </summary>
/// <param name="seriesTitle">The series title.</param>
/// <param name="seasonNumber">The season number.</param>
/// <param name="episodeNumber">The episode number.</param>
/// <returns></returns>
protected abstract IList<String> GetSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber);
/// <summary>
/// This method can be overwritten to provide indexer specific info parsing
/// </summary>
/// <param name="item">RSS item that needs to be parsed</param>
/// <param name="currentResult">Result of the built in parse function.</param>
/// <returns></returns>
protected virtual EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult)
{
return currentResult;
}
/// <summary>
/// Generates direct link to download an NZB
/// </summary>
/// <param name = "item">RSS Feed item to generate the link for</param>
/// <returns>Download link URL</returns>
protected abstract string NzbDownloadUrl(SyndicationItem item);
/// <summary>
/// Fetches RSS feed and process each news item.
/// </summary>
public IList<EpisodeParseResult> FetchRss()
public virtual IList<EpisodeParseResult> FetchRss()
{
_logger.Debug("Fetching feeds from " + Name);
@ -72,7 +94,7 @@ namespace NzbDrone.Core.Providers.Indexer
}
public IList<EpisodeParseResult> FetchEpisode(string seriesTitle, int seasonNumber, int episodeNumber)
public virtual IList<EpisodeParseResult> FetchEpisode(string seriesTitle, int seasonNumber, int episodeNumber)
{
_logger.Debug("Searching {0} for {1}-S{2}E{3:00}", Name, seriesTitle, seasonNumber, episodeNumber);
@ -90,7 +112,7 @@ namespace NzbDrone.Core.Providers.Indexer
}
private IList<EpisodeParseResult> Fetch(string url)
private IEnumerable<EpisodeParseResult> Fetch(string url)
{
var result = new List<EpisodeParseResult>();
@ -140,23 +162,5 @@ namespace NzbDrone.Core.Providers.Indexer
return CustomParser(item, episodeParseResult);
}
/// <summary>
/// This method can be overwritten to provide indexer specific info parsing
/// </summary>
/// <param name="item">RSS item that needs to be parsed</param>
/// <param name="currentResult">Result of the built in parse function.</param>
/// <returns></returns>
protected virtual EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult)
{
return currentResult;
}
/// <summary>
/// Generates direct link to download an NZB
/// </summary>
/// <param name = "item">RSS Feed item to generate the link for</param>
/// <returns>Download link URL</returns>
protected abstract string NzbDownloadUrl(SyndicationItem item);
}
}

View File

@ -13,6 +13,8 @@ namespace NzbDrone.Core.Providers
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly IRepository _repository;
private IList<IndexerBase> _indexers = new List<IndexerBase>();
public IndexerProvider(IRepository repository)
{
_repository = repository;
@ -23,7 +25,13 @@ namespace NzbDrone.Core.Providers
}
public virtual List<IndexerSetting> All()
public virtual IList<IndexerBase> GetEnabledIndexers()
{
var all = GetAllISettings();
return _indexers.Where(i => all.Exists(c => c.IndexProviderType == i.GetType().ToString() && c.Enable)).ToList();
}
public virtual List<IndexerSetting> GetAllISettings()
{
return _repository.All<IndexerSetting>().ToList();
}
@ -47,23 +55,20 @@ namespace NzbDrone.Core.Providers
return _repository.Single<IndexerSetting>(s => s.IndexProviderType == type.ToString());
}
public virtual IndexerSetting GetSettings(int id)
{
return _repository.Single<IndexerSetting>(s => s.Id == id);
}
public virtual void InitializeIndexers(IList<IndexerBase> indexers)
{
Logger.Info("Initializing indexers. Count {0}", indexers.Count);
var currentIndexers = All();
_indexers = indexers;
var currentIndexers = GetAllISettings();
foreach (var feedProvider in indexers)
{
IndexerBase indexerLocal = feedProvider;
if (!currentIndexers.Exists(c => c.IndexProviderType == indexerLocal.GetType().ToString()))
{
var settings = new IndexerSetting()
var settings = new IndexerSetting
{
Enable = false,
IndexProviderType = indexerLocal.GetType().ToString(),

View File

@ -12,20 +12,23 @@ namespace NzbDrone.Core.Providers
private readonly SeasonProvider _seasonProvider;
private readonly EpisodeProvider _episodeProvider;
private readonly HistoryProvider _historyProvider;
private readonly SabProvider _sabProvider;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public InventoryProvider(SeriesProvider seriesProvider, SeasonProvider seasonProvider, EpisodeProvider episodeProvider, HistoryProvider historyProvider, SabProvider sabProvider)
public InventoryProvider(SeriesProvider seriesProvider, SeasonProvider seasonProvider, EpisodeProvider episodeProvider, HistoryProvider historyProvider)
{
_seriesProvider = seriesProvider;
_seasonProvider = seasonProvider;
_episodeProvider = episodeProvider;
_historyProvider = historyProvider;
_sabProvider = sabProvider;
}
internal bool IsNeeded(EpisodeParseResult parseResult)
public InventoryProvider()
{
}
public virtual bool IsNeeded(EpisodeParseResult parseResult)
{
var series = _seriesProvider.FindSeries(parseResult.CleanTitle);

View File

@ -5,12 +5,12 @@ using NLog;
using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Notification;
using NzbDrone.Core.Providers.Indexer;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers.Jobs
{
public class EpisodeSearchJob : IJob
{
private readonly IEnumerable<IndexerBase> _indexers;
private readonly InventoryProvider _inventoryProvider;
private readonly DownloadProvider _downloadProvider;
private readonly IndexerProvider _indexerProvider;
@ -19,9 +19,8 @@ namespace NzbDrone.Core.Providers.Jobs
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public EpisodeSearchJob(IEnumerable<IndexerBase> indexers, InventoryProvider inventoryProvider, DownloadProvider downloadProvider, IndexerProvider indexerProvider, EpisodeProvider episodeProvider)
public EpisodeSearchJob(InventoryProvider inventoryProvider, DownloadProvider downloadProvider, IndexerProvider indexerProvider, EpisodeProvider episodeProvider)
{
_indexers = indexers;
_inventoryProvider = inventoryProvider;
_downloadProvider = downloadProvider;
_indexerProvider = indexerProvider;
@ -40,27 +39,53 @@ namespace NzbDrone.Core.Providers.Jobs
public void Start(ProgressNotification notification, int targetId)
{
var reports = new List<EpisodeParseResult>();
if (targetId <= 0)
throw new ArgumentOutOfRangeException("targetId");
var episode = _episodeProvider.GetEpisode(targetId);
if (episode == null)
{
Logger.Error("Unbale to find an episode {0} in database", targetId);
return;
}
var indexers = _indexerProvider.GetEnabledIndexers();
var reports = new List<EpisodeParseResult>();
foreach (var indexer in _indexers.Where(i => _indexerProvider.GetSettings(i.GetType()).Enable))
foreach (var indexer in indexers)
{
try
{
notification.CurrentMessage = String.Format("Searching for {0} in {1}", episode, indexer.Name);
reports.AddRange(indexer.FetchRss());
IList<EpisodeParseResult> indexerResults = new List<EpisodeParseResult>();
if (episode.IsDailyEpisode)
{
//TODO:Add support for daily episodes
}
else
{
indexerResults = indexer.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber);
}
reports.AddRange(indexerResults);
}
catch (Exception e)
{
Logger.ErrorException("An error has occured while fetching items from " + indexer.Name, e);
Logger.ErrorException("An error has occurred while fetching items from " + indexer.Name, e);
}
}
Logger.Debug("Finished searching all indexers. Total {0}", reports.Count);
notification.CurrentMessage = "Proccessing search results";
notification.CurrentMessage = "Processing search results";
foreach (var episodeParseResult in reports.OrderBy(c => c.Quality).ThenBy(c => c.Proper))
ProcessResults(notification, episode, reports);
}
public void ProcessResults(ProgressNotification notification, Episode episode, IEnumerable<EpisodeParseResult> reports)
{
foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality).ThenByDescending(c => c.Proper))
{
try
{
@ -73,13 +98,11 @@ namespace NzbDrone.Core.Providers.Jobs
}
catch (Exception e)
{
Logger.ErrorException("An error has occured while processing parse result items from " + episodeParseResult, e);
Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e);
}
}
Logger.Warn("Unable to find {0} in any of indexers.", episode);
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
@ -21,6 +22,8 @@ namespace NzbDrone.Core.Providers.Jobs
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private List<int> _attemptedSeries;
public ImportNewSeriesJob(SeriesProvider seriesProvider, SeasonProvider seasonProvider,
MediaFileProvider mediaFileProvider, UpdateInfoJob updateInfoJob, DiskScanJob diskScanJob)
{
@ -43,12 +46,13 @@ namespace NzbDrone.Core.Providers.Jobs
public void Start(ProgressNotification notification, int targetId)
{
_attemptedSeries = new List<int>();
ScanSeries(notification);
}
private void ScanSeries(ProgressNotification notification)
{
var syncList = _seriesProvider.GetAllSeries().Where(s => s.LastInfoSync == null).ToList();
var syncList = _seriesProvider.GetAllSeries().Where(s => s.LastInfoSync == null && !_attemptedSeries.Contains(s.SeriesId)).ToList();
if (syncList.Count == 0)
{
return;
@ -58,6 +62,7 @@ namespace NzbDrone.Core.Providers.Jobs
{
try
{
_attemptedSeries.Add(currentSeries.SeriesId);
notification.CurrentMessage = String.Format("Searching for '{0}'", new DirectoryInfo(currentSeries.Path).Name);
_updateInfoJob.Start(notification, currentSeries.SeriesId);

View File

@ -25,6 +25,14 @@ namespace NzbDrone.Core.Repository
public Boolean Ignored { get; set; }
[SubSonicIgnore]
public Boolean IsDailyEpisode
{
get
{
return EpisodeNumber == 0;
}
}
/// <summary>
/// Gets or sets the grab date.
@ -72,10 +80,12 @@ namespace NzbDrone.Core.Repository
public override string ToString()
{
if (EpisodeNumber == 0)
return string.Format("{0} - {1}", Series.Title, AirDate.Date);
var seriesTitle = Series == null ? "[NULL]" : Series.Title;
return string.Format("{0} - S{1:00}E{2}", Series.Title, SeasonNumber, EpisodeNumber);
if (IsDailyEpisode)
return string.Format("{0} - {1}", seriesTitle, AirDate.Date);
return string.Format("{0} - S{1:00}E{2}", seriesTitle, SeasonNumber, EpisodeNumber);
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace NzbDrone.Web.Controllers
{
public class StreamController : Controller
{
//
// GET: /Stream/
public ActionResult Index()
{
return File(@"Z:\Clone High\Season 1\S01E02 - Episode Two- Election Blu-Galoo.avi", "video/divx");
//return File(@"Z:\30 Rock\Season 5\S05E04 - Live Show (East Coast Taping) - HD TV.mkv", "video/divx");
}
}
}

View File

@ -26,7 +26,7 @@ namespace NzbDrone.Web.Controllers
public ActionResult Indexers()
{
return View(_indexerProvider.All());
return View(_indexerProvider.GetAllISettings());
}

View File

@ -31,6 +31,7 @@
<MvcBuildViews>true</MvcBuildViews>
<EnableUpdateable>false</EnableUpdateable>
<ExcludeApp_Data>true</ExcludeApp_Data>
<CodeAnalysisRuleSet>BasicCorrectnessRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@ -223,6 +224,7 @@
<Compile Include="Controllers\SeriesController.cs" />
<Compile Include="Controllers\SettingsController.cs" />
<Compile Include="Controllers\SharedController.cs" />
<Compile Include="Controllers\StreamController.cs" />
<Compile Include="Controllers\SystemController.cs" />
<Compile Include="Controllers\UpcomingController.cs" />
<Compile Include="Global.asax.cs">

View File

@ -39,6 +39,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<UseVSHostingProcess>true</UseVSHostingProcess>
<CodeAnalysisRuleSet>BasicCorrectnessRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>