Merge pull request #280 from Mbucari/master

Add support for Audible's new  hierarchical chapters
This commit is contained in:
rmcrackan 2022-06-20 20:57:06 -04:00 committed by GitHub
commit d3a9ff539e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 73 additions and 25 deletions

View File

@ -165,6 +165,8 @@ namespace FileLiberator
LameConfig = GetLameOptions(config) LameConfig = GetLameOptions(config)
}; };
var chapters = getChapters(contentLic.ContentMetadata.ChapterInfo.Chapters).OrderBy(c => c.StartOffsetMs).ToList();
if (config.AllowLibationFixup || outputFormat == OutputFormat.Mp3) if (config.AllowLibationFixup || outputFormat == OutputFormat.Mp3)
{ {
long startMs = dlOptions.TrimOutputToChapterLength ? long startMs = dlOptions.TrimOutputToChapterLength ?
@ -172,15 +174,15 @@ namespace FileLiberator
dlOptions.ChapterInfo = new AAXClean.ChapterInfo(TimeSpan.FromMilliseconds(startMs)); 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; long chapLenMs = chapter.LengthMs;
if (i == 0) if (i == 0)
chapLenMs -= startMs; chapLenMs -= startMs;
if (config.StripAudibleBrandAudio && i == contentLic.ContentMetadata.ChapterInfo.Chapters.Length - 1) if (config.StripAudibleBrandAudio && i == chapters.Count - 1)
chapLenMs -= contentLic.ContentMetadata.ChapterInfo.BrandOutroDurationMs; chapLenMs -= contentLic.ContentMetadata.ChapterInfo.BrandOutroDurationMs;
dlOptions.ChapterInfo.AddChapter(chapter.Title, TimeSpan.FromMilliseconds(chapLenMs)); dlOptions.ChapterInfo.AddChapter(chapter.Title, TimeSpan.FromMilliseconds(chapLenMs));
@ -190,6 +192,25 @@ namespace FileLiberator
return dlOptions; return dlOptions;
} }
private List<AudibleApi.Common.Chapter> getChapters(IEnumerable<AudibleApi.Common.Chapter> chapters)
{
List<AudibleApi.Common.Chapter> 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) private static void downloadValidation(LibraryBook libraryBook)
{ {
string errorString(string field) string errorString(string field)

View File

@ -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;
using System.Text; using System.Text;
@ -17,15 +18,36 @@ namespace FileManager
/// <summary>Generate a valid path for this file or directory</summary> /// <summary>Generate a valid path for this file or directory</summary>
public LongPath GetFilePath(bool returnFirstExisting = false) public LongPath GetFilePath(bool returnFirstExisting = false)
{ {
int lastSlash = Template.LastIndexOf('\\'); string fileName = Template;
List<string> pathParts = new();
var directoryName = lastSlash >= 0 ? Template[..(lastSlash + 1)] : string.Empty;
var filename = lastSlash >= 0 ? Template[(lastSlash + 1)..] : Template;
List<StringBuilder> filenameParts = new();
var paramReplacements = ParameterReplacements.ToDictionary(r => $"<{formatKey(r.Key)}>", r => formatValue(r.Value)); 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<string,string> paramReplacements)
{
List<StringBuilder> filenameParts = new();
//Build the filename in parts, replacing replacement parameters with //Build the filename in parts, replacing replacement parameters with
//their values, and storing the parts in a list. //their values, and storing the parts in a list.
while (!string.IsNullOrEmpty(filename)) while (!string.IsNullOrEmpty(filename))
@ -66,10 +88,7 @@ namespace FileManager
maxEntry.Remove(maxLength - 1, 1); maxEntry.Remove(maxLength - 1, 1);
} }
return string.Join("", filenameParts);
filename = string.Join("", filenameParts);
return FileUtility.GetValidFilename(directoryName + filename, IllegalCharacterReplacements, returnFirstExisting);
} }
private string formatValue(object value) private string formatValue(object value)

View File

@ -16,7 +16,6 @@ namespace FileManager
private const int MAX_PATH = 260; private const int MAX_PATH = 260;
private const string LONG_PATH_PREFIX = "\\\\?\\"; private const string LONG_PATH_PREFIX = "\\\\?\\";
private static readonly StringBuilder longPathBuffer = new(MaxPathLength);
public string Path { get; init; } public string Path { get; init; }
public override string ToString() => Path; public override string ToString() => Path;
@ -71,18 +70,24 @@ namespace FileManager
// //
// "Based on the above settings, 8dot3 name creation is [enabled/disabled] on c:" // "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: //from an elevated command prompt:
// //
// fsutil 8dot3name set c: 0 // 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 //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 //for newly-created entries in ther file system. Existing entries made while
//8dot3 names were disabled will not be reachable by short paths. //8dot3 names were disabled will not be reachable by short paths.
if (Path is null) return null; 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 get
{ {
if (Path is null) return null; if (Path is null) return null;
StringBuilder longPathBuffer = new(MaxPathLength);
GetLongPathName(Path, longPathBuffer, MaxPathLength); GetLongPathName(Path, longPathBuffer, MaxPathLength);
return longPathBuffer.ToString(); return longPathBuffer.ToString();
} }

View File

@ -140,12 +140,11 @@ namespace LibationWinForms.Dialogs
richTextBox1.SelectionFont = reg; richTextBox1.SelectionFont = reg;
if (isChapterTitle) if (isChapterTitle)
{
richTextBox1.SelectionFont = bold; richTextBox1.SelectionFont = bold;
richTextBox1.AppendText(chapterTitle); richTextBox1.AppendText(chapterTitle);
if (isChapterTitle)
return; return;
}
richTextBox1.AppendText(slashWrap(books)); richTextBox1.AppendText(slashWrap(books));
richTextBox1.AppendText(sing); richTextBox1.AppendText(sing);

View File

@ -212,7 +212,8 @@ namespace LibationWinForms.ProcessQueue
private async void cancelAllBtn_Click(object sender, EventArgs e) private async void cancelAllBtn_Click(object sender, EventArgs e)
{ {
Queue.ClearQueue(); Queue.ClearQueue();
await Queue.Current?.CancelAsync(); if (Queue.Current is not null)
await Queue.Current.CancelAsync();
virtualFlowControl2.VirtualControlCount = Queue.Count; virtualFlowControl2.VirtualControlCount = Queue.Count;
UpdateAllControls(); UpdateAllControls();
} }
@ -331,6 +332,7 @@ namespace LibationWinForms.ProcessQueue
ProcessBook item = Queue[queueIndex]; ProcessBook item = Queue[queueIndex];
if (buttonName == nameof(panelClicked.cancelBtn)) if (buttonName == nameof(panelClicked.cancelBtn))
{ {
if (item is not null)
await item.CancelAsync(); await item.CancelAsync();
Queue.RemoveQueued(item); Queue.RemoveQueued(item);
virtualFlowControl2.VirtualControlCount = Queue.Count; virtualFlowControl2.VirtualControlCount = Queue.Count;