diff --git a/DataLayer/EfClasses/Book.cs b/DataLayer/EfClasses/Book.cs
index dcce8c73..18d4f282 100644
--- a/DataLayer/EfClasses/Book.cs
+++ b/DataLayer/EfClasses/Book.cs
@@ -31,8 +31,6 @@ 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; }
diff --git a/DataLayer/Migrations/20210622205558_RemoveAaxcDecryptionKeys.Designer.cs b/DataLayer/Migrations/20210622205558_RemoveAaxcDecryptionKeys.Designer.cs
new file mode 100644
index 00000000..bd492aec
--- /dev/null
+++ b/DataLayer/Migrations/20210622205558_RemoveAaxcDecryptionKeys.Designer.cs
@@ -0,0 +1,381 @@
+//
+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("20210622205558_RemoveAaxcDecryptionKeys")]
+ partial class RemoveAaxcDecryptionKeys
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "5.0.5");
+
+ modelBuilder.Entity("DataLayer.Book", b =>
+ {
+ b.Property("BookId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AudibleProductId")
+ .HasColumnType("TEXT");
+
+ b.Property("CategoryId")
+ .HasColumnType("INTEGER");
+
+ b.Property("DatePublished")
+ .HasColumnType("TEXT");
+
+ b.Property("Description")
+ .HasColumnType("TEXT");
+
+ b.Property("IsAbridged")
+ .HasColumnType("INTEGER");
+
+ b.Property("LengthInMinutes")
+ .HasColumnType("INTEGER");
+
+ b.Property("Locale")
+ .HasColumnType("TEXT");
+
+ b.Property("PictureId")
+ .HasColumnType("TEXT");
+
+ b.Property("Title")
+ .HasColumnType("TEXT");
+
+ b.HasKey("BookId");
+
+ b.HasIndex("AudibleProductId");
+
+ b.HasIndex("CategoryId");
+
+ b.ToTable("Books");
+ });
+
+ modelBuilder.Entity("DataLayer.BookContributor", b =>
+ {
+ b.Property("BookId")
+ .HasColumnType("INTEGER");
+
+ b.Property("ContributorId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Role")
+ .HasColumnType("INTEGER");
+
+ b.Property("Order")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("BookId", "ContributorId", "Role");
+
+ b.HasIndex("BookId");
+
+ b.HasIndex("ContributorId");
+
+ b.ToTable("BookContributor");
+ });
+
+ modelBuilder.Entity("DataLayer.Category", b =>
+ {
+ b.Property("CategoryId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AudibleCategoryId")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasColumnType("TEXT");
+
+ b.Property("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("ContributorId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AudibleContributorId")
+ .HasColumnType("TEXT");
+
+ b.Property("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("BookId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Account")
+ .HasColumnType("TEXT");
+
+ b.Property("DateAdded")
+ .HasColumnType("TEXT");
+
+ b.HasKey("BookId");
+
+ b.ToTable("Library");
+ });
+
+ modelBuilder.Entity("DataLayer.Series", b =>
+ {
+ b.Property("SeriesId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AudibleSeriesId")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasColumnType("TEXT");
+
+ b.HasKey("SeriesId");
+
+ b.HasIndex("AudibleSeriesId");
+
+ b.ToTable("Series");
+ });
+
+ modelBuilder.Entity("DataLayer.SeriesBook", b =>
+ {
+ b.Property("SeriesId")
+ .HasColumnType("INTEGER");
+
+ b.Property("BookId")
+ .HasColumnType("INTEGER");
+
+ b.Property("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("BookId")
+ .HasColumnType("INTEGER");
+
+ b1.Property("OverallRating")
+ .HasColumnType("REAL");
+
+ b1.Property("PerformanceRating")
+ .HasColumnType("REAL");
+
+ b1.Property("StoryRating")
+ .HasColumnType("REAL");
+
+ b1.HasKey("BookId");
+
+ b1.ToTable("Books");
+
+ b1.WithOwner()
+ .HasForeignKey("BookId");
+ });
+
+ b.OwnsMany("DataLayer.Supplement", "Supplements", b1 =>
+ {
+ b1.Property("SupplementId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b1.Property("BookId")
+ .HasColumnType("INTEGER");
+
+ b1.Property("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("BookId")
+ .HasColumnType("INTEGER");
+
+ b1.Property("Tags")
+ .HasColumnType("TEXT");
+
+ b1.HasKey("BookId");
+
+ b1.ToTable("UserDefinedItem");
+
+ b1.WithOwner("Book")
+ .HasForeignKey("BookId");
+
+ b1.OwnsOne("DataLayer.Rating", "Rating", b2 =>
+ {
+ b2.Property("UserDefinedItemBookId")
+ .HasColumnType("INTEGER");
+
+ b2.Property("OverallRating")
+ .HasColumnType("REAL");
+
+ b2.Property("PerformanceRating")
+ .HasColumnType("REAL");
+
+ b2.Property("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
+ }
+ }
+}
diff --git a/DataLayer/Migrations/20210622205558_RemoveAaxcDecryptionKeys.cs b/DataLayer/Migrations/20210622205558_RemoveAaxcDecryptionKeys.cs
new file mode 100644
index 00000000..bab97a55
--- /dev/null
+++ b/DataLayer/Migrations/20210622205558_RemoveAaxcDecryptionKeys.cs
@@ -0,0 +1,33 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace DataLayer.Migrations
+{
+ public partial class RemoveAaxcDecryptionKeys : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "AudibleIV",
+ table: "Books");
+
+ migrationBuilder.DropColumn(
+ name: "AudibleKey",
+ table: "Books");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "AudibleIV",
+ table: "Books",
+ type: "TEXT",
+ nullable: true);
+
+ migrationBuilder.AddColumn(
+ name: "AudibleKey",
+ table: "Books",
+ type: "TEXT",
+ nullable: true);
+ }
+ }
+}
diff --git a/DataLayer/Migrations/LibationContextModelSnapshot.cs b/DataLayer/Migrations/LibationContextModelSnapshot.cs
index 6d65ba5f..aa084c32 100644
--- a/DataLayer/Migrations/LibationContextModelSnapshot.cs
+++ b/DataLayer/Migrations/LibationContextModelSnapshot.cs
@@ -22,12 +22,6 @@ namespace DataLayer.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
- b.Property("AudibleIV")
- .HasColumnType("TEXT");
-
- b.Property("AudibleKey")
- .HasColumnType("TEXT");
-
b.Property("AudibleProductId")
.HasColumnType("TEXT");
diff --git a/FileLiberator/DecryptBook.cs b/FileLiberator/DecryptBook.cs
index 9b2e8871..47ef3e3a 100644
--- a/FileLiberator/DecryptBook.cs
+++ b/FileLiberator/DecryptBook.cs
@@ -57,30 +57,33 @@ namespace FileLiberator
if (AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId))
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
- var api = await AudibleApiActions.GetApiAsync(libraryBook.Account, libraryBook.Book.Locale);
+ var chapters = await downloadChapterNamesAsync(libraryBook);
- var chapters = await downloadChapterNames(libraryBook, api);
-
- var outputAudioFilename = await aaxToM4bConverterDecrypt(aaxFilename, libraryBook, chapters, api);
+ var outputAudioFilename = await aaxToM4bConverterDecryptAsync(aaxFilename, libraryBook, chapters);
// decrypt failed
if (outputAudioFilename == null)
return new StatusHandler { "Decrypt failed" };
+ // moves files and returns dest dir. Do not put inside of if(RetainAaxFiles)
var destinationDir = moveFilesToBooksDir(libraryBook.Book, outputAudioFilename);
- var config = Configuration.Instance;
- if (config.RetainAaxFiles)
+ var jsonFilename = PathLib.ReplaceExtension(aaxFilename, "json");
+ if (Configuration.Instance.RetainAaxFiles)
{
var newAaxFilename = FileUtility.GetValidFilename(
destinationDir,
Path.GetFileNameWithoutExtension(aaxFilename),
"aax");
File.Move(aaxFilename, newAaxFilename);
+
+ var newJsonFilename = PathLib.ReplaceExtension(newAaxFilename, "json");
+ File.Move(jsonFilename, newJsonFilename);
}
else
{
Dinah.Core.IO.FileExt.SafeDelete(aaxFilename);
+ Dinah.Core.IO.FileExt.SafeDelete(jsonFilename);
}
var finalAudioExists = AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId);
@@ -95,10 +98,11 @@ namespace FileLiberator
}
}
- private static async Task downloadChapterNames(LibraryBook libraryBook, Api api)
+ private static async Task downloadChapterNamesAsync(LibraryBook libraryBook)
{
try
{
+ var api = await AudibleApiActions.GetApiAsync(libraryBook.Account, libraryBook.Book.Locale);
var contentMetadata = await api.GetLibraryBookMetadataAsync(libraryBook.Book.AudibleProductId);
if (contentMetadata?.ChapterInfo is null)
return null;
@@ -111,15 +115,17 @@ namespace FileLiberator
}
}
- private async Task aaxToM4bConverterDecrypt(string aaxFilename, LibraryBook libraryBook, Chapters chapters, Api api)
+ private async Task aaxToM4bConverterDecryptAsync(string aaxFilename, LibraryBook libraryBook, Chapters chapters)
{
DecryptBegin?.Invoke(this, $"Begin decrypting {aaxFilename}");
try
{
- using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
+ var jsonPath = PathLib.ReplaceExtension(aaxFilename, "json");
+ var jsonContents = File.ReadAllText(jsonPath);
+ var dlLic = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonContents);
- var converter = await AaxToM4bConverter.CreateAsync(aaxFilename, libraryBook.Book.AudibleKey, libraryBook.Book.AudibleIV, chapters);
+ var converter = await AaxToM4bConverter.CreateAsync(aaxFilename, dlLic.AudibleKey, dlLic.AudibleIV, chapters);
converter.AppName = "Libation";
TitleDiscovered?.Invoke(this, converter.tags.title);
diff --git a/FileLiberator/DownloadBook.cs b/FileLiberator/DownloadBook.cs
index 7ef272d2..cd69c2c1 100644
--- a/FileLiberator/DownloadBook.cs
+++ b/FileLiberator/DownloadBook.cs
@@ -41,6 +41,7 @@ namespace FileLiberator
libraryBook.Book.Title,
"aax",
libraryBook.Book.AudibleProductId);
+
private async Task downloadAaxcBookAsync(LibraryBook libraryBook, string tempAaxFilename)
{
validate(libraryBook);
@@ -49,24 +50,30 @@ namespace FileLiberator
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));
+ (p) => client.DownloadFileAsync(new Uri(dlLic.DownloadUrl).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;
+ // success. save json and return
if (length > 100)
- return actualFilePath;
+ {
+ // save along side book
+ var jsonPath = PathLib.ReplaceExtension(actualFilePath, "json");
+ var jsonContents = Newtonsoft.Json.JsonConvert.SerializeObject(dlLic, Newtonsoft.Json.Formatting.Indented);
+ File.WriteAllText(jsonPath, jsonContents);
+ return actualFilePath;
+ }
+
+ // else: failure. clean up and throw
var contents = File.ReadAllText(actualFilePath);
File.Delete(actualFilePath);
@@ -119,6 +126,12 @@ namespace FileLiberator
"aax",
libraryBook.Book.AudibleProductId);
File.Move(actualFilePath, newAaxFilename);
+
+ // also move DownloadLicense json file
+ var jsonPathOld = PathLib.ReplaceExtension(actualFilePath, "json");
+ var jsonPathNew = PathLib.ReplaceExtension(newAaxFilename, "json");
+ File.Move(jsonPathOld, jsonPathNew);
+
Invoke_StatusUpdate($"Successfully downloaded. Moved to: {newAaxFilename}");
}