diff --git a/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/GetQualifiedReportsFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/GetQualifiedReportsFixture.cs index 4c8aafaa4..850421940 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/GetQualifiedReportsFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/GetQualifiedReportsFixture.cs @@ -14,7 +14,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests { [TestFixture] - public class DownloadApprovedReportsFixture : CoreTest + public class GetQualifiedReportsFixture : CoreTest { private Episode GetEpisode(int id) { diff --git a/src/NzbDrone.Core.Test/ParserTests/IsPossibleSpecialEpisodeFixture.cs b/src/NzbDrone.Core.Test/ParserTests/IsPossibleSpecialEpisodeFixture.cs index 5d0cc3829..b295f822a 100644 --- a/src/NzbDrone.Core.Test/ParserTests/IsPossibleSpecialEpisodeFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/IsPossibleSpecialEpisodeFixture.cs @@ -21,7 +21,7 @@ public void should_not_treat_files_without_a_series_title_as_a_special() SeriesTitle = "" }; - parsedEpisodeInfo.IsPossibleSpecialEpisode().Should().BeFalse(); + parsedEpisodeInfo.IsPossibleSpecialEpisode.Should().BeFalse(); } [Test] @@ -33,7 +33,15 @@ public void should_return_true_when_episode_numbers_is_empty() SeriesTitle = "" }; - parsedEpisodeInfo.IsPossibleSpecialEpisode().Should().BeTrue(); + parsedEpisodeInfo.IsPossibleSpecialEpisode.Should().BeTrue(); + } + + [TestCase("Under.the.Dome.S02.Special-Inside.Chesters.Mill.HDTV.x264-BAJSKORV")] + [TestCase("Under.the.Dome.S02.Special-Inside.Chesters.Mill.720p.HDTV.x264-BAJSKORV")] + [TestCase("Rookie.Blue.Behind.the.Badge.S05.Special.HDTV.x264-2HD")] + public void IsPossibleSpecialEpisode_should_be_true(string title) + { + Parser.Parser.ParseTitle(title).IsPossibleSpecialEpisode.Should().BeTrue(); } } } diff --git a/src/NzbDrone.Core.Test/ParserTests/SeasonParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/SeasonParserFixture.cs index 6789bc48b..624e7b1f1 100644 --- a/src/NzbDrone.Core.Test/ParserTests/SeasonParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/SeasonParserFixture.cs @@ -25,7 +25,7 @@ public class SeasonParserFixture : CoreTest [TestCase("Eureka S 01 720p WEB DL DD 5 1 h264 TjHD", "Eureka", 1)] [TestCase("Doctor Who Confidential Season 3", "Doctor Who Confidential", 3)] [TestCase("Fleming.S01.720p.WEBDL.DD5.1.H.264-NTb", "Fleming", 1)] - public void should_parsefull_season_release(string postTitle, string title, int season) + public void should_parse_full_season_release(string postTitle, string title, int season) { var result = Parser.Parser.ParseTitle(postTitle); result.SeasonNumber.Should().Be(season); diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs index cfed22a5c..15d964401 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using NLog; +using NzbDrone.Common; using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Instrumentation.Extensions; @@ -63,16 +64,17 @@ private IEnumerable GetDecisions(List reports, Se { var parsedEpisodeInfo = Parser.Parser.ParseTitle(report.Title); - if (parsedEpisodeInfo == null || parsedEpisodeInfo.IsPossibleSpecialEpisode()) + if (parsedEpisodeInfo == null || parsedEpisodeInfo.IsPossibleSpecialEpisode) { var specialEpisodeInfo = _parsingService.ParseSpecialEpisodeTitle(report.Title, report.TvRageId, searchCriteria); + if (specialEpisodeInfo != null) { parsedEpisodeInfo = specialEpisodeInfo; } } - if (parsedEpisodeInfo != null && !string.IsNullOrWhiteSpace(parsedEpisodeInfo.SeriesTitle)) + if (parsedEpisodeInfo != null && !parsedEpisodeInfo.SeriesTitle.IsNullOrWhiteSpace()) { var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, report.TvRageId, searchCriteria); remoteEpisode.Release = report; diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs index 6698d49dd..4d089ce68 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs @@ -36,7 +36,7 @@ public bool IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase search var episode = _episodeService.GetEpisode(dailySearchSpec.Series.Id, dailySearchSpec.AirDate.ToString(Episode.AIR_DATE_FORMAT)); - if (!remoteEpisode.ParsedEpisodeInfo.IsDaily() || remoteEpisode.ParsedEpisodeInfo.AirDate != episode.AirDate) + if (!remoteEpisode.ParsedEpisodeInfo.IsDaily || remoteEpisode.ParsedEpisodeInfo.AirDate != episode.AirDate) { _logger.Debug("Episode AirDate does not match searched episode number, skipping."); return false; diff --git a/src/NzbDrone.Core/Organizer/FilenameValidationService.cs b/src/NzbDrone.Core/Organizer/FilenameValidationService.cs index 2beed616e..5c2703cc1 100644 --- a/src/NzbDrone.Core/Organizer/FilenameValidationService.cs +++ b/src/NzbDrone.Core/Organizer/FilenameValidationService.cs @@ -46,7 +46,7 @@ public ValidationFailure ValidateDailyFilename(SampleResult sampleResult) return validationFailure; } - if (parsedEpisodeInfo.IsDaily()) + if (parsedEpisodeInfo.IsDaily) { if (!parsedEpisodeInfo.AirDate.Equals(sampleResult.Episodes.Single().AirDate)) { diff --git a/src/NzbDrone.Core/Parser/Model/ParsedEpisodeInfo.cs b/src/NzbDrone.Core/Parser/Model/ParsedEpisodeInfo.cs index 1b4c3cf33..2ab421b10 100644 --- a/src/NzbDrone.Core/Parser/Model/ParsedEpisodeInfo.cs +++ b/src/NzbDrone.Core/Parser/Model/ParsedEpisodeInfo.cs @@ -1,23 +1,24 @@ using System; using System.Linq; +using NzbDrone.Common; using NzbDrone.Core.Qualities; -using NzbDrone.Core.Tv; namespace NzbDrone.Core.Parser.Model { public class ParsedEpisodeInfo { - public string SeriesTitle { get; set; } + public String SeriesTitle { get; set; } public SeriesTitleInfo SeriesTitleInfo { get; set; } public QualityModel Quality { get; set; } - public int SeasonNumber { get; set; } - public int[] EpisodeNumbers { get; set; } - public int[] AbsoluteEpisodeNumbers { get; set; } + public Int32 SeasonNumber { get; set; } + public Int32[] EpisodeNumbers { get; set; } + public Int32[] AbsoluteEpisodeNumbers { get; set; } public String AirDate { get; set; } public Language Language { get; set; } - public bool FullSeason { get; set; } - public string ReleaseGroup { get; set; } - public string ReleaseHash { get; set; } + public Boolean FullSeason { get; set; } + public Boolean Special { get; set; } + public String ReleaseGroup { get; set; } + public String ReleaseHash { get; set; } public ParsedEpisodeInfo() { @@ -25,46 +26,56 @@ public ParsedEpisodeInfo() AbsoluteEpisodeNumbers = new int[0]; } - public bool IsDaily() + public bool IsDaily { - return !String.IsNullOrWhiteSpace(AirDate); + get + { + return !String.IsNullOrWhiteSpace(AirDate); + } } - public bool IsAbsoluteNumbering() + public bool IsAbsoluteNumbering { - return AbsoluteEpisodeNumbers.Any(); + get + { + return AbsoluteEpisodeNumbers.Any(); + } } - public bool IsPossibleSpecialEpisode() + public bool IsPossibleSpecialEpisode { - // if we dont have eny episode numbers we are likely a special episode and need to do a search by episode title - return String.IsNullOrWhiteSpace(AirDate) && - (EpisodeNumbers.Length == 0 || SeasonNumber == 0) && - String.IsNullOrWhiteSpace(SeriesTitle); + get + { + // if we don't have eny episode numbers we are likely a special episode and need to do a search by episode title + return (AirDate.IsNullOrWhiteSpace() && + SeriesTitle.IsNullOrWhiteSpace() && + (EpisodeNumbers.Length == 0 || SeasonNumber == 0) || + !SeriesTitle.IsNullOrWhiteSpace() && Special); + } } public override string ToString() { string episodeString = "[Unknown Episode]"; - if (IsDaily() && EpisodeNumbers == null) + if (IsDaily && EpisodeNumbers == null) { - episodeString = string.Format("{0}", AirDate); + episodeString = String.Format("{0}", AirDate); } else if (FullSeason) { - episodeString = string.Format("Season {0:00}", SeasonNumber); + episodeString = String.Format("Season {0:00}", SeasonNumber); } else if (EpisodeNumbers != null && EpisodeNumbers.Any()) { - episodeString = string.Format("S{0:00}E{1}", SeasonNumber, String.Join("-", EpisodeNumbers.Select(c => c.ToString("00")))); + episodeString = String.Format("S{0:00}E{1}", SeasonNumber, String.Join("-", EpisodeNumbers.Select(c => c.ToString("00")))); } else if (AbsoluteEpisodeNumbers != null && AbsoluteEpisodeNumbers.Any()) { - episodeString = string.Format("{0}", String.Join("-", AbsoluteEpisodeNumbers.Select(c => c.ToString("000")))); + episodeString = String.Format("{0}", String.Join("-", AbsoluteEpisodeNumbers.Select(c => c.ToString("000")))); } - return string.Format("{0} - {1} {2}", SeriesTitle, episodeString, Quality); + return String.Format("{0} - {1} {2}", SeriesTitle, episodeString, Quality); } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs index cb92c57ba..63576914b 100644 --- a/src/NzbDrone.Core/Parser/Parser.cs +++ b/src/NzbDrone.Core/Parser/Parser.cs @@ -190,7 +190,7 @@ public static ParsedEpisodeInfo ParseTitle(string title) var titleWithoutExtension = RemoveFileExtension(title).ToCharArray(); Array.Reverse(titleWithoutExtension); - title = new string(titleWithoutExtension) + title.Substring(titleWithoutExtension.Length); + title = new String(titleWithoutExtension) + title.Substring(titleWithoutExtension.Length); Logger.Debug("Reversed name detected. Converted to '{0}'", title); } @@ -213,8 +213,15 @@ public static ParsedEpisodeInfo ParseTitle(string title) try { var result = ParseMatchCollection(match); + if (result != null) { + if (result.FullSeason && title.ContainsIgnoreCase("Special")) + { + result.FullSeason = false; + result.Special = true; + } + result.Language = ParseLanguage(title); Logger.Debug("Language parsed: {0}", result.Language); @@ -379,12 +386,10 @@ private static ParsedEpisodeInfo ParseMatchCollection(MatchCollection matchColle } //If no season was found it should be treated as a mini series and season 1 - if (seasons.Count == 0) - seasons.Add(1); + if (seasons.Count == 0) seasons.Add(1); //If more than 1 season was parsed go to the next REGEX (A multi-season release is unlikely) - if (seasons.Distinct().Count() > 1) - return null; + if (seasons.Distinct().Count() > 1) return null; result = new ParsedEpisodeInfo { @@ -431,12 +436,12 @@ private static ParsedEpisodeInfo ParseMatchCollection(MatchCollection matchColle { //Check to see if this is an "Extras" or "SUBPACK" release, if it is, return NULL //Todo: Set a "Extras" flag in EpisodeParseResult if we want to download them ever - if (!String.IsNullOrWhiteSpace(matchCollection[0].Groups["extras"].Value)) - return null; + if (!matchCollection[0].Groups["extras"].Value.IsNullOrWhiteSpace()) return null; result.FullSeason = true; } } + if (result.AbsoluteEpisodeNumbers.Any() && !result.EpisodeNumbers.Any()) { result.SeasonNumber = 0; diff --git a/src/NzbDrone.Core/Parser/ParsingService.cs b/src/NzbDrone.Core/Parser/ParsingService.cs index e59de0c24..5acc542f7 100644 --- a/src/NzbDrone.Core/Parser/ParsingService.cs +++ b/src/NzbDrone.Core/Parser/ParsingService.cs @@ -44,15 +44,13 @@ public LocalEpisode GetLocalEpisode(string filename, Series series, bool sceneSo { var parsedEpisodeInfo = Parser.ParsePath(filename); - // do we have a possible special episode? - if (parsedEpisodeInfo == null || parsedEpisodeInfo.IsPossibleSpecialEpisode()) + if (parsedEpisodeInfo == null || parsedEpisodeInfo.IsPossibleSpecialEpisode) { - // try to parse as a special episode var title = Path.GetFileNameWithoutExtension(filename); var specialEpisodeInfo = ParseSpecialEpisodeTitle(title, series); + if (specialEpisodeInfo != null) { - // use special episode parsedEpisodeInfo = specialEpisodeInfo; } } @@ -131,7 +129,7 @@ public List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series ser return _episodeService.GetEpisodesBySeason(series.Id, parsedEpisodeInfo.SeasonNumber); } - if (parsedEpisodeInfo.IsDaily()) + if (parsedEpisodeInfo.IsDaily) { if (series.SeriesType == SeriesTypes.Standard) { @@ -149,7 +147,7 @@ public List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series ser return result; } - if (parsedEpisodeInfo.IsAbsoluteNumbering()) + if (parsedEpisodeInfo.IsAbsoluteNumbering) { var sceneSeasonNumber = _sceneMappingService.GetSeasonNumber(parsedEpisodeInfo.SeriesTitle); @@ -299,6 +297,7 @@ public ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, Series series) { // find special episode in series season 0 var episode = _episodeService.FindEpisodeByName(series.Id, 0, title); + if (episode != null) { // create parsed info from tv episode