From 52da5b643d459f6dc1f34d96af88dbed015658a9 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Tue, 22 Oct 2013 22:17:02 -0700 Subject: [PATCH] Using string for airdate instead of DateTime in models to prevent timezone issues Fixed: Manual search air by date shows can now be sent to download client --- src/NzbDrone.Api/Indexers/ReleaseResource.cs | 2 +- .../ParserTests/ParserFixture.cs | 3 +- .../ParsingServiceTests/GetEpisodesFixture.cs | 10 +++--- .../Search/DailyEpisodeMatchSpecification.cs | 4 +-- .../Definitions/DailyEpisodeSearchCriteria.cs | 4 +-- .../IndexerSearch/NzbSearchService.cs | 2 +- .../Indexers/IndexerFetchService.cs | 2 +- src/NzbDrone.Core/NzbDrone.Core.csproj | 1 + .../Parser/InvalidDateException.cs | 19 +++++++++++ .../Parser/Model/ParsedEpisodeInfo.cs | 11 +++++-- src/NzbDrone.Core/Parser/Parser.cs | 33 +++++++++++++------ src/NzbDrone.Core/Parser/ParsingService.cs | 8 ++--- src/NzbDrone.Core/Tv/EpisodeRepository.cs | 12 +++---- src/NzbDrone.Core/Tv/EpisodeService.cs | 8 ++--- 14 files changed, 79 insertions(+), 40 deletions(-) create mode 100644 src/NzbDrone.Core/Parser/InvalidDateException.cs diff --git a/src/NzbDrone.Api/Indexers/ReleaseResource.cs b/src/NzbDrone.Api/Indexers/ReleaseResource.cs index a745f3869..fda9ef67a 100644 --- a/src/NzbDrone.Api/Indexers/ReleaseResource.cs +++ b/src/NzbDrone.Api/Indexers/ReleaseResource.cs @@ -18,7 +18,7 @@ namespace NzbDrone.Api.Indexers public Boolean SceneSource { get; set; } public Int32 SeasonNumber { get; set; } public Language Language { get; set; } - public DateTime? AirDate { get; set; } + public String AirDate { get; set; } public String SeriesTitle { get; set; } public int[] EpisodeNumbers { get; set; } public Boolean Approved { get; set; } diff --git a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs index da0be0eb0..987917d9c 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using NzbDrone.Common.Expansive; using NzbDrone.Core.Parser; using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; using NzbDrone.Test.Common; namespace NzbDrone.Core.Test.ParserTests @@ -169,7 +170,7 @@ namespace NzbDrone.Core.Test.ParserTests var airDate = new DateTime(year, month, day); result.Should().NotBeNull(); result.SeriesTitle.Should().Be(title.CleanSeriesTitle()); - result.AirDate.Should().Be(airDate); + result.AirDate.Should().Be(airDate.ToString(Episode.AIR_DATE_FORMAT)); result.EpisodeNumbers.Should().BeNull(); } diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs index 35dc82373..b1d235dee 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs @@ -61,7 +61,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests private void GivenDailyParseResult() { - _parsedEpisodeInfo.AirDate = DateTime.Today; + _parsedEpisodeInfo.AirDate = DateTime.Today.ToString(Episode.AIR_DATE_FORMAT); } private void GivenSceneNumberingSeries() @@ -78,7 +78,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests Subject.Map(_parsedEpisodeInfo, _series.TvRageId); Mocker.GetMock() - .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Once()); + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Once()); } [Test] @@ -90,19 +90,19 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); Mocker.GetMock() - .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Never()); + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Never()); } [Test] public void should_fallback_to_daily_episode_lookup_when_search_criteria_episode_doesnt_match() { GivenDailySeries(); - _parsedEpisodeInfo.AirDate = DateTime.Today.AddDays(-5); + _parsedEpisodeInfo.AirDate = DateTime.Today.AddDays(-5).ToString(Episode.AIR_DATE_FORMAT); ; Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); Mocker.GetMock() - .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Once()); + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Once()); } [Test] diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs index daaf3146c..cee1ae288 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs @@ -34,9 +34,9 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search if (dailySearchSpec == null) return true; - var episode = _episodeService.GetEpisode(dailySearchSpec.Series.Id, dailySearchSpec.Airtime); + var episode = _episodeService.GetEpisode(dailySearchSpec.Series.Id, dailySearchSpec.AirDate.ToString(Episode.AIR_DATE_FORMAT)); - if (!remoteEpisode.ParsedEpisodeInfo.AirDate.HasValue || remoteEpisode.ParsedEpisodeInfo.AirDate.Value.ToString(Episode.AIR_DATE_FORMAT) != episode.AirDate) + if (!remoteEpisode.ParsedEpisodeInfo.IsDaily() || remoteEpisode.ParsedEpisodeInfo.AirDate != episode.AirDate) { _logger.Trace("Episode AirDate does not match searched episode number, skipping."); return false; diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/DailyEpisodeSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/DailyEpisodeSearchCriteria.cs index 72d7fb195..3ffba7578 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/DailyEpisodeSearchCriteria.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/DailyEpisodeSearchCriteria.cs @@ -4,11 +4,11 @@ namespace NzbDrone.Core.IndexerSearch.Definitions { public class DailyEpisodeSearchCriteria : SearchCriteriaBase { - public DateTime Airtime { get; set; } + public DateTime AirDate { get; set; } public override string ToString() { - return string.Format("[{0} : {1}", SceneTitle, Airtime); + return string.Format("[{0} : {1}", SceneTitle, AirDate); } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs b/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs index 96b52e8cf..0981c5eb9 100644 --- a/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs +++ b/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs @@ -98,7 +98,7 @@ namespace NzbDrone.Core.IndexerSearch { var airDate = DateTime.ParseExact(episode.AirDate, Episode.AIR_DATE_FORMAT, CultureInfo.InvariantCulture); var searchSpec = Get(series, new List{ episode }); - searchSpec.Airtime = airDate; + searchSpec.AirDate = airDate; return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); } diff --git a/src/NzbDrone.Core/Indexers/IndexerFetchService.cs b/src/NzbDrone.Core/Indexers/IndexerFetchService.cs index b571f6466..2de0c51b0 100644 --- a/src/NzbDrone.Core/Indexers/IndexerFetchService.cs +++ b/src/NzbDrone.Core/Indexers/IndexerFetchService.cs @@ -87,7 +87,7 @@ namespace NzbDrone.Core.Indexers { _logger.Debug("Searching for {0}", searchCriteria); - var searchUrls = indexer.GetDailyEpisodeSearchUrls(searchCriteria.QueryTitle, searchCriteria.Series.TvRageId, searchCriteria.Airtime); + var searchUrls = indexer.GetDailyEpisodeSearchUrls(searchCriteria.QueryTitle, searchCriteria.Series.TvRageId, searchCriteria.AirDate); var result = Fetch(indexer, searchUrls); _logger.Info("Finished searching {0} for {1}. Found {2}", indexer, searchCriteria, result.Count); diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 3906a937b..e3527efb7 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -307,6 +307,7 @@ + diff --git a/src/NzbDrone.Core/Parser/InvalidDateException.cs b/src/NzbDrone.Core/Parser/InvalidDateException.cs new file mode 100644 index 000000000..dfc509295 --- /dev/null +++ b/src/NzbDrone.Core/Parser/InvalidDateException.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NzbDrone.Common.Exceptions; + +namespace NzbDrone.Core.Parser +{ + public class InvalidDateException : NzbDroneException + { + public InvalidDateException(string message, params object[] args) : base(message, args) + { + } + + public InvalidDateException(string message) : base(message) + { + } + } +} diff --git a/src/NzbDrone.Core/Parser/Model/ParsedEpisodeInfo.cs b/src/NzbDrone.Core/Parser/Model/ParsedEpisodeInfo.cs index 492248882..fe89f6dee 100644 --- a/src/NzbDrone.Core/Parser/Model/ParsedEpisodeInfo.cs +++ b/src/NzbDrone.Core/Parser/Model/ParsedEpisodeInfo.cs @@ -10,7 +10,7 @@ namespace NzbDrone.Core.Parser.Model public QualityModel Quality { get; set; } public int SeasonNumber { get; set; } public int[] EpisodeNumbers { get; set; } - public DateTime? AirDate { get; set; } + public String AirDate { get; set; } public Language Language { get; set; } public bool FullSeason { get; set; } @@ -19,9 +19,9 @@ namespace NzbDrone.Core.Parser.Model { string episodeString = "[Unknown Episode]"; - if (AirDate != null && EpisodeNumbers == null) + if (IsDaily() && EpisodeNumbers == null) { - episodeString = string.Format("{0}", AirDate.Value.ToString("yyyy-MM-dd")); + episodeString = string.Format("{0}", AirDate); } else if (FullSeason) { @@ -34,5 +34,10 @@ namespace NzbDrone.Core.Parser.Model return string.Format("{0} - {1} {2}", SeriesTitle, episodeString, Quality); } + + public bool IsDaily() + { + return !String.IsNullOrWhiteSpace(AirDate); + } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs index d98429894..a58464c96 100644 --- a/src/NzbDrone.Core/Parser/Parser.cs +++ b/src/NzbDrone.Core/Parser/Parser.cs @@ -6,6 +6,7 @@ using System.Text.RegularExpressions; using NLog; using NzbDrone.Common.Instrumentation; using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Tv; namespace NzbDrone.Core.Parser { @@ -110,16 +111,20 @@ namespace NzbDrone.Core.Parser if (match.Count != 0) { - var result = ParseMatchCollection(match); - if (result != null) + try { - //Check if episode is in the future (most likely a parse error) - if (result.AirDate > DateTime.Now.AddDays(1).Date || result.AirDate < new DateTime(1970, 1, 1)) - break; - - result.Language = ParseLanguage(title); - result.Quality = QualityParser.ParseQuality(title); - return result; + var result = ParseMatchCollection(match); + if (result != null) + { + result.Language = ParseLanguage(title); + result.Quality = QualityParser.ParseQuality(title); + return result; + } + } + catch (InvalidDateException ex) + { + Logger.TraceException(ex.Message, ex); + break; } } } @@ -212,9 +217,17 @@ namespace NzbDrone.Core.Parser airmonth = tempDay; } + var airDate = new DateTime(airYear, airmonth, airday); + + //Check if episode is in the future (most likely a parse error) + if (airDate > DateTime.Now.AddDays(1).Date || airDate < new DateTime(1970, 1, 1)) + { + throw new InvalidDateException("Invalid date found: {0}", airDate); + } + result = new ParsedEpisodeInfo { - AirDate = new DateTime(airYear, airmonth, airday).Date, + AirDate = airDate.ToString(Episode.AIR_DATE_FORMAT), }; } diff --git a/src/NzbDrone.Core/Parser/ParsingService.cs b/src/NzbDrone.Core/Parser/ParsingService.cs index b678c5eeb..134e507e3 100644 --- a/src/NzbDrone.Core/Parser/ParsingService.cs +++ b/src/NzbDrone.Core/Parser/ParsingService.cs @@ -104,7 +104,7 @@ namespace NzbDrone.Core.Parser { var result = new List(); - if (parsedEpisodeInfo.AirDate.HasValue) + if (parsedEpisodeInfo.IsDaily()) { if (series.SeriesType == SeriesTypes.Standard) { @@ -112,7 +112,7 @@ namespace NzbDrone.Core.Parser return null; } - var episodeInfo = GetDailyEpisode(series, parsedEpisodeInfo.AirDate.Value, searchCriteria); + var episodeInfo = GetDailyEpisode(series, parsedEpisodeInfo.AirDate, searchCriteria); if (episodeInfo != null) { @@ -223,14 +223,14 @@ namespace NzbDrone.Core.Parser return series; } - private Episode GetDailyEpisode(Series series, DateTime airDate, SearchCriteriaBase searchCriteria) + private Episode GetDailyEpisode(Series series, String airDate, SearchCriteriaBase searchCriteria) { Episode episodeInfo = null; if (searchCriteria != null) { episodeInfo = searchCriteria.Episodes.SingleOrDefault( - e => e.AirDate == airDate.ToString(Episode.AIR_DATE_FORMAT)); + e => e.AirDate == airDate); } if (episodeInfo == null) diff --git a/src/NzbDrone.Core/Tv/EpisodeRepository.cs b/src/NzbDrone.Core/Tv/EpisodeRepository.cs index 57d8acbc1..daba4d5e0 100644 --- a/src/NzbDrone.Core/Tv/EpisodeRepository.cs +++ b/src/NzbDrone.Core/Tv/EpisodeRepository.cs @@ -11,8 +11,8 @@ namespace NzbDrone.Core.Tv public interface IEpisodeRepository : IBasicRepository { Episode Find(int seriesId, int season, int episodeNumber); - Episode Get(int seriesId, DateTime date); - Episode Find(int seriesId, DateTime date); + Episode Get(int seriesId, String date); + Episode Find(int seriesId, String date); List GetEpisodes(int seriesId); List GetEpisodes(int seriesId, int seasonNumber); List GetEpisodeByFileId(int fileId); @@ -39,14 +39,14 @@ namespace NzbDrone.Core.Tv return Query.SingleOrDefault(s => s.SeriesId == seriesId && s.SeasonNumber == season && s.EpisodeNumber == episodeNumber); } - public Episode Get(int seriesId, DateTime date) + public Episode Get(int seriesId, String date) { - return Query.Single(s => s.SeriesId == seriesId && s.AirDate == date.ToString(Episode.AIR_DATE_FORMAT)); + return Query.Single(s => s.SeriesId == seriesId && s.AirDate == date); } - public Episode Find(int seriesId, DateTime date) + public Episode Find(int seriesId, String date) { - return Query.SingleOrDefault(s => s.SeriesId == seriesId && s.AirDate == date.ToString(Episode.AIR_DATE_FORMAT)); + return Query.SingleOrDefault(s => s.SeriesId == seriesId && s.AirDate == date); } public List GetEpisodes(int seriesId) diff --git a/src/NzbDrone.Core/Tv/EpisodeService.cs b/src/NzbDrone.Core/Tv/EpisodeService.cs index 8bd88187d..9ef46761f 100644 --- a/src/NzbDrone.Core/Tv/EpisodeService.cs +++ b/src/NzbDrone.Core/Tv/EpisodeService.cs @@ -14,8 +14,8 @@ namespace NzbDrone.Core.Tv { Episode GetEpisode(int id); Episode FindEpisode(int seriesId, int seasonNumber, int episodeNumber, bool useScene = false); - Episode GetEpisode(int seriesId, DateTime date); - Episode FindEpisode(int seriesId, DateTime date); + Episode GetEpisode(int seriesId, String date); + Episode FindEpisode(int seriesId, String date); List GetEpisodeBySeries(int seriesId); List GetEpisodesBySeason(int seriesId, int seasonNumber); PagingSpec EpisodesWithoutFiles(PagingSpec pagingSpec); @@ -62,12 +62,12 @@ namespace NzbDrone.Core.Tv return _episodeRepository.Find(seriesId, seasonNumber, episodeNumber); } - public Episode GetEpisode(int seriesId, DateTime date) + public Episode GetEpisode(int seriesId, String date) { return _episodeRepository.Get(seriesId, date); } - public Episode FindEpisode(int seriesId, DateTime date) + public Episode FindEpisode(int seriesId, String date) { return _episodeRepository.Find(seriesId, date); }