Merge branch 'master' of https://github.com/rmcrackan/Libation
This commit is contained in:
commit
bfb1dbc69a
@ -1,5 +1,4 @@
|
|||||||
using AAXClean;
|
using AAXClean;
|
||||||
using Dinah.Core;
|
|
||||||
using Dinah.Core.IO;
|
using Dinah.Core.IO;
|
||||||
using Dinah.Core.Net.Http;
|
using Dinah.Core.Net.Http;
|
||||||
using Dinah.Core.StepRunner;
|
using Dinah.Core.StepRunner;
|
||||||
@ -8,52 +7,25 @@ using System.IO;
|
|||||||
|
|
||||||
namespace AaxDecrypter
|
namespace AaxDecrypter
|
||||||
{
|
{
|
||||||
public enum OutputFormat { Mp4a, Mp3 }
|
public class AaxcDownloadConverter : AudiobookDownloadBase
|
||||||
public class AaxcDownloadConverter
|
|
||||||
{
|
{
|
||||||
public event EventHandler<AppleTags> RetrievedTags;
|
protected override StepSequence steps { get; }
|
||||||
public event EventHandler<byte[]> RetrievedCoverArt;
|
|
||||||
public event EventHandler<DownloadProgress> DecryptProgressUpdate;
|
|
||||||
public event EventHandler<TimeSpan> DecryptTimeRemaining;
|
|
||||||
|
|
||||||
public string AppName { get; set; } = nameof(AaxcDownloadConverter);
|
|
||||||
|
|
||||||
private string outputFileName { get; }
|
|
||||||
private string cacheDir { get; }
|
|
||||||
private DownloadLicense downloadLicense { get; }
|
|
||||||
private AaxFile aaxFile;
|
private AaxFile aaxFile;
|
||||||
private OutputFormat OutputFormat;
|
|
||||||
|
|
||||||
private StepSequence steps { get; }
|
private OutputFormat OutputFormat { get; }
|
||||||
private NetworkFileStreamPersister nfsPersister;
|
|
||||||
private bool isCanceled { get; set; }
|
|
||||||
private string jsonDownloadState => Path.Combine(cacheDir, Path.GetFileNameWithoutExtension(outputFileName) + ".json");
|
|
||||||
private string tempFile => PathLib.ReplaceExtension(jsonDownloadState, ".aaxc");
|
|
||||||
|
|
||||||
public AaxcDownloadConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic, OutputFormat outputFormat)
|
public AaxcDownloadConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic, OutputFormat outputFormat)
|
||||||
|
:base(outFileName, cacheDirectory, dlLic)
|
||||||
{
|
{
|
||||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(outFileName, nameof(outFileName));
|
|
||||||
outputFileName = outFileName;
|
|
||||||
|
|
||||||
var outDir = Path.GetDirectoryName(outputFileName);
|
|
||||||
if (!Directory.Exists(outDir))
|
|
||||||
throw new ArgumentNullException(nameof(outDir), "Directory does not exist");
|
|
||||||
if (File.Exists(outputFileName))
|
|
||||||
File.Delete(outputFileName);
|
|
||||||
|
|
||||||
if (!Directory.Exists(cacheDirectory))
|
|
||||||
throw new ArgumentNullException(nameof(cacheDirectory), "Directory does not exist");
|
|
||||||
cacheDir = cacheDirectory;
|
|
||||||
|
|
||||||
downloadLicense = ArgumentValidator.EnsureNotNull(dlLic, nameof(dlLic));
|
|
||||||
OutputFormat = outputFormat;
|
OutputFormat = outputFormat;
|
||||||
|
|
||||||
steps = new StepSequence
|
steps = new StepSequence
|
||||||
{
|
{
|
||||||
Name = "Download and Convert Aaxc To " + (outputFormat == OutputFormat.Mp4a ? "M4b" : "Mp3"),
|
Name = "Download and Convert Aaxc To " + OutputFormat,
|
||||||
|
|
||||||
["Step 1: Get Aaxc Metadata"] = Step1_GetMetadata,
|
["Step 1: Get Aaxc Metadata"] = Step1_GetMetadata,
|
||||||
["Step 2: Download Decrypted Audiobook"] = Step2_DownloadAndCombine,
|
["Step 2: Download Decrypted Audiobook"] = Step2_DownloadAudiobook,
|
||||||
["Step 3: Create Cue"] = Step3_CreateCue,
|
["Step 3: Create Cue"] = Step3_CreateCue,
|
||||||
["Step 4: Cleanup"] = Step4_Cleanup,
|
["Step 4: Cleanup"] = Step4_Cleanup,
|
||||||
};
|
};
|
||||||
@ -62,102 +34,56 @@ namespace AaxDecrypter
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Setting cover art by this method will insert the art into the audiobook metadata
|
/// Setting cover art by this method will insert the art into the audiobook metadata
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetCoverArt(byte[] coverArt)
|
public override void SetCoverArt(byte[] coverArt)
|
||||||
{
|
{
|
||||||
if (coverArt is null) return;
|
base.SetCoverArt(coverArt);
|
||||||
|
|
||||||
aaxFile?.AppleTags.SetCoverArt(coverArt);
|
aaxFile?.AppleTags.SetCoverArt(coverArt);
|
||||||
|
|
||||||
RetrievedCoverArt?.Invoke(this, coverArt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Run()
|
protected override bool Step1_GetMetadata()
|
||||||
{
|
{
|
||||||
var (IsSuccess, Elapsed) = steps.Run();
|
aaxFile = new AaxFile(InputFileStream);
|
||||||
|
|
||||||
if (!IsSuccess)
|
OnRetrievedTitle(aaxFile.AppleTags.TitleSansUnabridged);
|
||||||
{
|
OnRetrievedAuthors(aaxFile.AppleTags.FirstAuthor ?? "[unknown]");
|
||||||
Console.WriteLine("WARNING-Conversion failed");
|
OnRetrievedNarrators(aaxFile.AppleTags.Narrator ?? "[unknown]");
|
||||||
return false;
|
OnRetrievedCoverArt(aaxFile.AppleTags.Cover);
|
||||||
}
|
|
||||||
|
|
||||||
var speedup = (int)(aaxFile.Duration.TotalSeconds / (long)Elapsed.TotalSeconds);
|
|
||||||
Serilog.Log.Logger.Information($"Speedup is {speedup}x realtime.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Step1_GetMetadata()
|
|
||||||
{
|
|
||||||
//Get metadata from the file over http
|
|
||||||
|
|
||||||
if (File.Exists(jsonDownloadState))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
nfsPersister = new NetworkFileStreamPersister(jsonDownloadState);
|
|
||||||
//If More than ~1 hour has elapsed since getting the download url, it will expire.
|
|
||||||
//The new url will be to the same file.
|
|
||||||
nfsPersister.NetworkFileStream.SetUriForSameFile(new Uri(downloadLicense.DownloadUrl));
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
FileExt.SafeDelete(jsonDownloadState);
|
|
||||||
FileExt.SafeDelete(tempFile);
|
|
||||||
nfsPersister = NewNetworkFilePersister();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nfsPersister = NewNetworkFilePersister();
|
|
||||||
}
|
|
||||||
|
|
||||||
aaxFile = new AaxFile(nfsPersister.NetworkFileStream);
|
|
||||||
|
|
||||||
RetrievedTags?.Invoke(this, aaxFile.AppleTags);
|
|
||||||
RetrievedCoverArt?.Invoke(this, aaxFile.AppleTags.Cover);
|
|
||||||
|
|
||||||
return !isCanceled;
|
return !isCanceled;
|
||||||
}
|
}
|
||||||
private NetworkFileStreamPersister NewNetworkFilePersister()
|
|
||||||
{
|
|
||||||
var headers = new System.Net.WebHeaderCollection
|
|
||||||
{
|
|
||||||
{ "User-Agent", downloadLicense.UserAgent }
|
|
||||||
};
|
|
||||||
|
|
||||||
var networkFileStream = new NetworkFileStream(tempFile, new Uri(downloadLicense.DownloadUrl), 0, headers);
|
protected override bool Step2_DownloadAudiobook()
|
||||||
return new NetworkFileStreamPersister(networkFileStream, jsonDownloadState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Step2_DownloadAndCombine()
|
|
||||||
{
|
{
|
||||||
var zeroProgress = new DownloadProgress
|
var zeroProgress = new DownloadProgress
|
||||||
{
|
{
|
||||||
BytesReceived = 0,
|
BytesReceived = 0,
|
||||||
ProgressPercentage = 0,
|
ProgressPercentage = 0,
|
||||||
TotalBytesToReceive = nfsPersister.NetworkFileStream.Length
|
TotalBytesToReceive = InputFileStream.Length
|
||||||
};
|
};
|
||||||
|
|
||||||
DecryptProgressUpdate?.Invoke(this, zeroProgress);
|
OnDecryptProgressUpdate(zeroProgress);
|
||||||
|
|
||||||
|
|
||||||
|
aaxFile.SetDecryptionKey(downloadLicense.AudibleKey, downloadLicense.AudibleIV);
|
||||||
|
|
||||||
|
|
||||||
if (File.Exists(outputFileName))
|
if (File.Exists(outputFileName))
|
||||||
FileExt.SafeDelete(outputFileName);
|
FileExt.SafeDelete(outputFileName);
|
||||||
|
|
||||||
FileStream outFile = File.Open(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
var outputFile = File.Open(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||||
|
|
||||||
aaxFile.SetDecryptionKey(downloadLicense.AudibleKey, downloadLicense.AudibleIV);
|
|
||||||
|
|
||||||
aaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate;
|
aaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate;
|
||||||
var decryptionResult = OutputFormat == OutputFormat.Mp4a ? aaxFile.ConvertToMp4a(outFile, downloadLicense.ChapterInfo) : aaxFile.ConvertToMp3(outFile);
|
var decryptionResult = OutputFormat == OutputFormat.M4b ? aaxFile.ConvertToMp4a(outputFile, downloadLicense.ChapterInfo) : aaxFile.ConvertToMp3(outputFile);
|
||||||
aaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate;
|
aaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate;
|
||||||
|
|
||||||
aaxFile.Close();
|
aaxFile.Close();
|
||||||
|
|
||||||
downloadLicense.ChapterInfo = aaxFile.Chapters;
|
downloadLicense.ChapterInfo = aaxFile.Chapters;
|
||||||
|
|
||||||
nfsPersister.Dispose();
|
CloseInputFileStream();
|
||||||
|
|
||||||
DecryptProgressUpdate?.Invoke(this, zeroProgress);
|
OnDecryptProgressUpdate(zeroProgress);
|
||||||
|
|
||||||
return decryptionResult == ConversionResult.NoErrorsDetected && !isCanceled;
|
return decryptionResult == ConversionResult.NoErrorsDetected && !isCanceled;
|
||||||
}
|
}
|
||||||
@ -169,47 +95,28 @@ namespace AaxDecrypter
|
|||||||
double estTimeRemaining = remainingSecsToProcess / e.ProcessSpeed;
|
double estTimeRemaining = remainingSecsToProcess / e.ProcessSpeed;
|
||||||
|
|
||||||
if (double.IsNormal(estTimeRemaining))
|
if (double.IsNormal(estTimeRemaining))
|
||||||
DecryptTimeRemaining?.Invoke(this, TimeSpan.FromSeconds(estTimeRemaining));
|
OnDecryptTimeRemaining(TimeSpan.FromSeconds(estTimeRemaining));
|
||||||
|
|
||||||
double progressPercent = 100 * e.ProcessPosition.TotalSeconds / duration.TotalSeconds;
|
double progressPercent = e.ProcessPosition.TotalSeconds / duration.TotalSeconds;
|
||||||
|
|
||||||
DecryptProgressUpdate?.Invoke(this,
|
OnDecryptProgressUpdate(
|
||||||
new DownloadProgress
|
new DownloadProgress
|
||||||
{
|
{
|
||||||
ProgressPercentage = progressPercent,
|
ProgressPercentage = 100 * progressPercent,
|
||||||
BytesReceived = (long)(nfsPersister.NetworkFileStream.Length * progressPercent),
|
BytesReceived = (long)(InputFileStream.Length * progressPercent),
|
||||||
TotalBytesToReceive = nfsPersister.NetworkFileStream.Length
|
TotalBytesToReceive = InputFileStream.Length
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Step3_CreateCue()
|
public override void Cancel()
|
||||||
{
|
|
||||||
// not a critical step. its failure should not prevent future steps from running
|
|
||||||
try
|
|
||||||
{
|
|
||||||
File.WriteAllText(PathLib.ReplaceExtension(outputFileName, ".cue"), Cue.CreateContents(Path.GetFileName(outputFileName), downloadLicense.ChapterInfo));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Serilog.Log.Logger.Error(ex, $"{nameof(Step3_CreateCue)}. FAILED");
|
|
||||||
}
|
|
||||||
return !isCanceled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Step4_Cleanup()
|
|
||||||
{
|
|
||||||
FileExt.SafeDelete(jsonDownloadState);
|
|
||||||
FileExt.SafeDelete(tempFile);
|
|
||||||
return !isCanceled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Cancel()
|
|
||||||
{
|
{
|
||||||
isCanceled = true;
|
isCanceled = true;
|
||||||
aaxFile?.Cancel();
|
aaxFile?.Cancel();
|
||||||
aaxFile?.Dispose();
|
aaxFile?.Dispose();
|
||||||
nfsPersister?.NetworkFileStream?.Close();
|
CloseInputFileStream();
|
||||||
nfsPersister?.Dispose();
|
}
|
||||||
}
|
|
||||||
|
protected override int GetSpeedup(TimeSpan elapsed)
|
||||||
|
=> (int)(aaxFile.Duration.TotalSeconds / (long)elapsed.TotalSeconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
165
AaxDecrypter/AudiobookDownloadBase.cs
Normal file
165
AaxDecrypter/AudiobookDownloadBase.cs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
using Dinah.Core;
|
||||||
|
using Dinah.Core.IO;
|
||||||
|
using Dinah.Core.Net.Http;
|
||||||
|
using Dinah.Core.StepRunner;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace AaxDecrypter
|
||||||
|
{
|
||||||
|
public enum OutputFormat { M4b, Mp3 }
|
||||||
|
|
||||||
|
public abstract class AudiobookDownloadBase
|
||||||
|
{
|
||||||
|
public event EventHandler<string> RetrievedTitle;
|
||||||
|
public event EventHandler<string> RetrievedAuthors;
|
||||||
|
public event EventHandler<string> RetrievedNarrators;
|
||||||
|
public event EventHandler<byte[]> RetrievedCoverArt;
|
||||||
|
public event EventHandler<DownloadProgress> DecryptProgressUpdate;
|
||||||
|
public event EventHandler<TimeSpan> DecryptTimeRemaining;
|
||||||
|
|
||||||
|
public string AppName { get; set; }
|
||||||
|
|
||||||
|
protected bool isCanceled { get; set; }
|
||||||
|
protected string outputFileName { get; }
|
||||||
|
protected string cacheDir { get; }
|
||||||
|
protected DownloadLicense downloadLicense { get; }
|
||||||
|
protected NetworkFileStream InputFileStream => (nfsPersister ??= OpenNetworkFileStream()).NetworkFileStream;
|
||||||
|
|
||||||
|
|
||||||
|
protected abstract StepSequence steps { get; }
|
||||||
|
private NetworkFileStreamPersister nfsPersister;
|
||||||
|
|
||||||
|
private string jsonDownloadState => Path.Combine(cacheDir, Path.GetFileNameWithoutExtension(outputFileName) + ".json");
|
||||||
|
private string tempFile => PathLib.ReplaceExtension(jsonDownloadState, ".tmp");
|
||||||
|
|
||||||
|
public AudiobookDownloadBase(string outFileName, string cacheDirectory, DownloadLicense dlLic)
|
||||||
|
{
|
||||||
|
AppName = GetType().Name;
|
||||||
|
|
||||||
|
ArgumentValidator.EnsureNotNullOrWhiteSpace(outFileName, nameof(outFileName));
|
||||||
|
outputFileName = outFileName;
|
||||||
|
|
||||||
|
var outDir = Path.GetDirectoryName(outputFileName);
|
||||||
|
if (!Directory.Exists(outDir))
|
||||||
|
throw new ArgumentNullException(nameof(outDir), "Directory does not exist");
|
||||||
|
if (File.Exists(outputFileName))
|
||||||
|
File.Delete(outputFileName);
|
||||||
|
|
||||||
|
if (!Directory.Exists(cacheDirectory))
|
||||||
|
throw new ArgumentNullException(nameof(cacheDirectory), "Directory does not exist");
|
||||||
|
cacheDir = cacheDirectory;
|
||||||
|
|
||||||
|
downloadLicense = ArgumentValidator.EnsureNotNull(dlLic, nameof(dlLic));
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Cancel();
|
||||||
|
protected abstract int GetSpeedup(TimeSpan elapsed);
|
||||||
|
protected abstract bool Step2_DownloadAudiobook();
|
||||||
|
protected abstract bool Step1_GetMetadata();
|
||||||
|
|
||||||
|
public virtual void SetCoverArt(byte[] coverArt)
|
||||||
|
{
|
||||||
|
if (coverArt is null) return;
|
||||||
|
|
||||||
|
OnRetrievedCoverArt(coverArt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public bool Run()
|
||||||
|
{
|
||||||
|
var (IsSuccess, Elapsed) = steps.Run();
|
||||||
|
|
||||||
|
if (!IsSuccess)
|
||||||
|
{
|
||||||
|
Console.WriteLine("WARNING-Conversion failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serilog.Log.Logger.Information($"Speedup is {GetSpeedup(Elapsed)}x realtime.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OnRetrievedTitle(string title)
|
||||||
|
=> RetrievedTitle?.Invoke(this, title);
|
||||||
|
protected void OnRetrievedAuthors(string authors)
|
||||||
|
=> RetrievedAuthors?.Invoke(this, authors);
|
||||||
|
protected void OnRetrievedNarrators(string narrators)
|
||||||
|
=> RetrievedNarrators?.Invoke(this, narrators);
|
||||||
|
protected void OnRetrievedCoverArt(byte[] coverArt)
|
||||||
|
=> RetrievedCoverArt?.Invoke(this, coverArt);
|
||||||
|
protected void OnDecryptProgressUpdate(DownloadProgress downloadProgress)
|
||||||
|
=> DecryptProgressUpdate?.Invoke(this, downloadProgress);
|
||||||
|
protected void OnDecryptTimeRemaining(TimeSpan timeRemaining)
|
||||||
|
=> DecryptTimeRemaining?.Invoke(this, timeRemaining);
|
||||||
|
|
||||||
|
protected void CloseInputFileStream()
|
||||||
|
{
|
||||||
|
nfsPersister?.NetworkFileStream?.Close();
|
||||||
|
nfsPersister?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool Step3_CreateCue()
|
||||||
|
{
|
||||||
|
// not a critical step. its failure should not prevent future steps from running
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllText(PathLib.ReplaceExtension(outputFileName, ".cue"), Cue.CreateContents(Path.GetFileName(outputFileName), downloadLicense.ChapterInfo));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Error(ex, $"{nameof(Step3_CreateCue)}. FAILED");
|
||||||
|
}
|
||||||
|
return !isCanceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool Step4_Cleanup()
|
||||||
|
{
|
||||||
|
FileExt.SafeDelete(jsonDownloadState);
|
||||||
|
FileExt.SafeDelete(tempFile);
|
||||||
|
return !isCanceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NetworkFileStreamPersister OpenNetworkFileStream()
|
||||||
|
{
|
||||||
|
NetworkFileStreamPersister nfsp;
|
||||||
|
|
||||||
|
if (File.Exists(jsonDownloadState))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nfsp = new NetworkFileStreamPersister(jsonDownloadState);
|
||||||
|
//If More than ~1 hour has elapsed since getting the download url, it will expire.
|
||||||
|
//The new url will be to the same file.
|
||||||
|
nfsp.NetworkFileStream.SetUriForSameFile(new Uri(downloadLicense.DownloadUrl));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
FileExt.SafeDelete(jsonDownloadState);
|
||||||
|
FileExt.SafeDelete(tempFile);
|
||||||
|
nfsp = NewNetworkFilePersister();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nfsp = NewNetworkFilePersister();
|
||||||
|
}
|
||||||
|
return nfsp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NetworkFileStreamPersister NewNetworkFilePersister()
|
||||||
|
{
|
||||||
|
var headers = new System.Net.WebHeaderCollection
|
||||||
|
{
|
||||||
|
{ "User-Agent", downloadLicense.UserAgent }
|
||||||
|
};
|
||||||
|
|
||||||
|
var networkFileStream = new NetworkFileStream(tempFile, new Uri(downloadLicense.DownloadUrl), 0, headers);
|
||||||
|
return new NetworkFileStreamPersister(networkFileStream, jsonDownloadState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -83,7 +83,7 @@ namespace AaxDecrypter
|
|||||||
private FileStream _readFile { get; }
|
private FileStream _readFile { get; }
|
||||||
private Stream _networkStream { get; set; }
|
private Stream _networkStream { get; set; }
|
||||||
private bool hasBegunDownloading { get; set; }
|
private bool hasBegunDownloading { get; set; }
|
||||||
private bool isCancelled { get; set; }
|
public bool IsCancelled { get; private set; }
|
||||||
private EventWaitHandle downloadEnded { get; set; }
|
private EventWaitHandle downloadEnded { get; set; }
|
||||||
private EventWaitHandle downloadedPiece { get; set; }
|
private EventWaitHandle downloadedPiece { get; set; }
|
||||||
|
|
||||||
@ -238,7 +238,7 @@ namespace AaxDecrypter
|
|||||||
downloadedPiece.Set();
|
downloadedPiece.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (downloadPosition < ContentLength && !isCancelled);
|
} while (downloadPosition < ContentLength && !IsCancelled);
|
||||||
|
|
||||||
_writeFile.Close();
|
_writeFile.Close();
|
||||||
_networkStream.Close();
|
_networkStream.Close();
|
||||||
@ -248,7 +248,7 @@ namespace AaxDecrypter
|
|||||||
downloadedPiece.Set();
|
downloadedPiece.Set();
|
||||||
downloadEnded.Set();
|
downloadEnded.Set();
|
||||||
|
|
||||||
if (!isCancelled && WritePosition < ContentLength)
|
if (!IsCancelled && WritePosition < ContentLength)
|
||||||
throw new WebException($"Downloaded size (0x{WritePosition:X10}) is less than {nameof(ContentLength)} (0x{ContentLength:X10}).");
|
throw new WebException($"Downloaded size (0x{WritePosition:X10}) is less than {nameof(ContentLength)} (0x{ContentLength:X10}).");
|
||||||
|
|
||||||
if (WritePosition > ContentLength)
|
if (WritePosition > ContentLength)
|
||||||
@ -421,12 +421,12 @@ namespace AaxDecrypter
|
|||||||
/// <param name="requiredPosition">The minimum required flished data length in <see cref="SaveFilePath"/>.</param>
|
/// <param name="requiredPosition">The minimum required flished data length in <see cref="SaveFilePath"/>.</param>
|
||||||
private void WaitToPosition(long requiredPosition)
|
private void WaitToPosition(long requiredPosition)
|
||||||
{
|
{
|
||||||
while (requiredPosition > WritePosition && !isCancelled && hasBegunDownloading && !downloadedPiece.WaitOne(1000)) ;
|
while (requiredPosition > WritePosition && !IsCancelled && hasBegunDownloading && !downloadedPiece.WaitOne(1000)) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Close()
|
public override void Close()
|
||||||
{
|
{
|
||||||
isCancelled = true;
|
IsCancelled = true;
|
||||||
|
|
||||||
while (downloadEnded is not null && !downloadEnded.WaitOne(1000)) ;
|
while (downloadEnded is not null && !downloadEnded.WaitOne(1000)) ;
|
||||||
|
|
||||||
|
|||||||
86
AaxDecrypter/UnencryptedAudiobookDownloader.cs
Normal file
86
AaxDecrypter/UnencryptedAudiobookDownloader.cs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
using Dinah.Core.IO;
|
||||||
|
using Dinah.Core.Net.Http;
|
||||||
|
using Dinah.Core.StepRunner;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace AaxDecrypter
|
||||||
|
{
|
||||||
|
public class UnencryptedAudiobookDownloader : AudiobookDownloadBase
|
||||||
|
{
|
||||||
|
protected override StepSequence steps { get; }
|
||||||
|
|
||||||
|
public UnencryptedAudiobookDownloader(string outFileName, string cacheDirectory, DownloadLicense dlLic)
|
||||||
|
: base(outFileName, cacheDirectory, dlLic)
|
||||||
|
{
|
||||||
|
|
||||||
|
steps = new StepSequence
|
||||||
|
{
|
||||||
|
Name = "Download Mp3 Audiobook",
|
||||||
|
|
||||||
|
["Step 1: Get Mp3 Metadata"] = Step1_GetMetadata,
|
||||||
|
["Step 2: Download Audiobook"] = Step2_DownloadAudiobook,
|
||||||
|
["Step 3: Create Cue"] = Step3_CreateCue,
|
||||||
|
["Step 4: Cleanup"] = Step4_Cleanup,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Cancel()
|
||||||
|
{
|
||||||
|
isCanceled = true;
|
||||||
|
CloseInputFileStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override int GetSpeedup(TimeSpan elapsed)
|
||||||
|
{
|
||||||
|
//Not implemented
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool Step1_GetMetadata()
|
||||||
|
{
|
||||||
|
OnRetrievedCoverArt(null);
|
||||||
|
|
||||||
|
return !isCanceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool Step2_DownloadAudiobook()
|
||||||
|
{
|
||||||
|
DateTime startTime = DateTime.Now;
|
||||||
|
|
||||||
|
//MUST put InputFileStream.Length first, because it starts background downloader.
|
||||||
|
|
||||||
|
while (InputFileStream.Length > InputFileStream.WritePosition && !InputFileStream.IsCancelled)
|
||||||
|
{
|
||||||
|
var rate = InputFileStream.WritePosition / (DateTime.Now - startTime).TotalSeconds;
|
||||||
|
|
||||||
|
var estTimeRemaining = (InputFileStream.Length - InputFileStream.WritePosition) / rate;
|
||||||
|
|
||||||
|
if (double.IsNormal(estTimeRemaining))
|
||||||
|
OnDecryptTimeRemaining(TimeSpan.FromSeconds(estTimeRemaining));
|
||||||
|
|
||||||
|
var progressPercent = (double)InputFileStream.WritePosition / InputFileStream.Length;
|
||||||
|
|
||||||
|
OnDecryptProgressUpdate(
|
||||||
|
new DownloadProgress
|
||||||
|
{
|
||||||
|
ProgressPercentage = 100 * progressPercent,
|
||||||
|
BytesReceived = (long)(InputFileStream.Length * progressPercent),
|
||||||
|
TotalBytesToReceive = InputFileStream.Length
|
||||||
|
});
|
||||||
|
Thread.Sleep(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseInputFileStream();
|
||||||
|
|
||||||
|
if (File.Exists(outputFileName))
|
||||||
|
FileExt.SafeDelete(outputFileName);
|
||||||
|
|
||||||
|
FileExt.SafeMove(InputFileStream.SaveFilePath, outputFileName);
|
||||||
|
|
||||||
|
return !isCanceled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -29,21 +29,6 @@ namespace FileLiberator
|
|||||||
public event EventHandler<string> StatusUpdate;
|
public event EventHandler<string> StatusUpdate;
|
||||||
public event EventHandler<LibraryBook> Completed;
|
public event EventHandler<LibraryBook> Completed;
|
||||||
|
|
||||||
public ConvertToMp3()
|
|
||||||
{
|
|
||||||
RequestCoverArt += (o, e) => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(RequestCoverArt) });
|
|
||||||
TitleDiscovered += (o, e) => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(TitleDiscovered), Title = e });
|
|
||||||
AuthorsDiscovered += (o, e) => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(AuthorsDiscovered), Authors = e });
|
|
||||||
NarratorsDiscovered += (o, e) => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(NarratorsDiscovered), Narrators = e });
|
|
||||||
CoverImageDiscovered += (o, e) => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(CoverImageDiscovered), CoverImageBytes = e?.Length });
|
|
||||||
|
|
||||||
StreamingBegin += (o, e) => Serilog.Log.Logger.Information("Event fired {@DebugInfo}", new { Name = nameof(StreamingBegin), Message = e });
|
|
||||||
StreamingCompleted += (o, e) => Serilog.Log.Logger.Information("Event fired {@DebugInfo}", new { Name = nameof(StreamingCompleted), Message = e });
|
|
||||||
|
|
||||||
Begin += (o, e) => Serilog.Log.Logger.Information("Event fired {@DebugInfo}", new { Name = nameof(Begin), Book = e.LogFriendly() });
|
|
||||||
Completed += (o, e) => Serilog.Log.Logger.Information("Event fired {@DebugInfo}", new { Name = nameof(Completed), Book = e.LogFriendly() });
|
|
||||||
}
|
|
||||||
|
|
||||||
private long fileSize;
|
private long fileSize;
|
||||||
private string Mp3FileName(string m4bPath) => m4bPath is null ? string.Empty : PathLib.ReplaceExtension(m4bPath, ".mp3");
|
private string Mp3FileName(string m4bPath) => m4bPath is null ? string.Empty : PathLib.ReplaceExtension(m4bPath, ".mp3");
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ namespace FileLiberator
|
|||||||
{
|
{
|
||||||
public class DownloadDecryptBook : IAudioDecodable
|
public class DownloadDecryptBook : IAudioDecodable
|
||||||
{
|
{
|
||||||
private AaxcDownloadConverter aaxcDownloader;
|
private AudiobookDownloadBase aaxcDownloader;
|
||||||
|
|
||||||
public event EventHandler<TimeSpan> StreamingTimeRemaining;
|
public event EventHandler<TimeSpan> StreamingTimeRemaining;
|
||||||
public event EventHandler<Action<byte[]>> RequestCoverArt;
|
public event EventHandler<Action<byte[]>> RequestCoverArt;
|
||||||
@ -30,21 +30,6 @@ namespace FileLiberator
|
|||||||
public event EventHandler<string> StatusUpdate;
|
public event EventHandler<string> StatusUpdate;
|
||||||
public event EventHandler<LibraryBook> Completed;
|
public event EventHandler<LibraryBook> Completed;
|
||||||
|
|
||||||
public DownloadDecryptBook()
|
|
||||||
{
|
|
||||||
RequestCoverArt += (o, e) => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(RequestCoverArt) });
|
|
||||||
TitleDiscovered += (o, e) => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(TitleDiscovered), Title = e });
|
|
||||||
AuthorsDiscovered += (o, e) => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(AuthorsDiscovered), Authors = e });
|
|
||||||
NarratorsDiscovered += (o, e) => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(NarratorsDiscovered), Narrators = e });
|
|
||||||
CoverImageDiscovered += (o, e) => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(CoverImageDiscovered), CoverImageBytes = e?.Length });
|
|
||||||
|
|
||||||
StreamingBegin += (o, e) => Serilog.Log.Logger.Information("Event fired {@DebugInfo}", new { Name = nameof(StreamingBegin), Message = e });
|
|
||||||
StreamingCompleted += (o, e) => Serilog.Log.Logger.Information("Event fired {@DebugInfo}", new { Name = nameof(StreamingCompleted), Message = e });
|
|
||||||
|
|
||||||
Begin += (o, e) => Serilog.Log.Logger.Information("Event fired {@DebugInfo}", new { Name = nameof(Begin), Book = e.LogFriendly() });
|
|
||||||
Completed += (o, e) => Serilog.Log.Logger.Information("Event fired {@DebugInfo}", new { Name = nameof(Completed), Book = e.LogFriendly() });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<StatusHandler> ProcessAsync(LibraryBook libraryBook)
|
public async Task<StatusHandler> ProcessAsync(LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
Begin?.Invoke(this, libraryBook);
|
Begin?.Invoke(this, libraryBook);
|
||||||
@ -54,7 +39,7 @@ namespace FileLiberator
|
|||||||
if (libraryBook.Book.Audio_Exists)
|
if (libraryBook.Book.Audio_Exists)
|
||||||
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
|
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
|
||||||
|
|
||||||
var outputAudioFilename = await aaxToM4bConverterDecryptAsync(AudibleFileStorage.DownloadsInProgress, AudibleFileStorage.DecryptInProgress, libraryBook);
|
var outputAudioFilename = await downloadAudiobookAsync(AudibleFileStorage.DownloadsInProgress, AudibleFileStorage.DecryptInProgress, libraryBook);
|
||||||
|
|
||||||
// decrypt failed
|
// decrypt failed
|
||||||
if (outputAudioFilename is null)
|
if (outputAudioFilename is null)
|
||||||
@ -76,7 +61,7 @@ namespace FileLiberator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> aaxToM4bConverterDecryptAsync(string cacheDir, string destinationDir, LibraryBook libraryBook)
|
private async Task<string> downloadAudiobookAsync(string cacheDir, string destinationDir, LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
StreamingBegin?.Invoke(this, $"Begin decrypting {libraryBook}");
|
StreamingBegin?.Invoke(this, $"Begin decrypting {libraryBook}");
|
||||||
|
|
||||||
@ -87,7 +72,7 @@ namespace FileLiberator
|
|||||||
var api = await libraryBook.GetApiAsync();
|
var api = await libraryBook.GetApiAsync();
|
||||||
var contentLic = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId);
|
var contentLic = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId);
|
||||||
|
|
||||||
var aaxcDecryptDlLic = new DownloadLicense
|
var audiobookDlLic = new DownloadLicense
|
||||||
(
|
(
|
||||||
contentLic?.ContentMetadata?.ContentUrl?.OfflineUrl,
|
contentLic?.ContentMetadata?.ContentUrl?.OfflineUrl,
|
||||||
contentLic?.Voucher?.Key,
|
contentLic?.Voucher?.Key,
|
||||||
@ -95,32 +80,31 @@ namespace FileLiberator
|
|||||||
Resources.USER_AGENT
|
Resources.USER_AGENT
|
||||||
);
|
);
|
||||||
|
|
||||||
if (Configuration.Instance.AllowLibationFixup)
|
//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)
|
||||||
{
|
{
|
||||||
aaxcDecryptDlLic.ChapterInfo = new AAXClean.ChapterInfo();
|
audiobookDlLic.ChapterInfo = new AAXClean.ChapterInfo();
|
||||||
|
|
||||||
foreach (var chap in contentLic.ContentMetadata?.ChapterInfo?.Chapters)
|
foreach (var chap in contentLic.ContentMetadata?.ChapterInfo?.Chapters)
|
||||||
aaxcDecryptDlLic.ChapterInfo.AddChapter(chap.Title, TimeSpan.FromMilliseconds(chap.LengthMs));
|
audiobookDlLic.ChapterInfo.AddChapter(chap.Title, TimeSpan.FromMilliseconds(chap.LengthMs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var outFileName = Path.Combine(destinationDir, $"{PathLib.ToPathSafeString(libraryBook.Book.Title)} [{libraryBook.Book.AudibleProductId}].{outputFormat.ToString().ToLower()}");
|
||||||
|
|
||||||
var format = Configuration.Instance.DecryptToLossy ? OutputFormat.Mp3 : OutputFormat.Mp4a;
|
aaxcDownloader = contentLic.DrmType == AudibleApi.Common.DrmType.Adrm ? new AaxcDownloadConverter(outFileName, cacheDir, audiobookDlLic, outputFormat) { AppName = "Libation" } : new UnencryptedAudiobookDownloader(outFileName, cacheDir, audiobookDlLic);
|
||||||
|
|
||||||
var extension = format switch
|
|
||||||
{
|
|
||||||
OutputFormat.Mp4a => "m4b",
|
|
||||||
OutputFormat.Mp3 => "mp3",
|
|
||||||
_ => throw new NotImplementedException(),
|
|
||||||
};
|
|
||||||
|
|
||||||
var outFileName = Path.Combine(destinationDir, $"{PathLib.ToPathSafeString(libraryBook.Book.Title)} [{libraryBook.Book.AudibleProductId}].{extension}");
|
|
||||||
|
|
||||||
|
|
||||||
aaxcDownloader = new AaxcDownloadConverter(outFileName, cacheDir, aaxcDecryptDlLic, format) { AppName = "Libation" };
|
|
||||||
aaxcDownloader.DecryptProgressUpdate += (s, progress) => StreamingProgressChanged?.Invoke(this, progress);
|
aaxcDownloader.DecryptProgressUpdate += (s, progress) => StreamingProgressChanged?.Invoke(this, progress);
|
||||||
aaxcDownloader.DecryptTimeRemaining += (s, remaining) => StreamingTimeRemaining?.Invoke(this, remaining);
|
aaxcDownloader.DecryptTimeRemaining += (s, remaining) => StreamingTimeRemaining?.Invoke(this, remaining);
|
||||||
|
aaxcDownloader.RetrievedTitle += (s, title) => TitleDiscovered?.Invoke(this, title);
|
||||||
|
aaxcDownloader.RetrievedAuthors += (s, authors) => AuthorsDiscovered?.Invoke(this, authors);
|
||||||
|
aaxcDownloader.RetrievedNarrators += (s, narrators) => NarratorsDiscovered?.Invoke(this, narrators);
|
||||||
aaxcDownloader.RetrievedCoverArt += AaxcDownloader_RetrievedCoverArt;
|
aaxcDownloader.RetrievedCoverArt += AaxcDownloader_RetrievedCoverArt;
|
||||||
aaxcDownloader.RetrievedTags += aaxcDownloader_RetrievedTags;
|
|
||||||
|
|
||||||
// REAL WORK DONE HERE
|
// REAL WORK DONE HERE
|
||||||
var success = await Task.Run(() => aaxcDownloader.Run());
|
var success = await Task.Run(() => aaxcDownloader.Run());
|
||||||
@ -137,7 +121,6 @@ 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)
|
if (e is null && Configuration.Instance.AllowLibationFixup)
|
||||||
@ -151,13 +134,6 @@ namespace FileLiberator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void aaxcDownloader_RetrievedTags(object sender, AAXClean.AppleTags e)
|
|
||||||
{
|
|
||||||
TitleDiscovered?.Invoke(this, e.TitleSansUnabridged);
|
|
||||||
AuthorsDiscovered?.Invoke(this, e.FirstAuthor ?? "[unknown]");
|
|
||||||
NarratorsDiscovered?.Invoke(this, e.Narrator ?? "[unknown]");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (string destinationDir, bool movedAudioFile) MoveFilesToBooksDir(Book product, string outputAudioFilename)
|
private static (string destinationDir, bool movedAudioFile) MoveFilesToBooksDir(Book product, string outputAudioFilename)
|
||||||
{
|
{
|
||||||
// create final directory. move each file into it. MOVE AUDIO FILE LAST
|
// create final directory. move each file into it. MOVE AUDIO FILE LAST
|
||||||
|
|||||||
@ -13,12 +13,6 @@ namespace FileLiberator
|
|||||||
public event EventHandler<string> StreamingCompleted;
|
public event EventHandler<string> StreamingCompleted;
|
||||||
public event EventHandler<TimeSpan> StreamingTimeRemaining;
|
public event EventHandler<TimeSpan> StreamingTimeRemaining;
|
||||||
|
|
||||||
public DownloadFile()
|
|
||||||
{
|
|
||||||
StreamingBegin += (o, e) => Serilog.Log.Logger.Information("Event fired {@DebugInfo}", new { Name = nameof(StreamingBegin), Message = e });
|
|
||||||
StreamingCompleted += (o, e) => Serilog.Log.Logger.Information("Event fired {@DebugInfo}", new { Name = nameof(StreamingCompleted), Message = e });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> PerformDownloadFileAsync(string downloadUrl, string proposedDownloadFilePath)
|
public async Task<string> PerformDownloadFileAsync(string downloadUrl, string proposedDownloadFilePath)
|
||||||
{
|
{
|
||||||
var client = new HttpClient();
|
var client = new HttpClient();
|
||||||
|
|||||||
@ -17,15 +17,6 @@ namespace FileLiberator
|
|||||||
=> !string.IsNullOrWhiteSpace(getdownloadUrl(libraryBook))
|
=> !string.IsNullOrWhiteSpace(getdownloadUrl(libraryBook))
|
||||||
&& !libraryBook.Book.PDF_Exists;
|
&& !libraryBook.Book.PDF_Exists;
|
||||||
|
|
||||||
public DownloadPdf()
|
|
||||||
{
|
|
||||||
StreamingBegin += (o, e) => Serilog.Log.Logger.Information("Event fired {@DebugInfo}", new { Name = nameof(StreamingBegin), Message = e });
|
|
||||||
StreamingCompleted += (o, e) => Serilog.Log.Logger.Information("Event fired {@DebugInfo}", new { Name = nameof(StreamingCompleted), Message = e });
|
|
||||||
|
|
||||||
Begin += (o, e) => Serilog.Log.Logger.Information("Event fired {@DebugInfo}", new { Name = nameof(Begin), Book = e.LogFriendly() });
|
|
||||||
Completed += (o, e) => Serilog.Log.Logger.Information("Event fired {@DebugInfo}", new { Name = nameof(Completed), Book = e.LogFriendly() });
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook)
|
public override async Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
var proposedDownloadFilePath = getProposedDownloadFilePath(libraryBook);
|
var proposedDownloadFilePath = getProposedDownloadFilePath(libraryBook);
|
||||||
|
|||||||
@ -24,7 +24,10 @@ namespace LibationWinForms.BookLiberation
|
|||||||
base.OnBegin(sender, libraryBook);
|
base.OnBegin(sender, libraryBook);
|
||||||
}
|
}
|
||||||
public override void OnCompleted(object sender, LibraryBook libraryBook)
|
public override void OnCompleted(object sender, LibraryBook libraryBook)
|
||||||
=> LogMe.Info($"Convert Step, Completed: {libraryBook.Book}{Environment.NewLine}");
|
{
|
||||||
|
base.OnCompleted(sender, libraryBook);
|
||||||
|
LogMe.Info($"Convert Step, Completed: {libraryBook.Book}{Environment.NewLine}");
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,8 @@ namespace LibationWinForms.BookLiberation
|
|||||||
#region IProcessable event handler overrides
|
#region IProcessable event handler overrides
|
||||||
public override void OnBegin(object sender, LibraryBook libraryBook)
|
public override void OnBegin(object sender, LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
|
base.OnBegin(sender, libraryBook);
|
||||||
|
|
||||||
GetCoverArtDelegate = () => FileManager.PictureStorage.GetPictureSynchronously(
|
GetCoverArtDelegate = () => FileManager.PictureStorage.GetPictureSynchronously(
|
||||||
new FileManager.PictureDefinition(
|
new FileManager.PictureDefinition(
|
||||||
libraryBook.Book.PictureId,
|
libraryBook.Book.PictureId,
|
||||||
@ -41,6 +43,7 @@ namespace LibationWinForms.BookLiberation
|
|||||||
#region IStreamable event handler overrides
|
#region IStreamable event handler overrides
|
||||||
public override void OnStreamingProgressChanged(object sender, DownloadProgress downloadProgress)
|
public override void OnStreamingProgressChanged(object sender, DownloadProgress downloadProgress)
|
||||||
{
|
{
|
||||||
|
base.OnStreamingProgressChanged(sender, downloadProgress);
|
||||||
if (!downloadProgress.ProgressPercentage.HasValue)
|
if (!downloadProgress.ProgressPercentage.HasValue)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -51,16 +54,23 @@ namespace LibationWinForms.BookLiberation
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override void OnStreamingTimeRemaining(object sender, TimeSpan timeRemaining)
|
public override void OnStreamingTimeRemaining(object sender, TimeSpan timeRemaining)
|
||||||
=> updateRemainingTime((int)timeRemaining.TotalSeconds);
|
{
|
||||||
|
base.OnStreamingTimeRemaining(sender, timeRemaining);
|
||||||
|
updateRemainingTime((int)timeRemaining.TotalSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region IAudioDecodable event handlers
|
#region IAudioDecodable event handlers
|
||||||
public override void OnRequestCoverArt(object sender, Action<byte[]> setCoverArtDelegate)
|
public override void OnRequestCoverArt(object sender, Action<byte[]> setCoverArtDelegate)
|
||||||
=> setCoverArtDelegate(GetCoverArtDelegate?.Invoke());
|
{
|
||||||
|
base.OnRequestCoverArt(sender, setCoverArtDelegate);
|
||||||
|
setCoverArtDelegate(GetCoverArtDelegate?.Invoke());
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnTitleDiscovered(object sender, string title)
|
public override void OnTitleDiscovered(object sender, string title)
|
||||||
{
|
{
|
||||||
|
base.OnTitleDiscovered(sender, title);
|
||||||
this.UIThreadAsync(() => this.Text = DecodeActionName + " " + title);
|
this.UIThreadAsync(() => this.Text = DecodeActionName + " " + title);
|
||||||
this.title = title;
|
this.title = title;
|
||||||
updateBookInfo();
|
updateBookInfo();
|
||||||
@ -68,18 +78,23 @@ namespace LibationWinForms.BookLiberation
|
|||||||
|
|
||||||
public override void OnAuthorsDiscovered(object sender, string authors)
|
public override void OnAuthorsDiscovered(object sender, string authors)
|
||||||
{
|
{
|
||||||
|
base.OnAuthorsDiscovered(sender, authors);
|
||||||
authorNames = authors;
|
authorNames = authors;
|
||||||
updateBookInfo();
|
updateBookInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnNarratorsDiscovered(object sender, string narrators)
|
public override void OnNarratorsDiscovered(object sender, string narrators)
|
||||||
{
|
{
|
||||||
|
base.OnNarratorsDiscovered(sender, narrators);
|
||||||
narratorNames = narrators;
|
narratorNames = narrators;
|
||||||
updateBookInfo();
|
updateBookInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnCoverImageDiscovered(object sender, byte[] coverArt)
|
public override void OnCoverImageDiscovered(object sender, byte[] coverArt)
|
||||||
=> pictureBox1.UIThreadAsync(() => pictureBox1.Image = Dinah.Core.Drawing.ImageReader.ToImage(coverArt));
|
{
|
||||||
|
base.OnCoverImageDiscovered(sender, coverArt);
|
||||||
|
pictureBox1.UIThreadAsync(() => pictureBox1.Image = Dinah.Core.Drawing.ImageReader.ToImage(coverArt));
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
// thread-safe UI updates
|
// thread-safe UI updates
|
||||||
|
|||||||
@ -24,7 +24,10 @@ namespace LibationWinForms.BookLiberation
|
|||||||
base.OnBegin(sender, libraryBook);
|
base.OnBegin(sender, libraryBook);
|
||||||
}
|
}
|
||||||
public override void OnCompleted(object sender, LibraryBook libraryBook)
|
public override void OnCompleted(object sender, LibraryBook libraryBook)
|
||||||
=> LogMe.Info($"Download & Decrypt Step, Completed: {libraryBook.Book}{Environment.NewLine}");
|
{
|
||||||
|
base.OnCompleted(sender, libraryBook);
|
||||||
|
LogMe.Info($"Download & Decrypt Step, Completed: {libraryBook.Book}{Environment.NewLine}");
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@ -137,24 +137,36 @@ namespace LibationWinForms.BookLiberation.BaseForms
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region IStreamable event handlers
|
#region IStreamable event handlers
|
||||||
public virtual void OnStreamingBegin(object sender, string beginString) { }
|
public virtual void OnStreamingBegin(object sender, string beginString)
|
||||||
|
=> Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(IStreamable.StreamingBegin), Message = beginString });
|
||||||
public virtual void OnStreamingProgressChanged(object sender, DownloadProgress downloadProgress) { }
|
public virtual void OnStreamingProgressChanged(object sender, DownloadProgress downloadProgress) { }
|
||||||
public virtual void OnStreamingTimeRemaining(object sender, TimeSpan timeRemaining) { }
|
public virtual void OnStreamingTimeRemaining(object sender, TimeSpan timeRemaining) { }
|
||||||
public virtual void OnStreamingCompleted(object sender, string completedString) { }
|
public virtual void OnStreamingCompleted(object sender, string completedString)
|
||||||
|
=> Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(IStreamable.StreamingCompleted), Message = completedString });
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region IProcessable event handlers
|
#region IProcessable event handlers
|
||||||
public virtual void OnBegin(object sender, LibraryBook libraryBook) { }
|
public virtual void OnBegin(object sender, LibraryBook libraryBook)
|
||||||
public virtual void OnStatusUpdate(object sender, string statusUpdate) { }
|
=> Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(IProcessable.Begin), Book = libraryBook.LogFriendly() });
|
||||||
public virtual void OnCompleted(object sender, LibraryBook libraryBook) { }
|
public virtual void OnStatusUpdate(object sender, string statusUpdate)
|
||||||
|
=> Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(IProcessable.StatusUpdate), Status = statusUpdate });
|
||||||
|
public virtual void OnCompleted(object sender, LibraryBook libraryBook)
|
||||||
|
=> Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(IProcessable.Completed), Book = libraryBook.LogFriendly() });
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region IAudioDecodable event handlers
|
#region IAudioDecodable event handlers
|
||||||
public virtual void OnRequestCoverArt(object sender, Action<byte[]> setCoverArtDelegate) { }
|
public virtual void OnRequestCoverArt(object sender, Action<byte[]> setCoverArtDelegate)
|
||||||
public virtual void OnTitleDiscovered(object sender, string title) { }
|
=> Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(IAudioDecodable.RequestCoverArt) });
|
||||||
public virtual void OnAuthorsDiscovered(object sender, string authors) { }
|
public virtual void OnTitleDiscovered(object sender, string title)
|
||||||
public virtual void OnNarratorsDiscovered(object sender, string narrators) { }
|
=> Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(IAudioDecodable.TitleDiscovered), Title = title });
|
||||||
public virtual void OnCoverImageDiscovered(object sender, byte[] coverArt) { }
|
public virtual void OnAuthorsDiscovered(object sender, string authors)
|
||||||
|
=> Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(IAudioDecodable.AuthorsDiscovered), Authors = authors });
|
||||||
|
public virtual void OnNarratorsDiscovered(object sender, string narrators)
|
||||||
|
=> Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(IAudioDecodable.NarratorsDiscovered), Narrators = narrators });
|
||||||
|
public virtual void OnCoverImageDiscovered(object sender, byte[] coverArt)
|
||||||
|
=> Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(IAudioDecodable.CoverImageDiscovered), CoverImageBytes = coverArt?.Length });
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,10 +20,12 @@ namespace LibationWinForms.BookLiberation
|
|||||||
#region IStreamable event handler overrides
|
#region IStreamable event handler overrides
|
||||||
public override void OnStreamingBegin(object sender, string beginString)
|
public override void OnStreamingBegin(object sender, string beginString)
|
||||||
{
|
{
|
||||||
|
base.OnStreamingBegin(sender, beginString);
|
||||||
filenameLbl.UIThreadAsync(() => filenameLbl.Text = beginString);
|
filenameLbl.UIThreadAsync(() => filenameLbl.Text = beginString);
|
||||||
}
|
}
|
||||||
public override void OnStreamingProgressChanged(object sender, DownloadProgress downloadProgress)
|
public override void OnStreamingProgressChanged(object sender, DownloadProgress downloadProgress)
|
||||||
{
|
{
|
||||||
|
base.OnStreamingProgressChanged(sender, downloadProgress);
|
||||||
// this won't happen with download file. it will happen with download string
|
// this won't happen with download file. it will happen with download string
|
||||||
if (!downloadProgress.TotalBytesToReceive.HasValue || downloadProgress.TotalBytesToReceive.Value <= 0)
|
if (!downloadProgress.TotalBytesToReceive.HasValue || downloadProgress.TotalBytesToReceive.Value <= 0)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -4,7 +4,15 @@ namespace LibationWinForms.BookLiberation
|
|||||||
{
|
{
|
||||||
internal class PdfDownloadForm : DownloadForm
|
internal class PdfDownloadForm : DownloadForm
|
||||||
{
|
{
|
||||||
public override void OnBegin(object sender, LibraryBook libraryBook) => LogMe.Info($"PDF Step, Begin: {libraryBook.Book}");
|
public override void OnBegin(object sender, LibraryBook libraryBook)
|
||||||
public override void OnCompleted(object sender, LibraryBook libraryBook) => LogMe.Info($"PDF Step, Completed: {libraryBook.Book}");
|
{
|
||||||
|
base.OnBegin(sender, libraryBook);
|
||||||
|
LogMe.Info($"PDF Step, Begin: {libraryBook.Book}");
|
||||||
|
}
|
||||||
|
public override void OnCompleted(object sender, LibraryBook libraryBook)
|
||||||
|
{
|
||||||
|
base.OnCompleted(sender, libraryBook);
|
||||||
|
LogMe.Info($"PDF Step, Completed: {libraryBook.Book}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -204,9 +204,9 @@
|
|||||||
this.convertLossyRb.AutoSize = true;
|
this.convertLossyRb.AutoSize = true;
|
||||||
this.convertLossyRb.Location = new System.Drawing.Point(6, 81);
|
this.convertLossyRb.Location = new System.Drawing.Point(6, 81);
|
||||||
this.convertLossyRb.Name = "convertLossyRb";
|
this.convertLossyRb.Name = "convertLossyRb";
|
||||||
this.convertLossyRb.Size = new System.Drawing.Size(242, 19);
|
this.convertLossyRb.Size = new System.Drawing.Size(329, 19);
|
||||||
this.convertLossyRb.TabIndex = 10;
|
this.convertLossyRb.TabIndex = 10;
|
||||||
this.convertLossyRb.Text = "Download my books as .MP3 files (Lossy)";
|
this.convertLossyRb.Text = "Download my books as .MP3 files (transcode if necessary)";
|
||||||
this.convertLossyRb.UseVisualStyleBackColor = true;
|
this.convertLossyRb.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// convertLosslessRb
|
// convertLosslessRb
|
||||||
@ -215,10 +215,10 @@
|
|||||||
this.convertLosslessRb.Checked = true;
|
this.convertLosslessRb.Checked = true;
|
||||||
this.convertLosslessRb.Location = new System.Drawing.Point(6, 56);
|
this.convertLosslessRb.Location = new System.Drawing.Point(6, 56);
|
||||||
this.convertLosslessRb.Name = "convertLosslessRb";
|
this.convertLosslessRb.Name = "convertLosslessRb";
|
||||||
this.convertLosslessRb.Size = new System.Drawing.Size(327, 19);
|
this.convertLosslessRb.Size = new System.Drawing.Size(335, 19);
|
||||||
this.convertLosslessRb.TabIndex = 9;
|
this.convertLosslessRb.TabIndex = 9;
|
||||||
this.convertLosslessRb.TabStop = true;
|
this.convertLosslessRb.TabStop = true;
|
||||||
this.convertLosslessRb.Text = "Download my books as .M4B files (Lossless Mp4a format)";
|
this.convertLosslessRb.Text = "Download my books in the original audio format (Lossless)";
|
||||||
this.convertLosslessRb.UseVisualStyleBackColor = true;
|
this.convertLosslessRb.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// inProgressSelectControl
|
// inProgressSelectControl
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user