diff --git a/NzbDrone.Core.Test/BannerDownloadJobTest.cs b/NzbDrone.Core.Test/BannerDownloadJobTest.cs new file mode 100644 index 000000000..35a85ede1 --- /dev/null +++ b/NzbDrone.Core.Test/BannerDownloadJobTest.cs @@ -0,0 +1,250 @@ +using System; +using System.Collections.Generic; +using AutoMoq; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.Model; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; +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 BannerDownloadJobTest : TestBase + { + [Test] + public void BannerDownload_all() + { + //Setup + var fakeSeries = Builder.CreateListOfSize(10) + .Build(); + + var mocker = new AutoMoqer(MockBehavior.Strict); + + var notification = new ProgressNotification("Banner Download"); + + mocker.GetMock() + .Setup(c => c.GetAllSeries()) + .Returns(fakeSeries); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), It.IsAny())) + .Returns(true); + + mocker.GetMock() + .Setup(S => S.CreateDirectory(It.IsAny())) + .Returns(""); + + //Act + mocker.Resolve().Start(notification, 0, 0); + + //Assert + mocker.VerifyAllMocks(); + mocker.GetMock().Verify(s => s.DownloadFile(It.IsAny(), It.IsAny()), + Times.Exactly(fakeSeries.Count)); + } + + [Test] + public void BannerDownload_some_null_BannerUrl() + { + //Setup + var fakeSeries = Builder.CreateListOfSize(10) + .WhereRandom(2) + .Have(s => s.BannerUrl = null) + .Build(); + + var mocker = new AutoMoqer(MockBehavior.Strict); + + var notification = new ProgressNotification("Banner Download"); + + mocker.GetMock() + .Setup(c => c.GetAllSeries()) + .Returns(fakeSeries); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), It.IsAny())) + .Returns(true); + + mocker.GetMock() + .Setup(S => S.CreateDirectory(It.IsAny())) + .Returns(""); + + //Act + mocker.Resolve().Start(notification, 0, 0); + + //Assert + mocker.VerifyAllMocks(); + mocker.GetMock().Verify(s => s.DownloadFile(It.IsAny(), It.IsAny()), + Times.Exactly(8)); + } + + [Test] + public void BannerDownload_some_failed_download() + { + //Setup + var fakeSeries = Builder.CreateListOfSize(10) + .Build(); + + const string path = @"C:\Users\mark.mcdowall\Dropbox\Visual Studio 2010\NzbDrone\NzbDrone.Core.Test\bin\Debug\Content\Images\Banners\"; + + var mocker = new AutoMoqer(MockBehavior.Strict); + + var notification = new ProgressNotification("Banner Download"); + + mocker.GetMock() + .Setup(c => c.GetAllSeries()) + .Returns(fakeSeries); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), path + "1.jpg")) + .Returns(false); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), path + "2.jpg")) + .Returns(true); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), path + "3.jpg")) + .Returns(false); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), path + "4.jpg")) + .Returns(true); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), path + "5.jpg")) + .Returns(false); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), path + "6.jpg")) + .Returns(true); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), path + "7.jpg")) + .Returns(false); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), path + "8.jpg")) + .Returns(true); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), path + "9.jpg")) + .Returns(false); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), path + "10.jpg")) + .Returns(true); + + mocker.GetMock() + .Setup(S => S.CreateDirectory(It.IsAny())) + .Returns(""); + + //Act + mocker.Resolve().Start(notification, 0, 0); + + //Assert + mocker.VerifyAllMocks(); + mocker.GetMock().Verify(s => s.DownloadFile(It.IsAny(), It.IsAny()), + Times.Exactly(fakeSeries.Count)); + } + + [Test] + public void BannerDownload_all_failed_download() + { + //Setup + var fakeSeries = Builder.CreateListOfSize(10) + .Build(); + + var mocker = new AutoMoqer(MockBehavior.Strict); + + var notification = new ProgressNotification("Banner Download"); + + mocker.GetMock() + .Setup(c => c.GetAllSeries()) + .Returns(fakeSeries); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), It.IsAny())) + .Returns(false); + + mocker.GetMock() + .Setup(S => S.CreateDirectory(It.IsAny())) + .Returns(""); + + //Act + mocker.Resolve().Start(notification, 0, 0); + + //Assert + mocker.VerifyAllMocks(); + mocker.GetMock().Verify(s => s.DownloadFile(It.IsAny(), It.IsAny()), + Times.Exactly(fakeSeries.Count)); + } + + [Test] + public void BannerDownload_single_banner() + { + //Setup + var fakeSeries = Builder.CreateNew() + .With(s => s.SeriesId = 1) + .Build(); + + var mocker = new AutoMoqer(MockBehavior.Strict); + + var notification = new ProgressNotification("Banner Download"); + + mocker.GetMock() + .Setup(c => c.GetSeries(1)) + .Returns(fakeSeries); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), It.IsAny())) + .Returns(true); + + mocker.GetMock() + .Setup(S => S.CreateDirectory(It.IsAny())) + .Returns(""); + + //Act + mocker.Resolve().Start(notification, 1, 0); + + //Assert + mocker.VerifyAllMocks(); + mocker.GetMock().Verify(s => s.DownloadFile(It.IsAny(), It.IsAny()), + Times.Once()); + } + + [Test] + public void Download_Banner() + { + //Setup + var fakeSeries = Builder.CreateNew() + .With(s => s.SeriesId = 1) + .Build(); + + var mocker = new AutoMoqer(MockBehavior.Strict); + + var notification = new ProgressNotification("Banner Download"); + + mocker.GetMock() + .Setup(s => s.DownloadFile(It.IsAny(), It.IsAny())) + .Returns(true); + + //Act + mocker.Resolve().DownloadBanner(notification, fakeSeries); + + //Assert + mocker.VerifyAllMocks(); + mocker.GetMock().Verify(s => s.DownloadFile(It.IsAny(), It.IsAny()), + Times.Once()); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/ImportNewSeriesJobTest.cs b/NzbDrone.Core.Test/ImportNewSeriesJobTest.cs index c1432c486..b4fc05662 100644 --- a/NzbDrone.Core.Test/ImportNewSeriesJobTest.cs +++ b/NzbDrone.Core.Test/ImportNewSeriesJobTest.cs @@ -44,6 +44,8 @@ namespace NzbDrone.Core.Test .Setup(j => j.Start(notification, series[1].SeriesId, 0)) .Callback(() => series[1].LastDiskSync = DateTime.Now); + mocker.GetMock() + .Setup(j => j.Start(notification, It.IsAny(), 0)); mocker.GetMock() .Setup(j => j.Start(notification, series[0].SeriesId, 0)) @@ -109,6 +111,8 @@ namespace NzbDrone.Core.Test .Setup(j => j.Start(notification, series[0].SeriesId, 0)) .Callback(() => series[0].LastDiskSync = DateTime.Now); + mocker.GetMock() + .Setup(j => j.Start(notification, series[0].SeriesId, 0)); mocker.GetMock() .Setup(s => s.GetSeries(series[0].SeriesId)).Returns(series[0]); diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index b82f2f874..ea245f194 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -89,6 +89,7 @@ + diff --git a/NzbDrone.Core/CentralDispatch.cs b/NzbDrone.Core/CentralDispatch.cs index 4ae1ec392..4a0860b39 100644 --- a/NzbDrone.Core/CentralDispatch.cs +++ b/NzbDrone.Core/CentralDispatch.cs @@ -109,6 +109,7 @@ namespace NzbDrone.Core _kernel.Bind().To().InSingletonScope(); _kernel.Bind().To().InSingletonScope(); _kernel.Bind().To().InSingletonScope(); + _kernel.Bind().To().InSingletonScope(); _kernel.Get().Initialize(); _kernel.Get().StartTimer(30); diff --git a/NzbDrone.Core/Datastore/Migrations/Migration20110909.cs b/NzbDrone.Core/Datastore/Migrations/Migration20110909.cs new file mode 100644 index 000000000..9e7f7de76 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migrations/Migration20110909.cs @@ -0,0 +1,22 @@ +using System; +using System.Data; +using Migrator.Framework; + +namespace NzbDrone.Core.Datastore.Migrations +{ + + [Migration(20110909)] + public class Migration20110909 : Migration + { + public override void Up() + { + Database.AddColumn("Series", "Runtime", DbType.Int32, ColumnProperty.Null); + Database.AddColumn("Series", "BannerUrl", DbType.String, ColumnProperty.Null); + } + + public override void Down() + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index c66fbfbfb..51de3be66 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -176,6 +176,7 @@ + @@ -204,6 +205,7 @@ + diff --git a/NzbDrone.Core/Providers/Jobs/BannerDownloadJob.cs b/NzbDrone.Core/Providers/Jobs/BannerDownloadJob.cs new file mode 100644 index 000000000..9d8596dd6 --- /dev/null +++ b/NzbDrone.Core/Providers/Jobs/BannerDownloadJob.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Ninject; +using NLog; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Repository; + +namespace NzbDrone.Core.Providers.Jobs +{ + public class BannerDownloadJob : IJob + { + private readonly SeriesProvider _seriesProvider; + private readonly HttpProvider _httpProvider; + private readonly DiskProvider _diskProvider; + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + private string _bannerPath = ""; + private const string _bannerUrlPrefix = "http://www.thetvdb.com/banners/"; + + [Inject] + public BannerDownloadJob(SeriesProvider seriesProvider, HttpProvider httpProvider, DiskProvider diskProvider) + { + _seriesProvider = seriesProvider; + _httpProvider = httpProvider; + _diskProvider = diskProvider; + } + + public BannerDownloadJob() + { + } + + public string Name + { + get { return "Banner Download"; } + } + + public int DefaultInterval + { + //30 days + get { return 43200; } + } + + public virtual void Start(ProgressNotification notification, int targetId, int secondaryTargetId) + { + Logger.Debug("Starting banner download job"); + + _bannerPath = Path.Combine(CentralDispatch.AppPath, "Content", "Images", "Banners"); + _diskProvider.CreateDirectory(_bannerPath); + + if (targetId > 0) + { + var series = _seriesProvider.GetSeries(targetId); + + if (series != null && !String.IsNullOrEmpty(series.BannerUrl)) + DownloadBanner(notification, series); + + return; + } + + var seriesInDb = _seriesProvider.GetAllSeries(); + + foreach (var series in seriesInDb.Where(s => !String.IsNullOrEmpty(s.BannerUrl))) + { + DownloadBanner(notification, series); + } + + Logger.Debug("Finished banner download job"); + } + + public virtual void DownloadBanner(ProgressNotification notification, Series series) + { + var bannerFilename = String.Format("{0}{1}{2}.jpg", _bannerPath, Path.DirectorySeparatorChar, series.SeriesId); + + notification.CurrentMessage = string.Format("Downloading banner for '{0}'", series.Title); + + if (_httpProvider.DownloadFile(_bannerUrlPrefix + series.BannerUrl, bannerFilename)) + notification.CurrentMessage = string.Format("Successfully download banner for '{0}'", series.Title); + + else + { + Logger.Debug("Failed to download banner for '{0}'", series.Title); + notification.CurrentMessage = string.Format("Failed to download banner for '{0}'", series.Title); + } + } + } +} diff --git a/NzbDrone.Core/Providers/Jobs/ImportNewSeriesJob.cs b/NzbDrone.Core/Providers/Jobs/ImportNewSeriesJob.cs index e9a0f333a..a0f554ad2 100644 --- a/NzbDrone.Core/Providers/Jobs/ImportNewSeriesJob.cs +++ b/NzbDrone.Core/Providers/Jobs/ImportNewSeriesJob.cs @@ -20,6 +20,7 @@ namespace NzbDrone.Core.Providers.Jobs private readonly MediaFileProvider _mediaFileProvider; private readonly UpdateInfoJob _updateInfoJob; private readonly DiskScanJob _diskScanJob; + private readonly BannerDownloadJob _bannerDownloadJob; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); @@ -27,13 +28,15 @@ namespace NzbDrone.Core.Providers.Jobs [Inject] public ImportNewSeriesJob(SeriesProvider seriesProvider, EpisodeProvider episodeProvider, - MediaFileProvider mediaFileProvider, UpdateInfoJob updateInfoJob, DiskScanJob diskScanJob) + MediaFileProvider mediaFileProvider, UpdateInfoJob updateInfoJob, + DiskScanJob diskScanJob, BannerDownloadJob bannerDownloadJob) { _seriesProvider = seriesProvider; _episodeProvider = episodeProvider; _mediaFileProvider = mediaFileProvider; _updateInfoJob = updateInfoJob; _diskScanJob = diskScanJob; + _bannerDownloadJob = bannerDownloadJob; } public string Name @@ -73,9 +76,12 @@ namespace NzbDrone.Core.Providers.Jobs var updatedSeries = _seriesProvider.GetSeries(currentSeries.SeriesId); AutoIgnoreSeasons(updatedSeries.SeriesId); - notification.CurrentMessage = String.Format("{0} was successfully imported", updatedSeries.Title); + //Download the banner for the new series + _bannerDownloadJob.Start(notification, updatedSeries.SeriesId, 0); + notification.CurrentMessage = String.Format("{0} was successfully imported", updatedSeries.Title); } + catch (Exception e) { Logger.ErrorException(e.Message, e); diff --git a/NzbDrone.Core/Providers/SeriesProvider.cs b/NzbDrone.Core/Providers/SeriesProvider.cs index 038e1c5ab..4141b2b8d 100644 --- a/NzbDrone.Core/Providers/SeriesProvider.cs +++ b/NzbDrone.Core/Providers/SeriesProvider.cs @@ -94,6 +94,8 @@ namespace NzbDrone.Core.Providers series.Language = tvDbSeries.Language != null ? tvDbSeries.Language.Abbriviation : string.Empty; series.CleanTitle = Parser.NormalizeTitle(tvDbSeries.SeriesName); series.LastInfoSync = DateTime.Now; + series.Runtime = (int)tvDbSeries.Runtime; + series.BannerUrl = tvDbSeries.BannerPath; UpdateSeries(series); return series; diff --git a/NzbDrone.Core/Repository/Series.cs b/NzbDrone.Core/Repository/Series.cs index b8b3b4210..63ef62179 100644 --- a/NzbDrone.Core/Repository/Series.cs +++ b/NzbDrone.Core/Repository/Series.cs @@ -37,6 +37,10 @@ namespace NzbDrone.Core.Repository public DateTime? LastDiskSync { get; set; } + public int Runtime { get; set; } + + public string BannerUrl { get; set; } + /// /// Gets or sets a value indicating whether this is hidden. ///