Fixed: Truncating too long filenames with unicode characters

closes #4085
pull/3999/head
Taloth Saldono 4 years ago
parent 05820ac272
commit 158e31d54a

@ -141,5 +141,45 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
result.Length.Should().BeLessOrEqualTo(255);
result.Should().Be("Lorem ipsum dolor sit amet, consectetur adipiscing elit Maecenas et magna sem Morbi vitae volutpat quam, id porta arcu Orci varius natoque penatibus et magnis dis parturient montes nascetur ridiculus musu Cras vestibulum - S01E01 - Episode Ti... HDTV-720p");
}
[Test]
public void should_truncate_titles_measuring_series_title_bytes()
{
_series.Title = "Lor\u00E9m ipsum dolor sit amet, consectetur adipiscing elit Maecenas et magna sem Morbi vitae volutpat quam, id porta arcu Orci varius natoque penatibus et magnis dis parturient montes nascetur ridiculus musu Cras vestibulum";
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}";
var result = Subject.BuildFileName(new List<Episode> { _episodes.First() }, _series, _episodeFile);
result.GetByteCount().Should().BeLessOrEqualTo(255);
result.Should().Be("Lor\u00E9m ipsum dolor sit amet, consectetur adipiscing elit Maecenas et magna sem Morbi vitae volutpat quam, id porta arcu Orci varius natoque penatibus et magnis dis parturient montes nascetur ridiculus musu Cras vestibulum - S01E01 - Episode T... HDTV-720p");
}
[Test]
public void should_truncate_titles_measuring_episode_title_bytes()
{
_series.Title = "Lorem ipsum dolor sit amet, consectetur adipiscing elit Maecenas et magna sem Morbi vitae volutpat quam, id porta arcu Orci varius natoque penatibus et magnis dis parturient montes nascetur ridiculus musu Cras vestibulum";
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}";
_episodes.First().Title = "Episod\u00E9 Title";
var result = Subject.BuildFileName(new List<Episode> { _episodes.First() }, _series, _episodeFile);
result.GetByteCount().Should().BeLessOrEqualTo(255);
result.Should().Be("Lorem ipsum dolor sit amet, consectetur adipiscing elit Maecenas et magna sem Morbi vitae volutpat quam, id porta arcu Orci varius natoque penatibus et magnis dis parturient montes nascetur ridiculus musu Cras vestibulum - S01E01 - Episod\u00E9 T... HDTV-720p");
}
[Test]
public void should_truncate_titles_measuring_episode_title_bytes_middle()
{
_series.Title = "Lorem ipsum dolor sit amet, consectetur adipiscing elit Maecenas et magna sem Morbi vitae volutpat quam, id porta arcu Orci varius natoque penatibus et magnis dis parturient montes nascetur ridiculus musu Cras vestibulum";
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}";
_episodes.First().Title = "Episode T\u00E9tle";
var result = Subject.BuildFileName(new List<Episode> { _episodes.First() }, _series, _episodeFile);
result.GetByteCount().Should().BeLessOrEqualTo(255);
result.Should().Be("Lorem ipsum dolor sit amet, consectetur adipiscing elit Maecenas et magna sem Morbi vitae volutpat quam, id porta arcu Orci varius natoque penatibus et magnis dis parturient montes nascetur ridiculus musu Cras vestibulum - S01E01 - Episode T... HDTV-720p");
}
}
}

@ -76,6 +76,11 @@ namespace NzbDrone.Core
return intList.Max();
}
public static int GetByteCount(this string input)
{
return Encoding.UTF8.GetByteCount(input);
}
public static string Truncate(this string s, int maxLength)
{
if (Encoding.UTF8.GetByteCount(s) <= maxLength)

@ -163,10 +163,10 @@ namespace NzbDrone.Core.Organizer
AddPreferredWords(tokenHandlers, series, episodeFile, preferredWords);
var component = ReplaceTokens(splitPattern, tokenHandlers, namingConfig, true).Trim();
var maxPathSegmentLength = LongPathSupport.MaxFileNameLength;
var maxPathSegmentLength = Math.Min(LongPathSupport.MaxFileNameLength, maxPath);
if (i == splitPatterns.Length - 1)
{
maxPathSegmentLength -= extension.Length;
maxPathSegmentLength -= extension.GetByteCount();
}
var maxEpisodeTitleLength = maxPathSegmentLength - GetLengthWithoutEpisodeTitle(component, namingConfig);
@ -193,7 +193,7 @@ namespace NzbDrone.Core.Organizer
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
var seasonPath = BuildSeasonPath(series, episodes.First().SeasonNumber);
var remainingPathLength = LongPathSupport.MaxFilePathLength - seasonPath.Length - 1;
var remainingPathLength = LongPathSupport.MaxFilePathLength - seasonPath.GetByteCount() - 1;
var fileName = BuildFileName(episodes, series, episodeFile, extension, remainingPathLength, namingConfig, preferredWords);
return Path.Combine(seasonPath, fileName);
@ -935,33 +935,35 @@ namespace NzbDrone.Core.Organizer
var joined = string.Join(separator, titles);
if (joined.Length <= maxLength)
if (joined.GetByteCount() <= maxLength)
{
return joined;
}
var firstTitle = titles.First();
var firstTitleLength = firstTitle.GetByteCount();
if (titles.Count >= 2)
{
var lastTitle = titles.Last();
if (firstTitle.Length + lastTitle.Length + 3 <= maxLength)
var lastTitleLength = lastTitle.GetByteCount();
if (firstTitleLength + lastTitleLength + 3 <= maxLength)
{
return $"{firstTitle.Trim(' ', '.')}{{ellipsis}}{lastTitle}";
return $"{firstTitle.TrimEnd(' ', '.')}{{ellipsis}}{lastTitle}";
}
}
if (titles.Count > 1 && firstTitle.Length + 3 <= maxLength)
if (titles.Count > 1 && firstTitleLength + 3 <= maxLength)
{
return $"{firstTitle.Trim(' ', '.')}{{ellipsis}}";
return $"{firstTitle.TrimEnd(' ', '.')}{{ellipsis}}";
}
if (titles.Count == 1 && firstTitle.Length <= maxLength)
if (titles.Count == 1 && firstTitleLength <= maxLength)
{
return firstTitle;
}
return $"{firstTitle.Substring(0, maxLength - 3).Trim(' ', '.')}{{ellipsis}}";
return $"{firstTitle.Truncate(maxLength - 3).TrimEnd(' ', '.')}{{ellipsis}}";
}
private string CleanupEpisodeTitle(string title)
@ -1023,7 +1025,7 @@ namespace NzbDrone.Core.Organizer
var result = ReplaceTokens(pattern, tokenHandlers, namingConfig);
return result.Length;
return result.GetByteCount();
}
}

Loading…
Cancel
Save