diff --git a/Libraries/XemLib/XemLib.dll b/Libraries/XemLib/XemLib.dll deleted file mode 100644 index 465036a6c..000000000 Binary files a/Libraries/XemLib/XemLib.dll and /dev/null differ diff --git a/Libraries/XemLib/XemLib.pdb b/Libraries/XemLib/XemLib.pdb deleted file mode 100644 index b65f156ff..000000000 Binary files a/Libraries/XemLib/XemLib.pdb and /dev/null differ diff --git a/NzbDrone.Common.Test/DiskProviderFixture.cs b/NzbDrone.Common.Test/DiskProviderFixture.cs index a4762bbdd..469cc77ab 100644 --- a/NzbDrone.Common.Test/DiskProviderFixture.cs +++ b/NzbDrone.Common.Test/DiskProviderFixture.cs @@ -175,6 +175,18 @@ public void check_last_write() Console.WriteLine(new DirectoryInfo(@"C:\DRIVERS").LastWriteTimeUtc); } + [Test] + public void IsChildOfPath_should_return_true_when_it_is_a_child() + { + Mocker.Resolve().IsChildOfPath(@"C:\Test\TV", @"C:\Test").Should().BeTrue(); + } + + [Test] + public void IsChildOfPath_should_return_false_when_it_is_not_a_child() + { + Mocker.Resolve().IsChildOfPath(@"C:\NOT_Test\TV", @"C:\Test").Should().BeFalse(); + } + private void VerifyCopy() { BinFolder.Refresh(); diff --git a/NzbDrone.Common/DiskProvider.cs b/NzbDrone.Common/DiskProvider.cs index f207af40b..4879b8296 100644 --- a/NzbDrone.Common/DiskProvider.cs +++ b/NzbDrone.Common/DiskProvider.cs @@ -268,5 +268,12 @@ public virtual bool IsFileLocked(FileInfo file) return false; } + public virtual bool IsChildOfPath(string child, string parent) + { + if (Path.GetFullPath(child).StartsWith(Path.GetFullPath(parent))) + return true; + + return false; + } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/Files/Xem/Failure.txt b/NzbDrone.Core.Test/Files/Xem/Failure.txt new file mode 100644 index 000000000..63d217c10 --- /dev/null +++ b/NzbDrone.Core.Test/Files/Xem/Failure.txt @@ -0,0 +1,7 @@ +{ + + "result": "failure", + "data": [ ], + "message": "no show with the tvdb_id 79488 found" + +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/Files/Xem/Ids.txt b/NzbDrone.Core.Test/Files/Xem/Ids.txt new file mode 100644 index 000000000..fad09850f --- /dev/null +++ b/NzbDrone.Core.Test/Files/Xem/Ids.txt @@ -0,0 +1,10 @@ +{ + + "result": "success", + "data": [ + "73141", + "79886", + ], + "message": "" + +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/Files/Xem/Mappings.txt b/NzbDrone.Core.Test/Files/Xem/Mappings.txt new file mode 100644 index 000000000..bc7f223ac --- /dev/null +++ b/NzbDrone.Core.Test/Files/Xem/Mappings.txt @@ -0,0 +1,32 @@ +{ + + "result": "success", + "data": [ + { + "scene": { + "season": 1, + "episode": 1, + "absolute": 1 + }, + "tvdb": { + "season": 1, + "episode": 1, + "absolute": 1 + } + }, + { + "scene": { + "season": 1, + "episode": 2, + "absolute": 2 + }, + "tvdb": { + "season": 1, + "episode": 2, + "absolute": 2 + } + } + ], + "message": "full mapping for 73388 on tvdb. this was a cached version" + +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/Files/Xem/Names.txt b/NzbDrone.Core.Test/Files/Xem/Names.txt new file mode 100644 index 000000000..fb005862c --- /dev/null +++ b/NzbDrone.Core.Test/Files/Xem/Names.txt @@ -0,0 +1,24 @@ +{ + + "result": "success", + "data": { + "220571": [ + "Is This a Zombie? Of the Dead", + "Kore wa Zombie Desuka?", + "Kore wa Zombie Desuka? Of the Dead", + "Kore wa Zombie Desuka Of the Dead", + "Kore wa Zombie Desu ka - Of the Dead", + "Kore wa Zombie Desu ka of the Dead" + ], + "79151": [ + "Fate Stay Night", + "Fate/Zero", + "Fate Zero", + "Fate/Zero (2012)", + "Fate Zero S2", + "Fate Zero" + ] + }, + "message": "" + +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/JobTests/ImportNewSeriesJobTest.cs b/NzbDrone.Core.Test/JobTests/ImportNewSeriesJobTest.cs index 46110a053..646a1921b 100644 --- a/NzbDrone.Core.Test/JobTests/ImportNewSeriesJobTest.cs +++ b/NzbDrone.Core.Test/JobTests/ImportNewSeriesJobTest.cs @@ -42,13 +42,15 @@ public void import_new_series_succesful() .Setup(j => j.Start(notification, It.Is(d => d.GetPropertyValue("SeriesId") == series[0].SeriesId))) .Callback(() => series[0].LastDiskSync = DateTime.Now); - Mocker.GetMock() .Setup(j => j.Start(notification, It.Is(d => d.GetPropertyValue("SeriesId") == series[1].SeriesId))) .Callback(() => series[1].LastDiskSync = DateTime.Now); Mocker.GetMock() - .Setup(j => j.Start(notification, It.Is(d => d.GetPropertyValue("SeriesId") >= 0))); + .Setup(j => j.Start(notification, It.Is(d => d.GetPropertyValue("SeriesId") > 0))); + + Mocker.GetMock() + .Setup(j => j.Start(notification, It.Is(d => d.GetPropertyValue("SeriesId") > 0))); Mocker.GetMock() .Setup(j => j.Start(notification, It.Is(d => d.GetPropertyValue("SeriesId") == series[0].SeriesId))) @@ -121,6 +123,9 @@ public void failed_import_should_not_be_stuck_in_loop() Mocker.GetMock() .Setup(s => s.GetSeriesFiles(It.IsAny())).Returns(new List()); + Mocker.GetMock() + .Setup(j => j.Start(notification, It.Is(d => d.GetPropertyValue("SeriesId") == series[0].SeriesId))); + //Act Mocker.Resolve().Start(notification, null); diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index c9942033b..0754ccd11 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -129,13 +129,14 @@ + + False + ..\Libraries\TvdbLib.dll + False ..\packages\WebActivator.1.5\lib\net40\WebActivator.dll - - ..\Libraries\XemLib\XemLib.dll - @@ -156,6 +157,7 @@ + @@ -194,9 +196,11 @@ + + - + @@ -206,7 +210,7 @@ - + @@ -228,7 +232,7 @@ - + @@ -335,6 +339,18 @@ Designer Always + + Always + + + Always + + + Always + + + Always + Designer Always diff --git a/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/CustomStartDateSpecificationFixture.cs b/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/CustomStartDateSpecificationFixture.cs index 21e50db85..95d3d3ada 100644 --- a/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/CustomStartDateSpecificationFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/CustomStartDateSpecificationFixture.cs @@ -32,6 +32,9 @@ public void Setup() { _customStartDateSpecification = Mocker.Resolve(); + firstEpisode = new Episode { AirDate = DateTime.Today }; + secondEpisode = new Episode { AirDate = DateTime.Today }; + fakeSeries = Builder.CreateNew() .With(c => c.Monitored = true) .With(c => c.CustomStartDate = null) @@ -43,6 +46,7 @@ public void Setup() Series = fakeSeries, EpisodeNumbers = new List { 3, 4 }, SeasonNumber = 12, + Episodes = new List { firstEpisode, secondEpisode } }; parseResultSingle = new EpisodeParseResult @@ -51,16 +55,8 @@ public void Setup() Series = fakeSeries, EpisodeNumbers = new List { 3 }, SeasonNumber = 12, + Episodes = new List { firstEpisode } }; - - firstEpisode = new Episode { AirDate = DateTime.Today }; - secondEpisode = new Episode { AirDate = DateTime.Today }; - - var singleEpisodeList = new List { firstEpisode }; - var doubleEpisodeList = new List { firstEpisode, secondEpisode }; - - Mocker.GetMock().Setup(c => c.GetEpisodesByParseResult(parseResultSingle)).Returns(singleEpisodeList); - Mocker.GetMock().Setup(c => c.GetEpisodesByParseResult(parseResultMulti)).Returns(doubleEpisodeList); } private void WithFirstEpisodeLastYear() diff --git a/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/UpgradeDiskSpecificationFixtrue.cs b/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/UpgradeDiskSpecificationFixtrue.cs index 5a6789f1b..74eddef0a 100644 --- a/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/UpgradeDiskSpecificationFixtrue.cs +++ b/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/UpgradeDiskSpecificationFixtrue.cs @@ -31,6 +31,12 @@ public void Setup() Mocker.Resolve(); _upgradeDisk = Mocker.Resolve(); + firstFile = new EpisodeFile { Quality = QualityTypes.Bluray1080p, Proper = true }; + secondFile = new EpisodeFile { Quality = QualityTypes.Bluray1080p, Proper = true }; + + var singleEpisodeList = new List { new Episode { EpisodeFile = firstFile }, new Episode { EpisodeFile = null } }; + var doubleEpisodeList = new List { new Episode { EpisodeFile = firstFile }, new Episode { EpisodeFile = secondFile }, new Episode { EpisodeFile = null } }; + var fakeSeries = Builder.CreateNew() .With(c => c.QualityProfile = new QualityProfile { Cutoff = QualityTypes.Bluray1080p }) .Build(); @@ -41,6 +47,7 @@ public void Setup() Quality = new QualityModel(QualityTypes.DVD, true), EpisodeNumbers = new List { 3, 4 }, SeasonNumber = 12, + Episodes = doubleEpisodeList }; parseResultSingle = new EpisodeParseResult @@ -49,17 +56,8 @@ public void Setup() Quality = new QualityModel(QualityTypes.DVD, true), EpisodeNumbers = new List { 3 }, SeasonNumber = 12, + Episodes = singleEpisodeList }; - - - firstFile = new EpisodeFile { Quality = QualityTypes.Bluray1080p, Proper = true }; - secondFile = new EpisodeFile { Quality = QualityTypes.Bluray1080p, Proper = true }; - - var singleEpisodeList = new List { new Episode { EpisodeFile = firstFile }, new Episode { EpisodeFile = null } }; - var doubleEpisodeList = new List { new Episode { EpisodeFile = firstFile }, new Episode { EpisodeFile = secondFile }, new Episode { EpisodeFile = null } }; - - Mocker.GetMock().Setup(c => c.GetEpisodesByParseResult(parseResultSingle)).Returns(singleEpisodeList); - Mocker.GetMock().Setup(c => c.GetEpisodesByParseResult(parseResultMulti)).Returns(doubleEpisodeList); } private void WithFirstFileUpgradable() @@ -73,9 +71,9 @@ private void WithSecondFileUpgradable() } [Test] - public void should_return_false_if_single_episode_doesnt_exist_on_disk() + public void should_return_true_if_single_episode_doesnt_exist_on_disk() { - Mocker.GetMock().Setup(c => c.GetEpisodesByParseResult(parseResultSingle)).Returns(new List()); + parseResultSingle.Episodes = new List(); _upgradeDisk.IsSatisfiedBy(parseResultSingle).Should().BeTrue(); } diff --git a/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/UpgradeHistorySpecificationFixtrue.cs b/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/UpgradeHistorySpecificationFixtrue.cs index fafc64c94..898585aac 100644 --- a/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/UpgradeHistorySpecificationFixtrue.cs +++ b/NzbDrone.Core.Test/ProviderTests/DecisionEngineTests/UpgradeHistorySpecificationFixtrue.cs @@ -31,6 +31,13 @@ public void Setup() Mocker.Resolve(); _upgradeHistory = Mocker.Resolve(); + var singleEpisodeList = new List { new Episode { SeasonNumber = 12, EpisodeNumber = 3 } }; + var doubleEpisodeList = new List { + new Episode { SeasonNumber = 12, EpisodeNumber = 3 }, + new Episode { SeasonNumber = 12, EpisodeNumber = 4 }, + new Episode { SeasonNumber = 12, EpisodeNumber = 5 } + }; + var fakeSeries = Builder.CreateNew() .With(c => c.QualityProfile = new QualityProfile { Cutoff = QualityTypes.Bluray1080p }) .Build(); @@ -41,6 +48,7 @@ public void Setup() Quality = new QualityModel(QualityTypes.DVD, true), EpisodeNumbers = new List { 3, 4 }, SeasonNumber = 12, + Episodes = doubleEpisodeList }; parseResultSingle = new EpisodeParseResult @@ -49,21 +57,12 @@ public void Setup() Quality = new QualityModel(QualityTypes.DVD, true), EpisodeNumbers = new List { 3 }, SeasonNumber = 12, + Episodes = singleEpisodeList }; firstQuality = new QualityModel(QualityTypes.Bluray1080p, true); secondQuality = new QualityModel(QualityTypes.Bluray1080p, true); - var singleEpisodeList = new List { new Episode { SeasonNumber = 12, EpisodeNumber = 3 } }; - var doubleEpisodeList = new List { - new Episode { SeasonNumber = 12, EpisodeNumber = 3 }, - new Episode { SeasonNumber = 12, EpisodeNumber = 4 }, - new Episode { SeasonNumber = 12, EpisodeNumber = 5 } - }; - - Mocker.GetMock().Setup(c => c.GetEpisodesByParseResult(parseResultSingle)).Returns(singleEpisodeList); - Mocker.GetMock().Setup(c => c.GetEpisodesByParseResult(parseResultMulti)).Returns(doubleEpisodeList); - Mocker.GetMock().Setup(c => c.GetBestQualityInHistory(fakeSeries.SeriesId, 12, 3)).Returns(firstQuality); Mocker.GetMock().Setup(c => c.GetBestQualityInHistory(fakeSeries.SeriesId, 12, 4)).Returns(secondQuality); Mocker.GetMock().Setup(c => c.GetBestQualityInHistory(fakeSeries.SeriesId, 12, 5)).Returns(null); diff --git a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/ImportFileFixture.cs b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/ImportFileFixture.cs index 4eab31abf..32edf4dcd 100644 --- a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/ImportFileFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/ImportFileFixture.cs @@ -175,6 +175,9 @@ public void import_file_with_no_episode_in_db_should_skip() .Setup(p => p.Exists(It.IsAny())) .Returns(false); + Mocker.GetMock(MockBehavior.Strict) + .Setup(e => e.IsChildOfPath(fileName, fakeSeries.Path)).Returns(false); + Mocker.GetMock() .Setup(c => c.GetEpisodesByParseResult(It.IsAny())) .Returns(new List()); @@ -392,5 +395,47 @@ private static void VerifySkipImport(EpisodeFile result, AutoMoqer Mocker) Mocker.GetMock().Verify(p => p.UpdateEpisode(It.IsAny()), Times.Never()); Mocker.GetMock().Verify(p => p.DeleteFile(It.IsAny()), Times.Never()); } + + [Test] + public void should_set_parseResult_SceneSource_if_not_in_series_Path() + { + var series = Builder + .CreateNew() + .With(s => s.Path == @"C:\Test\TV\30 Rock") + .Build(); + + const string path = @"C:\Test\Unsorted TV\30 Rock\30.rock.s01e01.pilot.mkv"; + + Mocker.GetMock().Setup(s => s.GetEpisodesByParseResult(It.IsAny())) + .Returns(new List()); + + Mocker.GetMock().Setup(s => s.IsChildOfPath(path, series.Path)) + .Returns(false); + + Mocker.Resolve().ImportFile(series, path); + + Mocker.Verify(s => s.GetEpisodesByParseResult(It.Is(p => p.SceneSource)), Times.Once()); + } + + [Test] + public void should_not_set_parseResult_SceneSource_if_in_series_Path() + { + var series = Builder + .CreateNew() + .With(s => s.Path == @"C:\Test\TV\30 Rock") + .Build(); + + const string path = @"C:\Test\TV\30 Rock\30.rock.s01e01.pilot.mkv"; + + Mocker.GetMock().Setup(s => s.GetEpisodesByParseResult(It.IsAny())) + .Returns(new List()); + + Mocker.GetMock().Setup(s => s.IsChildOfPath(path, series.Path)) + .Returns(true); + + Mocker.Resolve().ImportFile(series, path); + + Mocker.Verify(s => s.GetEpisodesByParseResult(It.Is(p => p.SceneSource == false)), Times.Once()); + } } } diff --git a/NzbDrone.Core.Test/ProviderTests/DownloadProviderFixture.cs b/NzbDrone.Core.Test/ProviderTests/DownloadProviderFixture.cs index 5b3b938de..5c224f8fb 100644 --- a/NzbDrone.Core.Test/ProviderTests/DownloadProviderFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/DownloadProviderFixture.cs @@ -52,6 +52,7 @@ private EpisodeParseResult SetupParseResult() .With(c => c.Quality = new QualityModel(QualityTypes.DVD, false)) .With(c => c.Series = Builder.CreateNew().Build()) .With(c => c.EpisodeNumbers = new List{2}) + .With(c => c.Episodes = episodes) .Build(); } @@ -191,6 +192,15 @@ public void create_proper_sab_titles(int seasons, int[] episodes, string title, .With(c => c.Title = "My Series Name") .Build(); + var fakeEpisodes = new List(); + + foreach(var episode in episodes) + fakeEpisodes.Add(Builder + .CreateNew() + .With(e => e.EpisodeNumber = episode) + .With(e => e.Title = title) + .Build()); + var parsResult = new EpisodeParseResult() { AirDate = DateTime.Now, @@ -198,7 +208,8 @@ public void create_proper_sab_titles(int seasons, int[] episodes, string title, Quality = new QualityModel(quality, proper), SeasonNumber = seasons, Series = series, - EpisodeTitle = title + EpisodeTitle = title, + Episodes = fakeEpisodes }; Mocker.Resolve().GetDownloadTitle(parsResult).Should().Be(expected); @@ -234,16 +245,49 @@ public string create_proper_sab_daily_titles(bool proper) .With(c => c.Title = "My Series Name") .Build(); + var episode = Builder.CreateNew() + .With(e => e.Title = "My Episode Title") + .Build(); + var parsResult = new EpisodeParseResult { AirDate = new DateTime(2011, 12, 1), Quality = new QualityModel(QualityTypes.Bluray720p, proper), Series = series, EpisodeTitle = "My Episode Title", + Episodes = new List{ episode } }; return Mocker.Resolve().GetDownloadTitle(parsResult); } + [Test] + public void should_not_repeat_the_same_episode_title() + { + var series = Builder.CreateNew() + .With(c => c.Title = "My Series Name") + .Build(); + + var fakeEpisodes = Builder.CreateListOfSize(2) + .All() + .With(e => e.SeasonNumber = 5) + .TheFirst(1) + .With(e => e.Title = "My Episode Title (1)") + .TheLast(1) + .With(e => e.Title = "My Episode Title (2)") + .Build(); + + var parsResult = new EpisodeParseResult + { + AirDate = DateTime.Now, + EpisodeNumbers = new List{ 10, 11 }, + Quality = new QualityModel(QualityTypes.HDTV, false), + SeasonNumber = 35, + Series = series, + Episodes = fakeEpisodes + }; + + Mocker.Resolve().GetDownloadTitle(parsResult).Should().Be("My Series Name - 5x01-5x02 - My Episode Title [HDTV]"); + } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTests/EpisodeProviderTest.cs similarity index 97% rename from NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs rename to NzbDrone.Core.Test/ProviderTests/EpisodeProviderTests/EpisodeProviderTest.cs index 125225091..3362b3e7f 100644 --- a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTests/EpisodeProviderTest.cs @@ -2,9 +2,7 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; - using FizzWare.NBuilder; using FluentAssertions; using Moq; @@ -15,11 +13,10 @@ using NzbDrone.Core.Repository; using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Test.Framework; -using NzbDrone.Test.Common.AutoMoq; using PetaPoco; -using XemLib.Data; +using TvdbLib.Data; -namespace NzbDrone.Core.Test.ProviderTests +namespace NzbDrone.Core.Test.ProviderTests.EpisodeProviderTests { [TestFixture] // ReSharper disable InconsistentNaming @@ -161,7 +158,7 @@ public void RefreshEpisodeInfo_emptyRepo() c => c.Episodes = new List(Builder.CreateListOfSize(episodeCount). All() - .With(l => l.Language = "en") + .With(l => l.Language = new TvdbLanguage(0, "eng", "a")) .Build()) ).With(c => c.Id = seriesId).Build(); @@ -172,7 +169,7 @@ public void RefreshEpisodeInfo_emptyRepo() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); //Act @@ -195,7 +192,7 @@ public void RefreshEpisodeInfo_should_set_older_than_1900_to_null() c => c.Episodes = new List(Builder.CreateListOfSize(10). All() - .With(l => l.Language = "en").And(e => e.FirstAired = DateTime.Now) + .With(l => l.Language = new TvdbLanguage(0, "eng", "a")).And(e => e.FirstAired = DateTime.Now) .TheFirst(7).With(e => e.FirstAired = new DateTime(1800, 1, 1)) .Build()) ).With(c => c.Id = seriesId).Build(); @@ -207,7 +204,7 @@ public void RefreshEpisodeInfo_should_set_older_than_1900_to_null() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -236,7 +233,7 @@ public void RefreshEpisodeInfo_should_set_older_than_1900_to_null_for_existing_e c => c.Episodes = new List(Builder.CreateListOfSize(1) .All() - .With(l => l.Language = "en").And(e => e.FirstAired = DateTime.Now) + .With(l => l.Language = new TvdbLanguage(0, "eng", "a")).And(e => e.FirstAired = DateTime.Now) .TheFirst(1).With(e => e.FirstAired = new DateTime(1800, 1, 1)) .Build()) ).With(c => c.Id = seriesId).Build(); @@ -248,7 +245,7 @@ public void RefreshEpisodeInfo_should_set_older_than_1900_to_null_for_existing_e Db.Insert(fakeEpisode); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeTvDbEpisodes); //Act @@ -271,7 +268,7 @@ public void RefreshEpisodeInfo_ignore_episode_zero() c => c.Episodes = new List(Builder.CreateListOfSize(episodeCount). All() - .With(l => l.Language = "en") + .With(l => l.Language = new TvdbLanguage(0, "eng", "a")) .TheFirst(1) .With(e => e.EpisodeNumber = 0) .With(e => e.SeasonNumber = 15) @@ -285,7 +282,7 @@ public void RefreshEpisodeInfo_ignore_episode_zero() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -324,7 +321,7 @@ public void RefreshEpisodeInfo_should_skip_future_episodes_with_no_title() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -362,7 +359,7 @@ public void RefreshEpisodeInfo_should_skip_older_than_1900_year_episodes_with_no Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -395,7 +392,7 @@ public void RefreshEpisodeInfo_should_add_future_episodes_with_title() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -428,7 +425,7 @@ public void RefreshEpisodeInfo_should_add_old_episodes_with_no_title() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); @@ -451,7 +448,7 @@ public void RefreshEpisodeInfo_ignore_season_zero() c => c.Episodes = new List(Builder.CreateListOfSize(episodeCount). All() - .With(l => l.Language = "en") + .With(l => l.Language = new TvdbLanguage(0, "eng", "a")) .With(e => e.SeasonNumber = 0) .Build()) ).With(c => c.Id = seriesId).Build(); @@ -463,7 +460,7 @@ public void RefreshEpisodeInfo_ignore_season_zero() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeEpisodes); Mocker.GetMock() @@ -493,7 +490,7 @@ public void new_episodes_only_calls_Insert() var currentEpisodes = new List(); Mocker.GetMock(MockBehavior.Strict) - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); Mocker.GetMock() @@ -528,7 +525,7 @@ public void existing_episodes_only_calls_Update() } Mocker.GetMock(MockBehavior.Strict) - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); Mocker.GetMock() @@ -565,7 +562,7 @@ public void should_try_to_get_existing_episode_using_tvdbid_first() .Returns(fakeEpisodeList); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(fakeTvDbResult); //Act @@ -602,7 +599,7 @@ public void should_try_to_get_existing_episode_using_tvdbid_first_then_season_ep var fakeSeries = Builder.CreateNew().With(c => c.SeriesId = seriesId).Build(); Mocker.GetMock(MockBehavior.Strict) - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); Mocker.GetMock() @@ -643,7 +640,7 @@ public void existing_episodes_keep_their_episodeId_file_id() } Mocker.GetMock(MockBehavior.Strict) - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); var updatedEpisodes = new List(); @@ -692,7 +689,7 @@ public void existing_episodes_remote_their_episodeId_file_id_when_episode_number } Mocker.GetMock(MockBehavior.Strict) - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); var updatedEpisodes = new List(); @@ -738,7 +735,7 @@ public void existing_episodes_remote_their_episodeId_file_id_when_season_number_ } Mocker.GetMock(MockBehavior.Strict) - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); var updatedEpisodes = new List(); @@ -777,7 +774,7 @@ public void RefreshEpisodeInfo_should_ignore_new_episode_for_ignored_season() c => c.Episodes = new List(Builder.CreateListOfSize(episodeCount). All() - .With(l => l.Language = "en") + .With(l => l.Language = new TvdbLanguage(0, "eng", "a")) .With(e => e.SeasonNumber = 5) .TheFirst(1) .With(e => e.EpisodeNumber = 1) @@ -796,7 +793,7 @@ public void RefreshEpisodeInfo_should_ignore_new_episode_for_ignored_season() Db.Insert(fakeEpisode); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); Mocker.GetMock() @@ -1487,7 +1484,7 @@ public void RefreshEpisodeInfo_should_ignore_episode_zero_except_if_season_one() c => c.Episodes = new List(Builder.CreateListOfSize(episodeCount). All() - .With(l => l.Language = "en") + .With(l => l.Language = new TvdbLanguage(0, "eng", "a")) .With(e => e.EpisodeNumber = 0) .TheFirst(1) .With(e => e.SeasonNumber = 1) @@ -1509,7 +1506,7 @@ public void RefreshEpisodeInfo_should_ignore_episode_zero_except_if_season_one() Db.Insert(fakeSeries); Mocker.GetMock() - .Setup(c => c.GetSeries(seriesId, true, false, false)) + .Setup(c => c.GetSeries(seriesId, true, false)) .Returns(tvdbSeries); //Act diff --git a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest_DeleteInvalidEpisodes.cs b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTests/EpisodeProviderTest_DeleteInvalidEpisodes.cs similarity index 94% rename from NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest_DeleteInvalidEpisodes.cs rename to NzbDrone.Core.Test/ProviderTests/EpisodeProviderTests/EpisodeProviderTest_DeleteInvalidEpisodes.cs index 2d43a5c03..70c185136 100644 --- a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest_DeleteInvalidEpisodes.cs +++ b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTests/EpisodeProviderTest_DeleteInvalidEpisodes.cs @@ -2,17 +2,15 @@ using System.Collections.Generic; using System.Linq; - using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Providers; using NzbDrone.Core.Repository; using NzbDrone.Core.Test.Framework; -using NzbDrone.Test.Common.AutoMoq; -using XemLib.Data; +using TvdbLib.Data; -namespace NzbDrone.Core.Test.ProviderTests +namespace NzbDrone.Core.Test.ProviderTests.EpisodeProviderTests { [TestFixture] // ReSharper disable InconsistentNaming @@ -27,7 +25,7 @@ public void Delete_None_Valid_TvDbEpisodeId() var tvDbSeries = Builder.CreateListOfSize(episodeCount). All() - .With(l => l.Language = "en") + .With(l => l.Language = new TvdbLanguage(0, "eng", "a")) .Build(); @@ -65,7 +63,7 @@ public void Delete_None_TvDbEpisodeId_is_zero() var tvDbSeries = Builder.CreateListOfSize(episodeCount). All() - .With(l => l.Language = "en") + .With(l => l.Language = new TvdbLanguage(0, "eng", "a")) .Build(); var fakeSeries = Builder.CreateNew() @@ -102,7 +100,7 @@ public void Delete_None_TvDbEpisodeId_is_null() var tvDbSeries = Builder.CreateListOfSize(episodeCount). All() - .With(l => l.Language = "en") + .With(l => l.Language = new TvdbLanguage(0, "eng", "a")) .Build(); var fakeSeries = Builder.CreateNew() @@ -139,7 +137,7 @@ public void Delete_TvDbId() var tvDbSeries = Builder.CreateListOfSize(episodeCount). All() - .With(l => l.Language = "en") + .With(l => l.Language = new TvdbLanguage(0, "eng", "a")) .Build(); var fakeSeries = Builder.CreateNew() @@ -179,7 +177,7 @@ public void Delete_TvDbId_multiple_series() var tvDbSeries = Builder.CreateListOfSize(episodeCount). All() - .With(l => l.Language = "en") + .With(l => l.Language = new TvdbLanguage(0, "eng", "a")) .Build(); var fakeSeries = Builder.CreateNew() diff --git a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest_GetEpisodesByParseResult.cs b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTests/EpisodeProviderTest_GetEpisodesByParseResult.cs similarity index 98% rename from NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest_GetEpisodesByParseResult.cs rename to NzbDrone.Core.Test/ProviderTests/EpisodeProviderTests/EpisodeProviderTest_GetEpisodesByParseResult.cs index 5f4b8bd4c..2d726a2fa 100644 --- a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest_GetEpisodesByParseResult.cs +++ b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTests/EpisodeProviderTest_GetEpisodesByParseResult.cs @@ -5,17 +5,14 @@ using System.Linq; using FizzWare.NBuilder; using FluentAssertions; -using Moq; using NUnit.Framework; using NzbDrone.Core.Model; using NzbDrone.Core.Providers; using NzbDrone.Core.Repository; using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common; -using NzbDrone.Test.Common.AutoMoq; -using PetaPoco; -namespace NzbDrone.Core.Test.ProviderTests +namespace NzbDrone.Core.Test.ProviderTests.EpisodeProviderTests { [TestFixture] // ReSharper disable InconsistentNaming diff --git a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTests/GetEpisodeBySceneNumberFixture.cs b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTests/GetEpisodeBySceneNumberFixture.cs new file mode 100644 index 000000000..b4a4726bc --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTests/GetEpisodeBySceneNumberFixture.cs @@ -0,0 +1,77 @@ +// ReSharper disable RedundantUsingDirective + +using System.Linq; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.ProviderTests.EpisodeProviderTests +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class GetEpisodeBySceneNumberFixture : CoreTest + { + private Series _series; + private Episode _episode; + + [SetUp] + public void Setup() + { + WithRealDb(); + + _series = Builder + .CreateNew() + .Build(); + + Db.Insert(_series); + } + + public void WithNullSceneNumbering() + { + _episode = Builder + .CreateNew() + .With(e => e.SeriesId = _series.SeriesId) + .Build(); + + Db.Insert(_episode); + Db.Execute("UPDATE Episodes SET SceneSeasonNumber = NULL, SceneEpisodeNumber = NULL"); + } + + public void WithSceneNumbering() + { + _episode = Builder + .CreateNew() + .With(e => e.SeriesId = _series.SeriesId) + .Build(); + + Db.Insert(_episode); + } + + [Test] + public void should_return_null_if_no_episodes_in_db() + { + Mocker.Resolve().GetEpisodeBySceneNumbering(_series.SeriesId, 1, 1).Should().BeNull(); + } + + [Test] + public void should_return_null_if_no_matching_episode_is_found() + { + WithNullSceneNumbering(); + Mocker.Resolve().GetEpisodeBySceneNumbering(_series.SeriesId, 1, 1).Should().BeNull(); + } + + [Test] + public void should_return_episode_if_matching_episode_is_found() + { + WithSceneNumbering(); + + var result = Mocker.Resolve() + .GetEpisodeBySceneNumbering(_series.SeriesId, _episode.SceneSeasonNumber, _episode.SceneEpisodeNumber); + + result.EpisodeId.Should().Be(_episode.EpisodeId); + } + } +} diff --git a/NzbDrone.Core.Test/ProviderTests/MediaFileProviderTests/GetNewFilenameFixture.cs b/NzbDrone.Core.Test/ProviderTests/MediaFileProviderTests/GetNewFilenameFixture.cs index 86d60ec7c..51cdbb0b7 100644 --- a/NzbDrone.Core.Test/ProviderTests/MediaFileProviderTests/GetNewFilenameFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/MediaFileProviderTests/GetNewFilenameFixture.cs @@ -699,5 +699,43 @@ public void should_have_two_episodeTitles_when_episode_titles_are_not_the_same() //Assert result.Should().Be("30 Rock - S06E06-E07 - Hello + World"); } + + [Test] + public void should_have_two_episodeTitles_when_distinct_count_is_two() + { + //Setup + var fakeConfig = Mocker.GetMock(); + fakeConfig.SetupGet(c => c.SortingIncludeSeriesName).Returns(true); + fakeConfig.SetupGet(c => c.SortingIncludeEpisodeTitle).Returns(true); + fakeConfig.SetupGet(c => c.SortingAppendQuality).Returns(false); + fakeConfig.SetupGet(c => c.SortingSeparatorStyle).Returns(0); + fakeConfig.SetupGet(c => c.SortingNumberStyle).Returns(2); + fakeConfig.SetupGet(c => c.SortingReplaceSpaces).Returns(false); + fakeConfig.SetupGet(c => c.SortingMultiEpisodeStyle).Returns(3); + + var episode = Builder.CreateNew() + .With(e => e.Title = "Hello (3)") + .With(e => e.SeasonNumber = 6) + .With(e => e.EpisodeNumber = 6) + .Build(); + + var episode2 = Builder.CreateNew() + .With(e => e.Title = "Hello (2)") + .With(e => e.SeasonNumber = 6) + .With(e => e.EpisodeNumber = 7) + .Build(); + + var episode3 = Builder.CreateNew() + .With(e => e.Title = "World") + .With(e => e.SeasonNumber = 6) + .With(e => e.EpisodeNumber = 8) + .Build(); + + //Act + string result = Mocker.Resolve().GetNewFilename(new List { episode, episode2, episode3 }, "30 Rock", QualityTypes.HDTV, false, new EpisodeFile()); + + //Assert + result.Should().Be("30 Rock - S06E06-E07-E08 - Hello + World"); + } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForEpisodeFile_Fixture.cs b/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForEpisodeFile_Fixture.cs index c4822c146..48b8ed3fc 100644 --- a/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForEpisodeFile_Fixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForEpisodeFile_Fixture.cs @@ -17,8 +17,8 @@ using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common.AutoMoq; using NzbDrone.Test.Common; -using XemLib.Data; -using XemLib.Data.Banner; +using TvdbLib.Data; +using TvdbLib.Data.Banner; namespace NzbDrone.Core.Test.ProviderTests.Metadata { @@ -52,30 +52,31 @@ public void Setup() .With(e => e.SeriesId = 79488) .With(e => e.SeasonNumber = 1) .With(e => e.Directors = new List{ "Fake Director" }) - .With(e => e.Writers = new List{ "Fake Writer" }) + .With(e => e.Writer = new List{ "Fake Writer" }) .With(e => e.GuestStars = new List { "Guest Star 1", "Guest Star 2", "Guest Star 3", "" }) .Build(); var seasonBanners = Builder .CreateListOfSize(4) .TheFirst(2) - .With(b => b.SeasonNumber = 1) + .With(b => b.Season = 1) .TheLast(2) - .With(b => b.SeasonNumber = 2) + .With(b => b.Season = 2) .TheFirst(1) - .With(b => b.BannerType = TvdbSeasonBanner.Type.Poster) + .With(b => b.BannerType = TvdbSeasonBanner.Type.season) .With(b => b.BannerPath = "seasons/79488-1-1.jpg") .TheNext(2) - .With(b => b.BannerType = TvdbSeasonBanner.Type.Banner) + .With(b => b.BannerType = TvdbSeasonBanner.Type.seasonwide) .With(b => b.BannerPath = "banners/seasons/79488-test.jpg") .TheLast(1) - .With(b => b.BannerType = TvdbSeasonBanner.Type.Poster) + .With(b => b.BannerType = TvdbSeasonBanner.Type.season) .With(b => b.BannerPath = "seasons/79488-2-1.jpg") .Build(); var seriesActors = Builder .CreateListOfSize(5) .All() + .With(a => a.ActorImage = Builder.CreateNew().Build()) .Build(); tvdbSeries = Builder @@ -84,10 +85,9 @@ public void Setup() .With(s => s.SeriesName = "30 Rock") .With(s => s.TvdbActors = seriesActors.ToList()) .With(s => s.Episodes = tvdbEpisodes.ToList()) - .With(s => s.Banners = new TvdbBanners()) .Build(); - tvdbSeries.Banners.SeasonBanners.AddRange(seasonBanners); + tvdbSeries.Banners.AddRange(seasonBanners); } private void WithUseBanners() @@ -128,7 +128,7 @@ private void WithNoDirectors() private void WithNoWriters() { - tvdbSeries.Episodes.ForEach(e => e.Writers = new List()); + tvdbSeries.Episodes.ForEach(e => e.Writer = new List()); } [Test] @@ -159,7 +159,7 @@ public void should_download_thumbnail_when_thumbnail_path_is_not_null() { WithSingleEpisodeFile(); Mocker.Resolve().CreateForEpisodeFile(episodeFile, tvdbSeries); - Mocker.GetMock().Verify(v => v.Download(tvdbSeries.Episodes.First().Banner, episodeFile.Path.Replace("avi", "tbn")), Times.Once()); + Mocker.GetMock().Verify(v => v.Download(tvdbSeries.Episodes.First().BannerPath, episodeFile.Path.Replace("avi", "tbn")), Times.Once()); } [Test] diff --git a/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForSeries_Fixture.cs b/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForSeries_Fixture.cs index 6151bfc11..1a55c0eb7 100644 --- a/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForSeries_Fixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/Metadata/Xbmc_ForSeries_Fixture.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; @@ -17,8 +16,8 @@ using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common.AutoMoq; using NzbDrone.Test.Common; -using XemLib.Data; -using XemLib.Data.Banner; +using TvdbLib.Data; +using TvdbLib.Data.Banner; namespace NzbDrone.Core.Test.ProviderTests.Metadata { @@ -43,23 +42,24 @@ public void Setup() var seasonBanners = Builder .CreateListOfSize(4) .TheFirst(2) - .With(b => b.SeasonNumber = 1) + .With(b => b.Season = 1) .TheLast(2) - .With(b => b.SeasonNumber = 2) + .With(b => b.Season = 2) .TheFirst(1) - .With(b => b.BannerType = TvdbSeasonBanner.Type.Poster) + .With(b => b.BannerType = TvdbSeasonBanner.Type.season) .With(b => b.BannerPath = "seasons/79488-1-1.jpg") .TheNext(2) - .With(b => b.BannerType = TvdbSeasonBanner.Type.Banner) + .With(b => b.BannerType = TvdbSeasonBanner.Type.seasonwide) .With(b => b.BannerPath = "banners/seasons/79488-test.jpg") .TheLast(1) - .With(b => b.BannerType = TvdbSeasonBanner.Type.Poster) + .With(b => b.BannerType = TvdbSeasonBanner.Type.season) .With(b => b.BannerPath = "seasons/79488-2-1.jpg") .Build(); var seriesActors = Builder .CreateListOfSize(5) .All() + .With(a => a.ActorImage = Builder.CreateNew().Build()) .Build(); tvdbSeries = Builder @@ -67,10 +67,9 @@ public void Setup() .With(s => s.Id = 79488) .With(s => s.SeriesName = "30 Rock") .With(s => s.TvdbActors = seriesActors.ToList()) - .With(s => s.Banners = new TvdbBanners()) .Build(); - tvdbSeries.Banners.SeasonBanners.AddRange(seasonBanners); + tvdbSeries.Banners.AddRange(seasonBanners); } private void WithUseBanners() @@ -83,18 +82,19 @@ private void WithSpecials() var seasonBanners = Builder .CreateListOfSize(2) .All() - .With(b => b.SeasonNumber = 0) + .With(b => b.Season = 0) .TheFirst(1) - .With(b => b.BannerType = TvdbSeasonBanner.Type.Poster) + .With(b => b.BannerType = TvdbSeasonBanner.Type.season) .With(b => b.BannerPath = "seasons/79488-0-1.jpg") .TheLast(1) - .With(b => b.BannerType = TvdbSeasonBanner.Type.Banner) + .With(b => b.BannerType = TvdbSeasonBanner.Type.seasonwide) .With(b => b.BannerPath = "banners/seasons/79488-0-1.jpg") .Build(); var seriesActors = Builder .CreateListOfSize(5) .All() + .With(a => a.ActorImage = Builder.CreateNew().Build()) .Build(); tvdbSeries = Builder @@ -102,11 +102,9 @@ private void WithSpecials() .With(s => s.Id = 79488) .With(s => s.SeriesName = "30 Rock") .With(s => s.TvdbActors = seriesActors.ToList()) - .With(s => s.Banners = new TvdbBanners()) - .With(s => s.Genres = new List { "Comedy" }) .Build(); - tvdbSeries.Banners.SeasonBanners.AddRange(seasonBanners); + tvdbSeries.Banners.AddRange(seasonBanners); } [Test] @@ -126,14 +124,14 @@ public void should_call_diskprovider_writeAllText() public void should_download_fanart() { Mocker.Resolve().CreateForSeries(series, tvdbSeries); - Mocker.GetMock().Verify(v => v.Download(tvdbSeries.Fanart, Path.Combine(series.Path, "fanart.jpg")), Times.Once()); + Mocker.GetMock().Verify(v => v.Download(tvdbSeries.FanartPath, Path.Combine(series.Path, "fanart.jpg")), Times.Once()); } [Test] public void should_download_poster_when_useBanners_is_false() { Mocker.Resolve().CreateForSeries(series, tvdbSeries); - Mocker.GetMock().Verify(v => v.Download(tvdbSeries.Poster, Path.Combine(series.Path, "folder.jpg")), Times.Once()); + Mocker.GetMock().Verify(v => v.Download(tvdbSeries.PosterPath, Path.Combine(series.Path, "folder.jpg")), Times.Once()); } [Test] @@ -141,7 +139,7 @@ public void should_download_banner_when_useBanners_is_true() { WithUseBanners(); Mocker.Resolve().CreateForSeries(series, tvdbSeries); - Mocker.GetMock().Verify(v => v.Download(tvdbSeries.Banner, Path.Combine(series.Path, "folder.jpg")), Times.Once()); + Mocker.GetMock().Verify(v => v.Download(tvdbSeries.BannerPath, Path.Combine(series.Path, "folder.jpg")), Times.Once()); } [Test] diff --git a/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/CleanupFixture.cs b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/CleanupFixture.cs index 26e367da3..dd4255406 100644 --- a/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/CleanupFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/CleanupFixture.cs @@ -18,6 +18,7 @@ using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common.AutoMoq; using PetaPoco; +using TvdbLib.Data; namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests { diff --git a/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteDirectoryFixture.cs b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteDirectoryFixture.cs index 162baf1e8..e4cd1fd3b 100644 --- a/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteDirectoryFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteDirectoryFixture.cs @@ -18,6 +18,7 @@ using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common.AutoMoq; using PetaPoco; +using TvdbLib.Data; namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests { diff --git a/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteFileFixture.cs b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteFileFixture.cs index 3f4310402..5ac5c3378 100644 --- a/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteFileFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteFileFixture.cs @@ -18,6 +18,7 @@ using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common.AutoMoq; using PetaPoco; +using TvdbLib.Data; namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests { diff --git a/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/EmptyFixture.cs b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/EmptyFixture.cs index 4b7853392..1835dda2a 100644 --- a/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/EmptyFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/EmptyFixture.cs @@ -18,6 +18,7 @@ using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common.AutoMoq; using PetaPoco; +using TvdbLib.Data; namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests { diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/SearchFixture.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/SearchFixture.cs index 11b119803..2f23e3ed8 100644 --- a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/SearchFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/SearchFixture.cs @@ -185,7 +185,7 @@ public void PartialSeasonSearch_should_skip_daily_series() } [Test] - public void EpisodeSearch_should_skip_if_air_date_is_null() + public void EpisodeSearch_should_skip_if_air_date_is_null_and_is_a_daily_series() { //Setup _series.IsDaily = true; diff --git a/NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs index 248fb82dc..fa400ab90 100644 --- a/NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs @@ -16,6 +16,7 @@ using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common.AutoMoq; using PetaPoco; +using TvdbLib.Data; namespace NzbDrone.Core.Test.ProviderTests { diff --git a/NzbDrone.Core.Test/ProviderTests/TvDbProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/TvDbProviderTest.cs index 42232678e..413a5abc9 100644 --- a/NzbDrone.Core.Test/ProviderTests/TvDbProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/TvDbProviderTest.cs @@ -10,8 +10,8 @@ using NzbDrone.Core.Providers; using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common; -using XemLib.Data; -using XemLib.Exceptions; +using TvdbLib.Data; +using TvdbLib.Exceptions; namespace NzbDrone.Core.Test.ProviderTests { @@ -30,7 +30,7 @@ public void Setup() [TearDown] public void TearDown() { - ExceptionVerification.MarkInconclusive(typeof(TheTvbdbUnavailableException)); + ExceptionVerification.MarkInconclusive(typeof(TvdbNotAvailableException)); } [TestCase("The Simpsons")] diff --git a/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetSceneTvdbMappingsFixture.cs b/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetSceneTvdbMappingsFixture.cs new file mode 100644 index 000000000..f5ffc95ae --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetSceneTvdbMappingsFixture.cs @@ -0,0 +1,77 @@ +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.Repository; +using NzbDrone.Core.Repository.Quality; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common.AutoMoq; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.ProviderTests.XemCommunicationProviderTests +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class GetSceneTvdbMappingsFixture : CoreTest + { + private void WithFailureJson() + { + Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) + .Returns(File.ReadAllText(@".\Files\Xem\Failure.txt")); + } + + private void WithIdsJson() + { + Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) + .Returns(File.ReadAllText(@".\Files\Xem\Ids.txt")); + } + + private void WithMappingsJson() + { + Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) + .Returns(File.ReadAllText(@".\Files\Xem\Mappings.txt")); + } + + [Test] + public void should_throw_when_failure_is_found() + { + WithFailureJson(); + Assert.Throws(() => Mocker.Resolve().GetSceneTvdbMappings(12345)); + } + + [Test] + public void should_get_list_of_mappings() + { + WithMappingsJson(); + Mocker.Resolve().GetSceneTvdbMappings(12345).Should().NotBeEmpty(); + } + + [Test] + public void should_have_two_mappings() + { + WithMappingsJson(); + Mocker.Resolve().GetSceneTvdbMappings(12345).Should().HaveCount(2); + } + + [Test] + public void should_have_expected_results() + { + WithMappingsJson(); + var results = Mocker.Resolve().GetSceneTvdbMappings(12345); + var first = results.First(); + first.Scene.Absolute.Should().Be(1); + first.Scene.Season.Should().Be(1); + first.Scene.Episode.Should().Be(1); + first.Tvdb.Absolute.Should().Be(1); + first.Tvdb.Season.Should().Be(1); + first.Tvdb.Episode.Should().Be(1); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetXemSeriesIdsFixture.cs b/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetXemSeriesIdsFixture.cs new file mode 100644 index 000000000..55c6885de --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetXemSeriesIdsFixture.cs @@ -0,0 +1,63 @@ +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.Repository; +using NzbDrone.Core.Repository.Quality; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common.AutoMoq; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.ProviderTests.XemCommunicationProviderTests +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class GetXemSeriesIdsFixture : CoreTest + { + private void WithFailureJson() + { + Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) + .Returns(File.ReadAllText(@".\Files\Xem\Failure.txt")); + } + + private void WithIdsJson() + { + Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) + .Returns(File.ReadAllText(@".\Files\Xem\Ids.txt")); + } + + private void WithMappingsJson() + { + Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) + .Returns(File.ReadAllText(@".\Files\Xem\Mappings.txt")); + } + + [Test] + public void should_throw_when_failure_is_found() + { + WithFailureJson(); + Assert.Throws(() => Mocker.Resolve().GetXemSeriesIds()); + } + + [Test] + public void should_get_list_of_int() + { + WithIdsJson(); + Mocker.Resolve().GetXemSeriesIds().Should().NotBeEmpty(); + } + + [Test] + public void should_have_two_ids() + { + WithIdsJson(); + Mocker.Resolve().GetXemSeriesIds().Should().HaveCount(2); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/CentralDispatch.cs b/NzbDrone.Core/CentralDispatch.cs index 733fc839c..64e631e6a 100644 --- a/NzbDrone.Core/CentralDispatch.cs +++ b/NzbDrone.Core/CentralDispatch.cs @@ -137,6 +137,7 @@ private void InitJobs() Kernel.Bind().To().InSingletonScope(); Kernel.Bind().To().InSingletonScope(); Kernel.Bind().To().InSingletonScope(); + Kernel.Bind().To().InSingletonScope(); Kernel.Get().Initialize(); Kernel.Get().StartTimer(30); diff --git a/NzbDrone.Core/Datastore/Migrations/Migration20121016.cs b/NzbDrone.Core/Datastore/Migrations/Migration20121016.cs new file mode 100644 index 000000000..0a8c55e5b --- /dev/null +++ b/NzbDrone.Core/Datastore/Migrations/Migration20121016.cs @@ -0,0 +1,20 @@ +using System; +using System.Data; +using Migrator.Framework; +using NzbDrone.Common; + +namespace NzbDrone.Core.Datastore.Migrations +{ + [Migration(20121016)] + public class Migration20121016 : NzbDroneMigration + { + protected override void MainDbUpgrade() + { + Database.AddColumn("Episodes", new Column("SceneAbsoluteEpisodeNumber", DbType.Int32, ColumnProperty.Null)); + Database.AddColumn("Episodes", new Column("SceneSeasonNumber", DbType.Int32, ColumnProperty.Null)); + Database.AddColumn("Episodes", new Column("SceneEpisodeNumber", DbType.Int32, ColumnProperty.Null)); + + Database.AddColumn("Series", new Column("UseSceneNumbering", DbType.Boolean, ColumnProperty.Null)); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/ImportNewSeriesJob.cs b/NzbDrone.Core/Jobs/ImportNewSeriesJob.cs index fdba25b51..fea7cf090 100644 --- a/NzbDrone.Core/Jobs/ImportNewSeriesJob.cs +++ b/NzbDrone.Core/Jobs/ImportNewSeriesJob.cs @@ -22,6 +22,7 @@ public class ImportNewSeriesJob : IJob private readonly DiskScanJob _diskScanJob; private readonly BannerDownloadJob _bannerDownloadJob; private readonly SeasonProvider _seasonProvider; + private readonly XemUpdateJob _xemUpdateJob; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); @@ -30,7 +31,8 @@ public class ImportNewSeriesJob : IJob [Inject] public ImportNewSeriesJob(SeriesProvider seriesProvider, EpisodeProvider episodeProvider, MediaFileProvider mediaFileProvider, UpdateInfoJob updateInfoJob, - DiskScanJob diskScanJob, BannerDownloadJob bannerDownloadJob,SeasonProvider seasonProvider) + DiskScanJob diskScanJob, BannerDownloadJob bannerDownloadJob, + SeasonProvider seasonProvider, XemUpdateJob xemUpdateJob) { _seriesProvider = seriesProvider; _episodeProvider = episodeProvider; @@ -39,6 +41,7 @@ public ImportNewSeriesJob(SeriesProvider seriesProvider, EpisodeProvider episode _diskScanJob = diskScanJob; _bannerDownloadJob = bannerDownloadJob; _seasonProvider = seasonProvider; + _xemUpdateJob = xemUpdateJob; } public string Name @@ -81,6 +84,9 @@ private void ScanSeries(ProgressNotification notification) //Download the banner for the new series _bannerDownloadJob.Start(notification, new { SeriesId = updatedSeries.SeriesId }); + //Get Scene Numbering if applicable + _xemUpdateJob.Start(notification, new { SeriesId = updatedSeries.SeriesId }); + notification.CurrentMessage = String.Format("{0} was successfully imported", updatedSeries.Title); } diff --git a/NzbDrone.Core/Jobs/XemUpdateJob.cs b/NzbDrone.Core/Jobs/XemUpdateJob.cs new file mode 100644 index 000000000..56066fec2 --- /dev/null +++ b/NzbDrone.Core/Jobs/XemUpdateJob.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NLog; +using Ninject; +using NzbDrone.Core.Helpers; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Repository; + +namespace NzbDrone.Core.Jobs +{ + public class XemUpdateJob : IJob + { + private readonly XemProvider _xemProvider; + + private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); + + [Inject] + public XemUpdateJob(XemProvider xemProvider) + { + _xemProvider = xemProvider; + } + + public XemUpdateJob() + { + + } + + public string Name + { + get { return "XEM Update"; } + } + + public TimeSpan DefaultInterval + { + get { return TimeSpan.FromHours(12); } + } + + public virtual void Start(ProgressNotification notification, dynamic options) + { + if (options == null || options.SeriesId == 0) + { + _logger.Trace("Starting XEM Update for all series"); + _xemProvider.UpdateMappings(); + } + + else + { + _logger.Trace("Starting XEM Update for series: {0}", options.SeriesId); + _xemProvider.UpdateMappings(options.SeriesId); + } + + _logger.Trace("XEM Update complete"); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Model/EpisodeParseResult.cs b/NzbDrone.Core/Model/EpisodeParseResult.cs index 12a8201a7..991e7a16f 100644 --- a/NzbDrone.Core/Model/EpisodeParseResult.cs +++ b/NzbDrone.Core/Model/EpisodeParseResult.cs @@ -46,6 +46,10 @@ public string CleanTitle public string ReleaseGroup { get; set; } + public bool SceneSource { get; set; } + + public IList Episodes { get; set; } + public override string ToString() { diff --git a/NzbDrone.Core/Model/Xem/XemResult.cs b/NzbDrone.Core/Model/Xem/XemResult.cs new file mode 100644 index 000000000..408aa6551 --- /dev/null +++ b/NzbDrone.Core/Model/Xem/XemResult.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Model.Xem +{ + public class XemResult + { + public string Result { get; set; } + public T Data { get; set; } + public string Message { get; set; } + } +} diff --git a/NzbDrone.Core/Model/Xem/XemSceneTvdbMapping.cs b/NzbDrone.Core/Model/Xem/XemSceneTvdbMapping.cs new file mode 100644 index 000000000..29e242e5b --- /dev/null +++ b/NzbDrone.Core/Model/Xem/XemSceneTvdbMapping.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Model.Xem +{ + public class XemSceneTvdbMapping + { + public XemValues Scene { get; set; } + public XemValues Tvdb { get; set; } + } +} diff --git a/NzbDrone.Core/Model/Xem/XemValues.cs b/NzbDrone.Core/Model/Xem/XemValues.cs new file mode 100644 index 000000000..781fbd5c4 --- /dev/null +++ b/NzbDrone.Core/Model/Xem/XemValues.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Model.Xem +{ + public class XemValues + { + public int Season { get; set; } + public int Episode { get; set; } + public int Absolute { get; set; } + } +} diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index ef19116ad..275cbbb70 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -206,6 +206,10 @@ + + False + ..\Libraries\TvdbLib.dll + ..\packages\twitterizer.2.4.0.26532\lib\net40\Twitterizer2.dll @@ -213,9 +217,6 @@ False ..\packages\WebActivator.1.5\lib\net40\WebActivator.dll - - ..\Libraries\XemLib\XemLib.dll - @@ -226,6 +227,7 @@ + @@ -256,6 +258,7 @@ + @@ -291,6 +294,9 @@ + + + @@ -329,6 +335,8 @@ + + diff --git a/NzbDrone.Core/Providers/DecisionEngine/MonitoredEpisodeSpecification.cs b/NzbDrone.Core/Providers/DecisionEngine/MonitoredEpisodeSpecification.cs index 713b556e3..d92e79ae5 100644 --- a/NzbDrone.Core/Providers/DecisionEngine/MonitoredEpisodeSpecification.cs +++ b/NzbDrone.Core/Providers/DecisionEngine/MonitoredEpisodeSpecification.cs @@ -42,6 +42,7 @@ public virtual bool IsSatisfiedBy(EpisodeParseResult subject) } var episodes = _episodeProvider.GetEpisodesByParseResult(subject); + subject.Episodes = episodes; //return monitored if any of the episodes are monitored if (episodes.Any(episode => !episode.Ignored)) diff --git a/NzbDrone.Core/Providers/DecisionEngine/UpgradeDiskSpecification.cs b/NzbDrone.Core/Providers/DecisionEngine/UpgradeDiskSpecification.cs index 0f080364e..23e7b7e68 100644 --- a/NzbDrone.Core/Providers/DecisionEngine/UpgradeDiskSpecification.cs +++ b/NzbDrone.Core/Providers/DecisionEngine/UpgradeDiskSpecification.cs @@ -24,7 +24,7 @@ public UpgradeDiskSpecification() public virtual bool IsSatisfiedBy(EpisodeParseResult subject) { - foreach (var file in _episodeProvider.GetEpisodesByParseResult(subject).Select(c => c.EpisodeFile).Where(c => c != null)) + foreach (var file in subject.Episodes.Select(c => c.EpisodeFile).Where(c => c != null)) { logger.Trace("Comparing file quality with report. Existing file is {0} proper:{1}", file.Quality, file.Proper); if (!_qualityUpgradeSpecification.IsSatisfiedBy(new QualityModel { Quality = file.Quality, Proper = file.Proper }, subject.Quality, subject.Series.QualityProfile.Cutoff)) diff --git a/NzbDrone.Core/Providers/DecisionEngine/UpgradeHistorySpecification.cs b/NzbDrone.Core/Providers/DecisionEngine/UpgradeHistorySpecification.cs index 86c61c586..11bcb161b 100644 --- a/NzbDrone.Core/Providers/DecisionEngine/UpgradeHistorySpecification.cs +++ b/NzbDrone.Core/Providers/DecisionEngine/UpgradeHistorySpecification.cs @@ -27,7 +27,7 @@ public UpgradeHistorySpecification() public virtual bool IsSatisfiedBy(EpisodeParseResult subject) { - foreach (var episode in _episodeProvider.GetEpisodesByParseResult(subject)) + foreach (var episode in subject.Episodes) { var bestQualityInHistory = _historyProvider.GetBestQualityInHistory(subject.Series.SeriesId, episode.SeasonNumber, episode.EpisodeNumber); if (bestQualityInHistory != null) diff --git a/NzbDrone.Core/Providers/DiskScanProvider.cs b/NzbDrone.Core/Providers/DiskScanProvider.cs index 7809f6039..1258a59a5 100644 --- a/NzbDrone.Core/Providers/DiskScanProvider.cs +++ b/NzbDrone.Core/Providers/DiskScanProvider.cs @@ -122,6 +122,9 @@ public virtual EpisodeFile ImportFile(Series series, string filePath) if (parseResult == null) return null; + if (!_diskProvider.IsChildOfPath(filePath, series.Path)) + parseResult.SceneSource = true; + parseResult.SeriesTitle = series.Title; //replaces the nasty path as title to help with logging parseResult.Series = series; @@ -203,6 +206,7 @@ public virtual EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, bool newDown var parseResult = Parser.ParsePath(episodeFile.Path); parseResult.Series = series; parseResult.Quality = new QualityModel{ Quality = episodeFile.Quality, Proper = episodeFile.Proper }; + parseResult.Episodes = episodes; var message = _downloadProvider.GetDownloadTitle(parseResult); diff --git a/NzbDrone.Core/Providers/DownloadProvider.cs b/NzbDrone.Core/Providers/DownloadProvider.cs index 64828eefa..dde52a4e9 100644 --- a/NzbDrone.Core/Providers/DownloadProvider.cs +++ b/NzbDrone.Core/Providers/DownloadProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Ninject; using NLog; using NzbDrone.Core.Model; @@ -48,13 +49,13 @@ public virtual bool DownloadReport(EpisodeParseResult parseResult) var provider = GetActiveDownloadClient(); - bool success = provider.DownloadNzb(parseResult.NzbUrl, GetDownloadTitle(parseResult)); + bool success = provider.DownloadNzb(parseResult.NzbUrl, downloadTitle); if (success) { logger.Trace("Download added to Queue: {0}", downloadTitle); - foreach (var episode in _episodeProvider.GetEpisodesByParseResult(parseResult)) + foreach (var episode in parseResult.Episodes) { var history = new History { @@ -115,7 +116,7 @@ public virtual String GetDownloadTitle(EpisodeParseResult parseResult) if (parseResult.Series.IsDaily) { var dailyResult = String.Format("{0} - {1:yyyy-MM-dd} - {2} [{3}]", seriesTitle, - parseResult.AirDate, parseResult.EpisodeTitle, parseResult.Quality.Quality); + parseResult.AirDate, parseResult.Episodes.First().Title, parseResult.Quality.Quality); if (parseResult.Quality.Proper) dailyResult += " [Proper]"; @@ -126,15 +127,25 @@ public virtual String GetDownloadTitle(EpisodeParseResult parseResult) //Show Name - 1x01-1x02 - Episode Name //Show Name - 1x01 - Episode Name var episodeString = new List(); + var episodeNames = new List(); - foreach (var episode in parseResult.EpisodeNumbers) + foreach (var episode in parseResult.Episodes) { - episodeString.Add(String.Format("{0}x{1:00}", parseResult.SeasonNumber, episode)); + episodeString.Add(String.Format("{0}x{1:00}", episode.SeasonNumber, episode.EpisodeNumber)); + episodeNames.Add(Parser.CleanupEpisodeTitle(episode.Title)); } var epNumberString = String.Join("-", episodeString); + string episodeName; - var result = String.Format("{0} - {1} - {2} [{3}]", seriesTitle, epNumberString, parseResult.EpisodeTitle, parseResult.Quality.Quality); + + if (episodeNames.Distinct().Count() == 1) + episodeName = episodeNames.First(); + + else + episodeName = String.Join(" + ", episodeNames.Distinct()); + + var result = String.Format("{0} - {1} - {2} [{3}]", seriesTitle, epNumberString, episodeName, parseResult.Quality.Quality); if (parseResult.Quality.Proper) { diff --git a/NzbDrone.Core/Providers/EpisodeProvider.cs b/NzbDrone.Core/Providers/EpisodeProvider.cs index b8221b152..1e44607aa 100644 --- a/NzbDrone.Core/Providers/EpisodeProvider.cs +++ b/NzbDrone.Core/Providers/EpisodeProvider.cs @@ -7,7 +7,7 @@ using NzbDrone.Core.Model; using NzbDrone.Core.Repository; using PetaPoco; -using XemLib.Data; +using TvdbLib.Data; namespace NzbDrone.Core.Providers { @@ -188,11 +188,20 @@ public virtual IList GetEpisodesByParseResult(EpisodeParseResult parseR foreach (var episodeNumber in parseResult.EpisodeNumbers) { - var episodeInfo = GetEpisode(parseResult.Series.SeriesId, parseResult.SeasonNumber, episodeNumber); - if (episodeInfo == null && parseResult.AirDate != null) + Episode episodeInfo = null; + + if (parseResult.SceneSource && parseResult.Series.UseSceneNumbering) + episodeInfo = GetEpisodeBySceneNumbering(parseResult.Series.SeriesId, parseResult.SeasonNumber, episodeNumber); + + if (episodeInfo == null) { - episodeInfo = GetEpisode(parseResult.Series.SeriesId, parseResult.AirDate.Value); + episodeInfo = GetEpisode(parseResult.Series.SeriesId, parseResult.SeasonNumber, episodeNumber); + if (episodeInfo == null && parseResult.AirDate != null) + { + episodeInfo = GetEpisode(parseResult.Series.SeriesId, parseResult.AirDate.Value); + } } + //if still null we should add the temp episode if (episodeInfo == null && autoAddNew) { @@ -217,6 +226,16 @@ public virtual IList GetEpisodesByParseResult(EpisodeParseResult parseR { result.Add(episodeInfo); + if (parseResult.Series.UseSceneNumbering) + { + logger.Info("Using Scene to TVDB Mapping for: {0} - Scene: {1}x{2:00} - TVDB: {3}x{4:00}", + parseResult.Series.Title, + episodeInfo.SceneSeasonNumber, + episodeInfo.SceneEpisodeNumber, + episodeInfo.SeasonNumber, + episodeInfo.EpisodeNumber); + } + if (parseResult.EpisodeNumbers.Count == 1) { parseResult.EpisodeTitle = episodeInfo.Title.Trim(); @@ -331,7 +350,7 @@ public virtual void RefreshEpisodeInfo(Series series) episodeToUpdate.TvDbEpisodeId = episode.Id; episodeToUpdate.EpisodeNumber = episode.EpisodeNumber; episodeToUpdate.SeasonNumber = episode.SeasonNumber; - episodeToUpdate.AbsoluteEpisodeNumber = episode.AbsoluteEpisodeNumber; + episodeToUpdate.AbsoluteEpisodeNumber = episode.AbsoluteNumber; episodeToUpdate.Title = episode.EpisodeName; episodeToUpdate.Overview = episode.Overview.Truncate(3500); @@ -435,5 +454,26 @@ public virtual void SetPostDownloadStatus(List episodeIds, PostDownloadStat logger.Trace("Updating PostDownloadStatus for all episodeIds in {0}", episodeIdString); _database.Execute(episodeIdQuery); } + + public virtual void UpdateEpisodes(List episodes) + { + _database.UpdateMany(episodes); + } + + public virtual Episode GetEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber) + { + var episode = _database.Fetch(@"SELECT * FROM Episodes + INNER JOIN Series ON Episodes.SeriesId = Series.SeriesId + LEFT JOIN EpisodeFiles ON Episodes.EpisodeFileId = EpisodeFiles.EpisodeFileId + WHERE Episodes.SeriesId = @0 AND Episodes.SceneSeasonNumber = @1 AND Episodes.SceneEpisodeNumber = @2", seriesId, seasonNumber, episodeNumber).SingleOrDefault(); + + if (episode == null) + return null; + + if (episode.EpisodeFileId == 0) + episode.EpisodeFile = null; + + return episode; + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/Indexer/IndexerBase.cs b/NzbDrone.Core/Providers/Indexer/IndexerBase.cs index 76243629e..11b8c2d91 100644 --- a/NzbDrone.Core/Providers/Indexer/IndexerBase.cs +++ b/NzbDrone.Core/Providers/Indexer/IndexerBase.cs @@ -247,6 +247,7 @@ public EpisodeParseResult ParseFeed(SyndicationItem item) { episodeParseResult.Age = DateTime.Now.Date.Subtract(item.PublishDate.Date).Days; episodeParseResult.OriginalString = title; + episodeParseResult.SceneSource = true; } _logger.Trace("Parsed: {0} from: {1}", episodeParseResult, item.Title.Text); diff --git a/NzbDrone.Core/Providers/MediaFileProvider.cs b/NzbDrone.Core/Providers/MediaFileProvider.cs index c0d49e097..fe2bfab4a 100644 --- a/NzbDrone.Core/Providers/MediaFileProvider.cs +++ b/NzbDrone.Core/Providers/MediaFileProvider.cs @@ -208,7 +208,7 @@ public virtual string GetNewFilename(IList episodes, string seriesTitle result += separatorStyle.Pattern + episodeNames.First(); else - result += separatorStyle.Pattern + String.Join(" + ", episodeNames); + result += separatorStyle.Pattern + String.Join(" + ", episodeNames.Distinct()); } if (_configProvider.SortingAppendQuality) diff --git a/NzbDrone.Core/Providers/Metadata/MetadataBase.cs b/NzbDrone.Core/Providers/Metadata/MetadataBase.cs index ee9de7e06..740785acc 100644 --- a/NzbDrone.Core/Providers/Metadata/MetadataBase.cs +++ b/NzbDrone.Core/Providers/Metadata/MetadataBase.cs @@ -4,7 +4,7 @@ using NzbDrone.Core.Model; using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Repository; -using XemLib.Data; +using TvdbLib.Data; namespace NzbDrone.Core.Providers.Metadata { diff --git a/NzbDrone.Core/Providers/Metadata/Xbmc.cs b/NzbDrone.Core/Providers/Metadata/Xbmc.cs index 44f9c9e5e..8182cc235 100644 --- a/NzbDrone.Core/Providers/Metadata/Xbmc.cs +++ b/NzbDrone.Core/Providers/Metadata/Xbmc.cs @@ -8,8 +8,8 @@ using NzbDrone.Core.Model; using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Repository; -using XemLib.Data; -using XemLib.Data.Banner; +using TvdbLib.Data; +using TvdbLib.Data.Banner; namespace NzbDrone.Core.Providers.Metadata { @@ -48,16 +48,16 @@ public override void CreateForSeries(Series series, TvdbSeries tvDbSeries) tvShow.Add(new XElement("episodeguideurl", episodeGuideUrl)); tvShow.Add(new XElement("mpaa", tvDbSeries.ContentRating)); tvShow.Add(new XElement("id", tvDbSeries.Id)); - tvShow.Add(new XElement("genre", tvDbSeries.Genres.FirstOrDefault())); + tvShow.Add(new XElement("genre", tvDbSeries.GenreString.Trim('|').Split('|')[0])); tvShow.Add(new XElement("premiered", tvDbSeries.FirstAired.ToString("yyyy-MM-dd"))); - tvShow.Add(new XElement("studio", tvDbSeries.Network)); + 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", "http://www.thetvdb.com/banners/" + actor.Image) + new XElement("thumb", "http://www.thetvdb.com/banners/" + actor.ActorImage.BannerPath) )); } @@ -71,7 +71,7 @@ public override void CreateForSeries(Series series, TvdbSeries tvDbSeries) if (!_diskProvider.FileExists(Path.Combine(series.Path, "fanart.jpg"))) { _logger.Debug("Downloading fanart for: {0}", series.Title); - _bannerProvider.Download(tvDbSeries.Fanart, Path.Combine(series.Path, "fanart.jpg")); + _bannerProvider.Download(tvDbSeries.FanartPath, Path.Combine(series.Path, "fanart.jpg")); } if (!_diskProvider.FileExists(Path.Combine(series.Path, "folder.jpg"))) @@ -79,19 +79,19 @@ public override void CreateForSeries(Series series, TvdbSeries tvDbSeries) if(_configProvider.MetadataUseBanners) { _logger.Debug("Downloading series banner for: {0}", series.Title); - _bannerProvider.Download(tvDbSeries.Banner, Path.Combine(series.Path, "folder.jpg")); + _bannerProvider.Download(tvDbSeries.BannerPath, Path.Combine(series.Path, "folder.jpg")); _logger.Debug("Downloading Season banners for {0}", series.Title); - DownloadSeasonThumbnails(series, tvDbSeries, TvdbSeasonBanner.Type.Banner); + DownloadSeasonThumbnails(series, tvDbSeries, TvdbSeasonBanner.Type.seasonwide); } else { _logger.Debug("Downloading series thumbnail for: {0}", series.Title); - _bannerProvider.Download(tvDbSeries.Poster, Path.Combine(series.Path, "folder.jpg")); + _bannerProvider.Download(tvDbSeries.PosterPath, Path.Combine(series.Path, "folder.jpg")); _logger.Debug("Downloading Season posters for {0}", series.Title); - DownloadSeasonThumbnails(series, tvDbSeries, TvdbSeasonBanner.Type.Poster); + DownloadSeasonThumbnails(series, tvDbSeries, TvdbSeasonBanner.Type.season); } } } @@ -112,7 +112,7 @@ public override void CreateForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tv e.SeasonNumber == episodeFile.SeasonNumber && e.EpisodeNumber == episodes.First().EpisodeNumber); - if (episodeFileThumbnail == null || String.IsNullOrWhiteSpace(episodeFileThumbnail.Banner)) + if (episodeFileThumbnail == null || String.IsNullOrWhiteSpace(episodeFileThumbnail.BannerPath)) { _logger.Debug("No thumbnail is available for this episode"); return; @@ -121,7 +121,7 @@ public override void CreateForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tv if (!_diskProvider.FileExists(episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".tbn"))) { _logger.Debug("Downloading episode thumbnail for: {0}", episodeFile.EpisodeFileId); - _bannerProvider.Download(episodeFileThumbnail.Banner, + _bannerProvider.Download(episodeFileThumbnail.BannerPath, episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".tbn")); } @@ -165,9 +165,9 @@ public override void CreateForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tv details.Add(new XElement("plot", tvdbEpisode.Overview)); details.Add(new XElement("displayseason")); details.Add(new XElement("displayepisode")); - details.Add(new XElement("thumb", "http://www.thetvdb.com/banners/" + tvdbEpisode.Banner)); + details.Add(new XElement("thumb", "http://www.thetvdb.com/banners/" + tvdbEpisode.BannerPath)); details.Add(new XElement("watched", "false")); - details.Add(new XElement("credits", tvdbEpisode.Writers.FirstOrDefault())); + details.Add(new XElement("credits", tvdbEpisode.Writer.FirstOrDefault())); details.Add(new XElement("director", tvdbEpisode.Directors.FirstOrDefault())); details.Add(new XElement("rating", tvdbEpisode.Rating)); @@ -186,7 +186,7 @@ public override void CreateForEpisodeFile(EpisodeFile episodeFile, TvdbSeries tv details.Add(new XElement("actor", new XElement("name", actor.Name), new XElement("role", actor.Role), - new XElement("thumb", "http://www.thetvdb.com/banners/" + actor.Image) + new XElement("thumb", "http://www.thetvdb.com/banners/" + actor.ActorImage.BannerPath) )); } @@ -235,11 +235,11 @@ public override void RemoveForEpisodeFile(EpisodeFile episodeFile) private void DownloadSeasonThumbnails(Series series, TvdbSeries tvDbSeries, TvdbSeasonBanner.Type bannerType) { - var seasons = tvDbSeries.Banners.SeasonBanners.Where(s => s.BannerType == bannerType).Select(s => s.SeasonNumber); + var seasons = tvDbSeries.SeasonBanners.Where(s => s.BannerType == bannerType).Select(s => s.Season); foreach (var season in seasons) { - var banner = tvDbSeries.Banners.SeasonBanners.FirstOrDefault(b => b.BannerType == bannerType && b.SeasonNumber == season); + 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) diff --git a/NzbDrone.Core/Providers/MetadataProvider.cs b/NzbDrone.Core/Providers/MetadataProvider.cs index d6d27ac08..caad52dba 100644 --- a/NzbDrone.Core/Providers/MetadataProvider.cs +++ b/NzbDrone.Core/Providers/MetadataProvider.cs @@ -8,7 +8,7 @@ using NzbDrone.Core.Providers.Metadata; using NzbDrone.Core.Repository; using PetaPoco; -using XemLib.Data; +using TvdbLib.Data; namespace NzbDrone.Core.Providers { @@ -92,7 +92,7 @@ public virtual void Initialize(IList metabaseProviders) public virtual void CreateForSeries(Series series) { - var tvDbSeries = _tvDbProvider.GetSeries(series.SeriesId, false, true, true); + var tvDbSeries = _tvDbProvider.GetSeries(series.SeriesId, false, true); CreateForSeries(series, tvDbSeries); } @@ -107,7 +107,7 @@ public virtual void CreateForSeries(Series series, TvdbSeries tvDbSeries) public virtual void CreateForEpisodeFile(EpisodeFile episodeFile) { - var tvDbSeries = _tvDbProvider.GetSeries(episodeFile.SeriesId, true, true, true); + var tvDbSeries = _tvDbProvider.GetSeries(episodeFile.SeriesId, true, true); CreateForEpisodeFile(episodeFile, tvDbSeries); } @@ -130,7 +130,7 @@ public virtual void CreateForEpisodeFiles(List episodeFiles) Logger.Trace("Creating metadata for {0} files.", episodeFiles.Count); - var tvDbSeries = _tvDbProvider.GetSeries(episodeFiles.First().SeriesId, true, true, true); + var tvDbSeries = _tvDbProvider.GetSeries(episodeFiles.First().SeriesId, true, true); foreach(var episodeFile in episodeFiles) { diff --git a/NzbDrone.Core/Providers/SearchHistoryProvider.cs b/NzbDrone.Core/Providers/SearchHistoryProvider.cs index fae40927b..4fb7e97a8 100644 --- a/NzbDrone.Core/Providers/SearchHistoryProvider.cs +++ b/NzbDrone.Core/Providers/SearchHistoryProvider.cs @@ -112,6 +112,8 @@ public virtual void ForceDownload(int itemId) parseResult.NzbUrl = item.NzbUrl; parseResult.Series = series; parseResult.Indexer = item.Indexer; + parseResult.Episodes = _episodeProvider.GetEpisodesByParseResult(parseResult); + parseResult.SceneSource = true; logger.Info("Forcing Download of: {0}", item.ReportTitle); _downloadProvider.DownloadReport(parseResult); diff --git a/NzbDrone.Core/Providers/SearchProvider.cs b/NzbDrone.Core/Providers/SearchProvider.cs index d318b93af..ac6856dd3 100644 --- a/NzbDrone.Core/Providers/SearchProvider.cs +++ b/NzbDrone.Core/Providers/SearchProvider.cs @@ -25,7 +25,7 @@ public class SearchProvider private readonly AllowedDownloadSpecification _allowedDownloadSpecification; private readonly SearchHistoryProvider _searchHistoryProvider; - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); [Inject] public SearchProvider(EpisodeProvider episodeProvider, DownloadProvider downloadProvider, SeriesProvider seriesProvider, @@ -60,7 +60,7 @@ public virtual List SeasonSearch(ProgressNotification notification, int ser if (series == null) { - Logger.Error("Unable to find an series {0} in database", seriesId); + _logger.Error("Unable to find an series {0} in database", seriesId); return new List(); } @@ -72,17 +72,17 @@ public virtual List SeasonSearch(ProgressNotification notification, int ser var reports = PerformSearch(notification, series, seasonNumber); - Logger.Debug("Finished searching all indexers. Total {0}", reports.Count); + _logger.Debug("Finished searching all indexers. Total {0}", reports.Count); if (reports.Count == 0) return new List(); - Logger.Debug("Getting episodes from database for series: {0} and season: {1}", seriesId, seasonNumber); + _logger.Debug("Getting episodes from database for series: {0} and season: {1}", seriesId, seasonNumber); var episodeNumbers = _episodeProvider.GetEpisodeNumbersBySeason(seriesId, seasonNumber); if (episodeNumbers == null || episodeNumbers.Count == 0) { - Logger.Warn("No episodes in database found for series: {0} and season: {1}.", seriesId, seasonNumber); + _logger.Warn("No episodes in database found for series: {0} and season: {1}.", seriesId, seasonNumber); return new List(); } @@ -111,7 +111,7 @@ public virtual List PartialSeasonSearch(ProgressNotification notification, if (series == null) { - Logger.Error("Unable to find an series {0} in database", seriesId); + _logger.Error("Unable to find an series {0} in database", seriesId); return new List(); } @@ -122,7 +122,7 @@ public virtual List PartialSeasonSearch(ProgressNotification notification, notification.CurrentMessage = String.Format("Searching for {0} Season {1}", series.Title, seasonNumber); var episodes = _episodeProvider.GetEpisodesBySeason(seriesId, seasonNumber); var reports = PerformSearch(notification, series, seasonNumber, episodes); - Logger.Debug("Finished searching all indexers. Total {0}", reports.Count); + _logger.Debug("Finished searching all indexers. Total {0}", reports.Count); if (reports.Count == 0) return new List(); @@ -140,14 +140,14 @@ public virtual bool EpisodeSearch(ProgressNotification notification, int episode if (episode == null) { - Logger.Error("Unable to find an episode {0} in database", episodeId); + _logger.Error("Unable to find an episode {0} in database", episodeId); return false; } //Check to see if an upgrade is possible before attempting if (!_upgradePossibleSpecification.IsSatisfiedBy(episode)) { - Logger.Info("Search for {0} was aborted, file in disk meets or exceeds Profile's Cutoff", episode); + _logger.Info("Search for {0} was aborted, file in disk meets or exceeds Profile's Cutoff", episode); notification.CurrentMessage = String.Format("Skipping search for {0}, the file you have is already at cutoff", episode); return false; } @@ -156,7 +156,7 @@ public virtual bool EpisodeSearch(ProgressNotification notification, int episode if (episode.Series.IsDaily && !episode.AirDate.HasValue) { - Logger.Warn("AirDate is not Valid for: {0}", episode); + _logger.Warn("AirDate is not Valid for: {0}", episode); notification.CurrentMessage = String.Format("Search for {0} Failed, AirDate is invalid", episode); return false; } @@ -169,7 +169,7 @@ public virtual bool EpisodeSearch(ProgressNotification notification, int episode var reports = PerformSearch(notification, episode.Series, episode.SeasonNumber, new List { episode }); - Logger.Debug("Finished searching all indexers. Total {0}", reports.Count); + _logger.Debug("Finished searching all indexers. Total {0}", reports.Count); notification.CurrentMessage = "Processing search results"; if (episode.Series.IsDaily) @@ -181,6 +181,34 @@ public virtual bool EpisodeSearch(ProgressNotification notification, int episode return true; } + else if (episode.Series.UseSceneNumbering) + { + searchResult.EpisodeId = episodeId; + + var seasonNumber = episode.SceneSeasonNumber; + var episodeNumber = episode.SceneEpisodeNumber; + + if (seasonNumber == 0 || episodeNumber == 0) + { + seasonNumber = episode.SeasonNumber; + episodeNumber = episode.EpisodeNumber; + } + + searchResult.SearchHistoryItems = ProcessSearchResults( + notification, + reports, + searchResult, + episode.Series, + seasonNumber, + episodeNumber + ); + + _searchHistoryProvider.Add(searchResult); + + if (searchResult.SearchHistoryItems.Any(r => r.Success)) + return true; + } + else { searchResult.EpisodeId = episodeId; @@ -188,10 +216,10 @@ public virtual bool EpisodeSearch(ProgressNotification notification, int episode _searchHistoryProvider.Add(searchResult); if (searchResult.SearchHistoryItems.Any(r => r.Success)) - return true; + return true; } - Logger.Warn("Unable to find {0} in any of indexers.", episode); + _logger.Warn("Unable to find {0} in any of indexers.", episode); if (reports.Any()) { @@ -228,7 +256,12 @@ public List PerformSearch(ProgressNotification notification, //Treat as single episode else if (episodes.Count == 1) { - if (!series.IsDaily) + //Use SceneNumbering - Only if SceneSN and SceneEN are greater than zero + if (series.UseSceneNumbering && episodes.First().SceneSeasonNumber > 0 && episodes.First().SceneEpisodeNumber > 0) + reports.AddRange(indexer.FetchEpisode(title, episodes.First().SceneSeasonNumber, episodes.First().SceneEpisodeNumber)); + + //Standard + else if (!series.IsDaily) reports.AddRange(indexer.FetchEpisode(title, seasonNumber, episodes.First().EpisodeNumber)); //Daily Episode @@ -250,7 +283,7 @@ public List PerformSearch(ProgressNotification notification, catch (Exception e) { - Logger.ErrorException("An error has occurred while fetching items from " + indexer.Name, e); + _logger.ErrorException("An error has occurred while fetching items from " + indexer.Name, e); } }); @@ -268,7 +301,7 @@ public List ProcessSearchResults(ProgressNotification notific { try { - Logger.Trace("Analysing report " + episodeParseResult); + _logger.Trace("Analysing report " + episodeParseResult); var item = new SearchHistoryItem { @@ -290,7 +323,7 @@ public List ProcessSearchResults(ProgressNotification notific //If series is null or doesn't match the series we're looking for return if (episodeParseResult.Series == null || episodeParseResult.Series.SeriesId != series.SeriesId) { - Logger.Trace("Unexpected series for search: {0}. Skipping.", episodeParseResult.CleanTitle); + _logger.Trace("Unexpected series for search: {0}. Skipping.", episodeParseResult.CleanTitle); item.SearchError = ReportRejectionType.WrongSeries; continue; } @@ -298,7 +331,7 @@ public List ProcessSearchResults(ProgressNotification notific //If SeasonNumber doesn't match or episode is not in the in the list in the parse result, skip the report. if (episodeParseResult.SeasonNumber != seasonNumber) { - Logger.Trace("Season number does not match searched season number, skipping."); + _logger.Trace("Season number does not match searched season number, skipping."); item.SearchError = ReportRejectionType.WrongSeason; continue; } @@ -306,7 +339,7 @@ public List ProcessSearchResults(ProgressNotification notific //If the EpisodeNumber was passed in and it is not contained in the parseResult, skip the report. if (episodeNumber.HasValue && !episodeParseResult.EpisodeNumbers.Contains(episodeNumber.Value)) { - Logger.Trace("Searched episode number is not contained in post, skipping."); + _logger.Trace("Searched episode number is not contained in post, skipping."); item.SearchError = ReportRejectionType.WrongEpisode; continue; } @@ -314,15 +347,17 @@ public List ProcessSearchResults(ProgressNotification notific //Make sure we haven't already downloaded a report with this episodenumber, if we have, skip the report. if (searchResult.Successes.Intersect(episodeParseResult.EpisodeNumbers).Any()) { - Logger.Trace("Episode has already been downloaded in this search, skipping."); + _logger.Trace("Episode has already been downloaded in this search, skipping."); item.SearchError = ReportRejectionType.Skipped; continue; } + episodeParseResult.Episodes = _episodeProvider.GetEpisodesByParseResult(episodeParseResult); + item.SearchError = _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult); if (item.SearchError == ReportRejectionType.None) { - Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); + _logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); try { if (_downloadProvider.DownloadReport(episodeParseResult)) @@ -340,7 +375,7 @@ public List ProcessSearchResults(ProgressNotification notific } catch (Exception e) { - Logger.ErrorException("Unable to add report to download queue." + episodeParseResult, e); + _logger.ErrorException("Unable to add report to download queue." + episodeParseResult, e); notification.CurrentMessage = String.Format("Unable to add report to download queue. {0}", episodeParseResult); item.SearchError = ReportRejectionType.DownloadClientFailure; } @@ -348,7 +383,7 @@ public List ProcessSearchResults(ProgressNotification notific } catch (Exception e) { - Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e); + _logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e); } } @@ -384,7 +419,7 @@ public List ProcessSearchResults(ProgressNotification notific continue; } - Logger.Trace("Analysing report " + episodeParseResult); + _logger.Trace("Analysing report " + episodeParseResult); //Get the matching series episodeParseResult.Series = _seriesProvider.FindSeries(episodeParseResult.CleanTitle); @@ -403,10 +438,12 @@ public List ProcessSearchResults(ProgressNotification notific continue; } + episodeParseResult.Episodes = _episodeProvider.GetEpisodesByParseResult(episodeParseResult); + item.SearchError = _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult); if (item.SearchError == ReportRejectionType.None) { - Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); + _logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); try { if (_downloadProvider.DownloadReport(episodeParseResult)) @@ -425,7 +462,7 @@ public List ProcessSearchResults(ProgressNotification notific } catch (Exception e) { - Logger.ErrorException("Unable to add report to download queue." + episodeParseResult, e); + _logger.ErrorException("Unable to add report to download queue." + episodeParseResult, e); notification.CurrentMessage = String.Format("Unable to add report to download queue. {0}", episodeParseResult); item.SearchError = ReportRejectionType.DownloadClientFailure; } @@ -433,7 +470,7 @@ public List ProcessSearchResults(ProgressNotification notific } catch (Exception e) { - Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e); + _logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e); } } diff --git a/NzbDrone.Core/Providers/SeriesProvider.cs b/NzbDrone.Core/Providers/SeriesProvider.cs index 9c6d61242..ecbded829 100644 --- a/NzbDrone.Core/Providers/SeriesProvider.cs +++ b/NzbDrone.Core/Providers/SeriesProvider.cs @@ -97,11 +97,11 @@ public virtual Series UpdateSeriesInfo(int seriesId) series.AirsDayOfWeek = tvDbSeries.AirsDayOfWeek; series.Overview = tvDbSeries.Overview; series.Status = tvDbSeries.Status; - series.Language = tvDbSeries.Language != null ? tvDbSeries.Language : string.Empty; + series.Language = tvDbSeries.Language != null ? tvDbSeries.Language.Abbriviation : string.Empty; series.CleanTitle = Parser.NormalizeTitle(tvDbSeries.SeriesName); series.LastInfoSync = DateTime.Now; series.Runtime = (int)tvDbSeries.Runtime; - series.BannerUrl = tvDbSeries.Banner; + series.BannerUrl = tvDbSeries.BannerPath; series.Network = tvDbSeries.Network; UpdateSeries(series); diff --git a/NzbDrone.Core/Providers/TvDbProvider.cs b/NzbDrone.Core/Providers/TvDbProvider.cs index aca384bbc..2019f1e48 100644 --- a/NzbDrone.Core/Providers/TvDbProvider.cs +++ b/NzbDrone.Core/Providers/TvDbProvider.cs @@ -5,8 +5,9 @@ using NLog; using Ninject; using NzbDrone.Common; -using XemLib; -using XemLib.Data; +using TvdbLib; +using TvdbLib.Cache; +using TvdbLib.Data; namespace NzbDrone.Core.Providers { @@ -16,13 +17,13 @@ public class TvDbProvider public const string TVDB_APIKEY = "5D2D188E86E07F4F"; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - private readonly XemClient _xemClient; + private readonly TvdbHandler _handler; [Inject] public TvDbProvider(EnvironmentProvider environmentProvider) { _environmentProvider = environmentProvider; - _xemClient = new XemClient(TVDB_APIKEY); + _handler = new TvdbHandler(new XmlCacheProvider(_environmentProvider.GetCacheFolder()), TVDB_APIKEY); } public TvDbProvider() @@ -32,27 +33,33 @@ public TvDbProvider() public virtual IList SearchSeries(string title) { - Logger.Debug("Searching TVDB for '{0}'", title); + lock (_handler) + { + Logger.Debug("Searching TVDB for '{0}'", title); - var result = _xemClient.SearchSeries(title); + var result = _handler.SearchSeries(title); - Logger.Debug("Search for '{0}' returned {1} possible results", title, result.Count); - return result; + Logger.Debug("Search for '{0}' returned {1} possible results", title, result.Count); + return result; + } } - public virtual TvdbSeries GetSeries(int id, bool loadEpisodes, bool loadActors = false, bool loadBanners = false) + public virtual TvdbSeries GetSeries(int id, bool loadEpisodes, bool loadActors = false) { - Logger.Debug("Fetching SeriesId'{0}' from tvdb", id); - var result = _xemClient.GetSeries(id, loadEpisodes, loadActors, true, TvdbLanguage.Default); + lock (_handler) + { + Logger.Debug("Fetching SeriesId'{0}' from tvdb", id); + var result = _handler.GetSeries(id, TvdbLanguage.DefaultLanguage, loadEpisodes, loadActors, true, true); - //Remove duplicated episodes - var episodes = result.Episodes.OrderByDescending(e => e.FirstAired).ThenByDescending(e => e.EpisodeName) - .GroupBy(e => e.SeriesId.ToString("000000") + e.SeasonNumber.ToString("000") + e.EpisodeNumber.ToString("000")) - .Select(e => e.First()); + //Remove duplicated episodes + var episodes = result.Episodes.OrderByDescending(e => e.FirstAired).ThenByDescending(e => e.EpisodeName) + .GroupBy(e => e.SeriesId.ToString("000000") + e.SeasonNumber.ToString("000") + e.EpisodeNumber.ToString("000")) + .Select(e => e.First()); - result.Episodes = episodes.ToList(); + result.Episodes = episodes.ToList(); - return result; + return result; + } } } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/XemCommunicationProvider.cs b/NzbDrone.Core/Providers/XemCommunicationProvider.cs new file mode 100644 index 000000000..3cbab3a12 --- /dev/null +++ b/NzbDrone.Core/Providers/XemCommunicationProvider.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NLog; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Ninject; +using NzbDrone.Common; +using NzbDrone.Core.Model.Xem; + +namespace NzbDrone.Core.Providers +{ + public class XemCommunicationProvider + { + private readonly HttpProvider _httpProvider; + + private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); + + private const string XEM_BASE_URL = "http://thexem.de/map/"; + + [Inject] + public XemCommunicationProvider(HttpProvider httpProvider) + { + _httpProvider = httpProvider; + } + + public XemCommunicationProvider() + { + } + + public virtual List GetXemSeriesIds(string origin = "tvdb") + { + _logger.Trace("Fetching Series IDs from: {0}", origin); + + var url = String.Format("{0}havemap?origin={1}", XEM_BASE_URL, origin); + var response =_httpProvider.DownloadString(url); + + CheckForFailureResult(response); + + var result = JsonConvert.DeserializeObject>>(response); + + return result.Data.ToList(); + } + + public virtual List GetSceneTvdbMappings(int id) + { + _logger.Trace("Fetching Mappings for: {0}", id); + var url = String.Format("{0}all?id={1}&origin=tvdb", XEM_BASE_URL, id); + var response = _httpProvider.DownloadString(url); + + CheckForFailureResult(response); + + var result = JsonConvert.DeserializeObject>(JObject.Parse(response).SelectToken("data").ToString()); + + return result; + } + + public virtual void CheckForFailureResult(string response) + { + var result = JsonConvert.DeserializeObject>(response); + + if (result != null && result.Result.Equals("failure", StringComparison.InvariantCultureIgnoreCase)) + throw new Exception("Error response received from Xem: " + result.Message); + } + } +} diff --git a/NzbDrone.Core/Providers/XemProvider.cs b/NzbDrone.Core/Providers/XemProvider.cs new file mode 100644 index 000000000..3c1fed45c --- /dev/null +++ b/NzbDrone.Core/Providers/XemProvider.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NLog; +using Ninject; +using NzbDrone.Core.Repository; + +namespace NzbDrone.Core.Providers +{ + public class XemProvider + { + private readonly SeriesProvider _seriesProvider; + private readonly EpisodeProvider _episodeProvider; + private readonly XemCommunicationProvider _xemCommunicationProvider; + + private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); + + [Inject] + public XemProvider(SeriesProvider seriesProvider, EpisodeProvider episodeProvider, + XemCommunicationProvider xemCommunicationProvider) + { + _seriesProvider = seriesProvider; + _episodeProvider = episodeProvider; + _xemCommunicationProvider = xemCommunicationProvider; + } + + public XemProvider() + { + } + + public virtual void UpdateMappings() + { + _logger.Trace("Starting scene numbering update"); + try + { + var ids = _xemCommunicationProvider.GetXemSeriesIds(); + var series = _seriesProvider.GetAllSeries(); + var wantedSeries = series.Where(s => ids.Contains(s.SeriesId)).ToList(); + + foreach(var ser in wantedSeries) + { + PerformUpdate(ser); + } + + _logger.Trace("Completed scene numbering update"); + } + + catch(Exception ex) + { + _logger.WarnException("Error updating Scene Mappings", ex); + throw; + } + } + + public virtual void UpdateMappings(int seriesId) + { + var series = _seriesProvider.GetSeries(seriesId); + + if (series == null) + { + _logger.Trace("Series could not be found: {0}", seriesId); + return; + } + + PerformUpdate(series); + } + + public virtual void PerformUpdate(Series series) + { + _logger.Trace("Updating scene numbering mapping for: {0}", series.Title); + try + { + var episodesToUpdate = new List(); + var mappings = _xemCommunicationProvider.GetSceneTvdbMappings(series.SeriesId); + + if (mappings == null) + { + _logger.Trace("Mappings for: {0} are null, skipping", series.Title); + return; + } + + var episodes = _episodeProvider.GetEpisodeBySeries(series.SeriesId); + + foreach (var mapping in mappings) + { + _logger.Trace("Setting scene numbering mappings for {0} S{1:00}E{2:00}", series.Title, mapping.Tvdb.Season, mapping.Tvdb.Episode); + + var episode = episodes.SingleOrDefault(e => e.SeasonNumber == mapping.Tvdb.Season && e.EpisodeNumber == mapping.Tvdb.Episode); + + if (episode == null) + { + _logger.Trace("Information hasn't been added to TheTVDB yet, skipping."); + continue; + } + + episode.AbsoluteEpisodeNumber = mapping.Scene.Absolute; + episode.SceneSeasonNumber = mapping.Scene.Season; + episode.SceneEpisodeNumber = mapping.Scene.Episode; + episodesToUpdate.Add(episode); + } + + _logger.Trace("Committing scene numbering mappings to database for: {0}", series.Title); + _episodeProvider.UpdateEpisodes(episodesToUpdate); + + _logger.Trace("Setting UseSceneMapping for {0}", series.Title); + series.UseSceneNumbering = true; + _seriesProvider.UpdateSeries(series); + } + + catch (Exception ex) + { + _logger.WarnException("Error updating scene numbering mappings for: " + series, ex); + } + } + } +} diff --git a/NzbDrone.Core/Repository/Episode.cs b/NzbDrone.Core/Repository/Episode.cs index 9c3e38968..f93782fd6 100644 --- a/NzbDrone.Core/Repository/Episode.cs +++ b/NzbDrone.Core/Repository/Episode.cs @@ -22,6 +22,9 @@ public class Episode public Boolean Ignored { get; set; } public PostDownloadStatusType PostDownloadStatus { get; set; } public int AbsoluteEpisodeNumber { get; set; } + public int SceneAbsoluteEpisodeNumber { get; set; } + public int SceneSeasonNumber { get; set; } + public int SceneEpisodeNumber { get; set; } /// /// Gets or sets the grab date. diff --git a/NzbDrone.Core/Repository/Series.cs b/NzbDrone.Core/Repository/Series.cs index 70d19cead..35a66ba92 100644 --- a/NzbDrone.Core/Repository/Series.cs +++ b/NzbDrone.Core/Repository/Series.cs @@ -50,6 +50,8 @@ public class Series public DateTime? CustomStartDate { get; set; } + public bool UseSceneNumbering { get; set; } + /// /// Gets or sets a value indicating whether this is hidden. /// diff --git a/NzbDrone.Web/Controllers/AddSeriesController.cs b/NzbDrone.Web/Controllers/AddSeriesController.cs index 541dcea23..96adab086 100644 --- a/NzbDrone.Web/Controllers/AddSeriesController.cs +++ b/NzbDrone.Web/Controllers/AddSeriesController.cs @@ -12,7 +12,7 @@ using NzbDrone.Core.Repository; using NzbDrone.Web.Filters; using NzbDrone.Web.Models; -using XemLib.Exceptions; +using TvdbLib.Exceptions; namespace NzbDrone.Web.Controllers { @@ -156,14 +156,14 @@ public JsonResult LookupSeries(string term) DisplayedTitle = r.FirstAired.Year > 1900 && !r.SeriesName.EndsWith("(" + r.FirstAired.Year + ")") ? string.Format("{0} ({1})", r.SeriesName, r.FirstAired.Year) : r.SeriesName, - Banner = r.Banner, + Banner = r.Banner.BannerPath, Url = String.Format("http://www.thetvdb.com/?tab=series&id={0}", r.Id) }).ToList(); return Json(tvDbResults, JsonRequestBehavior.AllowGet); } - catch (TheTvbdbUnavailableException ex) + catch(TvdbNotAvailableException ex) { logger.WarnException("Unable to lookup series on TheTVDB", ex); return JsonNotificationResult.Info("Lookup Failed", "TheTVDB is not available at this time."); diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index cb756f65c..77b8e0086 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -141,14 +141,14 @@ True + + False + ..\Libraries\TvdbLib.dll + False ..\packages\WebActivator.1.5.1\lib\net40\WebActivator.dll - - False - ..\Libraries\XemLib\XemLib.dll -