diff --git a/NzbDrone.Core.Test/ParserTest.cs b/NzbDrone.Core.Test/ParserTest.cs index 3704a7263..8f6c6075d 100644 --- a/NzbDrone.Core.Test/ParserTest.cs +++ b/NzbDrone.Core.Test/ParserTest.cs @@ -1,10 +1,12 @@ // ReSharper disable RedundantUsingDirective using System; +using System.Linq; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Model; using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; namespace NzbDrone.Core.Test { @@ -52,10 +54,10 @@ namespace NzbDrone.Core.Test public void ParseTitle_single(string postTitle, string title, int seasonNumber, int episodeNumber) { var result = Parser.ParseTitle(postTitle); + result.EpisodeNumbers.Should().HaveCount(1); result.SeasonNumber.Should().Be(seasonNumber); - result.EpisodeNumbers[0].Should().Be(episodeNumber); + result.EpisodeNumbers.First().Should().Be(episodeNumber); result.CleanTitle.Should().Be(Parser.NormalizeTitle(title)); - result.EpisodeNumbers.Count.Should().Be(1); } [Test] @@ -68,12 +70,15 @@ namespace NzbDrone.Core.Test [TestCase(@"P:\TV Shows\House\Season 6\S06E13 - 5 to 9 - 720p BluRay.mkv", 6, 13)] [TestCase(@"S:\TV Drop\House - 10x11 - Title [SDTV]\1011 - Title.avi", 10, 11)] [TestCase(@"S:\TV Drop\King of the Hill - 10x12 - 24 Hour Propane People [SDTV]\1012 - 24 Hour Propane People.avi", 10, 12)] + [TestCase(@"S:\TV Drop\King of the Hill - 10x12 - 24 Hour Propane People [SDTV]\Hour Propane People.avi", 10, 12)] public void PathParse_tests(string path, int season, int episode) { var result = Parser.ParsePath(path); result.EpisodeNumbers.Should().HaveCount(1); result.SeasonNumber.Should().Be(season); result.EpisodeNumbers[0].Should().Be(episode); + + ExceptionVerification.IgnoreWarns(); } [TestCase("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", QualityTypes.DVD)] @@ -345,6 +350,7 @@ namespace NzbDrone.Core.Test var result = Parser.ParseTitle(postTitle); result.Should().BeNull(); + ExceptionVerification.ExcpectedWarns(1); } [TestCase("Lie.to.Me.S03.SUBPACK.DVDRip.XviD-REWARD")] @@ -355,6 +361,8 @@ namespace NzbDrone.Core.Test var result = Parser.ParseTitle(postTitle); result.Should().BeNull(); + + ExceptionVerification.ExcpectedWarns(1); } } } diff --git a/NzbDrone.Core/Parser.cs b/NzbDrone.Core/Parser.cs index 126784e38..fece6bf26 100644 --- a/NzbDrone.Core/Parser.cs +++ b/NzbDrone.Core/Parser.cs @@ -62,22 +62,22 @@ namespace NzbDrone.Core RegexOptions.IgnoreCase | RegexOptions.Compiled); - /// - /// Parses a file path into list of episodes it contains - /// - /// Path of the file to parse - /// List of episodes contained in the file internal static EpisodeParseResult ParsePath(string path) { var fileInfo = new FileInfo(path); - return ParseTitle(fileInfo.Name); + + var result = ParseTitle(fileInfo.Name); + + if (result == null) + { + Logger.Trace("Attempting to parse episode info using full path. {0}", fileInfo.FullName); + result = ParseTitle(fileInfo.FullName); + } + + return result; } - /// - /// Parses a post title into list of episodes it contains - /// - /// Title of the report - /// List of episodes contained in the post + internal static EpisodeParseResult ParseTitle(string title) { Logger.Trace("Parsing string '{0}'", title); @@ -85,95 +85,96 @@ namespace NzbDrone.Core foreach (var regex in ReportTitleRegex) { - //Use only the filename, not the entire path var match = regex.Matches(simpleTitle); if (match.Count != 0) { - var seriesName = NormalizeTitle(match[0].Groups["title"].Value); - - int airyear; - Int32.TryParse(match[0].Groups["airyear"].Value, out airyear); - - EpisodeParseResult parsedEpisode; - - if (airyear < 1) + var result = ParseMatchCollection(match); + if (result != null) { - var seasons = new List(); - - foreach (Capture seasonCapture in match[0].Groups["season"].Captures) - { - int s; - if (Int32.TryParse(seasonCapture.Value, out s)) - seasons.Add(s); - } - - //If more than 1 season was parsed go to the next REGEX (A multi-season release is unlikely) - if (seasons.Distinct().Count() != 1) - continue; - - var season = seasons[0]; - - parsedEpisode = new EpisodeParseResult - { - CleanTitle = seriesName, - SeasonNumber = season, - EpisodeNumbers = new List() - }; - - foreach (Match matchGroup in match) - { - var count = matchGroup.Groups["episode"].Captures.Count; - - //Allows use to return a list of 0 episodes (We can handle that as a full season release) - if (count > 0) - { - var first = Convert.ToInt32(matchGroup.Groups["episode"].Captures[0].Value); - var last = Convert.ToInt32(matchGroup.Groups["episode"].Captures[count - 1].Value); - - for (int i = first; i <= last; i++) - { - parsedEpisode.EpisodeNumbers.Add(i); - } - } - - else - { - //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.IsNullOrEmpty(match[0].Groups["extras"].Value)) - return null; - - parsedEpisode.FullSeason = true; - } - } + result.Language = ParseLanguage(title); + result.Quality = ParseQuality(title); + return result; } - - else - { - //Try to Parse as a daily show - var airmonth = Convert.ToInt32(match[0].Groups["airmonth"].Value); - var airday = Convert.ToInt32(match[0].Groups["airday"].Value); - - parsedEpisode = new EpisodeParseResult - { - CleanTitle = seriesName, - AirDate = new DateTime(airyear, airmonth, airday), - Language = ParseLanguage(simpleTitle) - }; - } - - parsedEpisode.Quality = ParseQuality(title); - - Logger.Trace("Episode Parsed. {0}", parsedEpisode); - - return parsedEpisode; } } + Logger.Warn("Unable to parse episode info. {0}", title); return null; } + private static EpisodeParseResult ParseMatchCollection(MatchCollection matchCollection) + { + var seriesName = NormalizeTitle(matchCollection[0].Groups["title"].Value); + + int airyear; + Int32.TryParse(matchCollection[0].Groups["airyear"].Value, out airyear); + + EpisodeParseResult parsedEpisode; + + if (airyear < 1900) + { + var seasons = new List(); + + foreach (Capture seasonCapture in matchCollection[0].Groups["season"].Captures) + { + int parsedSeason; + if (Int32.TryParse(seasonCapture.Value, out parsedSeason)) + seasons.Add(parsedSeason); + } + + //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; + + parsedEpisode = new EpisodeParseResult + { + SeasonNumber = seasons.First(), + EpisodeNumbers = new List() + }; + + foreach (Match matchGroup in matchCollection) + { + var episodeCaptures = matchGroup.Groups["episode"].Captures.Cast().ToList(); + + //Allows use to return a list of 0 episodes (We can handle that as a full season release) + if (episodeCaptures.Any()) + { + var first = Convert.ToInt32(episodeCaptures.First().Value); + var last = Convert.ToInt32(episodeCaptures.Last().Value); + parsedEpisode.EpisodeNumbers = Enumerable.Range(first, last - first + 1).ToList(); + } + else + { + //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; + + parsedEpisode.FullSeason = true; + } + } + } + + else + { + //Try to Parse as a daily show + var airmonth = Convert.ToInt32(matchCollection[0].Groups["airmonth"].Value); + var airday = Convert.ToInt32(matchCollection[0].Groups["airday"].Value); + + parsedEpisode = new EpisodeParseResult + { + AirDate = new DateTime(airyear, airmonth, airday), + }; + } + + parsedEpisode.CleanTitle = seriesName; + + Logger.Trace("Episode Parsed. {0}", parsedEpisode); + + return parsedEpisode; + } + /// /// Parses a post title to find the series that relates to it /// @@ -294,7 +295,7 @@ namespace NzbDrone.Core return result; } - public static LanguageType ParseLanguage(string title) + internal static LanguageType ParseLanguage(string title) { var lowerTitle = title.ToLower();