Added Cancel method to stop download/decrypt and added estimated time remaining event.
This commit is contained in:
parent
b65f9567e0
commit
f0eb57a40b
@ -3,13 +3,14 @@ using Dinah.Core.Diagnostics;
|
|||||||
using Dinah.Core.IO;
|
using Dinah.Core.IO;
|
||||||
using Dinah.Core.StepRunner;
|
using Dinah.Core.StepRunner;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace AaxDecrypter
|
namespace AaxDecrypter
|
||||||
{
|
{
|
||||||
public interface ISimpleAaxToM4bConverter2
|
public interface ISimpleAaxToM4bConverter
|
||||||
{
|
{
|
||||||
event EventHandler<int> DecryptProgressUpdate;
|
event EventHandler<int> DecryptProgressUpdate;
|
||||||
bool Run();
|
bool Run();
|
||||||
@ -23,8 +24,9 @@ namespace AaxDecrypter
|
|||||||
string Narrator { get; }
|
string Narrator { get; }
|
||||||
byte[] CoverArt { get; }
|
byte[] CoverArt { get; }
|
||||||
}
|
}
|
||||||
public interface IAdvancedAaxcToM4bConverter : ISimpleAaxToM4bConverter2
|
public interface IAdvancedAaxcToM4bConverter : ISimpleAaxToM4bConverter
|
||||||
{
|
{
|
||||||
|
void Cancel();
|
||||||
bool Step1_CreateDir();
|
bool Step1_CreateDir();
|
||||||
bool Step2_DownloadAndCombine();
|
bool Step2_DownloadAndCombine();
|
||||||
bool Step3_RestoreMetadata();
|
bool Step3_RestoreMetadata();
|
||||||
@ -34,6 +36,7 @@ namespace AaxDecrypter
|
|||||||
public class AaxcDownloadConverter : IAdvancedAaxcToM4bConverter
|
public class AaxcDownloadConverter : IAdvancedAaxcToM4bConverter
|
||||||
{
|
{
|
||||||
public event EventHandler<int> DecryptProgressUpdate;
|
public event EventHandler<int> DecryptProgressUpdate;
|
||||||
|
public event EventHandler<TimeSpan> DecryptTimeRemaining;
|
||||||
public string AppName { get; set; } = nameof(AaxcDownloadConverter);
|
public string AppName { get; set; } = nameof(AaxcDownloadConverter);
|
||||||
public string outDir { get; private set; }
|
public string outDir { get; private set; }
|
||||||
public string outputFileName { get; private set; }
|
public string outputFileName { get; private set; }
|
||||||
@ -46,7 +49,7 @@ namespace AaxDecrypter
|
|||||||
private TagLib.Mpeg4.File aaxcTagLib { get; set; }
|
private TagLib.Mpeg4.File aaxcTagLib { get; set; }
|
||||||
private StepSequence steps { get; }
|
private StepSequence steps { get; }
|
||||||
private DownloadLicense downloadLicense { get; set; }
|
private DownloadLicense downloadLicense { get; set; }
|
||||||
|
private FFMpegAaxcProcesser aaxcProcesser;
|
||||||
public static async Task<AaxcDownloadConverter> CreateAsync(string outDirectory, DownloadLicense dlLic, ChapterInfo chapters = null)
|
public static async Task<AaxcDownloadConverter> CreateAsync(string outDirectory, DownloadLicense dlLic, ChapterInfo chapters = null)
|
||||||
{
|
{
|
||||||
var converter = new AaxcDownloadConverter(outDirectory, dlLic, chapters);
|
var converter = new AaxcDownloadConverter(outDirectory, dlLic, chapters);
|
||||||
@ -132,7 +135,7 @@ namespace AaxDecrypter
|
|||||||
|
|
||||||
public bool Step2_DownloadAndCombine()
|
public bool Step2_DownloadAndCombine()
|
||||||
{
|
{
|
||||||
var aaxcProcesser = new FFMpegAaxcProcesser(downloadLicense);
|
aaxcProcesser = new FFMpegAaxcProcesser(downloadLicense);
|
||||||
aaxcProcesser.ProgressUpdate += AaxcProcesser_ProgressUpdate;
|
aaxcProcesser.ProgressUpdate += AaxcProcesser_ProgressUpdate;
|
||||||
|
|
||||||
bool userSuppliedChapters = chapters != null;
|
bool userSuppliedChapters = chapters != null;
|
||||||
@ -166,11 +169,72 @@ namespace AaxDecrypter
|
|||||||
|
|
||||||
private void AaxcProcesser_ProgressUpdate(object sender, TimeSpan e)
|
private void AaxcProcesser_ProgressUpdate(object sender, TimeSpan e)
|
||||||
{
|
{
|
||||||
|
double averageRate = getAverageProcessRate(e);
|
||||||
|
|
||||||
|
double remainingSecsToProcess = (aaxcTagLib.Properties.Duration - e).TotalSeconds;
|
||||||
|
|
||||||
|
double estTimeRemaining = remainingSecsToProcess / averageRate;
|
||||||
|
|
||||||
|
if (double.IsNormal(estTimeRemaining))
|
||||||
|
DecryptTimeRemaining?.Invoke(this, TimeSpan.FromSeconds(estTimeRemaining));
|
||||||
|
|
||||||
|
|
||||||
double progressPercent = 100 * e.TotalSeconds / aaxcTagLib.Properties.Duration.TotalSeconds;
|
double progressPercent = 100 * e.TotalSeconds / aaxcTagLib.Properties.Duration.TotalSeconds;
|
||||||
|
|
||||||
DecryptProgressUpdate?.Invoke(this, (int)progressPercent);
|
DecryptProgressUpdate?.Invoke(this, (int)progressPercent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the average processing rate based on the last <see cref="MAX_NUM_AVERAGE"/> samples.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lastProcessedPosition">Position in the audio file last processed</param>
|
||||||
|
/// <returns>The average processing rate, in book_duration_seconds / second.</returns>
|
||||||
|
private double getAverageProcessRate(TimeSpan lastProcessedPosition)
|
||||||
|
{
|
||||||
|
streamPositions.Enqueue(new StreamPosition
|
||||||
|
{
|
||||||
|
ProcessPosition = lastProcessedPosition,
|
||||||
|
EventTime = DateTime.Now,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (streamPositions.Count < 2)
|
||||||
|
return double.PositiveInfinity;
|
||||||
|
|
||||||
|
//Calculate the harmonic mean of the last AVERAGE_NUM progress updates
|
||||||
|
//Units are Book_Duration_Seconds / second
|
||||||
|
|
||||||
|
var lastPos = streamPositions.Count > MAX_NUM_AVERAGE ? streamPositions.Dequeue() : null;
|
||||||
|
|
||||||
|
double harmonicDenominator = 0;
|
||||||
|
int harmonicNumerator = 0;
|
||||||
|
|
||||||
|
foreach (var pos in streamPositions)
|
||||||
|
{
|
||||||
|
if (lastPos is null)
|
||||||
|
{
|
||||||
|
lastPos = pos;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double dP = (pos.ProcessPosition - lastPos.ProcessPosition).TotalSeconds;
|
||||||
|
double dT = (pos.EventTime - lastPos.EventTime).TotalSeconds;
|
||||||
|
|
||||||
|
harmonicDenominator += dT / dP;
|
||||||
|
harmonicNumerator++;
|
||||||
|
lastPos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
double harmonicMean = harmonicNumerator / harmonicDenominator;
|
||||||
|
return harmonicMean;
|
||||||
|
}
|
||||||
|
private const int MAX_NUM_AVERAGE = 15;
|
||||||
|
private class StreamPosition
|
||||||
|
{
|
||||||
|
public TimeSpan ProcessPosition { get; set; }
|
||||||
|
public DateTime EventTime { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Queue<StreamPosition> streamPositions = new Queue<StreamPosition>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copy all aacx metadata to m4b file, including cover art.
|
/// Copy all aacx metadata to m4b file, including cover art.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -207,5 +271,10 @@ namespace AaxDecrypter
|
|||||||
File.WriteAllText(PathLib.ReplaceExtension(outputFileName, ".nfo"), NFO.CreateContents(AppName, aaxcTagLib, chapters));
|
File.WriteAllText(PathLib.ReplaceExtension(outputFileName, ".nfo"), NFO.CreateContents(AppName, aaxcTagLib, chapters));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Cancel()
|
||||||
|
{
|
||||||
|
aaxcProcesser.Cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,8 @@ namespace AaxDecrypter
|
|||||||
private StringBuilder remuxerError = new StringBuilder();
|
private StringBuilder remuxerError = new StringBuilder();
|
||||||
private StringBuilder downloaderError = new StringBuilder();
|
private StringBuilder downloaderError = new StringBuilder();
|
||||||
private static Regex processedTimeRegex = new Regex("time=(\\d{2}):(\\d{2}):(\\d{2}).\\d{2}", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
private static Regex processedTimeRegex = new Regex("time=(\\d{2}):(\\d{2}):(\\d{2}).\\d{2}", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
private Process downloader;
|
||||||
|
private Process remuxer;
|
||||||
|
|
||||||
public FFMpegAaxcProcesser( DownloadLicense downloadLicense)
|
public FFMpegAaxcProcesser( DownloadLicense downloadLicense)
|
||||||
{
|
{
|
||||||
@ -36,14 +38,14 @@ namespace AaxDecrypter
|
|||||||
{
|
{
|
||||||
//This process gets the aaxc from the url and streams the decrypted
|
//This process gets the aaxc from the url and streams the decrypted
|
||||||
//aac stream to standard output
|
//aac stream to standard output
|
||||||
var downloader = new Process
|
downloader = new Process
|
||||||
{
|
{
|
||||||
StartInfo = getDownloaderStartInfo()
|
StartInfo = getDownloaderStartInfo()
|
||||||
};
|
};
|
||||||
|
|
||||||
//This process retreves an aac stream from standard input and muxes
|
//This process retreves an aac stream from standard input and muxes
|
||||||
// it into an m4b along with the cover art and metadata.
|
// it into an m4b along with the cover art and metadata.
|
||||||
var remuxer = new Process
|
remuxer = new Process
|
||||||
{
|
{
|
||||||
StartInfo = getRemuxerStartInfo(outputFile, ffmetaChaptersPath)
|
StartInfo = getRemuxerStartInfo(outputFile, ffmetaChaptersPath)
|
||||||
};
|
};
|
||||||
@ -90,7 +92,11 @@ namespace AaxDecrypter
|
|||||||
IsRunning = false;
|
IsRunning = false;
|
||||||
Succeeded = downloader.ExitCode == 0 && remuxer.ExitCode == 0;
|
Succeeded = downloader.ExitCode == 0 && remuxer.ExitCode == 0;
|
||||||
}
|
}
|
||||||
|
public void Cancel()
|
||||||
|
{
|
||||||
|
if (IsRunning && !remuxer.HasExited)
|
||||||
|
remuxer.Kill();
|
||||||
|
}
|
||||||
private void Downloader_ErrorDataReceived(object sender, DataReceivedEventArgs e)
|
private void Downloader_ErrorDataReceived(object sender, DataReceivedEventArgs e)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(e.Data))
|
if (string.IsNullOrEmpty(e.Data))
|
||||||
|
|||||||
@ -22,10 +22,12 @@ namespace FileLiberator.AaxcDownloadDecrypt
|
|||||||
public event EventHandler<string> NarratorsDiscovered;
|
public event EventHandler<string> NarratorsDiscovered;
|
||||||
public event EventHandler<byte[]> CoverImageFilepathDiscovered;
|
public event EventHandler<byte[]> CoverImageFilepathDiscovered;
|
||||||
public event EventHandler<int> UpdateProgress;
|
public event EventHandler<int> UpdateProgress;
|
||||||
|
public event EventHandler<TimeSpan> UpdateRemainingTime;
|
||||||
public event EventHandler<string> DecryptCompleted;
|
public event EventHandler<string> DecryptCompleted;
|
||||||
public event EventHandler<LibraryBook> Completed;
|
public event EventHandler<LibraryBook> Completed;
|
||||||
public event EventHandler<string> StatusUpdate;
|
public event EventHandler<string> StatusUpdate;
|
||||||
|
|
||||||
|
private AaxcDownloadConverter aaxcDownloader;
|
||||||
public async Task<StatusHandler> ProcessAsync(LibraryBook libraryBook)
|
public async Task<StatusHandler> ProcessAsync(LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
Begin?.Invoke(this, libraryBook);
|
Begin?.Invoke(this, libraryBook);
|
||||||
@ -74,7 +76,6 @@ namespace FileLiberator.AaxcDownloadDecrypt
|
|||||||
|
|
||||||
var destinationDirectory = Path.GetDirectoryName(destinationDir);
|
var destinationDirectory = Path.GetDirectoryName(destinationDir);
|
||||||
|
|
||||||
AaxcDownloadConverter newDownloader;
|
|
||||||
if (Configuration.Instance.DownloadChapters)
|
if (Configuration.Instance.DownloadChapters)
|
||||||
{
|
{
|
||||||
var contentMetadata = await api.GetLibraryBookMetadataAsync(libraryBook.Book.AudibleProductId);
|
var contentMetadata = await api.GetLibraryBookMetadataAsync(libraryBook.Book.AudibleProductId);
|
||||||
@ -84,33 +85,34 @@ namespace FileLiberator.AaxcDownloadDecrypt
|
|||||||
foreach (var chap in contentMetadata?.ChapterInfo?.Chapters)
|
foreach (var chap in contentMetadata?.ChapterInfo?.Chapters)
|
||||||
aaxcDecryptChapters.AddChapter(new Chapter(chap.Title, chap.StartOffsetMs, chap.LengthMs));
|
aaxcDecryptChapters.AddChapter(new Chapter(chap.Title, chap.StartOffsetMs, chap.LengthMs));
|
||||||
|
|
||||||
newDownloader = await AaxcDownloadConverter.CreateAsync(destinationDirectory, aaxcDecryptDlLic, aaxcDecryptChapters);
|
aaxcDownloader = await AaxcDownloadConverter.CreateAsync(destinationDirectory, aaxcDecryptDlLic, aaxcDecryptChapters);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
newDownloader = await AaxcDownloadConverter.CreateAsync(destinationDirectory, aaxcDecryptDlLic);
|
aaxcDownloader = await AaxcDownloadConverter.CreateAsync(destinationDirectory, aaxcDecryptDlLic);
|
||||||
}
|
}
|
||||||
|
|
||||||
newDownloader.AppName = "Libation";
|
aaxcDownloader.AppName = "Libation";
|
||||||
|
|
||||||
TitleDiscovered?.Invoke(this, newDownloader.Title);
|
TitleDiscovered?.Invoke(this, aaxcDownloader.Title);
|
||||||
AuthorsDiscovered?.Invoke(this, newDownloader.Author);
|
AuthorsDiscovered?.Invoke(this, aaxcDownloader.Author);
|
||||||
NarratorsDiscovered?.Invoke(this, newDownloader.Narrator);
|
NarratorsDiscovered?.Invoke(this, aaxcDownloader.Narrator);
|
||||||
CoverImageFilepathDiscovered?.Invoke(this, newDownloader.CoverArt);
|
CoverImageFilepathDiscovered?.Invoke(this, aaxcDownloader.CoverArt);
|
||||||
|
|
||||||
// override default which was set in CreateAsync
|
// override default which was set in CreateAsync
|
||||||
var proposedOutputFile = Path.Combine(destinationDir, $"{libraryBook.Book.Title} [{libraryBook.Book.AudibleProductId}].m4b");
|
var proposedOutputFile = Path.Combine(destinationDir, $"{libraryBook.Book.Title} [{libraryBook.Book.AudibleProductId}].m4b");
|
||||||
newDownloader.SetOutputFilename(proposedOutputFile);
|
aaxcDownloader.SetOutputFilename(proposedOutputFile);
|
||||||
newDownloader.DecryptProgressUpdate += (s, progress) => UpdateProgress?.Invoke(this, progress);
|
aaxcDownloader.DecryptProgressUpdate += (s, progress) => UpdateProgress?.Invoke(this, progress);
|
||||||
|
aaxcDownloader.DecryptTimeRemaining += (s, remaining) => UpdateRemainingTime?.Invoke(this, remaining);
|
||||||
|
|
||||||
// REAL WORK DONE HERE
|
// REAL WORK DONE HERE
|
||||||
var success = await Task.Run(() => newDownloader.Run());
|
var success = await Task.Run(() => aaxcDownloader.Run());
|
||||||
|
|
||||||
// decrypt failed
|
// decrypt failed
|
||||||
if (!success)
|
if (!success)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return newDownloader.outputFileName;
|
return aaxcDownloader.outputFileName;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -195,6 +197,9 @@ namespace FileLiberator.AaxcDownloadDecrypt
|
|||||||
=> !AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId)
|
=> !AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId)
|
||||||
&& !AudibleFileStorage.AAX.Exists(libraryBook.Book.AudibleProductId);
|
&& !AudibleFileStorage.AAX.Exists(libraryBook.Book.AudibleProductId);
|
||||||
|
|
||||||
|
public void Cancel()
|
||||||
|
{
|
||||||
|
aaxcDownloader.Cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,9 @@ namespace FileLiberator
|
|||||||
event EventHandler<string> NarratorsDiscovered;
|
event EventHandler<string> NarratorsDiscovered;
|
||||||
event EventHandler<byte[]> CoverImageFilepathDiscovered;
|
event EventHandler<byte[]> CoverImageFilepathDiscovered;
|
||||||
event EventHandler<int> UpdateProgress;
|
event EventHandler<int> UpdateProgress;
|
||||||
|
event EventHandler<TimeSpan> UpdateRemainingTime;
|
||||||
|
|
||||||
event EventHandler<string> DecryptCompleted;
|
event EventHandler<string> DecryptCompleted;
|
||||||
|
void Cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
<!-- <PublishSingleFile>true</PublishSingleFile> -->
|
<!-- <PublishSingleFile>true</PublishSingleFile> -->
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
|
||||||
<Version>4.4.0.121</Version>
|
<Version>4.4.0.181</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -32,14 +32,16 @@
|
|||||||
this.bookInfoLbl = new System.Windows.Forms.Label();
|
this.bookInfoLbl = new System.Windows.Forms.Label();
|
||||||
this.progressBar1 = new System.Windows.Forms.ProgressBar();
|
this.progressBar1 = new System.Windows.Forms.ProgressBar();
|
||||||
this.rtbLog = new System.Windows.Forms.RichTextBox();
|
this.rtbLog = new System.Windows.Forms.RichTextBox();
|
||||||
|
this.remainingTimeLbl = new System.Windows.Forms.Label();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// pictureBox1
|
// pictureBox1
|
||||||
//
|
//
|
||||||
this.pictureBox1.Location = new System.Drawing.Point(12, 12);
|
this.pictureBox1.Location = new System.Drawing.Point(14, 14);
|
||||||
|
this.pictureBox1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.pictureBox1.Name = "pictureBox1";
|
this.pictureBox1.Name = "pictureBox1";
|
||||||
this.pictureBox1.Size = new System.Drawing.Size(100, 100);
|
this.pictureBox1.Size = new System.Drawing.Size(117, 115);
|
||||||
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
|
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
|
||||||
this.pictureBox1.TabIndex = 0;
|
this.pictureBox1.TabIndex = 0;
|
||||||
this.pictureBox1.TabStop = false;
|
this.pictureBox1.TabStop = false;
|
||||||
@ -47,9 +49,10 @@
|
|||||||
// bookInfoLbl
|
// bookInfoLbl
|
||||||
//
|
//
|
||||||
this.bookInfoLbl.AutoSize = true;
|
this.bookInfoLbl.AutoSize = true;
|
||||||
this.bookInfoLbl.Location = new System.Drawing.Point(118, 12);
|
this.bookInfoLbl.Location = new System.Drawing.Point(138, 14);
|
||||||
|
this.bookInfoLbl.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||||
this.bookInfoLbl.Name = "bookInfoLbl";
|
this.bookInfoLbl.Name = "bookInfoLbl";
|
||||||
this.bookInfoLbl.Size = new System.Drawing.Size(100, 13);
|
this.bookInfoLbl.Size = new System.Drawing.Size(121, 15);
|
||||||
this.bookInfoLbl.TabIndex = 0;
|
this.bookInfoLbl.TabIndex = 0;
|
||||||
this.bookInfoLbl.Text = "[multi-line book info]";
|
this.bookInfoLbl.Text = "[multi-line book info]";
|
||||||
//
|
//
|
||||||
@ -57,9 +60,10 @@
|
|||||||
//
|
//
|
||||||
this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.progressBar1.Location = new System.Drawing.Point(12, 526);
|
this.progressBar1.Location = new System.Drawing.Point(14, 607);
|
||||||
|
this.progressBar1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.progressBar1.Name = "progressBar1";
|
this.progressBar1.Name = "progressBar1";
|
||||||
this.progressBar1.Size = new System.Drawing.Size(582, 23);
|
this.progressBar1.Size = new System.Drawing.Size(611, 27);
|
||||||
this.progressBar1.TabIndex = 2;
|
this.progressBar1.TabIndex = 2;
|
||||||
//
|
//
|
||||||
// rtbLog
|
// rtbLog
|
||||||
@ -67,21 +71,33 @@
|
|||||||
this.rtbLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
this.rtbLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
| System.Windows.Forms.AnchorStyles.Left)
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.rtbLog.Location = new System.Drawing.Point(12, 118);
|
this.rtbLog.Location = new System.Drawing.Point(14, 136);
|
||||||
|
this.rtbLog.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.rtbLog.Name = "rtbLog";
|
this.rtbLog.Name = "rtbLog";
|
||||||
this.rtbLog.Size = new System.Drawing.Size(582, 402);
|
this.rtbLog.Size = new System.Drawing.Size(678, 463);
|
||||||
this.rtbLog.TabIndex = 1;
|
this.rtbLog.TabIndex = 1;
|
||||||
this.rtbLog.Text = "";
|
this.rtbLog.Text = "";
|
||||||
//
|
//
|
||||||
|
// remainingTimeLbl
|
||||||
|
//
|
||||||
|
this.remainingTimeLbl.Location = new System.Drawing.Point(632, 607);
|
||||||
|
this.remainingTimeLbl.Name = "remainingTimeLbl";
|
||||||
|
this.remainingTimeLbl.Size = new System.Drawing.Size(60, 31);
|
||||||
|
this.remainingTimeLbl.TabIndex = 3;
|
||||||
|
this.remainingTimeLbl.Text = "ETA:\r\n";
|
||||||
|
this.remainingTimeLbl.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||||
|
//
|
||||||
// DecryptForm
|
// DecryptForm
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(606, 561);
|
this.ClientSize = new System.Drawing.Size(707, 647);
|
||||||
|
this.Controls.Add(this.remainingTimeLbl);
|
||||||
this.Controls.Add(this.rtbLog);
|
this.Controls.Add(this.rtbLog);
|
||||||
this.Controls.Add(this.progressBar1);
|
this.Controls.Add(this.progressBar1);
|
||||||
this.Controls.Add(this.bookInfoLbl);
|
this.Controls.Add(this.bookInfoLbl);
|
||||||
this.Controls.Add(this.pictureBox1);
|
this.Controls.Add(this.pictureBox1);
|
||||||
|
this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.Name = "DecryptForm";
|
this.Name = "DecryptForm";
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||||
this.Text = "DecryptForm";
|
this.Text = "DecryptForm";
|
||||||
@ -99,5 +115,6 @@
|
|||||||
private System.Windows.Forms.Label bookInfoLbl;
|
private System.Windows.Forms.Label bookInfoLbl;
|
||||||
private System.Windows.Forms.ProgressBar progressBar1;
|
private System.Windows.Forms.ProgressBar progressBar1;
|
||||||
private System.Windows.Forms.RichTextBox rtbLog;
|
private System.Windows.Forms.RichTextBox rtbLog;
|
||||||
|
private System.Windows.Forms.Label remainingTimeLbl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,6 +59,17 @@ namespace LibationWinForms.BookLiberation
|
|||||||
public void SetCoverImage(byte[] coverBytes)
|
public void SetCoverImage(byte[] coverBytes)
|
||||||
=> pictureBox1.UIThread(() => pictureBox1.Image = ImageReader.ToImage(coverBytes));
|
=> pictureBox1.UIThread(() => pictureBox1.Image = ImageReader.ToImage(coverBytes));
|
||||||
|
|
||||||
public void UpdateProgress(int percentage) => progressBar1.UIThread(() => progressBar1.Value = percentage);
|
public void UpdateProgress(int percentage)
|
||||||
|
{
|
||||||
|
if (percentage == 0)
|
||||||
|
remainingTimeLbl.UIThread(() => remainingTimeLbl.Text = "ETA:\r\n0 sec");
|
||||||
|
|
||||||
|
progressBar1.UIThread(() => progressBar1.Value = percentage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateRemainingTime(TimeSpan remaining)
|
||||||
|
{
|
||||||
|
remainingTimeLbl.UIThread(() => remainingTimeLbl.Text = $"ETA:\r\n{(int)remaining.TotalSeconds} sec");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,64 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<root>
|
||||||
<root>
|
|
||||||
<!--
|
|
||||||
Microsoft ResX Schema
|
|
||||||
|
|
||||||
Version 2.0
|
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
|
||||||
that is mostly human readable. The generation and parsing of the
|
|
||||||
various data types are done through the TypeConverter classes
|
|
||||||
associated with the data types.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
|
||||||
<resheader name="version">2.0</resheader>
|
|
||||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
|
||||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
|
||||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
|
||||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
|
||||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
|
||||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
|
||||||
</data>
|
|
||||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
|
||||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
|
||||||
<comment>This is a comment</comment>
|
|
||||||
</data>
|
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
|
||||||
name/value pairs.
|
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
|
||||||
text/value conversion through the TypeConverter architecture.
|
|
||||||
Classes that don't support this are serialized and stored with the
|
|
||||||
mimetype set.
|
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
|
||||||
read any of the formats listed below.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
|
||||||
value : The object must be serialized with
|
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
|
||||||
value : The object must be serialized with
|
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
|
||||||
value : The object must be serialized into a byte array
|
|
||||||
: using a System.ComponentModel.TypeConverter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
-->
|
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
|||||||
@ -264,6 +264,7 @@ namespace LibationWinForms.BookLiberation
|
|||||||
void narratorsDiscovered(object _, string narrators) => decryptDialog.SetNarratorNames(narrators);
|
void narratorsDiscovered(object _, string narrators) => decryptDialog.SetNarratorNames(narrators);
|
||||||
void coverImageFilepathDiscovered(object _, byte[] coverBytes) => decryptDialog.SetCoverImage(coverBytes);
|
void coverImageFilepathDiscovered(object _, byte[] coverBytes) => decryptDialog.SetCoverImage(coverBytes);
|
||||||
void updateProgress(object _, int percentage) => decryptDialog.UpdateProgress(percentage);
|
void updateProgress(object _, int percentage) => decryptDialog.UpdateProgress(percentage);
|
||||||
|
void updateRemainingTime(object _, TimeSpan remaining) => decryptDialog.UpdateRemainingTime(remaining);
|
||||||
|
|
||||||
void decryptCompleted(object _, string __) => decryptDialog.Close();
|
void decryptCompleted(object _, string __) => decryptDialog.Close();
|
||||||
#endregion
|
#endregion
|
||||||
@ -276,6 +277,7 @@ namespace LibationWinForms.BookLiberation
|
|||||||
decryptBook.NarratorsDiscovered += narratorsDiscovered;
|
decryptBook.NarratorsDiscovered += narratorsDiscovered;
|
||||||
decryptBook.CoverImageFilepathDiscovered += coverImageFilepathDiscovered;
|
decryptBook.CoverImageFilepathDiscovered += coverImageFilepathDiscovered;
|
||||||
decryptBook.UpdateProgress += updateProgress;
|
decryptBook.UpdateProgress += updateProgress;
|
||||||
|
decryptBook.UpdateRemainingTime += updateRemainingTime;
|
||||||
|
|
||||||
decryptBook.DecryptCompleted += decryptCompleted;
|
decryptBook.DecryptCompleted += decryptCompleted;
|
||||||
#endregion
|
#endregion
|
||||||
@ -293,6 +295,7 @@ namespace LibationWinForms.BookLiberation
|
|||||||
decryptBook.UpdateProgress -= updateProgress;
|
decryptBook.UpdateProgress -= updateProgress;
|
||||||
|
|
||||||
decryptBook.DecryptCompleted -= decryptCompleted;
|
decryptBook.DecryptCompleted -= decryptCompleted;
|
||||||
|
decryptBook.Cancel();
|
||||||
};
|
};
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user