Custom File Naming complete. Final testing remains
This commit is contained in:
parent
c837fefbdd
commit
ab450c37c4
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<Version>6.3.4.19</Version>
|
<Version>6.4.0.1</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -11,42 +11,18 @@ namespace FileLiberator
|
|||||||
{
|
{
|
||||||
public static class AudioFileStorageExt
|
public static class AudioFileStorageExt
|
||||||
{
|
{
|
||||||
public class MultipartRenamer
|
private class MultipartRenamer
|
||||||
{
|
{
|
||||||
private LibraryBookDto libraryBookDto { get; }
|
private LibraryBook libraryBook { get; }
|
||||||
|
|
||||||
public MultipartRenamer(LibraryBook libraryBook) : this(libraryBook.ToDto()) { }
|
internal MultipartRenamer(LibraryBook libraryBook) => this.libraryBook = libraryBook;
|
||||||
public MultipartRenamer(LibraryBookDto libraryBookDto) => this.libraryBookDto = libraryBookDto;
|
|
||||||
|
|
||||||
internal string MultipartFilename(AaxDecrypter.MultiConvertFileProperties props)
|
internal string MultipartFilename(AaxDecrypter.MultiConvertFileProperties props)
|
||||||
=> MultipartFilename(props, Configuration.Instance.ChapterFileTemplate, AudibleFileStorage.DecryptInProgressDirectory);
|
=> Templates.ChapterFile.GetFilename(libraryBook.ToDto(), props);
|
||||||
|
|
||||||
public string MultipartFilename(AaxDecrypter.MultiConvertFileProperties props, string template, string fullDirPath)
|
|
||||||
{
|
|
||||||
var fileNamingTemplate = GetFileNamingTemplate(template, libraryBookDto, fullDirPath, Path.GetExtension(props.OutputFileName));
|
|
||||||
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChCount, props.PartsTotal);
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChNumber, props.PartsPosition);
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChNumber0, FileUtility.GetSequenceFormatted(props.PartsPosition, props.PartsTotal));
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChTitle, props.Title ?? "");
|
|
||||||
|
|
||||||
return fileNamingTemplate.GetFilePath();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Func<AaxDecrypter.MultiConvertFileProperties, string> CreateMultipartRenamerFunc(this AudioFileStorage _, LibraryBook libraryBook)
|
public static Func<AaxDecrypter.MultiConvertFileProperties, string> CreateMultipartRenamerFunc(this AudioFileStorage _, LibraryBook libraryBook)
|
||||||
=> new MultipartRenamer(libraryBook).MultipartFilename;
|
=> new MultipartRenamer(libraryBook).MultipartFilename;
|
||||||
public static Func<AaxDecrypter.MultiConvertFileProperties, string> CreateMultipartRenamerFunc(this AudioFileStorage _, LibraryBookDto libraryBookDto)
|
|
||||||
=> new MultipartRenamer(libraryBookDto).MultipartFilename;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DownloadDecryptBook:
|
|
||||||
/// Path: in progress directory.
|
|
||||||
/// File name: final file name.
|
|
||||||
/// </summary>
|
|
||||||
public static string GetInProgressFilename(this AudioFileStorage _, LibraryBook libraryBook, string extension)
|
|
||||||
=> GetFileNamingTemplate(Configuration.Instance.FileTemplate, libraryBook.ToDto(), AudibleFileStorage.DecryptInProgressDirectory, extension)
|
|
||||||
.GetFilePath();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// DownloadDecryptBook:
|
/// DownloadDecryptBook:
|
||||||
@ -55,50 +31,26 @@ namespace FileLiberator
|
|||||||
/// File name: n/a
|
/// File name: n/a
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string GetDestinationDirectory(this AudioFileStorage _, LibraryBook libraryBook)
|
public static string GetDestinationDirectory(this AudioFileStorage _, LibraryBook libraryBook)
|
||||||
=> GetFileNamingTemplate(Configuration.Instance.FolderTemplate, libraryBook.ToDto(), AudibleFileStorage.BooksDirectory, null)
|
=> Templates.Folder.GetFilename(libraryBook.ToDto());
|
||||||
.GetFilePath();
|
|
||||||
|
/// <summary>
|
||||||
|
/// DownloadDecryptBook:
|
||||||
|
/// Path: in progress directory.
|
||||||
|
/// File name: final file name.
|
||||||
|
/// </summary>
|
||||||
|
public static string GetInProgressFilename(this AudioFileStorage _, LibraryBook libraryBook, string extension)
|
||||||
|
=> Templates.File.GetFilename(libraryBook.ToDto(), AudibleFileStorage.DecryptInProgressDirectory, extension);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// PDF: audio file does not exist
|
/// PDF: audio file does not exist
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string GetBooksDirectoryFilename(this AudioFileStorage _, LibraryBook libraryBook, string extension)
|
public static string GetBooksDirectoryFilename(this AudioFileStorage _, LibraryBook libraryBook, string extension)
|
||||||
=> GetFileNamingTemplate(Configuration.Instance.FileTemplate, libraryBook.ToDto(), AudibleFileStorage.BooksDirectory, extension)
|
=> Templates.File.GetFilename(libraryBook.ToDto(), AudibleFileStorage.BooksDirectory, extension);
|
||||||
.GetFilePath();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// PDF: audio file already exists
|
/// PDF: audio file already exists
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string GetCustomDirFilename(this AudioFileStorage _, LibraryBook libraryBook, string dirFullPath, string extension)
|
public static string GetCustomDirFilename(this AudioFileStorage _, LibraryBook libraryBook, string dirFullPath, string extension)
|
||||||
=> GetFileNamingTemplate(Configuration.Instance.FileTemplate, libraryBook.ToDto(), dirFullPath, extension)
|
=> Templates.File.GetFilename(libraryBook.ToDto(), dirFullPath, extension);
|
||||||
.GetFilePath();
|
|
||||||
|
|
||||||
public static FileNamingTemplate GetFileNamingTemplate(string template, LibraryBookDto libraryBookDto, string dirFullPath, string extension)
|
|
||||||
{
|
|
||||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(template, nameof(template));
|
|
||||||
ArgumentValidator.EnsureNotNull(libraryBookDto, nameof(libraryBookDto));
|
|
||||||
|
|
||||||
dirFullPath = dirFullPath?.Trim() ?? "";
|
|
||||||
var t = template + FileUtility.GetStandardizedExtension(extension);
|
|
||||||
var fullfilename = dirFullPath == "" ? t : Path.Combine(dirFullPath, t);
|
|
||||||
|
|
||||||
var fileNamingTemplate = new FileNamingTemplate(fullfilename) { IllegalCharacterReplacements = "_" };
|
|
||||||
|
|
||||||
var title = libraryBookDto.Title ?? "";
|
|
||||||
var titleShort = title.IndexOf(':') < 1 ? title : title.Substring(0, title.IndexOf(':'));
|
|
||||||
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.Id, libraryBookDto.AudibleProductId);
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.Title, title);
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.TitleShort, titleShort);
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.Author, libraryBookDto.AuthorNames);
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.FirstAuthor, libraryBookDto.FirstAuthor);
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.Narrator, libraryBookDto.NarratorNames);
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.FirstNarrator, libraryBookDto.FirstNarrator);
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.Series, libraryBookDto.SeriesName);
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.SeriesNumber, libraryBookDto.SeriesNumber);
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.Account, libraryBookDto.Account);
|
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.Locale, libraryBookDto.Locale);
|
|
||||||
|
|
||||||
return fileNamingTemplate;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\AaxDecrypter\AaxDecrypter.csproj" />
|
||||||
<ProjectReference Include="..\FileManager\FileManager.csproj" />
|
<ProjectReference Include="..\FileManager\FileManager.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ namespace LibationFileManager
|
|||||||
IsChapterOnly = isChapterOnly;
|
IsChapterOnly = isChapterOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
// putting these first is the incredibly lazy way to make them show up first in the settings dialog
|
// putting these first is the incredibly lazy way to make them show up first in the EditTemplateDialog
|
||||||
public static TemplateTags ChCount { get; } = new TemplateTags("ch count", "Number of chapters", true);
|
public static TemplateTags ChCount { get; } = new TemplateTags("ch count", "Number of chapters", true);
|
||||||
public static TemplateTags ChTitle { get; } = new TemplateTags("ch title", "Chapter title", true);
|
public static TemplateTags ChTitle { get; } = new TemplateTags("ch title", "Chapter title", true);
|
||||||
public static TemplateTags ChNumber { get; } = new TemplateTags("ch#", "Chapter number", true);
|
public static TemplateTags ChNumber { get; } = new TemplateTags("ch#", "Chapter number", true);
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
using System;
|
using Dinah.Core;
|
||||||
|
using FileManager;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -18,74 +20,30 @@ namespace LibationFileManager
|
|||||||
public const string WARNING_HAS_CHAPTER_TAGS = "Chapter tags should only be used in the template used for naming files which are split by chapter. Eg: <ch title>";
|
public const string WARNING_HAS_CHAPTER_TAGS = "Chapter tags should only be used in the template used for naming files which are split by chapter. Eg: <ch title>";
|
||||||
public const string WARNING_NO_CHAPTER_NUMBER_TAG = "Should include chapter number tag in template used for naming files which are split by chapter. Ie: <ch#> or <ch# 0>";
|
public const string WARNING_NO_CHAPTER_NUMBER_TAG = "Should include chapter number tag in template used for naming files which are split by chapter. Ie: <ch#> or <ch# 0>";
|
||||||
|
|
||||||
public static Templates Folder { get; } = new FolderTemplate();
|
public static FolderTemplate Folder { get; } = new FolderTemplate();
|
||||||
public static Templates File { get; } = new FileTemplate();
|
public static FileTemplate File { get; } = new FileTemplate();
|
||||||
public static Templates ChapterFile { get; } = new ChapterFileTemplate();
|
public static ChapterFileTemplate ChapterFile { get; } = new ChapterFileTemplate();
|
||||||
|
|
||||||
public abstract string Name { get; }
|
public abstract string Name { get; }
|
||||||
public abstract string Description { get; }
|
public abstract string Description { get; }
|
||||||
public abstract string DefaultTemplate { get; }
|
public abstract string DefaultTemplate { get; }
|
||||||
protected abstract bool IsChapterized { get; }
|
protected abstract bool IsChapterized { get; }
|
||||||
|
|
||||||
internal string GetValid(string configValue)
|
protected Templates() { }
|
||||||
|
|
||||||
|
#region validation
|
||||||
|
internal string GetValid(string configValue)
|
||||||
{
|
{
|
||||||
var value = configValue?.Trim();
|
var value = configValue?.Trim();
|
||||||
return IsValid(value) ? value : DefaultTemplate;
|
return IsValid(value) ? value : DefaultTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Sanitize(string template)
|
|
||||||
{
|
|
||||||
var value = template ?? "";
|
|
||||||
|
|
||||||
// don't use alt slash
|
|
||||||
value = value.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
|
||||||
|
|
||||||
// don't allow double slashes
|
|
||||||
var sing = $"{Path.DirectorySeparatorChar}";
|
|
||||||
var dbl = $"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}";
|
|
||||||
while (value.Contains(dbl))
|
|
||||||
value = value.Replace(dbl, sing);
|
|
||||||
|
|
||||||
// trim. don't start or end with slash
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
var start = value.Length;
|
|
||||||
value = value
|
|
||||||
.Trim()
|
|
||||||
.Trim(Path.DirectorySeparatorChar);
|
|
||||||
var end = value.Length;
|
|
||||||
if (start == end)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract IEnumerable<string> GetErrors(string template);
|
public abstract IEnumerable<string> GetErrors(string template);
|
||||||
public bool IsValid(string template) => !GetErrors(template).Any();
|
public bool IsValid(string template) => !GetErrors(template).Any();
|
||||||
|
|
||||||
public abstract IEnumerable<string> GetWarnings(string template);
|
public abstract IEnumerable<string> GetWarnings(string template);
|
||||||
public bool HasWarnings(string template) => GetWarnings(template).Any();
|
public bool HasWarnings(string template) => GetWarnings(template).Any();
|
||||||
|
|
||||||
public IEnumerable<TemplateTags> GetTemplateTags()
|
|
||||||
=> TemplateTags.GetAll()
|
|
||||||
// yeah, this line is a little funky but it works when you think through it. also: trust the unit tests
|
|
||||||
.Where(t => IsChapterized || !t.IsChapterOnly);
|
|
||||||
|
|
||||||
public int TagCount(string template)
|
|
||||||
=> GetTemplateTags()
|
|
||||||
// for <id><id> == 1, use:
|
|
||||||
// .Count(t => template.Contains($"<{t.TagName}>"))
|
|
||||||
// .Sum() impl: <id><id> == 2
|
|
||||||
.Sum(t => template.Split($"<{t.TagName}>").Length - 1);
|
|
||||||
|
|
||||||
public static bool ContainsChapterOnlyTags(string template)
|
|
||||||
=> TemplateTags.GetAll()
|
|
||||||
.Where(t => t.IsChapterOnly)
|
|
||||||
.Any(t => ContainsTag(template, t.TagName));
|
|
||||||
|
|
||||||
public static bool ContainsTag(string template, string tag) => template.Contains($"<{tag}>");
|
|
||||||
|
|
||||||
protected static string[] GetFileErrors(string template)
|
protected static string[] GetFileErrors(string template)
|
||||||
{
|
{
|
||||||
// File name only; not path. all other path chars are valid enough to pass this check and will be handled on final save.
|
// File name only; not path. all other path chars are valid enough to pass this check and will be handled on final save.
|
||||||
@ -123,13 +81,102 @@ namespace LibationFileManager
|
|||||||
return warnings;
|
return warnings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FolderTemplate : Templates
|
internal int TagCount(string template)
|
||||||
|
=> GetTemplateTags()
|
||||||
|
// for <id><id> == 1, use:
|
||||||
|
// .Count(t => template.Contains($"<{t.TagName}>"))
|
||||||
|
// .Sum() impl: <id><id> == 2
|
||||||
|
.Sum(t => template.Split($"<{t.TagName}>").Length - 1);
|
||||||
|
|
||||||
|
internal static bool ContainsChapterOnlyTags(string template)
|
||||||
|
=> TemplateTags.GetAll()
|
||||||
|
.Where(t => t.IsChapterOnly)
|
||||||
|
.Any(t => ContainsTag(template, t.TagName));
|
||||||
|
|
||||||
|
internal static bool ContainsTag(string template, string tag) => template.Contains($"<{tag}>");
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region to file name
|
||||||
|
/// <summary>
|
||||||
|
/// EditTemplateDialog: Get template generated filename for portion of path
|
||||||
|
/// </summary>
|
||||||
|
public string GetPortionFilename(LibraryBookDto libraryBookDto, string template)
|
||||||
|
=> getFileNamingTemplate(libraryBookDto, template, null, null)
|
||||||
|
.GetFilePath();
|
||||||
|
|
||||||
|
internal static FileNamingTemplate getFileNamingTemplate(LibraryBookDto libraryBookDto, string template, string dirFullPath, string extension)
|
||||||
|
{
|
||||||
|
ArgumentValidator.EnsureNotNullOrWhiteSpace(template, nameof(template));
|
||||||
|
ArgumentValidator.EnsureNotNull(libraryBookDto, nameof(libraryBookDto));
|
||||||
|
|
||||||
|
dirFullPath = dirFullPath?.Trim() ?? "";
|
||||||
|
var t = template + FileUtility.GetStandardizedExtension(extension);
|
||||||
|
var fullfilename = dirFullPath == "" ? t : Path.Combine(dirFullPath, t);
|
||||||
|
|
||||||
|
var fileNamingTemplate = new FileNamingTemplate(fullfilename) { IllegalCharacterReplacements = "_" };
|
||||||
|
|
||||||
|
var title = libraryBookDto.Title ?? "";
|
||||||
|
var titleShort = title.IndexOf(':') < 1 ? title : title.Substring(0, title.IndexOf(':'));
|
||||||
|
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.Id, libraryBookDto.AudibleProductId);
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.Title, title);
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.TitleShort, titleShort);
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.Author, libraryBookDto.AuthorNames);
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.FirstAuthor, libraryBookDto.FirstAuthor);
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.Narrator, libraryBookDto.NarratorNames);
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.FirstNarrator, libraryBookDto.FirstNarrator);
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.Series, libraryBookDto.SeriesName);
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.SeriesNumber, libraryBookDto.SeriesNumber);
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.Account, libraryBookDto.Account);
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.Locale, libraryBookDto.Locale);
|
||||||
|
|
||||||
|
return fileNamingTemplate;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public IEnumerable<TemplateTags> GetTemplateTags()
|
||||||
|
=> TemplateTags.GetAll()
|
||||||
|
// yeah, this line is a little funky but it works when you think through it. also: trust the unit tests
|
||||||
|
.Where(t => IsChapterized || !t.IsChapterOnly);
|
||||||
|
|
||||||
|
public string Sanitize(string template)
|
||||||
|
{
|
||||||
|
var value = template ?? "";
|
||||||
|
|
||||||
|
// don't use alt slash
|
||||||
|
value = value.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||||
|
|
||||||
|
// don't allow double slashes
|
||||||
|
var sing = $"{Path.DirectorySeparatorChar}";
|
||||||
|
var dbl = $"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}";
|
||||||
|
while (value.Contains(dbl))
|
||||||
|
value = value.Replace(dbl, sing);
|
||||||
|
|
||||||
|
// trim. don't start or end with slash
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var start = value.Length;
|
||||||
|
value = value
|
||||||
|
.Trim()
|
||||||
|
.Trim(Path.DirectorySeparatorChar);
|
||||||
|
var end = value.Length;
|
||||||
|
if (start == end)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FolderTemplate : Templates
|
||||||
{
|
{
|
||||||
public override string Name => "Folder Template";
|
public override string Name => "Folder Template";
|
||||||
public override string Description => Configuration.GetDescription(nameof(Configuration.FolderTemplate));
|
public override string Description => Configuration.GetDescription(nameof(Configuration.FolderTemplate));
|
||||||
public override string DefaultTemplate { get; } = "<title short> [<id>]";
|
public override string DefaultTemplate { get; } = "<title short> [<id>]";
|
||||||
protected override bool IsChapterized { get; } = false;
|
protected override bool IsChapterized { get; } = false;
|
||||||
|
|
||||||
|
internal FolderTemplate() : base() { }
|
||||||
|
|
||||||
|
#region validation
|
||||||
public override IEnumerable<string> GetErrors(string template)
|
public override IEnumerable<string> GetErrors(string template)
|
||||||
{
|
{
|
||||||
// null is invalid. whitespace is valid but not recommended
|
// null is invalid. whitespace is valid but not recommended
|
||||||
@ -144,27 +191,49 @@ namespace LibationFileManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> GetWarnings(string template) => GetStandardWarnings(template);
|
public override IEnumerable<string> GetWarnings(string template) => GetStandardWarnings(template);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region to file name
|
||||||
|
/// <summary>USES LIVE CONFIGURATION VALUES</summary>
|
||||||
|
public string GetFilename(LibraryBookDto libraryBookDto)
|
||||||
|
=> getFileNamingTemplate(libraryBookDto, Configuration.Instance.FolderTemplate, AudibleFileStorage.BooksDirectory, null)
|
||||||
|
.GetFilePath();
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FileTemplate : Templates
|
public class FileTemplate : Templates
|
||||||
{
|
{
|
||||||
public override string Name => "File Template";
|
public override string Name => "File Template";
|
||||||
public override string Description => Configuration.GetDescription(nameof(Configuration.FileTemplate));
|
public override string Description => Configuration.GetDescription(nameof(Configuration.FileTemplate));
|
||||||
public override string DefaultTemplate { get; } = "<title> [<id>]";
|
public override string DefaultTemplate { get; } = "<title> [<id>]";
|
||||||
protected override bool IsChapterized { get; } = false;
|
protected override bool IsChapterized { get; } = false;
|
||||||
|
|
||||||
|
internal FileTemplate() : base() { }
|
||||||
|
|
||||||
|
#region validation
|
||||||
public override IEnumerable<string> GetErrors(string template) => GetFileErrors(template);
|
public override IEnumerable<string> GetErrors(string template) => GetFileErrors(template);
|
||||||
|
|
||||||
public override IEnumerable<string> GetWarnings(string template) => GetStandardWarnings(template);
|
public override IEnumerable<string> GetWarnings(string template) => GetStandardWarnings(template);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region to file name
|
||||||
|
/// <summary>USES LIVE CONFIGURATION VALUES</summary>
|
||||||
|
public string GetFilename(LibraryBookDto libraryBookDto, string dirFullPath, string extension)
|
||||||
|
=> getFileNamingTemplate(libraryBookDto, Configuration.Instance.FileTemplate, dirFullPath, extension)
|
||||||
|
.GetFilePath();
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ChapterFileTemplate : Templates
|
public class ChapterFileTemplate : Templates
|
||||||
{
|
{
|
||||||
public override string Name => "Chapter File Template";
|
public override string Name => "Chapter File Template";
|
||||||
public override string Description => Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate));
|
public override string Description => Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate));
|
||||||
public override string DefaultTemplate { get; } = "<title> [<id>] - <ch# 0> - <ch title>";
|
public override string DefaultTemplate { get; } = "<title> [<id>] - <ch# 0> - <ch title>";
|
||||||
protected override bool IsChapterized { get; } = true;
|
protected override bool IsChapterized { get; } = true;
|
||||||
|
|
||||||
|
internal ChapterFileTemplate() : base() { }
|
||||||
|
|
||||||
|
#region validation
|
||||||
public override IEnumerable<string> GetErrors(string template) => GetFileErrors(template);
|
public override IEnumerable<string> GetErrors(string template) => GetFileErrors(template);
|
||||||
|
|
||||||
public override IEnumerable<string> GetWarnings(string template)
|
public override IEnumerable<string> GetWarnings(string template)
|
||||||
@ -178,7 +247,26 @@ namespace LibationFileManager
|
|||||||
warnings.Add(WARNING_NO_CHAPTER_NUMBER_TAG);
|
warnings.Add(WARNING_NO_CHAPTER_NUMBER_TAG);
|
||||||
|
|
||||||
return warnings;
|
return warnings;
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region to file name
|
||||||
|
/// <summary>USES LIVE CONFIGURATION VALUES</summary>
|
||||||
|
public string GetFilename(LibraryBookDto libraryBookDto, AaxDecrypter.MultiConvertFileProperties props)
|
||||||
|
=> GetPortionFilename(libraryBookDto, Configuration.Instance.ChapterFileTemplate, props, AudibleFileStorage.DecryptInProgressDirectory);
|
||||||
|
|
||||||
|
public string GetPortionFilename(LibraryBookDto libraryBookDto, string template, AaxDecrypter.MultiConvertFileProperties props, string fullDirPath)
|
||||||
|
{
|
||||||
|
var fileNamingTemplate = getFileNamingTemplate(libraryBookDto, template, fullDirPath, Path.GetExtension(props.OutputFileName));
|
||||||
|
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChCount, props.PartsTotal);
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChNumber, props.PartsPosition);
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChNumber0, FileUtility.GetSequenceFormatted(props.PartsPosition, props.PartsTotal));
|
||||||
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChTitle, props.Title ?? "");
|
||||||
|
|
||||||
|
return fileNamingTemplate.GetFilePath();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,15 +10,15 @@ namespace LibationWinForms.Dialogs
|
|||||||
{
|
{
|
||||||
public partial class EditTemplateDialog : Form
|
public partial class EditTemplateDialog : Form
|
||||||
{
|
{
|
||||||
// final valid value
|
// final value. post-validity check
|
||||||
public string TemplateText { get; private set; }
|
public string TemplateText { get; private set; }
|
||||||
|
|
||||||
// work-in-progress. not guaranteed to be valid
|
// hold the work-in-progress value. not guaranteed to be valid
|
||||||
private string _workingTemplateText;
|
private string _workingTemplateText;
|
||||||
private string workingTemplateText
|
private string workingTemplateText
|
||||||
{
|
{
|
||||||
get => _workingTemplateText;
|
get => _workingTemplateText;
|
||||||
set => _workingTemplateText = Templates.Sanitize(value);
|
set => _workingTemplateText = template.Sanitize(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetTextBox(string value) => this.templateTb.Text = workingTemplateText = value;
|
private void resetTextBox(string value) => this.templateTb.Text = workingTemplateText = value;
|
||||||
@ -82,24 +82,19 @@ namespace LibationWinForms.Dialogs
|
|||||||
var chaptersTotal = 10;
|
var chaptersTotal = 10;
|
||||||
|
|
||||||
var books = config.Books;
|
var books = config.Books;
|
||||||
var folder = FileLiberator.AudioFileStorageExt.GetFileNamingTemplate(
|
var folder = Templates.Folder.GetPortionFilename(
|
||||||
isFolder ? workingTemplateText : config.FolderTemplate,
|
|
||||||
libraryBookDto,
|
libraryBookDto,
|
||||||
null,
|
isFolder ? workingTemplateText : config.FolderTemplate);
|
||||||
null)
|
|
||||||
.GetFilePath();
|
|
||||||
var file
|
var file
|
||||||
= (template == Templates.ChapterFile)
|
= template == Templates.ChapterFile
|
||||||
? new FileLiberator.AudioFileStorageExt.MultipartRenamer(libraryBookDto).MultipartFilename(
|
? Templates.ChapterFile.GetPortionFilename(
|
||||||
new() { OutputFileName = "", PartsPosition = chapterNumber, PartsTotal = chaptersTotal, Title = chapterName },
|
|
||||||
workingTemplateText,
|
|
||||||
"")
|
|
||||||
: FileLiberator.AudioFileStorageExt.GetFileNamingTemplate(
|
|
||||||
isFolder ? config.FileTemplate : workingTemplateText,
|
|
||||||
libraryBookDto,
|
libraryBookDto,
|
||||||
null,
|
workingTemplateText,
|
||||||
null)
|
new() { OutputFileName = "", PartsPosition = chapterNumber, PartsTotal = chaptersTotal, Title = chapterName },
|
||||||
.GetFilePath();
|
"")
|
||||||
|
: Templates.File.GetPortionFilename(
|
||||||
|
libraryBookDto,
|
||||||
|
isFolder ? config.FileTemplate : workingTemplateText);
|
||||||
var ext = config.DecryptToLossy ? "mp3" : "m4b";
|
var ext = config.DecryptToLossy ? "mp3" : "m4b";
|
||||||
|
|
||||||
const char ZERO_WIDTH_SPACE = '\u200B';
|
const char ZERO_WIDTH_SPACE = '\u200B';
|
||||||
|
|||||||
@ -108,19 +108,11 @@ namespace LibationWinForms.Dialogs
|
|||||||
private void chapterFileTemplateBtn_Click(object sender, EventArgs e) => editTemplate(Templates.ChapterFile, chapterFileTemplateTb);
|
private void chapterFileTemplateBtn_Click(object sender, EventArgs e) => editTemplate(Templates.ChapterFile, chapterFileTemplateTb);
|
||||||
private static void editTemplate(Templates template, TextBox textBox)
|
private static void editTemplate(Templates template, TextBox textBox)
|
||||||
{
|
{
|
||||||
#if !DEBUG
|
|
||||||
TEMP_TEMP_TEMP();
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
var form = new EditTemplateDialog(template, textBox.Text);
|
var form = new EditTemplateDialog(template, textBox.Text);
|
||||||
if (form.ShowDialog() == DialogResult.OK)
|
if (form.ShowDialog() == DialogResult.OK)
|
||||||
textBox.Text = form.TemplateText;
|
textBox.Text = form.TemplateText;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TEMP_TEMP_TEMP()
|
|
||||||
=> MessageBox.Show("Sorry, not yet. Coming soon :)");
|
|
||||||
|
|
||||||
private void saveBtn_Click(object sender, EventArgs e)
|
private void saveBtn_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var newBooks = booksSelectControl.SelectedDirectory;
|
var newBooks = booksSelectControl.SelectedDirectory;
|
||||||
|
|||||||
@ -1,69 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Dinah.Core;
|
|
||||||
using FileLiberator;
|
|
||||||
using FluentAssertions;
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
|
|
||||||
using static AudioFileStorageExtTests.Shared;
|
|
||||||
|
|
||||||
namespace AudioFileStorageExtTests
|
|
||||||
{
|
|
||||||
public static class Shared
|
|
||||||
{
|
|
||||||
public static LibationFileManager.LibraryBookDto GetLibraryBook(string asin)
|
|
||||||
=> new()
|
|
||||||
{
|
|
||||||
Account = "my account",
|
|
||||||
AudibleProductId = asin,
|
|
||||||
Title = "A Study in Scarlet: A Sherlock Holmes Novel",
|
|
||||||
Locale = "us",
|
|
||||||
Authors = new List<string> { "Arthur Conan Doyle", "Stephen Fry - introductions" },
|
|
||||||
Narrators = new List<string> { "Stephen Fry" },
|
|
||||||
SeriesName = "Sherlock Holmes",
|
|
||||||
SeriesNumber = "1"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestClass]
|
|
||||||
public class MultipartRenamer_MultipartFilename
|
|
||||||
{
|
|
||||||
[TestMethod]
|
|
||||||
[DataRow("asin", "[<id>] <ch# 0> of <ch count> - <ch title>", @"C:\foo\", "txt", 6, 10, "chap", @"C:\foo\[asin] 06 of 10 - chap.txt")]
|
|
||||||
[DataRow("asin", "<ch#>", @"C:\foo\", "txt", 6, 10, "chap", @"C:\foo\6.txt")]
|
|
||||||
public void Tests(string asin, string template, string dir, string ext, int pos, int total, string chapter, string expected)
|
|
||||||
=> new AudioFileStorageExt.MultipartRenamer(GetLibraryBook(asin))
|
|
||||||
.MultipartFilename(new() { OutputFileName = $"xyz.{ext}", PartsPosition = pos, PartsTotal = total, Title = chapter }, template, dir)
|
|
||||||
.Should().Be(expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestClass]
|
|
||||||
public class GetFileNamingTemplate
|
|
||||||
{
|
|
||||||
[TestMethod]
|
|
||||||
[DataRow(null, "asin", @"C:\", "ext")]
|
|
||||||
[ExpectedException(typeof(ArgumentNullException))]
|
|
||||||
public void arg_null_exception(string template, string asin, string dirFullPath, string extension)
|
|
||||||
=> AudioFileStorageExt.GetFileNamingTemplate(template, GetLibraryBook(asin), dirFullPath, extension);
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
[DataRow("", "asin", @"C:\foo\bar", "ext")]
|
|
||||||
[DataRow(" ", "asin", @"C:\foo\bar", "ext")]
|
|
||||||
[ExpectedException(typeof(ArgumentException))]
|
|
||||||
public void arg_exception(string template, string asin, string dirFullPath, string extension)
|
|
||||||
=> AudioFileStorageExt.GetFileNamingTemplate(template, GetLibraryBook(asin), dirFullPath, extension);
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void null_extension() => Tests("f.txt", "asin", @"C:\foo\bar", null, @"C:\foo\bar\f.txt");
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
[DataRow("f.txt", "asin", @"C:\foo\bar", "ext", @"C:\foo\bar\f.txt.ext")]
|
|
||||||
[DataRow("f", "asin", @"C:\foo\bar", "ext", @"C:\foo\bar\f.ext")]
|
|
||||||
[DataRow("<id>", "asin", @"C:\foo\bar", "ext", @"C:\foo\bar\asin.ext")]
|
|
||||||
public void Tests(string template, string asin, string dirFullPath, string extension, string expected)
|
|
||||||
=> AudioFileStorageExt.GetFileNamingTemplate(template, GetLibraryBook(asin), dirFullPath, extension)
|
|
||||||
.GetFilePath()
|
|
||||||
.Should().Be(expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -7,8 +7,26 @@ using FluentAssertions;
|
|||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
using static TemplatesTests.Shared;
|
||||||
|
|
||||||
namespace TemplatesTests
|
namespace TemplatesTests
|
||||||
{
|
{
|
||||||
|
public static class Shared
|
||||||
|
{
|
||||||
|
public static LibraryBookDto GetLibraryBook(string asin)
|
||||||
|
=> new()
|
||||||
|
{
|
||||||
|
Account = "my account",
|
||||||
|
AudibleProductId = asin,
|
||||||
|
Title = "A Study in Scarlet: A Sherlock Holmes Novel",
|
||||||
|
Locale = "us",
|
||||||
|
Authors = new List<string> { "Arthur Conan Doyle", "Stephen Fry - introductions" },
|
||||||
|
Narrators = new List<string> { "Stephen Fry" },
|
||||||
|
SeriesName = "Sherlock Holmes",
|
||||||
|
SeriesNumber = "1"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class ContainsChapterOnlyTags
|
public class ContainsChapterOnlyTags
|
||||||
{
|
{
|
||||||
@ -29,6 +47,35 @@ namespace TemplatesTests
|
|||||||
[DataRow("<id><ch#>", "ch#", true)]
|
[DataRow("<id><ch#>", "ch#", true)]
|
||||||
public void Tests(string template, string tag, bool expected) => Templates.ContainsTag(template, tag).Should().Be(expected);
|
public void Tests(string template, string tag, bool expected) => Templates.ContainsTag(template, tag).Should().Be(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class getFileNamingTemplate
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
[DataRow(null, "asin", @"C:\", "ext")]
|
||||||
|
[ExpectedException(typeof(ArgumentNullException))]
|
||||||
|
public void arg_null_exception(string template, string asin, string dirFullPath, string extension)
|
||||||
|
=> Templates.getFileNamingTemplate(GetLibraryBook(asin), template, dirFullPath, extension);
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[DataRow("", "asin", @"C:\foo\bar", "ext")]
|
||||||
|
[DataRow(" ", "asin", @"C:\foo\bar", "ext")]
|
||||||
|
[ExpectedException(typeof(ArgumentException))]
|
||||||
|
public void arg_exception(string template, string asin, string dirFullPath, string extension)
|
||||||
|
=> Templates.getFileNamingTemplate(GetLibraryBook(asin), template, dirFullPath, extension);
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void null_extension() => Tests("f.txt", "asin", @"C:\foo\bar", null, @"C:\foo\bar\f.txt");
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[DataRow("f.txt", "asin", @"C:\foo\bar", "ext", @"C:\foo\bar\f.txt.ext")]
|
||||||
|
[DataRow("f", "asin", @"C:\foo\bar", "ext", @"C:\foo\bar\f.ext")]
|
||||||
|
[DataRow("<id>", "asin", @"C:\foo\bar", "ext", @"C:\foo\bar\asin.ext")]
|
||||||
|
public void Tests(string template, string asin, string dirFullPath, string extension, string expected)
|
||||||
|
=> Templates.getFileNamingTemplate(GetLibraryBook(asin), template, dirFullPath, extension)
|
||||||
|
.GetFilePath()
|
||||||
|
.Should().Be(expected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Templates_Folder_Tests
|
namespace Templates_Folder_Tests
|
||||||
@ -314,4 +361,15 @@ namespace Templates_ChapterFile_Tests
|
|||||||
[DataRow("<ID> case specific", 0)]
|
[DataRow("<ID> case specific", 0)]
|
||||||
public void Tests(string template, int expected) => Templates.ChapterFile.TagCount(template).Should().Be(expected);
|
public void Tests(string template, int expected) => Templates.ChapterFile.TagCount(template).Should().Be(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class GetPortionFilename
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
[DataRow("asin", "[<id>] <ch# 0> of <ch count> - <ch title>", @"C:\foo\", "txt", 6, 10, "chap", @"C:\foo\[asin] 06 of 10 - chap.txt")]
|
||||||
|
[DataRow("asin", "<ch#>", @"C:\foo\", "txt", 6, 10, "chap", @"C:\foo\6.txt")]
|
||||||
|
public void Tests(string asin, string template, string dir, string ext, int pos, int total, string chapter, string expected)
|
||||||
|
=> Templates.ChapterFile.GetPortionFilename(GetLibraryBook(asin), template, new() { OutputFileName = $"xyz.{ext}", PartsPosition = pos, PartsTotal = total, Title = chapter }, dir)
|
||||||
|
.Should().Be(expected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user