diff --git a/src/NzbDrone.Core.Test/MediaFiles/TrackImport/ImportDecisionMakerFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/TrackImport/ImportDecisionMakerFixture.cs index c0fca112a..acb762e9d 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/TrackImport/ImportDecisionMakerFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/TrackImport/ImportDecisionMakerFixture.cs @@ -186,7 +186,7 @@ namespace NzbDrone.Core.Test.MediaFiles.TrackImport public void should_use_file_language_if_folder_language_is_null() { GivenSpecifications(_pass1, _pass2, _pass3); - var expectedLanguage = Parser.Parser.ParseLanguage(_audioFiles.Single()); + var expectedLanguage = LanguageParser.ParseLanguage(_audioFiles.Single()); var result = Subject.GetImportDecisions(_audioFiles, _artist); diff --git a/src/NzbDrone.Core.Test/ParserTests/NormalizeTitleFixture.cs b/src/NzbDrone.Core.Test/ParserTests/NormalizeTitleFixture.cs index 6e2b01366..cd42a0737 100644 --- a/src/NzbDrone.Core.Test/ParserTests/NormalizeTitleFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/NormalizeTitleFixture.cs @@ -13,10 +13,10 @@ namespace NzbDrone.Core.Test.ParserTests [TestCase("Castle (2009)", "castle2009")] [TestCase("Parenthood.2010", "parenthood2010")] [TestCase("Law_and_Order_SVU", "lawordersvu")] - public void should_normalize_series_title(string parsedSeriesName, string seriesName) + public void should_normalize_artist_title(string parsedArtistName, string artistName) { - var result = parsedSeriesName.CleanSeriesTitle(); - result.Should().Be(seriesName); + var result = parsedArtistName.CleanArtistName(); + result.Should().Be(artistName); } [TestCase("CaPitAl", "capital")] @@ -27,7 +27,7 @@ namespace NzbDrone.Core.Test.ParserTests [TestCase("24", "24")] public void should_remove_special_characters_and_casing(string dirty, string clean) { - var result = dirty.CleanSeriesTitle(); + var result = dirty.CleanArtistName(); result.Should().Be(clean); } @@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.ParserTests foreach (var s in dirtyFormat) { var dirty = string.Format(s, word); - dirty.CleanSeriesTitle().Should().Be("wordword"); + dirty.CleanArtistName().Should().Be("wordword"); } } @@ -68,7 +68,7 @@ namespace NzbDrone.Core.Test.ParserTests foreach (var s in dirtyFormat) { var dirty = string.Format(s, "a"); - dirty.CleanSeriesTitle().Should().Be("wordword"); + dirty.CleanArtistName().Should().Be("wordword"); } } @@ -93,7 +93,7 @@ namespace NzbDrone.Core.Test.ParserTests foreach (var s in dirtyFormat) { var dirty = string.Format(s, word); - dirty.CleanSeriesTitle().Should().Be(("word" + word.ToLower() + "word")); + dirty.CleanArtistName().Should().Be(("word" + word.ToLower() + "word")); } } @@ -101,10 +101,10 @@ namespace NzbDrone.Core.Test.ParserTests [TestCase("The Office", "theoffice")] [TestCase("The Tonight Show With Jay Leno", "thetonightshowwithjayleno")] [TestCase("The.Daily.Show", "thedailyshow")] - public void should_not_remove_from_the_beginning_of_the_title(string parsedSeriesName, string seriesName) + public void should_not_remove_from_the_beginning_of_the_title(string parsedArtistName, string artistName) { - var result = parsedSeriesName.CleanSeriesTitle(); - result.Should().Be(seriesName); + var result = parsedArtistName.CleanArtistName(); + result.Should().Be(artistName); } [TestCase("the")] @@ -125,14 +125,14 @@ namespace NzbDrone.Core.Test.ParserTests foreach (var s in dirtyFormat) { var dirty = string.Format(s, word); - dirty.CleanSeriesTitle().Should().Be(word + "wordword"); + dirty.CleanArtistName().Should().Be(word + "wordword"); } } [Test] public void should_not_clean_trailing_a() { - "Tokyo Ghoul A".CleanSeriesTitle().Should().Be("tokyoghoula"); + "Tokyo Ghoul A".CleanArtistName().Should().Be("tokyoghoula"); } } } diff --git a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs index f51dff304..984bd6a1f 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs @@ -40,8 +40,8 @@ namespace NzbDrone.Core.Test.ParserTests [TestCase("Reno.911.S01.DVDRip.DD2.0.x264-DEEP", "Reno 911")] public void should_parse_series_name(string postTitle, string title) { - var result = Parser.Parser.ParseArtistName(postTitle).CleanSeriesTitle(); - result.Should().Be(title.CleanSeriesTitle()); + var result = Parser.Parser.ParseArtistName(postTitle).CleanArtistName(); + result.Should().Be(title.CleanArtistName()); } [Test] @@ -49,7 +49,7 @@ namespace NzbDrone.Core.Test.ParserTests { const string title = "Carniv\u00E0le"; - title.CleanSeriesTitle().Should().Be("carnivale"); + title.CleanArtistName().Should().Be("carnivale"); } [TestCase("Discovery TV - Gold Rush : 02 Road From Hell [S04].mp4")] diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs index c5872eb28..21ebe5e27 100644 --- a/src/NzbDrone.Core/Parser/Parser.cs +++ b/src/NzbDrone.Core/Parser/Parser.cs @@ -78,197 +78,6 @@ namespace NzbDrone.Core.Parser RegexOptions.IgnoreCase | RegexOptions.Compiled), }; - private static readonly Regex[] ReportTitleRegex = new[] - { - //Anime - Absolute Episode Number + Title + Season+Episode - //Todo: This currently breaks series that start with numbers -// new Regex(@"^(?:(?\d{2,3})(?:_|-|\s|\.)+)+(?.+?)(?:\W|_)+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]){1,2}(?<episode>\d{2}(?!\d+)))+)", -// RegexOptions.IgnoreCase | RegexOptions.Compiled), - - - //Multi-Part episodes without a title (S01E05.S01E06) - new Regex(@"^(?:\W*S?(?<season>(?<!\d+)(?:\d{1,2}|\d{4})(?!\d+))(?:(?:[ex]){1,2}(?<episode>\d{1,3}(?!\d+)))+){2,}", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Episodes without a title, Single (S01E05, 1x05) AND Multi (S01E04E05, 1x04x05, etc) - new Regex(@"^(?:S?(?<season>(?<!\d+)(?:\d{1,2}|\d{4})(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>\d{2,3}(?!\d+)))+)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Anime - [SubGroup] Title Episode Absolute Episode Number ([SubGroup] Series Title Episode 01) - new Regex(@"^(?:\[(?<subgroup>.+?)\][-_. ]?)(?<title>.+?)[-_. ](?:Episode)(?:[-_. ]+(?<absoluteepisode>(?<!\d+)\d{2,3}(?!\d+)))+(?:_|-|\s|\.)*?(?<hash>\[.{8}\])?(?:$|\.)?", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Anime - [SubGroup] Title Absolute Episode Number + Season+Episode - new Regex(@"^(?:\[(?<subgroup>.+?)\](?:_|-|\s|\.)?)(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+(?<absoluteepisode>\d{2,3}))+(?:_|-|\s|\.)+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]){1,2}(?<episode>\d{2}(?!\d+)))+).*?(?<hash>[(\[]\w{8}[)\]])?(?:$|\.)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Anime - [SubGroup] Title Season+Episode + Absolute Episode Number - new Regex(@"^(?:\[(?<subgroup>.+?)\](?:_|-|\s|\.)?)(?<title>.+?)(?:[-_\W](?<![()\[!]))+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]){1,2}(?<episode>\d{2}(?!\d+)))+)(?:(?:_|-|\s|\.)+(?<absoluteepisode>(?<!\d+)\d{2,3}(?!\d+)))+.*?(?<hash>\[\w{8}\])?(?:$|\.)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Anime - [SubGroup] Title Season+Episode - new Regex(@"^(?:\[(?<subgroup>.+?)\](?:_|-|\s|\.)?)(?<title>.+?)(?:[-_\W](?<![()\[!]))+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:[ex]|\W[ex]){1,2}(?<episode>\d{2}(?!\d+)))+)(?:\s|\.).*?(?<hash>\[\w{8}\])?(?:$|\.)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Anime - [SubGroup] Title with trailing number Absolute Episode Number - new Regex(@"^\[(?<subgroup>.+?)\][-_. ]?(?<title>[^-]+?\d+?)[-_. ]+(?:[-_. ]?(?<absoluteepisode>\d{3}(?!\d+)))+(?:[-_. ]+(?<special>special|ova|ovd))?.*?(?<hash>\[\w{8}\])?(?:$|\.mkv)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Anime - [SubGroup] Title - Absolute Episode Number - new Regex(@"^\[(?<subgroup>.+?)\][-_. ]?(?<title>.+?)(?:[. ]-[. ](?<absoluteepisode>\d{2,3}(?!\d+|[-])))+(?:[-_. ]+(?<special>special|ova|ovd))?.*?(?<hash>\[\w{8}\])?(?:$|\.mkv)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Anime - [SubGroup] Title Absolute Episode Number - new Regex(@"^\[(?<subgroup>.+?)\][-_. ]?(?<title>.+?)[-_. ]+\(?(?:[-_. ]?(?<absoluteepisode>\d{2,3}(?!\d+)))+\)?(?:[-_. ]+(?<special>special|ova|ovd))?.*?(?<hash>\[\w{8}\])?(?:$|\.mkv)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Anime - Title Season EpisodeNumber + Absolute Episode Number [SubGroup] - new Regex(@"^(?<title>.+?)(?:[-_\W](?<![()\[!]))+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:[ex]|\W[ex]){1,2}(?<episode>(?<!\d+)\d{2}(?!\d+)))+).+?(?:[-_. ]?(?<absoluteepisode>(?<!\d+)\d{3}(?!\d+)))+.+?\[(?<subgroup>.+?)\](?:$|\.mkv)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Anime - Title Absolute Episode Number [SubGroup] - new Regex(@"^(?<title>.+?)(?:(?:_|-|\s|\.)+(?<absoluteepisode>\d{3}(?!\d+)))+(?:.+?)\[(?<subgroup>.+?)\].*?(?<hash>\[\w{8}\])?(?:$|\.)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Anime - Title Absolute Episode Number [Hash] - new Regex(@"^(?<title>.+?)(?:(?:_|-|\s|\.)+(?<absoluteepisode>\d{2,3}(?!\d+)))+(?:[-_. ]+(?<special>special|ova|ovd))?[-_. ]+.*?(?<hash>\[\w{8}\])(?:$|\.)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Episodes with airdate AND season/episode number, capture season/epsiode only - new Regex(@"^(?<title>.+?)?\W*(?<airdate>\d{4}\W+[0-1][0-9]\W+[0-3][0-9])(?!\W+[0-3][0-9])[-_. ](?:s?(?<season>(?<!\d+)(?:\d{1,2})(?!\d+)))(?:[ex](?<episode>(?<!\d+)(?:\d{1,3})(?!\d+)))", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Episodes with airdate AND season/episode number - new Regex(@"^(?<title>.+?)?\W*(?<airyear>\d{4})\W+(?<airmonth>[0-1][0-9])\W+(?<airday>[0-3][0-9])(?!\W+[0-3][0-9]).+?(?:s?(?<season>(?<!\d+)(?:\d{1,2})(?!\d+)))(?:[ex](?<episode>(?<!\d+)(?:\d{1,3})(?!\d+)))", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Multi-episode Repeated (S01E05 - S01E06, 1x05 - 1x06, etc) - new Regex(@"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+S?(?<season>(?<!\d+)(?:\d{1,2}|\d{4})(?!\d+))(?:(?:[ex]|[-_. ]e){1,2}(?<episode>\d{1,3}(?!\d+)))+){2,}", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Episodes with a title, Single episodes (S01E05, 1x05, etc) & Multi-episode (S01E05E06, S01E05-06, S01E05 E06, etc) - new Regex(@"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+S?(?<season>(?<!\d+)(?:\d{1,2})(?!\d+))(?:[ex]|\W[ex]|_){1,2}(?<episode>\d{2,3}(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>\d{2,3}(?!\d+)))*)\W?(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Episodes with a title, 4 digit season number, Single episodes (S2016E05, etc) & Multi-episode (S2016E05E06, S2016E05-06, S2016E05 E06, etc) - new Regex(@"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+S(?<season>(?<!\d+)(?:\d{4})(?!\d+))(?:e|\We|_){1,2}(?<episode>\d{2,3}(?!\d+))(?:(?:\-|e|\We|_){1,2}(?<episode>\d{2,3}(?!\d+)))*)\W?(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Episodes with a title, 4 digit season number, Single episodes (2016x05, etc) & Multi-episode (2016x05x06, 2016x05-06, 2016x05 x06, etc) - new Regex(@"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+(?<season>(?<!\d+)(?:\d{4})(?!\d+))(?:x|\Wx|_){1,2}(?<episode>\d{2,3}(?!\d+))(?:(?:\-|x|\Wx|_){1,2}(?<episode>\d{2,3}(?!\d+)))*)\W?(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Mini-Series with year in title, treated as season 1, episodes are labelled as Part01, Part 01, Part.1 - new Regex(@"^(?<title>.+?\d{4})(?:\W+(?:(?:Part\W?|e)(?<episode>\d{1,2}(?!\d+)))+)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Mini-Series, treated as season 1, episodes are labelled as Part01, Part 01, Part.1 - new Regex(@"^(?<title>.+?)(?:\W+(?:(?:Part\W?|(?<!\d+\W+)e)(?<episode>\d{1,2}(?!\d+)))+)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Mini-Series, treated as season 1, episodes are labelled as Part One/Two/Three/...Nine, Part.One, Part_One - new Regex(@"^(?<title>.+?)(?:\W+(?:Part[-._ ](?<episode>One|Two|Three|Four|Five|Six|Seven|Eight|Nine)(?>[-._ ])))", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Mini-Series, treated as season 1, episodes are labelled as XofY - new Regex(@"^(?<title>.+?)(?:\W+(?:(?<episode>(?<!\d+)\d{1,2}(?!\d+))of\d+)+)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Supports Season 01 Episode 03 - new Regex(@"(?:.*(?:\""|^))(?<title>.*?)(?:[-_\W](?<![()\[]))+(?:\W?Season\W?)(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:\W|_)+(?:Episode\W)(?:[-_. ]?(?<episode>(?<!\d+)\d{1,2}(?!\d+)))+", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Multi-episode release with no space between series title and season (S01E11E12) - new Regex(@"(?:.*(?:^))(?<title>.*?)(?:\W?|_)S(?<season>(?<!\d+)\d{2}(?!\d+))(?:E(?<episode>(?<!\d+)\d{2}(?!\d+)))+", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Multi-episode with single episode numbers (S6.E1-E2, S6.E1E2, S6E1E2, etc) - new Regex(@"^(?<title>.+?)[-_. ]S(?<season>(?<!\d+)(?:\d{1,2}|\d{4})(?!\d+))(?:[-_. ]?[ex]?(?<episode>(?<!\d+)\d{1,2}(?!\d+)))+", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Single episode season or episode S1E1 or S1-E1 - new Regex(@"(?:.*(?:\""|^))(?<title>.*?)(?:\W?|_)S(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:\W|_)?E(?<episode>(?<!\d+)\d{1,2}(?!\d+))", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //3 digit season S010E05 - new Regex(@"(?:.*(?:\""|^))(?<title>.*?)(?:\W?|_)S(?<season>(?<!\d+)\d{3}(?!\d+))(?:\W|_)?E(?<episode>(?<!\d+)\d{1,2}(?!\d+))", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //5 digit episode number with a title - new Regex(@"^(?:(?<title>.+?)(?:_|-|\s|\.)+)(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+)))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>(?<!\d+)\d{5}(?!\d+)))", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //5 digit multi-episode with a title - new Regex(@"^(?:(?<title>.+?)(?:_|-|\s|\.)+)(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+)))(?:(?:[-_. ]{1,3}ep){1,2}(?<episode>(?<!\d+)\d{5}(?!\d+)))+", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - // Separated season and episode numbers S01 - E01 - new Regex(@"^(?<title>.+?)(?:_|-|\s|\.)+S(?<season>\d{2}(?!\d+))(\W-\W)E(?<episode>(?<!\d+)\d{2}(?!\d+))(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Season only releases - new Regex(@"^(?<title>.+?)\W(?:S|Season)\W?(?<season>\d{1,2}(?!\d+))(\W+|_|$)(?<extras>EXTRAS|SUBPACK)?(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //4 digit season only releases - new Regex(@"^(?<title>.+?)\W(?:S|Season)\W?(?<season>\d{4}(?!\d+))(\W+|_|$)(?<extras>EXTRAS|SUBPACK)?(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Episodes with a title and season/episode in square brackets - new Regex(@"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+\[S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>(?<!\d+)\d{2}(?!\d+|i|p)))+\])\W?(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Supports 103/113 naming - new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))+(?<season>(?<!\d+)[1-9])(?<episode>[1-9][0-9]|[0][1-9])(?![a-z]|\d+))+", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Episodes with airdate - new Regex(@"^(?<title>.+?)?\W*(?<airyear>\d{4})\W+(?<airmonth>[0-1][0-9])\W+(?<airday>[0-3][0-9])(?!\W+[0-3][0-9])", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Supports 1103/1113 naming - new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))*(?<season>(?<!\d+|\(|\[|e|x)\d{2})(?<episode>(?<!e|x)\d{2}(?!p|i|\d+|\)|\]|\W\d+)))+(\W+|_|$)(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //4 digit episode number - //Episodes without a title, Single (S01E05, 1x05) AND Multi (S01E04E05, 1x04x05, etc) - new Regex(@"^(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>\d{4}(?!\d+|i|p)))+)(\W+|_|$)(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //4 digit episode number - //Episodes with a title, Single episodes (S01E05, 1x05, etc) & Multi-episode (S01E05E06, S01E05-06, S01E05 E06, etc) - new Regex(@"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>\d{4}(?!\d+|i|p)))+)\W?(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Episodes with single digit episode number (S01E1, S01E5E6, etc) - new Regex(@"^(?<title>.*?)(?:(?:[-_\W](?<![()\[!]))+S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]){1,2}(?<episode>\d{1}))+)+(\W+|_|$)(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //iTunes Season 1\05 Title (Quality).ext - new Regex(@"^(?:Season(?:_|-|\s|\.)(?<season>(?<!\d+)\d{1,2}(?!\d+)))(?:_|-|\s|\.)(?<episode>(?<!\d+)\d{1,2}(?!\d+))", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Anime - Title Absolute Episode Number (e66) - new Regex(@"^(?:\[(?<subgroup>.+?)\][-_. ]?)?(?<title>.+?)(?:(?:_|-|\s|\.)+(?:e|ep)(?<absoluteepisode>\d{2,3}))+.*?(?<hash>\[\w{8}\])?(?:$|\.)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Anime - Title Episode Absolute Episode Number (Series Title Episode 01) - new Regex(@"^(?<title>.+?)[-_. ](?:Episode)(?:[-_. ]+(?<absoluteepisode>(?<!\d+)\d{2,3}(?!\d+)))+(?:_|-|\s|\.)*?(?<hash>\[.{8}\])?(?:$|\.)?", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Anime - Title Absolute Episode Number - new Regex(@"^(?:\[(?<subgroup>.+?)\][-_. ]?)?(?<title>.+?)(?:[-_. ]+(?<absoluteepisode>(?<!\d+)\d{2,3}(?!\d+)))+(?:_|-|\s|\.)*?(?<hash>\[.{8}\])?(?:$|\.)?", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Anime - Title {Absolute Episode Number} - new Regex(@"^(?:\[(?<subgroup>.+?)\][-_. ]?)?(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+(?<absoluteepisode>(?<!\d+)\d{2,3}(?!\d+)))+(?:_|-|\s|\.)*?(?<hash>\[.{8}\])?(?:$|\.)?", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - - //Extant, terrible multi-episode naming (extant.10708.hdtv-lol.mp4) - new Regex(@"^(?<title>.+?)[-_. ](?<season>[0]?\d?)(?:(?<episode>\d{2}){2}(?!\d+))[-_. ]", - RegexOptions.IgnoreCase | RegexOptions.Compiled) - }; - private static readonly Regex[] RejectHashedReleasesRegex = new Regex[] { // Generic match for md5 and mixed-case hashes. @@ -328,9 +137,6 @@ namespace NzbDrone.Core.Parser private static readonly Regex AnimeReleaseGroupRegex = new Regex(@"^(?:\[(?<subgroup>(?!\s).+?(?<!\s))\](?:_|-|\s|\.)?)", RegexOptions.IgnoreCase | RegexOptions.Compiled); - private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_)(?<italian>\b(?:ita|italian)\b)|(?<german>german\b|videomann)|(?<flemish>flemish)|(?<greek>greek)|(?<french>(?:\W|_)(?:FR|VOSTFR)(?:\W|_))|(?<russian>\brus\b)|(?<dutch>nl\W?subs?)|(?<hungarian>\b(?:HUNDUB|HUN)\b)|(?<spanish>\b(?:espaƱol|castellano)\b)", - RegexOptions.IgnoreCase | RegexOptions.Compiled); - private static readonly Regex YearInTitleRegex = new Regex(@"^(?<title>.+?)(?:\W|_)?(?<year>\d{4})", RegexOptions.IgnoreCase | RegexOptions.Compiled); @@ -346,59 +152,20 @@ namespace NzbDrone.Core.Parser public static ParsedTrackInfo ParseMusicPath(string path) { + var fileInfo = new FileInfo(path); - var file = TagLib.File.Create(path); - var trackNumber = file.Tag.Track; - var trackTitle = file.Tag.Title; - var artist = file.Tag.FirstAlbumArtist; + var result = new ParsedTrackInfo{ }; - if (artist.IsNullOrWhiteSpace()) + if (MediaFiles.MediaFileExtensions.Extensions.Contains(fileInfo.Extension)) { - artist = file.Tag.FirstPerformer; + result = ParseAudioTags(path); } - - var artistTitleInfo = new ArtistTitleInfo + else { - Title = artist, - Year = (int)file.Tag.Year - }; - - var temp = new int[1]; - temp[0] = (int)trackNumber; - var result = new ParsedTrackInfo - { - Language = Language.English, //TODO Parse from Tag/Mediainfo - AlbumTitle = file.Tag.Album, - ArtistTitle = artist, - ArtistMBId = file.Tag.MusicBrainzArtistId, - AlbumMBId = file.Tag.MusicBrainzReleaseId, - TrackMBId = file.Tag.MusicBrainzReleaseType, - TrackNumbers = temp, - ArtistTitleInfo = artistTitleInfo, - Title = trackTitle - }; - - foreach (TagLib.ICodec codec in file.Properties.Codecs) - { - TagLib.IAudioCodec acodec = codec as TagLib.IAudioCodec; - TagLib.IVideoCodec vcodec = codec as TagLib.IVideoCodec; - - if (acodec != null && (acodec.MediaTypes & TagLib.MediaTypes.Audio) != TagLib.MediaTypes.None) - { - Logger.Debug("Audio Properties : " + acodec.Description); - Logger.Debug("Bitrate: " + acodec.AudioBitrate); - Logger.Debug("SampleRate: " + acodec.AudioSampleRate); - Logger.Debug("Channels: " + acodec.AudioChannels + "\n"); - - result.Quality = QualityParser.ParseQuality(acodec.Description, acodec.AudioBitrate, acodec.AudioSampleRate); - Logger.Debug("Quality parsed: {0}", result.Quality); - } - - + result = null; } - // TODO: Check if it is common that we might need to fallback to parser to gather details //var result = ParseMusicTitle(fileInfo.Name); @@ -632,17 +399,6 @@ namespace NzbDrone.Core.Parser return null; } - public static string CleanSeriesTitle(this string title) - { - long number = 0; - - //If Title only contains numbers return it as is. - if (long.TryParse(title, out number)) - return title; - - return NormalizeRegex.Replace(title, string.Empty).ToLower().RemoveAccent(); - } - public static string CleanArtistName(this string name) { long number = 0; @@ -722,97 +478,59 @@ namespace NzbDrone.Core.Parser return title; } - public static Language ParseLanguage(string title) + private static ParsedTrackInfo ParseAudioTags(string path) { - var lowerTitle = title.ToLower(); - - if (lowerTitle.Contains("english")) - return Language.English; - - if (lowerTitle.Contains("french")) - return Language.French; - - if (lowerTitle.Contains("spanish")) - return Language.Spanish; - - if (lowerTitle.Contains("danish")) - return Language.Danish; - - if (lowerTitle.Contains("dutch")) - return Language.Dutch; - - if (lowerTitle.Contains("japanese")) - return Language.Japanese; - - if (lowerTitle.Contains("cantonese")) - return Language.Cantonese; - - if (lowerTitle.Contains("mandarin")) - return Language.Mandarin; - - if (lowerTitle.Contains("korean")) - return Language.Korean; - - if (lowerTitle.Contains("russian")) - return Language.Russian; - - if (lowerTitle.Contains("polish")) - return Language.Polish; - - if (lowerTitle.Contains("vietnamese")) - return Language.Vietnamese; - - if (lowerTitle.Contains("swedish")) - return Language.Swedish; - - if (lowerTitle.Contains("norwegian")) - return Language.Norwegian; - - if (lowerTitle.Contains("nordic")) - return Language.Norwegian; - - if (lowerTitle.Contains("finnish")) - return Language.Finnish; - - if (lowerTitle.Contains("turkish")) - return Language.Turkish; - - if (lowerTitle.Contains("portuguese")) - return Language.Portuguese; - - if (lowerTitle.Contains("hungarian")) - return Language.Hungarian; - - var match = LanguageRegex.Match(title); - - if (match.Groups["italian"].Captures.Cast<Capture>().Any()) - return Language.Italian; - - if (match.Groups["german"].Captures.Cast<Capture>().Any()) - return Language.German; + var fileInfo = new FileInfo(path); + var file = TagLib.File.Create(path); + var trackNumber = file.Tag.Track; + var trackTitle = file.Tag.Title; - if (match.Groups["flemish"].Captures.Cast<Capture>().Any()) - return Language.Flemish; + var artist = file.Tag.FirstAlbumArtist; - if (match.Groups["greek"].Captures.Cast<Capture>().Any()) - return Language.Greek; + if (artist.IsNullOrWhiteSpace()) + { + artist = file.Tag.FirstPerformer; + } - if (match.Groups["spanish"].Captures.Cast<Capture>().Any()) - return Language.Spanish; + var artistTitleInfo = new ArtistTitleInfo + { + Title = artist, + Year = (int)file.Tag.Year + }; - if (match.Groups["french"].Success) - return Language.French; + var temp = new int[1]; + temp[0] = (int)trackNumber; + var result = new ParsedTrackInfo + { + Language = Language.English, //TODO Parse from Tag/Mediainfo + AlbumTitle = file.Tag.Album, + ArtistTitle = artist, + ArtistMBId = file.Tag.MusicBrainzArtistId, + AlbumMBId = file.Tag.MusicBrainzReleaseId, + TrackMBId = file.Tag.MusicBrainzReleaseType, + TrackNumbers = temp, + ArtistTitleInfo = artistTitleInfo, + Title = trackTitle + }; - if (match.Groups["russian"].Success) - return Language.Russian; + foreach (TagLib.ICodec codec in file.Properties.Codecs) + { + TagLib.IAudioCodec acodec = codec as TagLib.IAudioCodec; + TagLib.IVideoCodec vcodec = codec as TagLib.IVideoCodec; - if (match.Groups["dutch"].Success) - return Language.Dutch; + if (acodec != null && (acodec.MediaTypes & TagLib.MediaTypes.Audio) != TagLib.MediaTypes.None) + { + Logger.Debug("Audio Properties : " + acodec.Description); + Logger.Debug("Bitrate: " + acodec.AudioBitrate); + Logger.Debug("SampleRate: " + acodec.AudioSampleRate); + Logger.Debug("Channels: " + acodec.AudioChannels + "\n"); - if (match.Groups["hungarian"].Success) - return Language.Hungarian; + result.Quality = QualityParser.ParseQuality(acodec.Description, acodec.AudioBitrate, acodec.AudioSampleRate); + Logger.Debug("Quality parsed: {0}", result.Quality); + } + } - return Language.English; + return result; } private static ParsedTrackInfo ParseMatchMusicCollection(MatchCollection matchCollection) @@ -898,7 +616,7 @@ namespace NzbDrone.Core.Parser if (parseResult == null) { - return CleanSeriesTitle(title); + return CleanArtistName(title); } return parseResult.ArtistName; diff --git a/src/NzbDrone.Core/Parser/ParsingService.cs b/src/NzbDrone.Core/Parser/ParsingService.cs index 3f270deea..3f6972dec 100644 --- a/src/NzbDrone.Core/Parser/ParsingService.cs +++ b/src/NzbDrone.Core/Parser/ParsingService.cs @@ -156,7 +156,7 @@ namespace NzbDrone.Core.Parser if (searchCriteria != null) { - if (searchCriteria.Artist.CleanName == parsedAlbumInfo.ArtistName.CleanSeriesTitle()) + if (searchCriteria.Artist.CleanName == parsedAlbumInfo.ArtistName.CleanArtistName()) { return searchCriteria.Artist; }