diff --git a/NzbDrone.App.Test/NzbDrone.App.Test.ncrunchproject b/NzbDrone.App.Test/NzbDrone.App.Test.ncrunchproject new file mode 100644 index 000000000..8641d3614 --- /dev/null +++ b/NzbDrone.App.Test/NzbDrone.App.Test.ncrunchproject @@ -0,0 +1,19 @@ + + false + false + false + true + false + false + false + false + true + true + false + true + true + 60000 + + + AutoDetect + \ No newline at end of file diff --git a/NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject b/NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject new file mode 100644 index 000000000..8641d3614 --- /dev/null +++ b/NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject @@ -0,0 +1,19 @@ + + false + false + false + true + false + false + false + false + true + true + false + true + true + 60000 + + + AutoDetect + \ No newline at end of file diff --git a/NzbDrone.Common/DiskProvider.cs b/NzbDrone.Common/DiskProvider.cs index 4322f3d0d..66b7414bf 100644 --- a/NzbDrone.Common/DiskProvider.cs +++ b/NzbDrone.Common/DiskProvider.cs @@ -200,6 +200,10 @@ namespace NzbDrone.Common return File.ReadAllText(filePath); } + public virtual void WriteAllText(string filename, string contents) + { + File.WriteAllText(filename, contents); + } public static bool PathEquals(string firstPath, string secondPath) { diff --git a/NzbDrone.Common/NzbDrone.Common.ncrunchproject b/NzbDrone.Common/NzbDrone.Common.ncrunchproject new file mode 100644 index 000000000..8641d3614 --- /dev/null +++ b/NzbDrone.Common/NzbDrone.Common.ncrunchproject @@ -0,0 +1,19 @@ + + false + false + false + true + false + false + false + false + true + true + false + true + true + 60000 + + + AutoDetect + \ No newline at end of file diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index c4938453d..cfaef9e0f 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -114,6 +114,8 @@ + + diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.ncrunchproject b/NzbDrone.Core.Test/NzbDrone.Core.Test.ncrunchproject new file mode 100644 index 000000000..8641d3614 --- /dev/null +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.ncrunchproject @@ -0,0 +1,19 @@ + + false + false + false + true + false + false + false + false + true + true + false + true + true + 60000 + + + AutoDetect + \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest.cs index b4af0456e..596da2640 100644 --- a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest.cs @@ -212,7 +212,7 @@ namespace NzbDrone.Core.Test.ProviderTests var result = Mocker.Resolve().MoveEpisodeFile(file, false); //Assert - result.Should().BeFalse(); + result.Should().BeNull(); } [Test] @@ -368,7 +368,7 @@ namespace NzbDrone.Core.Test.ProviderTests var result = Mocker.Resolve().MoveEpisodeFile(file, true); //Assert - result.Should().BeTrue(); + result.Should().NotBeNull(); Mocker.GetMock() .Verify(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny()), Times.Once()); } diff --git a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs index 7798d6695..28229bb67 100644 --- a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs @@ -172,7 +172,7 @@ namespace NzbDrone.Core.Test.ProviderTests Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); //Act @@ -207,7 +207,7 @@ namespace NzbDrone.Core.Test.ProviderTests Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -248,7 +248,7 @@ namespace NzbDrone.Core.Test.ProviderTests Db.Insert(fakeEpisode); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeTvDbEpisodes); //Act @@ -285,7 +285,7 @@ namespace NzbDrone.Core.Test.ProviderTests Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -324,7 +324,7 @@ namespace NzbDrone.Core.Test.ProviderTests Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -362,7 +362,7 @@ namespace NzbDrone.Core.Test.ProviderTests Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -395,7 +395,7 @@ namespace NzbDrone.Core.Test.ProviderTests Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -428,7 +428,7 @@ namespace NzbDrone.Core.Test.ProviderTests Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -463,7 +463,7 @@ namespace NzbDrone.Core.Test.ProviderTests Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); Mocker.GetMock() @@ -493,7 +493,7 @@ namespace NzbDrone.Core.Test.ProviderTests var currentEpisodes = new List(); Mocker.GetMock(MockBehavior.Strict) - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); Mocker.GetMock() @@ -528,7 +528,7 @@ namespace NzbDrone.Core.Test.ProviderTests } Mocker.GetMock(MockBehavior.Strict) - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); Mocker.GetMock() @@ -565,7 +565,7 @@ namespace NzbDrone.Core.Test.ProviderTests .Returns(fakeEpisodeList); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeTvDbResult); //Act @@ -602,7 +602,7 @@ namespace NzbDrone.Core.Test.ProviderTests var fakeSeries = Builder.CreateNew().With(c => c.SeriesId = seriesId).Build(); Mocker.GetMock(MockBehavior.Strict) - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); Mocker.GetMock() @@ -634,7 +634,7 @@ namespace NzbDrone.Core.Test.ProviderTests } Mocker.GetMock(MockBehavior.Strict) - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); var updatedEpisodes = new List(); @@ -695,7 +695,7 @@ namespace NzbDrone.Core.Test.ProviderTests Db.Insert(fakeEpisode); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); Mocker.GetMock() @@ -1407,7 +1407,7 @@ namespace NzbDrone.Core.Test.ProviderTests Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); //Act diff --git a/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForEpisodeFile_Fixture.cs b/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForEpisodeFile_Fixture.cs new file mode 100644 index 000000000..c3499c2d3 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForEpisodeFile_Fixture.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Providers.Metadata; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Repository.Quality; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common.AutoMoq; +using NzbDrone.Test.Common; +using TvdbLib.Data; +using TvdbLib.Data.Banner; + +namespace NzbDrone.Core.Test.ProviderTests.Metadata +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class Xbmc_ForEpisoddeFile_Fixture : CoreTest + { + private Series series; + private EpisodeFile episodeFile; + private TvdbSeries tvdbSeries; + + [SetUp] + public void Setup() + { + WithTempAsAppPath(); + + series = Builder + .CreateNew() + .With(s => s.SeriesId == 79488) + .With(s => s.Title == "30 Rock") + .Build(); + + episodeFile = Builder.CreateNew() + .With(f => f.SeriesId = 79488) + .With(f => f.SeasonNumber = 1) + .With(f => f.Path = @"C:\Test\30 Rock\Season 01\30 Rock - S01E01 - Pilot.avi") + .Build(); + + var tvdbEpisodes = Builder.CreateListOfSize(2) + .All() + .With(e => e.SeriesId = 79488) + .With(e => e.SeasonNumber = 1) + .With(e => e.Directors = new List{ "Fake Director" }) + .With(e => e.Writer = new List{ "Fake Writer" }) + .With(e => e.GuestStars = new List { "Guest Star 1", "Guest Star 2", "Guest Star 3", "" }) + .Build(); + + var seasonBanners = Builder + .CreateListOfSize(4) + .TheFirst(2) + .With(b => b.Season = 1) + .TheLast(2) + .With(b => b.Season = 2) + .TheFirst(1) + .With(b => b.BannerType = TvdbSeasonBanner.Type.season) + .With(b => b.BannerPath = "seasons/79488-1-1.jpg") + .TheNext(2) + .With(b => b.BannerType = TvdbSeasonBanner.Type.seasonwide) + .With(b => b.BannerPath = "banners/seasons/79488-test.jpg") + .TheLast(1) + .With(b => b.BannerType = TvdbSeasonBanner.Type.season) + .With(b => b.BannerPath = "seasons/79488-2-1.jpg") + .Build(); + + var seriesActors = Builder + .CreateListOfSize(5) + .Build(); + + tvdbSeries = Builder + .CreateNew() + .With(s => s.Id = 79488) + .With(s => s.SeriesName = "30 Rock") + .With(s => s.TvdbActors = seriesActors.ToList()) + .With(s => s.Episodes = tvdbEpisodes.ToList()) + .Build(); + + tvdbSeries.Banners.AddRange(seasonBanners); + } + + private void WithUseBanners() + { + Mocker.GetMock().SetupGet(s => s.MetadataUseBanners).Returns(true); + } + + private void WithSingleEpisodeFile() + { + var episode = Builder.CreateNew() + .With(e => e.SeasonNumber = 1) + .With(e => e.SeriesId = 79488) + .With(e => e.EpisodeNumber = 1) + .Build(); + + Mocker.GetMock() + .Setup(s => s.GetEpisodesByFileId(episodeFile.EpisodeFileId)) + .Returns(new List { episode }); + } + + private void WithMultiEpisodeFile() + { + var episodes = Builder.CreateListOfSize(2) + .All() + .With(e => e.SeriesId = 79488) + .With(e => e.SeasonNumber = 1) + .Build(); + + Mocker.GetMock() + .Setup(s => s.GetEpisodesByFileId(episodeFile.EpisodeFileId)) + .Returns(episodes.ToList()); + } + + [Test] + public void should_not_blowup() + { + WithSingleEpisodeFile(); + Mocker.Resolve().CreateForEpisodeFile(episodeFile, tvdbSeries); + } + + [Test] + public void should_call_diskprovider_writeAllText_once_for_single_episode() + { + WithSingleEpisodeFile(); + Mocker.Resolve().CreateForEpisodeFile(episodeFile, tvdbSeries); + Mocker.GetMock().Verify(v => v.WriteAllText(episodeFile.Path.Replace("avi", "nfo"), It.IsAny()), Times.Once()); + } + + [Test] + public void should_call_diskprovider_writeAllText_once_for_multi_episode() + { + WithMultiEpisodeFile(); + Mocker.Resolve().CreateForEpisodeFile(episodeFile, tvdbSeries); + Mocker.GetMock().Verify(v => v.WriteAllText(episodeFile.Path.Replace("avi", "nfo"), It.IsAny()), Times.Once()); + } + + [Test] + public void should_download_thumbnail_when_thumbnail_path_is_not_null() + { + WithSingleEpisodeFile(); + Mocker.Resolve().CreateForEpisodeFile(episodeFile, tvdbSeries); + Mocker.GetMock().Verify(v => v.Download(tvdbSeries.Episodes.First().BannerPath, episodeFile.Path.Replace("avi", "tbn")), Times.Once()); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForSeries_Fixture.cs b/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForSeries_Fixture.cs new file mode 100644 index 000000000..30ac38dd6 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForSeries_Fixture.cs @@ -0,0 +1,165 @@ +using System; +using System.IO; +using System.Linq; +using System.Net; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Providers.Metadata; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Repository.Quality; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common.AutoMoq; +using NzbDrone.Test.Common; +using TvdbLib.Data; +using TvdbLib.Data.Banner; + +namespace NzbDrone.Core.Test.ProviderTests.Metadata +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class Xbmc_ForSeries_Fixture : CoreTest + { + private Series series; + private TvdbSeries tvdbSeries; + + [SetUp] + public void Setup() + { + WithTempAsAppPath(); + + series = Builder + .CreateNew() + .With(s => s.SeriesId == 79488) + .With(s => s.Title == "30 Rock") + .Build(); + + var seasonBanners = Builder + .CreateListOfSize(4) + .TheFirst(2) + .With(b => b.Season = 1) + .TheLast(2) + .With(b => b.Season = 2) + .TheFirst(1) + .With(b => b.BannerType = TvdbSeasonBanner.Type.season) + .With(b => b.BannerPath = "seasons/79488-1-1.jpg") + .TheNext(2) + .With(b => b.BannerType = TvdbSeasonBanner.Type.seasonwide) + .With(b => b.BannerPath = "banners/seasons/79488-test.jpg") + .TheLast(1) + .With(b => b.BannerType = TvdbSeasonBanner.Type.season) + .With(b => b.BannerPath = "seasons/79488-2-1.jpg") + .Build(); + + var seriesActors = Builder + .CreateListOfSize(5) + .Build(); + + tvdbSeries = Builder + .CreateNew() + .With(s => s.Id = 79488) + .With(s => s.SeriesName = "30 Rock") + .With(s => s.TvdbActors = seriesActors.ToList()) + .Build(); + + tvdbSeries.Banners.AddRange(seasonBanners); + } + + private void WithUseBanners() + { + Mocker.GetMock().SetupGet(s => s.MetadataUseBanners).Returns(true); + } + + private void WithSpecials() + { + var seasonBanners = Builder + .CreateListOfSize(2) + .All() + .With(b => b.Season = 0) + .TheFirst(1) + .With(b => b.BannerType = TvdbSeasonBanner.Type.season) + .With(b => b.BannerPath = "seasons/79488-0-1.jpg") + .TheLast(1) + .With(b => b.BannerType = TvdbSeasonBanner.Type.seasonwide) + .With(b => b.BannerPath = "banners/seasons/79488-0-1.jpg") + .Build(); + + var seriesActors = Builder + .CreateListOfSize(5) + .Build(); + + tvdbSeries = Builder + .CreateNew() + .With(s => s.Id = 79488) + .With(s => s.SeriesName = "30 Rock") + .With(s => s.TvdbActors = seriesActors.ToList()) + .Build(); + + tvdbSeries.Banners.AddRange(seasonBanners); + } + + [Test] + public void should_not_blowup() + { + Mocker.Resolve().CreateForSeries(series, tvdbSeries); + } + + [Test] + public void should_call_diskprovider_writeAllText() + { + Mocker.Resolve().CreateForSeries(series, tvdbSeries); + Mocker.GetMock().Verify(v => v.WriteAllText(Path.Combine(series.Path, "tvshow.nfo"), It.IsAny()), Times.Once()); + } + + [Test] + public void should_download_fanart() + { + Mocker.Resolve().CreateForSeries(series, tvdbSeries); + Mocker.GetMock().Verify(v => v.Download(tvdbSeries.FanartPath, Path.Combine(series.Path, "fanart.jpg")), Times.Once()); + } + + [Test] + public void should_download_poster_when_useBanners_is_false() + { + Mocker.Resolve().CreateForSeries(series, tvdbSeries); + Mocker.GetMock().Verify(v => v.Download(tvdbSeries.PosterPath, Path.Combine(series.Path, "folder.jpg")), Times.Once()); + } + + [Test] + public void should_download_banner_when_useBanners_is_true() + { + WithUseBanners(); + Mocker.Resolve().CreateForSeries(series, tvdbSeries); + Mocker.GetMock().Verify(v => v.Download(tvdbSeries.BannerPath, Path.Combine(series.Path, "folder.jpg")), Times.Once()); + } + + [Test] + public void should_download_season_poster_when_useBanners_is_false() + { + Mocker.Resolve().CreateForSeries(series, tvdbSeries); + Mocker.GetMock().Verify(v => v.Download(It.Is(s => !s.Contains("banners")), It.IsRegex(@"season\d{2}\.tbn")), Times.Exactly(2)); + } + + [Test] + public void should_download_season_banner_when_useBanners_is_true() + { + WithUseBanners(); + Mocker.Resolve().CreateForSeries(series, tvdbSeries); + Mocker.GetMock().Verify(v => v.Download(It.Is(s => s.Contains("banners")), It.IsRegex(@"season\d{2}\.tbn")), Times.Exactly(2)); + } + + [Test] + public void should_download_special_thumb_with_proper_name() + { + WithUseBanners(); + WithSpecials(); + Mocker.Resolve().CreateForSeries(series, tvdbSeries); + Mocker.GetMock().Verify(v => v.Download(It.Is(s => s.Contains("banners")), It.IsRegex(@"season-specials.tbn")), Times.Exactly(1)); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadProviderFixture.cs b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadProviderFixture.cs index 9ce2a76dd..68b051838 100644 --- a/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadProviderFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadProviderFixture.cs @@ -210,7 +210,7 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests Mocker.GetMock().Setup(s => s.MoveDirectory(droppedFolder.FullName, taggedFolder)); Mocker.GetMock().Setup(s => s.GetDirectorySize(droppedFolder.FullName)).Returns(Constants.IgnoreFileSize + 10.Megabytes()); Mocker.GetMock().Setup(s => s.Scan(fakeSeries, droppedFolder.FullName)).Returns(fakeEpisodeFiles); - Mocker.GetMock().Setup(s => s.MoveEpisodeFile(It.IsAny(), true)).Returns(true); + Mocker.GetMock().Setup(s => s.MoveEpisodeFile(It.IsAny(), true)).Returns(new EpisodeFile()); Mocker.Resolve().ProcessDownload(droppedFolder); @@ -281,9 +281,10 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests Mocker.GetMock().Setup(s => s.FindSeries("office")).Returns(fakeSeries); Mocker.GetMock().Setup(s => s.CleanUpDropFolder(droppedFolder.FullName)); Mocker.GetMock().Setup(s => s.Scan(fakeSeries, droppedFolder.FullName)).Returns(fakeEpisodeFiles); - Mocker.GetMock().Setup(s => s.MoveEpisodeFile(It.IsAny(), true)).Returns(true); + Mocker.GetMock().Setup(s => s.MoveEpisodeFile(It.IsAny(), true)).Returns(new EpisodeFile()); Mocker.GetMock().Setup(s => s.GetDirectorySize(droppedFolder.FullName)).Returns(Constants.IgnoreFileSize - 1.Megabytes()); Mocker.GetMock().Setup(s => s.DeleteFolder(droppedFolder.FullName, true)); + Mocker.GetMock().Setup(s => s.CreateForEpisodeFiles(It.IsAny>())); //Act Mocker.Resolve().ProcessDownload(droppedFolder); diff --git a/NzbDrone.Core/CentralDispatch.cs b/NzbDrone.Core/CentralDispatch.cs index 845396139..3d8f2f9e5 100644 --- a/NzbDrone.Core/CentralDispatch.cs +++ b/NzbDrone.Core/CentralDispatch.cs @@ -13,6 +13,7 @@ using NzbDrone.Core.Providers; using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.ExternalNotification; using NzbDrone.Core.Providers.Indexer; +using NzbDrone.Core.Providers.Metadata; using NzbDrone.Core.Repository; using PetaPoco; using SignalR; @@ -20,6 +21,7 @@ using SignalR.Hosting.AspNet; using SignalR.Infrastructure; using SignalR.Ninject; using Connection = NzbDrone.Core.Datastore.Connection; +using Xbmc = NzbDrone.Core.Providers.ExternalNotification.Xbmc; namespace NzbDrone.Core { @@ -45,8 +47,9 @@ namespace NzbDrone.Core InitQuality(); InitExternalNotifications(); + InitMetadataProviders(); InitIndexers(); - InitJobs(); + InitJobs(); } private void InitDatabase() @@ -161,6 +164,16 @@ namespace NzbDrone.Core Kernel.Get().InitializeNotifiers(notifiers.ToList()); } + private void InitMetadataProviders() + { + logger.Debug("Initializing Metadata Providers..."); + + Kernel.Bind().To().InSingletonScope(); + + var providers = Kernel.GetAll(); + Kernel.Get().Initialize(providers.ToList()); + } + public void DedicateToHost() { try diff --git a/NzbDrone.Core/Datastore/Migrations/Migration20120707.cs b/NzbDrone.Core/Datastore/Migrations/Migration20120707.cs new file mode 100644 index 000000000..e93c4446d --- /dev/null +++ b/NzbDrone.Core/Datastore/Migrations/Migration20120707.cs @@ -0,0 +1,21 @@ +using System.Data; +using Migrator.Framework; + +namespace NzbDrone.Core.Datastore.Migrations +{ + + [Migration(20120707)] + public class Migration20120707 : NzbDroneMigration + { + protected override void MainDbUpgrade() + { + Database.AddTable("MetadataDefinitions", new[] + { + new Column("Id", DbType.Int32, ColumnProperty.PrimaryKeyWithIdentity), + new Column("Enable", DbType.Boolean, ColumnProperty.NotNull), + new Column("MetadataProviderType", DbType.String, ColumnProperty.NotNull), + new Column("Name", DbType.String, ColumnProperty.NotNull) + }); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/RenameSeasonJob.cs b/NzbDrone.Core/Jobs/RenameSeasonJob.cs index 39fc70ebb..e379d9ab6 100644 --- a/NzbDrone.Core/Jobs/RenameSeasonJob.cs +++ b/NzbDrone.Core/Jobs/RenameSeasonJob.cs @@ -1,9 +1,11 @@ +using System.Collections.Generic; using System.Linq; using System; using NLog; using Ninject; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers; +using NzbDrone.Core.Repository; namespace NzbDrone.Core.Jobs { @@ -13,17 +15,20 @@ namespace NzbDrone.Core.Jobs private readonly DiskScanProvider _diskScanProvider; private readonly ExternalNotificationProvider _externalNotificationProvider; private readonly SeriesProvider _seriesProvider; + private readonly MetadataProvider _metadataProvider; private static readonly Logger logger = LogManager.GetCurrentClassLogger(); [Inject] public RenameSeasonJob(MediaFileProvider mediaFileProvider, DiskScanProvider diskScanProvider, - ExternalNotificationProvider externalNotificationProvider, SeriesProvider seriesProvider) + ExternalNotificationProvider externalNotificationProvider, SeriesProvider seriesProvider, + MetadataProvider metadataProvider) { _mediaFileProvider = mediaFileProvider; _diskScanProvider = diskScanProvider; _externalNotificationProvider = externalNotificationProvider; _seriesProvider = seriesProvider; + _metadataProvider = metadataProvider; } public string Name @@ -57,18 +62,32 @@ namespace NzbDrone.Core.Jobs return; } + var newEpisodeFiles = new List(); + var oldEpisodeFiles = new List(); + foreach (var episodeFile in episodeFiles) { try { - _diskScanProvider.MoveEpisodeFile(episodeFile); + var newFile = _diskScanProvider.MoveEpisodeFile(episodeFile); + + if (newFile != null) + { + newEpisodeFiles.Add(newFile); + oldEpisodeFiles.Add(episodeFile); + } } - catch (Exception exception) + + catch (Exception e) { - logger.WarnException("An error has occurred while renaming file", exception); + logger.WarnException("An error has occurred while renaming file", e); } } + //Remove & Create Metadata for episode files + _metadataProvider.RemoveForEpisodeFiles(oldEpisodeFiles); + _metadataProvider.CreateForEpisodeFiles(newEpisodeFiles); + //Start AfterRename var message = String.Format("Renamed: Series {0}, Season: {1}", series.Title, secondaryTargetId); diff --git a/NzbDrone.Core/Jobs/RenameSeriesJob.cs b/NzbDrone.Core/Jobs/RenameSeriesJob.cs index e11f744ff..f44f85786 100644 --- a/NzbDrone.Core/Jobs/RenameSeriesJob.cs +++ b/NzbDrone.Core/Jobs/RenameSeriesJob.cs @@ -1,9 +1,11 @@ +using System.Collections.Generic; using System.Linq; using System; using NLog; using Ninject; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers; +using NzbDrone.Core.Repository; namespace NzbDrone.Core.Jobs { @@ -13,17 +15,20 @@ namespace NzbDrone.Core.Jobs private readonly DiskScanProvider _diskScanProvider; private readonly ExternalNotificationProvider _externalNotificationProvider; private readonly SeriesProvider _seriesProvider; + private readonly MetadataProvider _metadataProvider; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); [Inject] public RenameSeriesJob(MediaFileProvider mediaFileProvider, DiskScanProvider diskScanProvider, - ExternalNotificationProvider externalNotificationProvider, SeriesProvider seriesProvider) + ExternalNotificationProvider externalNotificationProvider, SeriesProvider seriesProvider, + MetadataProvider metadataProvider) { _mediaFileProvider = mediaFileProvider; _diskScanProvider = diskScanProvider; _externalNotificationProvider = externalNotificationProvider; _seriesProvider = seriesProvider; + _metadataProvider = metadataProvider; } public string Name @@ -54,19 +59,32 @@ namespace NzbDrone.Core.Jobs return; } + var newEpisodeFiles = new List(); + var oldEpisodeFiles = new List(); + foreach (var episodeFile in episodeFiles) { try { - _diskScanProvider.MoveEpisodeFile(episodeFile); + var newFile = _diskScanProvider.MoveEpisodeFile(episodeFile); + + if (newFile != null) + { + newEpisodeFiles.Add(newFile); + oldEpisodeFiles.Add(episodeFile); + } } + catch(Exception e) { Logger.WarnException("An error has occurred while renaming file", e); - } - + } } + //Remove & Create Metadata for episode files + _metadataProvider.RemoveForEpisodeFiles(oldEpisodeFiles); + _metadataProvider.CreateForEpisodeFiles(newEpisodeFiles); + //Start AfterRename var message = String.Format("Renamed: Series {0}", series.Title); diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index bf58918d0..61643737a 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -239,6 +239,7 @@ + @@ -285,6 +286,9 @@ + + + @@ -314,6 +318,7 @@ Code + diff --git a/NzbDrone.Core/NzbDrone.Core.ncrunchproject b/NzbDrone.Core/NzbDrone.Core.ncrunchproject new file mode 100644 index 000000000..8641d3614 --- /dev/null +++ b/NzbDrone.Core/NzbDrone.Core.ncrunchproject @@ -0,0 +1,19 @@ + + false + false + false + true + false + false + false + false + true + true + false + true + true + 60000 + + + AutoDetect + \ No newline at end of file diff --git a/NzbDrone.Core/Providers/BannerProvider.cs b/NzbDrone.Core/Providers/BannerProvider.cs index 2176e9d7c..16f091a43 100644 --- a/NzbDrone.Core/Providers/BannerProvider.cs +++ b/NzbDrone.Core/Providers/BannerProvider.cs @@ -81,5 +81,22 @@ namespace NzbDrone.Core.Providers } return true; } + + public virtual void Download(string remotePath, string filename) + { + var url = BANNER_URL_PREFIX + remotePath; + + try + { + _httpProvider.DownloadFile(url, filename); + logger.Trace("Successfully download banner from '{0}' to '{1}'", url, filename); + } + catch (Exception ex) + { + var message = String.Format("Failed to download Banner from '{0}' to '{1}'", url, filename); + logger.DebugException(message, ex); + throw; + } + } } } diff --git a/NzbDrone.Core/Providers/Core/ConfigProvider.cs b/NzbDrone.Core/Providers/Core/ConfigProvider.cs index 2cdc6be0e..c2637dc3c 100644 --- a/NzbDrone.Core/Providers/Core/ConfigProvider.cs +++ b/NzbDrone.Core/Providers/Core/ConfigProvider.cs @@ -501,6 +501,13 @@ namespace NzbDrone.Core.Providers.Core set { SetValue("PlexPassword", value); } } + public virtual Boolean MetadataUseBanners + { + get { return GetValueBoolean("MetadataUseBanners"); } + + set { SetValue("MetadataUseBanners", value); } + } + private string GetValue(string key) { return GetValue(key, String.Empty); diff --git a/NzbDrone.Core/Providers/DiskScanProvider.cs b/NzbDrone.Core/Providers/DiskScanProvider.cs index d7dec59c3..27d9b32c7 100644 --- a/NzbDrone.Core/Providers/DiskScanProvider.cs +++ b/NzbDrone.Core/Providers/DiskScanProvider.cs @@ -168,7 +168,7 @@ namespace NzbDrone.Core.Providers return episodeFile; } - public virtual bool MoveEpisodeFile(EpisodeFile episodeFile, bool newDownload = false) + public virtual EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, bool newDownload = false) { if (episodeFile == null) throw new ArgumentNullException("episodeFile"); @@ -182,14 +182,14 @@ namespace NzbDrone.Core.Providers if (DiskProvider.PathEquals(episodeFile.Path, newFile.FullName)) { Logger.Debug("Skipping file rename, source and destination are the same: {0}", episodeFile.Path); - return false; + return null; } _diskProvider.CreateDirectory(newFile.DirectoryName); Logger.Debug("Moving [{0}] > [{1}]", episodeFile.Path, newFile.FullName); _diskProvider.MoveFile(episodeFile.Path, newFile.FullName); - + _diskProvider.InheritFolderPermissions(newFile.FullName); episodeFile.Path = newFile.FullName; @@ -213,7 +213,7 @@ namespace NzbDrone.Core.Providers _externalNotificationProvider.OnRename(message, series); } - return true; + return episodeFile; } /// diff --git a/NzbDrone.Core/Providers/Metadata/MetadataBase.cs b/NzbDrone.Core/Providers/Metadata/MetadataBase.cs new file mode 100644 index 000000000..740785acc --- /dev/null +++ b/NzbDrone.Core/Providers/Metadata/MetadataBase.cs @@ -0,0 +1,65 @@ +using System; +using NLog; +using NzbDrone.Common; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Repository; +using TvdbLib.Data; + +namespace NzbDrone.Core.Providers.Metadata +{ + public abstract class MetadataBase + { + protected readonly Logger _logger; + protected readonly ConfigProvider _configProvider; + protected readonly DiskProvider _diskProvider; + protected readonly BannerProvider _bannerProvider; + protected readonly EpisodeProvider _episodeProvider; + + protected MetadataBase(ConfigProvider configProvider, DiskProvider diskProvider, + BannerProvider bannerProvider, EpisodeProvider episodeProvider) + { + _configProvider = configProvider; + _diskProvider = diskProvider; + _bannerProvider = bannerProvider; + _episodeProvider = episodeProvider; + _logger = LogManager.GetLogger(GetType().ToString()); + } + + /// + /// Gets the name for the metabase provider + /// + public abstract string Name { get; } + + /// + /// Creates metadata for a series + /// + /// The series to create the metadata for + /// Series information from TheTvDb + public abstract void CreateForSeries(Series series, TvdbSeries tvDbSeries); + + /// + /// Creates metadata for the episode file + /// + /// The episode file to create the metadata + /// Series information from TheTvDb + public abstract void CreateForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tvDbSeries); + + /// + /// Removes metadata for a series + /// + /// The series to create the metadata for + public abstract void RemoveForSeries(Series series); + + /// + /// Removes metadata for the episode file + /// + /// The episode file to create the metadata + public abstract void RemoveForEpisodeFile(EpisodeFile episodeFile); + + public virtual string GetEpisodeGuideUrl(int seriesId) + { + return String.Format("http://www.thetvdb.com/api/{0}/series/{1}/all/en.zip", TvDbProvider.TVDB_APIKEY, seriesId); + } + } +} diff --git a/NzbDrone.Core/Providers/Metadata/Xbmc.cs b/NzbDrone.Core/Providers/Metadata/Xbmc.cs new file mode 100644 index 000000000..6f4a5587a --- /dev/null +++ b/NzbDrone.Core/Providers/Metadata/Xbmc.cs @@ -0,0 +1,248 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using NLog; +using NzbDrone.Common; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Repository; +using TvdbLib.Data; +using TvdbLib.Data.Banner; + +namespace NzbDrone.Core.Providers.Metadata +{ + public class Xbmc : MetadataBase + { + protected readonly Logger _logger = LogManager.GetCurrentClassLogger(); + + public Xbmc(ConfigProvider configProvider, DiskProvider diskProvider, BannerProvider bannerProvider, EpisodeProvider episodeProvider) + : base(configProvider, diskProvider, bannerProvider, episodeProvider) + { + } + + public override string Name + { + get { return "XBMC"; } + } + + public override void CreateForSeries(Series series, TvdbSeries tvDbSeries) + { + //Create tvshow.nfo, fanart.jpg, folder.jpg and season##.tbn + var episodeGuideUrl = GetEpisodeGuideUrl(series.SeriesId); + + _logger.Debug("Generating tvshow.nfo for: {0}", series.Title); + var sb = new StringBuilder(); + var xws = new XmlWriterSettings(); + xws.OmitXmlDeclaration = true; + xws.Indent = false; + + using (var xw = XmlWriter.Create(sb, xws)) + { + var tvShow = new XElement("tvshow"); + tvShow.Add(new XElement("title", tvDbSeries.SeriesName)); + tvShow.Add(new XElement("rating", tvDbSeries.Rating)); + tvShow.Add(new XElement("plot", tvDbSeries.Overview)); + tvShow.Add(new XElement("episodeguide", new XElement("url"), episodeGuideUrl)); + tvShow.Add(new XElement("episodeguideurl", episodeGuideUrl)); + tvShow.Add(new XElement("mpaa", tvDbSeries.ContentRating)); + tvShow.Add(new XElement("genre", tvDbSeries.GenreString)); + tvShow.Add(new XElement("premiered", tvDbSeries.FirstAired.ToString("yyyy-MM-dd"))); + tvShow.Add(new XElement("studio", tvDbSeries.Network)); + + foreach(var actor in tvDbSeries.TvdbActors) + { + tvShow.Add(new XElement("actor", + new XElement("name", actor.Name), + new XElement("role", actor.Role), + new XElement("thumb", actor.ActorImage) + )); + } + + var doc = new XDocument(tvShow); + doc.Save(xw); + + _logger.Debug("Saving tvshow.nfo for {0}", series.Title); + _diskProvider.WriteAllText(Path.Combine(series.Path, "tvshow.nfo"), doc.ToString()); + } + + _logger.Debug("Downloading fanart for: {0}", series.Title); + _bannerProvider.Download(tvDbSeries.FanartPath, Path.Combine(series.Path, "fanart.jpg")); + + if (_configProvider.MetadataUseBanners) + { + _logger.Debug("Downloading series banner for: {0}", series.Title); + _bannerProvider.Download(tvDbSeries.BannerPath, Path.Combine(series.Path, "folder.jpg")); + + _logger.Debug("Downloading Season banners for {0}", series.Title); + DownloadSeasonThumbnails(series, tvDbSeries, TvdbSeasonBanner.Type.seasonwide); + } + + else + { + _logger.Debug("Downloading series thumbnail for: {0}", series.Title); + _bannerProvider.Download(tvDbSeries.PosterPath, Path.Combine(series.Path, "folder.jpg")); + + _logger.Debug("Downloading Season posters for {0}", series.Title); + DownloadSeasonThumbnails(series, tvDbSeries, TvdbSeasonBanner.Type.season); + } + } + + public override void CreateForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tvDbSeries) + { + //Create filename.tbn and filename.nfo + var episodes = _episodeProvider.GetEpisodesByFileId(episodeFile.EpisodeFileId); + + if (!episodes.Any()) + { + _logger.Debug("No episodes where found for this episode file: {0}", episodeFile.EpisodeFileId); + return; + } + + var episodeFileThumbnail = tvDbSeries.Episodes.FirstOrDefault( + e => + e.SeasonNumber == episodeFile.SeasonNumber && + e.EpisodeNumber == episodes.First().EpisodeNumber); + + if (episodeFileThumbnail == null || String.IsNullOrWhiteSpace(episodeFileThumbnail.BannerPath)) + { + _logger.Debug("No thumbnail is available for this episode"); + return; + } + + _logger.Debug("Downloading episode thumbnail for: {0}", episodeFile.EpisodeFileId); + _bannerProvider.Download(episodeFileThumbnail.BannerPath, episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".tbn")); + + _logger.Debug("Generating filename.nfo for: {0}", episodeFile.EpisodeFileId); + + var xmlResult = String.Empty; + foreach (var episode in episodes) + { + var sb = new StringBuilder(); + var xws = new XmlWriterSettings(); + xws.OmitXmlDeclaration = true; + xws.Indent = false; + + using (var xw = XmlWriter.Create(sb, xws)) + { + var doc = new XDocument(); + var tvdbEpisode = tvDbSeries.Episodes.FirstOrDefault( + e => + e.Id == episode.TvDbEpisodeId); + + if (tvdbEpisode == null) + { + _logger.Debug("Looking up by TvDbEpisodeId failed, trying to match via season/episode number combination"); + tvdbEpisode = tvDbSeries.Episodes.FirstOrDefault( + e => + e.SeasonNumber == episode.SeasonNumber && + e.EpisodeNumber == episode.EpisodeNumber); + } + + if (tvdbEpisode == null) + { + _logger.Debug("Unable to find episode from TvDb - skipping"); + return; + } + + var details = new XElement("episodedetails"); + details.Add(new XElement("title", tvdbEpisode.EpisodeName)); + details.Add(new XElement("season", tvdbEpisode.SeasonNumber)); + details.Add(new XElement("episode", tvdbEpisode.EpisodeNumber)); + details.Add(new XElement("aired", tvdbEpisode.FirstAired)); + details.Add(new XElement("plot", tvDbSeries.Overview)); + details.Add(new XElement("displayseason")); + details.Add(new XElement("displayepisode")); + details.Add(new XElement("thumb", "http://www.thetvdb.com/banners/" + tvdbEpisode.BannerPath)); + details.Add(new XElement("watched", "false")); + details.Add(new XElement("credits", tvdbEpisode.Writer.First())); + details.Add(new XElement("director", tvdbEpisode.Directors.First())); + details.Add(new XElement("rating", tvDbSeries.Rating)); + + foreach(var actor in tvdbEpisode.GuestStars) + { + if (!String.IsNullOrWhiteSpace(actor)) + continue; + + details.Add(new XElement("actor", + new XElement("name", actor) + )); + } + + foreach(var actor in tvDbSeries.TvdbActors) + { + details.Add(new XElement("actor", + new XElement("name", actor.Name), + new XElement("role", actor.Role), + new XElement("thumb", actor.ActorImage) + )); + } + + doc.Add(details); + doc.Save(xw); + + xmlResult += doc.ToString(); + } + } + var filename = episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".nfo"); + _logger.Debug("Saving episodedetails to: {0}", filename); + _diskProvider.WriteAllText(filename, xmlResult); + } + + public override void RemoveForSeries(Series series) + { + //Remove tvshow.nfo, fanart.jpg, folder.jpg and season##.tbn + _logger.Debug("Deleting series metadata for: ", series.Title); + + _diskProvider.DeleteFile(Path.Combine(series.Path, "tvshow.nfo")); + _diskProvider.DeleteFile(Path.Combine(series.Path, "fanart.jpg")); + _diskProvider.DeleteFile(Path.Combine(series.Path, "fanart.jpg")); + + foreach (var file in _diskProvider.GetFiles(series.Path, SearchOption.TopDirectoryOnly)) + { + if (Path.GetExtension(file) != ".tbn") + continue; + + if (!Path.GetFileName(file).StartsWith("season")) + continue; + + _logger.Debug("Deleting season thumbnail: {0}", file); + _diskProvider.DeleteFile(file); + } + } + + public override void RemoveForEpisodeFile(EpisodeFile episodeFile) + { + //Remove filename.tbn and filename.nfo + _logger.Debug("Deleting episode metadata for: {0}", episodeFile); + + _diskProvider.DeleteFile(episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".nfo")); + _diskProvider.DeleteFile(episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".tbn")); + } + + private void DownloadSeasonThumbnails(Series series, TvdbSeries tvDbSeries, TvdbSeasonBanner.Type bannerType) + { + var seasons = tvDbSeries.SeasonBanners.Where(s => s.BannerType == bannerType).Select(s => s.Season); + + foreach (var season in seasons) + { + var banner = tvDbSeries.SeasonBanners.FirstOrDefault(b => b.BannerType == bannerType && b.Season == season); + _logger.Debug("Downloading banner for Season: {0} Series: {1}", season, series.Title); + + if (season == 0) + { + _bannerProvider.Download(banner.BannerPath, + Path.Combine(series.Path, "season-specials.tbn")); + } + + else + { + _bannerProvider.Download(banner.BannerPath, + Path.Combine(series.Path, String.Format("season{0:00}.tbn", season))); + } + } + } + } +} diff --git a/NzbDrone.Core/Providers/MetadataProvider.cs b/NzbDrone.Core/Providers/MetadataProvider.cs new file mode 100644 index 000000000..3907ce274 --- /dev/null +++ b/NzbDrone.Core/Providers/MetadataProvider.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Ninject; +using NLog; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers.ExternalNotification; +using NzbDrone.Core.Providers.Metadata; +using NzbDrone.Core.Repository; +using PetaPoco; +using TvdbLib.Data; + +namespace NzbDrone.Core.Providers +{ + public class MetadataProvider + { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private readonly IDatabase _database; + + private IEnumerable _metadataProviders; + private readonly TvDbProvider _tvDbProvider; + + [Inject] + public MetadataProvider(IDatabase database, IEnumerable metadataProviders, TvDbProvider tvDbProvider) + { + _database = database; + _metadataProviders = metadataProviders; + _tvDbProvider = tvDbProvider; + } + + public MetadataProvider() + { + + } + + public virtual List All() + { + return _database.Fetch(); + } + + public virtual void SaveSettings(MetadataDefinition settings) + { + if (settings.Id == 0) + { + Logger.Debug("Adding Metabase definition for {0}", settings.Name); + _database.Insert(settings); + } + + else + { + Logger.Debug("Updating Metabase definition for {0}", settings.Name); + _database.Update(settings); + } + } + + public virtual MetadataDefinition GetSettings(Type type) + { + return _database.SingleOrDefault("WHERE MetadataProviderType = @0", type.ToString()); + } + + public virtual IList GetEnabledMetabaseProviders() + { + var all = All(); + return _metadataProviders.Where(i => all.Exists(c => c.MetadataProviderType == i.GetType().ToString() && c.Enable)).ToList(); + } + + public virtual void Initialize(IList metabaseProviders) + { + Logger.Debug("Initializing metabases. Count {0}", metabaseProviders.Count); + + _metadataProviders = metabaseProviders; + + var currentNotifiers = All(); + + foreach (var notificationProvider in metabaseProviders) + { + MetadataBase metadataProviderLocal = notificationProvider; + if (!currentNotifiers.Exists(c => c.MetadataProviderType == metadataProviderLocal.GetType().ToString())) + { + var settings = new MetadataDefinition + { + Enable = false, + MetadataProviderType = metadataProviderLocal.GetType().ToString(), + Name = metadataProviderLocal.Name + }; + + SaveSettings(settings); + } + } + } + + public virtual void CreateForSeries(Series series) + { + var tvDbSeries = _tvDbProvider.GetSeries(series.SeriesId, false, true); + + CreateForSeries(series, tvDbSeries); + } + + public virtual void CreateForSeries(Series series, TvdbSeries tvDbSeries) + { + foreach (var provider in _metadataProviders.Where(i => GetSettings(i.GetType()).Enable)) + { + provider.CreateForSeries(series, tvDbSeries); + } + } + + public virtual void CreateForEpisodeFile(EpisodeFile episodeFile) + { + var tvDbSeries = _tvDbProvider.GetSeries(episodeFile.SeriesId, true, true); + + CreateForEpisodeFile(episodeFile, tvDbSeries); + } + + public virtual void CreateForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tvDbSeries) + { + foreach (var provider in _metadataProviders.Where(i => GetSettings(i.GetType()).Enable)) + { + provider.CreateForEpisodeFile(episodeFile, tvDbSeries); + } + } + + public virtual void CreateForEpisodeFiles(List episodeFiles) + { + var tvDbSeries = _tvDbProvider.GetSeries(episodeFiles.First().SeriesId, true, true); + + foreach(var episodeFile in episodeFiles) + { + foreach (var provider in _metadataProviders.Where(i => GetSettings(i.GetType()).Enable)) + { + provider.CreateForEpisodeFile(episodeFile, tvDbSeries); + } + } + } + + public virtual void RemoveForSeries(Series series) + { + foreach (var provider in _metadataProviders.Where(i => GetSettings(i.GetType()).Enable)) + { + provider.RemoveForSeries(series); + } + } + + public virtual void RemoveForEpisodeFile(EpisodeFile episodeFile) + { + foreach (var provider in _metadataProviders.Where(i => GetSettings(i.GetType()).Enable)) + { + provider.RemoveForEpisodeFile(episodeFile); + } + } + + public virtual void RemoveForEpisodeFiles(List episodeFiles) + { + foreach (var episodeFile in episodeFiles) + { + foreach (var provider in _metadataProviders.Where(i => GetSettings(i.GetType()).Enable)) + { + provider.RemoveForEpisodeFile(episodeFile); + } + } + } + + public virtual void RenameForEpisodeFile(EpisodeFile episodeFile) + { + + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Providers/PostDownloadProvider.cs b/NzbDrone.Core/Providers/PostDownloadProvider.cs index 9bd196b7b..244a202b3 100644 --- a/NzbDrone.Core/Providers/PostDownloadProvider.cs +++ b/NzbDrone.Core/Providers/PostDownloadProvider.cs @@ -16,14 +16,16 @@ namespace NzbDrone.Core.Providers private readonly DiskProvider _diskProvider; private readonly DiskScanProvider _diskScanProvider; private readonly SeriesProvider _seriesProvider; + private readonly MetadataProvider _metadataProvider; [Inject] public PostDownloadProvider(DiskProvider diskProvider, DiskScanProvider diskScanProvider, - SeriesProvider seriesProvider) + SeriesProvider seriesProvider, MetadataProvider metadataProvider) { _diskProvider = diskProvider; _diskScanProvider = diskScanProvider; _seriesProvider = seriesProvider; + _metadataProvider = metadataProvider; } public PostDownloadProvider() @@ -71,6 +73,9 @@ namespace NzbDrone.Core.Providers var importedFiles = _diskScanProvider.Scan(series, subfolderInfo.FullName); importedFiles.ForEach(file => _diskScanProvider.MoveEpisodeFile(file, true)); + //Create Metadata for all the episode files found + _metadataProvider.CreateForEpisodeFiles(importedFiles); + //Delete the folder only if folder is small enough if (_diskProvider.GetDirectorySize(subfolderInfo.FullName) < Constants.IgnoreFileSize) { diff --git a/NzbDrone.Core/Providers/SeriesProvider.cs b/NzbDrone.Core/Providers/SeriesProvider.cs index 0a5efdbac..aee5b150d 100644 --- a/NzbDrone.Core/Providers/SeriesProvider.cs +++ b/NzbDrone.Core/Providers/SeriesProvider.cs @@ -20,17 +20,19 @@ namespace NzbDrone.Core.Providers private readonly IDatabase _database; private readonly SceneMappingProvider _sceneNameMappingProvider; private readonly BannerProvider _bannerProvider; + private readonly MetadataProvider _metadataProvider; private static readonly Regex TimeRegex = new Regex(@"^(?