diff --git a/Source/AaxDecrypter/AaxDecrypter.csproj b/Source/AaxDecrypter/AaxDecrypter.csproj
index b27b02b1..9ef266c7 100644
--- a/Source/AaxDecrypter/AaxDecrypter.csproj
+++ b/Source/AaxDecrypter/AaxDecrypter.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/Source/AaxDecrypter/AudiobookDownloadBase.cs b/Source/AaxDecrypter/AudiobookDownloadBase.cs
index 34e10579..5980fb93 100644
--- a/Source/AaxDecrypter/AudiobookDownloadBase.cs
+++ b/Source/AaxDecrypter/AudiobookDownloadBase.cs
@@ -42,10 +42,10 @@ namespace AaxDecrypter
var outDir = Path.GetDirectoryName(OutputFileName);
if (!Directory.Exists(outDir))
- throw new DirectoryNotFoundException($"Directory does not exist: {nameof(outDir)}");
+ Directory.CreateDirectory(outDir);
if (!Directory.Exists(cacheDirectory))
- throw new DirectoryNotFoundException($"Directory does not exist: {nameof(cacheDirectory)}");
+ Directory.CreateDirectory(cacheDirectory);
jsonDownloadState = Path.Combine(cacheDirectory, Path.GetFileName(Path.ChangeExtension(OutputFileName, ".json")));
TempFilePath = Path.ChangeExtension(jsonDownloadState, ".aaxc");
diff --git a/Source/FileLiberator/ConvertToMp3.cs b/Source/FileLiberator/ConvertToMp3.cs
index 58f54a0b..40077c6c 100644
--- a/Source/FileLiberator/ConvertToMp3.cs
+++ b/Source/FileLiberator/ConvertToMp3.cs
@@ -1,5 +1,6 @@
using System;
using System.IO;
+using System.Linq;
using System.Threading.Tasks;
using AAXClean;
using AAXClean.Codecs;
@@ -19,12 +20,12 @@ namespace FileLiberator
private long fileSize;
private static string Mp3FileName(string m4bPath) => Path.ChangeExtension(m4bPath ?? "", ".mp3");
- public override Task CancelAsync() => m4bBook?.CancelAsync();
+ public override Task CancelAsync() => m4bBook?.CancelAsync() ?? Task.CompletedTask;
public static bool ValidateMp3(LibraryBook libraryBook)
{
- var path = AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId);
- return path?.ToString()?.ToLower()?.EndsWith(".m4b") == true && !File.Exists(Mp3FileName(path));
+ var paths = AudibleFileStorage.Audio.GetPaths(libraryBook.Book.AudibleProductId);
+ return paths.Any(path => path?.ToString()?.ToLower()?.EndsWith(".m4b") == true && !File.Exists(Mp3FileName(path)));
}
public override bool Validate(LibraryBook libraryBook) => ValidateMp3(libraryBook);
@@ -35,33 +36,38 @@ namespace FileLiberator
try
{
- var m4bPath = AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId);
- m4bBook = new Mp4File(m4bPath, FileAccess.Read);
- m4bBook.ConversionProgressUpdate += M4bBook_ConversionProgressUpdate;
+ var m4bPaths = AudibleFileStorage.Audio.GetPaths(libraryBook.Book.AudibleProductId);
- fileSize = m4bBook.InputStream.Length;
+ foreach (var m4bPath in m4bPaths)
+ {
+ var proposedMp3Path = Mp3FileName(m4bPath);
+ if (File.Exists(proposedMp3Path) || !File.Exists(m4bPath)) continue;
- OnTitleDiscovered(m4bBook.AppleTags.Title);
- OnAuthorsDiscovered(m4bBook.AppleTags.FirstAuthor);
- OnNarratorsDiscovered(m4bBook.AppleTags.Narrator);
- OnCoverImageDiscovered(m4bBook.AppleTags.Cover);
+ m4bBook = new Mp4File(m4bPath, FileAccess.Read);
+ m4bBook.ConversionProgressUpdate += M4bBook_ConversionProgressUpdate;
- using var mp3File = File.OpenWrite(Path.GetTempFileName());
- var lameConfig = GetLameOptions(Configuration.Instance);
- var result = await Task.Run(() => m4bBook.ConvertToMp3(mp3File, lameConfig));
- m4bBook.InputStream.Close();
- mp3File.Close();
+ fileSize = m4bBook.InputStream.Length;
- var proposedMp3Path = Mp3FileName(m4bPath);
- var realMp3Path = FileUtility.SaferMoveToValidPath(mp3File.Name, proposedMp3Path);
- OnFileCreated(libraryBook, realMp3Path);
+ OnTitleDiscovered(m4bBook.AppleTags.Title);
+ OnAuthorsDiscovered(m4bBook.AppleTags.FirstAuthor);
+ OnNarratorsDiscovered(m4bBook.AppleTags.Narrator);
+ OnCoverImageDiscovered(m4bBook.AppleTags.Cover);
- if (result == ConversionResult.Failed)
- return new StatusHandler { "Conversion failed" };
- else if (result == ConversionResult.Cancelled)
- return new StatusHandler { "Cancelled" };
- else
- return new StatusHandler();
+ using var mp3File = File.OpenWrite(Path.GetTempFileName());
+ var lameConfig = GetLameOptions(Configuration.Instance);
+ var result = await Task.Run(() => m4bBook.ConvertToMp3(mp3File, lameConfig));
+ m4bBook.InputStream.Close();
+ mp3File.Close();
+
+ var realMp3Path = FileUtility.SaferMoveToValidPath(mp3File.Name, proposedMp3Path);
+ OnFileCreated(libraryBook, realMp3Path);
+
+ if (result == ConversionResult.Failed)
+ return new StatusHandler { "Conversion failed" };
+ else if (result == ConversionResult.Cancelled)
+ return new StatusHandler { "Cancelled" };
+ }
+ return new StatusHandler();
}
finally
{
diff --git a/Source/FileLiberator/DownloadDecryptBook.cs b/Source/FileLiberator/DownloadDecryptBook.cs
index 49da0437..435e3fad 100644
--- a/Source/FileLiberator/DownloadDecryptBook.cs
+++ b/Source/FileLiberator/DownloadDecryptBook.cs
@@ -21,7 +21,7 @@ namespace FileLiberator
public override bool Validate(LibraryBook libraryBook) => !libraryBook.Book.Audio_Exists();
- public override Task CancelAsync() => abDownloader?.CancelAsync();
+ public override Task CancelAsync() => abDownloader?.CancelAsync() ?? Task.CompletedTask;
public override async Task ProcessAsync(LibraryBook libraryBook)
{
diff --git a/Source/FileManager/BackgroundFileSystem.cs b/Source/FileManager/BackgroundFileSystem.cs
index c3503e2d..4f68003d 100644
--- a/Source/FileManager/BackgroundFileSystem.cs
+++ b/Source/FileManager/BackgroundFileSystem.cs
@@ -32,12 +32,18 @@ namespace FileManager
Init();
}
- public string FindFile(System.Text.RegularExpressions.Regex regex)
+ public LongPath FindFile(System.Text.RegularExpressions.Regex regex)
{
lock (fsCacheLocker)
return fsCache.FirstOrDefault(s => regex.IsMatch(s));
}
+ public List FindFiles(System.Text.RegularExpressions.Regex regex)
+ {
+ lock (fsCacheLocker)
+ return fsCache.Where(s => regex.IsMatch(s)).ToList();
+ }
+
public void RefreshFiles()
{
lock (fsCacheLocker)
diff --git a/Source/FileManager/FileUtility.cs b/Source/FileManager/FileUtility.cs
index 978613d9..f7435c91 100644
--- a/Source/FileManager/FileUtility.cs
+++ b/Source/FileManager/FileUtility.cs
@@ -90,9 +90,9 @@ namespace FileManager
var pathNoPrefix = path.PathWithoutPrefix;
+ pathNoPrefix = pathNoPrefix?.Replace(':', '꞉')?.Replace('?', '︖')?.Replace('*', '⁎');
+
pathNoPrefix = replaceInvalidChars(pathNoPrefix, illegalCharacterReplacements);
- pathNoPrefix = standardizeSlashes(pathNoPrefix);
- pathNoPrefix = replaceColons(pathNoPrefix, illegalCharacterReplacements);
pathNoPrefix = removeDoubleSlashes(pathNoPrefix);
return pathNoPrefix;
@@ -107,24 +107,6 @@ namespace FileManager
private static string replaceInvalidChars(string path, string illegalCharacterReplacements)
=> string.Join(illegalCharacterReplacements ?? "", path.Split(invalidChars));
- private static string standardizeSlashes(string path)
- => path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
-
- private static string replaceColons(string path, string illegalCharacterReplacements)
- {
- // replace all colons except within the first 2 chars
- var builder = new System.Text.StringBuilder();
- for (var i = 0; i < path.Length; i++)
- {
- var c = path[i];
- if (i >= 2 && c == ':')
- builder.Append(illegalCharacterReplacements);
- else
- builder.Append(c);
- }
- return builder.ToString();
- }
-
private static string removeDoubleSlashes(string path)
{
if (path.Length < 2)
diff --git a/Source/FileManager/LongPath.cs b/Source/FileManager/LongPath.cs
index 18f59e8b..535da6d0 100644
--- a/Source/FileManager/LongPath.cs
+++ b/Source/FileManager/LongPath.cs
@@ -27,7 +27,7 @@ namespace FileManager
//File I/O functions in the Windows API convert "/" to "\" as part of converting
//the name to an NT-style name, except when using the "\\?\" prefix
- path = path.Replace('/', '\\');
+ path = path.Replace(System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar);
if (path.StartsWith(LONG_PATH_PREFIX))
return new LongPath { Path = path };
diff --git a/Source/LibationFileManager/AudibleFileStorage.cs b/Source/LibationFileManager/AudibleFileStorage.cs
index d5c6a344..eae2ca31 100644
--- a/Source/LibationFileManager/AudibleFileStorage.cs
+++ b/Source/LibationFileManager/AudibleFileStorage.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
@@ -9,6 +10,7 @@ namespace LibationFileManager
public abstract class AudibleFileStorage
{
protected abstract LongPath GetFilePathCustom(string productId);
+ protected abstract List GetFilePathsCustom(string productId);
#region static
public static LongPath DownloadsInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DownloadsInProgress")).FullName;
@@ -57,6 +59,9 @@ namespace LibationFileManager
return firstOrNull;
}
+ public List GetPaths(string productId)
+ => GetFilePathsCustom(productId);
+
protected Regex GetBookSearchRegex(string productId)
{
var pattern = string.Format(regexTemplate, productId);
@@ -70,11 +75,14 @@ namespace LibationFileManager
internal AaxcFileStorage() : base(FileType.AAXC) { }
protected override LongPath GetFilePathCustom(string productId)
+ => GetFilePathsCustom(productId).FirstOrDefault();
+
+ protected override List GetFilePathsCustom(string productId)
{
var regex = GetBookSearchRegex(productId);
return FileUtility
.SaferEnumerateFiles(DownloadsInProgressDirectory, "*.*", SearchOption.AllDirectories)
- .FirstOrDefault(s => regex.IsMatch(s));
+ .Where(s => regex.IsMatch(s)).ToList();
}
public bool Exists(string productId) => GetFilePath(productId) is not null;
@@ -87,7 +95,11 @@ namespace LibationFileManager
private static BackgroundFileSystem BookDirectoryFiles { get; set; }
private static object bookDirectoryFilesLocker { get; } = new();
+
protected override LongPath GetFilePathCustom(string productId)
+ => GetFilePathsCustom(productId).FirstOrDefault();
+
+ protected override List GetFilePathsCustom(string productId)
{
// If user changed the BooksDirectory: reinitialize
lock (bookDirectoryFilesLocker)
@@ -95,11 +107,12 @@ namespace LibationFileManager
BookDirectoryFiles = new BackgroundFileSystem(BooksDirectory, "*.*", SearchOption.AllDirectories);
var regex = GetBookSearchRegex(productId);
- return BookDirectoryFiles.FindFile(regex);
+ return BookDirectoryFiles.FindFiles(regex);
}
public void Refresh() => BookDirectoryFiles.RefreshFiles();
public LongPath GetPath(string productId) => GetFilePath(productId);
- }
+
+ }
}
diff --git a/Source/LibationWinForms/Form1.Liberate.cs b/Source/LibationWinForms/Form1.Liberate.cs
index 619fc69a..d9ab6667 100644
--- a/Source/LibationWinForms/Form1.Liberate.cs
+++ b/Source/LibationWinForms/Form1.Liberate.cs
@@ -38,7 +38,7 @@ namespace LibationWinForms
{
SetQueueCollapseState(false);
await Task.Run(() => processBookQueue1.AddConvertMp3(ApplicationServices.DbContexts.GetLibrary_Flat_NoTracking()
- .Where(lb => lb.Book.UserDefinedItem.BookStatus is DataLayer.LiberatedStatus.Liberated)));
+ .Where(lb => lb.Book.UserDefinedItem.BookStatus is DataLayer.LiberatedStatus.Liberated && lb.Book.ContentType is DataLayer.ContentType.Product)));
}
//Only Queue Liberated books for conversion. This isn't a perfect filter, but it's better than nothing.
}
diff --git a/Source/LibationWinForms/LibationWinForms.csproj b/Source/LibationWinForms/LibationWinForms.csproj
index 7ca31c26..7fab1293 100644
--- a/Source/LibationWinForms/LibationWinForms.csproj
+++ b/Source/LibationWinForms/LibationWinForms.csproj
@@ -43,6 +43,12 @@
Form1.cs
+
+
+
+ Dialogs\SettingsDialog.cs
+
+
diff --git a/Source/LibationWinForms/ProcessQueue/ProcessBook.cs b/Source/LibationWinForms/ProcessQueue/ProcessBook.cs
index d4666e2e..0b5655f5 100644
--- a/Source/LibationWinForms/ProcessQueue/ProcessBook.cs
+++ b/Source/LibationWinForms/ProcessQueue/ProcessBook.cs
@@ -138,7 +138,7 @@ namespace LibationWinForms.ProcessQueue
return Result;
}
- public async Task Cancel()
+ public async Task CancelAsync()
{
try
{
diff --git a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs
index f698e29b..ce073ea0 100644
--- a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs
+++ b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs
@@ -212,7 +212,7 @@ namespace LibationWinForms.ProcessQueue
private void cancelAllBtn_Click(object sender, EventArgs e)
{
Queue.ClearQueue();
- Queue.Current?.Cancel();
+ Queue.Current?.CancelAsync();
virtualFlowControl2.VirtualControlCount = Queue.Count;
UpdateAllControls();
}
@@ -331,7 +331,7 @@ namespace LibationWinForms.ProcessQueue
ProcessBook item = Queue[queueIndex];
if (buttonName == nameof(panelClicked.cancelBtn))
{
- await item.Cancel();
+ await item.CancelAsync();
Queue.RemoveQueued(item);
virtualFlowControl2.VirtualControlCount = Queue.Count;
}