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

View File

@ -48,7 +48,8 @@ namespace AaxDecrypter
public event EventHandler<int> DecryptProgressUpdate;
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; }
public byte[] coverBytes { get; private set; }
@ -62,20 +63,21 @@ namespace AaxDecrypter
public Tags tags { get; private set; }
public EncodingInfo encodingInfo { get; private set; }
private Func<Task<string>> getKeyFuncAsync { get; }
public static async Task<AaxToM4bConverter> CreateAsync(string inputFile, string decryptKey, Func<Task<string>> getKeyFunc, Chapters chapters = null)
public static async Task<AaxToM4bConverter> CreateAsync(string inputFile, string audible_key, string audible_iv, 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);
await converter.prelimProcessing();
converter.printPrelim();
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))
throw new ArgumentNullException(nameof(inputFile), "File does not exist");
@ -94,8 +96,8 @@ namespace AaxDecrypter
};
inputFileName = inputFile;
this.decryptKey = decryptKey;
this.getKeyFuncAsync = getKeyFunc;
this.audible_key = audible_key;
this.audible_iv = audible_iv;
}
private async Task prelimProcessing()
@ -109,17 +111,17 @@ namespace AaxDecrypter
PathLib.ToPathSafeString(tags.title) + ".m4b"
);
// set default name
SetOutputFilename(defaultFilename);
// set default name
SetOutputFilename(defaultFilename);
await Task.Run(() => saveCover(inputFileName));
}
private void saveCover(string aaxFile)
{
using var file = TagLib.File.Create(aaxFile, "audio/mp4", TagLib.ReadStyle.Average);
coverBytes = file.Tag.Pictures[0].Data.Data;
}
using var file = TagLib.File.Create(aaxFile, "audio/mp4", TagLib.ReadStyle.Average);
coverBytes = file.Tag.Pictures[0].Data.Data;
}
private void printPrelim()
{
@ -171,27 +173,16 @@ namespace AaxDecrypter
{
DecryptProgressUpdate?.Invoke(this, 0);
var tempRipFile = Path.Combine(outDir, "funny.aac");
var tempRipFile = Path.Combine(outDir, "funny.mp4");
var fail = "WARNING-Decrypt failure. ";
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");
else if (returnCode == 0)
{
@ -200,8 +191,6 @@ namespace AaxDecrypter
DecryptProgressUpdate?.Invoke(this, 100);
return true;
}
else if (returnCode == -99)
Console.WriteLine($"{fail}Incorrect decrypt key: {decryptKey}");
else // any other returnCode
Console.WriteLine($"{fail}Unknown failure code: {returnCode}");
@ -210,24 +199,16 @@ namespace AaxDecrypter
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)
{
FileExt.SafeDelete(tempRipFile);
Console.WriteLine("Decrypting with key " + decryptKey);
Console.WriteLine($"Decrypting with key={audible_key}, iv={audible_iv}");
var returnCode = 100;
var thread = new Thread(() => returnCode = ngDecrypt());
thread.Start();
var thread = new Thread((b) => returnCode = ngDecrypt(b));
thread.Start(tempRipFile);
double fileLen = new FileInfo(inputFileName).Length;
while (thread.IsAlive && returnCode == 100)
@ -244,25 +225,35 @@ namespace AaxDecrypter
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
{
FileName = DecryptSupportLibraries.mp4trackdumpPath,
Arguments = "-c " + encodingInfo.channels + " -r " + encodingInfo.sampleRate + " \"" + inputFileName + "\""
FileName = DecryptSupportLibraries.ffmpegPath,
Arguments = args
};
info.EnvironmentVariables["VARIABLE"] = decryptKey;
var result = info.RunHidden();
// bad checksum -- bad decrypt key
if (result.Output.Contains("checksums mismatch, aborting!"))
// failed to decrypt
if (result.Error.Contains("aac bitstream error"))
return -99;
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 tempChapsPath => Path.Combine(outDir, $"tempChaps_{tempChapsGuid}.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
// 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)
@ -16,6 +16,5 @@ namespace AaxDecrypter
public static string ffmpegPath { get; } = Path.Combine(decryptLib_, "ffmpeg.exe");
public static string ffprobePath { get; } = Path.Combine(decryptLib_, "ffprobe.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();
var account = persister
.AccountsSettings
.GetAccount(libraryBook.Account, libraryBook.Book.Locale);
var converter = await AaxToM4bConverter.CreateAsync(aaxFilename, account.DecryptKey, api.GetActivationBytesAsync, chapters);
var converter = await AaxToM4bConverter.CreateAsync(aaxFilename, libraryBook.Book.AudibleKey, libraryBook.Book.AudibleIV, chapters);
converter.AppName = "Libation";
TitleDiscovered?.Invoke(this, converter.tags.title);
@ -143,8 +139,6 @@ namespace FileLiberator
if (!success)
return null;
account.DecryptKey = converter.decryptKey;
return converter.outputFileName;
}
finally

View File

@ -6,7 +6,8 @@ using DataLayer;
using Dinah.Core;
using Dinah.Core.ErrorHandling;
using FileManager;
using InternalUtilities;
using System.Net.Http;
using Dinah.Core.Net.Http;
namespace FileLiberator
{
@ -29,7 +30,7 @@ namespace FileLiberator
public override async Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook)
{
var tempAaxFilename = getDownloadPath(libraryBook);
var actualFilePath = await downloadBookAsync(libraryBook, tempAaxFilename);
var actualFilePath = await downloadAacxBookAsync(libraryBook, tempAaxFilename);
moveBook(libraryBook, actualFilePath);
return verifyDownload(libraryBook);
}
@ -40,7 +41,53 @@ namespace FileLiberator
libraryBook.Book.Title,
"aax",
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)
{
validate(libraryBook);

View File

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