commit
28731e51f5
@ -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; }
|
||||
|
||||
381
DataLayer/Migrations/20210622205558_RemoveAaxcDecryptionKeys.Designer.cs
generated
Normal file
381
DataLayer/Migrations/20210622205558_RemoveAaxcDecryptionKeys.Designer.cs
generated
Normal file
@ -0,0 +1,381 @@
|
||||
// <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("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<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<string>(
|
||||
name: "AudibleIV",
|
||||
table: "Books",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "AudibleKey",
|
||||
table: "Books",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -22,12 +22,6 @@ namespace DataLayer.Migrations
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AudibleIV")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AudibleKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AudibleProductId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
||||
@ -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<Chapters> downloadChapterNames(LibraryBook libraryBook, Api api)
|
||||
private static async Task<Chapters> 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<string> aaxToM4bConverterDecrypt(string aaxFilename, LibraryBook libraryBook, Chapters chapters, Api api)
|
||||
private async Task<string> 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<AudibleApiDTOs.DownloadLicense>(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);
|
||||
|
||||
@ -41,6 +41,7 @@ namespace FileLiberator
|
||||
libraryBook.Book.Title,
|
||||
"aax",
|
||||
libraryBook.Book.AudibleProductId);
|
||||
|
||||
private async Task<string> 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}");
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user