Properly truncate filenames
This commit is contained in:
parent
20474e0b3c
commit
c72b64d74c
@ -25,12 +25,12 @@ namespace FileLiberator
|
|||||||
|
|
||||||
if (seriesParent is not null)
|
if (seriesParent is not null)
|
||||||
{
|
{
|
||||||
var baseDir = Templates.Folder.GetFilename(seriesParent.ToDto(), "", "");
|
var baseDir = Templates.Folder.GetFilename(seriesParent.ToDto(), AudibleFileStorage.BooksDirectory, "");
|
||||||
return Templates.Folder.GetFilename(libraryBook.ToDto(), baseDir, "");
|
return Templates.Folder.GetFilename(libraryBook.ToDto(), baseDir, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Templates.Folder.GetFilename(libraryBook.ToDto(), "", "");
|
return Templates.Folder.GetFilename(libraryBook.ToDto(), AudibleFileStorage.BooksDirectory, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -106,7 +106,7 @@ public class NamingTemplate
|
|||||||
|
|
||||||
while (templateString.Length > 0)
|
while (templateString.Length > 0)
|
||||||
{
|
{
|
||||||
if (StartsWith(Classes, templateString, out string exactPropertyName, out var propertyTag, out var valueExpression))
|
if (StartsWith(templateString, out string exactPropertyName, out var propertyTag, out var valueExpression))
|
||||||
{
|
{
|
||||||
checkAndAddLiterals();
|
checkAndAddLiterals();
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ public class NamingTemplate
|
|||||||
|
|
||||||
templateString = templateString[exactPropertyName.Length..];
|
templateString = templateString[exactPropertyName.Length..];
|
||||||
}
|
}
|
||||||
else if (StartsWithClosing(Classes, templateString, out exactPropertyName, out var closingPropertyTag))
|
else if (StartsWithClosing(templateString, out exactPropertyName, out var closingPropertyTag))
|
||||||
{
|
{
|
||||||
checkAndAddLiterals();
|
checkAndAddLiterals();
|
||||||
|
|
||||||
@ -176,9 +176,9 @@ public class NamingTemplate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool StartsWith(IEnumerable<TagClass> propertyClasses, string template, out string exactName, out IPropertyTag propertyTag, out Expression valueExpression)
|
private bool StartsWith(string template, out string exactName, out IPropertyTag propertyTag, out Expression valueExpression)
|
||||||
{
|
{
|
||||||
foreach (var pc in propertyClasses)
|
foreach (var pc in Classes)
|
||||||
{
|
{
|
||||||
if (pc.StartsWith(template, out exactName, out propertyTag, out valueExpression))
|
if (pc.StartsWith(template, out exactName, out propertyTag, out valueExpression))
|
||||||
return true;
|
return true;
|
||||||
@ -189,9 +189,9 @@ public class NamingTemplate
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool StartsWithClosing(IEnumerable<TagClass> conditionalGroups, string template, out string exactName, out IClosingPropertyTag closingPropertyTag)
|
private bool StartsWithClosing(string template, out string exactName, out IClosingPropertyTag closingPropertyTag)
|
||||||
{
|
{
|
||||||
foreach (var pc in conditionalGroups)
|
foreach (var pc in Classes)
|
||||||
{
|
{
|
||||||
if (pc.StartsWithClosing(template, out exactName, out closingPropertyTag))
|
if (pc.StartsWithClosing(template, out exactName, out closingPropertyTag))
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -50,33 +50,22 @@ namespace LibationFileManager
|
|||||||
|
|
||||||
static Templates()
|
static Templates()
|
||||||
{
|
{
|
||||||
Configuration.Instance.PropertyChanged += FolderTemplate_PropertyChanged;
|
Configuration.Instance.PropertyChanged +=
|
||||||
Configuration.Instance.PropertyChanged += FileTemplate_PropertyChanged;
|
[PropertyChangeFilter(nameof(Configuration.FolderTemplate))]
|
||||||
Configuration.Instance.PropertyChanged += ChapterFileTemplate_PropertyChanged;
|
(_,e) => _folder = GetTemplate<FolderTemplate>((string)e.NewValue);
|
||||||
Configuration.Instance.PropertyChanged += ChapterTitleTemplate_PropertyChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
[PropertyChangeFilter(nameof(Configuration.FolderTemplate))]
|
Configuration.Instance.PropertyChanged
|
||||||
private static void FolderTemplate_PropertyChanged(object sender, PropertyChangedEventArgsEx e)
|
+= [PropertyChangeFilter(nameof(Configuration.FileTemplate))]
|
||||||
{
|
(_, e) => _file = GetTemplate<FileTemplate>((string)e.NewValue);
|
||||||
_folder = GetTemplate<FolderTemplate>((string)e.NewValue);
|
|
||||||
}
|
|
||||||
[PropertyChangeFilter(nameof(Configuration.FileTemplate))]
|
|
||||||
private static void FileTemplate_PropertyChanged(object sender, PropertyChangedEventArgsEx e)
|
|
||||||
{
|
|
||||||
_file = GetTemplate<FileTemplate>((string)e.NewValue);
|
|
||||||
}
|
|
||||||
[PropertyChangeFilter(nameof(Configuration.ChapterFileTemplate))]
|
|
||||||
private static void ChapterFileTemplate_PropertyChanged(object sender, PropertyChangedEventArgsEx e)
|
|
||||||
{
|
|
||||||
_chapterFile = GetTemplate<ChapterFileTemplate>((string)e.NewValue);
|
|
||||||
}
|
|
||||||
[PropertyChangeFilter(nameof(Configuration.ChapterTitleTemplate))]
|
|
||||||
private static void ChapterTitleTemplate_PropertyChanged(object sender, PropertyChangedEventArgsEx e)
|
|
||||||
{
|
|
||||||
_chapterTitle = GetTemplate<ChapterTitleTemplate>((string)e.NewValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Configuration.Instance.PropertyChanged
|
||||||
|
+= [PropertyChangeFilter(nameof(Configuration.ChapterFileTemplate))]
|
||||||
|
(_, e) => _chapterFile = GetTemplate<ChapterFileTemplate>((string)e.NewValue);
|
||||||
|
|
||||||
|
Configuration.Instance.PropertyChanged
|
||||||
|
+= [PropertyChangeFilter(nameof(Configuration.ChapterTitleTemplate))]
|
||||||
|
(_, e) => _chapterTitle = GetTemplate<ChapterTitleTemplate>((string)e.NewValue);
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Template Properties
|
#region Template Properties
|
||||||
@ -87,6 +76,7 @@ namespace LibationFileManager
|
|||||||
public string TemplateText => Template.TemplateText;
|
public string TemplateText => Template.TemplateText;
|
||||||
protected NamingTemplate Template { get; private set; }
|
protected NamingTemplate Template { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region validation
|
#region validation
|
||||||
@ -134,15 +124,24 @@ namespace LibationFileManager
|
|||||||
|
|
||||||
private LongPath GetFilename(string baseDir, string fileExtension, bool returnFirstExisting, ReplacementCharacters replacements, params object[] dtos)
|
private LongPath GetFilename(string baseDir, string fileExtension, bool returnFirstExisting, ReplacementCharacters replacements, params object[] dtos)
|
||||||
{
|
{
|
||||||
var parts = Template.Evaluate(dtos).ToList();
|
fileExtension = FileUtility.GetStandardizedExtension(fileExtension);
|
||||||
|
|
||||||
|
var parts = Template.Evaluate(dtos).ToList();
|
||||||
var pathParts = GetPathParts(GetTemplatePartsStrings(parts, replacements));
|
var pathParts = GetPathParts(GetTemplatePartsStrings(parts, replacements));
|
||||||
|
|
||||||
//Remove 1 character from the end of the longest filename part until
|
//Remove 1 character from the end of the longest filename part until
|
||||||
//the total filename is less than max filename length
|
//the total filename is less than max filename length
|
||||||
foreach (var part in pathParts)
|
for (int i = 0; i < pathParts.Count; i++)
|
||||||
{
|
{
|
||||||
while (part.Sum(LongPath.GetFilesystemStringLength) > LongPath.MaxFilenameLength)
|
var part = pathParts[i];
|
||||||
|
|
||||||
|
//If file already exists, GetValidFilename will append " (n)" to the filename.
|
||||||
|
//This could cause the filename length to exceed MaxFilenameLength, so reduce
|
||||||
|
//allowable filename length by 5 chars, allowing for up to 99 duplicates.
|
||||||
|
var maxFilenameLength = LongPath.MaxFilenameLength -
|
||||||
|
(i < pathParts.Count - 1 || string.IsNullOrEmpty(fileExtension) ? 0 : fileExtension.Length + 5);
|
||||||
|
|
||||||
|
while (part.Sum(LongPath.GetFilesystemStringLength) > maxFilenameLength)
|
||||||
{
|
{
|
||||||
int maxLength = part.Max(p => p.Length);
|
int maxLength = part.Max(p => p.Length);
|
||||||
var maxEntry = part.First(p => p.Length == maxLength);
|
var maxEntry = part.First(p => p.Length == maxLength);
|
||||||
|
|||||||
@ -297,9 +297,15 @@ namespace Templates_Other
|
|||||||
static ReplacementCharacters Replacements = ReplacementCharacters.Default;
|
static ReplacementCharacters Replacements = ReplacementCharacters.Default;
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[DataRow(@"C:\foo\bar", @"C:\foo\bar\Folder\my꞉ book 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\[ID123456].txt", PlatformID.Win32NT)]
|
[DataRow(@"C:\foo\bar", @"\Folder\<title>\[<id>]\", @"C:\foo\bar\Folder\my꞉ book 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\[ID123456].txt", PlatformID.Win32NT)]
|
||||||
[DataRow(@"/foo/bar", @"/foo/bar/Folder/my: book 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/[ID123456].txt", PlatformID.Unix)]
|
[DataRow("/foo/bar", "/Folder/<title>/[<id>]/", @" / foo/bar/Folder/my: book 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/[ID123456].txt", PlatformID.Unix)]
|
||||||
public void equiv_GetValidFilename(string dirFullPath, string expected, PlatformID platformID)
|
[DataRow(@"C:\foo\bar", @"\Folder\<title> [<id>]", @"C:\foo\bar\Folder\my꞉ book 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 [ID123456].txt", PlatformID.Win32NT)]
|
||||||
|
[DataRow("/foo/bar", "/Folder/<title> [<id>]", @"/foo/bar/Folder/my: book 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 [ID123456].txt", PlatformID.Unix)]
|
||||||
|
[DataRow(@"C:\foo\bar", @"\Folder\<title> <title> <title> <title> <title> <title> <title> <title> <title> [<id>]", @"C:\foo\bar\Folder\my꞉ book 0000000000000000 my꞉ book 0000000000000000 my꞉ book 0000000000000000 my꞉ book 0000000000000000 my꞉ book 0000000000000000 my꞉ book 0000000000000000 my꞉ book 0000000000000000 my꞉ book 00000000000000000 my꞉ book 00000000000000000 [ID123456].txt", PlatformID.Win32NT)]
|
||||||
|
[DataRow("/foo/bar", "/Folder/<title> <title> <title> <title> <title> <title> <title> <title> <title> [<id>]", @"/foo/bar/Folder/my: book 0000000000000000 my: book 0000000000000000 my: book 0000000000000000 my: book 0000000000000000 my: book 0000000000000000 my: book 0000000000000000 my: book 0000000000000000 my: book 00000000000000000 my: book 00000000000000000 [ID123456].txt", PlatformID.Unix)]
|
||||||
|
[DataRow(@"C:\foo\bar", @"\<title>\<title> [<id>]", @"C:\foo\bar\my꞉ book 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\my꞉ book 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 [ID123456].txt", PlatformID.Win32NT)]
|
||||||
|
[DataRow("/foo/bar", @"/<title>/<title> [<id>]", "/foo/bar/my: book 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/my: book 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 [ID123456].txt", PlatformID.Unix)]
|
||||||
|
public void Test_trim_to_max_path(string dirFullPath, string template, string expected, PlatformID platformID)
|
||||||
{
|
{
|
||||||
if (Environment.OSVersion.Platform != platformID)
|
if (Environment.OSVersion.Platform != platformID)
|
||||||
return;
|
return;
|
||||||
@ -308,7 +314,21 @@ namespace Templates_Other
|
|||||||
sb.Append('0', 300);
|
sb.Append('0', 300);
|
||||||
var longText = sb.ToString();
|
var longText = sb.ToString();
|
||||||
|
|
||||||
NEW_GetValidFilename_FileNamingTemplate(dirFullPath, "my: book " + longText, "txt", "ID123456").Should().Be(expected);
|
NEW_GetValidFilename_FileNamingTemplate(dirFullPath, template, "my: book " + longText, "txt").Should().Be(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[DataRow(@"\foo\bar", @"<title>\<title>")]
|
||||||
|
[DataRow(@"\foooo\barrrr", "<title>")]
|
||||||
|
public void Test_windows_relative_path_too_long(string baseDir, string template)
|
||||||
|
{
|
||||||
|
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var sb = new System.Text.StringBuilder();
|
||||||
|
sb.Append('0', 300);
|
||||||
|
var longText = sb.ToString();
|
||||||
|
Assert.ThrowsException<PathTooLongException>(() => NEW_GetValidFilename_FileNamingTemplate(baseDir, template, "my: book " + longText, "txt"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TemplateTag : ITemplateTag
|
private class TemplateTag : ITemplateTag
|
||||||
@ -318,17 +338,13 @@ namespace Templates_Other
|
|||||||
public string Description { get; }
|
public string Description { get; }
|
||||||
public string Display { get; }
|
public string Display { get; }
|
||||||
}
|
}
|
||||||
private static string NEW_GetValidFilename_FileNamingTemplate(string dirFullPath, string filename, string extension, string metadataSuffix)
|
private static string NEW_GetValidFilename_FileNamingTemplate(string dirFullPath, string template, string title, string extension)
|
||||||
{
|
{
|
||||||
char slash = Path.DirectorySeparatorChar;
|
|
||||||
|
|
||||||
var template = $"{slash}Folder{slash}<title>{slash}[<id>]{slash}";
|
|
||||||
|
|
||||||
extension = FileUtility.GetStandardizedExtension(extension);
|
extension = FileUtility.GetStandardizedExtension(extension);
|
||||||
|
|
||||||
var lbDto = GetLibraryBook();
|
var lbDto = GetLibraryBook();
|
||||||
lbDto.Title = filename;
|
lbDto.Title = title;
|
||||||
lbDto.AudibleProductId = metadataSuffix;
|
lbDto.AudibleProductId = "ID123456";
|
||||||
|
|
||||||
Templates.TryGetTemplate<Templates.FolderTemplate>(template, out var fileNamingTemplate).Should().BeTrue();
|
Templates.TryGetTemplate<Templates.FolderTemplate>(template, out var fileNamingTemplate).Should().BeTrue();
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user