From c86d5980d386f519debe9461e5d7710a8e549e96 Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Tue, 11 Aug 2020 00:07:26 +0200 Subject: [PATCH] Fixed: Include extension when calculating maximum episode title length when renaming files Fixed: Option to override max filename length with MAX_NAME environment variable (cherry picked from commit 6efee036a826027391433b7d0c954ebc1a75c679) --- .../MoveTrackFileFixture.cs | 9 ++---- .../OrganizerTests/BuildFilePathFixture.cs | 17 +++++++++-- .../FileNameBuilderFixture.cs | 7 +++-- .../TruncatedTrackTitlesFixture.cs | 16 ++++++++++ .../MediaFiles/RenameTrackFileService.cs | 4 +-- .../MediaFiles/TrackFileMovingService.cs | 9 ++---- .../Organizer/FileNameBuilder.cs | 30 +++++++++++-------- .../Organizer/FileNameSampleService.cs | 2 +- 8 files changed, 61 insertions(+), 33 deletions(-) diff --git a/src/NzbDrone.Core.Test/MediaFiles/TrackFileMovingServiceTests/MoveTrackFileFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/TrackFileMovingServiceTests/MoveTrackFileFixture.cs index e2b18e5cc..41955ea53 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/TrackFileMovingServiceTests/MoveTrackFileFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/TrackFileMovingServiceTests/MoveTrackFileFixture.cs @@ -7,6 +7,7 @@ using Moq; using NUnit.Framework; using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; +using NzbDrone.Core.CustomFormats; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Events; @@ -43,12 +44,8 @@ namespace NzbDrone.Core.Test.MediaFiles.TrackFileMovingServiceTests .Build(); Mocker.GetMock() - .Setup(s => s.BuildTrackFileName(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), null, null)) - .Returns("Album\\File Name"); - - Mocker.GetMock() - .Setup(s => s.BuildTrackFilePath(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(@"C:\Test\Music\Artist\Album\File Name.mp3".AsOsAgnostic()); + .Setup(s => s.BuildTrackFilePath(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) + .Returns(@"C:\Test\Music\Artist\Album\File Name.mp3".AsOsAgnostic()); var rootFolder = @"C:\Test\Music\".AsOsAgnostic(); Mocker.GetMock() diff --git a/src/NzbDrone.Core.Test/OrganizerTests/BuildFilePathFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/BuildFilePathFixture.cs index 2d3abed95..cc7409e60 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/BuildFilePathFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/BuildFilePathFixture.cs @@ -1,6 +1,8 @@ +using System.Linq; using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; +using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Music; using NzbDrone.Core.Organizer; using NzbDrone.Core.Test.Framework; @@ -9,7 +11,6 @@ using NzbDrone.Test.Common; namespace NzbDrone.Core.Test.OrganizerTests { [TestFixture] - public class BuildFilePathFixture : CoreTest { private NamingConfig _namingConfig; @@ -29,12 +30,24 @@ namespace NzbDrone.Core.Test.OrganizerTests var filename = @"02 - Track Title"; var expectedPath = @"C:\Test\Fake- The Artist\02 - Track Title.flac"; + var fakeTracks = Builder.CreateListOfSize(1) + .All() + .With(s => s.Title = "Episode Title") + .With(s => s.AbsoluteTrackNumber = 5) + .Build().ToList(); var fakeArtist = Builder.CreateNew() .With(s => s.Name = "Fake: The Artist") .With(s => s.Path = @"C:\Test\Fake- The Artist".AsOsAgnostic()) .Build(); + var fakeAlbum = Builder.CreateNew() + .With(e => e.ArtistId = fakeArtist.Id) + .Build(); + var fakeTrackFile = Builder.CreateNew() + .With(s => s.SceneName = filename) + .With(f => f.Artist = fakeArtist) + .Build(); - Subject.BuildTrackFilePath(fakeArtist, filename, ".flac").Should().Be(expectedPath.AsOsAgnostic()); + Subject.BuildTrackFilePath(fakeTracks, fakeArtist, fakeAlbum, fakeTrackFile, ".flac").Should().Be(expectedPath.AsOsAgnostic()); } } } diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs index 8dea21bc4..3457b047a 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs @@ -15,7 +15,6 @@ using NzbDrone.Core.Test.Framework; namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests { [TestFixture] - public class FileNameBuilderFixture : CoreTest { private Artist _artist; @@ -511,6 +510,7 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests { _namingConfig.RenameTracks = false; _trackFile.Path = "Linkin Park - 06 - Test"; + _trackFile.SceneName = null; Subject.BuildTrackFileName(new List { _track1 }, _artist, _album, _trackFile) .Should().Be(Path.GetFileNameWithoutExtension(_trackFile.Path)); @@ -520,11 +520,11 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests public void use_file_name_when_sceneName_is_not_null() { _namingConfig.RenameTracks = false; + _trackFile.SceneName = "Linkin.Park.06.FLAC-LOL"; _trackFile.Path = "Linkin Park - 06 - Test"; - _trackFile.SceneName = "SceneName"; Subject.BuildTrackFileName(new List { _track1 }, _artist, _album, _trackFile) - .Should().Be(Path.GetFileNameWithoutExtension(_trackFile.Path)); + .Should().Be("Linkin.Park.06.FLAC-LOL"); } [Test] @@ -532,6 +532,7 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests { _namingConfig.RenameTracks = false; _trackFile.Path = @"C:\Test\Unsorted\Artist - 01 - Test"; + _trackFile.SceneName = null; Subject.BuildTrackFileName(new List { _track1 }, _artist, _album, _trackFile) .Should().Be(Path.GetFileNameWithoutExtension(_trackFile.Path)); diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TruncatedTrackTitlesFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TruncatedTrackTitlesFixture.cs index 1994a233c..f297c6f33 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TruncatedTrackTitlesFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/TruncatedTrackTitlesFixture.cs @@ -109,6 +109,22 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests _trackFile.Quality.Revision.Version = 2; } + [Test] + public void should_truncate_with_extension() + { + _artist.Name = "The Fantastic Life of Mr. Sisko"; + + _tracks[0].AbsoluteTrackNumber = 18; + _tracks[0].Title = "This title has to be 197 characters in length, combined with the series title, quality and episode number it becomes 254ish and the extension puts it above the 255 limit and triggers the truncation"; + _trackFile.Quality.Quality = Quality.FLAC; + _tracks = _tracks.Take(1).ToList(); + _namingConfig.StandardTrackFormat = "{Artist Name} - {Album Title} - {track:00} - {Track Title} [{Quality Title}]"; + + var result = Subject.BuildTrackFileName(_tracks, _artist, _album, _trackFile, ".flac"); + result.Length.Should().BeLessOrEqualTo(255); + result.Should().Be("The Fantastic Life of Mr. Sisko - Hail to the King - 18 - This title has to be 197 characters in length, combined with the series title, quality and episode number it becomes 254ish and the extension puts it above the 255 limit and triggers... [FLAC].flac"); + } + [Test] public void should_truncate_with_ellipsis_between_first_and_last_episode_titles() { diff --git a/src/NzbDrone.Core/MediaFiles/RenameTrackFileService.cs b/src/NzbDrone.Core/MediaFiles/RenameTrackFileService.cs index ca5209abc..349bffb19 100644 --- a/src/NzbDrone.Core/MediaFiles/RenameTrackFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/RenameTrackFileService.cs @@ -91,9 +91,7 @@ namespace NzbDrone.Core.MediaFiles } var album = _albumService.GetAlbum(tracksInFile.First().AlbumId); - - var newName = _filenameBuilder.BuildTrackFileName(tracksInFile, artist, album, file); - var newPath = _filenameBuilder.BuildTrackFilePath(artist, newName, Path.GetExtension(trackFilePath)); + var newPath = _filenameBuilder.BuildTrackFilePath(tracksInFile, artist, album, file, Path.GetExtension(trackFilePath)); if (!trackFilePath.PathEquals(newPath, StringComparison.Ordinal)) { diff --git a/src/NzbDrone.Core/MediaFiles/TrackFileMovingService.cs b/src/NzbDrone.Core/MediaFiles/TrackFileMovingService.cs index 95519482f..00036eb6a 100644 --- a/src/NzbDrone.Core/MediaFiles/TrackFileMovingService.cs +++ b/src/NzbDrone.Core/MediaFiles/TrackFileMovingService.cs @@ -64,8 +64,7 @@ namespace NzbDrone.Core.MediaFiles { var tracks = _trackService.GetTracksByFileId(trackFile.Id); var album = _albumService.GetAlbum(trackFile.AlbumId); - var newFileName = _buildFileNames.BuildTrackFileName(tracks, artist, album, trackFile); - var filePath = _buildFileNames.BuildTrackFilePath(artist, newFileName, Path.GetExtension(trackFile.Path)); + var filePath = _buildFileNames.BuildTrackFilePath(tracks, artist, album, trackFile, Path.GetExtension(trackFile.Path)); EnsureTrackFolder(trackFile, artist, album, filePath); @@ -76,8 +75,7 @@ namespace NzbDrone.Core.MediaFiles public TrackFile MoveTrackFile(TrackFile trackFile, LocalTrack localTrack) { - var newFileName = _buildFileNames.BuildTrackFileName(localTrack.Tracks, localTrack.Artist, localTrack.Album, trackFile); - var filePath = _buildFileNames.BuildTrackFilePath(localTrack.Artist, newFileName, Path.GetExtension(localTrack.Path)); + var filePath = _buildFileNames.BuildTrackFilePath(localTrack.Tracks, localTrack.Artist, localTrack.Album, trackFile, Path.GetExtension(trackFile.Path)); EnsureTrackFolder(trackFile, localTrack, filePath); @@ -88,8 +86,7 @@ namespace NzbDrone.Core.MediaFiles public TrackFile CopyTrackFile(TrackFile trackFile, LocalTrack localTrack) { - var newFileName = _buildFileNames.BuildTrackFileName(localTrack.Tracks, localTrack.Artist, localTrack.Album, trackFile); - var filePath = _buildFileNames.BuildTrackFilePath(localTrack.Artist, newFileName, Path.GetExtension(localTrack.Path)); + var filePath = _buildFileNames.BuildTrackFilePath(localTrack.Tracks, localTrack.Artist, localTrack.Album, trackFile, Path.GetExtension(trackFile.Path)); EnsureTrackFolder(trackFile, localTrack, filePath); diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index 6ea1a15ef..7d7e7e632 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -18,8 +18,8 @@ namespace NzbDrone.Core.Organizer { public interface IBuildFileNames { - string BuildTrackFileName(List tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig namingConfig = null, List customFormats = null); - string BuildTrackFilePath(Artist artist, string fileName, string extension); + string BuildTrackFileName(List tracks, Artist artist, Album album, TrackFile trackFile, string extension = "", NamingConfig namingConfig = null, List customFormats = null); + string BuildTrackFilePath(List tracks, Artist artist, Album album, TrackFile trackFile, string extension, NamingConfig namingConfig = null, List customFormats = null); BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec); string GetArtistFolder(Artist artist, NamingConfig namingConfig = null); } @@ -83,7 +83,7 @@ namespace NzbDrone.Core.Organizer _logger = logger; } - private string BuildTrackFileName(List tracks, Artist artist, Album album, TrackFile trackFile, int maxPath, NamingConfig namingConfig = null, List customFormats = null) + private string BuildTrackFileName(List tracks, Artist artist, Album album, TrackFile trackFile, string extension, int maxPath, NamingConfig namingConfig = null, List customFormats = null) { if (namingConfig == null) { @@ -92,7 +92,7 @@ namespace NzbDrone.Core.Organizer if (!namingConfig.RenameTracks) { - return GetOriginalFileName(trackFile); + return GetOriginalTitle(trackFile) + extension; } if (namingConfig.StandardTrackFormat.IsNullOrWhiteSpace() || namingConfig.MultiDiscTrackFormat.IsNullOrWhiteSpace()) @@ -112,9 +112,9 @@ namespace NzbDrone.Core.Organizer var splitPatterns = pattern.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries); var components = new List(); - foreach (var s in splitPatterns) + for (var i = 0; i < splitPatterns.Length; i++) { - var splitPattern = s; + var splitPattern = splitPatterns[i]; var tokenHandlers = new Dictionary>(FileNameBuilderTokenEqualityComparer.Instance); splitPattern = FormatTrackNumberTokens(splitPattern, "", tracks); splitPattern = FormatMediumNumberTokens(splitPattern, "", tracks); @@ -131,6 +131,10 @@ namespace NzbDrone.Core.Organizer var component = ReplaceTokens(splitPattern, tokenHandlers, namingConfig, true).Trim(); var maxPathSegmentLength = Math.Min(LongPathSupport.MaxFileNameLength, maxPath); + if (i == splitPatterns.Length - 1) + { + maxPathSegmentLength -= extension.GetByteCount(); + } var maxTrackTitleLength = maxPathSegmentLength - GetLengthWithoutTrackTitle(component, namingConfig); @@ -147,21 +151,23 @@ namespace NzbDrone.Core.Organizer } } - return Path.Combine(components.ToArray()); + return string.Join(Path.DirectorySeparatorChar.ToString(), components) + extension; } - public string BuildTrackFileName(List tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig namingConfig = null, List customFormats = null) + public string BuildTrackFileName(List tracks, Artist artist, Album album, TrackFile trackFile, string extension = "", NamingConfig namingConfig = null, List customFormats = null) { - return BuildTrackFileName(tracks, artist, album, trackFile, LongPathSupport.MaxFilePathLength, namingConfig, customFormats); + return BuildTrackFileName(tracks, artist, album, trackFile, extension, LongPathSupport.MaxFilePathLength, namingConfig, customFormats); } - public string BuildTrackFilePath(Artist artist, string fileName, string extension) + public string BuildTrackFilePath(List tracks, Artist artist, Album album, TrackFile trackFile, string extension, NamingConfig namingConfig = null, List customFormats = null) { Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace(); - var path = artist.Path; + var artistPath = artist.Path; + var remainingPathLength = LongPathSupport.MaxFilePathLength - artistPath.GetByteCount() - 1; + var fileName = BuildTrackFileName(tracks, artist, album, trackFile, extension, remainingPathLength, namingConfig, customFormats); - return Path.Combine(path, fileName + extension); + return Path.Combine(artistPath, fileName); } public BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec) diff --git a/src/NzbDrone.Core/Organizer/FileNameSampleService.cs b/src/NzbDrone.Core/Organizer/FileNameSampleService.cs index e16d24351..b86d6e226 100644 --- a/src/NzbDrone.Core/Organizer/FileNameSampleService.cs +++ b/src/NzbDrone.Core/Organizer/FileNameSampleService.cs @@ -177,7 +177,7 @@ namespace NzbDrone.Core.Organizer { try { - return _buildFileNames.BuildTrackFileName(tracks, artist, album, trackFile, nameSpec, _customFormats); + return _buildFileNames.BuildTrackFileName(tracks, artist, album, trackFile, "", nameSpec, _customFormats); } catch (NamingFormatException) {