Add <series#> tag zero padding (#466)

This commit is contained in:
Mbucari 2023-02-03 10:08:26 -07:00
parent c72b64d74c
commit 5c7db6cd23
9 changed files with 76 additions and 70 deletions

View File

@ -35,7 +35,11 @@ namespace FileLiberator
public bool MoveMoovToBeginning => config.MoveMoovToBeginning; public bool MoveMoovToBeginning => config.MoveMoovToBeginning;
public string GetMultipartFileName(MultiConvertFileProperties props) public string GetMultipartFileName(MultiConvertFileProperties props)
=> Templates.ChapterFile.GetFilename(LibraryBookDto, props); {
var baseDir = Path.GetDirectoryName(props.OutputFileName);
var extension = Path.GetExtension(props.OutputFileName);
return Templates.ChapterFile.GetFilename(LibraryBookDto, props, baseDir, extension);
}
public string GetMultipartTitle(MultiConvertFileProperties props) public string GetMultipartTitle(MultiConvertFileProperties props)
=> Templates.ChapterTitle.GetName(LibraryBookDto, props); => Templates.ChapterTitle.GetName(LibraryBookDto, props);

View File

@ -40,7 +40,7 @@ namespace FileLiberator
Narrators = libraryBook.Book.Narrators.Select(c => c.Name).ToList(), Narrators = libraryBook.Book.Narrators.Select(c => c.Name).ToList(),
SeriesName = libraryBook.Book.SeriesLink.FirstOrDefault()?.Series.Name, SeriesName = libraryBook.Book.SeriesLink.FirstOrDefault()?.Series.Name,
SeriesNumber = libraryBook.Book.SeriesLink.FirstOrDefault()?.Order, SeriesNumber = (int?)libraryBook.Book.SeriesLink.FirstOrDefault()?.Index,
IsPodcast = libraryBook.Book.IsEpisodeChild(), IsPodcast = libraryBook.Book.IsEpisodeChild(),
BitRate = libraryBook.Book.AudioFormat.Bitrate, BitRate = libraryBook.Book.AudioFormat.Bitrate,

View File

@ -47,7 +47,7 @@ public class NamingTemplate
/// <summary>Parse a template string to a <see cref="NamingTemplate"/></summary> /// <summary>Parse a template string to a <see cref="NamingTemplate"/></summary>
/// <param name="template">The template string to parse</param> /// <param name="template">The template string to parse</param>
/// <param name="tagClasses">A collection of <see cref="ITagClass"/> with /// <param name="tagClasses">A collection of <see cref="TagClass"/> with
/// properties registered to match to the <paramref name="template"/></param> /// properties registered to match to the <paramref name="template"/></param>
public static NamingTemplate Parse(string template, IEnumerable<TagClass> tagClasses) public static NamingTemplate Parse(string template, IEnumerable<TagClass> tagClasses)
{ {
@ -111,10 +111,10 @@ public class NamingTemplate
checkAndAddLiterals(); checkAndAddLiterals();
if (propertyTag is IClosingPropertyTag) if (propertyTag is IClosingPropertyTag)
currentNode = AddNewNode(currentNode, BinaryNode.CreateConditional(propertyTag.TemplateTag, valueExpression)); currentNode = currentNode.AddNewNode(BinaryNode.CreateConditional(propertyTag.TemplateTag, valueExpression));
else else
{ {
currentNode = AddNewNode(currentNode, BinaryNode.CreateValue(propertyTag.TemplateTag, valueExpression)); currentNode = currentNode.AddNewNode(BinaryNode.CreateValue(propertyTag.TemplateTag, valueExpression));
_tagsInUse.Add(propertyTag.TemplateTag); _tagsInUse.Add(propertyTag.TemplateTag);
} }
@ -170,7 +170,7 @@ public class NamingTemplate
{ {
if (literalChars.Count != 0) if (literalChars.Count != 0)
{ {
currentNode = AddNewNode(currentNode, BinaryNode.CreateValue(new string(literalChars.ToArray()))); currentNode = currentNode.AddNewNode(BinaryNode.CreateValue(new string(literalChars.ToArray())));
literalChars.Clear(); literalChars.Clear();
} }
} }
@ -201,34 +201,12 @@ public class NamingTemplate
return false; return false;
} }
private static BinaryNode AddNewNode(BinaryNode currentNode, BinaryNode newNode)
{
if (currentNode.LeftChild is null)
{
newNode.Parent = currentNode;
currentNode.LeftChild = newNode;
}
else if (currentNode.RightChild is null)
{
newNode.Parent = currentNode;
currentNode.RightChild = newNode;
}
else
{
currentNode.RightChild = BinaryNode.CreateConcatenation(currentNode.RightChild, newNode);
currentNode.RightChild.Parent = currentNode;
currentNode = currentNode.RightChild;
}
return newNode.IsConditional ? newNode : currentNode;
}
private class BinaryNode private class BinaryNode
{ {
public string Name { get; } public string Name { get; }
public BinaryNode Parent { get; set; } public BinaryNode Parent { get; private set; }
public BinaryNode RightChild { get; set; } public BinaryNode RightChild { get; private set; }
public BinaryNode LeftChild { get; set; } public BinaryNode LeftChild { get; private set; }
public Expression Expression { get; private init; } public Expression Expression { get; private init; }
public bool IsConditional { get; private init; } = false; public bool IsConditional { get; private init; } = false;
public bool IsValue { get; private init; } = false; public bool IsValue { get; private init; } = false;
@ -253,7 +231,7 @@ public class NamingTemplate
Expression = property Expression = property
}; };
public static BinaryNode CreateConcatenation(BinaryNode left, BinaryNode right) private static BinaryNode CreateConcatenation(BinaryNode left, BinaryNode right)
{ {
var newNode = new BinaryNode("Concatenation") var newNode = new BinaryNode("Concatenation")
{ {
@ -267,5 +245,29 @@ public class NamingTemplate
private BinaryNode(string name) => Name = name; private BinaryNode(string name) => Name = name;
public override string ToString() => Name; public override string ToString() => Name;
public BinaryNode AddNewNode(BinaryNode newNode)
{
BinaryNode currentNode = this;
if (LeftChild is null)
{
newNode.Parent = currentNode;
LeftChild = newNode;
}
else if (RightChild is null)
{
newNode.Parent = currentNode;
RightChild = newNode;
}
else
{
RightChild = CreateConcatenation(RightChild, newNode);
RightChild.Parent = currentNode;
currentNode = RightChild;
}
return newNode.IsConditional ? newNode : currentNode;
}
} }
} }

View File

@ -19,7 +19,7 @@ public class PropertyTagClass<TClass> : TagClass
/// <param name="formatter">Optional formatting function that accepts the <typeparamref name="U"/> property and a formatting string and returnes the value formatted to string</param> /// <param name="formatter">Optional formatting function that accepts the <typeparamref name="U"/> property and a formatting string and returnes the value formatted to string</param>
public void RegisterProperty<U>(ITemplateTag templateTag, Func<TClass, U?> propertyGetter, PropertyFormatter<U> formatter = null) public void RegisterProperty<U>(ITemplateTag templateTag, Func<TClass, U?> propertyGetter, PropertyFormatter<U> formatter = null)
where U : struct where U : struct
=> RegisterProperty(templateTag, propertyGetter, formatter?.Method); => RegisterPropertyInternal(templateTag, propertyGetter, formatter);
/// <summary> /// <summary>
/// Register a non-nullable value type property /// Register a non-nullable value type property
@ -29,7 +29,7 @@ public class PropertyTagClass<TClass> : TagClass
/// <param name="formatter">Optional formatting function that accepts the <typeparamref name="U"/> property and a formatting string and returnes the value formatted to string</param> /// <param name="formatter">Optional formatting function that accepts the <typeparamref name="U"/> property and a formatting string and returnes the value formatted to string</param>
public void RegisterProperty<U>(ITemplateTag templateTag, Func<TClass, U> propertyGetter, PropertyFormatter<U> formatter = null) public void RegisterProperty<U>(ITemplateTag templateTag, Func<TClass, U> propertyGetter, PropertyFormatter<U> formatter = null)
where U : struct where U : struct
=> RegisterProperty(templateTag, propertyGetter, formatter?.Method); => RegisterPropertyInternal(templateTag, propertyGetter, formatter);
/// <summary> /// <summary>
/// Register a string type property. /// Register a string type property.
@ -37,13 +37,16 @@ public class PropertyTagClass<TClass> : TagClass
/// <param name="propertyGetter">A Func to get the string property from <see cref="TClass"/></param> /// <param name="propertyGetter">A Func to get the string property from <see cref="TClass"/></param>
/// <param name="formatter">Optional formatting function that accepts the string property and a formatting string and returnes the value formatted to string</param> /// <param name="formatter">Optional formatting function that accepts the string property and a formatting string and returnes the value formatted to string</param>
public void RegisterProperty(ITemplateTag templateTag, Func<TClass, string> propertyGetter, PropertyFormatter<string> formatter = null) public void RegisterProperty(ITemplateTag templateTag, Func<TClass, string> propertyGetter, PropertyFormatter<string> formatter = null)
=> RegisterProperty(templateTag, propertyGetter, formatter?.Method); => RegisterPropertyInternal(templateTag, propertyGetter, formatter);
private void RegisterProperty(ITemplateTag templateTag, Delegate propertyGetter, MethodInfo formatter) private void RegisterPropertyInternal(ITemplateTag templateTag, Delegate propertyGetter, Delegate formatter)
{ {
if (formatter?.Target is not null)
throw new ArgumentException($"{nameof(formatter)} must be a static method");
var expr = Expression.Call(Expression.Constant(propertyGetter.Target), propertyGetter.Method, Parameter); var expr = Expression.Call(Expression.Constant(propertyGetter.Target), propertyGetter.Method, Parameter);
AddPropertyTag(new PropertyTag(templateTag, Options, expr, formatter)); AddPropertyTag(new PropertyTag(templateTag, Options, expr, formatter?.Method));
} }
private class PropertyTag : TagBase private class PropertyTag : TagBase

View File

@ -20,7 +20,8 @@ namespace LibationFileManager
public string FirstNarrator => Narrators.FirstOrDefault(); public string FirstNarrator => Narrators.FirstOrDefault();
public string SeriesName { get; set; } public string SeriesName { get; set; }
public string SeriesNumber { get; set; } public int? SeriesNumber { get; set; }
public bool IsSeries => !string.IsNullOrEmpty(SeriesName);
public bool IsPodcast { get; set; } public bool IsPodcast { get; set; }
public int BitRate { get; set; } public int BitRate { get; set; }

View File

@ -58,7 +58,7 @@ namespace LibationFileManager
Authors = new List<string> { "Arthur Conan Doyle", "Stephen Fry - introductions" }, Authors = new List<string> { "Arthur Conan Doyle", "Stephen Fry - introductions" },
Narrators = new List<string> { "Stephen Fry" }, Narrators = new List<string> { "Stephen Fry" },
SeriesName = "Sherlock Holmes", SeriesName = "Sherlock Holmes",
SeriesNumber = "1", SeriesNumber = 1,
BitRate = 128, BitRate = 128,
SampleRate = 44100, SampleRate = 44100,
Channels = 2, Channels = 2,

View File

@ -45,7 +45,8 @@ namespace LibationFileManager
public static TemplateTags FileDate { get; } = new TemplateTags("file date", "File date/time. e.g. yyyy-MM-dd HH-mm", $"<file date [{DEFAULT_DATE_FORMAT}]>", "<file date [...]>"); public static TemplateTags FileDate { get; } = new TemplateTags("file date", "File date/time. e.g. yyyy-MM-dd HH-mm", $"<file date [{DEFAULT_DATE_FORMAT}]>", "<file date [...]>");
public static TemplateTags DatePublished { get; } = new TemplateTags("pub date", "Publication date. e.g. yyyy-MM-dd", $"<pub date [{DEFAULT_DATE_FORMAT}]>", "<pub date [...]>"); public static TemplateTags DatePublished { get; } = new TemplateTags("pub date", "Publication date. e.g. yyyy-MM-dd", $"<pub date [{DEFAULT_DATE_FORMAT}]>", "<pub date [...]>");
public static TemplateTags DateAdded { get; } = new TemplateTags("date added", "Date added to your Audible account. e.g. yyyy-MM-dd", $"<date added [{DEFAULT_DATE_FORMAT}]>", "<date added [...]>"); public static TemplateTags DateAdded { get; } = new TemplateTags("date added", "Date added to your Audible account. e.g. yyyy-MM-dd", $"<date added [{DEFAULT_DATE_FORMAT}]>", "<date added [...]>");
public static TemplateTags IfSeries { get; } = new TemplateTags("if series", "Only include if part of a series", "<if series-><-if series>", "<if series->...<-if series>"); public static TemplateTags IfSeries { get; } = new TemplateTags("if series", "Only include if part of a book series or podcast", "<if series-><-if series>", "<if series->...<-if series>");
public static TemplateTags IfPodcast { get; } = new TemplateTags("if podcast", "Only include if part of a podcast", "<if podcast-><-if podcast>", "<if podcast->...<-if podcast>"); public static TemplateTags IfPodcast { get; } = new TemplateTags("if podcast", "Only include if part of a podcast", "<if podcast-><-if podcast>", "<if podcast->...<-if podcast>");
public static TemplateTags IfBookseries { get; } = new TemplateTags("if bookseries", "Only include if part of a book series", "<if bookseries-><-if bookseries>", "<if bookseries->...<-if bookseries>");
} }
} }

View File

@ -6,6 +6,7 @@ using AaxDecrypter;
using Dinah.Core; using Dinah.Core;
using FileManager; using FileManager;
using FileManager.NamingTemplate; using FileManager.NamingTemplate;
using Serilog.Formatting;
namespace LibationFileManager namespace LibationFileManager
{ {
@ -105,24 +106,24 @@ namespace LibationFileManager
ArgumentValidator.EnsureNotNull(fileExtension, nameof(fileExtension)); ArgumentValidator.EnsureNotNull(fileExtension, nameof(fileExtension));
replacements ??= Configuration.Instance.ReplacementCharacters; replacements ??= Configuration.Instance.ReplacementCharacters;
return GetFilename(baseDir, fileExtension, returnFirstExisting, replacements, libraryBookDto); return GetFilename(baseDir, fileExtension,replacements, returnFirstExisting, libraryBookDto);
} }
public LongPath GetFilename(LibraryBookDto libraryBookDto, MultiConvertFileProperties multiChapProps, string baseDir = "", string fileExtension = null, ReplacementCharacters replacements = null) public LongPath GetFilename(LibraryBookDto libraryBookDto, MultiConvertFileProperties multiChapProps, string baseDir, string fileExtension, ReplacementCharacters replacements = null, bool returnFirstExisting = false)
{ {
ArgumentValidator.EnsureNotNull(libraryBookDto, nameof(libraryBookDto)); ArgumentValidator.EnsureNotNull(libraryBookDto, nameof(libraryBookDto));
ArgumentValidator.EnsureNotNull(multiChapProps, nameof(multiChapProps)); ArgumentValidator.EnsureNotNull(multiChapProps, nameof(multiChapProps));
ArgumentValidator.EnsureNotNull(baseDir, nameof(baseDir)); ArgumentValidator.EnsureNotNull(baseDir, nameof(baseDir));
ArgumentValidator.EnsureNotNull(fileExtension, nameof(fileExtension));
replacements ??= Configuration.Instance.ReplacementCharacters; replacements ??= Configuration.Instance.ReplacementCharacters;
fileExtension ??= Path.GetExtension(multiChapProps.OutputFileName); return GetFilename(baseDir, fileExtension, replacements, returnFirstExisting, libraryBookDto, multiChapProps);
return GetFilename(baseDir, fileExtension, false, replacements, libraryBookDto, multiChapProps);
} }
protected virtual IEnumerable<string> GetTemplatePartsStrings(List<TemplatePart> parts, ReplacementCharacters replacements) protected virtual IEnumerable<string> GetTemplatePartsStrings(List<TemplatePart> parts, ReplacementCharacters replacements)
=> parts.Select(p => replacements.ReplaceFilenameChars(p.Value)); => parts.Select(p => replacements.ReplaceFilenameChars(p.Value));
private LongPath GetFilename(string baseDir, string fileExtension, bool returnFirstExisting, ReplacementCharacters replacements, params object[] dtos) private LongPath GetFilename(string baseDir, string fileExtension, ReplacementCharacters replacements, bool returnFirstExisting, params object[] dtos)
{ {
fileExtension = FileUtility.GetStandardizedExtension(fileExtension); fileExtension = FileUtility.GetStandardizedExtension(fileExtension);
@ -151,14 +152,15 @@ namespace LibationFileManager
part.Insert(maxIndex, maxEntry.Remove(maxLength - 1, 1)); part.Insert(maxIndex, maxEntry.Remove(maxLength - 1, 1));
} }
} }
//Any
var fullPath = Path.Combine(pathParts.Select(p => string.Join("", p)).Prepend(baseDir).ToArray()); var fullPath = Path.Combine(pathParts.Select(fileParts => string.Join("", fileParts)).Prepend(baseDir).ToArray());
return FileUtility.GetValidFilename(fullPath, replacements, fileExtension, returnFirstExisting); return FileUtility.GetValidFilename(fullPath, replacements, fileExtension, returnFirstExisting);
} }
/// <summary> /// <summary>
/// Organize template parts into directories. /// Organize template parts into directories. Any Extra slashes will be
/// returned as empty directories and are taken care of by Path.Combine()
/// </summary> /// </summary>
/// <returns>A List of template directories. Each directory is a list of template part strings</returns> /// <returns>A List of template directories. Each directory is a list of template part strings</returns>
private List<List<string>> GetPathParts(IEnumerable<string> templateParts) private List<List<string>> GetPathParts(IEnumerable<string> templateParts)
@ -196,8 +198,9 @@ namespace LibationFileManager
{ {
ConditionalTagClass<LibraryBookDto> lbConditions = new(); ConditionalTagClass<LibraryBookDto> lbConditions = new();
lbConditions.RegisterCondition(TemplateTags.IfSeries, lb => !string.IsNullOrWhiteSpace(lb.SeriesName)); lbConditions.RegisterCondition(TemplateTags.IfSeries, lb => lb.IsSeries);
lbConditions.RegisterCondition(TemplateTags.IfPodcast, lb => lb.IsPodcast); lbConditions.RegisterCondition(TemplateTags.IfPodcast, lb => lb.IsPodcast);
lbConditions.RegisterCondition(TemplateTags.IfBookseries, lb => lb.IsSeries && !lb.IsPodcast);
return lbConditions; return lbConditions;
} }
@ -206,16 +209,16 @@ namespace LibationFileManager
{ {
PropertyTagClass<LibraryBookDto> lbProperties = new(); PropertyTagClass<LibraryBookDto> lbProperties = new();
lbProperties.RegisterProperty(TemplateTags.Id, lb => lb.AudibleProductId); lbProperties.RegisterProperty(TemplateTags.Id, lb => lb.AudibleProductId);
lbProperties.RegisterProperty(TemplateTags.Title, lb => lb.Title ?? "", StringFormatter); lbProperties.RegisterProperty(TemplateTags.Title, lb => lb.Title, StringFormatter);
lbProperties.RegisterProperty(TemplateTags.TitleShort, lb => lb.Title.IndexOf(':') < 1 ? lb.Title : lb.Title.Substring(0, lb.Title.IndexOf(':')), StringFormatter); lbProperties.RegisterProperty(TemplateTags.TitleShort, lb => lb.Title.IndexOf(':') < 1 ? lb.Title : lb.Title.Substring(0, lb.Title.IndexOf(':')), StringFormatter);
lbProperties.RegisterProperty(TemplateTags.Author, lb => lb.AuthorNames, StringFormatter); lbProperties.RegisterProperty(TemplateTags.Author, lb => lb.AuthorNames, StringFormatter);
lbProperties.RegisterProperty(TemplateTags.FirstAuthor, lb => lb.FirstAuthor, StringFormatter); lbProperties.RegisterProperty(TemplateTags.FirstAuthor, lb => lb.FirstAuthor, StringFormatter);
lbProperties.RegisterProperty(TemplateTags.Narrator, lb => lb.NarratorNames, StringFormatter); lbProperties.RegisterProperty(TemplateTags.Narrator, lb => lb.NarratorNames, StringFormatter);
lbProperties.RegisterProperty(TemplateTags.FirstNarrator, lb => lb.FirstNarrator, StringFormatter); lbProperties.RegisterProperty(TemplateTags.FirstNarrator, lb => lb.FirstNarrator, StringFormatter);
lbProperties.RegisterProperty(TemplateTags.Series, lb => lb.SeriesName ?? "", StringFormatter); lbProperties.RegisterProperty(TemplateTags.Series, lb => lb.SeriesName, StringFormatter);
lbProperties.RegisterProperty(TemplateTags.SeriesNumber, lb => lb.SeriesNumber); lbProperties.RegisterProperty(TemplateTags.SeriesNumber, lb => lb.SeriesNumber, IntegerFormatter);
lbProperties.RegisterProperty(TemplateTags.Language, lb => lb.Language); lbProperties.RegisterProperty(TemplateTags.Language, lb => lb.Language, StringFormatter);
lbProperties.RegisterProperty(TemplateTags.LanguageShort, lb => getLanguageShort(lb.Language)); lbProperties.RegisterProperty(TemplateTags.LanguageShort, lb => getLanguageShort(lb.Language), StringFormatter);
lbProperties.RegisterProperty(TemplateTags.Bitrate, lb => lb.BitRate, IntegerFormatter); lbProperties.RegisterProperty(TemplateTags.Bitrate, lb => lb.BitRate, IntegerFormatter);
lbProperties.RegisterProperty(TemplateTags.SampleRate, lb => lb.SampleRate, IntegerFormatter); lbProperties.RegisterProperty(TemplateTags.SampleRate, lb => lb.SampleRate, IntegerFormatter);
lbProperties.RegisterProperty(TemplateTags.Channels, lb => lb.Channels, IntegerFormatter); lbProperties.RegisterProperty(TemplateTags.Channels, lb => lb.Channels, IntegerFormatter);
@ -233,14 +236,15 @@ namespace LibationFileManager
PropertyTagClass<LibraryBookDto> lbProperties = new(); PropertyTagClass<LibraryBookDto> lbProperties = new();
PropertyTagClass<MultiConvertFileProperties> multiConvertProperties = new(); PropertyTagClass<MultiConvertFileProperties> multiConvertProperties = new();
lbProperties.RegisterProperty(TemplateTags.Title, lb => lb.Title ?? ""); lbProperties.RegisterProperty(TemplateTags.Title, lb => lb.Title, StringFormatter);
lbProperties.RegisterProperty(TemplateTags.TitleShort, lb => lb.Title.IndexOf(':') < 1 ? lb.Title : lb.Title.Substring(0, lb.Title.IndexOf(':'))); lbProperties.RegisterProperty(TemplateTags.TitleShort, lb => lb?.Title?.IndexOf(':') > 0 ? lb.Title.Substring(0, lb.Title.IndexOf(':')) : lb.Title, StringFormatter);
lbProperties.RegisterProperty(TemplateTags.Series, lb => lb.SeriesName ?? ""); lbProperties.RegisterProperty(TemplateTags.Series, lb => lb.SeriesName, StringFormatter);
multiConvertProperties.RegisterProperty(TemplateTags.ChCount, lb => lb.PartsTotal, IntegerFormatter); multiConvertProperties.RegisterProperty(TemplateTags.ChCount, lb => lb.PartsTotal, IntegerFormatter);
multiConvertProperties.RegisterProperty(TemplateTags.ChNumber, lb => lb.PartsPosition, IntegerFormatter); multiConvertProperties.RegisterProperty(TemplateTags.ChNumber, lb => lb.PartsPosition, IntegerFormatter);
multiConvertProperties.RegisterProperty(TemplateTags.ChNumber0, m => m.PartsPosition.ToString("D" + ((int)Math.Log10(m.PartsTotal) + 1))); multiConvertProperties.RegisterProperty(TemplateTags.ChNumber0, m => m.PartsPosition.ToString("D" + ((int)Math.Log10(m.PartsTotal) + 1)));
multiConvertProperties.RegisterProperty(TemplateTags.ChTitle, m => m.Title ?? "", StringFormatter); multiConvertProperties.RegisterProperty(TemplateTags.ChTitle, m => m.Title, StringFormatter);
multiConvertProperties.RegisterProperty(TemplateTags.FileDate, lb => lb.FileDate, DateTimeFormatter);
return new List<TagClass> { lbProperties, multiConvertProperties }; return new List<TagClass> { lbProperties, multiConvertProperties };
} }
@ -303,15 +307,6 @@ namespace LibationFileManager
else else
tp.Value = replacements.ReplaceFilenameChars(tp.Value); tp.Value = replacements.ReplaceFilenameChars(tp.Value);
} }
if (parts.Count > 0)
{
//Remove DirectorySeparatorChar at beginning and end of template
if (parts[0].Value.Length > 0 && parts[0].Value[0] == Path.DirectorySeparatorChar)
parts[0].Value = parts[0].Value.Remove(0,1);
if (parts[^1].Value.Length > 0 && parts[^1].Value[^1] == Path.DirectorySeparatorChar)
parts[^1].Value = parts[^1].Value.Remove(parts[^1].Value.Length - 1, 1);
}
return parts.Select(p => p.Value).ToList(); return parts.Select(p => p.Value).ToList();
} }
} }

View File

@ -37,7 +37,7 @@ namespace TemplatesTests
Authors = new List<string> { "Arthur Conan Doyle", "Stephen Fry - introductions" }, Authors = new List<string> { "Arthur Conan Doyle", "Stephen Fry - introductions" },
Narrators = new List<string> { "Stephen Fry" }, Narrators = new List<string> { "Stephen Fry" },
SeriesName = seriesName ?? "", SeriesName = seriesName ?? "",
SeriesNumber = "1", SeriesNumber = 1,
BitRate = 128, BitRate = 128,
SampleRate = 44100, SampleRate = 44100,
Channels = 2, Channels = 2,
@ -297,8 +297,8 @@ namespace Templates_Other
static ReplacementCharacters Replacements = ReplacementCharacters.Default; static ReplacementCharacters Replacements = ReplacementCharacters.Default;
[TestMethod] [TestMethod]
[DataRow(@"C:\foo\bar", @"\Folder\<title>\[<id>]\", @"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", "/Folder/<title>/[<id>]/", @" / 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)]
[DataRow(@"C:\foo\bar", @"\Folder\<title> [<id>]", @"C:\foo\bar\Folder\my book 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 [ID123456].txt", PlatformID.Win32NT)] [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("/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(@"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)]