Fixed: Truncating too long filenames with unicode characters
closes #4085
This commit is contained in:
parent
05820ac272
commit
158e31d54a
|
@ -141,5 +141,45 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||||
result.Length.Should().BeLessOrEqualTo(255);
|
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");
|
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();
|
return intList.Max();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int GetByteCount(this string input)
|
||||||
|
{
|
||||||
|
return Encoding.UTF8.GetByteCount(input);
|
||||||
|
}
|
||||||
|
|
||||||
public static string Truncate(this string s, int maxLength)
|
public static string Truncate(this string s, int maxLength)
|
||||||
{
|
{
|
||||||
if (Encoding.UTF8.GetByteCount(s) <= maxLength)
|
if (Encoding.UTF8.GetByteCount(s) <= maxLength)
|
||||||
|
|
|
@ -163,10 +163,10 @@ namespace NzbDrone.Core.Organizer
|
||||||
AddPreferredWords(tokenHandlers, series, episodeFile, preferredWords);
|
AddPreferredWords(tokenHandlers, series, episodeFile, preferredWords);
|
||||||
|
|
||||||
var component = ReplaceTokens(splitPattern, tokenHandlers, namingConfig, true).Trim();
|
var component = ReplaceTokens(splitPattern, tokenHandlers, namingConfig, true).Trim();
|
||||||
var maxPathSegmentLength = LongPathSupport.MaxFileNameLength;
|
var maxPathSegmentLength = Math.Min(LongPathSupport.MaxFileNameLength, maxPath);
|
||||||
if (i == splitPatterns.Length - 1)
|
if (i == splitPatterns.Length - 1)
|
||||||
{
|
{
|
||||||
maxPathSegmentLength -= extension.Length;
|
maxPathSegmentLength -= extension.GetByteCount();
|
||||||
}
|
}
|
||||||
var maxEpisodeTitleLength = maxPathSegmentLength - GetLengthWithoutEpisodeTitle(component, namingConfig);
|
var maxEpisodeTitleLength = maxPathSegmentLength - GetLengthWithoutEpisodeTitle(component, namingConfig);
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
|
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
|
||||||
|
|
||||||
var seasonPath = BuildSeasonPath(series, episodes.First().SeasonNumber);
|
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);
|
var fileName = BuildFileName(episodes, series, episodeFile, extension, remainingPathLength, namingConfig, preferredWords);
|
||||||
|
|
||||||
return Path.Combine(seasonPath, fileName);
|
return Path.Combine(seasonPath, fileName);
|
||||||
|
@ -935,33 +935,35 @@ namespace NzbDrone.Core.Organizer
|
||||||
|
|
||||||
var joined = string.Join(separator, titles);
|
var joined = string.Join(separator, titles);
|
||||||
|
|
||||||
if (joined.Length <= maxLength)
|
if (joined.GetByteCount() <= maxLength)
|
||||||
{
|
{
|
||||||
return joined;
|
return joined;
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstTitle = titles.First();
|
var firstTitle = titles.First();
|
||||||
|
var firstTitleLength = firstTitle.GetByteCount();
|
||||||
|
|
||||||
if (titles.Count >= 2)
|
if (titles.Count >= 2)
|
||||||
{
|
{
|
||||||
var lastTitle = titles.Last();
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $"{firstTitle.Substring(0, maxLength - 3).Trim(' ', '.')}{{ellipsis}}";
|
return $"{firstTitle.Truncate(maxLength - 3).TrimEnd(' ', '.')}{{ellipsis}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CleanupEpisodeTitle(string title)
|
private string CleanupEpisodeTitle(string title)
|
||||||
|
@ -1023,7 +1025,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
|
|
||||||
var result = ReplaceTokens(pattern, tokenHandlers, namingConfig);
|
var result = ReplaceTokens(pattern, tokenHandlers, namingConfig);
|
||||||
|
|
||||||
return result.Length;
|
return result.GetByteCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue