diff --git a/.gitignore b/.gitignore index dc61ab746..eb8ef0eec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,54 +1,127 @@ +# Build Folders (you can keep bin if you'd like, to store dlls and pdbs) +[Bb]in/ +[Oo]bj/ -#ignore thumbnails created by windows -Thumbs.db -#Ignore files build by Visual Studio -*.obj +# mstest test results +TestResults + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo *.user -*.aps -*.pch -*.vspscc +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +x64/ *_i.c *_p.c -*.ncb -*.suo +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr *.tlb +*.tli *.tlh -*.bak -*.cache -*.ilk +*.tmp *.log -[Bb]in -[Dd]ebug*/ -[Rr]elease]*/ -*.lib -*.sbr -*.nzb -obj/ -_ReSharper*/ -[Tt]est +*.vspscc +*.vssscc +.builds + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf [Rr]esult[s] -[Nn]zbs -[Bb]uild/ -[Ll]ogs/ -[Aa]pp_Data/ -packages/ -/FakesAssemblies/ -#NZBDrone specific -*.db -*Web.Publish.xml -NzbDrone.Web/NzbDrone.Web.Publish.xml *.sdf -[Bb]anners + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Mindbench SASS cache +.sass-cache/ + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Publish Web Output +*.Publish.xml + +# NuGet Packages Directory +packages + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql +TestResults +[Tt]est[Rr]esult* +*.Cache +ClientBin +[Ss]tyle[Cc]op.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +# Git Files *.orig -_rawPackage/ -NzbDrone.zip -NzbDrone.sln.DotSettings.user* -config.xml -UpdateLogs/ -NzbDrone.Web/MediaCover -NzbDrone.fpr -nzbdrone.log*txt -_rawPackage_service/ + +# Tools _NCrunch_* _TeamCity* -NCrunch_* \ No newline at end of file + +# NzbDrone +config.xml +nzbdrone.log*txt +NzbDrone.Web/MediaCover +UpdateLogs/ \ No newline at end of file diff --git a/NzbDrone.Common.Test/ProcessProviderTests.cs b/NzbDrone.Common.Test/ProcessProviderTests.cs index 7e4c764ac..ade73a3bc 100644 --- a/NzbDrone.Common.Test/ProcessProviderTests.cs +++ b/NzbDrone.Common.Test/ProcessProviderTests.cs @@ -1,6 +1,7 @@ // ReSharper disable InconsistentNaming using System; +using System.ComponentModel; using System.Diagnostics; using System.Linq; using FluentAssertions; @@ -88,6 +89,7 @@ namespace NzbDrone.Common.Test public void ToString_on_new_processInfo() { Console.WriteLine(new ProcessInfo().ToString()); + ExceptionVerification.MarkInconclusive(typeof(Win32Exception)); } } diff --git a/NzbDrone.Core.Test/Files/TvRage/SearchResults_empty.xml b/NzbDrone.Core.Test/Files/TvRage/SearchResults_empty.xml new file mode 100644 index 000000000..4f8843778 --- /dev/null +++ b/NzbDrone.Core.Test/Files/TvRage/SearchResults_empty.xml @@ -0,0 +1,3 @@ + + +0 \ No newline at end of file diff --git a/NzbDrone.Core.Test/Files/TvRage/SearchResults_many.xml b/NzbDrone.Core.Test/Files/TvRage/SearchResults_many.xml new file mode 100644 index 000000000..31206736b --- /dev/null +++ b/NzbDrone.Core.Test/Files/TvRage/SearchResults_many.xml @@ -0,0 +1,420 @@ + + + + + 6753 + Top Gear + http://www.tvrage.com/Top_Gear + UK + Oct/20/2002 + + 18 + Returning Series + 60 + Reality + + + Automobiles + Comedy + + BBC TWO + 20:00 + Sunday + + + 19321 + Top Gear (1978) + http://www.tvrage.com/shows/id-19321 + UK + Jul/13/1978 + Dec/17/2001 + 24 + Canceled/Ended + 30 + Reality + + Automobiles + + BBC TWO + 12:00 + Sunday + + Top Gear Xtra + + + + 20568 + Top Gear (US) + http://www.tvrage.com/Top_Gear_US + US + Nov/21/2010 + + 3 + Returning Series + 60 + Documentary + + Automobiles + Comedy + + History Channel + 21:00 + Tuesday + + Top Gear USA + + + + 20324 + Top Gear Australia + http://www.tvrage.com/shows/id-20324 + AU + Sep/29/2008 + + 4 + Returning Series + 60 + Documentary + + Automobiles + Comedy + + GEM + 18:30 + Saturday + + + 20569 + Top Gear Motorsport + http://www.tvrage.com/shows/id-20569 + UK + Mar/24/1995 + 1998 + 3 + Canceled/Ended + 30 + Sports + + Educational + Family + How To/Do It Yourself + + BBC TWO + 12:00 + Wednesday + + + 6249 + Top Secret Life of Edgar Briggs + http://www.tvrage.com/shows/id-6249 + UK + Sep/15/1974 + Dec/20/1974 + 1 + Canceled/Ended + 30 + Scripted + + Comedy + + ITV + 19:00 + Friday + + + 25253 + Popstar Wesley: Op weg naar de Top + http://www.tvrage.com/shows/id-25253 + NL + Feb/06/2010 + Feb/27/2010 + 1 + Canceled/Ended + 15 + Reality + + How To/Do It Yourself + Music + Talent + + SBS 6 + 23:15 + Saturday + + + 19649 + Top Chef: Masters + http://www.tvrage.com/Top_Chef-Masters + US + Jun/10/2009 + + 4 + Returning Series + 60 + Reality + + Cooking/Food + Family + Talent + + Bravo + 22:00 + Wednesday + + + 26212 + Top Chef: Just Desserts + http://www.tvrage.com/Top_Chef-Just_Desserts + US + Sep/15/2010 + + 2 + New Series + 60 + Reality + + Celebrities + Cooking/Food + Educational + Family + How To/Do It Yourself + Talent + + Bravo + 22:00 + Wednesday + + + 11210 + Top Design + http://www.tvrage.com/shows/id-11210 + US + Jan/31/2007 + Nov/05/2008 + 2 + Canceled/Ended + 60 + Reality + + Celebrities + Educational + Family + Housing/Building + How To/Do It Yourself + + Bravo + 22:00 + Wednesday + + Top Decorator + Top Designer + + + + 15390 + Air Gear + http://www.tvrage.com/shows/id-15390 + AJ + Apr/04/2006 + Sep/26/2006 + 1 + Canceled/Ended + 30 + Animation + + Anime + Adventure + Sci-Fi + Tech/Gaming + + TV Tokyo + 12:00 + Wednesday + + + 30933 + Top Guns + http://www.tvrage.com/shows/id-30933 + US + Feb/15/2012 + + 1 + New Series + 60 + Reality + + Action + Family + How To/Do It Yourself + Talent + + H2 TV + 22:00 + Wednesday + + + 28150 + Top Chef Canada + http://www.tvrage.com/shows/id-28150 + CA + Apr/11/2011 + + 2 + New Series + 60 + Reality + + Cooking/Food + Family + Talent + + Food Network Canada + 21:00 + Monday + + + 6386 + Top of the Pops Saturday + http://www.tvrage.com/shows/id-6386 + UK + Sep/20/2003 + Mar/26/2005 + 2 + Canceled/Ended + 60 + Variety + + Children + + BBC One + 23:00 + Saturday + + + 29633 + Top Secret Recipe + http://www.tvrage.com/shows/id-29633 + US + Oct/07/2011 + + 1 + New Series + 60 + Reality + + Cooking/Food + How To/Do It Yourself + + CMT + 21:00 + Thursday + + + 26650 + The Top 100: NFL's Greatest Players + http://www.tvrage.com/shows/id-26650 + US + Sep/03/2010 + Nov/04/2010 + 1 + New Series + 60 + Sports + + Sports + + NFL Network + 21:00 + Thursday + + + 7047 + Top of the Pops Reloaded + http://www.tvrage.com/shows/id-7047 + UK + Sep/2005 + Mar/2006 + 2 + Canceled/Ended + 45 + Variety + + Children + Music + Sketch/Improv + + CBBC + 11:00 + Saturday + + + 19475 + Top Model Ghana + http://www.tvrage.com/shows/id-19475 + GH + Aug/26/2006 + Oct/16/2006 + 1 + Canceled/Ended + 60 + Reality + + Celebrities + Family + Fashion/Make-up + Talent + Travel + + GTV + 21:00 + Monday + + Ghana's Next Top Model + TMG + Top Model Ghana, Cycle + + + + 31823 + Top 100 Video Games of All Time + http://www.tvrage.com/shows/id-31823 + US + Jun/11/2012 + + 1 + New Series + 60 + Mini-Series + + Family + Teens + + G4 + 20:00 + Weekdays + + + 25003 + Cantore Stories: On Top of the World + http://www.tvrage.com/Cantore_Stories-On_Top_of_the_World + US + Jan/24/2010 + + 1 + New Series + 60 + Reality + + Adventure + Educational + Family + Travel + + The Weather Channel + 22:00 + Sunday + + Cantore Stories + + + \ No newline at end of file diff --git a/NzbDrone.Core.Test/Files/TvRage/SearchResults_one.xml b/NzbDrone.Core.Test/Files/TvRage/SearchResults_one.xml new file mode 100644 index 000000000..4517c22bc --- /dev/null +++ b/NzbDrone.Core.Test/Files/TvRage/SearchResults_one.xml @@ -0,0 +1,26 @@ + + + + + 27518 + Suits + http://www.tvrage.com/Suits + US + Jun/23/2011 + + 2 + Returning Series + 60 + Scripted + + Drama + Financial/Business + + USA + 22:00 + Thursday + + A Legal Mind + + + \ No newline at end of file diff --git a/NzbDrone.Core.Test/Files/TvRage/SeriesInfo_empty.xml b/NzbDrone.Core.Test/Files/TvRage/SeriesInfo_empty.xml new file mode 100644 index 000000000..52671b237 --- /dev/null +++ b/NzbDrone.Core.Test/Files/TvRage/SeriesInfo_empty.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/NzbDrone.Core.Test/Files/TvRage/SeriesInfo_one.xml b/NzbDrone.Core.Test/Files/TvRage/SeriesInfo_one.xml new file mode 100644 index 000000000..f5eddca5b --- /dev/null +++ b/NzbDrone.Core.Test/Files/TvRage/SeriesInfo_one.xml @@ -0,0 +1,22 @@ + + + + 29999 + Anger Management + http://tvrage.com/shows/id-29999 + 2 + 2012 + Jun/28/2012 + + US + Returning Series + Scripted + + Comedy + + 30 + FX + 21:30 + Thursday + GMT-5 -DST + \ No newline at end of file diff --git a/NzbDrone.Core.Test/SortHelperTest.cs b/NzbDrone.Core.Test/HelperTests/SortHelperFixture.cs similarity index 85% rename from NzbDrone.Core.Test/SortHelperTest.cs rename to NzbDrone.Core.Test/HelperTests/SortHelperFixture.cs index 4035fb7d5..19f6d028a 100644 --- a/NzbDrone.Core.Test/SortHelperTest.cs +++ b/NzbDrone.Core.Test/HelperTests/SortHelperFixture.cs @@ -12,16 +12,12 @@ using NzbDrone.Core.Providers; using NzbDrone.Core.Repository; using NzbDrone.Core.Test.Framework; -namespace NzbDrone.Core.Test +namespace NzbDrone.Core.Test.HelperTests { [TestFixture] // ReSharper disable InconsistentNaming public class SortHelperTest : CoreTest { - //American Gladiators - //Ancient Apocalypse - //There Will Be Brawl - [TestCase("The Office (US)", "Office (US)")] [TestCase("A Man in Anger", "Man in Anger")] [TestCase("An Idiot Abroad", "Idiot Abroad")] @@ -32,7 +28,7 @@ namespace NzbDrone.Core.Test [TestCase(null, "")] public void SkipArticles(string title, string expected) { - var result = SortHelper.SkipArticles(title); + var result = title.IgnoreArticles(); result.Should().Be(expected); } } diff --git a/NzbDrone.Core.Test/HelperTests/XElementHelperTests/ConvertToDayOfWeekFixture.cs b/NzbDrone.Core.Test/HelperTests/XElementHelperTests/ConvertToDayOfWeekFixture.cs new file mode 100644 index 000000000..424c8f4a1 --- /dev/null +++ b/NzbDrone.Core.Test/HelperTests/XElementHelperTests/ConvertToDayOfWeekFixture.cs @@ -0,0 +1,67 @@ +// ReSharper disable RedundantUsingDirective + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using FluentAssertions; +using NUnit.Framework; +using Ninject; +using NzbDrone.Common; +using NzbDrone.Core.Helpers; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; +using TvdbLib.Data; +using TvdbLib.Exceptions; + +namespace NzbDrone.Core.Test.HelperTests.XElementHelperTests +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class ParseDayOfWeekFixture : CoreTest + { + [Test] + public void should_return_null_if_xelement_is_null() + { + XElement test = null; + test.ConvertToDayOfWeek().Should().Be(null); + } + + [Test] + public void should_return_null_if_value_is_null() + { + new XElement("airday", null).ConvertToDayOfWeek().Should().Be(null); + } + + [Test] + public void should_return_null_if_value_is_empty() + { + new XElement("airday", "").ConvertToDayOfWeek().Should().Be(null); + } + + [Test] + public void should_return_null_if_value_is_daily() + { + new XElement("airday", "Daily").ConvertToDayOfWeek().Should().Be(null); + } + + [Test] + public void should_return_null_if_value_is_weekdays() + { + new XElement("airday", "Weekdays").ConvertToDayOfWeek().Should().Be(null); + } + + [TestCase("Sunday", DayOfWeek.Sunday)] + [TestCase("Monday", DayOfWeek.Monday)] + [TestCase("Tuesday", DayOfWeek.Tuesday)] + [TestCase("Wednesday", DayOfWeek.Wednesday)] + [TestCase("Thursday", DayOfWeek.Thursday)] + [TestCase("Friday", DayOfWeek.Friday)] + [TestCase("Saturday", DayOfWeek.Saturday)] + public void should_return_dayOfWeek_when_it_is_valid(string value, DayOfWeek expected) + { + new XElement("airday", value).ConvertToDayOfWeek().Should().Be(expected); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/HelperTests/XElementHelperTests/ConvertToTFixture.cs b/NzbDrone.Core.Test/HelperTests/XElementHelperTests/ConvertToTFixture.cs new file mode 100644 index 000000000..71256c40d --- /dev/null +++ b/NzbDrone.Core.Test/HelperTests/XElementHelperTests/ConvertToTFixture.cs @@ -0,0 +1,70 @@ +// ReSharper disable RedundantUsingDirective +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.Helpers; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.HelperTests.XElementHelperTests +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class XElementHelperTest : CoreTest + { + [Test] + public void Int32_should_return_zero_when_xelement_is_null() + { + XElement test = null; + + test.ConvertTo().Should().Be(0); + } + + [Test] + public void Int32_should_return_zero_when_value_is_null() + { + new XElement("test", null).ConvertTo().Should().Be(0); + } + + [Test] + public void Int32_should_return_value_when_value_is_an_int() + { + new XElement("test", 10).ConvertTo().Should().Be(10); + } + + [Test] + public void Nullable_Int32_should_return_null_when_xelement_is_null() + { + XElement test = null; + + test.ConvertTo>().Should().Be(null); + } + + [Test] + public void DateTime_should_return_zero_when_xelement_is_null() + { + XElement test = null; + + test.ConvertTo().Should().Be(DateTime.MinValue); + } + + [Test] + public void DateTime_should_return_zero_when_value_is_null() + { + new XElement("test", null).ConvertTo().Should().Be(DateTime.MinValue); + } + + [Test] + public void DateTime_should_return_value_when_value_is_a_date() + { + var date = DateTime.Today; + new XElement("test", date.ToString()).ConvertTo().Should().Be(date); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/JobTests/DiskScanJobTest.cs b/NzbDrone.Core.Test/JobTests/DiskScanJobTest.cs index f5590c3fe..a126348a2 100644 --- a/NzbDrone.Core.Test/JobTests/DiskScanJobTest.cs +++ b/NzbDrone.Core.Test/JobTests/DiskScanJobTest.cs @@ -27,8 +27,6 @@ namespace NzbDrone.Core.Test.JobTests .With(s => s.SeriesId = 12) .Build(); - WithStrictMocker(); - Mocker.GetMock() .Setup(p => p.GetSeries(series.SeriesId)) .Returns(series); @@ -54,8 +52,6 @@ namespace NzbDrone.Core.Test.JobTests .TheNext(1).With(s => s.SeriesId = 15) .Build(); - WithStrictMocker(); - Mocker.GetMock() .Setup(p => p.GetAllSeries()) .Returns(series); @@ -82,8 +78,6 @@ namespace NzbDrone.Core.Test.JobTests .TheNext(1).With(s => s.SeriesId = 15) .Build(); - WithStrictMocker(); - Mocker.GetMock() .Setup(p => p.GetAllSeries()) .Returns(series); @@ -111,8 +105,6 @@ namespace NzbDrone.Core.Test.JobTests .TheNext(1).With(s => s.SeriesId = 15) .Build(); - WithStrictMocker(); - Mocker.GetMock() .Setup(p => p.GetAllSeries()) .Returns(series); diff --git a/NzbDrone.Core.Test/JobTests/RenameSeasonJobFixture.cs b/NzbDrone.Core.Test/JobTests/RenameSeasonJobFixture.cs new file mode 100644 index 000000000..e7ae66846 --- /dev/null +++ b/NzbDrone.Core.Test/JobTests/RenameSeasonJobFixture.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using FizzWare.NBuilder; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.Jobs; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Repository; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.JobTests +{ + [TestFixture] + public class RenameSeasonJobFixture : TestBase + { + private ProgressNotification _testNotification; + private Series _series; + private IList _episodeFiles; + + [SetUp] + public void Setup() + { + _testNotification = new ProgressNotification("TEST"); + + _series = Builder + .CreateNew() + .Build(); + + _episodeFiles = Builder + .CreateListOfSize(5) + .All() + .With(e => e.SeasonNumber = 5) + .Build(); + + Mocker.GetMock() + .Setup(s => s.GetSeries(_series.SeriesId)) + .Returns(_series); + + Mocker.GetMock() + .Setup(s => s.GetSeasonFiles(_series.SeriesId, 5)) + .Returns(_episodeFiles); + } + + private void WithMovedFiles() + { + Mocker.GetMock() + .Setup(s => s.MoveEpisodeFile(It.IsAny(), false)) + .Returns(_episodeFiles.First()); + } + + [Test] + public void should_throw_if_seriesId_is_zero() + { + Assert.Throws(() => + Mocker.Resolve().Start(_testNotification, new { SeriesId = 0, SeasonNumber = 10 })); + } + + [Test] + public void should_throw_if_seasonId_is_less_than_zero() + { + Assert.Throws(() => + Mocker.Resolve().Start(_testNotification, new { SeriesId = _series.SeriesId, SeasonNumber = -10 })); + } + + [Test] + public void should_log_warning_if_no_episode_files_are_found() + { + Mocker.Resolve().Start(_testNotification, new { SeriesId = _series.SeriesId, SeasonNumber = 10 }); + + ExceptionVerification.ExpectedWarns(1); + } + + [Test] + public void should_return_if_no_episodes_are_moved() + { + Mocker.Resolve().Start(_testNotification, new { SeriesId = _series.SeriesId, SeasonNumber = 5 }); + + Mocker.GetMock().Verify(v => v.RemoveForEpisodeFiles(It.IsAny>()), Times.Never()); + } + + [Test] + public void should_return_process_metadata_if_files_are_moved() + { + WithMovedFiles(); + Mocker.Resolve().Start(_testNotification, new { SeriesId = _series.SeriesId, SeasonNumber = 5 }); + + Mocker.GetMock().Verify(v => v.RemoveForEpisodeFiles(It.IsAny>()), Times.Once()); + } + } +} diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 7b028df3f..56e5ac8c9 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -144,6 +144,15 @@ + + + + + + + + + @@ -204,7 +213,7 @@ - + @@ -333,6 +342,21 @@ Designer Always + + Always + + + Always + + + Always + + + Always + + + Always + Always diff --git a/NzbDrone.Core.Test/ParserFixture/ParserTest.cs b/NzbDrone.Core.Test/ParserFixture/ParserTest.cs index d7f65f732..b7cdfbebc 100644 --- a/NzbDrone.Core.Test/ParserFixture/ParserTest.cs +++ b/NzbDrone.Core.Test/ParserFixture/ParserTest.cs @@ -72,6 +72,10 @@ namespace NzbDrone.Core.Test.ParserFixture [TestCase("Top Gear - 07x03 - 2005.11.70", "Top Gear", 7, 3)] [TestCase("Hatfields and McCoys 2012 Part 1 REPACK 720p HDTV x264 2HD", "Hatfields and McCoys 2012", 1, 1)] [TestCase("Glee.S04E09.Swan.Song.1080p.WEB-DL.DD5.1.H.264-ECI", "Glee", 4, 9)] + [TestCase("S08E20 50-50 Carla [DVD]", "", 8, 20)] + [TestCase("Cheers S08E20 50-50 Carla [DVD]", "Cheers", 8, 20)] + [TestCase("S02E10 6-50 to SLC [SDTV]", "", 2, 10)] + [TestCase("Franklin & Bash S02E10 6-50 to SLC [SDTV]", "Franklin & Bash", 2, 10)] public void ParseTitle_single(string postTitle, string title, int seasonNumber, int episodeNumber) { var result = Parser.ParseTitle(postTitle); @@ -396,5 +400,12 @@ namespace NzbDrone.Core.Test.ParserFixture { Parser.ParseHeader(title).Should().Be(expected); } + + [TestCase("password - \"bdc435cb-93c4-4902-97ea-ca00568c3887.337\" yEnc")] + public void should_not_parse_encypted_posts(string title) + { + Parser.ParseTitle(title).Should().BeNull(); + ExceptionVerification.IgnoreWarns(); + } } } diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/GetSeriesTitleFixture.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/GetSeriesTitleFixture.cs new file mode 100644 index 000000000..2fad2dfa1 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/GetSeriesTitleFixture.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Repository; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests +{ + public class GetSeriesTitleFixture : TestBase + { + private Series _series; + private const string SCENE_NAME = "Scandal"; + + [SetUp] + public void Setup() + { + _series = Builder + .CreateNew() + .With(s => s.Title = "Scandal (2012)") + .Build(); + } + + private void WithSceneName() + { + Mocker.GetMock() + .Setup(s => s.GetSceneName(_series.SeriesId)) + .Returns("Scandal"); + } + + [Test] + public void should_return_scene_name_when_sceneName_is_available() + { + WithSceneName(); + + Mocker.Resolve().GetSeriesTitle(_series).Should().Be(SCENE_NAME); + } + + [Test] + public void should_return_seriesTitle_when_sceneName_is_not_available() + { + Mocker.Resolve().GetSeriesTitle(_series).Should().Be(_series.Title); + } + + [TestCase("Mike & Molly", "Mike and Molly")] + [TestCase("Franklin & Bash", "Franklin and Bash")] + [TestCase("Law & Order", "Law and Order")] + public void should_replace_ampersand_with_and_when_returning_title(string seriesTitle, string expectedTitle) + { + _series.Title = seriesTitle; + + Mocker.Resolve().GetSeriesTitle(_series).Should().Be(expectedTitle); + } + } +} diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs index 111081e4e..f3de2b4c2 100644 --- a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs @@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests .Returns(parseResults); _episodeIndexer1.Setup(c => c.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(parseResults); - + _episodeIndexer1.Setup(s => s.Name).Returns("Episode Indexer 1"); _episodeIndexer2 = new Mock(); _episodeIndexer2.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) @@ -74,14 +74,17 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests .Returns(parseResults); _episodeIndexer2.Setup(c => c.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(parseResults); + _episodeIndexer2.Setup(s => s.Name).Returns("Episode Indexer 2"); _brokenIndexer = new Mock(); _brokenIndexer.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) .Throws(new Exception()); + _brokenIndexer.Setup(s => s.Name).Returns("Broken Indexer"); _nullIndexer = new Mock(); _nullIndexer.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) .Returns>(null); + _nullIndexer.Setup(s => s.Name).Returns("Null Indexer"); } private void WithTwoGoodOneBrokenIndexer() diff --git a/NzbDrone.Core.Test/ProviderTests/TvDbProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/TvDbProviderTest.cs index 413a5abc9..9f792ed68 100644 --- a/NzbDrone.Core.Test/ProviderTests/TvDbProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/TvDbProviderTest.cs @@ -36,6 +36,7 @@ namespace NzbDrone.Core.Test.ProviderTests [TestCase("The Simpsons")] [TestCase("Family Guy")] [TestCase("South Park")] + [TestCase("Franklin & Bash")] public void successful_search(string title) { var result = tvDbProvider.SearchSeries(title); diff --git a/NzbDrone.Core.Test/ProviderTests/TvRageMappingProviderTests/FindMatchingTvRageSeriesFixture.cs b/NzbDrone.Core.Test/ProviderTests/TvRageMappingProviderTests/FindMatchingTvRageSeriesFixture.cs new file mode 100644 index 000000000..b6f4eafa1 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/TvRageMappingProviderTests/FindMatchingTvRageSeriesFixture.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Model.TvRage; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Repository; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.ProviderTests.TvRageMappingProviderTests +{ + public class FindMatchingTvRageSeriesFixture : TestBase + { + private IList _searchResults; + private Series _series; + private Episode _episode; + private TvRageSeries _tvRageSeries; + + [SetUp] + public void Setup() + { + _searchResults = Builder + .CreateListOfSize(5) + .Build(); + + _series = Builder + .CreateNew() + .With(s => s.TvRageId = 0) + .With(s => s.TvRageTitle = null) + .With(s => s.UtcOffset = 0) + .Build(); + + _episode = Builder + .CreateNew() + .With(e => e.AirDate = DateTime.Today.AddDays(-365)) + .Build(); + + _tvRageSeries = Builder + .CreateNew() + .With(s => s.UtcOffset = -8) + .Build(); + + Mocker.GetMock() + .Setup(s => s.GetEpisode(_series.SeriesId, 1, 1)) + .Returns(_episode); + + Mocker.GetMock() + .Setup(s => s.GetCleanName(_series.SeriesId)) + .Returns(""); + + Mocker.GetMock() + .Setup(s => s.SearchSeries(_series.Title)) + .Returns(_searchResults); + + Mocker.GetMock() + .Setup(s => s.GetSeries(_searchResults.First().ShowId)) + .Returns(_tvRageSeries); + } + + private void WithMatchingResult() + { + _series.CleanTitle = Parser.NormalizeTitle(_searchResults.First().Name); + } + + [Test] + public void should_not_set_tvRage_info_when_result_is_null() + { + var result = Mocker.Resolve() + .FindMatchingTvRageSeries(_series); + + result.TvRageId.Should().Be(0); + result.TvRageTitle.Should().Be(null); + result.UtcOffset.Should().Be(0); + } + + [Test] + public void should_set_tvRage_info_when_result_is_returned() + { + WithMatchingResult(); + + var result = Mocker.Resolve() + .FindMatchingTvRageSeries(_series); + + result.TvRageId.Should().Be(_searchResults.First().ShowId); + result.TvRageTitle.Should().Be(_searchResults.First().Name); + result.UtcOffset.Should().Be(_tvRageSeries.UtcOffset); + } + } +} diff --git a/NzbDrone.Core.Test/ProviderTests/TvRageMappingProviderTests/ProcessResultsFixture.cs b/NzbDrone.Core.Test/ProviderTests/TvRageMappingProviderTests/ProcessResultsFixture.cs new file mode 100644 index 000000000..afd8e0779 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/TvRageMappingProviderTests/ProcessResultsFixture.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Model.TvRage; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Repository; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.ProviderTests.TvRageMappingProviderTests +{ + public class ProcessResultsFixture : TestBase + { + private IList _searchResults; + private Series _series; + private Episode _episode; + + [SetUp] + public void Setup() + { + _searchResults = Builder + .CreateListOfSize(5) + .Build(); + + _series = Builder.CreateNew().Build(); + + _episode = Builder + .CreateNew() + .With(e => e.AirDate = DateTime.Today.AddDays(-365)) + .Build(); + } + + [Test] + public void should_return_null_if_no_match_is_found() + { + Mocker.Resolve() + .ProcessResults(_searchResults, _series, "nomatchhere", _episode) + .Should() + .BeNull(); + } + + [Test] + public void should_return_result_if_series_clean_name_matches() + { + _series.CleanTitle = Parser.NormalizeTitle(_searchResults.First().Name); + + Mocker.Resolve() + .ProcessResults(_searchResults, _series, "nomatchhere", _episode) + .Should() + .Be(_searchResults.First()); + } + + [Test] + public void should_return_result_if_scene_clean_name_matches() + { + Mocker.Resolve() + .ProcessResults(_searchResults, _series, Parser.NormalizeTitle(_searchResults.First().Name), _episode) + .Should() + .Be(_searchResults.First()); + } + + [Test] + public void should_return_result_if_firstAired_matches() + { + _episode.AirDate = _searchResults.Last().Started; + + Mocker.Resolve() + .ProcessResults(_searchResults, _series, "nomatchhere", _episode) + .Should() + .Be(_searchResults.Last()); + } + } +} diff --git a/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetSeriesFixture.cs b/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetSeriesFixture.cs new file mode 100644 index 000000000..d5adac66a --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetSeriesFixture.cs @@ -0,0 +1,59 @@ +// ReSharper disable RedundantUsingDirective + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using Ninject; +using NzbDrone.Common; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; +using TvdbLib.Data; +using TvdbLib.Exceptions; + +namespace NzbDrone.Core.Test.ProviderTests.TvRageProviderTests +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class GetSeriesFixture : CoreTest + { + private const string showinfo = "http://services.tvrage.com/feeds/showinfo.php?key=NW4v0PSmQIoVmpbASLdD&sid="; + + private void WithEmptyResults() + { + Mocker.GetMock() + .Setup(s => s.DownloadStream(It.Is(u => u.StartsWith(showinfo)), null)) + .Returns(new FileStream(@".\Files\TVRage\SeriesInfo_empty.xml", FileMode.Open, FileAccess.Read, FileShare.Read)); + } + + private void WithOneResult() + { + Mocker.GetMock() + .Setup(s => s.DownloadStream(It.Is(u => u.StartsWith(showinfo)), null)) + .Returns(new FileStream(@".\Files\TVRage\SeriesInfo_one.xml", FileMode.Open, FileAccess.Read, FileShare.Read)); + } + + [Test] + public void should_be_null_when_no_showinfo_is_returned() + { + WithEmptyResults(); + Mocker.Resolve().GetSeries(100).Should().BeNull(); + + ExceptionVerification.ExpectedWarns(1); + } + + [Test] + public void should_return_series_when_showinfo_is_valid() + { + WithOneResult(); + var result = Mocker.Resolve().GetSeries(29999); + + result.ShowId.Should().Be(29999); + result.Name.Should().Be("Anger Management"); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetUtcOffsetFixture.cs b/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetUtcOffsetFixture.cs new file mode 100644 index 000000000..8c55feaef --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetUtcOffsetFixture.cs @@ -0,0 +1,50 @@ +// ReSharper disable RedundantUsingDirective + +using System; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using NUnit.Framework; +using Ninject; +using NzbDrone.Common; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; +using TvdbLib.Data; +using TvdbLib.Exceptions; + +namespace NzbDrone.Core.Test.ProviderTests.TvRageProviderTests +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class GetUtcOffsetFixture : CoreTest + { + [Test] + public void should_return_zero_if_timeZone_is_empty() + { + Mocker.Resolve().GetUtcOffset("").Should().Be(0); + } + + [Test] + public void should_return_zero_if_cannot_be_coverted_to_int() + { + Mocker.Resolve().GetUtcOffset("adfhadfhdjaf").Should().Be(0); + } + + [TestCase("GMT-5", -5)] + [TestCase("GMT+0", 0)] + [TestCase("GMT+8", 8)] + public void should_return_offset_when_not_dst(string timezone, int expected) + { + Mocker.Resolve().GetUtcOffset(timezone).Should().Be(expected); + } + + [TestCase("GMT-5 +DST", -4)] + [TestCase("GMT+0 +DST", 1)] + [TestCase("GMT+8 +DST", 9)] + public void should_return_offset_plus_one_when_dst(string timezone, int expected) + { + Mocker.Resolve().GetUtcOffset(timezone).Should().Be(expected); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/SearchSeriesFixture.cs b/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/SearchSeriesFixture.cs new file mode 100644 index 000000000..1d64de299 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/SearchSeriesFixture.cs @@ -0,0 +1,82 @@ +// ReSharper disable RedundantUsingDirective + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using Ninject; +using NzbDrone.Common; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; +using TvdbLib.Data; +using TvdbLib.Exceptions; + +namespace NzbDrone.Core.Test.ProviderTests.TvRageProviderTests +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class SearchSeriesFixture : CoreTest + { + private const string search = "http://services.tvrage.com/feeds/full_search.php?show="; + + private void WithEmptyResults() + { + Mocker.GetMock() + .Setup(s => s.DownloadStream(It.Is(u => u.StartsWith(search)), null)) + .Returns(new FileStream(@".\Files\TVRage\SearchResults_empty.xml", FileMode.Open, FileAccess.Read, FileShare.Read)); + } + + private void WithManyResults() + { + Mocker.GetMock() + .Setup(s => s.DownloadStream(It.Is(u => u.StartsWith(search)), null)) + .Returns(new FileStream(@".\Files\TVRage\SearchResults_many.xml", FileMode.Open, FileAccess.Read, FileShare.Read)); + } + + private void WithOneResult() + { + Mocker.GetMock() + .Setup(s => s.DownloadStream(It.Is(u => u.StartsWith(search)), null)) + .Returns(new FileStream(@".\Files\TVRage\SearchResults_one.xml", FileMode.Open, FileAccess.Read, FileShare.Read)); + } + + [Test] + public void should_be_empty_when_no_results_are_found() + { + WithEmptyResults(); + Mocker.Resolve().SearchSeries("asdasdasdasdas").Should().BeEmpty(); + } + + [Test] + public void should_be_have_more_than_one_when_multiple_results_are_returned() + { + WithManyResults(); + Mocker.Resolve().SearchSeries("top+gear").Should().NotBeEmpty(); + } + + [Test] + public void should_have_one_when_only_one_result_is_found() + { + WithOneResult(); + Mocker.Resolve().SearchSeries("suits").Should().HaveCount(1); + } + + [Test] + public void ended_should_not_have_a_value_when_series_has_not_ended() + { + WithOneResult(); + Mocker.Resolve().SearchSeries("suits").First().Ended.HasValue.Should().BeFalse(); + } + + [Test] + public void started_should_match_series() + { + WithOneResult(); + Mocker.Resolve().SearchSeries("suits").First().Started.Should().Be(new DateTime(2011, 6, 23)); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/Migrations/Migration20121218.cs b/NzbDrone.Core/Datastore/Migrations/Migration20121218.cs new file mode 100644 index 000000000..00061a9e1 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migrations/Migration20121218.cs @@ -0,0 +1,18 @@ +using System; +using System.Data; +using Migrator.Framework; +using NzbDrone.Common; + +namespace NzbDrone.Core.Datastore.Migrations +{ + [Migration(20121218)] + public class Migration20121218 : NzbDroneMigration + { + protected override void MainDbUpgrade() + { + Database.AddColumn("Series", new Column("TvRageId", DbType.Int32, ColumnProperty.Null)); + Database.AddColumn("Series", new Column("TvRageTitle", DbType.String, ColumnProperty.Null)); + Database.AddColumn("Series", new Column("UtcOffset", DbType.Int32, ColumnProperty.Null)); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Fluent.cs b/NzbDrone.Core/Fluent.cs index 98e484f1d..4889d8cab 100644 --- a/NzbDrone.Core/Fluent.cs +++ b/NzbDrone.Core/Fluent.cs @@ -112,6 +112,12 @@ namespace NzbDrone.Core private const Decimal ONE_GIGABYTE = ONE_MEGABYTE * 1024M; public static string ToBestFileSize(this long bytes, int precision = 0) + { + var ulongBytes = (ulong)bytes; + return ulongBytes.ToBestFileSize(precision); + } + + public static string ToBestFileSize(this ulong bytes, int precision = 0) { if (bytes == 0) return "0B"; diff --git a/NzbDrone.Core/Helpers/SortHelper.cs b/NzbDrone.Core/Helpers/SortHelper.cs index 853e570ce..bee13809c 100644 --- a/NzbDrone.Core/Helpers/SortHelper.cs +++ b/NzbDrone.Core/Helpers/SortHelper.cs @@ -5,9 +5,9 @@ using System.Text; namespace NzbDrone.Core.Helpers { - public class SortHelper + public static class SortHelper { - public static string SkipArticles(string input) + public static string IgnoreArticles(this string input) { if (String.IsNullOrEmpty(input)) return String.Empty; diff --git a/NzbDrone.Core/Helpers/XElementHelper.cs b/NzbDrone.Core/Helpers/XElementHelper.cs new file mode 100644 index 000000000..d455474dd --- /dev/null +++ b/NzbDrone.Core/Helpers/XElementHelper.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace NzbDrone.Core.Helpers +{ + public static class XElementHelper + { + public static T ConvertTo(this XElement element) + { + if (element == null) + return default(T); + + if (String.IsNullOrEmpty(element.Value)) + return default(T); + + var converter = TypeDescriptor.GetConverter(typeof(T)); + try + { + return (T)converter.ConvertFromString(element.Value); + } + + catch + { + return default(T); + } + } + + public static DayOfWeek? ConvertToDayOfWeek(this XElement element) + { + if (element == null) + return null; + + if (String.IsNullOrWhiteSpace(element.Value)) + return null; + + try + { + return (DayOfWeek)Enum.Parse(typeof(DayOfWeek), element.Value); + } + catch (Exception) + { + } + + return null; + } + } +} diff --git a/NzbDrone.Core/Jobs/DiskScanJob.cs b/NzbDrone.Core/Jobs/DiskScanJob.cs index aeb97a8d4..7100f7bb1 100644 --- a/NzbDrone.Core/Jobs/DiskScanJob.cs +++ b/NzbDrone.Core/Jobs/DiskScanJob.cs @@ -15,13 +15,16 @@ namespace NzbDrone.Core.Jobs { private readonly SeriesProvider _seriesProvider; private readonly DiskScanProvider _diskScanProvider; + private readonly ConfigProvider _configProvider; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); [Inject] - public DiskScanJob(SeriesProvider seriesProvider, DiskScanProvider diskScanProvider) + public DiskScanJob(SeriesProvider seriesProvider, DiskScanProvider diskScanProvider, + ConfigProvider configProvider) { _seriesProvider = seriesProvider; _diskScanProvider = diskScanProvider; + _configProvider = configProvider; } public DiskScanJob() @@ -43,7 +46,11 @@ namespace NzbDrone.Core.Jobs IList seriesToScan; if (options == null || options.SeriesId == 0) { - seriesToScan = _seriesProvider.GetAllSeries().OrderBy(o => SortHelper.SkipArticles(o.Title)).ToList(); + if (_configProvider.IgnoreArticlesWhenSortingSeries) + seriesToScan = _seriesProvider.GetAllSeries().OrderBy(o => o.Title.IgnoreArticles()).ToList(); + + else + seriesToScan = _seriesProvider.GetAllSeries().OrderBy(o => o.Title).ToList(); } else { diff --git a/NzbDrone.Core/Jobs/RenameSeasonJob.cs b/NzbDrone.Core/Jobs/RenameSeasonJob.cs index f442fa372..de771c24f 100644 --- a/NzbDrone.Core/Jobs/RenameSeasonJob.cs +++ b/NzbDrone.Core/Jobs/RenameSeasonJob.cs @@ -85,12 +85,20 @@ namespace NzbDrone.Core.Jobs } } + if(!oldEpisodeFiles.Any()) + { + logger.Trace("No episodes were renamed for: {0} Season {1}, no changes were made", series.Title, + options.SeasonNumber); + notification.CurrentMessage = String.Format("Rename completed for: {0} Season {1}, no changes were made", series.Title, options.SeasonNumber); + return; + } + //Remove & Create Metadata for episode files + //Todo: Add a metadata manager to avoid this hack _metadataProvider.RemoveForEpisodeFiles(oldEpisodeFiles); _metadataProvider.CreateForEpisodeFiles(newEpisodeFiles); //Start AfterRename - var message = String.Format("Renamed: Series {0}, Season: {1}", series.Title, options.SeasonNumber); _externalNotificationProvider.AfterRename(message, series); diff --git a/NzbDrone.Core/Jobs/UpdateInfoJob.cs b/NzbDrone.Core/Jobs/UpdateInfoJob.cs index 269879608..f7cebf4f6 100644 --- a/NzbDrone.Core/Jobs/UpdateInfoJob.cs +++ b/NzbDrone.Core/Jobs/UpdateInfoJob.cs @@ -6,6 +6,7 @@ using Ninject; using NzbDrone.Core.Helpers; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Repository; namespace NzbDrone.Core.Jobs @@ -15,15 +16,17 @@ namespace NzbDrone.Core.Jobs private readonly SeriesProvider _seriesProvider; private readonly EpisodeProvider _episodeProvider; private readonly ReferenceDataProvider _referenceDataProvider; + private readonly ConfigProvider _configProvider; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); [Inject] public UpdateInfoJob(SeriesProvider seriesProvider, EpisodeProvider episodeProvider, - ReferenceDataProvider referenceDataProvider) + ReferenceDataProvider referenceDataProvider, ConfigProvider configProvider) { _seriesProvider = seriesProvider; _episodeProvider = episodeProvider; _referenceDataProvider = referenceDataProvider; + _configProvider = configProvider; } public UpdateInfoJob() @@ -46,7 +49,11 @@ namespace NzbDrone.Core.Jobs IList seriesToUpdate; if (options == null || options.SeriesId == 0) { - seriesToUpdate = _seriesProvider.GetAllSeries().OrderBy(o => SortHelper.SkipArticles(o.Title)).ToList(); + if (_configProvider.IgnoreArticlesWhenSortingSeries) + seriesToUpdate = _seriesProvider.GetAllSeries().OrderBy(o => o.Title.IgnoreArticles()).ToList(); + + else + seriesToUpdate = _seriesProvider.GetAllSeries().OrderBy(o => o.Title).ToList(); } else { diff --git a/NzbDrone.Core/Model/EpisodeParseResult.cs b/NzbDrone.Core/Model/EpisodeParseResult.cs index 991e7a16f..59ab062d6 100644 --- a/NzbDrone.Core/Model/EpisodeParseResult.cs +++ b/NzbDrone.Core/Model/EpisodeParseResult.cs @@ -8,6 +8,7 @@ namespace NzbDrone.Core.Model public class EpisodeParseResult { public string SeriesTitle { get; set; } + public string CleanTitle { get diff --git a/NzbDrone.Core/Model/TvRage/TvRageEpisode.cs b/NzbDrone.Core/Model/TvRage/TvRageEpisode.cs new file mode 100644 index 000000000..e7149fb11 --- /dev/null +++ b/NzbDrone.Core/Model/TvRage/TvRageEpisode.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Model.TvRage +{ + public class TvRageEpisode + { + public int EpisodeNumber { get; set; } + public int SeasonNumber { get; set; } + public string ProductionCode { get; set; } + public DateTime AirDate { get; set; } + public string Link { get; set; } + public string Title { get; set; } + } +} diff --git a/NzbDrone.Core/Model/TvRage/TvRageSearchResult.cs b/NzbDrone.Core/Model/TvRage/TvRageSearchResult.cs new file mode 100644 index 000000000..9d7c6a8b6 --- /dev/null +++ b/NzbDrone.Core/Model/TvRage/TvRageSearchResult.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Model.TvRage +{ + public class TvRageSearchResult + { + public int ShowId { get; set; } + public string Name { get; set; } + public string Link { get; set; } + public string Country { get; set; } + public DateTime Started { get; set; } + public DateTime? Ended { get; set; } + public int Seasons { get; set; } + public string Status { get; set; } + public int RunTime { get; set; } + public DateTime AirTime { get; set; } + public DayOfWeek? AirDay { get; set; } + } +} diff --git a/NzbDrone.Core/Model/TvRage/TvRageSeries.cs b/NzbDrone.Core/Model/TvRage/TvRageSeries.cs new file mode 100644 index 000000000..ebc69f022 --- /dev/null +++ b/NzbDrone.Core/Model/TvRage/TvRageSeries.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Model.TvRage +{ + public class TvRageSeries + { + public int ShowId { get; set; } + public string Name { get; set; } + public string Link { get; set; } + public int Seasons { get; set; } + public int Started { get; set; } + public DateTime StartDate { get; set; } + public DateTime Ended { get; set; } + public string OriginCountry { get; set; } + public string Status { get; set; } + public int RunTime { get; set; } + public string Network { get; set; } + public DateTime AirTime { get; set; } + public DayOfWeek? AirDay { get; set; } + public int UtcOffset { get; set; } + } +} diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 29ac80896..8e2c7a77a 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -229,6 +229,7 @@ + @@ -262,6 +263,7 @@ + @@ -292,6 +294,9 @@ + + + @@ -346,6 +351,8 @@ + + diff --git a/NzbDrone.Core/Parser.cs b/NzbDrone.Core/Parser.cs index 44dc8128b..b39abbeff 100644 --- a/NzbDrone.Core/Parser.cs +++ b/NzbDrone.Core/Parser.cs @@ -30,7 +30,7 @@ namespace NzbDrone.Core RegexOptions.IgnoreCase | RegexOptions.Compiled), //Episodes without a title, Single (S01E05, 1x05) AND Multi (S01E04E05, 1x04x05, etc) - new Regex(@"^(?:S?(?(?\d{2}(?!\d+)))+\W*)+\W?(?!\\)", + new Regex(@"^(?:S?(?(?\d{2}(?!\d+)))+)\W?(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled), //Episodes with a title, Single episodes (S01E05, 1x05, etc) & Multi-episode (S01E05E06, S01E05-06, S01E05 E06, etc) @@ -140,7 +140,8 @@ namespace NzbDrone.Core } catch (Exception e) { - Logger.ErrorException("An error has occurred while trying to parse " + title, e); + if (!title.ToLower().Contains("password") && !title.ToLower().Contains("yenc")) + Logger.ErrorException("An error has occurred while trying to parse " + title, e); } Logger.Trace("Unable to parse {0}", title); @@ -150,7 +151,7 @@ namespace NzbDrone.Core private static EpisodeParseResult ParseMatchCollection(MatchCollection matchCollection) { - var seriesName = matchCollection[0].Groups["title"].Value; + var seriesName = matchCollection[0].Groups["title"].Value.Replace('.', ' '); int airyear; Int32.TryParse(matchCollection[0].Groups["airyear"].Value, out airyear); diff --git a/NzbDrone.Core/Providers/Core/ConfigProvider.cs b/NzbDrone.Core/Providers/Core/ConfigProvider.cs index dee849168..7ff283afc 100644 --- a/NzbDrone.Core/Providers/Core/ConfigProvider.cs +++ b/NzbDrone.Core/Providers/Core/ConfigProvider.cs @@ -529,6 +529,13 @@ namespace NzbDrone.Core.Providers.Core set { SetValue("OmgwtfnzbsApiKey", value); } } + public virtual Boolean IgnoreArticlesWhenSortingSeries + { + get { return GetValueBoolean("IgnoreArticlesWhenSortingSeries", true); } + + set { SetValue("IgnoreArticlesWhenSortingSeries", value); } + } + private string GetValue(string key) { return GetValue(key, String.Empty); diff --git a/NzbDrone.Core/Providers/RootDirProvider.cs b/NzbDrone.Core/Providers/RootDirProvider.cs index ee30580b3..b49336655 100644 --- a/NzbDrone.Core/Providers/RootDirProvider.cs +++ b/NzbDrone.Core/Providers/RootDirProvider.cs @@ -4,6 +4,7 @@ using System.IO; using Ninject; using NLog; using NzbDrone.Common; +using NzbDrone.Core.Model; using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Repository; using PetaPoco; @@ -25,8 +26,6 @@ namespace NzbDrone.Core.Providers _seriesProvider = seriesProvider; } - #region IRootDirProvider - public virtual List GetAll() { return _database.Fetch(); @@ -51,7 +50,7 @@ namespace NzbDrone.Core.Providers _database.Delete(rootDirId); } - public List GetUnmappedFolders(string path) + public virtual List GetUnmappedFolders(string path) { Logger.Debug("Generating list of unmapped folders"); if (String.IsNullOrEmpty(path)) @@ -77,26 +76,16 @@ namespace NzbDrone.Core.Providers return results; } - public virtual string GetMostFreeRootDir() + public virtual List AllWithFreeSpace() { - ulong maxSize = 0; - var maxPath = String.Empty; - var rootDirs = GetAll(); foreach (var rootDir in rootDirs) { rootDir.FreeSpace = _diskProvider.FreeDiskSpace(new DirectoryInfo(rootDir.Path)); - if (rootDir.FreeSpace > maxSize) - { - maxPath = rootDir.Path; - maxSize = rootDir.FreeSpace; - } } - return maxPath; + return rootDirs; } - - #endregion } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/SceneMappingProvider.cs b/NzbDrone.Core/Providers/SceneMappingProvider.cs index f5961c59b..bc0f6b61d 100644 --- a/NzbDrone.Core/Providers/SceneMappingProvider.cs +++ b/NzbDrone.Core/Providers/SceneMappingProvider.cs @@ -101,5 +101,15 @@ namespace NzbDrone.Core.Providers return false; } + + public virtual string GetCleanName(int seriesId) + { + var item = _database.FirstOrDefault("WHERE SeriesId = @0", seriesId); + + if (item == null) + return null; + + return item.CleanTitle; + } } } diff --git a/NzbDrone.Core/Providers/SearchProvider.cs b/NzbDrone.Core/Providers/SearchProvider.cs index 63d263d0c..5c611ff56 100644 --- a/NzbDrone.Core/Providers/SearchProvider.cs +++ b/NzbDrone.Core/Providers/SearchProvider.cs @@ -570,8 +570,11 @@ namespace NzbDrone.Core.Providers { var title = _sceneMappingProvider.GetSceneName(series.SeriesId); - if (String.IsNullOrWhiteSpace(title)) + if(String.IsNullOrWhiteSpace(title)) + { title = series.Title; + title = title.Replace("&", "and"); + } return title; } diff --git a/NzbDrone.Core/Providers/SeriesProvider.cs b/NzbDrone.Core/Providers/SeriesProvider.cs index 26b6caa5c..43b766074 100644 --- a/NzbDrone.Core/Providers/SeriesProvider.cs +++ b/NzbDrone.Core/Providers/SeriesProvider.cs @@ -15,19 +15,24 @@ namespace NzbDrone.Core.Providers { public class SeriesProvider { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private readonly ConfigProvider _configProvider; private readonly TvDbProvider _tvDbProvider; private readonly IDatabase _database; private readonly SceneMappingProvider _sceneNameMappingProvider; private readonly BannerProvider _bannerProvider; private readonly MetadataProvider _metadataProvider; + private readonly TvRageMappingProvider _tvRageMappingProvider; + + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + private static readonly Regex TimeRegex = new Regex(@"^(? diff --git a/NzbDrone.Web/Views/Settings/Misc.cshtml b/NzbDrone.Web/Views/Settings/Misc.cshtml index fbc82091c..c2230d6ce 100644 --- a/NzbDrone.Web/Views/Settings/Misc.cshtml +++ b/NzbDrone.Web/Views/Settings/Misc.cshtml @@ -27,6 +27,11 @@ @Html.TextBoxFor(m => m.AllowedReleaseGroups, new { @class = "inputClass" }) + + @Html.CheckBoxFor(m => m.IgnoreArticlesWhenSortingSeries, new { @class = "inputClass checkClass" }) +
diff --git a/NzbDrone.Web/Views/Shared/FreeSpace.cshtml b/NzbDrone.Web/Views/Shared/FreeSpace.cshtml new file mode 100644 index 000000000..5eb2ba311 --- /dev/null +++ b/NzbDrone.Web/Views/Shared/FreeSpace.cshtml @@ -0,0 +1,15 @@ +@using NzbDrone.Core +@model IEnumerable +@{ + Layout = null; +} + + +@{ + foreach(var rootDir in Model) + { +
+ @rootDir.FreeSpace.ToBestFileSize(1) Free +
+ } +} \ No newline at end of file diff --git a/NzbDrone.Web/Views/Shared/_Layout.cshtml b/NzbDrone.Web/Views/Shared/_Layout.cshtml index d744b5d00..8d3ac22ef 100644 --- a/NzbDrone.Web/Views/Shared/_Layout.cshtml +++ b/NzbDrone.Web/Views/Shared/_Layout.cshtml @@ -30,9 +30,16 @@ @MvcHtmlString.Create(Html.CurrentControllerLink("Missing", "Index", "Missing")) @MvcHtmlString.Create(Html.CurrentControllerLink("Settings", "Index", "Settings")) @MvcHtmlString.Create(Html.CurrentControllerLink("Logs", "Index", "Log")) + - +
+
    +
  • @{ Html.RenderAction("FreeSpace", "Shared"); }
  • +
  • +
+
+