Merge remote-tracking branch 'origin/metadata'

This commit is contained in:
Mark McDowall 2012-07-13 11:03:15 -07:00
commit 7c803c4691
43 changed files with 1348 additions and 91 deletions

View File

@ -0,0 +1,19 @@
<ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration />
<ProxyProcessPath />
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
</ProjectConfiguration>

View File

@ -0,0 +1,19 @@
<ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration />
<ProxyProcessPath />
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
</ProjectConfiguration>

View File

@ -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)
{

View File

@ -0,0 +1,19 @@
<ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration />
<ProxyProcessPath />
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
</ProjectConfiguration>

View File

@ -114,6 +114,8 @@
<Compile Include="ProviderTests\AnalyticsProviderTests\AnalyticsProviderFixture.cs" />
<Compile Include="ProviderTests\ConfigProviderTests\ConfigCachingFixture.cs" />
<Compile Include="ProviderTests\BannerProviderTest.cs" />
<Compile Include="ProviderTests\Metadata\Xbmc_ForEpisodeFile_Fixture.cs" />
<Compile Include="ProviderTests\Metadata\Xbmc_ForSeries_Fixture.cs" />
<Compile Include="ProviderTests\SearchHistoryProviderTest.cs" />
<Compile Include="ProviderTests\PlexProviderTest.cs" />
<Compile Include="ProviderTests\SeasonProviderTest.cs" />

View File

@ -0,0 +1,19 @@
<ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration />
<ProxyProcessPath />
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
</ProjectConfiguration>

View File

@ -212,7 +212,7 @@ namespace NzbDrone.Core.Test.ProviderTests
var result = Mocker.Resolve<DiskScanProvider>().MoveEpisodeFile(file, false);
//Assert
result.Should().BeFalse();
result.Should().BeNull();
}
[Test]
@ -368,7 +368,7 @@ namespace NzbDrone.Core.Test.ProviderTests
var result = Mocker.Resolve<DiskScanProvider>().MoveEpisodeFile(file, true);
//Assert
result.Should().BeTrue();
result.Should().NotBeNull();
Mocker.GetMock<ExternalNotificationProvider>()
.Verify(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny<Series>()), Times.Once());
}

View File

@ -172,7 +172,7 @@ namespace NzbDrone.Core.Test.ProviderTests
Db.Insert(fakeSeries);
Mocker.GetMock<TvDbProvider>()
.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<TvDbProvider>()
.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<TvDbProvider>()
.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<TvDbProvider>()
.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<TvDbProvider>()
.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<TvDbProvider>()
.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<TvDbProvider>()
.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<TvDbProvider>()
.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<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true))
.Setup(c => c.GetSeries(seriesId, true, false))
.Returns(fakeEpisodes);
Mocker.GetMock<SeasonProvider>()
@ -493,7 +493,7 @@ namespace NzbDrone.Core.Test.ProviderTests
var currentEpisodes = new List<Episode>();
Mocker.GetMock<TvDbProvider>(MockBehavior.Strict)
.Setup(c => c.GetSeries(seriesId, true))
.Setup(c => c.GetSeries(seriesId, true, false))
.Returns(tvdbSeries);
Mocker.GetMock<IDatabase>()
@ -528,7 +528,7 @@ namespace NzbDrone.Core.Test.ProviderTests
}
Mocker.GetMock<TvDbProvider>(MockBehavior.Strict)
.Setup(c => c.GetSeries(seriesId, true))
.Setup(c => c.GetSeries(seriesId, true, false))
.Returns(tvdbSeries);
Mocker.GetMock<IDatabase>()
@ -565,7 +565,7 @@ namespace NzbDrone.Core.Test.ProviderTests
.Returns(fakeEpisodeList);
Mocker.GetMock<TvDbProvider>()
.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<Series>.CreateNew().With(c => c.SeriesId = seriesId).Build();
Mocker.GetMock<TvDbProvider>(MockBehavior.Strict)
.Setup(c => c.GetSeries(seriesId, true))
.Setup(c => c.GetSeries(seriesId, true, false))
.Returns(tvdbSeries);
Mocker.GetMock<IDatabase>()
@ -634,7 +634,7 @@ namespace NzbDrone.Core.Test.ProviderTests
}
Mocker.GetMock<TvDbProvider>(MockBehavior.Strict)
.Setup(c => c.GetSeries(seriesId, true))
.Setup(c => c.GetSeries(seriesId, true, false))
.Returns(tvdbSeries);
var updatedEpisodes = new List<Episode>();
@ -695,7 +695,7 @@ namespace NzbDrone.Core.Test.ProviderTests
Db.Insert(fakeEpisode);
Mocker.GetMock<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true))
.Setup(c => c.GetSeries(seriesId, true, false))
.Returns(tvdbSeries);
Mocker.GetMock<SeasonProvider>()
@ -1407,7 +1407,7 @@ namespace NzbDrone.Core.Test.ProviderTests
Db.Insert(fakeSeries);
Mocker.GetMock<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true))
.Setup(c => c.GetSeries(seriesId, true, false))
.Returns(tvdbSeries);
//Act

View File

@ -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<Series>
.CreateNew()
.With(s => s.SeriesId == 79488)
.With(s => s.Title == "30 Rock")
.Build();
episodeFile = Builder<EpisodeFile>.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<TvdbEpisode>.CreateListOfSize(2)
.All()
.With(e => e.SeriesId = 79488)
.With(e => e.SeasonNumber = 1)
.With(e => e.Directors = new List<string>{ "Fake Director" })
.With(e => e.Writer = new List<string>{ "Fake Writer" })
.With(e => e.GuestStars = new List<string> { "Guest Star 1", "Guest Star 2", "Guest Star 3", "" })
.Build();
var seasonBanners = Builder<TvdbSeasonBanner>
.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<TvdbActor>
.CreateListOfSize(5)
.Build();
tvdbSeries = Builder<TvdbSeries>
.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<ConfigProvider>().SetupGet(s => s.MetadataUseBanners).Returns(true);
}
private void WithSingleEpisodeFile()
{
var episode = Builder<Episode>.CreateNew()
.With(e => e.SeasonNumber = 1)
.With(e => e.SeriesId = 79488)
.With(e => e.EpisodeNumber = 1)
.Build();
Mocker.GetMock<EpisodeProvider>()
.Setup(s => s.GetEpisodesByFileId(episodeFile.EpisodeFileId))
.Returns(new List<Episode> { episode });
}
private void WithMultiEpisodeFile()
{
var episodes = Builder<Episode>.CreateListOfSize(2)
.All()
.With(e => e.SeriesId = 79488)
.With(e => e.SeasonNumber = 1)
.Build();
Mocker.GetMock<EpisodeProvider>()
.Setup(s => s.GetEpisodesByFileId(episodeFile.EpisodeFileId))
.Returns(episodes.ToList());
}
[Test]
public void should_not_blowup()
{
WithSingleEpisodeFile();
Mocker.Resolve<Xbmc>().CreateForEpisodeFile(episodeFile, tvdbSeries);
}
[Test]
public void should_call_diskprovider_writeAllText_once_for_single_episode()
{
WithSingleEpisodeFile();
Mocker.Resolve<Xbmc>().CreateForEpisodeFile(episodeFile, tvdbSeries);
Mocker.GetMock<DiskProvider>().Verify(v => v.WriteAllText(episodeFile.Path.Replace("avi", "nfo"), It.IsAny<string>()), Times.Once());
}
[Test]
public void should_call_diskprovider_writeAllText_once_for_multi_episode()
{
WithMultiEpisodeFile();
Mocker.Resolve<Xbmc>().CreateForEpisodeFile(episodeFile, tvdbSeries);
Mocker.GetMock<DiskProvider>().Verify(v => v.WriteAllText(episodeFile.Path.Replace("avi", "nfo"), It.IsAny<string>()), Times.Once());
}
[Test]
public void should_download_thumbnail_when_thumbnail_path_is_not_null()
{
WithSingleEpisodeFile();
Mocker.Resolve<Xbmc>().CreateForEpisodeFile(episodeFile, tvdbSeries);
Mocker.GetMock<BannerProvider>().Verify(v => v.Download(tvdbSeries.Episodes.First().BannerPath, episodeFile.Path.Replace("avi", "tbn")), Times.Once());
}
}
}

View File

@ -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<Series>
.CreateNew()
.With(s => s.SeriesId == 79488)
.With(s => s.Title == "30 Rock")
.Build();
var seasonBanners = Builder<TvdbSeasonBanner>
.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<TvdbActor>
.CreateListOfSize(5)
.Build();
tvdbSeries = Builder<TvdbSeries>
.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<ConfigProvider>().SetupGet(s => s.MetadataUseBanners).Returns(true);
}
private void WithSpecials()
{
var seasonBanners = Builder<TvdbSeasonBanner>
.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<TvdbActor>
.CreateListOfSize(5)
.Build();
tvdbSeries = Builder<TvdbSeries>
.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<Xbmc>().CreateForSeries(series, tvdbSeries);
}
[Test]
public void should_call_diskprovider_writeAllText()
{
Mocker.Resolve<Xbmc>().CreateForSeries(series, tvdbSeries);
Mocker.GetMock<DiskProvider>().Verify(v => v.WriteAllText(Path.Combine(series.Path, "tvshow.nfo"), It.IsAny<string>()), Times.Once());
}
[Test]
public void should_download_fanart()
{
Mocker.Resolve<Xbmc>().CreateForSeries(series, tvdbSeries);
Mocker.GetMock<BannerProvider>().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<Xbmc>().CreateForSeries(series, tvdbSeries);
Mocker.GetMock<BannerProvider>().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<Xbmc>().CreateForSeries(series, tvdbSeries);
Mocker.GetMock<BannerProvider>().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<Xbmc>().CreateForSeries(series, tvdbSeries);
Mocker.GetMock<BannerProvider>().Verify(v => v.Download(It.Is<string>(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<Xbmc>().CreateForSeries(series, tvdbSeries);
Mocker.GetMock<BannerProvider>().Verify(v => v.Download(It.Is<string>(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<Xbmc>().CreateForSeries(series, tvdbSeries);
Mocker.GetMock<BannerProvider>().Verify(v => v.Download(It.Is<string>(s => s.Contains("banners")), It.IsRegex(@"season-specials.tbn")), Times.Exactly(1));
}
}
}

View File

@ -210,7 +210,7 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests
Mocker.GetMock<DiskProvider>().Setup(s => s.MoveDirectory(droppedFolder.FullName, taggedFolder));
Mocker.GetMock<DiskProvider>().Setup(s => s.GetDirectorySize(droppedFolder.FullName)).Returns(Constants.IgnoreFileSize + 10.Megabytes());
Mocker.GetMock<DiskScanProvider>().Setup(s => s.Scan(fakeSeries, droppedFolder.FullName)).Returns(fakeEpisodeFiles);
Mocker.GetMock<DiskScanProvider>().Setup(s => s.MoveEpisodeFile(It.IsAny<EpisodeFile>(), true)).Returns(true);
Mocker.GetMock<DiskScanProvider>().Setup(s => s.MoveEpisodeFile(It.IsAny<EpisodeFile>(), true)).Returns(new EpisodeFile());
Mocker.Resolve<PostDownloadProvider>().ProcessDownload(droppedFolder);
@ -281,9 +281,10 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests
Mocker.GetMock<SeriesProvider>().Setup(s => s.FindSeries("office")).Returns(fakeSeries);
Mocker.GetMock<DiskScanProvider>().Setup(s => s.CleanUpDropFolder(droppedFolder.FullName));
Mocker.GetMock<DiskScanProvider>().Setup(s => s.Scan(fakeSeries, droppedFolder.FullName)).Returns(fakeEpisodeFiles);
Mocker.GetMock<DiskScanProvider>().Setup(s => s.MoveEpisodeFile(It.IsAny<EpisodeFile>(), true)).Returns(true);
Mocker.GetMock<DiskScanProvider>().Setup(s => s.MoveEpisodeFile(It.IsAny<EpisodeFile>(), true)).Returns(new EpisodeFile());
Mocker.GetMock<DiskProvider>().Setup(s => s.GetDirectorySize(droppedFolder.FullName)).Returns(Constants.IgnoreFileSize - 1.Megabytes());
Mocker.GetMock<DiskProvider>().Setup(s => s.DeleteFolder(droppedFolder.FullName, true));
Mocker.GetMock<MetadataProvider>().Setup(s => s.CreateForEpisodeFiles(It.IsAny<List<EpisodeFile>>()));
//Act
Mocker.Resolve<PostDownloadProvider>().ProcessDownload(droppedFolder);

View File

@ -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<ExternalNotificationProvider>().InitializeNotifiers(notifiers.ToList());
}
private void InitMetadataProviders()
{
logger.Debug("Initializing Metadata Providers...");
Kernel.Bind<MetadataBase>().To<Providers.Metadata.Xbmc>().InSingletonScope();
var providers = Kernel.GetAll<MetadataBase>();
Kernel.Get<MetadataProvider>().Initialize(providers.ToList());
}
public void DedicateToHost()
{
try

View File

@ -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)
});
}
}
}

View File

@ -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<EpisodeFile>();
var oldEpisodeFiles = new List<EpisodeFile>();
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);

View File

@ -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<EpisodeFile>();
var oldEpisodeFiles = new List<EpisodeFile>();
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);

View File

@ -239,6 +239,7 @@
<Compile Include="Datastore\Migrations\Migration20110726.cs" />
<Compile Include="Datastore\Migrations\Migration20110707.cs" />
<Compile Include="Datastore\DbProviderFactory.cs" />
<Compile Include="Datastore\Migrations\Migration20120707.cs" />
<Compile Include="Datastore\Migrations\NzbDroneMigration.cs" />
<Compile Include="Datastore\Migrations\SchemaInfo.cs" />
<Compile Include="Datastore\PetaPoco\EpisodeSeasonRelator.cs" />
@ -285,6 +286,9 @@
<Compile Include="Providers\Indexer\NzbIndex.cs" />
<Compile Include="Providers\Indexer\FileSharingTalk.cs" />
<Compile Include="Providers\Indexer\Wombles.cs" />
<Compile Include="Providers\MetadataProvider.cs" />
<Compile Include="Providers\Metadata\MetadataBase.cs" />
<Compile Include="Providers\Metadata\Xbmc.cs" />
<Compile Include="Providers\SearchHistoryProvider.cs" />
<Compile Include="Providers\SeasonProvider.cs" />
<Compile Include="Jobs\RecentBacklogSearchJob.cs" />
@ -314,6 +318,7 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="Providers\StatsProvider.cs" />
<Compile Include="Repository\MetadataDefinition.cs" />
<Compile Include="Repository\Search\SearchHistoryItem.cs" />
<Compile Include="Repository\Search\SearchHistory.cs" />
<Compile Include="Model\ReportRejectionType.cs" />

View File

@ -0,0 +1,19 @@
<ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration />
<ProxyProcessPath />
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
</ProjectConfiguration>

View File

@ -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;
}
}
}
}

View File

@ -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);

View File

@ -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;
}
/// <summary>

View File

@ -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());
}
/// <summary>
/// Gets the name for the metabase provider
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Creates metadata for a series
/// </summary>
/// <param name = "series">The series to create the metadata for</param>
/// <param name = "tvDbSeries">Series information from TheTvDb</param>
public abstract void CreateForSeries(Series series, TvdbSeries tvDbSeries);
/// <summary>
/// Creates metadata for the episode file
/// </summary>
/// <param name = "episodeFile">The episode file to create the metadata</param>
/// <param name = "tvDbSeries">Series information from TheTvDb</param>
public abstract void CreateForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tvDbSeries);
/// <summary>
/// Removes metadata for a series
/// </summary>
/// <param name = "series">The series to create the metadata for</param>
public abstract void RemoveForSeries(Series series);
/// <summary>
/// Removes metadata for the episode file
/// </summary>
/// <param name = "episodeFile">The episode file to create the metadata</param>
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);
}
}
}

View File

@ -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)));
}
}
}
}
}

View File

@ -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<MetadataBase> _metadataProviders;
private readonly TvDbProvider _tvDbProvider;
[Inject]
public MetadataProvider(IDatabase database, IEnumerable<MetadataBase> metadataProviders, TvDbProvider tvDbProvider)
{
_database = database;
_metadataProviders = metadataProviders;
_tvDbProvider = tvDbProvider;
}
public MetadataProvider()
{
}
public virtual List<MetadataDefinition> All()
{
return _database.Fetch<MetadataDefinition>();
}
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<MetadataDefinition>("WHERE MetadataProviderType = @0", type.ToString());
}
public virtual IList<MetadataBase> GetEnabledMetabaseProviders()
{
var all = All();
return _metadataProviders.Where(i => all.Exists(c => c.MetadataProviderType == i.GetType().ToString() && c.Enable)).ToList();
}
public virtual void Initialize(IList<MetadataBase> 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<EpisodeFile> 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<EpisodeFile> 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)
{
}
}
}

View File

@ -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)
{

View File

@ -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(@"^(?<time>\d+:?\d*)\W*(?<meridiem>am|pm)?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public SeriesProvider(IDatabase database, ConfigProvider configProviderProvider,
TvDbProvider tvDbProviderProvider, SceneMappingProvider sceneNameMappingProvider,
BannerProvider bannerProvider)
BannerProvider bannerProvider, MetadataProvider metadataProvider)
{
_database = database;
_configProvider = configProviderProvider;
_tvDbProvider = tvDbProviderProvider;
_sceneNameMappingProvider = sceneNameMappingProvider;
_bannerProvider = bannerProvider;
_metadataProvider = metadataProvider;
}
public SeriesProvider()
@ -86,7 +88,7 @@ namespace NzbDrone.Core.Providers
public virtual Series UpdateSeriesInfo(int seriesId)
{
var tvDbSeries = _tvDbProvider.GetSeries(seriesId, false);
var tvDbSeries = _tvDbProvider.GetSeries(seriesId, false, true);
var series = GetSeries(seriesId);
series.SeriesId = tvDbSeries.Id;
@ -103,6 +105,8 @@ namespace NzbDrone.Core.Providers
series.Network = tvDbSeries.Network;
UpdateSeries(series);
_metadataProvider.CreateForSeries(series, tvDbSeries);
return series;
}

View File

@ -14,7 +14,7 @@ namespace NzbDrone.Core.Providers
public class TvDbProvider
{
private readonly EnvironmentProvider _environmentProvider;
private const string TVDB_APIKEY = "5D2D188E86E07F4F";
public const string TVDB_APIKEY = "5D2D188E86E07F4F";
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly TvdbHandler _handler;
@ -44,13 +44,12 @@ namespace NzbDrone.Core.Providers
}
}
public virtual TvdbSeries GetSeries(int id, bool loadEpisodes)
public virtual TvdbSeries GetSeries(int id, bool loadEpisodes, bool loadActors = false)
{
lock (_handler)
{
Logger.Debug("Fetching SeriesId'{0}' from tvdb", id);
var result = _handler.GetSeries(id, TvdbLanguage.DefaultLanguage, loadEpisodes, false, true, true);
var result = _handler.GetSeries(id, TvdbLanguage.DefaultLanguage, loadEpisodes, loadActors, true, true);
//Fix American Dad's scene gongshow
if (result != null && result.Id == 73141)
@ -86,6 +85,5 @@ namespace NzbDrone.Core.Providers
return result;
}
}
}
}

View File

@ -0,0 +1,17 @@
using PetaPoco;
namespace NzbDrone.Core.Repository
{
[TableName("MetadataDefinitions")]
[PrimaryKey("Id", autoIncrement = true)]
public class MetadataDefinition
{
public int Id { get; set; }
public bool Enable { get; set; }
public string MetadataProviderType { get; set; }
public string Name { get; set; }
}
}

View File

@ -0,0 +1,19 @@
<ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration />
<ProxyProcessPath />
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
</ProjectConfiguration>

View File

@ -0,0 +1,19 @@
<ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration />
<ProxyProcessPath />
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
</ProjectConfiguration>

View File

@ -0,0 +1,19 @@
<ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration />
<ProxyProcessPath />
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
</ProjectConfiguration>

View File

@ -0,0 +1,19 @@
<ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration />
<ProxyProcessPath />
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
</ProjectConfiguration>

View File

@ -0,0 +1,19 @@
<ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration />
<ProxyProcessPath />
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
</ProjectConfiguration>

View File

@ -0,0 +1,19 @@
<ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration />
<ProxyProcessPath />
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
</ProjectConfiguration>

View File

@ -0,0 +1,19 @@
<ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration />
<ProxyProcessPath />
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
</ProjectConfiguration>

View File

@ -31,17 +31,19 @@ namespace NzbDrone.Web.Controllers
private readonly QualityTypeProvider _qualityTypeProvider;
private readonly ConfigFileProvider _configFileProvider;
private readonly NewznabProvider _newznabProvider;
private readonly MetadataProvider _metadataProvider;
public SettingsController(ConfigProvider configProvider, IndexerProvider indexerProvider,
QualityProvider qualityProvider, AutoConfigureProvider autoConfigureProvider,
SeriesProvider seriesProvider, ExternalNotificationProvider externalNotificationProvider,
QualityTypeProvider qualityTypeProvider,
ConfigFileProvider configFileProvider, NewznabProvider newznabProvider)
QualityProvider qualityProvider, AutoConfigureProvider autoConfigureProvider,
SeriesProvider seriesProvider, ExternalNotificationProvider externalNotificationProvider,
QualityTypeProvider qualityTypeProvider, ConfigFileProvider configFileProvider,
NewznabProvider newznabProvider, MetadataProvider metadataProvider)
{
_externalNotificationProvider = externalNotificationProvider;
_qualityTypeProvider = qualityTypeProvider;
_configFileProvider = configFileProvider;
_newznabProvider = newznabProvider;
_metadataProvider = metadataProvider;
_configProvider = configProvider;
_indexerProvider = indexerProvider;
_qualityProvider = qualityProvider;
@ -207,6 +209,10 @@ namespace NzbDrone.Web.Controllers
model.SeparatorStyles = new SelectList(EpisodeSortingHelper.GetSeparatorStyles(), "Id", "Name");
model.NumberStyles = new SelectList(EpisodeSortingHelper.GetNumberStyles(), "Id", "Name");
model.MultiEpisodeStyles = new SelectList(EpisodeSortingHelper.GetMultiEpisodeStyles(), "Id", "Name");
//Metadata
model.MetadataXbmcEnabled = _metadataProvider.GetSettings(typeof(Core.Providers.Metadata.Xbmc)).Enable;
model.MetadataUseBanners = _configProvider.MetadataUseBanners;
return View(model);
}
@ -589,6 +595,14 @@ namespace NzbDrone.Web.Controllers
_configProvider.SortingNumberStyle = data.NumberStyle;
_configProvider.SortingMultiEpisodeStyle = data.MultiEpisodeStyle;
//Metadata
_configProvider.MetadataUseBanners = data.MetadataUseBanners;
//Xbmc
var xbmc = _metadataProvider.GetSettings(typeof(Core.Providers.Metadata.Xbmc));
xbmc.Enable = data.MetadataXbmcEnabled;
_metadataProvider.SaveSettings(xbmc);
return GetSuccessResult();
}

View File

@ -43,6 +43,14 @@ namespace NzbDrone.Web.Models
[Description("How will multi-episode files be named?")]
public int MultiEpisodeStyle { get; set; }
[DisplayName("XBMC")]
[Description("Enable creating metadata for XBMC")]
public bool MetadataXbmcEnabled { get; set; }
[DisplayName("Use Banners")]
[Description("Use banners instead of posters?")]
public bool MetadataUseBanners { get; set; }
public SelectList SeparatorStyles { get; set; }
public SelectList NumberStyles { get; set; }
public SelectList MultiEpisodeStyles { get; set; }

View File

@ -543,6 +543,12 @@
<ItemGroup>
<Content Include="Views\System\Stats.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Settings\EpisodeNamingPartial.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Settings\MetadataPartial.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@ -0,0 +1,19 @@
<ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration />
<ProxyProcessPath />
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
</ProjectConfiguration>

View File

@ -0,0 +1,53 @@
@using NzbDrone.Web.Helpers
@model NzbDrone.Web.Models.EpisodeNamingModel
@{
Layout = null;
}
<div class="settingsContainer">
@Html.ValidationSummary(true, "Unable to save your settings. Please correct the errors and try again.")
<label class="labelClass">@Html.LabelFor(m => m.SeriesName)
<span class="small">@Html.DescriptionFor(m => m.SeriesName)</span>
</label>
@Html.CheckBoxFor(m => m.SeriesName, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.EpisodeName)
<span class="small">@Html.DescriptionFor(m => m.EpisodeName)</span>
</label>
@Html.CheckBoxFor(m => m.EpisodeName, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.ReplaceSpaces)
<span class="small">@Html.DescriptionFor(m => m.ReplaceSpaces)</span>
</label>
@Html.CheckBoxFor(m => m.ReplaceSpaces, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.AppendQuality)
<span class="small">@Html.DescriptionFor(m => m.AppendQuality)</span>
</label>
@Html.CheckBoxFor(m => m.AppendQuality, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.SeasonFolders)
<span class="small">@Html.DescriptionFor(m => m.SeasonFolders)</span>
</label>
@Html.CheckBoxFor(m => m.SeasonFolders, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.SeasonFolderFormat)
<span class="small">@Html.DescriptionFor(m => m.SeasonFolderFormat)</span>
</label>
@Html.TextBoxFor(m => m.SeasonFolderFormat, new { @class = "inputClass" })
<label class="labelClass">@Html.LabelFor(m => m.SeparatorStyle)
<span class="small">@Html.DescriptionFor(m => m.SeparatorStyle)</span>
</label>
@Html.DropDownListFor(m => m.SeparatorStyle, Model.SeparatorStyles, new { @class = "inputClass selectClass" })
<label class="labelClass">@Html.LabelFor(m => m.NumberStyle)
<span class="small">@Html.DescriptionFor(m => m.NumberStyle)</span>
</label>
@Html.DropDownListFor(m => m.NumberStyle, Model.NumberStyles, new { @class = "inputClass selectClass" })
<label class="labelClass">@Html.LabelFor(m => m.MultiEpisodeStyle)
<span class="small">@Html.DescriptionFor(m => m.MultiEpisodeStyle)</span>
</label>
@Html.DropDownListFor(m => m.MultiEpisodeStyle, Model.MultiEpisodeStyles, new { @class = "inputClass selectClass" })
</div>
<div id="examples">
<div id="singleEpisodeExample">
<b>Single Episode Example: </b><span class="result"></span>
</div>
<div id="multiEpisodeExample">
<b>Multi-Episode Example: </b><span class="result"></span>
</div>
</div>

View File

@ -0,0 +1,15 @@
@using NzbDrone.Web.Helpers
@model NzbDrone.Web.Models.EpisodeNamingModel
@{
Layout = null;
}
<label class="labelClass">@Html.LabelFor(m => m.MetadataXbmcEnabled)
<span class="small">@Html.DescriptionFor(m => m.MetadataXbmcEnabled)</span>
</label>
@Html.CheckBoxFor(m => m.MetadataXbmcEnabled, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.MetadataUseBanners)
<span class="small">@Html.DescriptionFor(m => m.MetadataUseBanners)</span>
</label>
@Html.CheckBoxFor(m => m.MetadataUseBanners, new { @class = "inputClass checkClass" })

View File

@ -20,51 +20,15 @@
<div id="stylized">
@using (Html.BeginForm("SaveNaming", "Settings", FormMethod.Post, new { id = "NamingForm", name = "NamingForm", @class = "settingsForm" }))
{
<div class="settingsContainer">
@Html.ValidationSummary(true, "Unable to save your settings. Please correct the errors and try again.")
<label class="labelClass">@Html.LabelFor(m => m.SeriesName)
<span class="small">@Html.DescriptionFor(m => m.SeriesName)</span>
</label>
@Html.CheckBoxFor(m => m.SeriesName, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.EpisodeName)
<span class="small">@Html.DescriptionFor(m => m.EpisodeName)</span>
</label>
@Html.CheckBoxFor(m => m.EpisodeName, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.ReplaceSpaces)
<span class="small">@Html.DescriptionFor(m => m.ReplaceSpaces)</span>
</label>
@Html.CheckBoxFor(m => m.ReplaceSpaces, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.AppendQuality)
<span class="small">@Html.DescriptionFor(m => m.AppendQuality)</span>
</label>
@Html.CheckBoxFor(m => m.AppendQuality, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.SeasonFolders)
<span class="small">@Html.DescriptionFor(m => m.SeasonFolders)</span>
</label>
@Html.CheckBoxFor(m => m.SeasonFolders, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.SeasonFolderFormat)
<span class="small">@Html.DescriptionFor(m => m.SeasonFolderFormat)</span>
</label>
@Html.TextBoxFor(m => m.SeasonFolderFormat, new { @class = "inputClass" })
<label class="labelClass">@Html.LabelFor(m => m.SeparatorStyle)
<span class="small">@Html.DescriptionFor(m => m.SeparatorStyle)</span>
</label>
@Html.DropDownListFor(m => m.SeparatorStyle, Model.SeparatorStyles, new { @class = "inputClass selectClass" })
<label class="labelClass">@Html.LabelFor(m => m.NumberStyle)
<span class="small">@Html.DescriptionFor(m => m.NumberStyle)</span>
</label>
@Html.DropDownListFor(m => m.NumberStyle, Model.NumberStyles, new { @class = "inputClass selectClass" })
<label class="labelClass">@Html.LabelFor(m => m.MultiEpisodeStyle)
<span class="small">@Html.DescriptionFor(m => m.MultiEpisodeStyle)</span>
</label>
@Html.DropDownListFor(m => m.MultiEpisodeStyle, Model.MultiEpisodeStyles, new { @class = "inputClass selectClass" })
</div>
<div id="examples">
<div id="singleEpisodeExample">
<b>Single Episode Example: </b><span class="result"></span>
<div class="jquery-accordion">
<h3><a href="#">Episode Naming</a></h3>
<div id="episodeNamingContainer">
@{Html.RenderPartial("EpisodeNamingPartial", Model);}
</div>
<div id="multiEpisodeExample">
<b>Multi-Episode Example: </b><span class="result"></span>
<h3><a href="#">Metadata</a></h3>
<div id="metadataContainer">
@{Html.RenderPartial("MetaDataPartial", Model);}
</div>
</div>

View File

@ -1,10 +1,12 @@
<SolutionConfiguration>
<FileVersion>0</FileVersion>
<AutoEnableOnStartup>Default</AutoEnableOnStartup>
<FileVersion>1</FileVersion>
<AutoEnableOnStartup>True</AutoEnableOnStartup>
<AllowParallelTestExecution>false</AllowParallelTestExecution>
<FrameworkUtilisationTypeForNUnit>UseDynamicAnalysis</FrameworkUtilisationTypeForNUnit>
<FrameworkUtilisationTypeForGallio>UseStaticAnalysis</FrameworkUtilisationTypeForGallio>
<FrameworkUtilisationTypeForMSpec>UseStaticAnalysis</FrameworkUtilisationTypeForMSpec>
<FrameworkUtilisationTypeForMSTest>UseStaticAnalysis</FrameworkUtilisationTypeForMSTest>
<EngineModes>Run all tests automatically:BFRydWU=;Run all tests manually:BUZhbHNl;Run impacted tests automatically, others manually (experimental!):CklzSW1wYWN0ZWQ=;Run pinned tests automatically, others manually:CElzUGlubmVk</EngineModes>
<MetricsExclusionList>
</MetricsExclusionList>
</SolutionConfiguration>

View File

@ -0,0 +1,19 @@
<ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration />
<ProxyProcessPath />
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
</ProjectConfiguration>