diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/EditionTagsFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/EditionTagsFixture.cs index 08324f112..74a0378d1 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/EditionTagsFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/EditionTagsFixture.cs @@ -24,7 +24,7 @@ public void Setup() { _movie = Builder .CreateNew() - .With(s => s.Title = "South Park") + .With(s => s.Title = "Movie Title") .Build(); _movieFile = new MovieFile { Quality = new QualityModel(), ReleaseGroup = "SonarrTest" }; @@ -51,7 +51,7 @@ public void should_add_edition_tag() _namingConfig.StandardMovieFormat = "{Movie Title} [{Edition Tags}]"; Subject.BuildFileName(_movie, _movieFile) - .Should().Be("South Park [Uncut]"); + .Should().Be("Movie Title [Uncut]"); } [TestCase("{Movie Title} {edition-{Edition Tags}}")] @@ -61,7 +61,100 @@ public void should_conditional_hide_edition_tags_in_plex_format(string movieForm _namingConfig.StandardMovieFormat = movieFormat; Subject.BuildFileName(_movie, _movieFile) - .Should().Be("South Park"); + .Should().Be("Movie Title"); + } + + [Test] + [TestCase("1st anniversary edition", "{Movie Title} [{Edition Tags}]", "Movie Title [1st Anniversary Edition]")] + [TestCase("2nd Anniversary edition", "{Movie Title} [{Edition Tags}]", "Movie Title [2nd Anniversary Edition]")] + [TestCase("3rd anniversary Edition", "{Movie Title} [{Edition Tags}]", "Movie Title [3rd Anniversary Edition]")] + [TestCase("4th anNiverSary eDitIOn", "{Movie Title} [{Edition Tags}]", "Movie Title [4th Anniversary Edition]")] + [TestCase("5th anniversary edition", "{Movie Title} [{Edition Tags}]", "Movie Title [5th Anniversary Edition]")] + [TestCase("6th anNiverSary EDITION", "{Movie Title} [{Edition Tags}]", "Movie Title [6th Anniversary Edition]")] + [TestCase("7TH anniversary edition", "{Movie Title} [{Edition Tags}]", "Movie Title [7th Anniversary Edition]")] + [TestCase("8Th anniversary edition", "{Movie Title} [{Edition Tags}]", "Movie Title [8th Anniversary Edition]")] + [TestCase("9tH anniversary edition", "{Movie Title} [{Edition Tags}]", "Movie Title [9th Anniversary Edition]")] + + [TestCase("10th anniversary edition", "{Movie Title} [{edition tags}]", "Movie Title [10th anniversary edition]")] + [TestCase("10TH anniversary edition", "{Movie Title} [{edition tags}]", "Movie Title [10th anniversary edition]")] + [TestCase("10Th anniversary edition", "{Movie Title} [{edition tags}]", "Movie Title [10th anniversary edition]")] + [TestCase("10th anniversary edition", "{Movie Title} [{Edition Tags}]", "Movie Title [10th Anniversary Edition]")] + [TestCase("10TH anniversary edition", "{Movie Title} [{Edition Tags}]", "Movie Title [10th Anniversary Edition]")] + [TestCase("10Th anniversary edition", "{Movie Title} [{Edition Tags}]", "Movie Title [10th Anniversary Edition]")] + [TestCase("10th anniversary edition", "{Movie Title} [{EDITION TAGS}]", "Movie Title [10TH ANNIVERSARY EDITION]")] + [TestCase("10TH anniversary edition", "{Movie Title} [{EDITION TAGS}]", "Movie Title [10TH ANNIVERSARY EDITION]")] + [TestCase("10Th anniversary edition", "{Movie Title} [{EDITION TAGS}]", "Movie Title [10TH ANNIVERSARY EDITION]")] + public void should_always_lowercase_ordinals(string edition, string movieFormat, string expected) + { + _movieFile.Edition = edition; + _namingConfig.StandardMovieFormat = movieFormat; + + Subject.BuildFileName(_movie, _movieFile) + .Should().Be(expected); + } + + [Test] + [TestCase("imax", "{Movie Title} [{edition tags}]", "Movie Title [imax]")] + [TestCase("IMAX", "{Movie Title} [{edition tags}]", "Movie Title [imax]")] + [TestCase("Imax", "{Movie Title} [{edition tags}]", "Movie Title [imax]")] + [TestCase("imax", "{Movie Title} [{Edition Tags}]", "Movie Title [IMAX]")] + [TestCase("IMAX", "{Movie Title} [{Edition Tags}]", "Movie Title [IMAX]")] + [TestCase("Imax", "{Movie Title} [{Edition Tags}]", "Movie Title [IMAX]")] + [TestCase("imax", "{Movie Title} [{EDITION TAGS}]", "Movie Title [IMAX]")] + [TestCase("IMAX", "{Movie Title} [{EDITION TAGS}]", "Movie Title [IMAX]")] + [TestCase("Imax", "{Movie Title} [{EDITION TAGS}]", "Movie Title [IMAX]")] + + [TestCase("imax edition", "{Movie Title} [{edition tags}]", "Movie Title [imax edition]")] + [TestCase("imax edition", "{Movie Title} [{Edition Tags}]", "Movie Title [IMAX Edition]")] + [TestCase("Imax edition", "{Movie Title} [{EDITION TAGS}]", "Movie Title [IMAX EDITION]")] + [TestCase("imax version", "{Movie Title} [{Edition Tags}]", "Movie Title [IMAX Version]")] + [TestCase("IMAX-edition", "{Movie Title} [{Edition Tags}]", "Movie Title [IMAX-Edition]")] + [TestCase("IMAX_edition", "{Movie Title} [{Edition Tags}]", "Movie Title [IMAX_Edition]")] + [TestCase("IMAX.eDiTioN", "{Movie Title} [{Edition Tags}]", "Movie Title [IMAX.Edition]")] + [TestCase("IMAX ed.", "{Movie Title} [{edition tags}]", "Movie Title [imax ed.]")] + [TestCase("IMAX ed.", "{Movie Title} [{Edition Tags}]", "Movie Title [IMAX Ed.]")] + [TestCase("Imax-ed.", "{Movie Title} [{Edition Tags}]", "Movie Title [IMAX-Ed.]")] + [TestCase("imax.Ed", "{Movie Title} [{Edition Tags}]", "Movie Title [IMAX.Ed]")] + [TestCase("Imax_ed", "{Movie Title} [{Edition Tags}]", "Movie Title [IMAX_Ed]")] + + [TestCase("3d", "{Movie Title} [{edition tags}]", "Movie Title [3d]")] + [TestCase("3D", "{Movie Title} [{edition tags}]", "Movie Title [3d]")] + [TestCase("3d", "{Movie Title} [{Edition Tags}]", "Movie Title [3D]")] + [TestCase("3D", "{Movie Title} [{Edition Tags}]", "Movie Title [3D]")] + [TestCase("3d", "{Movie Title} [{EDITION TAGS}]", "Movie Title [3D]")] + [TestCase("3D", "{Movie Title} [{EDITION TAGS}]", "Movie Title [3D]")] + + [TestCase("hdr", "{Movie Title} [{edition tags}]", "Movie Title [hdr]")] + [TestCase("HDR", "{Movie Title} [{edition tags}]", "Movie Title [hdr]")] + [TestCase("Hdr", "{Movie Title} [{edition tags}]", "Movie Title [hdr]")] + [TestCase("hdr", "{Movie Title} [{Edition Tags}]", "Movie Title [HDR]")] + [TestCase("HDR", "{Movie Title} [{Edition Tags}]", "Movie Title [HDR]")] + [TestCase("Hdr", "{Movie Title} [{Edition Tags}]", "Movie Title [HDR]")] + [TestCase("hdr", "{Movie Title} [{EDITION TAGS}]", "Movie Title [HDR]")] + [TestCase("HDR", "{Movie Title} [{EDITION TAGS}]", "Movie Title [HDR]")] + [TestCase("Hdr", "{Movie Title} [{EDITION TAGS}]", "Movie Title [HDR]")] + + [TestCase("sdr", "{Movie Title} [{edition tags}]", "Movie Title [sdr]")] + [TestCase("SDR", "{Movie Title} [{edition tags}]", "Movie Title [sdr]")] + [TestCase("Sdr", "{Movie Title} [{edition tags}]", "Movie Title [sdr]")] + [TestCase("sdr", "{Movie Title} [{Edition Tags}]", "Movie Title [SDR]")] + [TestCase("SDR", "{Movie Title} [{Edition Tags}]", "Movie Title [SDR]")] + [TestCase("Sdr", "{Movie Title} [{Edition Tags}]", "Movie Title [SDR]")] + [TestCase("sdr", "{Movie Title} [{EDITION TAGS}]", "Movie Title [SDR]")] + [TestCase("SDR", "{Movie Title} [{EDITION TAGS}]", "Movie Title [SDR]")] + [TestCase("Sdr", "{Movie Title} [{EDITION TAGS}]", "Movie Title [SDR]")] + + // Test non-special strings + [TestCase("THEATRICAL", "{Movie Title} [{Edition Tags}]", "Movie Title [Theatrical]")] + [TestCase("director's CUt", "{Movie Title} [{Edition Tags}]", "Movie Title [Director's Cut]")] + + public void should_always_uppercase_special_strings(string edition, string movieFormat, string expected) + { + _movieFile.Edition = edition; + _namingConfig.StandardMovieFormat = movieFormat; + + Subject.BuildFileName(_movie, _movieFile) + .Should().Be(expected); } } } diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index 1e865b543..4ad89e111 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -309,6 +309,42 @@ private string GetLanguageTitle(Movie movie, string isoCodes) return movie.Title; } + private string ChangeCaseOfMatchesInString(string text, string exp, Func changeCase) + { + // find all matches and their indexes + var wb = @"(?:\b|[_.-])"; + var matches = Regex.Matches(text, $"{wb}{exp}{wb}", RegexOptions.IgnoreCase) + .Cast() + .Select(m => new { m.Index, m.Value }) + .ToList(); + + // replace each match with the uppercase version + foreach (var match in matches) + { + text = text.Remove(match.Index, match.Value.Length).Insert(match.Index, changeCase(match.Value)); + } + + return text; + } + + private string ToTitleCaseIgnoreOrdinals(string text) + { + var exp = @"([0-9]{1,3}(?:st|th|rd|nd))"; + return ChangeCaseOfMatchesInString(text, exp, m => m.ToLower()); + } + + private string ToTitleCaseAlwaysUpper(string text) + { + var exp = @"(imax|3d|sdr|hdr)"; + return ChangeCaseOfMatchesInString(text, exp, m => m.ToUpper()); + } + + private string ToEditionTitleCase(MovieFile movieFile) + { + var titleCase = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(movieFile.Edition.ToLower()); + return ToTitleCaseAlwaysUpper(ToTitleCaseIgnoreOrdinals(titleCase)); + } + private void AddEditionTagsTokens(Dictionary> tokenHandlers, MovieFile movieFile) { if (movieFile.Edition.IsNotNullOrWhiteSpace())