diff --git a/src/NzbDrone.Api/Config/NamingConfigResource.cs b/src/NzbDrone.Api/Config/NamingConfigResource.cs index e7f13f48a..93665a5f4 100644 --- a/src/NzbDrone.Api/Config/NamingConfigResource.cs +++ b/src/NzbDrone.Api/Config/NamingConfigResource.cs @@ -6,6 +6,7 @@ namespace NzbDrone.Api.Config public class NamingConfigResource : RestResource { public bool RenameEpisodes { get; set; } + public bool ReplaceIllegalCharacters { get; set; } public int MultiEpisodeStyle { get; set; } public string StandardEpisodeFormat { get; set; } public string DailyEpisodeFormat { get; set; } diff --git a/src/NzbDrone.Core.Test/OrganizerTests/BuildFilePathFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/BuildFilePathFixture.cs index 18bf8b5ea..3848659c9 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/BuildFilePathFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/BuildFilePathFixture.cs @@ -17,7 +17,7 @@ namespace NzbDrone.Core.Test.OrganizerTests [SetUp] public void Setup() { - namingConfig = new NamingConfig(); + namingConfig = NamingConfig.Default; Mocker.GetMock() .Setup(c => c.GetConfig()).Returns(namingConfig); diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleFixture.cs index 23d0ab44e..f6aabeb9d 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleFixture.cs @@ -36,7 +36,7 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests _episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "SonarrTest" }; - _namingConfig = new NamingConfig(); + _namingConfig = NamingConfig.Default; _namingConfig.RenameEpisodes = true; Mocker.GetMock() diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/EpisodeTitleCollapseFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/EpisodeTitleCollapseFixture.cs index 0ec0e77c7..2f0aaddc8 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/EpisodeTitleCollapseFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/EpisodeTitleCollapseFixture.cs @@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests .Build(); - _namingConfig = new NamingConfig(); + _namingConfig = NamingConfig.Default; _namingConfig.RenameEpisodes = true; diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs index d6062a787..8794e55cd 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs @@ -31,7 +31,7 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests .Build(); - _namingConfig = new NamingConfig(); + _namingConfig = NamingConfig.Default; _namingConfig.RenameEpisodes = true; diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/MultiEpisodeFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/MultiEpisodeFixture.cs index 28afb5c19..aceb422c1 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/MultiEpisodeFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/MultiEpisodeFixture.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests .Build(); - _namingConfig = new NamingConfig(); + _namingConfig = NamingConfig.Default; _namingConfig.RenameEpisodes = true; diff --git a/src/NzbDrone.Core.Test/OrganizerTests/GetSeasonFolderFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/GetSeasonFolderFixture.cs index b971305a8..aeca92364 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/GetSeasonFolderFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/GetSeasonFolderFixture.cs @@ -15,7 +15,7 @@ namespace NzbDrone.Core.Test.OrganizerTests [SetUp] public void Setup() { - namingConfig = new NamingConfig(); + namingConfig = NamingConfig.Default; Mocker.GetMock() .Setup(c => c.GetConfig()).Returns(namingConfig); diff --git a/src/NzbDrone.Core.Test/OrganizerTests/GetSeriesFolderFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/GetSeriesFolderFixture.cs index 538a79e09..9cf0b5e01 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/GetSeriesFolderFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/GetSeriesFolderFixture.cs @@ -1,4 +1,3 @@ -using System; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Organizer; @@ -16,7 +15,7 @@ namespace NzbDrone.Core.Test.OrganizerTests [SetUp] public void Setup() { - namingConfig = new NamingConfig(); + namingConfig = NamingConfig.Default; Mocker.GetMock() .Setup(c => c.GetConfig()).Returns(namingConfig); diff --git a/src/NzbDrone.Core/Datastore/Migration/093_naming_config_replace_characters.cs b/src/NzbDrone.Core/Datastore/Migration/093_naming_config_replace_characters.cs new file mode 100644 index 000000000..4ba4be853 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/093_naming_config_replace_characters.cs @@ -0,0 +1,15 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(93)] + public class naming_config_replace_illegal_characters : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("NamingConfig").AddColumn("ReplaceIllegalCharacters").AsBoolean().WithDefaultValue(true); + Update.Table("NamingConfig").Set(new { ReplaceIllegalCharacters = true }).AllRows(); + } + } +} diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index e1299e004..75c4b246f 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -270,6 +270,7 @@ + diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index 80c65dcbc..e391ebb34 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -128,7 +128,7 @@ namespace NzbDrone.Core.Organizer AddQualityTokens(tokenHandlers, series, episodeFile); AddMediaInfoTokens(tokenHandlers, episodeFile); - var fileName = ReplaceTokens(pattern, tokenHandlers).Trim(); + var fileName = ReplaceTokens(pattern, tokenHandlers, namingConfig).Trim(); fileName = FileNameCleanupRegex.Replace(fileName, match => match.Captures[0].Value[0].ToString()); fileName = TrimSeparatorsRegex.Replace(fileName, string.Empty); @@ -224,7 +224,7 @@ namespace NzbDrone.Core.Organizer AddSeriesTokens(tokenHandlers, series); - return CleanFolderName(ReplaceTokens(namingConfig.SeriesFolderFormat, tokenHandlers)); + return CleanFolderName(ReplaceTokens(namingConfig.SeriesFolderFormat, tokenHandlers, namingConfig)); } public string GetSeasonFolder(Series series, int seasonNumber, NamingConfig namingConfig = null) @@ -239,7 +239,7 @@ namespace NzbDrone.Core.Organizer AddSeriesTokens(tokenHandlers, series); AddSeasonTokens(tokenHandlers, seasonNumber); - return CleanFolderName(ReplaceTokens(namingConfig.SeasonFolderFormat, tokenHandlers)); + return CleanFolderName(ReplaceTokens(namingConfig.SeasonFolderFormat, tokenHandlers, namingConfig)); } public static string CleanTitle(string title) @@ -251,7 +251,7 @@ namespace NzbDrone.Core.Organizer return title; } - public static string CleanFileName(string name) + public static string CleanFileName(string name, bool replace = true) { string result = name; string[] badCharacters = { "\\", "/", "<", ">", "?", "*", ":", "|", "\"" }; @@ -259,7 +259,7 @@ namespace NzbDrone.Core.Organizer for (int i = 0; i < badCharacters.Length; i++) { - result = result.Replace(badCharacters[i], goodCharacters[i]); + result = result.Replace(badCharacters[i], replace ? goodCharacters[i] : string.Empty); } return result.Trim(); @@ -551,12 +551,12 @@ namespace NzbDrone.Core.Organizer return string.Join("+", tokens.Distinct()); } - private string ReplaceTokens(string pattern, Dictionary> tokenHandlers) + private string ReplaceTokens(string pattern, Dictionary> tokenHandlers, NamingConfig namingConfig) { - return TitleRegex.Replace(pattern, match => ReplaceToken(match, tokenHandlers)); + return TitleRegex.Replace(pattern, match => ReplaceToken(match, tokenHandlers, namingConfig)); } - private string ReplaceToken(Match match, Dictionary> tokenHandlers) + private string ReplaceToken(Match match, Dictionary> tokenHandlers, NamingConfig namingConfig) { var tokenMatch = new TokenMatch { @@ -591,7 +591,7 @@ namespace NzbDrone.Core.Organizer replacementText = replacementText.Replace(" ", tokenMatch.Separator); } - replacementText = CleanFileName(replacementText); + replacementText = CleanFileName(replacementText, namingConfig.ReplaceIllegalCharacters); if (!replacementText.IsNullOrWhiteSpace()) { diff --git a/src/NzbDrone.Core/Organizer/NamingConfig.cs b/src/NzbDrone.Core/Organizer/NamingConfig.cs index 974713c39..63a8e2ec8 100644 --- a/src/NzbDrone.Core/Organizer/NamingConfig.cs +++ b/src/NzbDrone.Core/Organizer/NamingConfig.cs @@ -11,6 +11,7 @@ namespace NzbDrone.Core.Organizer return new NamingConfig { RenameEpisodes = false, + ReplaceIllegalCharacters = true, MultiEpisodeStyle = 0, StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}", DailyEpisodeFormat = "{Series Title} - {Air-Date} - {Episode Title} {Quality Full}", @@ -22,6 +23,7 @@ namespace NzbDrone.Core.Organizer } public bool RenameEpisodes { get; set; } + public bool ReplaceIllegalCharacters { get; set; } public int MultiEpisodeStyle { get; set; } public string StandardEpisodeFormat { get; set; } public string DailyEpisodeFormat { get; set; } @@ -29,4 +31,4 @@ namespace NzbDrone.Core.Organizer public string SeriesFolderFormat { get; set; } public string SeasonFolderFormat { get; set; } } -} \ No newline at end of file +} diff --git a/src/UI/Settings/MediaManagement/Naming/NamingViewTemplate.hbs b/src/UI/Settings/MediaManagement/Naming/NamingViewTemplate.hbs index 3abafdc09..361954d70 100644 --- a/src/UI/Settings/MediaManagement/Naming/NamingViewTemplate.hbs +++ b/src/UI/Settings/MediaManagement/Naming/NamingViewTemplate.hbs @@ -24,6 +24,29 @@ +
+ + +
+
+
+
+
@@ -184,7 +207,7 @@
- +