diff --git a/src/NzbDrone.Core.Test/OrganizerTests/CleanTitleFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleFixture.cs similarity index 97% rename from src/NzbDrone.Core.Test/OrganizerTests/CleanTitleFixture.cs rename to src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleFixture.cs index 24fc0798b..d0cb085e7 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/CleanTitleFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleFixture.cs @@ -9,7 +9,7 @@ using NzbDrone.Core.Qualities; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; -namespace NzbDrone.Core.Test.OrganizerTests +namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests { [TestFixture] public class CleanTitleFixture : CoreTest @@ -64,6 +64,7 @@ namespace NzbDrone.Core.Test.OrganizerTests [TestCase("Is this okay?", "Is this okay")] [TestCase("[a] title", "a title")] [TestCase("backslash \\ backlash", "backslash backlash")] + [TestCase("I'm the Boss", "Im the Boss")] //[TestCase("", "")] //[TestCase("", "")] public void should_get_expected_title_back(string title, string expected) diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/EpisodeTitleCollapseFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/EpisodeTitleCollapseFixture.cs new file mode 100644 index 000000000..b417b5a94 --- /dev/null +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/EpisodeTitleCollapseFixture.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using System.Linq; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.Organizer; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests +{ + [TestFixture] + public class EpisodeTitleCollapseFixture : CoreTest + { + private Series _series; + private Episode _episode1; + private Episode _episode2; + private Episode _episode3; + private EpisodeFile _episodeFile; + private NamingConfig _namingConfig; + + [SetUp] + public void Setup() + { + _series = Builder + .CreateNew() + .With(s => s.Title = "South Park") + .Build(); + + + _namingConfig = new NamingConfig(); + _namingConfig.RenameEpisodes = true; + + + Mocker.GetMock() + .Setup(c => c.GetConfig()).Returns(_namingConfig); + + _episode1 = Builder.CreateNew() + .With(e => e.Title = "City Sushi") + .With(e => e.SeasonNumber = 15) + .With(e => e.EpisodeNumber = 6) + .With(e => e.AbsoluteEpisodeNumber = 100) + .Build(); + + _episode2 = Builder.CreateNew() + .With(e => e.Title = "City Sushi") + .With(e => e.SeasonNumber = 15) + .With(e => e.EpisodeNumber = 7) + .With(e => e.AbsoluteEpisodeNumber = 101) + .Build(); + + _episode3 = Builder.CreateNew() + .With(e => e.Title = "City Sushi") + .With(e => e.SeasonNumber = 15) + .With(e => e.EpisodeNumber = 8) + .With(e => e.AbsoluteEpisodeNumber = 102) + .Build(); + + _episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "SonarrTest" }; + + Mocker.GetMock() + .Setup(v => v.Get(Moq.It.IsAny())) + .Returns(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v)); + } + + + [TestCase("Hey, Baby, What's Wrong (1)", "Hey, Baby, What's Wrong (2)", "Hey, Baby, What's Wrong")] + [TestCase("Meet the Guys and Girls of Cycle 20 Part 1", "Meet the Guys and Girls of Cycle 20 Part 2", "Meet the Guys and Girls of Cycle 20")] + [TestCase("Meet the Guys and Girls of Cycle 20 part 1", "Meet the Guys and Girls of Cycle 20 part 2", "Meet the Guys and Girls of Cycle 20")] + public void should_collapse_episode_titles_when_episode_titles_are_the_same(string title1, string title2, string expected) + { + _namingConfig.StandardEpisodeFormat = "{Episode Title}"; + + _episode1.Title = title1; + _episode2.Title = title2; + + Subject.BuildFileName(new List { _episode1, _episode2 }, _series, _episodeFile) + .Should().Be(expected); + } + + [Test] + public void should_not_collapse_episode_titles_when_episode_titles_are_not_the_same() + { + _namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}"; + _namingConfig.MultiEpisodeStyle = 3; + + _episode1.Title = "Hello"; + _episode2.Title = "World"; + + Subject.BuildFileName(new List { _episode1, _episode2 }, _series, _episodeFile) + .Should().Be("South Park - S15E06-E07 - Hello + World"); + } + } +} diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs similarity index 95% rename from src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderFixture.cs rename to src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs index 857c6a434..e6e6e1822 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs @@ -8,10 +8,10 @@ using NUnit.Framework; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Organizer; using NzbDrone.Core.Qualities; -using NzbDrone.Core.Tv; using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; -namespace NzbDrone.Core.Test.OrganizerTests +namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests { [TestFixture] @@ -264,42 +264,6 @@ namespace NzbDrone.Core.Test.OrganizerTests .Should().Be("30.Rock.S01E01.xvid-LOL"); } - [Test] - public void should_only_have_one_episodeTitle_when_episode_titles_are_the_same() - { - _namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}"; - _namingConfig.MultiEpisodeStyle = 3; - - var episode = Builder.CreateNew() - .With(e => e.Title = "Hey, Baby, What's Wrong? (1)") - .With(e => e.SeasonNumber = 6) - .With(e => e.EpisodeNumber = 6) - .Build(); - - var episode2 = Builder.CreateNew() - .With(e => e.Title = "Hey, Baby, What's Wrong? (2)") - .With(e => e.SeasonNumber = 6) - .With(e => e.EpisodeNumber = 7) - .Build(); - - - Subject.BuildFileName(new List {episode2, episode}, new Series {Title = "30 Rock"}, _episodeFile) - .Should().Be("30 Rock - S06E06-E07 - Hey, Baby, What's Wrong!"); - } - - [Test] - public void should_have_two_episodeTitles_when_episode_titles_are_not_the_same() - { - _namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}"; - _namingConfig.MultiEpisodeStyle = 3; - - _episode1.Title = "Hello"; - _episode2.Title = "World"; - - Subject.BuildFileName(new List {_episode1, _episode2}, _series, _episodeFile) - .Should().Be("South Park - S15E06-E07 - Hello + World"); - } - [Test] public void should_use_airDate_if_series_isDaily() { diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index 73605ccdc..b61b09f52 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -56,9 +56,11 @@ namespace NzbDrone.Core.Organizer private static readonly Regex FileNameCleanupRegex = new Regex(@"([- ._])(\1)+", RegexOptions.Compiled); private static readonly Regex TrimSeparatorsRegex = new Regex(@"[- ._]$", RegexOptions.Compiled); - private static readonly Regex ScenifyRemoveChars = new Regex(@"(?<=\s)(,|<|>|\/|\\|;|:|'|""|\||`|~|!|\?|@|$|%|^|\*|-|_|=){1}(?=\s)|('|:|\?)(?=s|\s|$)|(\(|\)|\[|\]|\{|\})", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex ScenifyRemoveChars = new Regex(@"(?<=\s)(,|<|>|\/|\\|;|:|'|""|\||`|~|!|\?|@|$|%|^|\*|-|_|=){1}(?=\s)|('|:|\?|,)(?=(?:(?:s|m)\s)|\s|$)|(\(|\)|\[|\]|\{|\})", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex ScenifyReplaceChars = new Regex(@"[\/]", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex MultiPartCleanupRegex = new Regex(@"(?:\(\d+\)|Part\s\d+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly char[] EpisodeTitleTrimCharacters = new[] { ' ', '.', '?' }; public FileNameBuilder(INamingConfigService namingConfigService, @@ -656,12 +658,18 @@ namespace NzbDrone.Core.Organizer var titles = episodes .Select(c => c.Title.TrimEnd(EpisodeTitleTrimCharacters)) - .Select(Parser.Parser.CleanupEpisodeTitle) + .Select(CleanupEpisodeTitle) .Distinct(); return String.Join(separator, titles); } + private string CleanupEpisodeTitle(string title) + { + //this will remove (1),(2) from the end of multi part episodes. + return MultiPartCleanupRegex.Replace(title, string.Empty).Trim(); + } + private string GetQualityProper(Series series, QualityModel quality) { if (quality.Revision.Version > 1) diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs index 76f70ccc1..1fb0dde86 100644 --- a/src/NzbDrone.Core/Parser/Parser.cs +++ b/src/NzbDrone.Core/Parser/Parser.cs @@ -162,8 +162,6 @@ namespace NzbDrone.Core.Parser private static readonly Regex ReleaseGroupRegex = new Regex(@"-(?[a-z0-9]+)\b(?\bita\b|italian)|(?german\b|videomann)|(?flemish)|(?greek)|(?(?:\W|_)(?:FR|VOSTFR)(?:\W|_))|(?\brus\b)|(?nl\W?subs?)", RegexOptions.IgnoreCase | RegexOptions.Compiled); @@ -331,12 +329,6 @@ namespace NzbDrone.Core.Parser return NormalizeRegex.Replace(title, String.Empty).ToLower().RemoveAccent(); } - public static string CleanupEpisodeTitle(string title) - { - //this will remove (1),(2) from the end of multi part episodes. - return MultiPartCleanupRegex.Replace(title, string.Empty).Trim(); - } - public static string NormalizeEpisodeTitle(string title) { return SpecialEpisodeWordRegex.Replace(title, String.Empty)