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)
pull/3864/head
Taloth Saldono 4 years ago committed by Bogdan
parent f4292be588
commit c86d5980d3

@ -7,6 +7,7 @@ using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.CustomFormats;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
@ -43,12 +44,8 @@ namespace NzbDrone.Core.Test.MediaFiles.TrackFileMovingServiceTests
.Build(); .Build();
Mocker.GetMock<IBuildFileNames>() Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.BuildTrackFileName(It.IsAny<List<Track>>(), It.IsAny<Artist>(), It.IsAny<Album>(), It.IsAny<TrackFile>(), null, null)) .Setup(s => s.BuildTrackFilePath(It.IsAny<List<Track>>(), It.IsAny<Artist>(), It.IsAny<Album>(), It.IsAny<TrackFile>(), It.IsAny<string>(), It.IsAny<NamingConfig>(), It.IsAny<List<CustomFormat>>()))
.Returns("Album\\File Name"); .Returns(@"C:\Test\Music\Artist\Album\File Name.mp3".AsOsAgnostic());
Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.BuildTrackFilePath(It.IsAny<Artist>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(@"C:\Test\Music\Artist\Album\File Name.mp3".AsOsAgnostic());
var rootFolder = @"C:\Test\Music\".AsOsAgnostic(); var rootFolder = @"C:\Test\Music\".AsOsAgnostic();
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()

@ -1,6 +1,8 @@
using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Music; using NzbDrone.Core.Music;
using NzbDrone.Core.Organizer; using NzbDrone.Core.Organizer;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -9,7 +11,6 @@ using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.OrganizerTests namespace NzbDrone.Core.Test.OrganizerTests
{ {
[TestFixture] [TestFixture]
public class BuildFilePathFixture : CoreTest<FileNameBuilder> public class BuildFilePathFixture : CoreTest<FileNameBuilder>
{ {
private NamingConfig _namingConfig; private NamingConfig _namingConfig;
@ -29,12 +30,24 @@ namespace NzbDrone.Core.Test.OrganizerTests
var filename = @"02 - Track Title"; var filename = @"02 - Track Title";
var expectedPath = @"C:\Test\Fake- The Artist\02 - Track Title.flac"; var expectedPath = @"C:\Test\Fake- The Artist\02 - Track Title.flac";
var fakeTracks = Builder<Track>.CreateListOfSize(1)
.All()
.With(s => s.Title = "Episode Title")
.With(s => s.AbsoluteTrackNumber = 5)
.Build().ToList();
var fakeArtist = Builder<Artist>.CreateNew() var fakeArtist = Builder<Artist>.CreateNew()
.With(s => s.Name = "Fake: The Artist") .With(s => s.Name = "Fake: The Artist")
.With(s => s.Path = @"C:\Test\Fake- The Artist".AsOsAgnostic()) .With(s => s.Path = @"C:\Test\Fake- The Artist".AsOsAgnostic())
.Build(); .Build();
var fakeAlbum = Builder<Album>.CreateNew()
.With(e => e.ArtistId = fakeArtist.Id)
.Build();
var fakeTrackFile = Builder<TrackFile>.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());
} }
} }
} }

@ -15,7 +15,6 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
{ {
[TestFixture] [TestFixture]
public class FileNameBuilderFixture : CoreTest<FileNameBuilder> public class FileNameBuilderFixture : CoreTest<FileNameBuilder>
{ {
private Artist _artist; private Artist _artist;
@ -511,6 +510,7 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
{ {
_namingConfig.RenameTracks = false; _namingConfig.RenameTracks = false;
_trackFile.Path = "Linkin Park - 06 - Test"; _trackFile.Path = "Linkin Park - 06 - Test";
_trackFile.SceneName = null;
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile) Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
.Should().Be(Path.GetFileNameWithoutExtension(_trackFile.Path)); .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() public void use_file_name_when_sceneName_is_not_null()
{ {
_namingConfig.RenameTracks = false; _namingConfig.RenameTracks = false;
_trackFile.SceneName = "Linkin.Park.06.FLAC-LOL";
_trackFile.Path = "Linkin Park - 06 - Test"; _trackFile.Path = "Linkin Park - 06 - Test";
_trackFile.SceneName = "SceneName";
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile) Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
.Should().Be(Path.GetFileNameWithoutExtension(_trackFile.Path)); .Should().Be("Linkin.Park.06.FLAC-LOL");
} }
[Test] [Test]
@ -532,6 +532,7 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
{ {
_namingConfig.RenameTracks = false; _namingConfig.RenameTracks = false;
_trackFile.Path = @"C:\Test\Unsorted\Artist - 01 - Test"; _trackFile.Path = @"C:\Test\Unsorted\Artist - 01 - Test";
_trackFile.SceneName = null;
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile) Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
.Should().Be(Path.GetFileNameWithoutExtension(_trackFile.Path)); .Should().Be(Path.GetFileNameWithoutExtension(_trackFile.Path));

@ -109,6 +109,22 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
_trackFile.Quality.Revision.Version = 2; _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] [Test]
public void should_truncate_with_ellipsis_between_first_and_last_episode_titles() public void should_truncate_with_ellipsis_between_first_and_last_episode_titles()
{ {

@ -91,9 +91,7 @@ namespace NzbDrone.Core.MediaFiles
} }
var album = _albumService.GetAlbum(tracksInFile.First().AlbumId); var album = _albumService.GetAlbum(tracksInFile.First().AlbumId);
var newPath = _filenameBuilder.BuildTrackFilePath(tracksInFile, artist, album, file, Path.GetExtension(trackFilePath));
var newName = _filenameBuilder.BuildTrackFileName(tracksInFile, artist, album, file);
var newPath = _filenameBuilder.BuildTrackFilePath(artist, newName, Path.GetExtension(trackFilePath));
if (!trackFilePath.PathEquals(newPath, StringComparison.Ordinal)) if (!trackFilePath.PathEquals(newPath, StringComparison.Ordinal))
{ {

@ -64,8 +64,7 @@ namespace NzbDrone.Core.MediaFiles
{ {
var tracks = _trackService.GetTracksByFileId(trackFile.Id); var tracks = _trackService.GetTracksByFileId(trackFile.Id);
var album = _albumService.GetAlbum(trackFile.AlbumId); var album = _albumService.GetAlbum(trackFile.AlbumId);
var newFileName = _buildFileNames.BuildTrackFileName(tracks, artist, album, trackFile); var filePath = _buildFileNames.BuildTrackFilePath(tracks, artist, album, trackFile, Path.GetExtension(trackFile.Path));
var filePath = _buildFileNames.BuildTrackFilePath(artist, newFileName, Path.GetExtension(trackFile.Path));
EnsureTrackFolder(trackFile, artist, album, filePath); EnsureTrackFolder(trackFile, artist, album, filePath);
@ -76,8 +75,7 @@ namespace NzbDrone.Core.MediaFiles
public TrackFile MoveTrackFile(TrackFile trackFile, LocalTrack localTrack) public TrackFile MoveTrackFile(TrackFile trackFile, LocalTrack localTrack)
{ {
var newFileName = _buildFileNames.BuildTrackFileName(localTrack.Tracks, localTrack.Artist, localTrack.Album, trackFile); var filePath = _buildFileNames.BuildTrackFilePath(localTrack.Tracks, localTrack.Artist, localTrack.Album, trackFile, Path.GetExtension(trackFile.Path));
var filePath = _buildFileNames.BuildTrackFilePath(localTrack.Artist, newFileName, Path.GetExtension(localTrack.Path));
EnsureTrackFolder(trackFile, localTrack, filePath); EnsureTrackFolder(trackFile, localTrack, filePath);
@ -88,8 +86,7 @@ namespace NzbDrone.Core.MediaFiles
public TrackFile CopyTrackFile(TrackFile trackFile, LocalTrack localTrack) public TrackFile CopyTrackFile(TrackFile trackFile, LocalTrack localTrack)
{ {
var newFileName = _buildFileNames.BuildTrackFileName(localTrack.Tracks, localTrack.Artist, localTrack.Album, trackFile); var filePath = _buildFileNames.BuildTrackFilePath(localTrack.Tracks, localTrack.Artist, localTrack.Album, trackFile, Path.GetExtension(trackFile.Path));
var filePath = _buildFileNames.BuildTrackFilePath(localTrack.Artist, newFileName, Path.GetExtension(localTrack.Path));
EnsureTrackFolder(trackFile, localTrack, filePath); EnsureTrackFolder(trackFile, localTrack, filePath);

@ -18,8 +18,8 @@ namespace NzbDrone.Core.Organizer
{ {
public interface IBuildFileNames public interface IBuildFileNames
{ {
string BuildTrackFileName(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig namingConfig = null, List<CustomFormat> customFormats = null); string BuildTrackFileName(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, string extension = "", NamingConfig namingConfig = null, List<CustomFormat> customFormats = null);
string BuildTrackFilePath(Artist artist, string fileName, string extension); string BuildTrackFilePath(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, string extension, NamingConfig namingConfig = null, List<CustomFormat> customFormats = null);
BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec); BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec);
string GetArtistFolder(Artist artist, NamingConfig namingConfig = null); string GetArtistFolder(Artist artist, NamingConfig namingConfig = null);
} }
@ -83,7 +83,7 @@ namespace NzbDrone.Core.Organizer
_logger = logger; _logger = logger;
} }
private string BuildTrackFileName(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, int maxPath, NamingConfig namingConfig = null, List<CustomFormat> customFormats = null) private string BuildTrackFileName(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, string extension, int maxPath, NamingConfig namingConfig = null, List<CustomFormat> customFormats = null)
{ {
if (namingConfig == null) if (namingConfig == null)
{ {
@ -92,7 +92,7 @@ namespace NzbDrone.Core.Organizer
if (!namingConfig.RenameTracks) if (!namingConfig.RenameTracks)
{ {
return GetOriginalFileName(trackFile); return GetOriginalTitle(trackFile) + extension;
} }
if (namingConfig.StandardTrackFormat.IsNullOrWhiteSpace() || namingConfig.MultiDiscTrackFormat.IsNullOrWhiteSpace()) if (namingConfig.StandardTrackFormat.IsNullOrWhiteSpace() || namingConfig.MultiDiscTrackFormat.IsNullOrWhiteSpace())
@ -112,9 +112,9 @@ namespace NzbDrone.Core.Organizer
var splitPatterns = pattern.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries); var splitPatterns = pattern.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
var components = new List<string>(); var components = new List<string>();
foreach (var s in splitPatterns) for (var i = 0; i < splitPatterns.Length; i++)
{ {
var splitPattern = s; var splitPattern = splitPatterns[i];
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance); var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
splitPattern = FormatTrackNumberTokens(splitPattern, "", tracks); splitPattern = FormatTrackNumberTokens(splitPattern, "", tracks);
splitPattern = FormatMediumNumberTokens(splitPattern, "", tracks); splitPattern = FormatMediumNumberTokens(splitPattern, "", tracks);
@ -131,6 +131,10 @@ namespace NzbDrone.Core.Organizer
var component = ReplaceTokens(splitPattern, tokenHandlers, namingConfig, true).Trim(); var component = ReplaceTokens(splitPattern, tokenHandlers, namingConfig, true).Trim();
var maxPathSegmentLength = Math.Min(LongPathSupport.MaxFileNameLength, maxPath); var maxPathSegmentLength = Math.Min(LongPathSupport.MaxFileNameLength, maxPath);
if (i == splitPatterns.Length - 1)
{
maxPathSegmentLength -= extension.GetByteCount();
}
var maxTrackTitleLength = maxPathSegmentLength - GetLengthWithoutTrackTitle(component, namingConfig); 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<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig namingConfig = null, List<CustomFormat> customFormats = null) public string BuildTrackFileName(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, string extension = "", NamingConfig namingConfig = null, List<CustomFormat> 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<Track> tracks, Artist artist, Album album, TrackFile trackFile, string extension, NamingConfig namingConfig = null, List<CustomFormat> customFormats = null)
{ {
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace(); 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) public BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec)

@ -177,7 +177,7 @@ namespace NzbDrone.Core.Organizer
{ {
try try
{ {
return _buildFileNames.BuildTrackFileName(tracks, artist, album, trackFile, nameSpec, _customFormats); return _buildFileNames.BuildTrackFileName(tracks, artist, album, trackFile, "", nameSpec, _customFormats);
} }
catch (NamingFormatException) catch (NamingFormatException)
{ {

Loading…
Cancel
Save