From f00b17ac478efc99c4a134414deb08d7ee2877b2 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Mon, 17 Dec 2012 22:41:08 -0800 Subject: [PATCH 01/22] Added TvRageProvider and model classes --- NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 2 + .../GetUtcOffsetFixture.cs | 50 ++++++ .../SearchSeriesFixture.cs | 82 +++++++++ NzbDrone.Core/Model/TvRage/TvRageEpisode.cs | 17 ++ .../Model/TvRage/TvRageSearchResult.cs | 22 +++ NzbDrone.Core/Model/TvRage/TvRageSeries.cs | 25 +++ NzbDrone.Core/NzbDrone.Core.csproj | 4 + NzbDrone.Core/Providers/TvRageProvider.cs | 168 ++++++++++++++++++ 8 files changed, 370 insertions(+) create mode 100644 NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetUtcOffsetFixture.cs create mode 100644 NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/SearchSeriesFixture.cs create mode 100644 NzbDrone.Core/Model/TvRage/TvRageEpisode.cs create mode 100644 NzbDrone.Core/Model/TvRage/TvRageSearchResult.cs create mode 100644 NzbDrone.Core/Model/TvRage/TvRageSeries.cs create mode 100644 NzbDrone.Core/Providers/TvRageProvider.cs diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 347380962..ae92b4807 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -139,6 +139,8 @@ + + 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/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..d109a991f --- /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..965738bbd --- /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 26db534af..4a0183fc6 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -291,6 +291,9 @@ + + + @@ -343,6 +346,7 @@ + diff --git a/NzbDrone.Core/Providers/TvRageProvider.cs b/NzbDrone.Core/Providers/TvRageProvider.cs new file mode 100644 index 000000000..51bc35a74 --- /dev/null +++ b/NzbDrone.Core/Providers/TvRageProvider.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using NLog; +using Ninject; +using NzbDrone.Common; +using NzbDrone.Core.Model.TvRage; + +namespace NzbDrone.Core.Providers +{ + public class TvRageProvider + { + private readonly HttpProvider _httpProvider; + private const string TVRAGE_APIKEY = "NW4v0PSmQIoVmpbASLdD"; + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + [Inject] + public TvRageProvider(HttpProvider httpProvider) + { + _httpProvider = httpProvider; + } + + public virtual IList SearchSeries(string title) + { + var searchResults = new List(); + + var xmlStream = _httpProvider.DownloadStream("http://services.tvrage.com/feeds/full_search.php?show=" + title, null); + + var xml = XDocument.Load(xmlStream); + var shows = xml.Descendants("Results").Descendants("show"); + + foreach (var s in shows) + { + try + { + var show = new TvRageSearchResult(); + show.ShowId = Int32.Parse(s.Element("showid").Value); + show.Name = s.Element("name").Value; + show.Link = s.Element("link").Value; + show.Country = s.Element("country").Value; + + DateTime started; + if (DateTime.TryParse(s.Element("started").Value, out started)) ; + show.Started = started; + + DateTime ended; + if (DateTime.TryParse(s.Element("ended").Value, out ended)) ; + show.Ended = ended; + + if (show.Ended < new DateTime(1900, 1, 1)) + show.Ended = null; + + show.Seasons = Int32.Parse(s.Element("seasons").Value); + show.Status = s.Element("status").Value; + show.RunTime = Int32.Parse(s.Element("runtime").Value); + show.AirTime = DateTime.Parse(s.Element("airtime").Value); + show.AirDay = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), s.Element("airday").Value); + + searchResults.Add(show); + } + + catch (Exception ex) + { + logger.DebugException("Failed to parse TvRage Search Result. Search Term : " + title, ex); + } + } + + return searchResults; + } + + public virtual TvRageSeries GetSeries(int id) + { + var url = string.Format("http://services.tvrage.com/feeds/showinfo.php?key={0}sid={1}", TVRAGE_APIKEY, id); + var xmlStream = _httpProvider.DownloadStream(url, null); + var xml = XDocument.Load(xmlStream); + var s = xml.Descendants("Showinfo").First(); + try + { + var show = new TvRageSeries(); + show.ShowId = Int32.Parse(s.Element("showid").Value); + show.Name = s.Element("showname").Value; + show.Link = s.Element("showlink").Value; + show.Seasons = Int32.Parse(s.Element("seasons").Value); + show.Started = Int32.Parse(s.Element("started").Value); + + DateTime startDate; + if (DateTime.TryParse(s.Element("startdate").Value, out startDate)) ; + show.StartDate = startDate; + + DateTime ended; + if (DateTime.TryParse(s.Element("ended").Value, out ended)) ; + show.Ended = ended; + + show.OriginCountry = s.Element("origin_country").Value; + show.Status = s.Element("status").Value; + show.RunTime = Int32.Parse(s.Element("runtime").Value); + show.Network = s.Element("network").Value; + show.AirTime = DateTime.Parse(s.Element("airtime").Value); + show.AirDay = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), s.Element("airday").Value); + show.UtcOffset = GetUtcOffset(s.Element("timezone").Value); + return show; + } + + catch (Exception ex) + { + logger.DebugException("Failed to parse ShowInfo for ID: " + id, ex); + return null; + } + } + + public virtual List GetEpisodes(int id) + { + var url = String.Format("http://services.tvrage.com/feeds/episode_list.php?key={0}sid={1}", TVRAGE_APIKEY, id); + var xmlStream = _httpProvider.DownloadStream(url, null); + var xml = XDocument.Load(xmlStream); + var show = xml.Descendants("Show"); + var seasons = show.Descendants("Season"); + + var episodes = new List(); + + foreach (var season in seasons) + { + var eps = season.Descendants("episode"); + + foreach (var e in eps) + { + try + { + var episode = new TvRageEpisode(); + episode.EpisodeNumber = Int32.Parse(e.Element("epnum").Value); + episode.SeasonNumber = Int32.Parse(e.Element("seasonnum").Value); + episode.ProductionCode = e.Element("prodnum").Value; + episode.AirDate = DateTime.Parse(e.Element("airdate").Value); + episode.Link = e.Element("link").Value; + episode.Title = e.Element("title").Value; + episodes.Add(episode); + } + + catch (Exception ex) + { + logger.DebugException("Failed to parse TV Rage episode", ex); + } + } + } + + return episodes; + } + + internal int GetUtcOffset(string timeZone) + { + if (String.IsNullOrWhiteSpace(timeZone)) + return 0; + + var offsetString = timeZone.Substring(3, 2); + int offset; + + if (!Int32.TryParse(offsetString, out offset)) + return 0; + + if (timeZone.IndexOf("+DST", StringComparison.CurrentCultureIgnoreCase) > 0) + offset++; + + return offset; + } + } +} From 253426873c48ca47a5f1e5f0a31b838d70711120 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Wed, 19 Dec 2012 08:41:51 -0800 Subject: [PATCH 02/22] Matching TvRage to TvDb #ND-15 In Progress --- .../Files/TvRage/SearchResults_empty.xml | 3 + .../Files/TvRage/SearchResults_many.xml | 420 ++++++++++++++++++ .../Files/TvRage/SearchResults_one.xml | 26 ++ .../Files/TvRage/SeriesInfo_empty.xml | 3 + .../Files/TvRage/SeriesInfo_one.xml | 22 + NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 10 + .../TvRageProviderTests/GetSeriesFixture.cs | 57 +++ .../ParseDayOfWeekFixture.cs | 65 +++ .../Datastore/Migrations/Migration20121218.cs | 18 + .../Model/TvRage/TvRageSearchResult.cs | 2 +- NzbDrone.Core/Model/TvRage/TvRageSeries.cs | 2 +- NzbDrone.Core/NzbDrone.Core.csproj | 2 + .../Providers/SceneMappingProvider.cs | 10 + NzbDrone.Core/Providers/SeriesProvider.cs | 8 +- .../Providers/TvRageMappingProvider.cs | 69 +++ NzbDrone.Core/Providers/TvRageProvider.cs | 37 +- NzbDrone.Core/Repository/Series.cs | 6 + 17 files changed, 753 insertions(+), 7 deletions(-) create mode 100644 NzbDrone.Core.Test/Files/TvRage/SearchResults_empty.xml create mode 100644 NzbDrone.Core.Test/Files/TvRage/SearchResults_many.xml create mode 100644 NzbDrone.Core.Test/Files/TvRage/SearchResults_one.xml create mode 100644 NzbDrone.Core.Test/Files/TvRage/SeriesInfo_empty.xml create mode 100644 NzbDrone.Core.Test/Files/TvRage/SeriesInfo_one.xml create mode 100644 NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetSeriesFixture.cs create mode 100644 NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/ParseDayOfWeekFixture.cs create mode 100644 NzbDrone.Core/Datastore/Migrations/Migration20121218.cs create mode 100644 NzbDrone.Core/Providers/TvRageMappingProvider.cs 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/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index ae92b4807..fb07bea6a 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -139,6 +139,7 @@ + @@ -330,6 +331,15 @@ Designer Always + + Always + + + Always + + + Always + Always diff --git a/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetSeriesFixture.cs b/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetSeriesFixture.cs new file mode 100644 index 000000000..bea08e55e --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetSeriesFixture.cs @@ -0,0 +1,57 @@ +// 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(); + } + + [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/ParseDayOfWeekFixture.cs b/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/ParseDayOfWeekFixture.cs new file mode 100644 index 000000000..95cf3699d --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/ParseDayOfWeekFixture.cs @@ -0,0 +1,65 @@ +// 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.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 ParseDayOfWeekFixture : CoreTest + { + [Test] + public void should_return_null_if_xelement_is_null() + { + Mocker.Resolve().ParseDayOfWeek(null).Should().Be(null); + } + + [Test] + public void should_return_null_if_value_is_null() + { + Mocker.Resolve().ParseDayOfWeek(new XElement("airday", null)).Should().Be(null); + } + + [Test] + public void should_return_null_if_value_is_empty() + { + Mocker.Resolve().ParseDayOfWeek(new XElement("airday", "")).Should().Be(null); + } + + [Test] + public void should_return_null_if_value_is_daily() + { + Mocker.Resolve().ParseDayOfWeek(new XElement("airday", "Daily")).Should().Be(null); + } + + [Test] + public void should_return_null_if_value_is_weekdays() + { + Mocker.Resolve().ParseDayOfWeek(new XElement("airday", "Weekdays")).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) + { + Mocker.Resolve().ParseDayOfWeek(new XElement("airday", value)).Should().Be(expected); + } + } +} \ 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/Model/TvRage/TvRageSearchResult.cs b/NzbDrone.Core/Model/TvRage/TvRageSearchResult.cs index d109a991f..9d7c6a8b6 100644 --- a/NzbDrone.Core/Model/TvRage/TvRageSearchResult.cs +++ b/NzbDrone.Core/Model/TvRage/TvRageSearchResult.cs @@ -17,6 +17,6 @@ namespace NzbDrone.Core.Model.TvRage public string Status { get; set; } public int RunTime { get; set; } public DateTime AirTime { get; set; } - public DayOfWeek AirDay { get; set; } + public DayOfWeek? AirDay { get; set; } } } diff --git a/NzbDrone.Core/Model/TvRage/TvRageSeries.cs b/NzbDrone.Core/Model/TvRage/TvRageSeries.cs index 965738bbd..ebc69f022 100644 --- a/NzbDrone.Core/Model/TvRage/TvRageSeries.cs +++ b/NzbDrone.Core/Model/TvRage/TvRageSeries.cs @@ -19,7 +19,7 @@ namespace NzbDrone.Core.Model.TvRage public int RunTime { get; set; } public string Network { get; set; } public DateTime AirTime { get; set; } - public DayOfWeek AirDay { 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 4a0183fc6..402f146d9 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -228,6 +228,7 @@ + @@ -346,6 +347,7 @@ + diff --git a/NzbDrone.Core/Providers/SceneMappingProvider.cs b/NzbDrone.Core/Providers/SceneMappingProvider.cs index ad70a6d65..d4f119d80 100644 --- a/NzbDrone.Core/Providers/SceneMappingProvider.cs +++ b/NzbDrone.Core/Providers/SceneMappingProvider.cs @@ -99,5 +99,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/SeriesProvider.cs b/NzbDrone.Core/Providers/SeriesProvider.cs index ecbded829..37e3e4dfb 100644 --- a/NzbDrone.Core/Providers/SeriesProvider.cs +++ b/NzbDrone.Core/Providers/SeriesProvider.cs @@ -21,11 +21,13 @@ namespace NzbDrone.Core.Providers private readonly SceneMappingProvider _sceneNameMappingProvider; private readonly BannerProvider _bannerProvider; private readonly MetadataProvider _metadataProvider; + private readonly TvRageMappingProvider _tvRageMappingProvider; private static readonly Regex TimeRegex = new Regex(@"^(? + + + @@ -331,6 +334,12 @@ Designer Always + + Always + + + Always + Always 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()); + } + } +} From 23019ebda6bd89046ad80d7ba6f6b344d751e67d Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Wed, 19 Dec 2012 20:48:05 -0800 Subject: [PATCH 04/22] Fixed broken test --- .../ProviderTests/TvRageProviderTests/GetSeriesFixture.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetSeriesFixture.cs b/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetSeriesFixture.cs index bea08e55e..d5adac66a 100644 --- a/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetSeriesFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/GetSeriesFixture.cs @@ -42,6 +42,8 @@ namespace NzbDrone.Core.Test.ProviderTests.TvRageProviderTests { WithEmptyResults(); Mocker.Resolve().GetSeries(100).Should().BeNull(); + + ExceptionVerification.ExpectedWarns(1); } [Test] From 7fb8f05421f93d8f930e6bc7a8b0bec008c0abb7 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Wed, 19 Dec 2012 22:17:18 -0800 Subject: [PATCH 05/22] Prevent TvRage issues from breaking InfoUpdate --- NzbDrone.Core/Providers/SeriesProvider.cs | 35 +++++++++++++++-------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/NzbDrone.Core/Providers/SeriesProvider.cs b/NzbDrone.Core/Providers/SeriesProvider.cs index 37e3e4dfb..70d167ae9 100644 --- a/NzbDrone.Core/Providers/SeriesProvider.cs +++ b/NzbDrone.Core/Providers/SeriesProvider.cs @@ -14,7 +14,7 @@ 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; @@ -22,6 +22,9 @@ namespace NzbDrone.Core.Providers 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.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; } From 5113bf38782c9b459822d624b1a4a103c061d240 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 20 Dec 2012 13:23:09 -0800 Subject: [PATCH 08/22] Renaming Season will succeed if no files are moved Fixed: Renaming a season that is already named correctly will not error --- .../JobTests/RenameSeasonJobFixture.cs | 93 +++++++++++++++++++ NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 1 + NzbDrone.Core/Jobs/RenameSeasonJob.cs | 10 +- 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 NzbDrone.Core.Test/JobTests/RenameSeasonJobFixture.cs 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 7c4d44a22..f3aa10b36 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -139,6 +139,7 @@ + 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); From c2f96791413edf22cdfabc87663048cbc64ead1c Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 20 Dec 2012 13:27:54 -0800 Subject: [PATCH 09/22] Ampersand removed from title before searching Fixed: Remove "&" from title before searching TheTVDB --- NzbDrone.Core.Test/ProviderTests/TvDbProviderTest.cs | 1 + NzbDrone.Core/Providers/TvDbProvider.cs | 6 ++++++ 2 files changed, 7 insertions(+) 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/Providers/TvDbProvider.cs b/NzbDrone.Core/Providers/TvDbProvider.cs index 2019f1e48..9d7677d8f 100644 --- a/NzbDrone.Core/Providers/TvDbProvider.cs +++ b/NzbDrone.Core/Providers/TvDbProvider.cs @@ -37,6 +37,12 @@ namespace NzbDrone.Core.Providers { Logger.Debug("Searching TVDB for '{0}'", title); + if(title.Contains(" & ")) + { + Logger.Debug("Removing ampersand before searching"); + title = title.Replace(" & ", " "); + } + var result = _handler.SearchSeries(title); Logger.Debug("Search for '{0}' returned {1} possible results", title, result.Count); From 9bc64e7ea407f40493596c52449210d477b80674 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 20 Dec 2012 13:48:14 -0800 Subject: [PATCH 10/22] Fixed gitignore file --- .gitignore | 157 +++++++++++++----- .../GetSeriesTitleFixture.cs | 59 +++++++ 2 files changed, 172 insertions(+), 44 deletions(-) create mode 100644 NzbDrone.Core.Test/ProviderTests/SearchProviderTests/GetSeriesTitleFixture.cs diff --git a/.gitignore b/.gitignore index fa36d309e..89cf69ce6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,54 +1,123 @@ +# 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*/ -[Rrelease]*/ -*.lib -*.sbr -*.nzb -obj/ -[Rr]elease*/ -_ReSharper*/ -[Tt]est[Rr]esult[s] -[Nn]zbs -[Bb]uild/ -[Ll]ogs/ -[Aa]pp_Data/ -/[Pp]ackage/ -[Pp]ackages -#NZBDrone specific -*.db -*Web.Publish.xml -NzbDrone.Web/NzbDrone.Web.Publish.xml +*.vspscc +*.vssscc +.builds + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf *.sdf -[Bb]anners -*.orig -_rawPackage/ -NzbDrone.zip -NzbDrone.sln.DotSettings.user* -config.xml -UpdateLogs/ -NzbDrone.Web/MediaCover -NzbDrone.fpr -nzbdrone.log*txt -_rawPackage_service/ + +# 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 + +# 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.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); + } + } +} From 79c1d6a7921f654094c8d48a451fc8571195179c Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 20 Dec 2012 14:00:57 -0800 Subject: [PATCH 11/22] Added indexer names for test indexers. --- .../SearchProviderTests/PerformSearchFixture.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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() From fb5b7a9277ca89292bfb69cf72208bd0827231b4 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 20 Dec 2012 15:29:14 -0800 Subject: [PATCH 12/22] Encypted errors be gone! Fixed: Stop logging errors when parsing encrypted posts --- NzbDrone.Core.Test/ParserFixture/ParserTest.cs | 7 +++++++ NzbDrone.Core/Parser.cs | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/NzbDrone.Core.Test/ParserFixture/ParserTest.cs b/NzbDrone.Core.Test/ParserFixture/ParserTest.cs index d7f65f732..bd53c5699 100644 --- a/NzbDrone.Core.Test/ParserFixture/ParserTest.cs +++ b/NzbDrone.Core.Test/ParserFixture/ParserTest.cs @@ -396,5 +396,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/Parser.cs b/NzbDrone.Core/Parser.cs index be5df9131..467259c78 100644 --- a/NzbDrone.Core/Parser.cs +++ b/NzbDrone.Core/Parser.cs @@ -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); From d8f8cfb4628875153e947e1ac030f2e017a86c07 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 20 Dec 2012 17:38:54 -0800 Subject: [PATCH 13/22] CRO reporting for duty Fixed: Better parsing of certain filenames --- NzbDrone.Core.Test/ParserFixture/ParserTest.cs | 4 ++++ NzbDrone.Core/Parser.cs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NzbDrone.Core.Test/ParserFixture/ParserTest.cs b/NzbDrone.Core.Test/ParserFixture/ParserTest.cs index bd53c5699..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); diff --git a/NzbDrone.Core/Parser.cs b/NzbDrone.Core/Parser.cs index 467259c78..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) From 3d46a86bb071e22837a3c9a8505b0ebe833807c6 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 20 Dec 2012 17:49:57 -0800 Subject: [PATCH 14/22] Mark occasionally failing test as inconclusive --- NzbDrone.Common.Test/ProcessProviderTests.cs | 2 ++ 1 file changed, 2 insertions(+) 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)); } } From 978e56484558211341a696e0b9658de1a2040255 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 20 Dec 2012 19:49:52 -0800 Subject: [PATCH 15/22] Added a donation button --- NzbDrone.Web/Content/NzbDrone.css | 6 ++++++ NzbDrone.Web/Views/Shared/_Layout.cshtml | 1 + 2 files changed, 7 insertions(+) diff --git a/NzbDrone.Web/Content/NzbDrone.css b/NzbDrone.Web/Content/NzbDrone.css index 7a7ff0dc9..7ed59f728 100644 --- a/NzbDrone.Web/Content/NzbDrone.css +++ b/NzbDrone.Web/Content/NzbDrone.css @@ -261,4 +261,10 @@ button span, input[type="button"] span, input[type="submit"] span, input[type="r /* Font-Awesome */ i[class*="icon-"]:not(.gridAction):hover { cursor: default; +} + +/* Donate */ +#donate a { + background-color: #065EFE; + color: #191919; } \ No newline at end of file diff --git a/NzbDrone.Web/Views/Shared/_Layout.cshtml b/NzbDrone.Web/Views/Shared/_Layout.cshtml index bc9ebe4f7..dcf5a4a3f 100644 --- a/NzbDrone.Web/Views/Shared/_Layout.cshtml +++ b/NzbDrone.Web/Views/Shared/_Layout.cshtml @@ -30,6 +30,7 @@ @MvcHtmlString.Create(Html.CurrentControllerLink("Missing", "Index", "Missing")) @MvcHtmlString.Create(Html.CurrentControllerLink("Settings", "Index", "Settings")) @MvcHtmlString.Create(Html.CurrentControllerLink("Logs", "Index", "Log")) + From 2439b9e087aaf62c61f70b9133c6be115b42a0cf Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 20 Dec 2012 21:36:48 -0800 Subject: [PATCH 16/22] Allow sorting with articles (option) New: Option to sort with articles (a, the, an) included --- NzbDrone.Core.Test/SortHelperTest.cs | 6 +----- NzbDrone.Core/Helpers/SortHelper.cs | 4 ++-- NzbDrone.Core/Jobs/DiskScanJob.cs | 11 +++++++++-- NzbDrone.Core/Jobs/UpdateInfoJob.cs | 11 +++++++++-- NzbDrone.Core/Providers/Core/ConfigProvider.cs | 7 +++++++ .../NzbDrone.Services.Service.csproj | 2 +- NzbDrone.Web/Controllers/HistoryController.cs | 9 +++++++-- NzbDrone.Web/Controllers/MissingController.cs | 8 ++++++-- NzbDrone.Web/Controllers/SeriesController.cs | 15 ++++++++++----- NzbDrone.Web/Controllers/SettingsController.cs | 2 ++ NzbDrone.Web/Models/MiscSettingsModel.cs | 4 ++++ NzbDrone.Web/Views/Settings/Misc.cshtml | 5 +++++ 12 files changed, 63 insertions(+), 21 deletions(-) diff --git a/NzbDrone.Core.Test/SortHelperTest.cs b/NzbDrone.Core.Test/SortHelperTest.cs index 4035fb7d5..e85045c46 100644 --- a/NzbDrone.Core.Test/SortHelperTest.cs +++ b/NzbDrone.Core.Test/SortHelperTest.cs @@ -18,10 +18,6 @@ namespace NzbDrone.Core.Test // 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/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/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/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/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.Services/NzbDrone.Services.Service/NzbDrone.Services.Service.csproj b/NzbDrone.Services/NzbDrone.Services.Service/NzbDrone.Services.Service.csproj index 15eb7f6ee..0751835eb 100644 --- a/NzbDrone.Services/NzbDrone.Services.Service/NzbDrone.Services.Service.csproj +++ b/NzbDrone.Services/NzbDrone.Services.Service/NzbDrone.Services.Service.csproj @@ -362,7 +362,7 @@ False True - 32122 + 25289 / http://localhost:62182/ False diff --git a/NzbDrone.Web/Controllers/HistoryController.cs b/NzbDrone.Web/Controllers/HistoryController.cs index fc92aa598..7f37c4a1b 100644 --- a/NzbDrone.Web/Controllers/HistoryController.cs +++ b/NzbDrone.Web/Controllers/HistoryController.cs @@ -8,6 +8,7 @@ using DataTables.Mvc.Core.Models; using NzbDrone.Core.Helpers; using NzbDrone.Core.Jobs; using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; using NzbDrone.Web.Models; namespace NzbDrone.Web.Controllers @@ -16,11 +17,14 @@ namespace NzbDrone.Web.Controllers { private readonly HistoryProvider _historyProvider; private readonly JobProvider _jobProvider; + private readonly ConfigProvider _configProvider; - public HistoryController(HistoryProvider historyProvider, JobProvider jobProvider) + public HistoryController(HistoryProvider historyProvider, JobProvider jobProvider, + ConfigProvider configProvider) { _historyProvider = historyProvider; _jobProvider = jobProvider; + _configProvider = configProvider; } public ActionResult Index() @@ -32,6 +36,7 @@ namespace NzbDrone.Web.Controllers { var pageResult = _historyProvider.GetPagedItems(pageRequest); var totalItems = _historyProvider.Count(); + var ignoreArticles = _configProvider.IgnoreArticlesWhenSortingSeries; var items = pageResult.Items.Select(h => new HistoryModel { @@ -41,7 +46,7 @@ namespace NzbDrone.Web.Controllers EpisodeTitle = h.EpisodeTitle, EpisodeOverview = h.EpisodeOverview, SeriesTitle = h.SeriesTitle, - SeriesTitleSorter = SortHelper.SkipArticles(h.SeriesTitle), + SeriesTitleSorter = ignoreArticles ? h.SeriesTitle.IgnoreArticles() : h.SeriesTitle, NzbTitle = h.NzbTitle, Quality = h.Quality.ToString(), IsProper = h.IsProper, diff --git a/NzbDrone.Web/Controllers/MissingController.cs b/NzbDrone.Web/Controllers/MissingController.cs index c111dc82b..b55208871 100644 --- a/NzbDrone.Web/Controllers/MissingController.cs +++ b/NzbDrone.Web/Controllers/MissingController.cs @@ -8,6 +8,7 @@ using System.Web.Script.Serialization; using NzbDrone.Core; using NzbDrone.Core.Helpers; using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; using NzbDrone.Web.Models; using ServiceStack.Text; @@ -16,15 +17,18 @@ namespace NzbDrone.Web.Controllers public class MissingController : Controller { private readonly EpisodeProvider _episodeProvider; + private readonly ConfigProvider _configProvider; - public MissingController(EpisodeProvider episodeProvider) + public MissingController(EpisodeProvider episodeProvider, ConfigProvider configProvider) { _episodeProvider = episodeProvider; + _configProvider = configProvider; } public ActionResult Index() { var missingEpisodes = _episodeProvider.EpisodesWithoutFiles(false); + var ignoreArticles = _configProvider.IgnoreArticlesWhenSortingSeries; var missing = missingEpisodes.Select(e => new MissingEpisodeModel { @@ -34,7 +38,7 @@ namespace NzbDrone.Web.Controllers EpisodeTitle = e.Title, Overview = e.Overview, SeriesTitle = e.Series.Title, - SeriesTitleSorter = SortHelper.SkipArticles(e.Series.Title), + SeriesTitleSorter = ignoreArticles ? e.Series.Title.IgnoreArticles() : e.Series.Title, AirDateSorter = e.AirDate.Value.ToString("o", CultureInfo.InvariantCulture), AirDate = e.AirDate.Value.ToBestDateString() }); diff --git a/NzbDrone.Web/Controllers/SeriesController.cs b/NzbDrone.Web/Controllers/SeriesController.cs index 2a3a299b7..f630d420a 100644 --- a/NzbDrone.Web/Controllers/SeriesController.cs +++ b/NzbDrone.Web/Controllers/SeriesController.cs @@ -11,6 +11,7 @@ using NzbDrone.Core.Helpers; using NzbDrone.Core.Jobs; using NzbDrone.Core.Model; using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Repository; using NzbDrone.Core.Repository.Quality; using NzbDrone.Web.Filters; @@ -25,17 +26,19 @@ namespace NzbDrone.Web.Controllers private readonly SeriesProvider _seriesProvider; private readonly JobProvider _jobProvider; private readonly SeasonProvider _seasonProvider; + private readonly ConfigProvider _configProvider; // // GET: /Series/ - public SeriesController(SeriesProvider seriesProvider, - QualityProvider qualityProvider, JobProvider jobProvider, - SeasonProvider seasonProvider) + public SeriesController(SeriesProvider seriesProvider, QualityProvider qualityProvider, + JobProvider jobProvider, SeasonProvider seasonProvider, + ConfigProvider configProvider) { _seriesProvider = seriesProvider; _qualityProvider = qualityProvider; _jobProvider = jobProvider; _seasonProvider = seasonProvider; + _configProvider = configProvider; } public ActionResult Index() @@ -177,7 +180,7 @@ namespace NzbDrone.Web.Controllers masterBacklogList.Insert(0, new KeyValuePair(-10, "Select...")); ViewData["MasterBacklogSettingSelectList"] = new SelectList(masterBacklogList, "Key", "Value"); - var series = GetSeriesModels(_seriesProvider.GetAllSeries()).OrderBy(o => SortHelper.SkipArticles(o.Title)); + var series = GetSeriesModels(_seriesProvider.GetAllSeries()); return View(series); } @@ -206,11 +209,13 @@ namespace NzbDrone.Web.Controllers private List GetSeriesModels(IList seriesInDb) { + var ignoreArticles = _configProvider.IgnoreArticlesWhenSortingSeries; + var series = seriesInDb.Select(s => new SeriesModel { SeriesId = s.SeriesId, Title = s.Title, - TitleSorter = SortHelper.SkipArticles(s.Title), + TitleSorter = ignoreArticles? s.Title.IgnoreArticles() : s.Title, AirsDayOfWeek = s.AirsDayOfWeek.ToString(), Monitored = s.Monitored, Overview = s.Overview, diff --git a/NzbDrone.Web/Controllers/SettingsController.cs b/NzbDrone.Web/Controllers/SettingsController.cs index 9b50255fe..948facf33 100644 --- a/NzbDrone.Web/Controllers/SettingsController.cs +++ b/NzbDrone.Web/Controllers/SettingsController.cs @@ -253,6 +253,7 @@ namespace NzbDrone.Web.Controllers model.EnableBacklogSearching = _configProvider.EnableBacklogSearching; model.AutoIgnorePreviouslyDownloadedEpisodes = _configProvider.AutoIgnorePreviouslyDownloadedEpisodes; model.AllowedReleaseGroups = _configProvider.AllowedReleaseGroups; + model.IgnoreArticlesWhenSortingSeries = _configProvider.IgnoreArticlesWhenSortingSeries; return View(model); } @@ -662,6 +663,7 @@ namespace NzbDrone.Web.Controllers _configProvider.EnableBacklogSearching = data.EnableBacklogSearching; _configProvider.AutoIgnorePreviouslyDownloadedEpisodes = data.AutoIgnorePreviouslyDownloadedEpisodes; _configProvider.AllowedReleaseGroups = data.AllowedReleaseGroups; + _configProvider.IgnoreArticlesWhenSortingSeries = data.IgnoreArticlesWhenSortingSeries; return GetSuccessResult(); } diff --git a/NzbDrone.Web/Models/MiscSettingsModel.cs b/NzbDrone.Web/Models/MiscSettingsModel.cs index 7957f70a8..e069ae4af 100644 --- a/NzbDrone.Web/Models/MiscSettingsModel.cs +++ b/NzbDrone.Web/Models/MiscSettingsModel.cs @@ -20,5 +20,9 @@ namespace NzbDrone.Web.Models [Description("Comma separated list of release groups to download episodes (leave empty for all groups)")] [DisplayFormat(ConvertEmptyStringToNull = false)] public string AllowedReleaseGroups { get; set; } + + [DisplayName("Ignore Articles")] + [Description("Ignore articles when sorting by series title?")] + public bool IgnoreArticlesWhenSortingSeries { get; set; } } } \ No newline at end of file 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" }) +
From 8581896f58af40974b7e8502a2d2640b50add944 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 20 Dec 2012 21:44:59 -0800 Subject: [PATCH 17/22] Fixed broken tests and removed strict mocker --- NzbDrone.Core.Test/JobTests/DiskScanJobTest.cs | 8 -------- 1 file changed, 8 deletions(-) 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); From 580585dc104a13c8ef972295192fc8ed1fafb4ed Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Fri, 21 Dec 2012 09:38:35 -0800 Subject: [PATCH 18/22] Updated donation URL --- NzbDrone.Web/Views/Shared/_Layout.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NzbDrone.Web/Views/Shared/_Layout.cshtml b/NzbDrone.Web/Views/Shared/_Layout.cshtml index dcf5a4a3f..1ec68145b 100644 --- a/NzbDrone.Web/Views/Shared/_Layout.cshtml +++ b/NzbDrone.Web/Views/Shared/_Layout.cshtml @@ -30,7 +30,7 @@ @MvcHtmlString.Create(Html.CurrentControllerLink("Missing", "Index", "Missing")) @MvcHtmlString.Create(Html.CurrentControllerLink("Settings", "Index", "Settings")) @MvcHtmlString.Create(Html.CurrentControllerLink("Logs", "Index", "Log")) - + From 2bd866f590b37d5c289054d220bec1bdf4d0726e Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 22 Dec 2012 17:18:05 -0800 Subject: [PATCH 19/22] Better handling of xml errors on tvrage --- .../SortHelperFixture.cs} | 2 +- .../ConvertToDayOfWeekFixture.cs} | 14 ++-- .../XElementHelperTests/ConvertToTFixture.cs | 70 +++++++++++++++++++ NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 3 +- NzbDrone.Core/Helpers/XElementHelper.cs | 51 ++++++++++++++ NzbDrone.Core/NzbDrone.Core.csproj | 1 + NzbDrone.Core/Providers/TvRageProvider.cs | 66 +++++------------ 7 files changed, 152 insertions(+), 55 deletions(-) rename NzbDrone.Core.Test/{SortHelperTest.cs => HelperTests/SortHelperFixture.cs} (96%) rename NzbDrone.Core.Test/{ProviderTests/TvRageProviderTests/ParseDayOfWeekFixture.cs => HelperTests/XElementHelperTests/ConvertToDayOfWeekFixture.cs} (72%) create mode 100644 NzbDrone.Core.Test/HelperTests/XElementHelperTests/ConvertToTFixture.cs create mode 100644 NzbDrone.Core/Helpers/XElementHelper.cs diff --git a/NzbDrone.Core.Test/SortHelperTest.cs b/NzbDrone.Core.Test/HelperTests/SortHelperFixture.cs similarity index 96% rename from NzbDrone.Core.Test/SortHelperTest.cs rename to NzbDrone.Core.Test/HelperTests/SortHelperFixture.cs index e85045c46..19f6d028a 100644 --- a/NzbDrone.Core.Test/SortHelperTest.cs +++ b/NzbDrone.Core.Test/HelperTests/SortHelperFixture.cs @@ -12,7 +12,7 @@ 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 diff --git a/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/ParseDayOfWeekFixture.cs b/NzbDrone.Core.Test/HelperTests/XElementHelperTests/ConvertToDayOfWeekFixture.cs similarity index 72% rename from NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/ParseDayOfWeekFixture.cs rename to NzbDrone.Core.Test/HelperTests/XElementHelperTests/ConvertToDayOfWeekFixture.cs index 95cf3699d..3fe30360a 100644 --- a/NzbDrone.Core.Test/ProviderTests/TvRageProviderTests/ParseDayOfWeekFixture.cs +++ b/NzbDrone.Core.Test/HelperTests/XElementHelperTests/ConvertToDayOfWeekFixture.cs @@ -8,13 +8,14 @@ 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.ProviderTests.TvRageProviderTests +namespace NzbDrone.Core.Test.HelperTests.XElementHelperTests { [TestFixture] // ReSharper disable InconsistentNaming @@ -23,25 +24,26 @@ namespace NzbDrone.Core.Test.ProviderTests.TvRageProviderTests [Test] public void should_return_null_if_xelement_is_null() { - Mocker.Resolve().ParseDayOfWeek(null).Should().Be(null); + XElement test = null; + test.ConvertToDayOfWeek().Should().Be(null); } [Test] public void should_return_null_if_value_is_null() { - Mocker.Resolve().ParseDayOfWeek(new XElement("airday", null)).Should().Be(null); + new XElement("airday", null).ConvertToDayOfWeek().Should().Be(null); } [Test] public void should_return_null_if_value_is_empty() { - Mocker.Resolve().ParseDayOfWeek(new XElement("airday", "")).Should().Be(null); + new XElement("airday", "").ConvertToDayOfWeek().Should().Be(null); } [Test] public void should_return_null_if_value_is_daily() { - Mocker.Resolve().ParseDayOfWeek(new XElement("airday", "Daily")).Should().Be(null); + new XElement("airday", "Daily").ConvertToDayOfWeek().Should().Be(null); } [Test] @@ -59,7 +61,7 @@ namespace NzbDrone.Core.Test.ProviderTests.TvRageProviderTests [TestCase("Saturday", DayOfWeek.Saturday)] public void should_return_dayOfWeek_when_it_is_valid(string value, DayOfWeek expected) { - Mocker.Resolve().ParseDayOfWeek(new XElement("airday", value)).Should().Be(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/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index f3aa10b36..4c8b55320 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -139,6 +139,7 @@
+ @@ -207,7 +208,7 @@ - + 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/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 402f146d9..406132b38 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -262,6 +262,7 @@ + diff --git a/NzbDrone.Core/Providers/TvRageProvider.cs b/NzbDrone.Core/Providers/TvRageProvider.cs index 6347afdae..3570de1d2 100644 --- a/NzbDrone.Core/Providers/TvRageProvider.cs +++ b/NzbDrone.Core/Providers/TvRageProvider.cs @@ -6,6 +6,7 @@ using System.Xml.Linq; using NLog; using Ninject; using NzbDrone.Common; +using NzbDrone.Core.Helpers; using NzbDrone.Core.Model.TvRage; namespace NzbDrone.Core.Providers @@ -40,27 +41,22 @@ namespace NzbDrone.Core.Providers try { var show = new TvRageSearchResult(); - show.ShowId = Int32.Parse(s.Element("showid").Value); + show.ShowId = s.Element("showid").ConvertTo(); show.Name = s.Element("name").Value; show.Link = s.Element("link").Value; show.Country = s.Element("country").Value; - DateTime started; - if (DateTime.TryParse(s.Element("started").Value, out started)) ; - show.Started = started; - - DateTime ended; - if (DateTime.TryParse(s.Element("ended").Value, out ended)) ; - show.Ended = ended; + show.Started = s.Element("started").ConvertTo(); + show.Ended = s.Element("ended").ConvertTo(); if (show.Ended < new DateTime(1900, 1, 1)) show.Ended = null; - show.Seasons = Int32.Parse(s.Element("seasons").Value); + show.Seasons = s.Element("seasons").ConvertTo(); show.Status = s.Element("status").Value; - show.RunTime = Int32.Parse(s.Element("runtime").Value); - show.AirTime = DateTime.Parse(s.Element("airtime").Value); - show.AirDay = ParseDayOfWeek(s.Element("airday")); + show.RunTime = s.Element("seasons").ConvertTo(); + show.AirTime = s.Element("seasons").ConvertTo(); + show.AirDay = s.Element("airday").ConvertToDayOfWeek(); searchResults.Add(show); } @@ -89,26 +85,21 @@ namespace NzbDrone.Core.Providers } var show = new TvRageSeries(); - show.ShowId = Int32.Parse(s.Element("showid").Value); + show.ShowId = s.Element("showid").ConvertTo(); show.Name = s.Element("showname").Value; show.Link = s.Element("showlink").Value; - show.Seasons = Int32.Parse(s.Element("seasons").Value); - show.Started = Int32.Parse(s.Element("started").Value); - - DateTime startDate; - if (DateTime.TryParse(s.Element("startdate").Value, out startDate)) ; - show.StartDate = startDate; + show.Seasons = s.Element("seasons").ConvertTo(); + show.Started = s.Element("started").ConvertTo(); - DateTime ended; - if (DateTime.TryParse(s.Element("ended").Value, out ended)) ; - show.Ended = ended; + show.StartDate = s.Element("startdate").ConvertTo(); + show.Ended = s.Element("ended").ConvertTo(); show.OriginCountry = s.Element("origin_country").Value; show.Status = s.Element("status").Value; - show.RunTime = Int32.Parse(s.Element("runtime").Value); + show.RunTime = s.Element("runtime").ConvertTo(); show.Network = s.Element("network").Value; - show.AirTime = DateTime.Parse(s.Element("airtime").Value); - show.AirDay = ParseDayOfWeek(s.Element("airday")); + show.AirTime = s.Element("airtime").ConvertTo(); + show.AirDay = s.Element("airday").ConvertToDayOfWeek(); show.UtcOffset = GetUtcOffset(s.Element("timezone").Value); return show; } @@ -139,10 +130,10 @@ namespace NzbDrone.Core.Providers try { var episode = new TvRageEpisode(); - episode.EpisodeNumber = Int32.Parse(e.Element("epnum").Value); - episode.SeasonNumber = Int32.Parse(e.Element("seasonnum").Value); + episode.EpisodeNumber = e.Element("epnum").ConvertTo(); + episode.SeasonNumber = e.Element("seasonnum").ConvertTo(); episode.ProductionCode = e.Element("prodnum").Value; - episode.AirDate = DateTime.Parse(e.Element("airdate").Value); + episode.AirDate = e.Element("airdate").ConvertTo(); episode.Link = e.Element("link").Value; episode.Title = e.Element("title").Value; episodes.Add(episode); @@ -174,24 +165,5 @@ namespace NzbDrone.Core.Providers return offset; } - - internal DayOfWeek? ParseDayOfWeek(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; - } } } From 4212da9146db6aa99e26ae83df5ed06ff50fcab0 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 22 Dec 2012 21:35:36 -0800 Subject: [PATCH 20/22] Free diskspace in UI New: View Diskspace for root directories visible in UI #ND-98 fixed --- NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 4 +- NzbDrone.Core/Fluent.cs | 6 ++ NzbDrone.Core/Providers/RootDirProvider.cs | 19 ++----- NzbDrone.Web/Content/Menu.css | 2 + NzbDrone.Web/Content/NzbDrone.css | 58 +++++++++++++++----- NzbDrone.Web/Controllers/SharedController.cs | 14 ++++- NzbDrone.Web/NzbDrone.Web.csproj | 1 + NzbDrone.Web/Views/Shared/FreeSpace.cshtml | 15 +++++ NzbDrone.Web/Views/Shared/_Layout.cshtml | 8 ++- 9 files changed, 95 insertions(+), 32 deletions(-) create mode 100644 NzbDrone.Web/Views/Shared/FreeSpace.cshtml diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 4c8b55320..2e7cfd7c5 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -139,13 +139,13 @@ - + - + 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/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.Web/Content/Menu.css b/NzbDrone.Web/Content/Menu.css index 16f28f5f7..25e764c3e 100644 --- a/NzbDrone.Web/Content/Menu.css +++ b/NzbDrone.Web/Content/Menu.css @@ -1,6 +1,7 @@ #menu { height: 60px; + display: inline-block; } #menu ul @@ -9,6 +10,7 @@ padding: 5px 0px 5px 0px; list-style: none; line-height: normal; + overflow: hidden; } #menu li diff --git a/NzbDrone.Web/Content/NzbDrone.css b/NzbDrone.Web/Content/NzbDrone.css index 7ed59f728..d2e18480e 100644 --- a/NzbDrone.Web/Content/NzbDrone.css +++ b/NzbDrone.Web/Content/NzbDrone.css @@ -211,19 +211,6 @@ button span, input[type="button"] span, input[type="submit"] span, input[type="r cursor: pointer !important; } -/* Local Series Search */ -#localSeriesLookup -{ - width: 220px; - float: right; - margin-top: 7px; - margin-bottom: 0px; - border: 0px; - background: rgb(75, 75, 75); - color: rgb(169, 169, 169); - padding: 4px; -} - .ui-dialog-buttonset .ui-delete-button { background: url("jQueryUI/images/ui-bg_flat_30_b40404_40x100.png") repeat-x scroll 50% 50% #B40404; @@ -267,4 +254,49 @@ i[class*="icon-"]:not(.gridAction):hover { #donate a { background-color: #065EFE; color: #191919; +} + +/* Right Menu */ +#right-menu { + float: right; + display: inline-block; + height: 60px; +} + +#right-menu ul { + list-style: none; + overflow: hidden; +} + +#right-menu li +{ + display: block; + float: left; + padding: 0px 0px 10px 15px; +} + +#localSeriesLookup +{ + width: 220px; + border: 0px; + background: rgb(75, 75, 75); + color: rgb(169, 169, 169); + padding: 4px; +} + +.free-space { + display: inline-block; + margin-top: 2px; + padding-left: 5px; + padding-right: 5px; + height: 28px; + line-height: 28px; + background-color: #6e6e6e; + color: #d0d0d0; + /*color: #FFFFFF;*/ + cursor: default; +} + +.free-space span { + color: #191919; } \ No newline at end of file diff --git a/NzbDrone.Web/Controllers/SharedController.cs b/NzbDrone.Web/Controllers/SharedController.cs index a4ce35d43..ae99fa795 100644 --- a/NzbDrone.Web/Controllers/SharedController.cs +++ b/NzbDrone.Web/Controllers/SharedController.cs @@ -1,5 +1,6 @@ using System.Web.Mvc; using NzbDrone.Common; +using NzbDrone.Core.Providers; using NzbDrone.Web.Models; namespace NzbDrone.Web.Controllers @@ -7,10 +8,12 @@ namespace NzbDrone.Web.Controllers public class SharedController : Controller { private readonly EnvironmentProvider _environmentProvider; + private readonly RootDirProvider _rootDirProvider; - public SharedController(EnvironmentProvider environmentProvider) + public SharedController(EnvironmentProvider environmentProvider, RootDirProvider rootDirProvider) { _environmentProvider = environmentProvider; + _rootDirProvider = rootDirProvider; } public ActionResult Index() @@ -24,5 +27,14 @@ namespace NzbDrone.Web.Controllers { return PartialView(new FooterModel { BuildTime = _environmentProvider.BuildDateTime, Version = _environmentProvider.Version }); } + + [ChildActionOnly] + //[OutputCache(Duration = 600)] + public ActionResult FreeSpace() + { + var rootDirs = _rootDirProvider.AllWithFreeSpace(); + + return PartialView(rootDirs); + } } } \ No newline at end of file diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index 354702db6..e97e6f500 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -418,6 +418,7 @@ + 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 1ec68145b..6e83c9670 100644 --- a/NzbDrone.Web/Views/Shared/_Layout.cshtml +++ b/NzbDrone.Web/Views/Shared/_Layout.cshtml @@ -32,8 +32,14 @@ @MvcHtmlString.Create(Html.CurrentControllerLink("Logs", "Index", "Log")) - +
+
    +
  • @{ Html.RenderAction("FreeSpace", "Shared"); }
  • +
  • +
+
+ From 8f75de4a8be3b816282248b6bba540fc02567fe4 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 22 Dec 2012 21:40:07 -0800 Subject: [PATCH 21/22] Fixed broken test --- .../XElementHelperTests/ConvertToDayOfWeekFixture.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NzbDrone.Core.Test/HelperTests/XElementHelperTests/ConvertToDayOfWeekFixture.cs b/NzbDrone.Core.Test/HelperTests/XElementHelperTests/ConvertToDayOfWeekFixture.cs index 3fe30360a..424c8f4a1 100644 --- a/NzbDrone.Core.Test/HelperTests/XElementHelperTests/ConvertToDayOfWeekFixture.cs +++ b/NzbDrone.Core.Test/HelperTests/XElementHelperTests/ConvertToDayOfWeekFixture.cs @@ -49,7 +49,7 @@ namespace NzbDrone.Core.Test.HelperTests.XElementHelperTests [Test] public void should_return_null_if_value_is_weekdays() { - Mocker.Resolve().ParseDayOfWeek(new XElement("airday", "Weekdays")).Should().Be(null); + new XElement("airday", "Weekdays").ConvertToDayOfWeek().Should().Be(null); } [TestCase("Sunday", DayOfWeek.Sunday)] From 4804a18d6d78c236667a236d83cceebb637f49e3 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 22 Dec 2012 22:03:26 -0800 Subject: [PATCH 22/22] Cache freedisk space for 10 minutes --- NzbDrone.Web/Controllers/SharedController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NzbDrone.Web/Controllers/SharedController.cs b/NzbDrone.Web/Controllers/SharedController.cs index ae99fa795..f35196e55 100644 --- a/NzbDrone.Web/Controllers/SharedController.cs +++ b/NzbDrone.Web/Controllers/SharedController.cs @@ -29,7 +29,7 @@ namespace NzbDrone.Web.Controllers } [ChildActionOnly] - //[OutputCache(Duration = 600)] + [OutputCache(Duration = 600)] public ActionResult FreeSpace() { var rootDirs = _rootDirProvider.AllWithFreeSpace();