Skip to content

Commit

Permalink
Bug fix #350 : support old style of large multi-part books
Browse files Browse the repository at this point in the history
  • Loading branch information
rmcrackan committed Aug 16, 2022
1 parent b358145 commit 1510a86
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 16 deletions.
69 changes: 57 additions & 12 deletions Source/FileLiberator/DownloadDecryptBook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
using AaxDecrypter;
using ApplicationServices;
using AudibleApi;
using AudibleApi.Common;
using DataLayer;
using Dinah.Core;
using Dinah.Core.Collections.Generic;
using Dinah.Core.ErrorHandling;
using FileManager;
using LibationFileManager;
Expand Down Expand Up @@ -96,20 +98,63 @@ void FilePathCache_Removed(object sender, FilePathCache.CacheEntry e)
}
}

private async Task<bool> downloadAudiobookAsync(LibraryBook libraryBook)
private async Task<bool> downloadAudiobookAsync(LibraryBook libraryBook)
{
var config = Configuration.Instance;

downloadValidation(libraryBook);

var api = await libraryBook.GetApiAsync();
var contentLic = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId);

var catalogProduct = await api.GetCatalogProductAsync(libraryBook.Book.AudibleProductId, CatalogOptions.ResponseGroupOptions.ALL_OPTIONS);

// normal non-MultiPart
if (!catalogProduct.ContentDeliveryType.In(ContentDeliveryType.MultiPartIssue, ContentDeliveryType.MultiPartBook))
{
var contentLic = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId);
return await downloadAudiobookPartAsync(contentLic, libraryBook);
}

#region // notes on multi-part
// Frankly, decoupling asin and LibraryBook can be potentially dicey depending on how LibraryBook is used down the line.
// I checked and it passes cursory inspection. **No promises though.** If you find something that looks wrong, it probably is.

// the only multi-part title we've found in the wild so far is B002V1PM18
// run this un-authenticated call to see its Relationship values
// GetCatalogProductAsync("B002V1PM18", ALL_OPTIONS)

// there's a bit of guess-work in the code below. In B002V1PM18:
// there's some single part we don't need
// RelationshipToProduct: "child"
// RelationshipType: "merchant_title_authority"
// and the multiple Relationship parts we do want to download
// RelationshipToProduct: "child"
// RelationshipType: "component"
#endregion
// multi-part: handle each part separately
var asins = catalogProduct.Relationships
.Where(r => r.RelationshipType.In(RelationshipType.Component, RelationshipType.Episode))
.OrderBy(r => r.Sort)
.Select(r => r.Asin)
.ToList();
var result = true;
foreach (var asin in asins)
{
var contentLic = await api.GetDownloadLicenseAsync(asin);
var success = await downloadAudiobookPartAsync(contentLic, libraryBook);
result &= success;
}
return result;
}

private async Task<bool> downloadAudiobookPartAsync(ContentLicense contentLic, LibraryBook libraryBook)
{
var config = Configuration.Instance;

var dlOptions = BuildDownloadOptions(libraryBook, config, contentLic);

var outFileName = AudibleFileStorage.Audio.GetInProgressFilename(libraryBook, dlOptions.OutputFormat.ToString().ToLower());
var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory;

if (contentLic.DrmType != AudibleApi.Common.DrmType.Adrm)
if (contentLic.DrmType != DrmType.Adrm)
abDownloader = new UnencryptedAudiobookDownloader(outFileName, cacheDir, dlOptions);
else
{
Expand Down Expand Up @@ -138,15 +183,15 @@ AaxcDownloadConvertBase converter
return success;
}

private DownloadOptions BuildDownloadOptions(LibraryBook libraryBook, Configuration config, AudibleApi.Common.ContentLicense contentLic)
private DownloadOptions BuildDownloadOptions(LibraryBook libraryBook, Configuration config, ContentLicense contentLic)
{
//I assume if ContentFormat == "MPEG" that the delivered file is an unencrypted mp3.
//I also assume that if DrmType != Adrm, the file will be an mp3.
//These assumptions may be wrong, and only time and bug reports will tell.

bool encrypted = contentLic.DrmType == AudibleApi.Common.DrmType.Adrm;
var isEncrypted = contentLic.DrmType == DrmType.Adrm;

var outputFormat = !encrypted || (config.AllowLibationFixup && config.DecryptToLossy) ?
var outputFormat = !isEncrypted || (config.AllowLibationFixup && config.DecryptToLossy) ?
OutputFormat.Mp3 : OutputFormat.M4b;

long chapterStartMs = config.StripAudibleBrandAudio ?
Expand All @@ -163,7 +208,7 @@ private DownloadOptions BuildDownloadOptions(LibraryBook libraryBook, Configurat
AudibleIV = contentLic?.Voucher?.Iv,
OutputFormat = outputFormat,
TrimOutputToChapterLength = config.AllowLibationFixup && config.StripAudibleBrandAudio,
RetainEncryptedFile = config.RetainAaxFile && encrypted,
RetainEncryptedFile = config.RetainAaxFile && isEncrypted,
StripUnabridged = config.AllowLibationFixup && config.StripUnabridged,
Downsample = config.AllowLibationFixup && config.LameDownsampleMono,
MatchSourceBitrate = config.AllowLibationFixup && config.LameMatchSourceBR && config.LameTargetBitrate,
Expand Down Expand Up @@ -270,9 +315,9 @@ private DownloadOptions BuildDownloadOptions(LibraryBook libraryBook, Configurat
*/

public static List<AudibleApi.Common.Chapter> flattenChapters(IList<AudibleApi.Common.Chapter> chapters, string titleConcat = ": ")
public static List<Chapter> flattenChapters(IList<Chapter> chapters, string titleConcat = ": ")
{
List<AudibleApi.Common.Chapter> chaps = new();
List<Chapter> chaps = new();

foreach (var c in chapters)
{
Expand Down Expand Up @@ -301,7 +346,7 @@ public static List<AudibleApi.Common.Chapter> flattenChapters(IList<AudibleApi.C
return chaps;
}

public static void combineCredits(IList<AudibleApi.Common.Chapter> chapters)
public static void combineCredits(IList<Chapter> chapters)
{
if (chapters.Count > 1 && chapters[0].Title == "Opening Credits")
{
Expand Down
7 changes: 3 additions & 4 deletions Source/FileLiberator/DownloadOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ namespace FileLiberator
{
public class DownloadOptions : IDownloadOptions
{
public LibraryBook LibraryBook { get; }
public LibraryBookDto LibraryBookDto { get; }
public string DownloadUrl { get; }
public string UserAgent { get; }
Expand All @@ -35,12 +34,12 @@ public string GetMultipartTitleName(MultiConvertFileProperties props)

public DownloadOptions(LibraryBook libraryBook, string downloadUrl, string userAgent)
{
LibraryBook = ArgumentValidator.EnsureNotNull(libraryBook, nameof(libraryBook));
LibraryBookDto = ArgumentValidator
.EnsureNotNull(libraryBook, nameof(libraryBook))
.ToDto();
DownloadUrl = ArgumentValidator.EnsureNotNullOrEmpty(downloadUrl, nameof(downloadUrl));
UserAgent = ArgumentValidator.EnsureNotNullOrEmpty(userAgent, nameof(userAgent));

LibraryBookDto = LibraryBook.ToDto();

// no null/empty check for key/iv. unencrypted files do not have them
}
}
Expand Down

0 comments on commit 1510a86

Please sign in to comment.