diff --git a/NzbDrone.Core.Test/ParserTest.cs b/NzbDrone.Core.Test/ParserTest.cs
index eefe09956..9ee4165a8 100644
--- a/NzbDrone.Core.Test/ParserTest.cs
+++ b/NzbDrone.Core.Test/ParserTest.cs
@@ -17,10 +17,6 @@ namespace NzbDrone.Core.Test
* Unreported.World.Chinas.Lost.Sons.WS.PDTV.XviD-FTP
*/
-
-
-
-
[Test]
[TestCase("Sonny.With.a.Chance.S02E15", "Sonny.With.a.Chance", 2, 15)]
[TestCase("Two.and.a.Half.Me.103.720p.HDTV.X264-DIMENSION", "Two.and.a.Half.Me", 1, 3)]
@@ -137,6 +133,10 @@ namespace NzbDrone.Core.Test
[TestCase("White.Collar.2x04.2x05.720p.BluRay-FUTV", "White.Collar", 2, new[] { 4, 5 }, 2)]
[TestCase("Desperate.Housewives.S07E22E23.720p.HDTV.X264-DIMENSION", "Desperate.Housewives", 7, new[] { 22,23 }, 2)]
//[Row("The.Kennedys.Part.1.and.Part.2.DSR.XviD-SYS", 1, new[] { 1, 2 })]
+ [TestCase("S07E22 - 7x23 - And Lots of Security.. [HDTV].mkv", "", 7, new[] { 22, 23 }, 2)]
+ [TestCase("Desparate Housewives - S07E22 - 7x23 - And Lots of Security.. [HDTV].mkv", "Desparate Housewives", 7, new[] { 22, 23 }, 2)]
+ [TestCase("Desparate Housewives - S07E22 - S07E23 - And Lots of Security.. [HDTV].mkv", "Desparate Housewives", 7, new[] { 22, 23 }, 2)]
+ [TestCase("S03E01.S03E02.720p.HDTV.X264-DIMENSION", "", 3, new[] { 1, 2 }, 2)]
public void episode_multipart_parse(string postTitle, string title, int season, int[] episodes, int count)
{
var result = Parser.ParseEpisodeInfo(postTitle);
diff --git a/NzbDrone.Core/Parser.cs b/NzbDrone.Core/Parser.cs
index 8f1d15cfa..0b561f017 100644
--- a/NzbDrone.Core/Parser.cs
+++ b/NzbDrone.Core/Parser.cs
@@ -14,21 +14,36 @@ namespace NzbDrone.Core
private static readonly Regex[] ReportTitleRegex = new[]
{
+ //Episodes with airdate
new Regex(@"^(?
.+?)?\W*(?\d{4})\W+(?\d{2})\W+(?\d{2})\W?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
- new Regex(@"^(?.+?)(?:\WS?(?\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?\d{1,2}(?!\d+)))+)+\W?(?!\\)",
+
+ //Multi-Part episodes without a title (S01E05.S01E06)
+ new Regex(@"^(?:\W*S?(?\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?\d{1,2}(?!\d+)))+){2,}\W?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
- new Regex(@"^(?.*?)?(?:\W?S?(?\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?\d{1,2}(?!\d+)))+)+\W?(?!\\)",
- RegexOptions.IgnoreCase | RegexOptions.Compiled),
+
+ //Single episodes or multi-episode (S01E05E06, S01E05-06, etc)
+ new Regex(@"^(?.+?)(?:\W+S?(?\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?\d{1,2}(?!\d+)))+)+\W?(?!\\)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+
+ //No Title - Single episodes or multi-episode (S01E05E06, S01E05-06, etc)
+ new Regex(@"^(?:\W?S?(?\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?\d{1,2}(?!\d+)))+\W*)+\W?(?!\\)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+
+ //Supports 103/113 naming
new Regex(@"^(?.+?)?\W?(?:\W(?\d+)(?\d{2}))+\W?(?!\\)",
- RegexOptions.IgnoreCase | RegexOptions.Compiled), //Supports 103/113 naming
- new Regex(@"^(?.*?)?(?:\W?S?(?\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|to)+(?\d+))+)+\W?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
+
+ //Episodes over 99 (3-digits or more)
+ new Regex(@"^(?.*?)(?:\W?S?(?\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|to)+(?\d+))+)+\W?(?!\\)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+
+ //Supports Season only releases
new Regex(@"^(?.*?)\W(?:S|Season\W?)?(?\d{1,2}(?!\d+))+\W?(?!\\)",
- RegexOptions.IgnoreCase | RegexOptions.Compiled) //Supports Season only releases
+ RegexOptions.IgnoreCase | RegexOptions.Compiled)
};
- private static readonly Regex NormalizeRegex = new Regex(@"((^|\W)(a|an|the|and|or|of)($|\W))|\W|\b(?!(?:19\d{2}|20\d{2}))\d+\b",
+ private static readonly Regex NormalizeRegex = new Regex(@"((^|\W)(a|an|the|and|or|of)($|\W))|\W|(?:(?<=[^0-9]+)|\b)(?!(?:19\d{2}|20\d{2}))\d+(?=[^0-9ip]+|\b)",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex SimpleTitleRegex = new Regex(@"480[i|p]|720[i|p]|1080[i|p]|[x|h]264|\\|\/|\<|\>|\?|\*|\:|\|",
@@ -203,19 +218,19 @@ namespace NzbDrone.Core
Logger.Trace("Trying to parse quality for {0}", name);
name = name.Trim();
- var normilizedName = NormalizeTitle(name);
+ var normalizedName = NormalizeTitle(name);
var result = new Quality { QualityType = QualityTypes.Unknown };
- result.Proper = normilizedName.Contains("proper");
+ result.Proper = normalizedName.Contains("proper");
- if (normilizedName.Contains("dvd") || normilizedName.Contains("bdrip") || normilizedName.Contains("brrip"))
+ if (normalizedName.Contains("dvd") || normalizedName.Contains("bdrip") || normalizedName.Contains("brrip"))
{
result.QualityType = QualityTypes.DVD;
return result;
}
- if (normilizedName.Contains("xvid") || normilizedName.Contains("divx"))
+ if (normalizedName.Contains("xvid") || normalizedName.Contains("divx"))
{
- if (normilizedName.Contains("bluray"))
+ if (normalizedName.Contains("bluray"))
{
result.QualityType = QualityTypes.DVD;
return result;
@@ -225,15 +240,15 @@ namespace NzbDrone.Core
return result;
}
- if (normilizedName.Contains("bluray"))
+ if (normalizedName.Contains("bluray"))
{
- if (normilizedName.Contains("720p"))
+ if (normalizedName.Contains("720p"))
{
result.QualityType = QualityTypes.Bluray720p;
return result;
}
- if (normilizedName.Contains("1080p"))
+ if (normalizedName.Contains("1080p"))
{
result.QualityType = QualityTypes.Bluray1080p;
return result;
@@ -242,12 +257,12 @@ namespace NzbDrone.Core
result.QualityType = QualityTypes.Bluray720p;
return result;
}
- if (normilizedName.Contains("webdl"))
+ if (normalizedName.Contains("webdl"))
{
result.QualityType = QualityTypes.WEBDL;
return result;
}
- if (normilizedName.Contains("x264") || normilizedName.Contains("h264") || normilizedName.Contains("720p"))
+ if (normalizedName.Contains("x264") || normalizedName.Contains("h264") || normalizedName.Contains("720p"))
{
result.QualityType = QualityTypes.HDTV;
return result;
@@ -284,7 +299,7 @@ namespace NzbDrone.Core
}
}
- if (normilizedName.Contains("sdtv") || (result.QualityType == QualityTypes.Unknown && normilizedName.Contains("hdtv")))
+ if (normalizedName.Contains("sdtv") || (result.QualityType == QualityTypes.Unknown && normalizedName.Contains("hdtv")))
{
result.QualityType = QualityTypes.SDTV;
return result;