diff --git a/frontend/src/Settings/MediaManagement/Naming/NamingModal.js b/frontend/src/Settings/MediaManagement/Naming/NamingModal.js index 9ad4d27aa..ab7c4bc09 100644 --- a/frontend/src/Settings/MediaManagement/Naming/NamingModal.js +++ b/frontend/src/Settings/MediaManagement/Naming/NamingModal.js @@ -120,7 +120,8 @@ const editionTokens = [ ]; const customFormatTokens = [ - { token: '{Custom Formats}', example: 'Surround Sound x264' } + { token: '{Custom Formats}', example: 'Surround Sound x264' }, + { token: '{Custom Format:FormatName}', example: 'AMZN' } ]; const originalTokens = [ diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CustomFormatsFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CustomFormatsFixture.cs new file mode 100644 index 000000000..3fa2b39b4 --- /dev/null +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CustomFormatsFixture.cs @@ -0,0 +1,138 @@ +using System.Collections.Generic; +using System.Linq; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.CustomFormats; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.Movies; +using NzbDrone.Core.Organizer; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests +{ + [TestFixture] + + public class CustomFormatsFixture : CoreTest + { + private Movie _movie; + private MovieFile _movieFile; + private NamingConfig _namingConfig; + + private List _customFormats; + + [SetUp] + public void Setup() + { + _movie = Builder + .CreateNew() + .With(s => s.Title = "South Park") + .Build(); + + _namingConfig = NamingConfig.Default; + _namingConfig.RenameMovies = true; + + Mocker.GetMock() + .Setup(c => c.GetConfig()).Returns(_namingConfig); + + _movieFile = new MovieFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "RadarrTest" }; + + _customFormats = new List() + { + new CustomFormat() + { + Name = "INTERNAL", + IncludeCustomFormatWhenRenaming = true + }, + new CustomFormat() + { + Name = "AMZN", + IncludeCustomFormatWhenRenaming = true + }, + new CustomFormat() + { + Name = "NAME WITH SPACES", + IncludeCustomFormatWhenRenaming = true + }, + new CustomFormat() + { + Name = "NotIncludedFormat", + IncludeCustomFormatWhenRenaming = false + } + }; + + Mocker.GetMock() + .Setup(v => v.Get(Moq.It.IsAny())) + .Returns(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v)); + } + + [TestCase("{Custom Formats}", "INTERNAL AMZN NAME WITH SPACES")] + public void should_replace_custom_formats(string format, string expected) + { + _namingConfig.StandardMovieFormat = format; + + Subject.BuildFileName(_movie, _movieFile, customFormats: _customFormats) + .Should().Be(expected); + } + + [TestCase("{Custom Formats}", "")] + public void should_replace_custom_formats_with_no_custom_formats(string format, string expected) + { + _namingConfig.StandardMovieFormat = format; + + Subject.BuildFileName(_movie, _movieFile, customFormats: new List()) + .Should().Be(expected); + } + + [TestCase("{Custom Formats:-INTERNAL}", "AMZN NAME WITH SPACES")] + [TestCase("{Custom Formats:-NAME WITH SPACES}", "INTERNAL AMZN")] + [TestCase("{Custom Formats:-INTERNAL,NAME WITH SPACES}", "AMZN")] + [TestCase("{Custom Formats:INTERNAL}", "INTERNAL")] + [TestCase("{Custom Formats:NAME WITH SPACES}", "NAME WITH SPACES")] + [TestCase("{Custom Formats:INTERNAL,NAME WITH SPACES}", "INTERNAL NAME WITH SPACES")] + public void should_replace_custom_formats_with_filtered_names(string format, string expected) + { + _namingConfig.StandardMovieFormat = format; + + Subject.BuildFileName(_movie, _movieFile, customFormats: _customFormats) + .Should().Be(expected); + } + + [TestCase("{Custom Formats:-}", "{Custom Formats:-}")] + [TestCase("{Custom Formats:}", "{Custom Formats:}")] + public void should_not_replace_custom_formats_due_to_invalid_token(string format, string expected) + { + _namingConfig.StandardMovieFormat = format; + + Subject.BuildFileName(_movie, _movieFile, customFormats: _customFormats) + .Should().Be(expected); + } + + [TestCase("{Custom Format}", "")] + [TestCase("{Custom Format:INTERNAL}", "INTERNAL")] + [TestCase("{Custom Format:AMZN}", "AMZN")] + [TestCase("{Custom Format:NAME WITH SPACES}", "NAME WITH SPACES")] + [TestCase("{Custom Format:DOESNOTEXIST}", "")] + [TestCase("{Custom Format:INTERNAL} - {Custom Format:AMZN}", "INTERNAL - AMZN")] + [TestCase("{Custom Format:AMZN} - {Custom Format:INTERNAL}", "AMZN - INTERNAL")] + public void should_replace_custom_format(string format, string expected) + { + _namingConfig.StandardMovieFormat = format; + + Subject.BuildFileName(_movie, _movieFile, customFormats: _customFormats) + .Should().Be(expected); + } + + [TestCase("{Custom Format}", "")] + [TestCase("{Custom Format:INTERNAL}", "")] + [TestCase("{Custom Format:AMZN}", "")] + public void should_replace_custom_format_with_no_custom_formats(string format, string expected) + { + _namingConfig.StandardMovieFormat = format; + + Subject.BuildFileName(_movie, _movieFile, customFormats: new List()) + .Should().Be(expected); + } + } +} diff --git a/src/NzbDrone.Core/CustomFormats/CustomFormat.cs b/src/NzbDrone.Core/CustomFormats/CustomFormat.cs index 51e61e287..8c58b1e07 100644 --- a/src/NzbDrone.Core/CustomFormats/CustomFormat.cs +++ b/src/NzbDrone.Core/CustomFormats/CustomFormat.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using NzbDrone.Core.Datastore; diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index 18914b3e1..f01574317 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -39,7 +39,7 @@ public class FileNameBuilder : IBuildFileNames private readonly ICustomFormatCalculationService _formatCalculator; private readonly Logger _logger; - private static readonly Regex TitleRegex = new Regex(@"(?\{(?:imdb-|edition-))?\{(?[- ._\[(]*)(?(?:[a-z0-9]+)(?:(?[- ._]+)(?:[a-z0-9]+))?)(?::(?[a-z0-9|+-]+(?[-} ._)\]]*)\}", + private static readonly Regex TitleRegex = new Regex(@"(?\{(?:imdb-|edition-))?\{(?[- ._\[(]*)(?(?:[a-z0-9]+)(?:(?[- ._]+)(?:[a-z0-9]+))?)(?::(?[ ,a-z0-9|+-]+(?[-} ._)\]]*)\}", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); public static readonly Regex MovieTitleRegex = new Regex(@"(?\{((?:(Movie|Original))(?[- ._])(Clean)?(Original)?(Title|Filename)(The)?)(?::(?[a-z0-9|-]+))?\})", @@ -414,7 +414,39 @@ private void AddCustomFormats(Dictionary> token customFormats = _formatCalculator.ParseCustomFormat(movieFile, movie); } - tokenHandlers["{Custom Formats}"] = m => string.Join(" ", customFormats.Where(x => x.IncludeCustomFormatWhenRenaming)); + tokenHandlers["{Custom Formats}"] = m => GetCustomFormatsToken(customFormats, m.CustomFormat); + tokenHandlers["{Custom Format}"] = m => + { + if (m.CustomFormat.IsNullOrWhiteSpace()) + { + return string.Empty; + } + + return customFormats.FirstOrDefault(x => x.IncludeCustomFormatWhenRenaming && x.Name == m.CustomFormat)?.ToString() ?? string.Empty; + }; + } + + private string GetCustomFormatsToken(List customFormats, string filter) + { + var tokens = customFormats.Where(x => x.IncludeCustomFormatWhenRenaming).ToList(); + + var filteredTokens = tokens; + + if (filter.IsNotNullOrWhiteSpace()) + { + if (filter.StartsWith("-")) + { + var splitFilter = filter.Substring(1).Split(','); + filteredTokens = tokens.Where(c => !splitFilter.Contains(c.Name)).ToList(); + } + else + { + var splitFilter = filter.Split(','); + filteredTokens = tokens.Where(c => splitFilter.Contains(c.Name)).ToList(); + } + } + + return string.Join(" ", filteredTokens); } private string GetLanguagesToken(List mediaInfoLanguages, string filter, bool skipEnglishOnly, bool quoted)