1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-05 10:32:35 +01:00

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 @@ public virtual string ReadAllText(string filePath)
return File.ReadAllText(filePath); return File.ReadAllText(filePath);
} }
public virtual void WriteAllText(string filename, string contents)
{
File.WriteAllText(filename, contents);
}
public static bool PathEquals(string firstPath, string secondPath) 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\AnalyticsProviderTests\AnalyticsProviderFixture.cs" />
<Compile Include="ProviderTests\ConfigProviderTests\ConfigCachingFixture.cs" /> <Compile Include="ProviderTests\ConfigProviderTests\ConfigCachingFixture.cs" />
<Compile Include="ProviderTests\BannerProviderTest.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\SearchHistoryProviderTest.cs" />
<Compile Include="ProviderTests\PlexProviderTest.cs" /> <Compile Include="ProviderTests\PlexProviderTest.cs" />
<Compile Include="ProviderTests\SeasonProviderTest.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 @@ public void move_should_not_move_file_if_source_and_destination_are_the_same_pat
var result = Mocker.Resolve<DiskScanProvider>().MoveEpisodeFile(file, false); var result = Mocker.Resolve<DiskScanProvider>().MoveEpisodeFile(file, false);
//Assert //Assert
result.Should().BeFalse(); result.Should().BeNull();
} }
[Test] [Test]
@ -368,7 +368,7 @@ public void MoveEpisodeFile_should_use_EpisodeFiles_quality()
var result = Mocker.Resolve<DiskScanProvider>().MoveEpisodeFile(file, true); var result = Mocker.Resolve<DiskScanProvider>().MoveEpisodeFile(file, true);
//Assert //Assert
result.Should().BeTrue(); result.Should().NotBeNull();
Mocker.GetMock<ExternalNotificationProvider>() Mocker.GetMock<ExternalNotificationProvider>()
.Verify(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny<Series>()), Times.Once()); .Verify(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny<Series>()), Times.Once());
} }

View File

@ -172,7 +172,7 @@ public void RefreshEpisodeInfo_emptyRepo()
Db.Insert(fakeSeries); Db.Insert(fakeSeries);
Mocker.GetMock<TvDbProvider>() Mocker.GetMock<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(fakeEpisodes); .Returns(fakeEpisodes);
//Act //Act
@ -207,7 +207,7 @@ public void RefreshEpisodeInfo_should_set_older_than_1900_to_null()
Db.Insert(fakeSeries); Db.Insert(fakeSeries);
Mocker.GetMock<TvDbProvider>() Mocker.GetMock<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(fakeEpisodes); .Returns(fakeEpisodes);
@ -248,7 +248,7 @@ public void RefreshEpisodeInfo_should_set_older_than_1900_to_null_for_existing_e
Db.Insert(fakeEpisode); Db.Insert(fakeEpisode);
Mocker.GetMock<TvDbProvider>() Mocker.GetMock<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(fakeTvDbEpisodes); .Returns(fakeTvDbEpisodes);
//Act //Act
@ -285,7 +285,7 @@ public void RefreshEpisodeInfo_ignore_episode_zero()
Db.Insert(fakeSeries); Db.Insert(fakeSeries);
Mocker.GetMock<TvDbProvider>() Mocker.GetMock<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(fakeEpisodes); .Returns(fakeEpisodes);
@ -324,7 +324,7 @@ public void RefreshEpisodeInfo_should_skip_future_episodes_with_no_title()
Db.Insert(fakeSeries); Db.Insert(fakeSeries);
Mocker.GetMock<TvDbProvider>() Mocker.GetMock<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(fakeEpisodes); .Returns(fakeEpisodes);
@ -362,7 +362,7 @@ public void RefreshEpisodeInfo_should_skip_older_than_1900_year_episodes_with_no
Db.Insert(fakeSeries); Db.Insert(fakeSeries);
Mocker.GetMock<TvDbProvider>() Mocker.GetMock<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(fakeEpisodes); .Returns(fakeEpisodes);
@ -395,7 +395,7 @@ public void RefreshEpisodeInfo_should_add_future_episodes_with_title()
Db.Insert(fakeSeries); Db.Insert(fakeSeries);
Mocker.GetMock<TvDbProvider>() Mocker.GetMock<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(fakeEpisodes); .Returns(fakeEpisodes);
@ -428,7 +428,7 @@ public void RefreshEpisodeInfo_should_add_old_episodes_with_no_title()
Db.Insert(fakeSeries); Db.Insert(fakeSeries);
Mocker.GetMock<TvDbProvider>() Mocker.GetMock<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(fakeEpisodes); .Returns(fakeEpisodes);
@ -463,7 +463,7 @@ public void RefreshEpisodeInfo_ignore_season_zero()
Db.Insert(fakeSeries); Db.Insert(fakeSeries);
Mocker.GetMock<TvDbProvider>() Mocker.GetMock<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(fakeEpisodes); .Returns(fakeEpisodes);
Mocker.GetMock<SeasonProvider>() Mocker.GetMock<SeasonProvider>()
@ -493,7 +493,7 @@ public void new_episodes_only_calls_Insert()
var currentEpisodes = new List<Episode>(); var currentEpisodes = new List<Episode>();
Mocker.GetMock<TvDbProvider>(MockBehavior.Strict) Mocker.GetMock<TvDbProvider>(MockBehavior.Strict)
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(tvdbSeries); .Returns(tvdbSeries);
Mocker.GetMock<IDatabase>() Mocker.GetMock<IDatabase>()
@ -528,7 +528,7 @@ public void existing_episodes_only_calls_Update()
} }
Mocker.GetMock<TvDbProvider>(MockBehavior.Strict) Mocker.GetMock<TvDbProvider>(MockBehavior.Strict)
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(tvdbSeries); .Returns(tvdbSeries);
Mocker.GetMock<IDatabase>() Mocker.GetMock<IDatabase>()
@ -565,7 +565,7 @@ public void should_try_to_get_existing_episode_using_tvdbid_first()
.Returns(fakeEpisodeList); .Returns(fakeEpisodeList);
Mocker.GetMock<TvDbProvider>() Mocker.GetMock<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(fakeTvDbResult); .Returns(fakeTvDbResult);
//Act //Act
@ -602,7 +602,7 @@ public void should_try_to_get_existing_episode_using_tvdbid_first_then_season_ep
var fakeSeries = Builder<Series>.CreateNew().With(c => c.SeriesId = seriesId).Build(); var fakeSeries = Builder<Series>.CreateNew().With(c => c.SeriesId = seriesId).Build();
Mocker.GetMock<TvDbProvider>(MockBehavior.Strict) Mocker.GetMock<TvDbProvider>(MockBehavior.Strict)
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(tvdbSeries); .Returns(tvdbSeries);
Mocker.GetMock<IDatabase>() Mocker.GetMock<IDatabase>()
@ -634,7 +634,7 @@ public void existing_episodes_keep_their_episodeId_file_id()
} }
Mocker.GetMock<TvDbProvider>(MockBehavior.Strict) Mocker.GetMock<TvDbProvider>(MockBehavior.Strict)
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(tvdbSeries); .Returns(tvdbSeries);
var updatedEpisodes = new List<Episode>(); var updatedEpisodes = new List<Episode>();
@ -695,7 +695,7 @@ public void RefreshEpisodeInfo_should_ignore_new_episode_for_ignored_season()
Db.Insert(fakeEpisode); Db.Insert(fakeEpisode);
Mocker.GetMock<TvDbProvider>() Mocker.GetMock<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(tvdbSeries); .Returns(tvdbSeries);
Mocker.GetMock<SeasonProvider>() Mocker.GetMock<SeasonProvider>()
@ -1407,7 +1407,7 @@ public void RefreshEpisodeInfo_should_ignore_episode_zero_except_if_season_one()
Db.Insert(fakeSeries); Db.Insert(fakeSeries);
Mocker.GetMock<TvDbProvider>() Mocker.GetMock<TvDbProvider>()
.Setup(c => c.GetSeries(seriesId, true)) .Setup(c => c.GetSeries(seriesId, true, false))
.Returns(tvdbSeries); .Returns(tvdbSeries);
//Act //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 @@ public void when_no_file_are_imported_and_folder_size_isnt_small_enought_folder_
Mocker.GetMock<DiskProvider>().Setup(s => s.MoveDirectory(droppedFolder.FullName, taggedFolder)); 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<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.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); Mocker.Resolve<PostDownloadProvider>().ProcessDownload(droppedFolder);
@ -281,9 +281,10 @@ public void when_files_are_imported_and_folder_is_small_enought_dir_should_be_de
Mocker.GetMock<SeriesProvider>().Setup(s => s.FindSeries("office")).Returns(fakeSeries); 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.CleanUpDropFolder(droppedFolder.FullName));
Mocker.GetMock<DiskScanProvider>().Setup(s => s.Scan(fakeSeries, droppedFolder.FullName)).Returns(fakeEpisodeFiles); 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.GetDirectorySize(droppedFolder.FullName)).Returns(Constants.IgnoreFileSize - 1.Megabytes());
Mocker.GetMock<DiskProvider>().Setup(s => s.DeleteFolder(droppedFolder.FullName, true)); Mocker.GetMock<DiskProvider>().Setup(s => s.DeleteFolder(droppedFolder.FullName, true));
Mocker.GetMock<MetadataProvider>().Setup(s => s.CreateForEpisodeFiles(It.IsAny<List<EpisodeFile>>()));
//Act //Act
Mocker.Resolve<PostDownloadProvider>().ProcessDownload(droppedFolder); Mocker.Resolve<PostDownloadProvider>().ProcessDownload(droppedFolder);

View File

@ -13,6 +13,7 @@
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.ExternalNotification; using NzbDrone.Core.Providers.ExternalNotification;
using NzbDrone.Core.Providers.Indexer; using NzbDrone.Core.Providers.Indexer;
using NzbDrone.Core.Providers.Metadata;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using PetaPoco; using PetaPoco;
using SignalR; using SignalR;
@ -20,6 +21,7 @@
using SignalR.Infrastructure; using SignalR.Infrastructure;
using SignalR.Ninject; using SignalR.Ninject;
using Connection = NzbDrone.Core.Datastore.Connection; using Connection = NzbDrone.Core.Datastore.Connection;
using Xbmc = NzbDrone.Core.Providers.ExternalNotification.Xbmc;
namespace NzbDrone.Core namespace NzbDrone.Core
{ {
@ -45,6 +47,7 @@ public CentralDispatch()
InitQuality(); InitQuality();
InitExternalNotifications(); InitExternalNotifications();
InitMetadataProviders();
InitIndexers(); InitIndexers();
InitJobs(); InitJobs();
} }
@ -161,6 +164,16 @@ private void InitExternalNotifications()
Kernel.Get<ExternalNotificationProvider>().InitializeNotifiers(notifiers.ToList()); 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() public void DedicateToHost()
{ {
try 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.Linq;
using System; using System;
using NLog; using NLog;
using Ninject; using Ninject;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Jobs namespace NzbDrone.Core.Jobs
{ {
@ -13,17 +15,20 @@ public class RenameSeasonJob : IJob
private readonly DiskScanProvider _diskScanProvider; private readonly DiskScanProvider _diskScanProvider;
private readonly ExternalNotificationProvider _externalNotificationProvider; private readonly ExternalNotificationProvider _externalNotificationProvider;
private readonly SeriesProvider _seriesProvider; private readonly SeriesProvider _seriesProvider;
private readonly MetadataProvider _metadataProvider;
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); private static readonly Logger logger = LogManager.GetCurrentClassLogger();
[Inject] [Inject]
public RenameSeasonJob(MediaFileProvider mediaFileProvider, DiskScanProvider diskScanProvider, public RenameSeasonJob(MediaFileProvider mediaFileProvider, DiskScanProvider diskScanProvider,
ExternalNotificationProvider externalNotificationProvider, SeriesProvider seriesProvider) ExternalNotificationProvider externalNotificationProvider, SeriesProvider seriesProvider,
MetadataProvider metadataProvider)
{ {
_mediaFileProvider = mediaFileProvider; _mediaFileProvider = mediaFileProvider;
_diskScanProvider = diskScanProvider; _diskScanProvider = diskScanProvider;
_externalNotificationProvider = externalNotificationProvider; _externalNotificationProvider = externalNotificationProvider;
_seriesProvider = seriesProvider; _seriesProvider = seriesProvider;
_metadataProvider = metadataProvider;
} }
public string Name public string Name
@ -57,18 +62,32 @@ public void Start(ProgressNotification notification, int targetId, int secondary
return; return;
} }
var newEpisodeFiles = new List<EpisodeFile>();
var oldEpisodeFiles = new List<EpisodeFile>();
foreach (var episodeFile in episodeFiles) foreach (var episodeFile in episodeFiles)
{ {
try try
{ {
_diskScanProvider.MoveEpisodeFile(episodeFile); var newFile = _diskScanProvider.MoveEpisodeFile(episodeFile);
}
catch (Exception exception) if (newFile != null)
{ {
logger.WarnException("An error has occurred while renaming file", exception); 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 //Start AfterRename
var message = String.Format("Renamed: Series {0}, Season: {1}", series.Title, secondaryTargetId); 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.Linq;
using System; using System;
using NLog; using NLog;
using Ninject; using Ninject;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Jobs namespace NzbDrone.Core.Jobs
{ {
@ -13,17 +15,20 @@ public class RenameSeriesJob : IJob
private readonly DiskScanProvider _diskScanProvider; private readonly DiskScanProvider _diskScanProvider;
private readonly ExternalNotificationProvider _externalNotificationProvider; private readonly ExternalNotificationProvider _externalNotificationProvider;
private readonly SeriesProvider _seriesProvider; private readonly SeriesProvider _seriesProvider;
private readonly MetadataProvider _metadataProvider;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
[Inject] [Inject]
public RenameSeriesJob(MediaFileProvider mediaFileProvider, DiskScanProvider diskScanProvider, public RenameSeriesJob(MediaFileProvider mediaFileProvider, DiskScanProvider diskScanProvider,
ExternalNotificationProvider externalNotificationProvider, SeriesProvider seriesProvider) ExternalNotificationProvider externalNotificationProvider, SeriesProvider seriesProvider,
MetadataProvider metadataProvider)
{ {
_mediaFileProvider = mediaFileProvider; _mediaFileProvider = mediaFileProvider;
_diskScanProvider = diskScanProvider; _diskScanProvider = diskScanProvider;
_externalNotificationProvider = externalNotificationProvider; _externalNotificationProvider = externalNotificationProvider;
_seriesProvider = seriesProvider; _seriesProvider = seriesProvider;
_metadataProvider = metadataProvider;
} }
public string Name public string Name
@ -54,19 +59,32 @@ public void Start(ProgressNotification notification, int targetId, int secondary
return; return;
} }
var newEpisodeFiles = new List<EpisodeFile>();
var oldEpisodeFiles = new List<EpisodeFile>();
foreach (var episodeFile in episodeFiles) foreach (var episodeFile in episodeFiles)
{ {
try try
{ {
_diskScanProvider.MoveEpisodeFile(episodeFile); var newFile = _diskScanProvider.MoveEpisodeFile(episodeFile);
if (newFile != null)
{
newEpisodeFiles.Add(newFile);
oldEpisodeFiles.Add(episodeFile);
} }
}
catch(Exception e) catch(Exception e)
{ {
Logger.WarnException("An error has occurred while renaming file", 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 //Start AfterRename
var message = String.Format("Renamed: Series {0}", series.Title); 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\Migration20110726.cs" />
<Compile Include="Datastore\Migrations\Migration20110707.cs" /> <Compile Include="Datastore\Migrations\Migration20110707.cs" />
<Compile Include="Datastore\DbProviderFactory.cs" /> <Compile Include="Datastore\DbProviderFactory.cs" />
<Compile Include="Datastore\Migrations\Migration20120707.cs" />
<Compile Include="Datastore\Migrations\NzbDroneMigration.cs" /> <Compile Include="Datastore\Migrations\NzbDroneMigration.cs" />
<Compile Include="Datastore\Migrations\SchemaInfo.cs" /> <Compile Include="Datastore\Migrations\SchemaInfo.cs" />
<Compile Include="Datastore\PetaPoco\EpisodeSeasonRelator.cs" /> <Compile Include="Datastore\PetaPoco\EpisodeSeasonRelator.cs" />
@ -285,6 +286,9 @@
<Compile Include="Providers\Indexer\NzbIndex.cs" /> <Compile Include="Providers\Indexer\NzbIndex.cs" />
<Compile Include="Providers\Indexer\FileSharingTalk.cs" /> <Compile Include="Providers\Indexer\FileSharingTalk.cs" />
<Compile Include="Providers\Indexer\Wombles.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\SearchHistoryProvider.cs" />
<Compile Include="Providers\SeasonProvider.cs" /> <Compile Include="Providers\SeasonProvider.cs" />
<Compile Include="Jobs\RecentBacklogSearchJob.cs" /> <Compile Include="Jobs\RecentBacklogSearchJob.cs" />
@ -314,6 +318,7 @@
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Providers\StatsProvider.cs" /> <Compile Include="Providers\StatsProvider.cs" />
<Compile Include="Repository\MetadataDefinition.cs" />
<Compile Include="Repository\Search\SearchHistoryItem.cs" /> <Compile Include="Repository\Search\SearchHistoryItem.cs" />
<Compile Include="Repository\Search\SearchHistory.cs" /> <Compile Include="Repository\Search\SearchHistory.cs" />
<Compile Include="Model\ReportRejectionType.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 @@ public virtual bool Delete(int seriesId)
} }
return true; 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 @@ public virtual string PlexPassword
set { SetValue("PlexPassword", value); } set { SetValue("PlexPassword", value); }
} }
public virtual Boolean MetadataUseBanners
{
get { return GetValueBoolean("MetadataUseBanners"); }
set { SetValue("MetadataUseBanners", value); }
}
private string GetValue(string key) private string GetValue(string key)
{ {
return GetValue(key, String.Empty); return GetValue(key, String.Empty);

View File

@ -168,7 +168,7 @@ public virtual EpisodeFile ImportFile(Series series, string filePath)
return episodeFile; return episodeFile;
} }
public virtual bool MoveEpisodeFile(EpisodeFile episodeFile, bool newDownload = false) public virtual EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, bool newDownload = false)
{ {
if (episodeFile == null) if (episodeFile == null)
throw new ArgumentNullException("episodeFile"); throw new ArgumentNullException("episodeFile");
@ -182,7 +182,7 @@ public virtual bool MoveEpisodeFile(EpisodeFile episodeFile, bool newDownload =
if (DiskProvider.PathEquals(episodeFile.Path, newFile.FullName)) if (DiskProvider.PathEquals(episodeFile.Path, newFile.FullName))
{ {
Logger.Debug("Skipping file rename, source and destination are the same: {0}", episodeFile.Path); Logger.Debug("Skipping file rename, source and destination are the same: {0}", episodeFile.Path);
return false; return null;
} }
_diskProvider.CreateDirectory(newFile.DirectoryName); _diskProvider.CreateDirectory(newFile.DirectoryName);
@ -213,7 +213,7 @@ public virtual bool MoveEpisodeFile(EpisodeFile episodeFile, bool newDownload =
_externalNotificationProvider.OnRename(message, series); _externalNotificationProvider.OnRename(message, series);
} }
return true; return episodeFile;
} }
/// <summary> /// <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 @@ public class PostDownloadProvider
private readonly DiskProvider _diskProvider; private readonly DiskProvider _diskProvider;
private readonly DiskScanProvider _diskScanProvider; private readonly DiskScanProvider _diskScanProvider;
private readonly SeriesProvider _seriesProvider; private readonly SeriesProvider _seriesProvider;
private readonly MetadataProvider _metadataProvider;
[Inject] [Inject]
public PostDownloadProvider(DiskProvider diskProvider, DiskScanProvider diskScanProvider, public PostDownloadProvider(DiskProvider diskProvider, DiskScanProvider diskScanProvider,
SeriesProvider seriesProvider) SeriesProvider seriesProvider, MetadataProvider metadataProvider)
{ {
_diskProvider = diskProvider; _diskProvider = diskProvider;
_diskScanProvider = diskScanProvider; _diskScanProvider = diskScanProvider;
_seriesProvider = seriesProvider; _seriesProvider = seriesProvider;
_metadataProvider = metadataProvider;
} }
public PostDownloadProvider() public PostDownloadProvider()
@ -71,6 +73,9 @@ public virtual void ProcessDownload(DirectoryInfo subfolderInfo)
var importedFiles = _diskScanProvider.Scan(series, subfolderInfo.FullName); var importedFiles = _diskScanProvider.Scan(series, subfolderInfo.FullName);
importedFiles.ForEach(file => _diskScanProvider.MoveEpisodeFile(file, true)); 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 //Delete the folder only if folder is small enough
if (_diskProvider.GetDirectorySize(subfolderInfo.FullName) < Constants.IgnoreFileSize) if (_diskProvider.GetDirectorySize(subfolderInfo.FullName) < Constants.IgnoreFileSize)
{ {

View File

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

View File

@ -14,7 +14,7 @@ namespace NzbDrone.Core.Providers
public class TvDbProvider public class TvDbProvider
{ {
private readonly EnvironmentProvider _environmentProvider; private readonly EnvironmentProvider _environmentProvider;
private const string TVDB_APIKEY = "5D2D188E86E07F4F"; public const string TVDB_APIKEY = "5D2D188E86E07F4F";
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly TvdbHandler _handler; private readonly TvdbHandler _handler;
@ -44,13 +44,12 @@ public virtual IList<TvdbSearchResult> SearchSeries(string title)
} }
} }
public virtual TvdbSeries GetSeries(int id, bool loadEpisodes, bool loadActors = false)
public virtual TvdbSeries GetSeries(int id, bool loadEpisodes)
{ {
lock (_handler) lock (_handler)
{ {
Logger.Debug("Fetching SeriesId'{0}' from tvdb", id); 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 //Fix American Dad's scene gongshow
if (result != null && result.Id == 73141) if (result != null && result.Id == 73141)
@ -86,6 +85,5 @@ public virtual TvdbSeries GetSeries(int id, bool loadEpisodes)
return result; 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 @@ public class SettingsController : Controller
private readonly QualityTypeProvider _qualityTypeProvider; private readonly QualityTypeProvider _qualityTypeProvider;
private readonly ConfigFileProvider _configFileProvider; private readonly ConfigFileProvider _configFileProvider;
private readonly NewznabProvider _newznabProvider; private readonly NewznabProvider _newznabProvider;
private readonly MetadataProvider _metadataProvider;
public SettingsController(ConfigProvider configProvider, IndexerProvider indexerProvider, public SettingsController(ConfigProvider configProvider, IndexerProvider indexerProvider,
QualityProvider qualityProvider, AutoConfigureProvider autoConfigureProvider, QualityProvider qualityProvider, AutoConfigureProvider autoConfigureProvider,
SeriesProvider seriesProvider, ExternalNotificationProvider externalNotificationProvider, SeriesProvider seriesProvider, ExternalNotificationProvider externalNotificationProvider,
QualityTypeProvider qualityTypeProvider, QualityTypeProvider qualityTypeProvider, ConfigFileProvider configFileProvider,
ConfigFileProvider configFileProvider, NewznabProvider newznabProvider) NewznabProvider newznabProvider, MetadataProvider metadataProvider)
{ {
_externalNotificationProvider = externalNotificationProvider; _externalNotificationProvider = externalNotificationProvider;
_qualityTypeProvider = qualityTypeProvider; _qualityTypeProvider = qualityTypeProvider;
_configFileProvider = configFileProvider; _configFileProvider = configFileProvider;
_newznabProvider = newznabProvider; _newznabProvider = newznabProvider;
_metadataProvider = metadataProvider;
_configProvider = configProvider; _configProvider = configProvider;
_indexerProvider = indexerProvider; _indexerProvider = indexerProvider;
_qualityProvider = qualityProvider; _qualityProvider = qualityProvider;
@ -208,6 +210,10 @@ public ActionResult Naming()
model.NumberStyles = new SelectList(EpisodeSortingHelper.GetNumberStyles(), "Id", "Name"); model.NumberStyles = new SelectList(EpisodeSortingHelper.GetNumberStyles(), "Id", "Name");
model.MultiEpisodeStyles = new SelectList(EpisodeSortingHelper.GetMultiEpisodeStyles(), "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); return View(model);
} }
@ -589,6 +595,14 @@ public JsonResult SaveNaming(EpisodeNamingModel data)
_configProvider.SortingNumberStyle = data.NumberStyle; _configProvider.SortingNumberStyle = data.NumberStyle;
_configProvider.SortingMultiEpisodeStyle = data.MultiEpisodeStyle; _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(); return GetSuccessResult();
} }

View File

@ -43,6 +43,14 @@ public class EpisodeNamingModel
[Description("How will multi-episode files be named?")] [Description("How will multi-episode files be named?")]
public int MultiEpisodeStyle { get; set; } 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 SeparatorStyles { get; set; }
public SelectList NumberStyles { get; set; } public SelectList NumberStyles { get; set; }
public SelectList MultiEpisodeStyles { get; set; } public SelectList MultiEpisodeStyles { get; set; }

View File

@ -543,6 +543,12 @@
<ItemGroup> <ItemGroup>
<Content Include="Views\System\Stats.cshtml" /> <Content Include="Views\System\Stats.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="Views\Settings\EpisodeNamingPartial.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Settings\MetadataPartial.cshtml" />
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion> <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> <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"> <div id="stylized">
@using (Html.BeginForm("SaveNaming", "Settings", FormMethod.Post, new { id = "NamingForm", name = "NamingForm", @class = "settingsForm" })) @using (Html.BeginForm("SaveNaming", "Settings", FormMethod.Post, new { id = "NamingForm", name = "NamingForm", @class = "settingsForm" }))
{ {
<div class="settingsContainer"> <div class="jquery-accordion">
@Html.ValidationSummary(true, "Unable to save your settings. Please correct the errors and try again.") <h3><a href="#">Episode Naming</a></h3>
<label class="labelClass">@Html.LabelFor(m => m.SeriesName) <div id="episodeNamingContainer">
<span class="small">@Html.DescriptionFor(m => m.SeriesName)</span> @{Html.RenderPartial("EpisodeNamingPartial", Model);}
</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>
<div id="examples">
<div id="singleEpisodeExample"> <h3><a href="#">Metadata</a></h3>
<b>Single Episode Example: </b><span class="result"></span> <div id="metadataContainer">
</div> @{Html.RenderPartial("MetaDataPartial", Model);}
<div id="multiEpisodeExample">
<b>Multi-Episode Example: </b><span class="result"></span>
</div> </div>
</div> </div>

View File

@ -1,10 +1,12 @@
<SolutionConfiguration> <SolutionConfiguration>
<FileVersion>0</FileVersion> <FileVersion>1</FileVersion>
<AutoEnableOnStartup>Default</AutoEnableOnStartup> <AutoEnableOnStartup>True</AutoEnableOnStartup>
<AllowParallelTestExecution>false</AllowParallelTestExecution> <AllowParallelTestExecution>false</AllowParallelTestExecution>
<FrameworkUtilisationTypeForNUnit>UseDynamicAnalysis</FrameworkUtilisationTypeForNUnit> <FrameworkUtilisationTypeForNUnit>UseDynamicAnalysis</FrameworkUtilisationTypeForNUnit>
<FrameworkUtilisationTypeForGallio>UseStaticAnalysis</FrameworkUtilisationTypeForGallio> <FrameworkUtilisationTypeForGallio>UseStaticAnalysis</FrameworkUtilisationTypeForGallio>
<FrameworkUtilisationTypeForMSpec>UseStaticAnalysis</FrameworkUtilisationTypeForMSpec> <FrameworkUtilisationTypeForMSpec>UseStaticAnalysis</FrameworkUtilisationTypeForMSpec>
<FrameworkUtilisationTypeForMSTest>UseStaticAnalysis</FrameworkUtilisationTypeForMSTest> <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> <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> </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>