diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/ReservedDeviceNameFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/ReservedDeviceNameFixture.cs new file mode 100644 index 000000000..383fbf64f --- /dev/null +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/ReservedDeviceNameFixture.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.MediaFiles.MediaInfo; +using NzbDrone.Core.Movies; +using NzbDrone.Core.Organizer; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests +{ + [TestFixture] + + public class ReservedDeviceNameFixture : CoreTest + { + private Movie _movie; + private MovieFile _movieFile; + private NamingConfig _namingConfig; + + [SetUp] + public void Setup() + { + _movie = Builder + .CreateNew() + .Build(); + + _namingConfig = NamingConfig.Default; + _namingConfig.RenameMovies = true; + + Mocker.GetMock() + .Setup(c => c.GetConfig()).Returns(_namingConfig); + + _movieFile = new MovieFile { 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)); + } + + [Test] + public void should_replace_reserved_device_name_in_movies_folder() + { + _movie.Title = "Con Man"; + _movie.Year = 2021; + _namingConfig.MovieFolderFormat = "{Movie.Title} ({Release Year})"; + + Subject.GetMovieFolder(_movie).Should().Be("Con_Man (2021)"); + } + + [Test] + public void should_replace_reserved_device_name_in_file_name() + { + _movie.Title = "Con Man"; + _movie.Year = 2021; + _namingConfig.StandardMovieFormat = "{Movie.Title} ({Release Year})"; + + Subject.BuildFileName(_movie, _movieFile).Should().Be("Con_Man (2021)"); + } + } +} diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index 1990a1a4f..3e6d91a1a 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -67,6 +67,8 @@ public class FileNameBuilder : IBuildFileNames private static readonly Regex TitlePrefixRegex = new Regex(@"^(The|An|A) (.*?)((?: *\([^)]+\))*)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex ReservedDeviceNamesRegex = new Regex(@"^(?:aux|com1|com2|com3|com4|com5|com6|com7|com8|com9|con|lpt1|lpt2|lpt3|lpt4|lpt5|lpt6|lpt7|lpt8|lpt9|nul|prn)\.", RegexOptions.Compiled | RegexOptions.IgnoreCase); + public FileNameBuilder(INamingConfigService namingConfigService, IQualityDefinitionService qualityDefinitionService, IUpdateMediaInfo mediaInfoUpdater, @@ -119,6 +121,7 @@ public string BuildFileName(Movie movie, MovieFile movieFile, NamingConfig namin component = FileNameCleanupRegex.Replace(component, match => match.Captures[0].Value[0].ToString()); component = TrimSeparatorsRegex.Replace(component, string.Empty); + component = ReplaceReservedDeviceNames(component); if (component.IsNotNullOrWhiteSpace()) { @@ -180,6 +183,7 @@ public string GetMovieFolder(Movie movie, NamingConfig namingConfig = null) var component = ReplaceTokens(splitPattern, tokenHandlers, namingConfig); component = CleanFolderName(component); + component = ReplaceReservedDeviceNames(component); if (component.IsNotNullOrWhiteSpace()) { @@ -566,6 +570,12 @@ private string GetOriginalFileName(MovieFile movieFile) return Path.GetFileNameWithoutExtension(movieFile.RelativePath); } + + private string ReplaceReservedDeviceNames(string input) + { + // Replace reserved windows device names with an alternative + return ReservedDeviceNamesRegex.Replace(input, match => match.Value.Replace(".", "_")); + } } internal sealed class TokenMatch