template file naming: code complete. Clean up and testing remain
This commit is contained in:
parent
46b120ee41
commit
c837fefbdd
@ -12,23 +12,13 @@ namespace AaxDecrypter
|
|||||||
{
|
{
|
||||||
protected override StepSequence Steps { get; }
|
protected override StepSequence Steps { get; }
|
||||||
|
|
||||||
private Func<string, int, int, NewSplitCallback, string> multipartFileNameCallback { get; }
|
private Func<MultiConvertFileProperties, string> multipartFileNameCallback { get; }
|
||||||
private static string DefaultMultipartFilename(string outputFileName, int partsPosition, int partsTotal, NewSplitCallback newSplitCallback)
|
|
||||||
{
|
|
||||||
var template = Path.ChangeExtension(outputFileName, null) + " - <ch# 0> - <title>" + Path.GetExtension(outputFileName);
|
|
||||||
|
|
||||||
var fileTemplate = new FileTemplate(template) { IllegalCharacterReplacements = " " };
|
|
||||||
fileTemplate.AddParameterReplacement("ch# 0", FileUtility.GetSequenceFormatted(partsPosition, partsTotal));
|
|
||||||
fileTemplate.AddParameterReplacement("title", newSplitCallback?.Chapter?.Title ?? "");
|
|
||||||
|
|
||||||
return fileTemplate.GetFilePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TimeSpan minChapterLength { get; } = TimeSpan.FromSeconds(3);
|
private static TimeSpan minChapterLength { get; } = TimeSpan.FromSeconds(3);
|
||||||
private List<string> multiPartFilePaths { get; } = new List<string>();
|
private List<string> multiPartFilePaths { get; } = new List<string>();
|
||||||
|
|
||||||
public AaxcDownloadMultiConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic, OutputFormat outputFormat,
|
public AaxcDownloadMultiConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic, OutputFormat outputFormat,
|
||||||
Func<string, int, int, NewSplitCallback, string> multipartFileNameCallback = null)
|
Func<MultiConvertFileProperties, string> multipartFileNameCallback = null)
|
||||||
: base(outFileName, cacheDirectory, dlLic, outputFormat)
|
: base(outFileName, cacheDirectory, dlLic, outputFormat)
|
||||||
{
|
{
|
||||||
Steps = new StepSequence
|
Steps = new StepSequence
|
||||||
@ -39,7 +29,7 @@ namespace AaxDecrypter
|
|||||||
["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsMultipleFilesPerChapter,
|
["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsMultipleFilesPerChapter,
|
||||||
["Step 3: Cleanup"] = Step_Cleanup,
|
["Step 3: Cleanup"] = Step_Cleanup,
|
||||||
};
|
};
|
||||||
this.multipartFileNameCallback = multipartFileNameCallback ?? DefaultMultipartFilename;
|
this.multipartFileNameCallback = multipartFileNameCallback ?? MultiConvertFileProperties.DefaultMultipartFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -133,7 +123,13 @@ That naming may not be desirable for everyone, but it's an easy change to instea
|
|||||||
|
|
||||||
private void createOutputFileStream(int currentChapter, ChapterInfo splitChapters, NewSplitCallback newSplitCallback)
|
private void createOutputFileStream(int currentChapter, ChapterInfo splitChapters, NewSplitCallback newSplitCallback)
|
||||||
{
|
{
|
||||||
var fileName = multipartFileNameCallback(OutputFileName, currentChapter, splitChapters.Count, newSplitCallback);
|
var fileName = multipartFileNameCallback(new()
|
||||||
|
{
|
||||||
|
OutputFileName = OutputFileName,
|
||||||
|
PartsPosition = currentChapter,
|
||||||
|
PartsTotal = splitChapters.Count,
|
||||||
|
Title = newSplitCallback?.Chapter?.Title
|
||||||
|
});
|
||||||
fileName = FileUtility.GetValidFilename(fileName);
|
fileName = FileUtility.GetValidFilename(fileName);
|
||||||
|
|
||||||
multiPartFilePaths.Add(fileName);
|
multiPartFilePaths.Add(fileName);
|
||||||
|
|||||||
25
AaxDecrypter/MultiConvertFileProperties.cs
Normal file
25
AaxDecrypter/MultiConvertFileProperties.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using FileManager;
|
||||||
|
|
||||||
|
namespace AaxDecrypter
|
||||||
|
{
|
||||||
|
public class MultiConvertFileProperties
|
||||||
|
{
|
||||||
|
public string OutputFileName { get; set; }
|
||||||
|
public int PartsPosition { get; set; }
|
||||||
|
public int PartsTotal { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
public static string DefaultMultipartFilename(MultiConvertFileProperties multiConvertFileProperties)
|
||||||
|
{
|
||||||
|
var template = Path.ChangeExtension(multiConvertFileProperties.OutputFileName, null) + " - <ch# 0> - <title>" + Path.GetExtension(multiConvertFileProperties.OutputFileName);
|
||||||
|
|
||||||
|
var fileNamingTemplate = new FileNamingTemplate(template) { IllegalCharacterReplacements = " " };
|
||||||
|
fileNamingTemplate.AddParameterReplacement("ch# 0", FileUtility.GetSequenceFormatted(multiConvertFileProperties.PartsPosition, multiConvertFileProperties.PartsTotal));
|
||||||
|
fileNamingTemplate.AddParameterReplacement("title", multiConvertFileProperties.Title ?? "");
|
||||||
|
|
||||||
|
return fileNamingTemplate.GetFilePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<Version>6.3.4.1</Version>
|
<Version>6.3.4.19</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -11,72 +11,94 @@ namespace FileLiberator
|
|||||||
{
|
{
|
||||||
public static class AudioFileStorageExt
|
public static class AudioFileStorageExt
|
||||||
{
|
{
|
||||||
private static void AddParameterReplacement(this FileTemplate fileTemplate, TemplateTags templateTags, object value)
|
public class MultipartRenamer
|
||||||
=> fileTemplate.AddParameterReplacement(templateTags.TagName, value);
|
|
||||||
|
|
||||||
internal class MultipartRenamer
|
|
||||||
{
|
{
|
||||||
public LibraryBook libraryBook { get; }
|
private LibraryBookDto libraryBookDto { get; }
|
||||||
|
|
||||||
public MultipartRenamer(LibraryBook libraryBook) => this.libraryBook = libraryBook;
|
public MultipartRenamer(LibraryBook libraryBook) : this(libraryBook.ToDto()) { }
|
||||||
|
public MultipartRenamer(LibraryBookDto libraryBookDto) => this.libraryBookDto = libraryBookDto;
|
||||||
|
|
||||||
internal string MultipartFilename(string outputFileName, int partsPosition, int partsTotal, AAXClean.NewSplitCallback newSplitCallback)
|
internal string MultipartFilename(AaxDecrypter.MultiConvertFileProperties props)
|
||||||
=> MultipartFilename(Configuration.Instance.ChapterFileTemplate, AudibleFileStorage.DecryptInProgressDirectory, Path.GetExtension(outputFileName), partsPosition, partsTotal, newSplitCallback?.Chapter?.Title ?? "");
|
=> MultipartFilename(props, Configuration.Instance.ChapterFileTemplate, AudibleFileStorage.DecryptInProgressDirectory);
|
||||||
|
|
||||||
internal string MultipartFilename(string template, string fullDirPath, string extension, int partsPosition, int partsTotal, string chapterTitle)
|
public string MultipartFilename(AaxDecrypter.MultiConvertFileProperties props, string template, string fullDirPath)
|
||||||
{
|
{
|
||||||
var fileTemplate = GetFileTemplateSingle(template, libraryBook, fullDirPath, extension);
|
var fileNamingTemplate = GetFileNamingTemplate(template, libraryBookDto, fullDirPath, Path.GetExtension(props.OutputFileName));
|
||||||
|
|
||||||
fileTemplate.AddParameterReplacement(TemplateTags.ChCount, partsTotal);
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChCount, props.PartsTotal);
|
||||||
fileTemplate.AddParameterReplacement(TemplateTags.ChNumber, partsPosition);
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChNumber, props.PartsPosition);
|
||||||
fileTemplate.AddParameterReplacement(TemplateTags.ChNumber0, FileUtility.GetSequenceFormatted(partsPosition, partsTotal));
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChNumber0, FileUtility.GetSequenceFormatted(props.PartsPosition, props.PartsTotal));
|
||||||
fileTemplate.AddParameterReplacement(TemplateTags.ChTitle, chapterTitle);
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChTitle, props.Title ?? "");
|
||||||
|
|
||||||
return fileTemplate.GetFilePath();
|
return fileNamingTemplate.GetFilePath();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Func<string, int, int, AAXClean.NewSplitCallback, 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)
|
public static string GetInProgressFilename(this AudioFileStorage _, LibraryBook libraryBook, string extension)
|
||||||
=> GetCustomDirFilename(_, libraryBook, AudibleFileStorage.DecryptInProgressDirectory, extension);
|
=> GetFileNamingTemplate(Configuration.Instance.FileTemplate, libraryBook.ToDto(), AudibleFileStorage.DecryptInProgressDirectory, extension)
|
||||||
|
.GetFilePath();
|
||||||
public static string GetBooksDirectoryFilename(this AudioFileStorage _, LibraryBook libraryBook, string extension)
|
|
||||||
=> GetCustomDirFilename(_, libraryBook, AudibleFileStorage.BooksDirectory, extension);
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DownloadDecryptBook:
|
||||||
|
/// File path for where to move files into.
|
||||||
|
/// Path: directory nested inside of Books directory
|
||||||
|
/// File name: n/a
|
||||||
|
/// </summary>
|
||||||
public static string GetDestinationDirectory(this AudioFileStorage _, LibraryBook libraryBook)
|
public static string GetDestinationDirectory(this AudioFileStorage _, LibraryBook libraryBook)
|
||||||
=> GetFileTemplateSingle(Configuration.Instance.FolderTemplate, libraryBook, AudibleFileStorage.BooksDirectory, null)
|
=> GetFileNamingTemplate(Configuration.Instance.FolderTemplate, libraryBook.ToDto(), AudibleFileStorage.BooksDirectory, null)
|
||||||
.GetFilePath();
|
.GetFilePath();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PDF: audio file does not exist
|
||||||
|
/// </summary>
|
||||||
|
public static string GetBooksDirectoryFilename(this AudioFileStorage _, LibraryBook libraryBook, string extension)
|
||||||
|
=> GetFileNamingTemplate(Configuration.Instance.FileTemplate, libraryBook.ToDto(), AudibleFileStorage.BooksDirectory, extension)
|
||||||
|
.GetFilePath();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PDF: audio file already exists
|
||||||
|
/// </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)
|
||||||
=> GetFileTemplateSingle(Configuration.Instance.FileTemplate, libraryBook, dirFullPath, extension)
|
=> GetFileNamingTemplate(Configuration.Instance.FileTemplate, libraryBook.ToDto(), dirFullPath, extension)
|
||||||
.GetFilePath();
|
.GetFilePath();
|
||||||
|
|
||||||
internal static FileTemplate GetFileTemplateSingle(string template, LibraryBook libraryBook, string dirFullPath, string extension)
|
public static FileNamingTemplate GetFileNamingTemplate(string template, LibraryBookDto libraryBookDto, string dirFullPath, string extension)
|
||||||
{
|
{
|
||||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(template, nameof(template));
|
ArgumentValidator.EnsureNotNullOrWhiteSpace(template, nameof(template));
|
||||||
ArgumentValidator.EnsureNotNull(libraryBook, nameof(libraryBook));
|
ArgumentValidator.EnsureNotNull(libraryBookDto, nameof(libraryBookDto));
|
||||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(dirFullPath, nameof(dirFullPath));
|
|
||||||
|
|
||||||
var fullfilename = Path.Combine(dirFullPath, template + FileUtility.GetStandardizedExtension(extension));
|
dirFullPath = dirFullPath?.Trim() ?? "";
|
||||||
var fileTemplate = new FileTemplate(fullfilename) { IllegalCharacterReplacements = "_" };
|
var t = template + FileUtility.GetStandardizedExtension(extension);
|
||||||
|
var fullfilename = dirFullPath == "" ? t : Path.Combine(dirFullPath, t);
|
||||||
|
|
||||||
var title = libraryBook.Book.Title ?? "";
|
var fileNamingTemplate = new FileNamingTemplate(fullfilename) { IllegalCharacterReplacements = "_" };
|
||||||
|
|
||||||
fileTemplate.AddParameterReplacement(TemplateTags.Id, libraryBook.Book.AudibleProductId);
|
var title = libraryBookDto.Title ?? "";
|
||||||
fileTemplate.AddParameterReplacement(TemplateTags.Title, title);
|
var titleShort = title.IndexOf(':') < 1 ? title : title.Substring(0, title.IndexOf(':'));
|
||||||
fileTemplate.AddParameterReplacement(TemplateTags.TitleShort, title.IndexOf(':') < 1 ? title : title.Substring(0, title.IndexOf(':')));
|
|
||||||
fileTemplate.AddParameterReplacement(TemplateTags.Author, libraryBook.Book.AuthorNames);
|
|
||||||
fileTemplate.AddParameterReplacement(TemplateTags.FirstAuthor, libraryBook.Book.Authors.FirstOrDefault()?.Name);
|
|
||||||
fileTemplate.AddParameterReplacement(TemplateTags.Narrator, libraryBook.Book.NarratorNames);
|
|
||||||
fileTemplate.AddParameterReplacement(TemplateTags.FirstNarrator, libraryBook.Book.Narrators.FirstOrDefault()?.Name);
|
|
||||||
|
|
||||||
var seriesLink = libraryBook.Book.SeriesLink.FirstOrDefault();
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.Id, libraryBookDto.AudibleProductId);
|
||||||
fileTemplate.AddParameterReplacement(TemplateTags.Series, seriesLink?.Series.Name);
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.Title, title);
|
||||||
fileTemplate.AddParameterReplacement(TemplateTags.SeriesNumber, seriesLink?.Order);
|
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 fileTemplate;
|
return fileNamingTemplate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
|
using LibationFileManager;
|
||||||
|
|
||||||
namespace FileLiberator
|
namespace FileLiberator
|
||||||
{
|
{
|
||||||
@ -22,5 +23,21 @@ namespace FileLiberator
|
|||||||
var apiExtended = await AudibleUtilities.ApiExtended.CreateAsync(libraryBook.Account, libraryBook.Book.Locale);
|
var apiExtended = await AudibleUtilities.ApiExtended.CreateAsync(libraryBook.Account, libraryBook.Book.Locale);
|
||||||
return apiExtended.Api;
|
return apiExtended.Api;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static LibraryBookDto ToDto(this LibraryBook libraryBook) => new()
|
||||||
|
{
|
||||||
|
Account = libraryBook.Account,
|
||||||
|
|
||||||
|
AudibleProductId = libraryBook.Book.AudibleProductId,
|
||||||
|
Title = libraryBook.Book.Title ?? "",
|
||||||
|
Locale = libraryBook.Book.Locale,
|
||||||
|
|
||||||
|
Authors = libraryBook.Book.Authors.Select(c => c.Name).ToList(),
|
||||||
|
|
||||||
|
Narrators = libraryBook.Book.Narrators.Select(c => c.Name).ToList(),
|
||||||
|
|
||||||
|
SeriesName = libraryBook.Book.SeriesLink.FirstOrDefault()?.Series.Name,
|
||||||
|
SeriesNumber = libraryBook.Book.SeriesLink.FirstOrDefault()?.Order
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,13 +6,13 @@ using Dinah.Core;
|
|||||||
namespace FileManager
|
namespace FileManager
|
||||||
{
|
{
|
||||||
/// <summary>Get valid filename. Advanced features incl. parameterized template</summary>
|
/// <summary>Get valid filename. Advanced features incl. parameterized template</summary>
|
||||||
public class FileTemplate
|
public class FileNamingTemplate
|
||||||
{
|
{
|
||||||
/// <summary>Proposed full file path. May contain optional html-styled template tags. Eg: <name></summary>
|
/// <summary>Proposed full file path. May contain optional html-styled template tags. Eg: <name></summary>
|
||||||
public string Template { get; }
|
public string Template { get; }
|
||||||
|
|
||||||
/// <param name="template">Proposed file name with optional html-styled template tags.</param>
|
/// <param name="template">Proposed file name with optional html-styled template tags.</param>
|
||||||
public FileTemplate(string template) => Template = ArgumentValidator.EnsureNotNullOrWhiteSpace(template, nameof(template));
|
public FileNamingTemplate(string template) => Template = ArgumentValidator.EnsureNotNullOrWhiteSpace(template, nameof(template));
|
||||||
|
|
||||||
/// <summary>Optional step 1: Replace html-styled template tags with parameters. Eg {"name", "Bill Gates"} => /<name>/ => /Bill Gates/</summary>
|
/// <summary>Optional step 1: Replace html-styled template tags with parameters. Eg {"name", "Bill Gates"} => /<name>/ => /Bill Gates/</summary>
|
||||||
public Dictionary<string, object> ParameterReplacements { get; } = new Dictionary<string, object>();
|
public Dictionary<string, object> ParameterReplacements { get; } = new Dictionary<string, object>();
|
||||||
@ -170,14 +170,10 @@ namespace LibationFileManager
|
|||||||
set => setTemplate(nameof(ChapterFileTemplate), Templates.ChapterFile, value);
|
set => setTemplate(nameof(ChapterFileTemplate), Templates.ChapterFile, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string getTemplate(string settingName, Templates templ)
|
private string getTemplate(string settingName, Templates templ) => templ.GetValid(persistentDictionary.GetString(settingName));
|
||||||
{
|
|
||||||
var value = persistentDictionary.GetString(settingName)?.Trim();
|
|
||||||
return templ.IsValid(value) ? value : templ.DefaultTemplate;
|
|
||||||
}
|
|
||||||
private void setTemplate(string settingName, Templates templ, string newValue)
|
private void setTemplate(string settingName, Templates templ, string newValue)
|
||||||
{
|
{
|
||||||
var template = newValue.Trim();
|
var template = newValue?.Trim();
|
||||||
if (templ.IsValid(template))
|
if (templ.IsValid(template))
|
||||||
persistentDictionary.SetString(settingName, template);
|
persistentDictionary.SetString(settingName, template);
|
||||||
}
|
}
|
||||||
|
|||||||
29
LibationFileManager/LibraryBookDto.cs
Normal file
29
LibationFileManager/LibraryBookDto.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace LibationFileManager
|
||||||
|
{
|
||||||
|
public class BookDto
|
||||||
|
{
|
||||||
|
public string AudibleProductId { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Locale { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<string> Authors { get; set; }
|
||||||
|
public string AuthorNames => string.Join(", ", Authors);
|
||||||
|
public string FirstAuthor => Authors.FirstOrDefault();
|
||||||
|
|
||||||
|
public IEnumerable<string> Narrators { get; set; }
|
||||||
|
public string NarratorNames => string.Join(", ", Narrators);
|
||||||
|
public string FirstNarrator => Narrators.FirstOrDefault();
|
||||||
|
|
||||||
|
public string SeriesName { get; set; }
|
||||||
|
public string SeriesNumber { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LibraryBookDto : BookDto
|
||||||
|
{
|
||||||
|
public string Account { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,6 +18,12 @@ namespace LibationFileManager
|
|||||||
IsChapterOnly = isChapterOnly;
|
IsChapterOnly = isChapterOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// putting these first is the incredibly lazy way to make them show up first in the settings dialog
|
||||||
|
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 ChNumber { get; } = new TemplateTags("ch#", "Chapter number", true);
|
||||||
|
public static TemplateTags ChNumber0 { get; } = new TemplateTags("ch# 0", "Chapter number with leading zeros", true);
|
||||||
|
|
||||||
public static TemplateTags Id { get; } = new TemplateTags("id", "Audible ID");
|
public static TemplateTags Id { get; } = new TemplateTags("id", "Audible ID");
|
||||||
public static TemplateTags Title { get; } = new TemplateTags("title", "Full title");
|
public static TemplateTags Title { get; } = new TemplateTags("title", "Full title");
|
||||||
public static TemplateTags TitleShort { get; } = new TemplateTags("title short", "Title. Stop at first colon");
|
public static TemplateTags TitleShort { get; } = new TemplateTags("title short", "Title. Stop at first colon");
|
||||||
@ -28,10 +34,7 @@ namespace LibationFileManager
|
|||||||
public static TemplateTags Series { get; } = new TemplateTags("series", "Name of series");
|
public static TemplateTags Series { get; } = new TemplateTags("series", "Name of series");
|
||||||
// can't also have a leading zeros version. Too many weird edge cases. Eg: "1-4"
|
// can't also have a leading zeros version. Too many weird edge cases. Eg: "1-4"
|
||||||
public static TemplateTags SeriesNumber { get; } = new TemplateTags("series#", "Number order in series");
|
public static TemplateTags SeriesNumber { get; } = new TemplateTags("series#", "Number order in series");
|
||||||
|
public static TemplateTags Account { get; } = new TemplateTags("account", "Audible account of this book");
|
||||||
public static TemplateTags ChCount { get; } = new TemplateTags("ch count", "Number of chapters", true);
|
public static TemplateTags Locale { get; } = new TemplateTags("locale", "Region/country");
|
||||||
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 ChNumber0 { get; } = new TemplateTags("ch# 0", "Chapter number with leading zeros", true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace LibationFileManager
|
namespace LibationFileManager
|
||||||
@ -8,7 +9,7 @@ namespace LibationFileManager
|
|||||||
{
|
{
|
||||||
protected static string[] Valid => Array.Empty<string>();
|
protected static string[] Valid => Array.Empty<string>();
|
||||||
public const string ERROR_NULL_IS_INVALID = "Null template is invalid.";
|
public const string ERROR_NULL_IS_INVALID = "Null template is invalid.";
|
||||||
public const string ERROR_FULL_PATH_IS_INVALID = @"No full paths allowed. Eg: should not start with C:\";
|
public const string ERROR_FULL_PATH_IS_INVALID = @"No colons or full paths allowed. Eg: should not start with C:\";
|
||||||
public const string ERROR_INVALID_FILE_NAME_CHAR = @"Only file name friendly characters allowed. Eg: no colons or slashes";
|
public const string ERROR_INVALID_FILE_NAME_CHAR = @"Only file name friendly characters allowed. Eg: no colons or slashes";
|
||||||
|
|
||||||
public const string WARNING_EMPTY = "Template is empty.";
|
public const string WARNING_EMPTY = "Template is empty.";
|
||||||
@ -26,6 +27,40 @@ namespace LibationFileManager
|
|||||||
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)
|
||||||
|
{
|
||||||
|
var value = configValue?.Trim();
|
||||||
|
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();
|
||||||
|
|
||||||
@ -51,7 +86,7 @@ namespace LibationFileManager
|
|||||||
|
|
||||||
public static bool ContainsTag(string template, string tag) => template.Contains($"<{tag}>");
|
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.
|
||||||
|
|
||||||
@ -60,15 +95,15 @@ namespace LibationFileManager
|
|||||||
return new[] { ERROR_NULL_IS_INVALID };
|
return new[] { ERROR_NULL_IS_INVALID };
|
||||||
|
|
||||||
if (template.Contains(':')
|
if (template.Contains(':')
|
||||||
|| template.Contains(System.IO.Path.DirectorySeparatorChar)
|
|| template.Contains(Path.DirectorySeparatorChar)
|
||||||
|| template.Contains(System.IO.Path.AltDirectorySeparatorChar)
|
|| template.Contains(Path.AltDirectorySeparatorChar)
|
||||||
)
|
)
|
||||||
return new[] { ERROR_INVALID_FILE_NAME_CHAR };
|
return new[] { ERROR_INVALID_FILE_NAME_CHAR };
|
||||||
|
|
||||||
return Valid;
|
return Valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IEnumerable<string> getWarnings(string template)
|
protected IEnumerable<string> GetStandardWarnings(string template)
|
||||||
{
|
{
|
||||||
var warnings = GetErrors(template).ToList();
|
var warnings = GetErrors(template).ToList();
|
||||||
if (template is null)
|
if (template is null)
|
||||||
@ -108,7 +143,7 @@ namespace LibationFileManager
|
|||||||
return Valid;
|
return Valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> GetWarnings(string template) => getWarnings(template);
|
public override IEnumerable<string> GetWarnings(string template) => GetStandardWarnings(template);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FileTemplate : Templates
|
private class FileTemplate : Templates
|
||||||
@ -118,9 +153,9 @@ namespace LibationFileManager
|
|||||||
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;
|
||||||
|
|
||||||
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) => getWarnings(template);
|
public override IEnumerable<string> GetWarnings(string template) => GetStandardWarnings(template);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ChapterFileTemplate : Templates
|
private class ChapterFileTemplate : Templates
|
||||||
@ -130,11 +165,11 @@ namespace LibationFileManager
|
|||||||
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;
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
var warnings = getWarnings(template).ToList();
|
var warnings = GetStandardWarnings(template).ToList();
|
||||||
if (template is null)
|
if (template is null)
|
||||||
return warnings;
|
return warnings;
|
||||||
|
|
||||||
|
|||||||
13
LibationFileManager/UtilityExtensions.cs
Normal file
13
LibationFileManager/UtilityExtensions.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using FileManager;
|
||||||
|
|
||||||
|
namespace LibationFileManager
|
||||||
|
{
|
||||||
|
public static class UtilityExtensions
|
||||||
|
{
|
||||||
|
public static void AddParameterReplacement(this FileNamingTemplate fileNamingTemplate, TemplateTags templateTags, object value)
|
||||||
|
=> fileNamingTemplate.AddParameterReplacement(templateTags.TagName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -33,16 +33,17 @@
|
|||||||
this.templateTb = new System.Windows.Forms.TextBox();
|
this.templateTb = new System.Windows.Forms.TextBox();
|
||||||
this.templateLbl = new System.Windows.Forms.Label();
|
this.templateLbl = new System.Windows.Forms.Label();
|
||||||
this.resetToDefaultBtn = new System.Windows.Forms.Button();
|
this.resetToDefaultBtn = new System.Windows.Forms.Button();
|
||||||
this.outputTb = new System.Windows.Forms.TextBox();
|
|
||||||
this.listView1 = new System.Windows.Forms.ListView();
|
this.listView1 = new System.Windows.Forms.ListView();
|
||||||
this.columnHeader1 = new System.Windows.Forms.ColumnHeader();
|
this.columnHeader1 = new System.Windows.Forms.ColumnHeader();
|
||||||
this.columnHeader2 = new System.Windows.Forms.ColumnHeader();
|
this.columnHeader2 = new System.Windows.Forms.ColumnHeader();
|
||||||
|
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
|
||||||
|
this.warningsLbl = new System.Windows.Forms.Label();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// saveBtn
|
// saveBtn
|
||||||
//
|
//
|
||||||
this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.saveBtn.Location = new System.Drawing.Point(714, 496);
|
this.saveBtn.Location = new System.Drawing.Point(714, 345);
|
||||||
this.saveBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
this.saveBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.saveBtn.Name = "saveBtn";
|
this.saveBtn.Name = "saveBtn";
|
||||||
this.saveBtn.Size = new System.Drawing.Size(88, 27);
|
this.saveBtn.Size = new System.Drawing.Size(88, 27);
|
||||||
@ -55,7 +56,7 @@
|
|||||||
//
|
//
|
||||||
this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.cancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
this.cancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||||
this.cancelBtn.Location = new System.Drawing.Point(832, 496);
|
this.cancelBtn.Location = new System.Drawing.Point(832, 345);
|
||||||
this.cancelBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
this.cancelBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.cancelBtn.Name = "cancelBtn";
|
this.cancelBtn.Name = "cancelBtn";
|
||||||
this.cancelBtn.Size = new System.Drawing.Size(88, 27);
|
this.cancelBtn.Size = new System.Drawing.Size(88, 27);
|
||||||
@ -94,19 +95,6 @@
|
|||||||
this.resetToDefaultBtn.UseVisualStyleBackColor = true;
|
this.resetToDefaultBtn.UseVisualStyleBackColor = true;
|
||||||
this.resetToDefaultBtn.Click += new System.EventHandler(this.resetToDefaultBtn_Click);
|
this.resetToDefaultBtn.Click += new System.EventHandler(this.resetToDefaultBtn_Click);
|
||||||
//
|
//
|
||||||
// outputTb
|
|
||||||
//
|
|
||||||
this.outputTb.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
|
||||||
| System.Windows.Forms.AnchorStyles.Left)
|
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
|
||||||
this.outputTb.Location = new System.Drawing.Point(346, 56);
|
|
||||||
this.outputTb.Multiline = true;
|
|
||||||
this.outputTb.Name = "outputTb";
|
|
||||||
this.outputTb.ReadOnly = true;
|
|
||||||
this.outputTb.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
|
||||||
this.outputTb.Size = new System.Drawing.Size(574, 434);
|
|
||||||
this.outputTb.TabIndex = 4;
|
|
||||||
//
|
|
||||||
// listView1
|
// listView1
|
||||||
//
|
//
|
||||||
this.listView1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
this.listView1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
@ -117,8 +105,8 @@
|
|||||||
this.listView1.HideSelection = false;
|
this.listView1.HideSelection = false;
|
||||||
this.listView1.Location = new System.Drawing.Point(12, 56);
|
this.listView1.Location = new System.Drawing.Point(12, 56);
|
||||||
this.listView1.Name = "listView1";
|
this.listView1.Name = "listView1";
|
||||||
this.listView1.Size = new System.Drawing.Size(328, 434);
|
this.listView1.Size = new System.Drawing.Size(328, 283);
|
||||||
this.listView1.TabIndex = 100;
|
this.listView1.TabIndex = 3;
|
||||||
this.listView1.UseCompatibleStateImageBehavior = false;
|
this.listView1.UseCompatibleStateImageBehavior = false;
|
||||||
this.listView1.View = System.Windows.Forms.View.Details;
|
this.listView1.View = System.Windows.Forms.View.Details;
|
||||||
//
|
//
|
||||||
@ -132,15 +120,41 @@
|
|||||||
this.columnHeader2.Text = "Description";
|
this.columnHeader2.Text = "Description";
|
||||||
this.columnHeader2.Width = 230;
|
this.columnHeader2.Width = 230;
|
||||||
//
|
//
|
||||||
|
// richTextBox1
|
||||||
|
//
|
||||||
|
this.richTextBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.richTextBox1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||||
|
this.richTextBox1.Location = new System.Drawing.Point(346, 56);
|
||||||
|
this.richTextBox1.Name = "richTextBox1";
|
||||||
|
this.richTextBox1.ReadOnly = true;
|
||||||
|
this.richTextBox1.Size = new System.Drawing.Size(574, 203);
|
||||||
|
this.richTextBox1.TabIndex = 4;
|
||||||
|
this.richTextBox1.Text = "";
|
||||||
|
//
|
||||||
|
// warningsLbl
|
||||||
|
//
|
||||||
|
this.warningsLbl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.warningsLbl.AutoSize = true;
|
||||||
|
this.warningsLbl.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
|
||||||
|
this.warningsLbl.ForeColor = System.Drawing.Color.Firebrick;
|
||||||
|
this.warningsLbl.Location = new System.Drawing.Point(346, 262);
|
||||||
|
this.warningsLbl.Name = "warningsLbl";
|
||||||
|
this.warningsLbl.Size = new System.Drawing.Size(40, 15);
|
||||||
|
this.warningsLbl.TabIndex = 100;
|
||||||
|
this.warningsLbl.Text = "label1";
|
||||||
|
//
|
||||||
// EditTemplateDialog
|
// EditTemplateDialog
|
||||||
//
|
//
|
||||||
this.AcceptButton = this.saveBtn;
|
this.AcceptButton = this.saveBtn;
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.CancelButton = this.cancelBtn;
|
this.CancelButton = this.cancelBtn;
|
||||||
this.ClientSize = new System.Drawing.Size(933, 539);
|
this.ClientSize = new System.Drawing.Size(933, 388);
|
||||||
|
this.Controls.Add(this.warningsLbl);
|
||||||
|
this.Controls.Add(this.richTextBox1);
|
||||||
this.Controls.Add(this.listView1);
|
this.Controls.Add(this.listView1);
|
||||||
this.Controls.Add(this.outputTb);
|
|
||||||
this.Controls.Add(this.resetToDefaultBtn);
|
this.Controls.Add(this.resetToDefaultBtn);
|
||||||
this.Controls.Add(this.templateLbl);
|
this.Controls.Add(this.templateLbl);
|
||||||
this.Controls.Add(this.templateTb);
|
this.Controls.Add(this.templateTb);
|
||||||
@ -163,9 +177,10 @@
|
|||||||
private System.Windows.Forms.TextBox templateTb;
|
private System.Windows.Forms.TextBox templateTb;
|
||||||
private System.Windows.Forms.Label templateLbl;
|
private System.Windows.Forms.Label templateLbl;
|
||||||
private System.Windows.Forms.Button resetToDefaultBtn;
|
private System.Windows.Forms.Button resetToDefaultBtn;
|
||||||
private System.Windows.Forms.TextBox outputTb;
|
|
||||||
private System.Windows.Forms.ListView listView1;
|
private System.Windows.Forms.ListView listView1;
|
||||||
private System.Windows.Forms.ColumnHeader columnHeader1;
|
private System.Windows.Forms.ColumnHeader columnHeader1;
|
||||||
private System.Windows.Forms.ColumnHeader columnHeader2;
|
private System.Windows.Forms.ColumnHeader columnHeader2;
|
||||||
|
private System.Windows.Forms.RichTextBox richTextBox1;
|
||||||
|
private System.Windows.Forms.Label warningsLbl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -10,8 +10,19 @@ namespace LibationWinForms.Dialogs
|
|||||||
{
|
{
|
||||||
public partial class EditTemplateDialog : Form
|
public partial class EditTemplateDialog : Form
|
||||||
{
|
{
|
||||||
|
// final valid value
|
||||||
public string TemplateText { get; private set; }
|
public string TemplateText { get; private set; }
|
||||||
|
|
||||||
|
// work-in-progress. not guaranteed to be valid
|
||||||
|
private string _workingTemplateText;
|
||||||
|
private string workingTemplateText
|
||||||
|
{
|
||||||
|
get => _workingTemplateText;
|
||||||
|
set => _workingTemplateText = Templates.Sanitize(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetTextBox(string value) => this.templateTb.Text = workingTemplateText = value;
|
||||||
|
|
||||||
private Configuration config { get; } = Configuration.Instance;
|
private Configuration config { get; } = Configuration.Instance;
|
||||||
|
|
||||||
private Templates template { get; }
|
private Templates template { get; }
|
||||||
@ -35,108 +46,124 @@ namespace LibationWinForms.Dialogs
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
warningsLbl.Text = "";
|
||||||
|
|
||||||
this.Text = $"Edit {template.Name}";
|
this.Text = $"Edit {template.Name}";
|
||||||
|
|
||||||
this.templateLbl.Text = template.Description;
|
this.templateLbl.Text = template.Description;
|
||||||
this.templateTb.Text = inputTemplateText;
|
resetTextBox(inputTemplateText);
|
||||||
|
|
||||||
// populate list view
|
// populate list view
|
||||||
foreach (var tag in template.GetTemplateTags())
|
foreach (var tag in template.GetTemplateTags())
|
||||||
listView1.Items.Add(new ListViewItem(new[] { $"<{tag.TagName}>", tag.Description }));
|
listView1.Items.Add(new ListViewItem(new[] { $"<{tag.TagName}>", tag.Description }));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetToDefaultBtn_Click(object sender, EventArgs e) => templateTb.Text = template.DefaultTemplate;
|
private void resetToDefaultBtn_Click(object sender, EventArgs e) => resetTextBox(template.DefaultTemplate);
|
||||||
|
|
||||||
private void templateTb_TextChanged(object sender, EventArgs e)
|
private void templateTb_TextChanged(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var t = templateTb.Text;
|
workingTemplateText = templateTb.Text;
|
||||||
|
|
||||||
var warnings
|
var isFolder = template == Templates.Folder;
|
||||||
= !template.HasWarnings(t)
|
|
||||||
? ""
|
|
||||||
: "Warnings:\r\n" +
|
|
||||||
template
|
|
||||||
.GetWarnings(t)
|
|
||||||
.Select(err => $"- {err}")
|
|
||||||
.Aggregate((a, b) => $"{a}\r\n{b}");
|
|
||||||
|
|
||||||
|
var libraryBookDto = new LibraryBookDto
|
||||||
|
{
|
||||||
|
Account = "my account",
|
||||||
|
AudibleProductId = "123456789",
|
||||||
|
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"
|
||||||
|
};
|
||||||
|
var chapterName = "A Flight for Life";
|
||||||
|
var chapterNumber = 4;
|
||||||
|
var chaptersTotal = 10;
|
||||||
|
|
||||||
var books = config.Books;
|
var books = config.Books;
|
||||||
var folderTemplate = template == Templates.Folder ? t : config.FolderTemplate;
|
var folder = FileLiberator.AudioFileStorageExt.GetFileNamingTemplate(
|
||||||
folderTemplate = folderTemplate.Trim().Trim(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }).Trim();
|
isFolder ? workingTemplateText : config.FolderTemplate,
|
||||||
var fileTemplate = template == Templates.Folder ? config.FileTemplate : t;
|
libraryBookDto,
|
||||||
fileTemplate = fileTemplate.Trim();
|
null,
|
||||||
|
null)
|
||||||
|
.GetFilePath();
|
||||||
|
var file
|
||||||
|
= (template == Templates.ChapterFile)
|
||||||
|
? new FileLiberator.AudioFileStorageExt.MultipartRenamer(libraryBookDto).MultipartFilename(
|
||||||
|
new() { OutputFileName = "", PartsPosition = chapterNumber, PartsTotal = chaptersTotal, Title = chapterName },
|
||||||
|
workingTemplateText,
|
||||||
|
"")
|
||||||
|
: FileLiberator.AudioFileStorageExt.GetFileNamingTemplate(
|
||||||
|
isFolder ? config.FileTemplate : workingTemplateText,
|
||||||
|
libraryBookDto,
|
||||||
|
null,
|
||||||
|
null)
|
||||||
|
.GetFilePath();
|
||||||
var ext = config.DecryptToLossy ? "mp3" : "m4b";
|
var ext = config.DecryptToLossy ? "mp3" : "m4b";
|
||||||
|
|
||||||
var path = Path.Combine(books, folderTemplate, $"{fileTemplate}.{ext}");
|
|
||||||
|
|
||||||
// this logic should be external
|
|
||||||
path = path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
|
||||||
var sing = $"{Path.DirectorySeparatorChar}";
|
|
||||||
var dbl = $"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}";
|
|
||||||
while (path.Contains(dbl))
|
|
||||||
path = path.Replace(dbl, sing);
|
|
||||||
|
|
||||||
// once path is finalized
|
|
||||||
const char ZERO_WIDTH_SPACE = '\u200B';
|
const char ZERO_WIDTH_SPACE = '\u200B';
|
||||||
path = path.Replace(sing, $"{ZERO_WIDTH_SPACE}{sing}");
|
var sing = $"{Path.DirectorySeparatorChar}";
|
||||||
|
|
||||||
// result: can wrap long paths. eg:
|
// result: can wrap long paths. eg:
|
||||||
// |-- LINE WRAP BOUNDARIES --|
|
// |-- LINE WRAP BOUNDARIES --|
|
||||||
// \books\author with a very <= normal line break on space between words
|
// \books\author with a very <= normal line break on space between words
|
||||||
// long name\narrator narrator
|
// long name\narrator narrator
|
||||||
// \title <= line break on the zero-with space we added before slashes
|
// \title <= line break on the zero-with space we added before slashes
|
||||||
|
string slashWrap(string val) => val.Replace(sing, $"{ZERO_WIDTH_SPACE}{sing}");
|
||||||
|
|
||||||
var book = new DataLayer.Book(
|
warningsLbl.Text
|
||||||
new DataLayer.AudibleProductId("123456789"),
|
= !template.HasWarnings(workingTemplateText)
|
||||||
"A Study in Scarlet: A Sherlock Holmes Novel",
|
? ""
|
||||||
"Fake description",
|
: "Warning:\r\n" +
|
||||||
1234,
|
template
|
||||||
DataLayer.ContentType.Product,
|
.GetWarnings(workingTemplateText)
|
||||||
new List<DataLayer.Contributor>
|
.Select(err => $"- {err}")
|
||||||
{
|
.Aggregate((a, b) => $"{a}\r\n{b}");
|
||||||
new("Arthur Conan Doyle"),
|
|
||||||
new("Stephen Fry - introductions")
|
|
||||||
},
|
|
||||||
new List<DataLayer.Contributor> { new("Stephen Fry") },
|
|
||||||
new DataLayer.Category(new DataLayer.AudibleCategoryId("cat12345"), "Mystery"),
|
|
||||||
"us"
|
|
||||||
);
|
|
||||||
var libraryBook = new DataLayer.LibraryBook(book, DateTime.Now, "my account");
|
|
||||||
|
|
||||||
outputTb.Text = @$"
|
var bold = new System.Drawing.Font(richTextBox1.Font, System.Drawing.FontStyle.Bold);
|
||||||
|
var reg = new System.Drawing.Font(richTextBox1.Font, System.Drawing.FontStyle.Regular);
|
||||||
|
|
||||||
Example:
|
richTextBox1.Clear();
|
||||||
|
richTextBox1.SelectionFont = reg;
|
||||||
|
|
||||||
{books}
|
richTextBox1.AppendText(slashWrap(books));
|
||||||
{folderTemplate}
|
richTextBox1.AppendText(sing);
|
||||||
{fileTemplate}
|
|
||||||
{ext}
|
|
||||||
{path}
|
|
||||||
|
|
||||||
{book.AudibleProductId}
|
if (isFolder)
|
||||||
{book.Title}
|
richTextBox1.SelectionFont = bold;
|
||||||
{book.AuthorNames}
|
|
||||||
{book.NarratorNames}
|
|
||||||
series: {"Sherlock Holmes"}
|
|
||||||
|
|
||||||
{warnings}
|
richTextBox1.AppendText(slashWrap(folder));
|
||||||
|
|
||||||
".Trim();
|
if (isFolder)
|
||||||
|
richTextBox1.SelectionFont = reg;
|
||||||
|
|
||||||
|
richTextBox1.AppendText(sing);
|
||||||
|
|
||||||
|
if (!isFolder)
|
||||||
|
richTextBox1.SelectionFont = bold;
|
||||||
|
|
||||||
|
richTextBox1.AppendText(file);
|
||||||
|
|
||||||
|
if (!isFolder)
|
||||||
|
richTextBox1.SelectionFont = reg;
|
||||||
|
|
||||||
|
richTextBox1.AppendText($".{ext}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveBtn_Click(object sender, EventArgs e)
|
private void saveBtn_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (!template.IsValid(templateTb.Text))
|
if (!template.IsValid(workingTemplateText))
|
||||||
{
|
{
|
||||||
var errors = template
|
var errors = template
|
||||||
.GetErrors(templateTb.Text)
|
.GetErrors(workingTemplateText)
|
||||||
.Select(err => $"- {err}")
|
.Select(err => $"- {err}")
|
||||||
.Aggregate((a, b) => $"{a}\r\n{b}");
|
.Aggregate((a, b) => $"{a}\r\n{b}");
|
||||||
MessageBox.Show($"This template text is not valid. Errors:\r\n{errors}", "Invalid", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show($"This template text is not valid. Errors:\r\n{errors}", "Invalid", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TemplateText = templateTb.Text;
|
TemplateText = workingTemplateText;
|
||||||
|
|
||||||
this.DialogResult = DialogResult.OK;
|
this.DialogResult = DialogResult.OK;
|
||||||
this.Close();
|
this.Close();
|
||||||
|
|||||||
@ -12,12 +12,18 @@ namespace AudioFileStorageExtTests
|
|||||||
{
|
{
|
||||||
public static class Shared
|
public static class Shared
|
||||||
{
|
{
|
||||||
public static DataLayer.LibraryBook GetLibraryBook(string asin)
|
public static LibationFileManager.LibraryBookDto GetLibraryBook(string asin)
|
||||||
|
=> new()
|
||||||
{
|
{
|
||||||
var book = new DataLayer.Book(new DataLayer.AudibleProductId(asin), "title", "desc", 1, DataLayer.ContentType.Product, new List<DataLayer.Contributor> { new DataLayer.Contributor("author") }, new List<DataLayer.Contributor> { new DataLayer.Contributor("narrator") }, new DataLayer.Category(new DataLayer.AudibleCategoryId("seriesId"), "name"), "us");
|
Account = "my account",
|
||||||
var libraryBook = new DataLayer.LibraryBook(book, DateTime.Now, "my us");
|
AudibleProductId = asin,
|
||||||
return libraryBook;
|
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]
|
||||||
@ -28,31 +34,25 @@ namespace AudioFileStorageExtTests
|
|||||||
[DataRow("asin", "<ch#>", @"C:\foo\", "txt", 6, 10, "chap", @"C:\foo\6.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)
|
public void Tests(string asin, string template, string dir, string ext, int pos, int total, string chapter, string expected)
|
||||||
=> new AudioFileStorageExt.MultipartRenamer(GetLibraryBook(asin))
|
=> new AudioFileStorageExt.MultipartRenamer(GetLibraryBook(asin))
|
||||||
.MultipartFilename(template, dir, ext, pos, total, chapter)
|
.MultipartFilename(new() { OutputFileName = $"xyz.{ext}", PartsPosition = pos, PartsTotal = total, Title = chapter }, template, dir)
|
||||||
.Should().Be(expected);
|
.Should().Be(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class GetFileTemplateSingle
|
public class GetFileNamingTemplate
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[DataRow(null, "asin", @"C:\", "ext")]
|
[DataRow(null, "asin", @"C:\", "ext")]
|
||||||
[DataRow("f.txt", null, @"C:\", "ext")]
|
|
||||||
[DataRow("f.txt", "asin", null, "ext")]
|
|
||||||
[ExpectedException(typeof(ArgumentNullException))]
|
[ExpectedException(typeof(ArgumentNullException))]
|
||||||
public void arg_null_exception(string template, string asin, string dirFullPath, string extension)
|
public void arg_null_exception(string template, string asin, string dirFullPath, string extension)
|
||||||
=> AudioFileStorageExt.GetFileTemplateSingle(template, GetLibraryBook(asin), dirFullPath, extension);
|
=> AudioFileStorageExt.GetFileNamingTemplate(template, GetLibraryBook(asin), dirFullPath, extension);
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[DataRow("", "asin", @"C:\foo\bar", "ext")]
|
[DataRow("", "asin", @"C:\foo\bar", "ext")]
|
||||||
[DataRow(" ", "asin", @"C:\foo\bar", "ext")]
|
[DataRow(" ", "asin", @"C:\foo\bar", "ext")]
|
||||||
[DataRow("f.txt", "", @"C:\foo\bar", "ext")]
|
|
||||||
[DataRow("f.txt", " ", @"C:\foo\bar", "ext")]
|
|
||||||
[DataRow("f.txt", "asin", "", "ext")]
|
|
||||||
[DataRow("f.txt", "asin", " ", "ext")]
|
|
||||||
[ExpectedException(typeof(ArgumentException))]
|
[ExpectedException(typeof(ArgumentException))]
|
||||||
public void arg_exception(string template, string asin, string dirFullPath, string extension)
|
public void arg_exception(string template, string asin, string dirFullPath, string extension)
|
||||||
=> AudioFileStorageExt.GetFileTemplateSingle(template, GetLibraryBook(asin), dirFullPath, extension);
|
=> AudioFileStorageExt.GetFileNamingTemplate(template, GetLibraryBook(asin), dirFullPath, extension);
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void null_extension() => Tests("f.txt", "asin", @"C:\foo\bar", null, @"C:\foo\bar\f.txt");
|
public void null_extension() => Tests("f.txt", "asin", @"C:\foo\bar", null, @"C:\foo\bar\f.txt");
|
||||||
@ -62,7 +62,7 @@ namespace AudioFileStorageExtTests
|
|||||||
[DataRow("f", "asin", @"C:\foo\bar", "ext", @"C:\foo\bar\f.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")]
|
[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)
|
public void Tests(string template, string asin, string dirFullPath, string extension, string expected)
|
||||||
=> AudioFileStorageExt.GetFileTemplateSingle(template, GetLibraryBook(asin), dirFullPath, extension)
|
=> AudioFileStorageExt.GetFileNamingTemplate(template, GetLibraryBook(asin), dirFullPath, extension)
|
||||||
.GetFilePath()
|
.GetFilePath()
|
||||||
.Should().Be(expected);
|
.Should().Be(expected);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ using FileManager;
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
namespace FileTemplateTests
|
namespace FileNamingTemplateTests
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class GetFilePath
|
public class GetFilePath
|
||||||
@ -17,7 +17,7 @@ namespace FileTemplateTests
|
|||||||
{
|
{
|
||||||
var expected = @"C:\foo\bar\my_ book LONG_1234567890_1234567890_1234567890_123 [ID123456].txt";
|
var expected = @"C:\foo\bar\my_ book LONG_1234567890_1234567890_1234567890_123 [ID123456].txt";
|
||||||
var f1 = OLD_GetValidFilename(@"C:\foo\bar", "my: book LONG_1234567890_1234567890_1234567890_12345", "txt", "ID123456");
|
var f1 = OLD_GetValidFilename(@"C:\foo\bar", "my: book LONG_1234567890_1234567890_1234567890_12345", "txt", "ID123456");
|
||||||
var f2 = NEW_GetValidFilename_FileTemplate(@"C:\foo\bar", "my: book LONG_1234567890_1234567890_1234567890_12345", "txt", "ID123456");
|
var f2 = NEW_GetValidFilename_FileNamingTemplate(@"C:\foo\bar", "my: book LONG_1234567890_1234567890_1234567890_12345", "txt", "ID123456");
|
||||||
|
|
||||||
f1.Should().Be(expected);
|
f1.Should().Be(expected);
|
||||||
f1.Should().Be(f2);
|
f1.Should().Be(f2);
|
||||||
@ -49,16 +49,16 @@ namespace FileTemplateTests
|
|||||||
|
|
||||||
return fullfilename;
|
return fullfilename;
|
||||||
}
|
}
|
||||||
private static string NEW_GetValidFilename_FileTemplate(string dirFullPath, string filename, string extension, string metadataSuffix)
|
private static string NEW_GetValidFilename_FileNamingTemplate(string dirFullPath, string filename, string extension, string metadataSuffix)
|
||||||
{
|
{
|
||||||
var template = $"<title> [<id>]";
|
var template = $"<title> [<id>]";
|
||||||
|
|
||||||
var fullfilename = Path.Combine(dirFullPath, template + FileUtility.GetStandardizedExtension(extension));
|
var fullfilename = Path.Combine(dirFullPath, template + FileUtility.GetStandardizedExtension(extension));
|
||||||
|
|
||||||
var fileTemplate = new FileTemplate(fullfilename) { IllegalCharacterReplacements = "_" };
|
var fileNamingTemplate = new FileNamingTemplate(fullfilename) { IllegalCharacterReplacements = "_" };
|
||||||
fileTemplate.AddParameterReplacement("title", filename);
|
fileNamingTemplate.AddParameterReplacement("title", filename);
|
||||||
fileTemplate.AddParameterReplacement("id", metadataSuffix);
|
fileNamingTemplate.AddParameterReplacement("id", metadataSuffix);
|
||||||
return fileTemplate.GetFilePath();
|
return fileNamingTemplate.GetFilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@ -66,7 +66,7 @@ namespace FileTemplateTests
|
|||||||
{
|
{
|
||||||
var expected = @"C:\foo\bar\my file - 002 - title.txt";
|
var expected = @"C:\foo\bar\my file - 002 - title.txt";
|
||||||
var f1 = OLD_GetMultipartFileName(@"C:\foo\bar\my file.txt", 2, 100, "title");
|
var f1 = OLD_GetMultipartFileName(@"C:\foo\bar\my file.txt", 2, 100, "title");
|
||||||
var f2 = NEW_GetMultipartFileName_FileTemplate(@"C:\foo\bar\my file.txt", 2, 100, "title");
|
var f2 = NEW_GetMultipartFileName_FileNamingTemplate(@"C:\foo\bar\my file.txt", 2, 100, "title");
|
||||||
|
|
||||||
f1.Should().Be(expected);
|
f1.Should().Be(expected);
|
||||||
f1.Should().Be(f2);
|
f1.Should().Be(f2);
|
||||||
@ -89,7 +89,7 @@ namespace FileTemplateTests
|
|||||||
var path = Path.Combine(Path.GetDirectoryName(originalPath), fileName + extension);
|
var path = Path.Combine(Path.GetDirectoryName(originalPath), fileName + extension);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
private static string NEW_GetMultipartFileName_FileTemplate(string originalPath, int partsPosition, int partsTotal, string suffix)
|
private static string NEW_GetMultipartFileName_FileNamingTemplate(string originalPath, int partsPosition, int partsTotal, string suffix)
|
||||||
{
|
{
|
||||||
// 1-9 => 1-9
|
// 1-9 => 1-9
|
||||||
// 10-99 => 01-99
|
// 10-99 => 01-99
|
||||||
@ -98,19 +98,18 @@ namespace FileTemplateTests
|
|||||||
|
|
||||||
var t = Path.ChangeExtension(originalPath, null) + " - <chapter> - <title>" + Path.GetExtension(originalPath);
|
var t = Path.ChangeExtension(originalPath, null) + " - <chapter> - <title>" + Path.GetExtension(originalPath);
|
||||||
|
|
||||||
var fileTemplate = new FileTemplate(t) { IllegalCharacterReplacements = " " };
|
var fileNamingTemplate = new FileNamingTemplate(t) { IllegalCharacterReplacements = " " };
|
||||||
fileTemplate.AddParameterReplacement("chapter", chapterCountLeadingZeros);
|
fileNamingTemplate.AddParameterReplacement("chapter", chapterCountLeadingZeros);
|
||||||
fileTemplate.AddParameterReplacement("title", suffix);
|
fileNamingTemplate.AddParameterReplacement("title", suffix);
|
||||||
|
return fileNamingTemplate.GetFilePath();
|
||||||
return fileTemplate.GetFilePath();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void remove_slashes()
|
public void remove_slashes()
|
||||||
{
|
{
|
||||||
var fileTemplate = new FileTemplate(@"\foo\<title>.txt");
|
var fileNamingTemplate = new FileNamingTemplate(@"\foo\<title>.txt");
|
||||||
fileTemplate.AddParameterReplacement("title", @"s\l/a\s/h\e/s");
|
fileNamingTemplate.AddParameterReplacement("title", @"s\l/a\s/h\e/s");
|
||||||
fileTemplate.GetFilePath().Should().Be(@"\foo\slashes.txt");
|
fileNamingTemplate.GetFilePath().Should().Be(@"\foo\slashes.txt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -24,7 +24,7 @@ STRUCTURE
|
|||||||
* 2 Utilities (domain ignorant)
|
* 2 Utilities (domain ignorant)
|
||||||
Stand-alone libraries with no knowledge of anything having to do with Libation or other programs. In theory any of these should be able to one day be converted to a nuget pkg
|
Stand-alone libraries with no knowledge of anything having to do with Libation or other programs. In theory any of these should be able to one day be converted to a nuget pkg
|
||||||
* 3 Domain Internal Utilities (db ignorant)
|
* 3 Domain Internal Utilities (db ignorant)
|
||||||
Can have knowledge of Libation concepts. Cannot access the database.
|
Cannot access the database. Can have knowledge of Libation concepts. Can even have knowledge of some db concepts, but no actual db access.
|
||||||
* 4 Domain (db)
|
* 4 Domain (db)
|
||||||
All database access
|
All database access
|
||||||
* 5 Domain Utilities (db aware)
|
* 5 Domain Utilities (db aware)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user