Merge pull request #238 from Mbucari/master

Add option to remove Audible branding audio
This commit is contained in:
rmcrackan 2022-05-08 12:57:21 -04:00 committed by GitHub
commit 2c6c08fbb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 621 additions and 568 deletions

View File

@ -5,8 +5,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AAXClean" Version="0.3.5" />
<PackageReference Include="AAXClean.Codecs" Version="0.1.12" />
<PackageReference Include="AAXClean" Version="0.4.6" />
<PackageReference Include="AAXClean.Codecs" Version="0.2.5" />
</ItemGroup>
<ItemGroup>

View File

@ -8,15 +8,10 @@ namespace AaxDecrypter
{
public event EventHandler<AppleTags> RetrievedMetadata;
protected OutputFormat OutputFormat { get; }
protected AaxFile AaxFile;
protected AaxcDownloadConvertBase(string outFileName, string cacheDirectory, DownloadLicense dlLic, OutputFormat outputFormat)
: base(outFileName, cacheDirectory, dlLic)
{
OutputFormat = outputFormat;
}
protected AaxcDownloadConvertBase(string outFileName, string cacheDirectory, DownloadLicense dlLic)
: base(outFileName, cacheDirectory, dlLic) { }
/// <summary>Setting cover art by this method will insert the art into the audiobook metadata</summary>
public override void SetCoverArt(byte[] coverArt)

View File

@ -18,13 +18,13 @@ namespace AaxDecrypter
private static TimeSpan minChapterLength { get; } = TimeSpan.FromSeconds(3);
private List<string> multiPartFilePaths { get; } = new List<string>();
public AaxcDownloadMultiConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic, OutputFormat outputFormat,
public AaxcDownloadMultiConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic,
Func<MultiConvertFileProperties, string> multipartFileNameCallback = null)
: base(outFileName, cacheDirectory, dlLic, outputFormat)
: base(outFileName, cacheDirectory, dlLic)
{
Steps = new StepSequence
{
Name = "Download and Convert Aaxc To " + OutputFormat,
Name = "Download and Convert Aaxc To " + DownloadLicense.OutputFormat,
["Step 1: Get Aaxc Metadata"] = Step_GetMetadata,
["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsMultipleFilesPerChapter,
@ -64,7 +64,7 @@ That naming may not be desirable for everyone, but it's an easy change to instea
var chapters = DownloadLicense.ChapterInfo.Chapters.ToList();
// Ensure split files are at least minChapterLength in duration.
var splitChapters = new ChapterInfo();
var splitChapters = new ChapterInfo(DownloadLicense.ChapterInfo.StartOffset);
var runningTotal = TimeSpan.Zero;
string title = "";
@ -86,16 +86,18 @@ That naming may not be desirable for everyone, but it's an easy change to instea
// reset, just in case
multiPartFilePaths.Clear();
ConversionResult result;
AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate;
if (OutputFormat == OutputFormat.M4b)
ConvertToMultiMp4a(splitChapters);
if (DownloadLicense.OutputFormat == OutputFormat.M4b)
result = ConvertToMultiMp4a(splitChapters);
else
ConvertToMultiMp3(splitChapters);
result = ConvertToMultiMp3(splitChapters);
AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate;
Step_DownloadAudiobook_End(zeroProgress);
var success = !IsCanceled;
var success = result == ConversionResult.NoErrorsDetected;
if (success)
foreach (var path in multiPartFilePaths)
@ -104,22 +106,22 @@ That naming may not be desirable for everyone, but it's an easy change to instea
return success;
}
private void ConvertToMultiMp4a(ChapterInfo splitChapters)
private ConversionResult ConvertToMultiMp4a(ChapterInfo splitChapters)
{
var chapterCount = 0;
AaxFile.ConvertToMultiMp4a(splitChapters, newSplitCallback =>
createOutputFileStream(++chapterCount, splitChapters, newSplitCallback)
);
return AaxFile.ConvertToMultiMp4a(splitChapters, newSplitCallback =>
createOutputFileStream(++chapterCount, splitChapters, newSplitCallback),
DownloadLicense.TrimOutputToChapterLength);
}
private void ConvertToMultiMp3(ChapterInfo splitChapters)
private ConversionResult ConvertToMultiMp3(ChapterInfo splitChapters)
{
var chapterCount = 0;
AaxFile.ConvertToMultiMp3(splitChapters, newSplitCallback =>
return AaxFile.ConvertToMultiMp3(splitChapters, newSplitCallback =>
{
createOutputFileStream(++chapterCount, splitChapters, newSplitCallback);
((NAudio.Lame.LameConfig)newSplitCallback.UserState).ID3.Track = chapterCount.ToString();
});
}, null, DownloadLicense.TrimOutputToChapterLength);
}
private void createOutputFileStream(int currentChapter, ChapterInfo splitChapters, NewSplitCallback newSplitCallback)

View File

@ -11,12 +11,12 @@ namespace AaxDecrypter
{
protected override StepSequence Steps { get; }
public AaxcDownloadSingleConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic, OutputFormat outputFormat)
: base(outFileName, cacheDirectory, dlLic, outputFormat)
public AaxcDownloadSingleConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic)
: base(outFileName, cacheDirectory, dlLic)
{
Steps = new StepSequence
{
Name = "Download and Convert Aaxc To " + OutputFormat,
Name = "Download and Convert Aaxc To " + DownloadLicense.OutputFormat,
["Step 1: Get Aaxc Metadata"] = Step_GetMetadata,
["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsSingleFile,
@ -35,9 +35,9 @@ namespace AaxDecrypter
AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate;
var decryptionResult
= OutputFormat == OutputFormat.M4b
? AaxFile.ConvertToMp4a(outputFile, DownloadLicense.ChapterInfo)
: AaxFile.ConvertToMp3(outputFile);
= DownloadLicense.OutputFormat == OutputFormat.M4b
? AaxFile.ConvertToMp4a(outputFile, DownloadLicense.ChapterInfo, DownloadLicense.TrimOutputToChapterLength)
: AaxFile.ConvertToMp3(outputFile, null, DownloadLicense.ChapterInfo, DownloadLicense.TrimOutputToChapterLength);
AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate;
DownloadLicense.ChapterInfo = AaxFile.Chapters;

View File

@ -6,19 +6,19 @@ namespace AaxDecrypter
public class DownloadLicense
{
public string DownloadUrl { get; }
public string AudibleKey { get; }
public string AudibleIV { get; }
public string UserAgent { get; }
public string AudibleKey { get; init; }
public string AudibleIV { get; init; }
public OutputFormat OutputFormat { get; init; }
public bool TrimOutputToChapterLength { get; init; }
public ChapterInfo ChapterInfo { get; set; }
public DownloadLicense(string downloadUrl, string audibleKey, string audibleIV, string userAgent)
public DownloadLicense(string downloadUrl, string userAgent)
{
DownloadUrl = ArgumentValidator.EnsureNotNullOrEmpty(downloadUrl, nameof(downloadUrl));
UserAgent = ArgumentValidator.EnsureNotNullOrEmpty(userAgent, nameof(userAgent));
// no null/empty check. unencrypted files do not have these
AudibleKey = audibleKey;
AudibleIV = audibleIV;
// no null/empty check for key/iv. unencrypted files do not have them
}
}
}

View File

@ -80,6 +80,9 @@ namespace AppScaffolding
if (!config.Exists(nameof(config.SplitFilesByChapter)))
config.SplitFilesByChapter = false;
if (!config.Exists(nameof(config.StripAudibleBrandAudio)))
config.StripAudibleBrandAudio = false;
if (!config.Exists(nameof(config.FolderTemplate)))
config.FolderTemplate = Templates.Folder.DefaultTemplate;

View File

@ -86,37 +86,15 @@ namespace FileLiberator
try
{
var config = Configuration.Instance;
downloadValidation(libraryBook);
var api = await libraryBook.GetApiAsync();
var contentLic = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId);
var audiobookDlLic = BuildDownloadLicense(config, contentLic);
var audiobookDlLic = new DownloadLicense
(
contentLic?.ContentMetadata?.ContentUrl?.OfflineUrl,
contentLic?.Voucher?.Key,
contentLic?.Voucher?.Iv,
Resources.USER_AGENT
);
//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.
var outputFormat =
contentLic.ContentMetadata.ContentReference.ContentFormat == "MPEG" ||
(Configuration.Instance.AllowLibationFixup && Configuration.Instance.DecryptToLossy) ?
OutputFormat.Mp3 : OutputFormat.M4b;
if (Configuration.Instance.AllowLibationFixup || outputFormat == OutputFormat.Mp3)
{
audiobookDlLic.ChapterInfo = new AAXClean.ChapterInfo();
foreach (var chap in contentLic.ContentMetadata?.ChapterInfo?.Chapters)
audiobookDlLic.ChapterInfo.AddChapter(chap.Title, TimeSpan.FromMilliseconds(chap.LengthMs));
}
var outFileName = AudibleFileStorage.Audio.GetInProgressFilename(libraryBook, outputFormat.ToString().ToLower());
var outFileName = AudibleFileStorage.Audio.GetInProgressFilename(libraryBook, audiobookDlLic.OutputFormat.ToString().ToLower());
var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory;
if (contentLic.DrmType != AudibleApi.Common.DrmType.Adrm)
@ -124,12 +102,12 @@ namespace FileLiberator
else
{
AaxcDownloadConvertBase converter
= Configuration.Instance.SplitFilesByChapter ? new AaxcDownloadMultiConverter(
outFileName, cacheDir, audiobookDlLic, outputFormat,
= config.SplitFilesByChapter ? new AaxcDownloadMultiConverter(
outFileName, cacheDir, audiobookDlLic,
AudibleFileStorage.Audio.CreateMultipartRenamerFunc(libraryBook))
: new AaxcDownloadSingleConverter(outFileName, cacheDir, audiobookDlLic, outputFormat);
: new AaxcDownloadSingleConverter(outFileName, cacheDir, audiobookDlLic);
if (Configuration.Instance.AllowLibationFixup)
if (config.AllowLibationFixup)
converter.RetrievedMetadata += (_, tags) => tags.Generes = string.Join(", ", libraryBook.Book.CategoriesNames);
abDownloader = converter;
@ -154,6 +132,56 @@ namespace FileLiberator
}
}
private static DownloadLicense BuildDownloadLicense(Configuration config, AudibleApi.Common.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.
var outputFormat =
contentLic?.ContentMetadata?.ContentReference?.ContentFormat == "MPEG" ||
(config.AllowLibationFixup && config.DecryptToLossy) ?
OutputFormat.Mp3 : OutputFormat.M4b;
var audiobookDlLic = new DownloadLicense
(
contentLic?.ContentMetadata?.ContentUrl?.OfflineUrl,
Resources.USER_AGENT
)
{
AudibleKey = contentLic?.Voucher?.Key,
AudibleIV = contentLic?.Voucher?.Iv,
OutputFormat = outputFormat,
TrimOutputToChapterLength = config.StripAudibleBrandAudio
};
if (config.AllowLibationFixup || outputFormat == OutputFormat.Mp3)
{
long startMs = audiobookDlLic.TrimOutputToChapterLength ?
contentLic.ContentMetadata.ChapterInfo.BrandIntroDurationMs : 0;
audiobookDlLic.ChapterInfo = new AAXClean.ChapterInfo(TimeSpan.FromMilliseconds(startMs));
for (int i = 0; i < contentLic.ContentMetadata.ChapterInfo.Chapters.Length; i++)
{
var chapter = contentLic.ContentMetadata.ChapterInfo.Chapters[i];
long chapLenMs = chapter.LengthMs;
if (i == 0)
chapLenMs -= startMs;
if (config.StripAudibleBrandAudio && i == contentLic.ContentMetadata.ChapterInfo.Chapters.Length - 1)
chapLenMs -= contentLic.ContentMetadata.ChapterInfo.BrandOutroDurationMs;
audiobookDlLic.ChapterInfo.AddChapter(chapter.Title, TimeSpan.FromMilliseconds(chapLenMs));
}
}
return audiobookDlLic;
}
private static void downloadValidation(LibraryBook libraryBook)
{
string errorString(string field)

View File

@ -96,6 +96,13 @@ namespace LibationFileManager
set => persistentDictionary.SetNonString(nameof(AllowLibationFixup), value);
}
[Description("Allow Libation to remove audible branding from the start\r\nand end of audiobooks. (e.g. \"This is Audible\")")]
public bool StripAudibleBrandAudio
{
get => persistentDictionary.GetNonString<bool>(nameof(StripAudibleBrandAudio));
set => persistentDictionary.SetNonString(nameof(StripAudibleBrandAudio), value);
}
[Description("Decrypt to lossy format?")]
public bool DecryptToLossy
{

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,7 @@ namespace LibationWinForms.Dialogs
this.inProgressDescLbl.Text = desc(nameof(config.InProgress));
this.allowLibationFixupCbox.Text = desc(nameof(config.AllowLibationFixup));
this.splitFilesByChapterCbox.Text = desc(nameof(config.SplitFilesByChapter));
this.stripAudibleBrandingCbox.Text = desc(nameof(config.StripAudibleBrandAudio));
booksSelectControl.SetSearchTitle("books location");
booksSelectControl.SetDirectoryItems(
@ -54,6 +55,7 @@ namespace LibationWinForms.Dialogs
convertLosslessRb.Checked = !config.DecryptToLossy;
convertLossyRb.Checked = config.DecryptToLossy;
splitFilesByChapterCbox.Checked = config.SplitFilesByChapter;
stripAudibleBrandingCbox.Checked = config.StripAudibleBrandAudio;
allowLibationFixupCbox_CheckedChanged(this, e);
@ -95,11 +97,13 @@ namespace LibationWinForms.Dialogs
convertLosslessRb.Enabled = allowLibationFixupCbox.Checked;
convertLossyRb.Enabled = allowLibationFixupCbox.Checked;
splitFilesByChapterCbox.Enabled = allowLibationFixupCbox.Checked;
stripAudibleBrandingCbox.Enabled =allowLibationFixupCbox.Checked;
if (!allowLibationFixupCbox.Checked)
{
convertLosslessRb.Checked = true;
splitFilesByChapterCbox.Checked = false;
stripAudibleBrandingCbox.Checked = false;
}
}
@ -174,6 +178,7 @@ namespace LibationWinForms.Dialogs
config.AllowLibationFixup = allowLibationFixupCbox.Checked;
config.DecryptToLossy = convertLossyRb.Checked;
config.SplitFilesByChapter = splitFilesByChapterCbox.Checked;
config.StripAudibleBrandAudio = stripAudibleBrandingCbox.Checked;
config.InProgress = inProgressSelectControl.SelectedDirectory;