split AaxcDownload single and multi
This commit is contained in:
parent
0f1ff0aa10
commit
2767f04621
91
AaxDecrypter/AaxcDownloadConvertBase.cs
Normal file
91
AaxDecrypter/AaxcDownloadConvertBase.cs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
using System;
|
||||||
|
using AAXClean;
|
||||||
|
using Dinah.Core.Net.Http;
|
||||||
|
|
||||||
|
namespace AaxDecrypter
|
||||||
|
{
|
||||||
|
public abstract class AaxcDownloadConvertBase : AudiobookDownloadBase
|
||||||
|
{
|
||||||
|
protected OutputFormat OutputFormat { get; }
|
||||||
|
|
||||||
|
protected AaxFile AaxFile;
|
||||||
|
|
||||||
|
protected AaxcDownloadConvertBase(string outFileName, string cacheDirectory, DownloadLicense dlLic, OutputFormat outputFormat)
|
||||||
|
: base(outFileName, cacheDirectory, dlLic)
|
||||||
|
{
|
||||||
|
OutputFormat = outputFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Setting cover art by this method will insert the art into the audiobook metadata</summary>
|
||||||
|
public override void SetCoverArt(byte[] coverArt)
|
||||||
|
{
|
||||||
|
base.SetCoverArt(coverArt);
|
||||||
|
if (coverArt is not null)
|
||||||
|
AaxFile?.AppleTags.SetCoverArt(coverArt);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool Step_GetMetadata()
|
||||||
|
{
|
||||||
|
AaxFile = new AaxFile(InputFileStream);
|
||||||
|
|
||||||
|
OnRetrievedTitle(AaxFile.AppleTags.TitleSansUnabridged);
|
||||||
|
OnRetrievedAuthors(AaxFile.AppleTags.FirstAuthor ?? "[unknown]");
|
||||||
|
OnRetrievedNarrators(AaxFile.AppleTags.Narrator ?? "[unknown]");
|
||||||
|
OnRetrievedCoverArt(AaxFile.AppleTags.Cover);
|
||||||
|
|
||||||
|
return !IsCanceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DownloadProgress Step_DownloadAudiobook_Start()
|
||||||
|
{
|
||||||
|
var zeroProgress = new DownloadProgress
|
||||||
|
{
|
||||||
|
BytesReceived = 0,
|
||||||
|
ProgressPercentage = 0,
|
||||||
|
TotalBytesToReceive = InputFileStream.Length
|
||||||
|
};
|
||||||
|
|
||||||
|
OnDecryptProgressUpdate(zeroProgress);
|
||||||
|
|
||||||
|
AaxFile.SetDecryptionKey(DownloadLicense.AudibleKey, DownloadLicense.AudibleIV);
|
||||||
|
return zeroProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Step_DownloadAudiobook_End(DownloadProgress zeroProgress)
|
||||||
|
{
|
||||||
|
AaxFile.Close();
|
||||||
|
|
||||||
|
CloseInputFileStream();
|
||||||
|
|
||||||
|
OnDecryptProgressUpdate(zeroProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void AaxFile_ConversionProgressUpdate(object sender, ConversionProgressEventArgs e)
|
||||||
|
{
|
||||||
|
var duration = AaxFile.Duration;
|
||||||
|
var remainingSecsToProcess = (duration - e.ProcessPosition).TotalSeconds;
|
||||||
|
var estTimeRemaining = remainingSecsToProcess / e.ProcessSpeed;
|
||||||
|
|
||||||
|
if (double.IsNormal(estTimeRemaining))
|
||||||
|
OnDecryptTimeRemaining(TimeSpan.FromSeconds(estTimeRemaining));
|
||||||
|
|
||||||
|
var progressPercent = e.ProcessPosition.TotalSeconds / duration.TotalSeconds;
|
||||||
|
|
||||||
|
OnDecryptProgressUpdate(
|
||||||
|
new DownloadProgress
|
||||||
|
{
|
||||||
|
ProgressPercentage = 100 * progressPercent,
|
||||||
|
BytesReceived = (long)(InputFileStream.Length * progressPercent),
|
||||||
|
TotalBytesToReceive = InputFileStream.Length
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Cancel()
|
||||||
|
{
|
||||||
|
IsCanceled = true;
|
||||||
|
AaxFile?.Cancel();
|
||||||
|
AaxFile?.Dispose();
|
||||||
|
CloseInputFileStream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,238 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using AAXClean;
|
|
||||||
using Dinah.Core.Net.Http;
|
|
||||||
using Dinah.Core.StepRunner;
|
|
||||||
using FileManager;
|
|
||||||
|
|
||||||
namespace AaxDecrypter
|
|
||||||
{
|
|
||||||
public class AaxcDownloadConverter : AudiobookDownloadBase
|
|
||||||
{
|
|
||||||
private static readonly TimeSpan minChapterLength = TimeSpan.FromSeconds(3);
|
|
||||||
protected override StepSequence Steps { get; }
|
|
||||||
|
|
||||||
private AaxFile aaxFile;
|
|
||||||
private OutputFormat OutputFormat { get; }
|
|
||||||
|
|
||||||
private List<string> multiPartFilePaths { get; } = new List<string>();
|
|
||||||
|
|
||||||
public AaxcDownloadConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic, OutputFormat outputFormat, bool splitFileByChapters)
|
|
||||||
: base(outFileName, cacheDirectory, dlLic)
|
|
||||||
{
|
|
||||||
OutputFormat = outputFormat;
|
|
||||||
|
|
||||||
Steps = new StepSequence
|
|
||||||
{
|
|
||||||
Name = "Download and Convert Aaxc To " + OutputFormat,
|
|
||||||
|
|
||||||
["Step 1: Get Aaxc Metadata"] = Step1_GetMetadata,
|
|
||||||
["Step 2: Download Decrypted Audiobook"] = splitFileByChapters
|
|
||||||
? Step2_DownloadAudiobookAsMultipleFilesPerChapter
|
|
||||||
: Step2_DownloadAudiobookAsSingleFile,
|
|
||||||
["Step 3: Create Cue"] = splitFileByChapters
|
|
||||||
? () => true
|
|
||||||
: Step3_CreateCue,
|
|
||||||
["Step 4: Cleanup"] = Step4_Cleanup,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Setting cover art by this method will insert the art into the audiobook metadata
|
|
||||||
/// </summary>
|
|
||||||
public override void SetCoverArt(byte[] coverArt)
|
|
||||||
{
|
|
||||||
base.SetCoverArt(coverArt);
|
|
||||||
|
|
||||||
aaxFile?.AppleTags.SetCoverArt(coverArt);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool Step1_GetMetadata()
|
|
||||||
{
|
|
||||||
aaxFile = new AaxFile(InputFileStream);
|
|
||||||
|
|
||||||
OnRetrievedTitle(aaxFile.AppleTags.TitleSansUnabridged);
|
|
||||||
OnRetrievedAuthors(aaxFile.AppleTags.FirstAuthor ?? "[unknown]");
|
|
||||||
OnRetrievedNarrators(aaxFile.AppleTags.Narrator ?? "[unknown]");
|
|
||||||
OnRetrievedCoverArt(aaxFile.AppleTags.Cover);
|
|
||||||
|
|
||||||
return !IsCanceled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool Step2_DownloadAudiobookAsSingleFile()
|
|
||||||
{
|
|
||||||
var zeroProgress = Step2_Start();
|
|
||||||
|
|
||||||
FileUtility.SaferDelete(OutputFileName);
|
|
||||||
|
|
||||||
var outputFile = File.Open(OutputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
|
||||||
|
|
||||||
aaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate;
|
|
||||||
var decryptionResult = OutputFormat == OutputFormat.M4b ? aaxFile.ConvertToMp4a(outputFile, DownloadLicense.ChapterInfo) : aaxFile.ConvertToMp3(outputFile);
|
|
||||||
aaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate;
|
|
||||||
|
|
||||||
DownloadLicense.ChapterInfo = aaxFile.Chapters;
|
|
||||||
|
|
||||||
Step2_End(zeroProgress);
|
|
||||||
|
|
||||||
var success = decryptionResult == ConversionResult.NoErrorsDetected && !IsCanceled;
|
|
||||||
if (success)
|
|
||||||
base.OnFileCreated(OutputFileName);
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
https://github.com/rmcrackan/Libation/pull/127#issuecomment-939088489
|
|
||||||
|
|
||||||
If the chapter truly is empty, that is, 0 audio frames in length, then yes it is ignored.
|
|
||||||
If the chapter is shorter than 3 seconds long but still has some audio frames, those frames are combined with the following chapter and not split into a new file.
|
|
||||||
|
|
||||||
I also implemented file naming by chapter title. When 2 or more consecutive chapters are combined, the first of the combined chapter's title is used in the file name. For example, given an audiobook with the following chapters:
|
|
||||||
|
|
||||||
00:00:00 - 00:00:02 | Part 1
|
|
||||||
00:00:02 - 00:35:00 | Chapter 1
|
|
||||||
00:35:02 - 01:02:00 | Chapter 2
|
|
||||||
01:02:00 - 01:02:02 | Part 2
|
|
||||||
01:02:02 - 01:41:00 | Chapter 3
|
|
||||||
01:41:00 - 02:05:00 | Chapter 4
|
|
||||||
|
|
||||||
The book will be split into the following files:
|
|
||||||
|
|
||||||
00:00:00 - 00:35:00 | Book - 01 - Part 1.m4b
|
|
||||||
00:35:00 - 01:02:00 | Book - 02 - Chapter 2.m4b
|
|
||||||
01:02:00 - 01:41:00 | Book - 03 - Part 2.m4b
|
|
||||||
01:41:00 - 02:05:00 | Book - 04 - Chapter 4.m4b
|
|
||||||
|
|
||||||
That naming may not be desirable for everyone, but it's an easy change to instead use the last of the combined chapter's title in the file name.
|
|
||||||
*/
|
|
||||||
private bool Step2_DownloadAudiobookAsMultipleFilesPerChapter()
|
|
||||||
{
|
|
||||||
var zeroProgress = Step2_Start();
|
|
||||||
|
|
||||||
var chapters = DownloadLicense.ChapterInfo.Chapters.ToList();
|
|
||||||
|
|
||||||
//Ensure split files are at least minChapterLength in duration.
|
|
||||||
var splitChapters = new ChapterInfo();
|
|
||||||
|
|
||||||
var runningTotal = TimeSpan.Zero;
|
|
||||||
string title = "";
|
|
||||||
|
|
||||||
for (int i = 0; i < chapters.Count; i++)
|
|
||||||
{
|
|
||||||
if (runningTotal == TimeSpan.Zero)
|
|
||||||
title = chapters[i].Title;
|
|
||||||
|
|
||||||
runningTotal += chapters[i].Duration;
|
|
||||||
|
|
||||||
if (runningTotal >= minChapterLength)
|
|
||||||
{
|
|
||||||
splitChapters.AddChapter(title, runningTotal);
|
|
||||||
runningTotal = TimeSpan.Zero;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset, just in case
|
|
||||||
multiPartFilePaths.Clear();
|
|
||||||
|
|
||||||
aaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate;
|
|
||||||
if (OutputFormat == OutputFormat.M4b)
|
|
||||||
ConvertToMultiMp4b(splitChapters);
|
|
||||||
else
|
|
||||||
ConvertToMultiMp3(splitChapters);
|
|
||||||
aaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate;
|
|
||||||
|
|
||||||
Step2_End(zeroProgress);
|
|
||||||
|
|
||||||
var success = !IsCanceled;
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
foreach (var path in multiPartFilePaths)
|
|
||||||
OnFileCreated(path);
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DownloadProgress Step2_Start()
|
|
||||||
{
|
|
||||||
var zeroProgress = new DownloadProgress
|
|
||||||
{
|
|
||||||
BytesReceived = 0,
|
|
||||||
ProgressPercentage = 0,
|
|
||||||
TotalBytesToReceive = InputFileStream.Length
|
|
||||||
};
|
|
||||||
|
|
||||||
OnDecryptProgressUpdate(zeroProgress);
|
|
||||||
|
|
||||||
aaxFile.SetDecryptionKey(DownloadLicense.AudibleKey, DownloadLicense.AudibleIV);
|
|
||||||
return zeroProgress;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Step2_End(DownloadProgress zeroProgress)
|
|
||||||
{
|
|
||||||
aaxFile.Close();
|
|
||||||
|
|
||||||
CloseInputFileStream();
|
|
||||||
|
|
||||||
OnDecryptProgressUpdate(zeroProgress);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConvertToMultiMp4b(ChapterInfo splitChapters)
|
|
||||||
{
|
|
||||||
var chapterCount = 0;
|
|
||||||
aaxFile.ConvertToMultiMp4a(splitChapters, newSplitCallback =>
|
|
||||||
createOutputFileStream(++chapterCount, splitChapters, newSplitCallback)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConvertToMultiMp3(ChapterInfo splitChapters)
|
|
||||||
{
|
|
||||||
var chapterCount = 0;
|
|
||||||
aaxFile.ConvertToMultiMp3(splitChapters, newSplitCallback =>
|
|
||||||
{
|
|
||||||
createOutputFileStream(++chapterCount, splitChapters, newSplitCallback);
|
|
||||||
newSplitCallback.LameConfig.ID3.Track = chapterCount.ToString();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createOutputFileStream(int currentChapter, ChapterInfo splitChapters, NewSplitCallback newSplitCallback)
|
|
||||||
{
|
|
||||||
var fileName = FileUtility.GetMultipartFileName(OutputFileName, currentChapter, splitChapters.Count, newSplitCallback.Chapter.Title);
|
|
||||||
multiPartFilePaths.Add(fileName);
|
|
||||||
|
|
||||||
FileUtility.SaferDelete(fileName);
|
|
||||||
|
|
||||||
newSplitCallback.OutputFile = File.Open(fileName, FileMode.OpenOrCreate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AaxFile_ConversionProgressUpdate(object sender, ConversionProgressEventArgs e)
|
|
||||||
{
|
|
||||||
var duration = aaxFile.Duration;
|
|
||||||
double remainingSecsToProcess = (duration - e.ProcessPosition).TotalSeconds;
|
|
||||||
double estTimeRemaining = remainingSecsToProcess / e.ProcessSpeed;
|
|
||||||
|
|
||||||
if (double.IsNormal(estTimeRemaining))
|
|
||||||
OnDecryptTimeRemaining(TimeSpan.FromSeconds(estTimeRemaining));
|
|
||||||
|
|
||||||
double progressPercent = e.ProcessPosition.TotalSeconds / duration.TotalSeconds;
|
|
||||||
|
|
||||||
OnDecryptProgressUpdate(
|
|
||||||
new DownloadProgress
|
|
||||||
{
|
|
||||||
ProgressPercentage = 100 * progressPercent,
|
|
||||||
BytesReceived = (long)(InputFileStream.Length * progressPercent),
|
|
||||||
TotalBytesToReceive = InputFileStream.Length
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Cancel()
|
|
||||||
{
|
|
||||||
IsCanceled = true;
|
|
||||||
aaxFile?.Cancel();
|
|
||||||
aaxFile?.Dispose();
|
|
||||||
CloseInputFileStream();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
130
AaxDecrypter/AaxcDownloadMultiConverter.cs
Normal file
130
AaxDecrypter/AaxcDownloadMultiConverter.cs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using AAXClean;
|
||||||
|
using Dinah.Core.StepRunner;
|
||||||
|
using FileManager;
|
||||||
|
|
||||||
|
namespace AaxDecrypter
|
||||||
|
{
|
||||||
|
public class AaxcDownloadMultiConverter : AaxcDownloadConvertBase
|
||||||
|
{
|
||||||
|
protected override StepSequence Steps { get; }
|
||||||
|
|
||||||
|
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)
|
||||||
|
: base(outFileName, cacheDirectory, dlLic, outputFormat)
|
||||||
|
{
|
||||||
|
Steps = new StepSequence
|
||||||
|
{
|
||||||
|
Name = "Download and Convert Aaxc To " + OutputFormat,
|
||||||
|
|
||||||
|
["Step 1: Get Aaxc Metadata"] = Step_GetMetadata,
|
||||||
|
["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsMultipleFilesPerChapter,
|
||||||
|
["Step 3: Cleanup"] = Step_Cleanup,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
https://github.com/rmcrackan/Libation/pull/127#issuecomment-939088489
|
||||||
|
|
||||||
|
If the chapter truly is empty, that is, 0 audio frames in length, then yes it is ignored.
|
||||||
|
If the chapter is shorter than 3 seconds long but still has some audio frames, those frames are combined with the following chapter and not split into a new file.
|
||||||
|
|
||||||
|
I also implemented file naming by chapter title. When 2 or more consecutive chapters are combined, the first of the combined chapter's title is used in the file name. For example, given an audiobook with the following chapters:
|
||||||
|
|
||||||
|
00:00:00 - 00:00:02 | Part 1
|
||||||
|
00:00:02 - 00:35:00 | Chapter 1
|
||||||
|
00:35:02 - 01:02:00 | Chapter 2
|
||||||
|
01:02:00 - 01:02:02 | Part 2
|
||||||
|
01:02:02 - 01:41:00 | Chapter 3
|
||||||
|
01:41:00 - 02:05:00 | Chapter 4
|
||||||
|
|
||||||
|
The book will be split into the following files:
|
||||||
|
|
||||||
|
00:00:00 - 00:35:00 | Book - 01 - Part 1.m4b
|
||||||
|
00:35:00 - 01:02:00 | Book - 02 - Chapter 2.m4b
|
||||||
|
01:02:00 - 01:41:00 | Book - 03 - Part 2.m4b
|
||||||
|
01:41:00 - 02:05:00 | Book - 04 - Chapter 4.m4b
|
||||||
|
|
||||||
|
That naming may not be desirable for everyone, but it's an easy change to instead use the last of the combined chapter's title in the file name.
|
||||||
|
*/
|
||||||
|
private bool Step_DownloadAudiobookAsMultipleFilesPerChapter()
|
||||||
|
{
|
||||||
|
var zeroProgress = Step_DownloadAudiobook_Start();
|
||||||
|
|
||||||
|
var chapters = DownloadLicense.ChapterInfo.Chapters.ToList();
|
||||||
|
|
||||||
|
// Ensure split files are at least minChapterLength in duration.
|
||||||
|
var splitChapters = new ChapterInfo();
|
||||||
|
|
||||||
|
var runningTotal = TimeSpan.Zero;
|
||||||
|
string title = "";
|
||||||
|
|
||||||
|
for (int i = 0; i < chapters.Count; i++)
|
||||||
|
{
|
||||||
|
if (runningTotal == TimeSpan.Zero)
|
||||||
|
title = chapters[i].Title;
|
||||||
|
|
||||||
|
runningTotal += chapters[i].Duration;
|
||||||
|
|
||||||
|
if (runningTotal >= minChapterLength)
|
||||||
|
{
|
||||||
|
splitChapters.AddChapter(title, runningTotal);
|
||||||
|
runningTotal = TimeSpan.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset, just in case
|
||||||
|
multiPartFilePaths.Clear();
|
||||||
|
|
||||||
|
AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate;
|
||||||
|
if (OutputFormat == OutputFormat.M4b)
|
||||||
|
ConvertToMultiMp4a(splitChapters);
|
||||||
|
else
|
||||||
|
ConvertToMultiMp3(splitChapters);
|
||||||
|
AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate;
|
||||||
|
|
||||||
|
Step_DownloadAudiobook_End(zeroProgress);
|
||||||
|
|
||||||
|
var success = !IsCanceled;
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
foreach (var path in multiPartFilePaths)
|
||||||
|
OnFileCreated(path);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConvertToMultiMp4a(ChapterInfo splitChapters)
|
||||||
|
{
|
||||||
|
var chapterCount = 0;
|
||||||
|
AaxFile.ConvertToMultiMp4a(splitChapters, newSplitCallback =>
|
||||||
|
createOutputFileStream(++chapterCount, splitChapters, newSplitCallback)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConvertToMultiMp3(ChapterInfo splitChapters)
|
||||||
|
{
|
||||||
|
var chapterCount = 0;
|
||||||
|
AaxFile.ConvertToMultiMp3(splitChapters, newSplitCallback =>
|
||||||
|
{
|
||||||
|
createOutputFileStream(++chapterCount, splitChapters, newSplitCallback);
|
||||||
|
newSplitCallback.LameConfig.ID3.Track = chapterCount.ToString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createOutputFileStream(int currentChapter, ChapterInfo splitChapters, NewSplitCallback newSplitCallback)
|
||||||
|
{
|
||||||
|
var fileName = FileUtility.GetMultipartFileName(OutputFileName, currentChapter, splitChapters.Count, newSplitCallback.Chapter.Title);
|
||||||
|
multiPartFilePaths.Add(fileName);
|
||||||
|
|
||||||
|
FileUtility.SaferDelete(fileName);
|
||||||
|
|
||||||
|
newSplitCallback.OutputFile = File.Open(fileName, FileMode.OpenOrCreate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
53
AaxDecrypter/AaxcDownloadSingleConverter.cs
Normal file
53
AaxDecrypter/AaxcDownloadSingleConverter.cs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using AAXClean;
|
||||||
|
using Dinah.Core.StepRunner;
|
||||||
|
using FileManager;
|
||||||
|
|
||||||
|
namespace AaxDecrypter
|
||||||
|
{
|
||||||
|
public class AaxcDownloadSingleConverter : AaxcDownloadConvertBase
|
||||||
|
{
|
||||||
|
protected override StepSequence Steps { get; }
|
||||||
|
|
||||||
|
public AaxcDownloadSingleConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic, OutputFormat outputFormat)
|
||||||
|
: base(outFileName, cacheDirectory, dlLic, outputFormat)
|
||||||
|
{
|
||||||
|
Steps = new StepSequence
|
||||||
|
{
|
||||||
|
Name = "Download and Convert Aaxc To " + OutputFormat,
|
||||||
|
|
||||||
|
["Step 1: Get Aaxc Metadata"] = Step_GetMetadata,
|
||||||
|
["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsSingleFile,
|
||||||
|
["Step 3: Create Cue"] = Step_CreateCue,
|
||||||
|
["Step 4: Cleanup"] = Step_Cleanup,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Step_DownloadAudiobookAsSingleFile()
|
||||||
|
{
|
||||||
|
var zeroProgress = Step_DownloadAudiobook_Start();
|
||||||
|
|
||||||
|
FileUtility.SaferDelete(OutputFileName);
|
||||||
|
|
||||||
|
var outputFile = File.Open(OutputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||||
|
|
||||||
|
AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate;
|
||||||
|
var decryptionResult
|
||||||
|
= OutputFormat == OutputFormat.M4b
|
||||||
|
? AaxFile.ConvertToMp4a(outputFile, DownloadLicense.ChapterInfo)
|
||||||
|
: AaxFile.ConvertToMp3(outputFile);
|
||||||
|
AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate;
|
||||||
|
|
||||||
|
DownloadLicense.ChapterInfo = AaxFile.Chapters;
|
||||||
|
|
||||||
|
Step_DownloadAudiobook_End(zeroProgress);
|
||||||
|
|
||||||
|
var success = decryptionResult == ConversionResult.NoErrorsDetected && !IsCanceled;
|
||||||
|
if (success)
|
||||||
|
base.OnFileCreated(OutputFileName);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -34,7 +34,7 @@ namespace AaxDecrypter
|
|||||||
private string jsonDownloadState => Path.Combine(cacheDir, Path.ChangeExtension(OutputFileName, ".json"));
|
private string jsonDownloadState => Path.Combine(cacheDir, Path.ChangeExtension(OutputFileName, ".json"));
|
||||||
private string tempFile => Path.ChangeExtension(jsonDownloadState, ".tmp");
|
private string tempFile => Path.ChangeExtension(jsonDownloadState, ".tmp");
|
||||||
|
|
||||||
public AudiobookDownloadBase(string outFileName, string cacheDirectory, DownloadLicense dlLic)
|
protected AudiobookDownloadBase(string outFileName, string cacheDirectory, DownloadLicense dlLic)
|
||||||
{
|
{
|
||||||
OutputFileName = ArgumentValidator.EnsureNotNullOrWhiteSpace(outFileName, nameof(outFileName));
|
OutputFileName = ArgumentValidator.EnsureNotNullOrWhiteSpace(outFileName, nameof(outFileName));
|
||||||
|
|
||||||
@ -52,14 +52,11 @@ namespace AaxDecrypter
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract void Cancel();
|
public abstract void Cancel();
|
||||||
protected abstract bool Step1_GetMetadata();
|
|
||||||
|
|
||||||
public virtual void SetCoverArt(byte[] coverArt)
|
public virtual void SetCoverArt(byte[] coverArt)
|
||||||
{
|
{
|
||||||
if (coverArt is null)
|
if (coverArt is not null)
|
||||||
return;
|
OnRetrievedCoverArt(coverArt);
|
||||||
|
|
||||||
OnRetrievedCoverArt(coverArt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Run()
|
public bool Run()
|
||||||
@ -93,7 +90,7 @@ namespace AaxDecrypter
|
|||||||
nfsPersister?.Dispose();
|
nfsPersister?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool Step3_CreateCue()
|
protected bool Step_CreateCue()
|
||||||
{
|
{
|
||||||
// not a critical step. its failure should not prevent future steps from running
|
// not a critical step. its failure should not prevent future steps from running
|
||||||
try
|
try
|
||||||
@ -105,12 +102,12 @@ namespace AaxDecrypter
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Error(ex, $"{nameof(Step3_CreateCue)}. FAILED");
|
Serilog.Log.Logger.Error(ex, $"{nameof(Step_CreateCue)}. FAILED");
|
||||||
}
|
}
|
||||||
return !IsCanceled;
|
return !IsCanceled;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool Step4_Cleanup()
|
protected bool Step_Cleanup()
|
||||||
{
|
{
|
||||||
FileUtility.SaferDelete(jsonDownloadState);
|
FileUtility.SaferDelete(jsonDownloadState);
|
||||||
FileUtility.SaferDelete(tempFile);
|
FileUtility.SaferDelete(tempFile);
|
||||||
|
|||||||
@ -17,10 +17,10 @@ namespace AaxDecrypter
|
|||||||
{
|
{
|
||||||
Name = "Download Mp3 Audiobook",
|
Name = "Download Mp3 Audiobook",
|
||||||
|
|
||||||
["Step 1: Get Mp3 Metadata"] = Step1_GetMetadata,
|
["Step 1: Get Mp3 Metadata"] = Step_GetMetadata,
|
||||||
["Step 2: Download Audiobook"] = Step2_DownloadAudiobookAsSingleFile,
|
["Step 2: Download Audiobook"] = Step_DownloadAudiobookAsSingleFile,
|
||||||
["Step 3: Create Cue"] = Step3_CreateCue,
|
["Step 3: Create Cue"] = Step_CreateCue,
|
||||||
["Step 4: Cleanup"] = Step4_Cleanup,
|
["Step 4: Cleanup"] = Step_Cleanup,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,18 +30,18 @@ namespace AaxDecrypter
|
|||||||
CloseInputFileStream();
|
CloseInputFileStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool Step1_GetMetadata()
|
protected bool Step_GetMetadata()
|
||||||
{
|
{
|
||||||
OnRetrievedCoverArt(null);
|
OnRetrievedCoverArt(null);
|
||||||
|
|
||||||
return !IsCanceled;
|
return !IsCanceled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool Step2_DownloadAudiobookAsSingleFile()
|
private bool Step_DownloadAudiobookAsSingleFile()
|
||||||
{
|
{
|
||||||
DateTime startTime = DateTime.Now;
|
DateTime startTime = DateTime.Now;
|
||||||
|
|
||||||
//MUST put InputFileStream.Length first, because it starts background downloader.
|
// MUST put InputFileStream.Length first, because it starts background downloader.
|
||||||
|
|
||||||
while (InputFileStream.Length > InputFileStream.WritePosition && !InputFileStream.IsCancelled)
|
while (InputFileStream.Length > InputFileStream.WritePosition && !InputFileStream.IsCancelled)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -120,9 +120,10 @@ namespace FileLiberator
|
|||||||
|
|
||||||
var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory;
|
var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory;
|
||||||
|
|
||||||
abDownloader = contentLic.DrmType == AudibleApi.Common.DrmType.Adrm
|
abDownloader
|
||||||
? new AaxcDownloadConverter(outFileName, cacheDir, audiobookDlLic, outputFormat, Configuration.Instance.SplitFilesByChapter)
|
= contentLic.DrmType != AudibleApi.Common.DrmType.Adrm ? new UnencryptedAudiobookDownloader(outFileName, cacheDir, audiobookDlLic)
|
||||||
: new UnencryptedAudiobookDownloader(outFileName, cacheDir, audiobookDlLic);
|
: Configuration.Instance.SplitFilesByChapter ? new AaxcDownloadMultiConverter(outFileName, cacheDir, audiobookDlLic, outputFormat)
|
||||||
|
: new AaxcDownloadSingleConverter(outFileName, cacheDir, audiobookDlLic, outputFormat);
|
||||||
abDownloader.DecryptProgressUpdate += (_, progress) => OnStreamingProgressChanged(progress);
|
abDownloader.DecryptProgressUpdate += (_, progress) => OnStreamingProgressChanged(progress);
|
||||||
abDownloader.DecryptTimeRemaining += (_, remaining) => OnStreamingTimeRemaining(remaining);
|
abDownloader.DecryptTimeRemaining += (_, remaining) => OnStreamingTimeRemaining(remaining);
|
||||||
abDownloader.RetrievedTitle += (_, title) => OnTitleDiscovered(title);
|
abDownloader.RetrievedTitle += (_, title) => OnTitleDiscovered(title);
|
||||||
@ -166,11 +167,10 @@ namespace FileLiberator
|
|||||||
|
|
||||||
private void AaxcDownloader_RetrievedCoverArt(object sender, byte[] e)
|
private void AaxcDownloader_RetrievedCoverArt(object sender, byte[] e)
|
||||||
{
|
{
|
||||||
if (e is null && Configuration.Instance.AllowLibationFixup)
|
|
||||||
OnRequestCoverArt(abDownloader.SetCoverArt);
|
|
||||||
|
|
||||||
if (e is not null)
|
if (e is not null)
|
||||||
OnCoverImageDiscovered(e);
|
OnCoverImageDiscovered(e);
|
||||||
|
else if (Configuration.Instance.AllowLibationFixup)
|
||||||
|
OnRequestCoverArt(abDownloader.SetCoverArt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Move new files to 'Books' directory</summary>
|
/// <summary>Move new files to 'Books' directory</summary>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user