diff --git a/Source/FileLiberator/DownloadDecryptBook.cs b/Source/FileLiberator/DownloadDecryptBook.cs index 79cc5bf8..906acca3 100644 --- a/Source/FileLiberator/DownloadDecryptBook.cs +++ b/Source/FileLiberator/DownloadDecryptBook.cs @@ -165,6 +165,8 @@ namespace FileLiberator LameConfig = GetLameOptions(config) }; + var chapters = getChapters(contentLic.ContentMetadata.ChapterInfo.Chapters).OrderBy(c => c.StartOffsetMs).ToList(); + if (config.AllowLibationFixup || outputFormat == OutputFormat.Mp3) { long startMs = dlOptions.TrimOutputToChapterLength ? @@ -172,15 +174,15 @@ namespace FileLiberator dlOptions.ChapterInfo = new AAXClean.ChapterInfo(TimeSpan.FromMilliseconds(startMs)); - for (int i = 0; i < contentLic.ContentMetadata.ChapterInfo.Chapters.Length; i++) + for (int i = 0; i < chapters.Count; i++) { - var chapter = contentLic.ContentMetadata.ChapterInfo.Chapters[i]; + var chapter = chapters[i]; long chapLenMs = chapter.LengthMs; if (i == 0) chapLenMs -= startMs; - if (config.StripAudibleBrandAudio && i == contentLic.ContentMetadata.ChapterInfo.Chapters.Length - 1) + if (config.StripAudibleBrandAudio && i == chapters.Count - 1) chapLenMs -= contentLic.ContentMetadata.ChapterInfo.BrandOutroDurationMs; dlOptions.ChapterInfo.AddChapter(chapter.Title, TimeSpan.FromMilliseconds(chapLenMs)); @@ -190,6 +192,25 @@ namespace FileLiberator return dlOptions; } + private List getChapters(IEnumerable chapters) + { + List chaps = chapters.ToList(); + + foreach (var c in chapters) + { + if (c.Chapters is not null) + { + var children = getChapters(c.Chapters); + + foreach (var child in children) + child.Title = string.IsNullOrEmpty(c.Title) ? child.Title : $"{c.Title}: {child.Title}"; + + chaps.AddRange(children); + } + } + return chaps; + } + private static void downloadValidation(LibraryBook libraryBook) { string errorString(string field) diff --git a/Source/FileManager/FileNamingTemplate.cs b/Source/FileManager/FileNamingTemplate.cs index 642f5b25..ae9b92c7 100644 --- a/Source/FileManager/FileNamingTemplate.cs +++ b/Source/FileManager/FileNamingTemplate.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; @@ -17,18 +18,39 @@ namespace FileManager /// Generate a valid path for this file or directory public LongPath GetFilePath(bool returnFirstExisting = false) { - int lastSlash = Template.LastIndexOf('\\'); - - var directoryName = lastSlash >= 0 ? Template[..(lastSlash + 1)] : string.Empty; - var filename = lastSlash >= 0 ? Template[(lastSlash + 1)..] : Template; - - List filenameParts = new(); + string fileName = Template; + List pathParts = new(); var paramReplacements = ParameterReplacements.ToDictionary(r => $"<{formatKey(r.Key)}>", r => formatValue(r.Value)); + while (!string.IsNullOrEmpty(fileName)) + { + var file = Path.GetFileName(fileName); + + if (Path.IsPathRooted(Template) && file == string.Empty) + { + pathParts.Add(fileName); + break; + } + else + { + file = replaceFileName(file, paramReplacements); + fileName = Path.GetDirectoryName(fileName); + pathParts.Add(file); + } + } + + pathParts.Reverse(); + + return FileUtility.GetValidFilename(Path.Join(pathParts.ToArray()), IllegalCharacterReplacements, returnFirstExisting); + } + + private string replaceFileName(string filename, Dictionary paramReplacements) + { + List filenameParts = new(); //Build the filename in parts, replacing replacement parameters with //their values, and storing the parts in a list. - while(!string.IsNullOrEmpty(filename)) + while (!string.IsNullOrEmpty(filename)) { int openIndex = filename.IndexOf('<'); int closeIndex = filename.IndexOf('>'); @@ -59,17 +81,14 @@ namespace FileManager //Remove 1 character from the end of the longest filename part until //the total filename is less than max filename length - while(filenameParts.Sum(p => p.Length) > LongPath.MaxFilenameLength) + while (filenameParts.Sum(p => p.Length) > LongPath.MaxFilenameLength) { int maxLength = filenameParts.Max(p => p.Length); var maxEntry = filenameParts.First(p => p.Length == maxLength); maxEntry.Remove(maxLength - 1, 1); } - - filename = string.Join("", filenameParts); - - return FileUtility.GetValidFilename(directoryName + filename, IllegalCharacterReplacements, returnFirstExisting); + return string.Join("", filenameParts); } private string formatValue(object value) diff --git a/Source/FileManager/LongPath.cs b/Source/FileManager/LongPath.cs index 36258da1..1c1bd08a 100644 --- a/Source/FileManager/LongPath.cs +++ b/Source/FileManager/LongPath.cs @@ -16,7 +16,6 @@ namespace FileManager private const int MAX_PATH = 260; private const string LONG_PATH_PREFIX = "\\\\?\\"; - private static readonly StringBuilder longPathBuffer = new(MaxPathLength); public string Path { get; init; } public override string ToString() => Path; @@ -71,18 +70,24 @@ namespace FileManager // // "Based on the above settings, 8dot3 name creation is [enabled/disabled] on c:" // - //To enable short names on all volumes on the system, run the following command + //To enable short names on a volume on the system, run the following command //from an elevated command prompt: // // fsutil 8dot3name set c: 0 // + //or for all volumes on the system: + // + // fsutil 8dot3name set 0 + // //Note that after enabling 8dot3 names on a volume, they will only be available //for newly-created entries in ther file system. Existing entries made while //8dot3 names were disabled will not be reachable by short paths. if (Path is null) return null; - GetShortPathName(Path, longPathBuffer, MaxPathLength); - return longPathBuffer.ToString(); + + StringBuilder shortPathBuffer = new(MaxPathLength); + GetShortPathName(Path, shortPathBuffer, MaxPathLength); + return shortPathBuffer.ToString(); } } @@ -92,6 +97,8 @@ namespace FileManager get { if (Path is null) return null; + + StringBuilder longPathBuffer = new(MaxPathLength); GetLongPathName(Path, longPathBuffer, MaxPathLength); return longPathBuffer.ToString(); } diff --git a/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs b/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs index 00ff52dc..a1ecee98 100644 --- a/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs +++ b/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs @@ -140,12 +140,11 @@ namespace LibationWinForms.Dialogs richTextBox1.SelectionFont = reg; if (isChapterTitle) + { richTextBox1.SelectionFont = bold; - - richTextBox1.AppendText(chapterTitle); - - if (isChapterTitle) + richTextBox1.AppendText(chapterTitle); return; + } richTextBox1.AppendText(slashWrap(books)); richTextBox1.AppendText(sing); diff --git a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs index 7ba46dd7..e34e8ae2 100644 --- a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs +++ b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs @@ -212,7 +212,8 @@ namespace LibationWinForms.ProcessQueue private async void cancelAllBtn_Click(object sender, EventArgs e) { Queue.ClearQueue(); - await Queue.Current?.CancelAsync(); + if (Queue.Current is not null) + await Queue.Current.CancelAsync(); virtualFlowControl2.VirtualControlCount = Queue.Count; UpdateAllControls(); } @@ -331,7 +332,8 @@ namespace LibationWinForms.ProcessQueue ProcessBook item = Queue[queueIndex]; if (buttonName == nameof(panelClicked.cancelBtn)) { - await item.CancelAsync(); + if (item is not null) + await item.CancelAsync(); Queue.RemoveQueued(item); virtualFlowControl2.VirtualControlCount = Queue.Count; }