From b11a4887d7ae919428ce7ea522440c1b444dbd7e Mon Sep 17 00:00:00 2001 From: MBucari Date: Wed, 7 May 2025 23:17:39 -0600 Subject: [PATCH] Pad final chapter to prevent tuncation from incorrect chapter info (#1246) --- .../AaxDecrypter/AaxcDownloadConvertBase.cs | 2 +- Source/FileLiberator/DownloadDecryptBook.cs | 26 +++++++++++++++++++ Source/FileLiberator/DownloadOptions.cs | 26 +++++++++---------- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/Source/AaxDecrypter/AaxcDownloadConvertBase.cs b/Source/AaxDecrypter/AaxcDownloadConvertBase.cs index f96dfeea..22279818 100644 --- a/Source/AaxDecrypter/AaxcDownloadConvertBase.cs +++ b/Source/AaxDecrypter/AaxcDownloadConvertBase.cs @@ -8,7 +8,7 @@ namespace AaxDecrypter { public event EventHandler RetrievedMetadata; - protected Mp4File AaxFile { get; private set; } + public Mp4File AaxFile { get; private set; } protected Mp4Operation AaxConversion { get; set; } protected AaxcDownloadConvertBase(string outFileName, string cacheDirectory, IDownloadOptions dlOptions) diff --git a/Source/FileLiberator/DownloadDecryptBook.cs b/Source/FileLiberator/DownloadDecryptBook.cs index f6a70deb..a5ec6f9e 100644 --- a/Source/FileLiberator/DownloadDecryptBook.cs +++ b/Source/FileLiberator/DownloadDecryptBook.cs @@ -171,6 +171,32 @@ namespace FileLiberator if (sender is not AaxcDownloadConvertBase converter || converter.DownloadOptions is not DownloadOptions options) return; + #region Prevent erroneous truncation due to incorrect chapter info + + //Sometimes the chapter info is not accurate. Since AAXClean trims audio + //files to the chapters start and end, if the last chapter's end time is + //before the end of the audio file, the file will be truncated to match + //the chapter. This is never desirable, so pad the last chapter to match + //the original audio length. + + var fileDuration = converter.AaxFile.Duration; + if (options.Config.StripAudibleBrandAudio) + fileDuration -= TimeSpan.FromMilliseconds(options.ContentMetadata.ChapterInfo.BrandOutroDurationMs); + + var durationDelta = fileDuration - options.ChapterInfo.EndOffset; + if (durationDelta.TotalMilliseconds > 0) + { + //only fix chapters which are shorter than the file. Chapters which are longer + //than the file will be truncated to match the file length, which is correct. + var chapters = options.ChapterInfo.Chapters as List; + var lastChapter = chapters[^1]; + + chapters.Remove(lastChapter); + options.ChapterInfo.Add(lastChapter.Title, lastChapter.Duration + durationDelta); + } + + #endregion + tags.Title ??= options.LibraryBookDto.TitleWithSubtitle; tags.Album ??= tags.Title; tags.Artist ??= string.Join("; ", options.LibraryBook.Book.Authors.Select(a => a.Name)); diff --git a/Source/FileLiberator/DownloadOptions.cs b/Source/FileLiberator/DownloadOptions.cs index 3101aa72..e48ce069 100644 --- a/Source/FileLiberator/DownloadOptions.cs +++ b/Source/FileLiberator/DownloadOptions.cs @@ -31,16 +31,16 @@ namespace FileLiberator public float? SeriesNumber => LibraryBookDto.FirstSeries?.Number; public NAudio.Lame.LameConfig LameConfig { get; init; } public string UserAgent => AudibleApi.Resources.Download_User_Agent; - public bool TrimOutputToChapterLength => config.AllowLibationFixup && config.StripAudibleBrandAudio; - public bool StripUnabridged => config.AllowLibationFixup && config.StripUnabridged; - public bool CreateCueSheet => config.CreateCueSheet; - public bool DownloadClipsBookmarks => config.DownloadClipsBookmarks; - public long DownloadSpeedBps => config.DownloadSpeedLimit; - public bool RetainEncryptedFile => config.RetainAaxFile; - public bool FixupFile => config.AllowLibationFixup; - public bool Downsample => config.AllowLibationFixup && config.LameDownsampleMono; - public bool MatchSourceBitrate => config.AllowLibationFixup && config.LameMatchSourceBR && config.LameTargetBitrate; - public bool MoveMoovToBeginning => config.MoveMoovToBeginning; + public bool TrimOutputToChapterLength => Config.AllowLibationFixup && Config.StripAudibleBrandAudio; + public bool StripUnabridged => Config.AllowLibationFixup && Config.StripUnabridged; + public bool CreateCueSheet => Config.CreateCueSheet; + public bool DownloadClipsBookmarks => Config.DownloadClipsBookmarks; + public long DownloadSpeedBps => Config.DownloadSpeedLimit; + public bool RetainEncryptedFile => Config.RetainAaxFile; + public bool FixupFile => Config.AllowLibationFixup; + public bool Downsample => Config.AllowLibationFixup && Config.LameDownsampleMono; + public bool MatchSourceBitrate => Config.AllowLibationFixup && Config.LameMatchSourceBR && Config.LameTargetBitrate; + public bool MoveMoovToBeginning => Config.MoveMoovToBeginning; public AAXClean.FileType? InputType { get; init; } public AudibleApi.Common.DrmType DrmType { get; init; } public AudibleApi.Common.ContentMetadata ContentMetadata { get; init; } @@ -59,7 +59,7 @@ namespace FileLiberator { if (DownloadClipsBookmarks) { - var format = config.ClipsBookmarksFileFormat; + var format = Config.ClipsBookmarksFileFormat; var formatExtension = format.ToString().ToLowerInvariant(); var filePath = Path.ChangeExtension(fileName, formatExtension); @@ -84,7 +84,7 @@ namespace FileLiberator return string.Empty; } - private readonly Configuration config; + public Configuration Config { get; } private readonly IDisposable cancellation; public void Dispose() { @@ -94,7 +94,7 @@ namespace FileLiberator private DownloadOptions(Configuration config, LibraryBook libraryBook, [System.Diagnostics.CodeAnalysis.NotNull] string downloadUrl) { - this.config = ArgumentValidator.EnsureNotNull(config, nameof(config)); + Config = ArgumentValidator.EnsureNotNull(config, nameof(config)); LibraryBook = ArgumentValidator.EnsureNotNull(libraryBook, nameof(libraryBook)); DownloadUrl = ArgumentValidator.EnsureNotNullOrEmpty(downloadUrl, nameof(downloadUrl)); // no null/empty check for key/iv. unencrypted files do not have them