Merge pull request #27 from Mbucari/master

Add support for AXXC
This commit is contained in:
rmcrackan 2021-06-21 14:01:02 -04:00 committed by GitHub
commit c1f50a184a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 536 additions and 94 deletions

View File

@ -18,37 +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">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="DecryptLib\cygcrypto-1.0.0.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="DecryptLib\cyggcc_s-1.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="DecryptLib\cygmp4v2-2.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="DecryptLib\cygstdc++-6.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="DecryptLib\cygwin1.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="DecryptLib\cygz.dll">
<None Update="DecryptLib\avutil-56.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="DecryptLib\ffmpeg.exe">
@ -57,16 +39,13 @@
<None Update="DecryptLib\ffprobe.exe">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="DecryptLib\mp4trackdump.exe">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<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.

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

@ -31,6 +31,8 @@ namespace DataLayer
// mutable
public string PictureId { get; set; }
public string AudibleKey { get; set; }
public string AudibleIV { get; set; }
// book details
public bool IsAbridged { get; private set; }

View File

@ -0,0 +1,387 @@
// <auto-generated />
using System;
using DataLayer;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace DataLayer.Migrations
{
[DbContext(typeof(LibationContext))]
[Migration("20210619030017_AddAaxcDecryptionKeys")]
partial class AddAaxcDecryptionKeys
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "5.0.5");
modelBuilder.Entity("DataLayer.Book", b =>
{
b.Property<int>("BookId")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("AudibleIV")
.HasColumnType("TEXT");
b.Property<string>("AudibleKey")
.HasColumnType("TEXT");
b.Property<string>("AudibleProductId")
.HasColumnType("TEXT");
b.Property<int>("CategoryId")
.HasColumnType("INTEGER");
b.Property<DateTime?>("DatePublished")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<bool>("IsAbridged")
.HasColumnType("INTEGER");
b.Property<int>("LengthInMinutes")
.HasColumnType("INTEGER");
b.Property<string>("Locale")
.HasColumnType("TEXT");
b.Property<string>("PictureId")
.HasColumnType("TEXT");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.HasKey("BookId");
b.HasIndex("AudibleProductId");
b.HasIndex("CategoryId");
b.ToTable("Books");
});
modelBuilder.Entity("DataLayer.BookContributor", b =>
{
b.Property<int>("BookId")
.HasColumnType("INTEGER");
b.Property<int>("ContributorId")
.HasColumnType("INTEGER");
b.Property<int>("Role")
.HasColumnType("INTEGER");
b.Property<byte>("Order")
.HasColumnType("INTEGER");
b.HasKey("BookId", "ContributorId", "Role");
b.HasIndex("BookId");
b.HasIndex("ContributorId");
b.ToTable("BookContributor");
});
modelBuilder.Entity("DataLayer.Category", b =>
{
b.Property<int>("CategoryId")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("AudibleCategoryId")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<int?>("ParentCategoryCategoryId")
.HasColumnType("INTEGER");
b.HasKey("CategoryId");
b.HasIndex("AudibleCategoryId");
b.HasIndex("ParentCategoryCategoryId");
b.ToTable("Categories");
b.HasData(
new
{
CategoryId = -1,
AudibleCategoryId = "",
Name = ""
});
});
modelBuilder.Entity("DataLayer.Contributor", b =>
{
b.Property<int>("ContributorId")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("AudibleContributorId")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.HasKey("ContributorId");
b.HasIndex("Name");
b.ToTable("Contributors");
b.HasData(
new
{
ContributorId = -1,
Name = ""
});
});
modelBuilder.Entity("DataLayer.LibraryBook", b =>
{
b.Property<int>("BookId")
.HasColumnType("INTEGER");
b.Property<string>("Account")
.HasColumnType("TEXT");
b.Property<DateTime>("DateAdded")
.HasColumnType("TEXT");
b.HasKey("BookId");
b.ToTable("Library");
});
modelBuilder.Entity("DataLayer.Series", b =>
{
b.Property<int>("SeriesId")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("AudibleSeriesId")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.HasKey("SeriesId");
b.HasIndex("AudibleSeriesId");
b.ToTable("Series");
});
modelBuilder.Entity("DataLayer.SeriesBook", b =>
{
b.Property<int>("SeriesId")
.HasColumnType("INTEGER");
b.Property<int>("BookId")
.HasColumnType("INTEGER");
b.Property<float?>("Index")
.HasColumnType("REAL");
b.HasKey("SeriesId", "BookId");
b.HasIndex("BookId");
b.HasIndex("SeriesId");
b.ToTable("SeriesBook");
});
modelBuilder.Entity("DataLayer.Book", b =>
{
b.HasOne("DataLayer.Category", "Category")
.WithMany()
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.OwnsOne("DataLayer.Rating", "Rating", b1 =>
{
b1.Property<int>("BookId")
.HasColumnType("INTEGER");
b1.Property<float>("OverallRating")
.HasColumnType("REAL");
b1.Property<float>("PerformanceRating")
.HasColumnType("REAL");
b1.Property<float>("StoryRating")
.HasColumnType("REAL");
b1.HasKey("BookId");
b1.ToTable("Books");
b1.WithOwner()
.HasForeignKey("BookId");
});
b.OwnsMany("DataLayer.Supplement", "Supplements", b1 =>
{
b1.Property<int>("SupplementId")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b1.Property<int>("BookId")
.HasColumnType("INTEGER");
b1.Property<string>("Url")
.HasColumnType("TEXT");
b1.HasKey("SupplementId");
b1.HasIndex("BookId");
b1.ToTable("Supplement");
b1.WithOwner("Book")
.HasForeignKey("BookId");
b1.Navigation("Book");
});
b.OwnsOne("DataLayer.UserDefinedItem", "UserDefinedItem", b1 =>
{
b1.Property<int>("BookId")
.HasColumnType("INTEGER");
b1.Property<string>("Tags")
.HasColumnType("TEXT");
b1.HasKey("BookId");
b1.ToTable("UserDefinedItem");
b1.WithOwner("Book")
.HasForeignKey("BookId");
b1.OwnsOne("DataLayer.Rating", "Rating", b2 =>
{
b2.Property<int>("UserDefinedItemBookId")
.HasColumnType("INTEGER");
b2.Property<float>("OverallRating")
.HasColumnType("REAL");
b2.Property<float>("PerformanceRating")
.HasColumnType("REAL");
b2.Property<float>("StoryRating")
.HasColumnType("REAL");
b2.HasKey("UserDefinedItemBookId");
b2.ToTable("UserDefinedItem");
b2.WithOwner()
.HasForeignKey("UserDefinedItemBookId");
});
b1.Navigation("Book");
b1.Navigation("Rating");
});
b.Navigation("Category");
b.Navigation("Rating");
b.Navigation("Supplements");
b.Navigation("UserDefinedItem");
});
modelBuilder.Entity("DataLayer.BookContributor", b =>
{
b.HasOne("DataLayer.Book", "Book")
.WithMany("ContributorsLink")
.HasForeignKey("BookId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("DataLayer.Contributor", "Contributor")
.WithMany("BooksLink")
.HasForeignKey("ContributorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Book");
b.Navigation("Contributor");
});
modelBuilder.Entity("DataLayer.Category", b =>
{
b.HasOne("DataLayer.Category", "ParentCategory")
.WithMany()
.HasForeignKey("ParentCategoryCategoryId");
b.Navigation("ParentCategory");
});
modelBuilder.Entity("DataLayer.LibraryBook", b =>
{
b.HasOne("DataLayer.Book", "Book")
.WithOne()
.HasForeignKey("DataLayer.LibraryBook", "BookId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Book");
});
modelBuilder.Entity("DataLayer.SeriesBook", b =>
{
b.HasOne("DataLayer.Book", "Book")
.WithMany("SeriesLink")
.HasForeignKey("BookId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("DataLayer.Series", "Series")
.WithMany("BooksLink")
.HasForeignKey("SeriesId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Book");
b.Navigation("Series");
});
modelBuilder.Entity("DataLayer.Book", b =>
{
b.Navigation("ContributorsLink");
b.Navigation("SeriesLink");
});
modelBuilder.Entity("DataLayer.Contributor", b =>
{
b.Navigation("BooksLink");
});
modelBuilder.Entity("DataLayer.Series", b =>
{
b.Navigation("BooksLink");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,33 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace DataLayer.Migrations
{
public partial class AddAaxcDecryptionKeys : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "AudibleIV",
table: "Books",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "AudibleKey",
table: "Books",
type: "TEXT",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "AudibleIV",
table: "Books");
migrationBuilder.DropColumn(
name: "AudibleKey",
table: "Books");
}
}
}

View File

@ -14,7 +14,7 @@ namespace DataLayer.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.7");
.HasAnnotation("ProductVersion", "5.0.5");
modelBuilder.Entity("DataLayer.Book", b =>
{
@ -22,6 +22,12 @@ namespace DataLayer.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("AudibleIV")
.HasColumnType("TEXT");
b.Property<string>("AudibleKey")
.HasColumnType("TEXT");
b.Property<string>("AudibleProductId")
.HasColumnType("TEXT");
@ -244,6 +250,8 @@ namespace DataLayer.Migrations
b1.WithOwner("Book")
.HasForeignKey("BookId");
b1.Navigation("Book");
});
b.OwnsOne("DataLayer.UserDefinedItem", "UserDefinedItem", b1 =>
@ -282,7 +290,19 @@ namespace DataLayer.Migrations
b2.WithOwner()
.HasForeignKey("UserDefinedItemBookId");
});
b1.Navigation("Book");
b1.Navigation("Rating");
});
b.Navigation("Category");
b.Navigation("Rating");
b.Navigation("Supplements");
b.Navigation("UserDefinedItem");
});
modelBuilder.Entity("DataLayer.BookContributor", b =>
@ -298,6 +318,10 @@ namespace DataLayer.Migrations
.HasForeignKey("ContributorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Book");
b.Navigation("Contributor");
});
modelBuilder.Entity("DataLayer.Category", b =>
@ -305,6 +329,8 @@ namespace DataLayer.Migrations
b.HasOne("DataLayer.Category", "ParentCategory")
.WithMany()
.HasForeignKey("ParentCategoryCategoryId");
b.Navigation("ParentCategory");
});
modelBuilder.Entity("DataLayer.LibraryBook", b =>
@ -314,6 +340,8 @@ namespace DataLayer.Migrations
.HasForeignKey("DataLayer.LibraryBook", "BookId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Book");
});
modelBuilder.Entity("DataLayer.SeriesBook", b =>
@ -329,6 +357,27 @@ namespace DataLayer.Migrations
.HasForeignKey("SeriesId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Book");
b.Navigation("Series");
});
modelBuilder.Entity("DataLayer.Book", b =>
{
b.Navigation("ContributorsLink");
b.Navigation("SeriesLink");
});
modelBuilder.Entity("DataLayer.Contributor", b =>
{
b.Navigation("BooksLink");
});
modelBuilder.Entity("DataLayer.Series", b =>
{
b.Navigation("BooksLink");
});
#pragma warning restore 612, 618
}

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 downloadAaxcBookAsync(libraryBook, tempAaxFilename);
moveBook(libraryBook, actualFilePath);
return verifyDownload(libraryBook);
}
@ -40,16 +41,23 @@ namespace FileLiberator
libraryBook.Book.Title,
"aax",
libraryBook.Book.AudibleProductId);
private async Task<string> downloadBookAsync(LibraryBook libraryBook, string tempAaxFilename)
private async Task<string> downloadAaxcBookAsync(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) => api.DownloadAaxWorkaroundAsync(libraryBook.Book.AudibleProductId, tempAaxFilename, p));
(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

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>