diff --git a/AaxDecrypter/AaxcDownloadConvertBase.cs b/AaxDecrypter/AaxcDownloadConvertBase.cs
index 4784be37..1567d8b3 100644
--- a/AaxDecrypter/AaxcDownloadConvertBase.cs
+++ b/AaxDecrypter/AaxcDownloadConvertBase.cs
@@ -10,7 +10,7 @@ namespace AaxDecrypter
protected AaxFile AaxFile;
- protected AaxcDownloadConvertBase(string outFileName, string cacheDirectory, DownloadLicense dlLic)
+ protected AaxcDownloadConvertBase(string outFileName, string cacheDirectory, DownloadOptions dlLic)
: base(outFileName, cacheDirectory, dlLic) { }
/// Setting cover art by this method will insert the art into the audiobook metadata
@@ -46,7 +46,7 @@ namespace AaxDecrypter
OnDecryptProgressUpdate(zeroProgress);
- AaxFile.SetDecryptionKey(DownloadLicense.AudibleKey, DownloadLicense.AudibleIV);
+ AaxFile.SetDecryptionKey(DownloadOptions.AudibleKey, DownloadOptions.AudibleIV);
return zeroProgress;
}
@@ -68,7 +68,7 @@ namespace AaxDecrypter
if (double.IsNormal(estTimeRemaining))
OnDecryptTimeRemaining(TimeSpan.FromSeconds(estTimeRemaining));
- var progressPercent = e.ProcessPosition.TotalSeconds / duration.TotalSeconds;
+ var progressPercent = (e.ProcessPosition / e.TotalDuration);
OnDecryptProgressUpdate(
new DownloadProgress
diff --git a/AaxDecrypter/AaxcDownloadMultiConverter.cs b/AaxDecrypter/AaxcDownloadMultiConverter.cs
index ba4dc68c..82e8d047 100644
--- a/AaxDecrypter/AaxcDownloadMultiConverter.cs
+++ b/AaxDecrypter/AaxcDownloadMultiConverter.cs
@@ -18,13 +18,13 @@ namespace AaxDecrypter
private static TimeSpan minChapterLength { get; } = TimeSpan.FromSeconds(3);
private List multiPartFilePaths { get; } = new List();
- public AaxcDownloadMultiConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic,
+ public AaxcDownloadMultiConverter(string outFileName, string cacheDirectory, DownloadOptions dlLic,
Func multipartFileNameCallback = null)
: base(outFileName, cacheDirectory, dlLic)
{
Steps = new StepSequence
{
- Name = "Download and Convert Aaxc To " + DownloadLicense.OutputFormat,
+ Name = "Download and Convert Aaxc To " + DownloadOptions.OutputFormat,
["Step 1: Get Aaxc Metadata"] = Step_GetMetadata,
["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsMultipleFilesPerChapter,
@@ -61,10 +61,10 @@ That naming may not be desirable for everyone, but it's an easy change to instea
{
var zeroProgress = Step_DownloadAudiobook_Start();
- var chapters = DownloadLicense.ChapterInfo.Chapters.ToList();
+ var chapters = DownloadOptions.ChapterInfo.Chapters.ToList();
// Ensure split files are at least minChapterLength in duration.
- var splitChapters = new ChapterInfo(DownloadLicense.ChapterInfo.StartOffset);
+ var splitChapters = new ChapterInfo(DownloadOptions.ChapterInfo.StartOffset);
var runningTotal = TimeSpan.Zero;
string title = "";
@@ -89,7 +89,7 @@ That naming may not be desirable for everyone, but it's an easy change to instea
ConversionResult result;
AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate;
- if (DownloadLicense.OutputFormat == OutputFormat.M4b)
+ if (DownloadOptions.OutputFormat == OutputFormat.M4b)
result = ConvertToMultiMp4a(splitChapters);
else
result = ConvertToMultiMp3(splitChapters);
@@ -97,13 +97,7 @@ That naming may not be desirable for everyone, but it's an easy change to instea
Step_DownloadAudiobook_End(zeroProgress);
- var success = result == ConversionResult.NoErrorsDetected;
-
- if (success)
- foreach (var path in multiPartFilePaths)
- OnFileCreated(path);
-
- return success;
+ return result == ConversionResult.NoErrorsDetected;
}
private ConversionResult ConvertToMultiMp4a(ChapterInfo splitChapters)
@@ -111,7 +105,7 @@ That naming may not be desirable for everyone, but it's an easy change to instea
var chapterCount = 0;
return AaxFile.ConvertToMultiMp4a(splitChapters, newSplitCallback =>
createOutputFileStream(++chapterCount, splitChapters, newSplitCallback),
- DownloadLicense.TrimOutputToChapterLength);
+ DownloadOptions.TrimOutputToChapterLength);
}
private ConversionResult ConvertToMultiMp3(ChapterInfo splitChapters)
@@ -121,7 +115,7 @@ That naming may not be desirable for everyone, but it's an easy change to instea
{
createOutputFileStream(++chapterCount, splitChapters, newSplitCallback);
((NAudio.Lame.LameConfig)newSplitCallback.UserState).ID3.Track = chapterCount.ToString();
- }, null, DownloadLicense.TrimOutputToChapterLength);
+ }, null, DownloadOptions.TrimOutputToChapterLength);
}
private void createOutputFileStream(int currentChapter, ChapterInfo splitChapters, NewSplitCallback newSplitCallback)
@@ -140,6 +134,8 @@ That naming may not be desirable for everyone, but it's an easy change to instea
FileUtility.SaferDelete(fileName);
newSplitCallback.OutputFile = File.Open(fileName, FileMode.OpenOrCreate);
+
+ OnFileCreated(fileName);
}
}
}
diff --git a/AaxDecrypter/AaxcDownloadSingleConverter.cs b/AaxDecrypter/AaxcDownloadSingleConverter.cs
index 0592d108..e9492d20 100644
--- a/AaxDecrypter/AaxcDownloadSingleConverter.cs
+++ b/AaxDecrypter/AaxcDownloadSingleConverter.cs
@@ -11,12 +11,12 @@ namespace AaxDecrypter
{
protected override StepSequence Steps { get; }
- public AaxcDownloadSingleConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic)
+ public AaxcDownloadSingleConverter(string outFileName, string cacheDirectory, DownloadOptions dlLic)
: base(outFileName, cacheDirectory, dlLic)
{
Steps = new StepSequence
{
- Name = "Download and Convert Aaxc To " + DownloadLicense.OutputFormat,
+ Name = "Download and Convert Aaxc To " + DownloadOptions.OutputFormat,
["Step 1: Get Aaxc Metadata"] = Step_GetMetadata,
["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsSingleFile,
@@ -32,15 +32,16 @@ namespace AaxDecrypter
FileUtility.SaferDelete(OutputFileName);
var outputFile = File.Open(OutputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
+ OnFileCreated(OutputFileName);
AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate;
var decryptionResult
- = DownloadLicense.OutputFormat == OutputFormat.M4b
- ? AaxFile.ConvertToMp4a(outputFile, DownloadLicense.ChapterInfo, DownloadLicense.TrimOutputToChapterLength)
- : AaxFile.ConvertToMp3(outputFile, null, DownloadLicense.ChapterInfo, DownloadLicense.TrimOutputToChapterLength);
+ = DownloadOptions.OutputFormat == OutputFormat.M4b
+ ? AaxFile.ConvertToMp4a(outputFile, DownloadOptions.ChapterInfo, DownloadOptions.TrimOutputToChapterLength)
+ : AaxFile.ConvertToMp3(outputFile, null, DownloadOptions.ChapterInfo, DownloadOptions.TrimOutputToChapterLength);
AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate;
- DownloadLicense.ChapterInfo = AaxFile.Chapters;
+ DownloadOptions.ChapterInfo = AaxFile.Chapters;
Step_DownloadAudiobook_End(zeroProgress);
diff --git a/AaxDecrypter/AudiobookDownloadBase.cs b/AaxDecrypter/AudiobookDownloadBase.cs
index 80ab189d..0de90a27 100644
--- a/AaxDecrypter/AudiobookDownloadBase.cs
+++ b/AaxDecrypter/AudiobookDownloadBase.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
using Dinah.Core;
using Dinah.Core.Net.Http;
@@ -20,8 +21,9 @@ namespace AaxDecrypter
public event EventHandler FileCreated;
protected bool IsCanceled { get; set; }
+
protected string OutputFileName { get; private set; }
- protected DownloadLicense DownloadLicense { get; }
+ protected DownloadOptions DownloadOptions { get; }
protected NetworkFileStream InputFileStream => (nfsPersister ??= OpenNetworkFileStream()).NetworkFileStream;
// Don't give the property a 'set'. This should have to be an obvious choice; not accidental
@@ -31,9 +33,9 @@ namespace AaxDecrypter
private NetworkFileStreamPersister nfsPersister;
private string jsonDownloadState { get; }
- private string tempFile => Path.ChangeExtension(jsonDownloadState, ".tmp");
+ public string TempFilePath { get; }
- protected AudiobookDownloadBase(string outFileName, string cacheDirectory, DownloadLicense dlLic)
+ protected AudiobookDownloadBase(string outFileName, string cacheDirectory, DownloadOptions dlLic)
{
OutputFileName = ArgumentValidator.EnsureNotNullOrWhiteSpace(outFileName, nameof(outFileName));
@@ -43,9 +45,11 @@ namespace AaxDecrypter
if (!Directory.Exists(cacheDirectory))
throw new DirectoryNotFoundException($"Directory does not exist: {nameof(cacheDirectory)}");
- jsonDownloadState = Path.Combine(cacheDirectory, Path.ChangeExtension(OutputFileName, ".json"));
- DownloadLicense = ArgumentValidator.EnsureNotNull(dlLic, nameof(dlLic));
+ jsonDownloadState = Path.Combine(cacheDirectory, Path.GetFileName(Path.ChangeExtension(OutputFileName, ".json")));
+ TempFilePath = Path.ChangeExtension(jsonDownloadState, ".tmp");
+
+ DownloadOptions = ArgumentValidator.EnsureNotNull(dlLic, nameof(dlLic));
// delete file after validation is complete
FileUtility.SaferDelete(OutputFileName);
@@ -99,7 +103,7 @@ namespace AaxDecrypter
{
var path = Path.ChangeExtension(OutputFileName, ".cue");
path = FileUtility.GetValidFilename(path);
- File.WriteAllText(path, Cue.CreateContents(Path.GetFileName(OutputFileName), DownloadLicense.ChapterInfo));
+ File.WriteAllText(path, Cue.CreateContents(Path.GetFileName(OutputFileName), DownloadOptions.ChapterInfo));
OnFileCreated(path);
}
catch (Exception ex)
@@ -111,9 +115,27 @@ namespace AaxDecrypter
protected bool Step_Cleanup()
{
- FileUtility.SaferDelete(jsonDownloadState);
- FileUtility.SaferDelete(tempFile);
- return !IsCanceled;
+ bool success = !IsCanceled;
+ if (success)
+ {
+ FileUtility.SaferDelete(jsonDownloadState);
+
+ if (DownloadOptions.RetainEncryptedFile)
+ {
+ string aaxPath = Path.ChangeExtension(TempFilePath, ".aax");
+ FileUtility.SaferMove(TempFilePath, aaxPath);
+ OnFileCreated(aaxPath);
+ }
+ else
+ FileUtility.SaferDelete(TempFilePath);
+ }
+ else
+ {
+ FileUtility.SaferDelete(OutputFileName);
+ }
+
+
+ return success;
}
private NetworkFileStreamPersister OpenNetworkFileStream()
@@ -126,13 +148,13 @@ namespace AaxDecrypter
var nfsp = new NetworkFileStreamPersister(jsonDownloadState);
// If More than ~1 hour has elapsed since getting the download url, it will expire.
// The new url will be to the same file.
- nfsp.NetworkFileStream.SetUriForSameFile(new Uri(DownloadLicense.DownloadUrl));
+ nfsp.NetworkFileStream.SetUriForSameFile(new Uri(DownloadOptions.DownloadUrl));
return nfsp;
}
catch
{
FileUtility.SaferDelete(jsonDownloadState);
- FileUtility.SaferDelete(tempFile);
+ FileUtility.SaferDelete(TempFilePath);
return NewNetworkFilePersister();
}
}
@@ -141,10 +163,10 @@ namespace AaxDecrypter
{
var headers = new System.Net.WebHeaderCollection
{
- { "User-Agent", DownloadLicense.UserAgent }
+ { "User-Agent", DownloadOptions.UserAgent }
};
- var networkFileStream = new NetworkFileStream(tempFile, new Uri(DownloadLicense.DownloadUrl), 0, headers);
+ var networkFileStream = new NetworkFileStream(TempFilePath, new Uri(DownloadOptions.DownloadUrl), 0, headers);
return new NetworkFileStreamPersister(networkFileStream, jsonDownloadState);
}
}
diff --git a/AaxDecrypter/DownloadLicense.cs b/AaxDecrypter/DownloadOptions.cs
similarity index 82%
rename from AaxDecrypter/DownloadLicense.cs
rename to AaxDecrypter/DownloadOptions.cs
index 51b69c71..6105b1ed 100644
--- a/AaxDecrypter/DownloadLicense.cs
+++ b/AaxDecrypter/DownloadOptions.cs
@@ -3,7 +3,7 @@ using Dinah.Core;
namespace AaxDecrypter
{
- public class DownloadLicense
+ public class DownloadOptions
{
public string DownloadUrl { get; }
public string UserAgent { get; }
@@ -11,9 +11,10 @@ namespace AaxDecrypter
public string AudibleIV { get; init; }
public OutputFormat OutputFormat { get; init; }
public bool TrimOutputToChapterLength { get; init; }
+ public bool RetainEncryptedFile { get; init; }
public ChapterInfo ChapterInfo { get; set; }
- public DownloadLicense(string downloadUrl, string userAgent)
+ public DownloadOptions(string downloadUrl, string userAgent)
{
DownloadUrl = ArgumentValidator.EnsureNotNullOrEmpty(downloadUrl, nameof(downloadUrl));
UserAgent = ArgumentValidator.EnsureNotNullOrEmpty(userAgent, nameof(userAgent));
diff --git a/AaxDecrypter/UnencryptedAudiobookDownloader.cs b/AaxDecrypter/UnencryptedAudiobookDownloader.cs
index 108a662f..885e839b 100644
--- a/AaxDecrypter/UnencryptedAudiobookDownloader.cs
+++ b/AaxDecrypter/UnencryptedAudiobookDownloader.cs
@@ -10,7 +10,7 @@ namespace AaxDecrypter
{
protected override StepSequence Steps { get; }
- public UnencryptedAudiobookDownloader(string outFileName, string cacheDirectory, DownloadLicense dlLic)
+ public UnencryptedAudiobookDownloader(string outFileName, string cacheDirectory, DownloadOptions dlLic)
: base(outFileName, cacheDirectory, dlLic)
{
Steps = new StepSequence
diff --git a/AppScaffolding/LibationScaffolding.cs b/AppScaffolding/LibationScaffolding.cs
index 2ce6611b..1317a097 100644
--- a/AppScaffolding/LibationScaffolding.cs
+++ b/AppScaffolding/LibationScaffolding.cs
@@ -83,6 +83,9 @@ namespace AppScaffolding
if (!config.Exists(nameof(config.StripAudibleBrandAudio)))
config.StripAudibleBrandAudio = false;
+
+ if (!config.Exists(nameof(config.RetainAaxFile)))
+ config.RetainAaxFile = false;
if (!config.Exists(nameof(config.FolderTemplate)))
config.FolderTemplate = Templates.Folder.DefaultTemplate;
diff --git a/FileLiberator/AudioFileStorageExt.cs b/FileLiberator/AudioFileStorageExt.cs
index a58bf648..049efaed 100644
--- a/FileLiberator/AudioFileStorageExt.cs
+++ b/FileLiberator/AudioFileStorageExt.cs
@@ -39,7 +39,7 @@ namespace FileLiberator
/// File name: final file name.
///
public static string GetInProgressFilename(this AudioFileStorage _, LibraryBook libraryBook, string extension)
- => Templates.File.GetFilename(libraryBook.ToDto(), AudibleFileStorage.DecryptInProgressDirectory, extension);
+ => Templates.File.GetFilename(libraryBook.ToDto(), AudibleFileStorage.DecryptInProgressDirectory, extension, returnFirstExisting: true);
///
/// PDF: audio file does not exist
diff --git a/FileLiberator/DownloadDecryptBook.cs b/FileLiberator/DownloadDecryptBook.cs
index 339a2c4b..654936f8 100644
--- a/FileLiberator/DownloadDecryptBook.cs
+++ b/FileLiberator/DownloadDecryptBook.cs
@@ -61,7 +61,12 @@ namespace FileLiberator
// decrypt failed
if (!success)
+ {
+ foreach (var tmpFile in entries)
+ FileUtility.SaferDelete(tmpFile.Path);
+
return new StatusHandler { "Decrypt failed" };
+ }
// moves new files from temp dir to final dest
var movedAudioFile = moveFilesToBooksDir(libraryBook, entries);
@@ -92,7 +97,7 @@ namespace FileLiberator
var api = await libraryBook.GetApiAsync();
var contentLic = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId);
- var audiobookDlLic = BuildDownloadLicense(config, contentLic);
+ var audiobookDlLic = BuildDownloadOptions(config, contentLic);
var outFileName = AudibleFileStorage.Audio.GetInProgressFilename(libraryBook, audiobookDlLic.OutputFormat.ToString().ToLower());
var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory;
@@ -132,28 +137,28 @@ namespace FileLiberator
}
}
- private static DownloadLicense BuildDownloadLicense(Configuration config, AudibleApi.Common.ContentLicense contentLic)
+ private static DownloadOptions BuildDownloadOptions(Configuration config, AudibleApi.Common.ContentLicense contentLic)
{
//I assume if ContentFormat == "MPEG" that the delivered file is an unencrypted mp3.
//I also assume that if DrmType != Adrm, the file will be an mp3.
//These assumptions may be wrong, and only time and bug reports will tell.
- var outputFormat =
- contentLic?.ContentMetadata?.ContentReference?.ContentFormat == "MPEG" ||
- (config.AllowLibationFixup && config.DecryptToLossy) ?
+
+ bool encrypted = contentLic.DrmType == AudibleApi.Common.DrmType.Adrm;
+
+ var outputFormat = !encrypted || (config.AllowLibationFixup && config.DecryptToLossy) ?
OutputFormat.Mp3 : OutputFormat.M4b;
- var audiobookDlLic = new DownloadLicense
+ var audiobookDlLic = new DownloadOptions
(
contentLic?.ContentMetadata?.ContentUrl?.OfflineUrl,
Resources.USER_AGENT
-
)
{
-
AudibleKey = contentLic?.Voucher?.Key,
AudibleIV = contentLic?.Voucher?.Iv,
OutputFormat = outputFormat,
- TrimOutputToChapterLength = config.StripAudibleBrandAudio
+ TrimOutputToChapterLength = config.StripAudibleBrandAudio,
+ RetainEncryptedFile = config.RetainAaxFile && encrypted
};
if (config.AllowLibationFixup || outputFormat == OutputFormat.Mp3)
diff --git a/FileManager/FileNamingTemplate.cs b/FileManager/FileNamingTemplate.cs
index 0b4dca26..1dba7e0f 100644
--- a/FileManager/FileNamingTemplate.cs
+++ b/FileManager/FileNamingTemplate.cs
@@ -29,14 +29,14 @@ namespace FileManager
public string IllegalCharacterReplacements { get; set; }
/// Generate a valid path for this file or directory
- public string GetFilePath()
+ public string GetFilePath(bool returnFirstExisting = false)
{
var filename = Template;
foreach (var r in ParameterReplacements)
filename = filename.Replace($"<{formatKey(r.Key)}>", formatValue(r.Value));
- return FileUtility.GetValidFilename(filename, IllegalCharacterReplacements);
+ return FileUtility.GetValidFilename(filename, IllegalCharacterReplacements, returnFirstExisting);
}
private static string formatKey(string key)
diff --git a/FileManager/FileUtility.cs b/FileManager/FileUtility.cs
index 025c63a8..61d44847 100644
--- a/FileManager/FileUtility.cs
+++ b/FileManager/FileUtility.cs
@@ -48,7 +48,7 @@ namespace FileManager
///
- ensure uniqueness
///
- enforce max file length
///
- public static string GetValidFilename(string path, string illegalCharacterReplacements = "")
+ public static string GetValidFilename(string path, string illegalCharacterReplacements = "", bool returnFirstExisting = false)
{
ArgumentValidator.EnsureNotNull(path, nameof(path));
@@ -69,7 +69,7 @@ namespace FileManager
fullfilename = removeInvalidWhitespace(fullfilename);
var i = 0;
- while (File.Exists(fullfilename))
+ while (File.Exists(fullfilename) && !returnFirstExisting)
{
var increm = $" ({++i})";
fullfilename = fileStem.Truncate(MAX_FILENAME_LENGTH - increm.Length - extension.Length) + increm + extension;
diff --git a/LibationFileManager/Configuration.cs b/LibationFileManager/Configuration.cs
index ae872e2c..767973fc 100644
--- a/LibationFileManager/Configuration.cs
+++ b/LibationFileManager/Configuration.cs
@@ -116,6 +116,13 @@ namespace LibationFileManager
get => persistentDictionary.GetNonString(nameof(SplitFilesByChapter));
set => persistentDictionary.SetNonString(nameof(SplitFilesByChapter), value);
}
+
+ [Description("Retain the Aax file after successfully decrypting")]
+ public bool RetainAaxFile
+ {
+ get => persistentDictionary.GetNonString(nameof(RetainAaxFile));
+ set => persistentDictionary.SetNonString(nameof(RetainAaxFile), value);
+ }
public enum BadBookAction
{
diff --git a/LibationFileManager/Templates.cs b/LibationFileManager/Templates.cs
index 4c427de5..b2826c15 100644
--- a/LibationFileManager/Templates.cs
+++ b/LibationFileManager/Templates.cs
@@ -230,9 +230,9 @@ namespace LibationFileManager
#region to file name
/// USES LIVE CONFIGURATION VALUES
- public string GetFilename(LibraryBookDto libraryBookDto, string dirFullPath, string extension)
+ public string GetFilename(LibraryBookDto libraryBookDto, string dirFullPath, string extension, bool returnFirstExisting = false)
=> getFileNamingTemplate(libraryBookDto, Configuration.Instance.FileTemplate, dirFullPath, extension)
- .GetFilePath();
+ .GetFilePath(returnFirstExisting);
#endregion
}