From f00b17ac478efc99c4a134414deb08d7ee2877b2 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Mon, 17 Dec 2012 22:41:08 -0800 Subject: [PATCH 1/5] 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 2/5] 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 4/5] 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 5/5] 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(@"^(?