Download and decrypt AAXC files. Upgraded ffmpeg to 4.4-19.

This commit is contained in:
Michael Bucari-Tovo 2021-06-19 00:59:39 -06:00
parent 54c21e969e
commit 310b90962c
21 changed files with 100 additions and 69 deletions

View File

@ -18,19 +18,19 @@
<None Update="DecryptLib\AtomicParsley.exe"> <None Update="DecryptLib\AtomicParsley.exe">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="DecryptLib\avcodec-57.dll"> <None Update="DecryptLib\avcodec-58.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="DecryptLib\avdevice-57.dll"> <None Update="DecryptLib\avdevice-58.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="DecryptLib\avfilter-6.dll"> <None Update="DecryptLib\avfilter-7.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="DecryptLib\avformat-57.dll"> <None Update="DecryptLib\avformat-58.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="DecryptLib\avutil-55.dll"> <None Update="DecryptLib\avutil-56.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="DecryptLib\cygcrypto-1.0.0.dll"> <None Update="DecryptLib\cygcrypto-1.0.0.dll">
@ -63,10 +63,10 @@
<None Update="DecryptLib\postproc-54.dll"> <None Update="DecryptLib\postproc-54.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="DecryptLib\swresample-2.dll"> <None Update="DecryptLib\swresample-3.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="DecryptLib\swscale-4.dll"> <None Update="DecryptLib\swscale-5.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="DecryptLib\taglib-sharp.dll"> <None Update="DecryptLib\taglib-sharp.dll">

View File

@ -48,7 +48,8 @@ namespace AaxDecrypter
public event EventHandler<int> DecryptProgressUpdate; public event EventHandler<int> DecryptProgressUpdate;
public string inputFileName { get; } public string inputFileName { get; }
public string decryptKey { get; private set; } public string audible_key { get; private set; }
public string audible_iv { get; private set; }
private StepSequence steps { get; } private StepSequence steps { get; }
public byte[] coverBytes { get; private set; } public byte[] coverBytes { get; private set; }
@ -62,20 +63,21 @@ namespace AaxDecrypter
public Tags tags { get; private set; } public Tags tags { get; private set; }
public EncodingInfo encodingInfo { get; private set; } public EncodingInfo encodingInfo { get; private set; }
private Func<Task<string>> getKeyFuncAsync { get; } public static async Task<AaxToM4bConverter> CreateAsync(string inputFile, string audible_key, string audible_iv, Chapters chapters = null)
public static async Task<AaxToM4bConverter> CreateAsync(string inputFile, string decryptKey, Func<Task<string>> getKeyFunc, Chapters chapters = null)
{ {
var converter = new AaxToM4bConverter(inputFile, decryptKey, getKeyFunc); var converter = new AaxToM4bConverter(inputFile, audible_key, audible_iv);
converter.chapters = chapters ?? new AAXChapters(inputFile); converter.chapters = chapters ?? new AAXChapters(inputFile);
await converter.prelimProcessing(); await converter.prelimProcessing();
converter.printPrelim(); converter.printPrelim();
return converter; return converter;
} }
private AaxToM4bConverter(string inputFile, string decryptKey, Func<Task<string>> getKeyFunc) private AaxToM4bConverter(string inputFile, string audible_key, string audible_iv)
{ {
ArgumentValidator.EnsureNotNullOrWhiteSpace(inputFile, nameof(inputFile)); ArgumentValidator.EnsureNotNullOrWhiteSpace(inputFile, nameof(inputFile));
ArgumentValidator.EnsureNotNullOrWhiteSpace(audible_key, nameof(audible_key));
ArgumentValidator.EnsureNotNullOrWhiteSpace(audible_iv, nameof(audible_iv));
if (!File.Exists(inputFile)) if (!File.Exists(inputFile))
throw new ArgumentNullException(nameof(inputFile), "File does not exist"); throw new ArgumentNullException(nameof(inputFile), "File does not exist");
@ -94,8 +96,8 @@ namespace AaxDecrypter
}; };
inputFileName = inputFile; inputFileName = inputFile;
this.decryptKey = decryptKey; this.audible_key = audible_key;
this.getKeyFuncAsync = getKeyFunc; this.audible_iv = audible_iv;
} }
private async Task prelimProcessing() private async Task prelimProcessing()
@ -109,17 +111,17 @@ namespace AaxDecrypter
PathLib.ToPathSafeString(tags.title) + ".m4b" PathLib.ToPathSafeString(tags.title) + ".m4b"
); );
// set default name // set default name
SetOutputFilename(defaultFilename); SetOutputFilename(defaultFilename);
await Task.Run(() => saveCover(inputFileName)); await Task.Run(() => saveCover(inputFileName));
} }
private void saveCover(string aaxFile) private void saveCover(string aaxFile)
{ {
using var file = TagLib.File.Create(aaxFile, "audio/mp4", TagLib.ReadStyle.Average); using var file = TagLib.File.Create(aaxFile, "audio/mp4", TagLib.ReadStyle.Average);
coverBytes = file.Tag.Pictures[0].Data.Data; coverBytes = file.Tag.Pictures[0].Data.Data;
} }
private void printPrelim() private void printPrelim()
{ {
@ -171,27 +173,16 @@ namespace AaxDecrypter
{ {
DecryptProgressUpdate?.Invoke(this, 0); DecryptProgressUpdate?.Invoke(this, 0);
var tempRipFile = Path.Combine(outDir, "funny.aac"); var tempRipFile = Path.Combine(outDir, "funny.mp4");
var fail = "WARNING-Decrypt failure. "; var fail = "WARNING-Decrypt failure. ";
int returnCode; int returnCode;
if (string.IsNullOrWhiteSpace(decryptKey))
{
returnCode = getKey_decrypt(tempRipFile);
}
else
{
returnCode = decrypt(tempRipFile);
if (returnCode == -99)
{
Console.WriteLine($"{fail}Incorrect decrypt key: {decryptKey}");
decryptKey = null;
returnCode = getKey_decrypt(tempRipFile);
}
}
if (returnCode == 100) returnCode = decrypt(tempRipFile);
if (returnCode == -99)
Console.WriteLine($"{fail}Incorrect decrypt key.");
else if (returnCode == 100)
Console.WriteLine($"{fail}Thread completed without changing return code. This shouldn't be possible"); Console.WriteLine($"{fail}Thread completed without changing return code. This shouldn't be possible");
else if (returnCode == 0) else if (returnCode == 0)
{ {
@ -200,8 +191,6 @@ namespace AaxDecrypter
DecryptProgressUpdate?.Invoke(this, 100); DecryptProgressUpdate?.Invoke(this, 100);
return true; return true;
} }
else if (returnCode == -99)
Console.WriteLine($"{fail}Incorrect decrypt key: {decryptKey}");
else // any other returnCode else // any other returnCode
Console.WriteLine($"{fail}Unknown failure code: {returnCode}"); Console.WriteLine($"{fail}Unknown failure code: {returnCode}");
@ -210,24 +199,16 @@ namespace AaxDecrypter
return false; return false;
} }
private int getKey_decrypt(string tempRipFile)
{
decryptKey = getKey();
return decrypt(tempRipFile);
}
// I am NOT happy about doing async this way. Async needs to be added to Step framework
string getKey() => getKeyFuncAsync().GetAwaiter().GetResult();
private int decrypt(string tempRipFile) private int decrypt(string tempRipFile)
{ {
FileExt.SafeDelete(tempRipFile); FileExt.SafeDelete(tempRipFile);
Console.WriteLine("Decrypting with key " + decryptKey); Console.WriteLine($"Decrypting with key={audible_key}, iv={audible_iv}");
var returnCode = 100; var returnCode = 100;
var thread = new Thread(() => returnCode = ngDecrypt()); var thread = new Thread((b) => returnCode = ngDecrypt(b));
thread.Start(); thread.Start(tempRipFile);
double fileLen = new FileInfo(inputFileName).Length; double fileLen = new FileInfo(inputFileName).Length;
while (thread.IsAlive && returnCode == 100) while (thread.IsAlive && returnCode == 100)
@ -244,25 +225,35 @@ namespace AaxDecrypter
return returnCode; return returnCode;
} }
private int ngDecrypt() private int ngDecrypt(object tempFileNameObj)
{ {
var tempFileName = tempFileNameObj as string;
string args = "-audible_key "
+ audible_key
+ " -audible_iv "
+ audible_iv
+ " -i "
+ "\"" + inputFileName + "\""
+ " -c:a copy -vn -sn -dn -y "
+ "\"" + tempFileName + "\"";
var info = new ProcessStartInfo var info = new ProcessStartInfo
{ {
FileName = DecryptSupportLibraries.mp4trackdumpPath, FileName = DecryptSupportLibraries.ffmpegPath,
Arguments = "-c " + encodingInfo.channels + " -r " + encodingInfo.sampleRate + " \"" + inputFileName + "\"" Arguments = args
}; };
info.EnvironmentVariables["VARIABLE"] = decryptKey;
var result = info.RunHidden(); var result = info.RunHidden();
// bad checksum -- bad decrypt key // failed to decrypt
if (result.Output.Contains("checksums mismatch, aborting!")) if (result.Error.Contains("aac bitstream error"))
return -99; return -99;
return result.ExitCode; return result.ExitCode;
} }
// temp file names for steps 3, 4, 5 // temp file names for steps 3, 4, 5
string tempChapsGuid { get; } = Guid.NewGuid().ToString().ToUpper().Replace("-", ""); string tempChapsGuid { get; } = Guid.NewGuid().ToString().ToUpper().Replace("-", "");
string tempChapsPath => Path.Combine(outDir, $"tempChaps_{tempChapsGuid}.mp4"); string tempChapsPath => Path.Combine(outDir, $"tempChaps_{tempChapsGuid}.mp4");
string mp4_file => outputFileWithNewExt(".mp4"); string mp4_file => outputFileWithNewExt(".mp4");

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -6,7 +6,7 @@ namespace AaxDecrypter
{ {
// OTHER EXTERNAL DEPENDENCIES // OTHER EXTERNAL DEPENDENCIES
// ffprobe has these pre-req.s as I'm using it: // ffprobe has these pre-req.s as I'm using it:
// avcodec-57.dll, avdevice-57.dll, avfilter-6.dll, avformat-57.dll, avutil-55.dll, postproc-54.dll, swresample-2.dll, swscale-4.dll, taglib-sharp.dll // avcodec-58.dll, avdevice-58.dll, avfilter-7.dll, avformat-58.dll, avutil-56.dll, postproc-54.dll, swresample-3.dll, swscale-5.dll, taglib-sharp.dll
// //
// something else needs the cygwin files (cyg*.dll) // something else needs the cygwin files (cyg*.dll)
@ -16,6 +16,5 @@ namespace AaxDecrypter
public static string ffmpegPath { get; } = Path.Combine(decryptLib_, "ffmpeg.exe"); public static string ffmpegPath { get; } = Path.Combine(decryptLib_, "ffmpeg.exe");
public static string ffprobePath { get; } = Path.Combine(decryptLib_, "ffprobe.exe"); public static string ffprobePath { get; } = Path.Combine(decryptLib_, "ffprobe.exe");
public static string atomicParsleyPath { get; } = Path.Combine(decryptLib_, "AtomicParsley.exe"); public static string atomicParsleyPath { get; } = Path.Combine(decryptLib_, "AtomicParsley.exe");
public static string mp4trackdumpPath { get; } = Path.Combine(decryptLib_, "mp4trackdump.exe");
} }
} }

View File

@ -119,11 +119,7 @@ namespace FileLiberator
{ {
using var persister = AudibleApiStorage.GetAccountsSettingsPersister(); using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
var account = persister var converter = await AaxToM4bConverter.CreateAsync(aaxFilename, libraryBook.Book.AudibleKey, libraryBook.Book.AudibleIV, chapters);
.AccountsSettings
.GetAccount(libraryBook.Account, libraryBook.Book.Locale);
var converter = await AaxToM4bConverter.CreateAsync(aaxFilename, account.DecryptKey, api.GetActivationBytesAsync, chapters);
converter.AppName = "Libation"; converter.AppName = "Libation";
TitleDiscovered?.Invoke(this, converter.tags.title); TitleDiscovered?.Invoke(this, converter.tags.title);
@ -143,8 +139,6 @@ namespace FileLiberator
if (!success) if (!success)
return null; return null;
account.DecryptKey = converter.decryptKey;
return converter.outputFileName; return converter.outputFileName;
} }
finally finally

View File

@ -6,7 +6,8 @@ using DataLayer;
using Dinah.Core; using Dinah.Core;
using Dinah.Core.ErrorHandling; using Dinah.Core.ErrorHandling;
using FileManager; using FileManager;
using InternalUtilities; using System.Net.Http;
using Dinah.Core.Net.Http;
namespace FileLiberator namespace FileLiberator
{ {
@ -29,7 +30,7 @@ namespace FileLiberator
public override async Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook) public override async Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook)
{ {
var tempAaxFilename = getDownloadPath(libraryBook); var tempAaxFilename = getDownloadPath(libraryBook);
var actualFilePath = await downloadBookAsync(libraryBook, tempAaxFilename); var actualFilePath = await downloadAacxBookAsync(libraryBook, tempAaxFilename);
moveBook(libraryBook, actualFilePath); moveBook(libraryBook, actualFilePath);
return verifyDownload(libraryBook); return verifyDownload(libraryBook);
} }
@ -40,7 +41,53 @@ namespace FileLiberator
libraryBook.Book.Title, libraryBook.Book.Title,
"aax", "aax",
libraryBook.Book.AudibleProductId); libraryBook.Book.AudibleProductId);
private async Task<string> downloadAacxBookAsync(LibraryBook libraryBook, string tempAaxFilename)
{
validate(libraryBook);
var api = await GetApiAsync(libraryBook);
var dlLic = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId);
libraryBook.Book.AudibleKey = dlLic.AudibleKey;
libraryBook.Book.AudibleIV = dlLic.AudibleIV;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("User-Agent", Resources.UserAgent);
var actualFilePath = await PerformDownloadAsync(
tempAaxFilename,
(p) => client.DownloadFileAsync(dlLic.DownloadUri.AbsoluteUri, tempAaxFilename, p));
System.Threading.Thread.Sleep(100);
// if bad file download, a 0-33 byte file will be created
// if service unavailable, a 52 byte string will be saved as file
var length = new FileInfo(actualFilePath).Length;
if (length > 100)
return actualFilePath;
var contents = File.ReadAllText(actualFilePath);
File.Delete(actualFilePath);
var exMsg = contents.StartsWithInsensitive(SERVICE_UNAVAILABLE)
? SERVICE_UNAVAILABLE
: "Error downloading file";
var ex = new Exception(exMsg);
Serilog.Log.Logger.Error(ex, "Download error {@DebugInfo}", new
{
libraryBook.Book.Title,
libraryBook.Book.AudibleProductId,
libraryBook.Book.Locale,
Account = libraryBook.Account?.ToMask() ?? "[empty]",
tempAaxFilename,
actualFilePath,
length,
contents
});
throw ex;
}
private async Task<string> downloadBookAsync(LibraryBook libraryBook, string tempAaxFilename) private async Task<string> downloadBookAsync(LibraryBook libraryBook, string tempAaxFilename)
{ {
validate(libraryBook); validate(libraryBook);

View File

@ -13,7 +13,7 @@
<!-- <PublishSingleFile>true</PublishSingleFile> --> <!-- <PublishSingleFile>true</PublishSingleFile> -->
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<Version>4.4.0.5</Version> <Version>4.4.0.34</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>