Redesign DookLiberation control flow.
This commit is contained in:
parent
963d632208
commit
0045cf05ef
@ -1,6 +1,7 @@
|
||||
using AAXClean;
|
||||
using Dinah.Core;
|
||||
using Dinah.Core.IO;
|
||||
using Dinah.Core.Net.Http;
|
||||
using Dinah.Core.StepRunner;
|
||||
using System;
|
||||
using System.IO;
|
||||
@ -12,8 +13,9 @@ namespace AaxDecrypter
|
||||
{
|
||||
public event EventHandler<AppleTags> RetrievedTags;
|
||||
public event EventHandler<byte[]> RetrievedCoverArt;
|
||||
public event EventHandler<int> DecryptProgressUpdate;
|
||||
public event EventHandler<DownloadProgress> DecryptProgressUpdate;
|
||||
public event EventHandler<TimeSpan> DecryptTimeRemaining;
|
||||
|
||||
public string AppName { get; set; } = nameof(AaxcDownloadConverter);
|
||||
|
||||
private string outputFileName { get; }
|
||||
@ -132,7 +134,14 @@ namespace AaxDecrypter
|
||||
|
||||
public bool Step2_DownloadAndCombine()
|
||||
{
|
||||
DecryptProgressUpdate?.Invoke(this, 0);
|
||||
var zeroProgress = new DownloadProgress
|
||||
{
|
||||
BytesReceived = 0,
|
||||
ProgressPercentage = 0,
|
||||
TotalBytesToReceive = nfsPersister.NetworkFileStream.Length
|
||||
};
|
||||
|
||||
DecryptProgressUpdate?.Invoke(this, zeroProgress);
|
||||
|
||||
if (File.Exists(outputFileName))
|
||||
FileExt.SafeDelete(outputFileName);
|
||||
@ -151,7 +160,7 @@ namespace AaxDecrypter
|
||||
|
||||
nfsPersister.Dispose();
|
||||
|
||||
DecryptProgressUpdate?.Invoke(this, 0);
|
||||
DecryptProgressUpdate?.Invoke(this, zeroProgress);
|
||||
|
||||
return decryptionResult == ConversionResult.NoErrorsDetected && !isCanceled;
|
||||
}
|
||||
@ -167,7 +176,13 @@ namespace AaxDecrypter
|
||||
|
||||
double progressPercent = 100 * e.ProcessPosition.TotalSeconds / duration.TotalSeconds;
|
||||
|
||||
DecryptProgressUpdate?.Invoke(this, (int)progressPercent);
|
||||
DecryptProgressUpdate?.Invoke(this,
|
||||
new DownloadProgress
|
||||
{
|
||||
ProgressPercentage = progressPercent,
|
||||
BytesReceived = (long)(nfsPersister.NetworkFileStream.Length * progressPercent),
|
||||
TotalBytesToReceive = nfsPersister.NetworkFileStream.Length
|
||||
});
|
||||
}
|
||||
|
||||
public bool Step3_CreateCue()
|
||||
@ -209,6 +224,7 @@ namespace AaxDecrypter
|
||||
{
|
||||
isCanceled = true;
|
||||
aaxFile?.Cancel();
|
||||
aaxFile?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
using System.Threading.Tasks;
|
||||
using DataLayer;
|
||||
using Dinah.Core.ErrorHandling;
|
||||
using FileManager;
|
||||
|
||||
namespace FileLiberator
|
||||
{
|
||||
@ -20,8 +19,14 @@ namespace FileLiberator
|
||||
public event EventHandler<string> StatusUpdate;
|
||||
public event EventHandler<LibraryBook> Completed;
|
||||
|
||||
public DownloadDecryptBook DownloadDecryptBook { get; } = new DownloadDecryptBook();
|
||||
public DownloadPdf DownloadPdf { get; } = new DownloadPdf();
|
||||
public DownloadDecryptBook DownloadDecryptBook { get; }
|
||||
public DownloadPdf DownloadPdf { get; }
|
||||
|
||||
public BackupBook(DownloadDecryptBook downloadDecryptBook, DownloadPdf downloadPdf)
|
||||
{
|
||||
DownloadDecryptBook = downloadDecryptBook;
|
||||
DownloadPdf = downloadPdf;
|
||||
}
|
||||
|
||||
public bool Validate(LibraryBook libraryBook)
|
||||
=> !ApplicationServices.TransitionalFileLocator.Audio_Exists(libraryBook.Book);
|
||||
|
||||
@ -3,6 +3,7 @@ using DataLayer;
|
||||
using Dinah.Core;
|
||||
using Dinah.Core.ErrorHandling;
|
||||
using Dinah.Core.IO;
|
||||
using Dinah.Core.Net.Http;
|
||||
using FileManager;
|
||||
using System;
|
||||
using System.IO;
|
||||
@ -11,24 +12,25 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace FileLiberator
|
||||
{
|
||||
public class ConvertToMp3 : IDecryptable
|
||||
public class ConvertToMp3 : IAudioDecodable
|
||||
{
|
||||
public event EventHandler<string> DecryptBegin;
|
||||
|
||||
private Mp4File m4bBook;
|
||||
|
||||
public event EventHandler<TimeSpan> StreamingTimeRemaining;
|
||||
public event EventHandler<Action<byte[]>> RequestCoverArt;
|
||||
public event EventHandler<string> TitleDiscovered;
|
||||
public event EventHandler<string> AuthorsDiscovered;
|
||||
public event EventHandler<string> NarratorsDiscovered;
|
||||
public event EventHandler<byte[]> CoverImageFilepathDiscovered;
|
||||
public event EventHandler<int> UpdateProgress;
|
||||
public event EventHandler<TimeSpan> UpdateRemainingTime;
|
||||
public event EventHandler<string> DecryptCompleted;
|
||||
public event EventHandler<string> StreamingBegin;
|
||||
public event EventHandler<DownloadProgress> StreamingProgressChanged;
|
||||
public event EventHandler<string> StreamingCompleted;
|
||||
public event EventHandler<LibraryBook> Begin;
|
||||
public event EventHandler<string> StatusUpdate;
|
||||
public event EventHandler<LibraryBook> Completed;
|
||||
|
||||
public event EventHandler<string> StatusUpdate;
|
||||
public event EventHandler<Action<byte[]>> RequestCoverArt;
|
||||
|
||||
private Mp4File m4bBook;
|
||||
|
||||
private long fileSize;
|
||||
private string Mp3FileName(string m4bPath) => m4bPath is null ? string.Empty : PathLib.ReplaceExtension(m4bPath, ".mp3");
|
||||
|
||||
public void Cancel() => m4bBook?.Cancel();
|
||||
@ -43,15 +45,16 @@ namespace FileLiberator
|
||||
{
|
||||
Begin?.Invoke(this, libraryBook);
|
||||
|
||||
DecryptBegin?.Invoke(this, $"Begin converting {libraryBook} to mp3");
|
||||
StreamingBegin?.Invoke(this, $"Begin converting {libraryBook} to mp3");
|
||||
|
||||
try
|
||||
{
|
||||
var m4bPath = ApplicationServices.TransitionalFileLocator.Audio_GetPath(libraryBook.Book);
|
||||
|
||||
m4bBook = new Mp4File(m4bPath, FileAccess.Read);
|
||||
m4bBook.ConversionProgressUpdate += M4bBook_ConversionProgressUpdate;
|
||||
|
||||
fileSize = m4bBook.InputStream.Length;
|
||||
|
||||
TitleDiscovered?.Invoke(this, m4bBook.AppleTags.Title);
|
||||
AuthorsDiscovered?.Invoke(this, m4bBook.AppleTags.FirstAuthor);
|
||||
NarratorsDiscovered?.Invoke(this, m4bBook.AppleTags.Narrator);
|
||||
@ -76,7 +79,7 @@ namespace FileLiberator
|
||||
}
|
||||
finally
|
||||
{
|
||||
DecryptCompleted?.Invoke(this, $"Completed converting to mp3: {libraryBook.Book.Title}");
|
||||
StreamingCompleted?.Invoke(this, $"Completed converting to mp3: {libraryBook.Book.Title}");
|
||||
Completed?.Invoke(this, libraryBook);
|
||||
}
|
||||
}
|
||||
@ -88,11 +91,17 @@ namespace FileLiberator
|
||||
double estTimeRemaining = remainingSecsToProcess / e.ProcessSpeed;
|
||||
|
||||
if (double.IsNormal(estTimeRemaining))
|
||||
UpdateRemainingTime?.Invoke(this, TimeSpan.FromSeconds(estTimeRemaining));
|
||||
StreamingTimeRemaining?.Invoke(this, TimeSpan.FromSeconds(estTimeRemaining));
|
||||
|
||||
double progressPercent = 100 * e.ProcessPosition.TotalSeconds / duration.TotalSeconds;
|
||||
|
||||
UpdateProgress?.Invoke(this, (int)progressPercent);
|
||||
StreamingProgressChanged?.Invoke(this,
|
||||
new DownloadProgress
|
||||
{
|
||||
ProgressPercentage = progressPercent,
|
||||
BytesReceived = (long)(fileSize * progressPercent),
|
||||
TotalBytesToReceive = fileSize
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,26 +8,29 @@ using AudibleApi;
|
||||
using DataLayer;
|
||||
using Dinah.Core;
|
||||
using Dinah.Core.ErrorHandling;
|
||||
using Dinah.Core.Net.Http;
|
||||
using FileManager;
|
||||
|
||||
namespace FileLiberator
|
||||
{
|
||||
public class DownloadDecryptBook : IDecryptable
|
||||
public class DownloadDecryptBook : IAudioDecodable
|
||||
{
|
||||
|
||||
private AaxcDownloadConverter aaxcDownloader;
|
||||
|
||||
public event EventHandler<TimeSpan> StreamingTimeRemaining;
|
||||
public event EventHandler<Action<byte[]>> RequestCoverArt;
|
||||
public event EventHandler<LibraryBook> Begin;
|
||||
public event EventHandler<string> DecryptBegin;
|
||||
public event EventHandler<string> TitleDiscovered;
|
||||
public event EventHandler<string> AuthorsDiscovered;
|
||||
public event EventHandler<string> NarratorsDiscovered;
|
||||
public event EventHandler<byte[]> CoverImageFilepathDiscovered;
|
||||
public event EventHandler<int> UpdateProgress;
|
||||
public event EventHandler<TimeSpan> UpdateRemainingTime;
|
||||
public event EventHandler<string> DecryptCompleted;
|
||||
public event EventHandler<LibraryBook> Completed;
|
||||
public event EventHandler<string> StreamingBegin;
|
||||
public event EventHandler<DownloadProgress> StreamingProgressChanged;
|
||||
public event EventHandler<string> StreamingCompleted;
|
||||
public event EventHandler<LibraryBook> Begin;
|
||||
public event EventHandler<string> StatusUpdate;
|
||||
public event EventHandler<LibraryBook> Completed;
|
||||
|
||||
private AaxcDownloadConverter aaxcDownloader;
|
||||
public async Task<StatusHandler> ProcessAsync(LibraryBook libraryBook)
|
||||
{
|
||||
Begin?.Invoke(this, libraryBook);
|
||||
@ -63,7 +66,7 @@ namespace FileLiberator
|
||||
|
||||
private async Task<string> aaxToM4bConverterDecryptAsync(string cacheDir, string destinationDir, LibraryBook libraryBook)
|
||||
{
|
||||
DecryptBegin?.Invoke(this, $"Begin decrypting {libraryBook}");
|
||||
StreamingBegin?.Invoke(this, $"Begin decrypting {libraryBook}");
|
||||
|
||||
try
|
||||
{
|
||||
@ -103,8 +106,8 @@ namespace FileLiberator
|
||||
|
||||
|
||||
aaxcDownloader = new AaxcDownloadConverter(outFileName, cacheDir, aaxcDecryptDlLic, format) { AppName = "Libation" };
|
||||
aaxcDownloader.DecryptProgressUpdate += (s, progress) => UpdateProgress?.Invoke(this, progress);
|
||||
aaxcDownloader.DecryptTimeRemaining += (s, remaining) => UpdateRemainingTime?.Invoke(this, remaining);
|
||||
aaxcDownloader.DecryptProgressUpdate += (s, progress) => StreamingProgressChanged?.Invoke(this, progress);
|
||||
aaxcDownloader.DecryptTimeRemaining += (s, remaining) => StreamingTimeRemaining?.Invoke(this, remaining);
|
||||
aaxcDownloader.RetrievedCoverArt += AaxcDownloader_RetrievedCoverArt;
|
||||
aaxcDownloader.RetrievedTags += aaxcDownloader_RetrievedTags;
|
||||
|
||||
@ -119,10 +122,11 @@ namespace FileLiberator
|
||||
}
|
||||
finally
|
||||
{
|
||||
DecryptCompleted?.Invoke(this, $"Completed downloading and decrypting {libraryBook.Book.Title}");
|
||||
StreamingCompleted?.Invoke(this, $"Completed downloading and decrypting {libraryBook.Book.Title}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void AaxcDownloader_RetrievedCoverArt(object sender, byte[] e)
|
||||
{
|
||||
if (e is null && Configuration.Instance.AllowLibationFixup)
|
||||
|
||||
@ -6,20 +6,21 @@ using Dinah.Core.Net.Http;
|
||||
namespace FileLiberator
|
||||
{
|
||||
// currently only used to download the .zip flies for upgrade
|
||||
public class DownloadFile : IDownloadable
|
||||
public class DownloadFile : IStreamable
|
||||
{
|
||||
public event EventHandler<string> DownloadBegin;
|
||||
public event EventHandler<DownloadProgress> DownloadProgressChanged;
|
||||
public event EventHandler<string> DownloadCompleted;
|
||||
public event EventHandler<string> StreamingBegin;
|
||||
public event EventHandler<DownloadProgress> StreamingProgressChanged;
|
||||
public event EventHandler<string> StreamingCompleted;
|
||||
public event EventHandler<TimeSpan> StreamingTimeRemaining;
|
||||
|
||||
public async Task<string> PerformDownloadFileAsync(string downloadUrl, string proposedDownloadFilePath)
|
||||
{
|
||||
var client = new HttpClient();
|
||||
|
||||
var progress = new Progress<DownloadProgress>();
|
||||
progress.ProgressChanged += (_, e) => DownloadProgressChanged?.Invoke(this, e);
|
||||
progress.ProgressChanged += OnProgressChanged;
|
||||
|
||||
DownloadBegin?.Invoke(this, proposedDownloadFilePath);
|
||||
StreamingBegin?.Invoke(this, proposedDownloadFilePath);
|
||||
|
||||
try
|
||||
{
|
||||
@ -28,8 +29,12 @@ namespace FileLiberator
|
||||
}
|
||||
finally
|
||||
{
|
||||
DownloadCompleted?.Invoke(this, proposedDownloadFilePath);
|
||||
}
|
||||
StreamingCompleted?.Invoke(this, proposedDownloadFilePath);
|
||||
}
|
||||
}
|
||||
private void OnProgressChanged(object sender, DownloadProgress e)
|
||||
{
|
||||
StreamingProgressChanged?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,16 +6,18 @@ using Dinah.Core.Net.Http;
|
||||
|
||||
namespace FileLiberator
|
||||
{
|
||||
public abstract class DownloadableBase : IDownloadableProcessable
|
||||
public abstract class DownloadableBase : IStreamProcessable
|
||||
{
|
||||
public event EventHandler<LibraryBook> Begin;
|
||||
public event EventHandler<LibraryBook> Completed;
|
||||
|
||||
public event EventHandler<string> DownloadBegin;
|
||||
public event EventHandler<DownloadProgress> DownloadProgressChanged;
|
||||
public event EventHandler<string> DownloadCompleted;
|
||||
public event EventHandler<string> StreamingBegin;
|
||||
public event EventHandler<DownloadProgress> StreamingProgressChanged;
|
||||
public event EventHandler<string> StreamingCompleted;
|
||||
|
||||
public event EventHandler<string> StatusUpdate;
|
||||
public event EventHandler<TimeSpan> StreamingTimeRemaining;
|
||||
|
||||
protected void Invoke_StatusUpdate(string message) => StatusUpdate?.Invoke(this, message);
|
||||
|
||||
public abstract bool Validate(LibraryBook libraryBook);
|
||||
@ -44,9 +46,9 @@ namespace FileLiberator
|
||||
protected async Task<string> PerformDownloadAsync(string proposedDownloadFilePath, Func<Progress<DownloadProgress>, Task<string>> func)
|
||||
{
|
||||
var progress = new Progress<DownloadProgress>();
|
||||
progress.ProgressChanged += (_, e) => DownloadProgressChanged?.Invoke(this, e);
|
||||
progress.ProgressChanged += (_, e) => StreamingProgressChanged?.Invoke(this, e);
|
||||
|
||||
DownloadBegin?.Invoke(this, proposedDownloadFilePath);
|
||||
StreamingBegin?.Invoke(this, proposedDownloadFilePath);
|
||||
|
||||
try
|
||||
{
|
||||
@ -57,7 +59,7 @@ namespace FileLiberator
|
||||
}
|
||||
finally
|
||||
{
|
||||
DownloadCompleted?.Invoke(this, proposedDownloadFilePath);
|
||||
StreamingCompleted?.Invoke(this, proposedDownloadFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,17 @@
|
||||
using System;
|
||||
using Dinah.Core.Net.Http;
|
||||
using System;
|
||||
|
||||
namespace FileLiberator
|
||||
{
|
||||
public interface IDecryptable : IProcessable
|
||||
public interface IAudioDecodable : IStreamProcessable
|
||||
{
|
||||
event EventHandler<string> DecryptBegin;
|
||||
|
||||
event EventHandler<Action<byte[]>> RequestCoverArt;
|
||||
event EventHandler<string> TitleDiscovered;
|
||||
event EventHandler<string> AuthorsDiscovered;
|
||||
event EventHandler<string> NarratorsDiscovered;
|
||||
event EventHandler<byte[]> CoverImageFilepathDiscovered;
|
||||
event EventHandler<int> UpdateProgress;
|
||||
event EventHandler<TimeSpan> UpdateRemainingTime;
|
||||
|
||||
event EventHandler<string> DecryptCompleted;
|
||||
void Cancel();
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
using System;
|
||||
using Dinah.Core.Net.Http;
|
||||
|
||||
namespace FileLiberator
|
||||
{
|
||||
public interface IDownloadable
|
||||
{
|
||||
event EventHandler<string> DownloadBegin;
|
||||
event EventHandler<DownloadProgress> DownloadProgressChanged;
|
||||
event EventHandler<string> DownloadCompleted;
|
||||
}
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
namespace FileLiberator
|
||||
{
|
||||
public interface IDownloadableProcessable : IDownloadable, IProcessable { }
|
||||
}
|
||||
5
FileLiberator/IStreamProcessable.cs
Normal file
5
FileLiberator/IStreamProcessable.cs
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
namespace FileLiberator
|
||||
{
|
||||
public interface IStreamProcessable : IStreamable, IProcessable { }
|
||||
}
|
||||
13
FileLiberator/IStreamable.cs
Normal file
13
FileLiberator/IStreamable.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using Dinah.Core.Net.Http;
|
||||
|
||||
namespace FileLiberator
|
||||
{
|
||||
public interface IStreamable
|
||||
{
|
||||
event EventHandler<string> StreamingBegin;
|
||||
event EventHandler<DownloadProgress> StreamingProgressChanged;
|
||||
event EventHandler<TimeSpan> StreamingTimeRemaining;
|
||||
event EventHandler<string> StreamingCompleted;
|
||||
}
|
||||
}
|
||||
@ -125,7 +125,7 @@ namespace FileManager
|
||||
private static HttpClient imageDownloadClient { get; } = new HttpClient();
|
||||
private static byte[] downloadBytes(PictureDefinition def)
|
||||
{
|
||||
var sz = ((int)def.Size).ToString();
|
||||
var sz = (int)def.Size;
|
||||
return imageDownloadClient.GetByteArrayAsync("ht" + $"tps://images-na.ssl-images-amazon.com/images/I/{def.PictureId}._SL{sz}_.jpg").Result;
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
<!-- <PublishSingleFile>true</PublishSingleFile> -->
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
|
||||
<Version>5.4.9.158</Version>
|
||||
<Version>5.4.9.200</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
|
||||
@ -63,6 +63,9 @@ namespace LibationLauncher
|
||||
#if !DEBUG
|
||||
checkForUpdate(config);
|
||||
#endif
|
||||
LibationWinForms.BookLiberation.ProcessorAutomationController.DownloadFile(
|
||||
"https://github.com/rmcrackan/Libation/releases/download/v5.4.9/Libation.5.4.9.zip",
|
||||
@"C:\Users\mbuca\Downloads\libation test dl.zip");
|
||||
|
||||
Application.Run(new Form1());
|
||||
}
|
||||
|
||||
28
LibationWinForms/BookLiberation/AudioConvertForm.cs
Normal file
28
LibationWinForms/BookLiberation/AudioConvertForm.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using DataLayer;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationWinForms.BookLiberation
|
||||
{
|
||||
class AudioConvertForm : AudioDecodeBaseForm
|
||||
{
|
||||
#region AudioDecodeBaseForm overrides
|
||||
public override string DecodeActionName => "Converting";
|
||||
#endregion
|
||||
|
||||
#region IProcessable event handler overrides
|
||||
public override void OnBegin(object sender, LibraryBook libraryBook)
|
||||
{
|
||||
InfoLogAction($"Convert Step, Begin: {libraryBook.Book}");
|
||||
|
||||
base.OnBegin(sender, libraryBook);
|
||||
}
|
||||
public override void OnCompleted(object sender, LibraryBook libraryBook)
|
||||
=> InfoLogAction($"Convert Step, Completed: {libraryBook.Book}{Environment.NewLine}");
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
namespace LibationWinForms.BookLiberation
|
||||
{
|
||||
partial class DecryptForm
|
||||
partial class AudioDecodeBaseForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
129
LibationWinForms/BookLiberation/AudioDecodeBaseForm.cs
Normal file
129
LibationWinForms/BookLiberation/AudioDecodeBaseForm.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using DataLayer;
|
||||
using Dinah.Core.Net.Http;
|
||||
using Dinah.Core.Windows.Forms;
|
||||
using FileLiberator;
|
||||
using System;
|
||||
|
||||
namespace LibationWinForms.BookLiberation
|
||||
{
|
||||
public partial class AudioDecodeBaseForm : ProcessBaseForm
|
||||
{
|
||||
public virtual string DecodeActionName { get; } = "Decoding";
|
||||
public AudioDecodeBaseForm() => InitializeComponent();
|
||||
|
||||
private Func<byte[]> GetCoverArtDelegate;
|
||||
|
||||
// book info
|
||||
private string title;
|
||||
private string authorNames;
|
||||
private string narratorNames;
|
||||
|
||||
public override void SetProcessable(IStreamable streamProcessable, Action<string> infoLog)
|
||||
{
|
||||
base.SetProcessable(streamProcessable, infoLog);
|
||||
|
||||
if (Streamable is not null && Streamable is IAudioDecodable audioDecodable)
|
||||
{
|
||||
OnUnsubscribeAll(this, EventArgs.Empty);
|
||||
|
||||
audioDecodable.RequestCoverArt += OnRequestCoverArt;
|
||||
audioDecodable.TitleDiscovered += OnTitleDiscovered;
|
||||
audioDecodable.AuthorsDiscovered += OnAuthorsDiscovered;
|
||||
audioDecodable.NarratorsDiscovered += OnNarratorsDiscovered;
|
||||
audioDecodable.CoverImageFilepathDiscovered += OnCoverImageFilepathDiscovered;
|
||||
|
||||
Disposed += OnUnsubscribeAll;
|
||||
}
|
||||
}
|
||||
private void OnUnsubscribeAll(object sender, EventArgs e)
|
||||
{
|
||||
Disposed -= OnUnsubscribeAll;
|
||||
if (Streamable is not null && Streamable is IAudioDecodable audioDecodable)
|
||||
{
|
||||
audioDecodable.RequestCoverArt -= OnRequestCoverArt;
|
||||
audioDecodable.TitleDiscovered -= OnTitleDiscovered;
|
||||
audioDecodable.AuthorsDiscovered -= OnAuthorsDiscovered;
|
||||
audioDecodable.NarratorsDiscovered -= OnNarratorsDiscovered;
|
||||
audioDecodable.CoverImageFilepathDiscovered -= OnCoverImageFilepathDiscovered;
|
||||
|
||||
audioDecodable.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
#region IProcessable event handler overrides
|
||||
public override void OnBegin(object sender, LibraryBook libraryBook)
|
||||
{
|
||||
GetCoverArtDelegate = () => FileManager.PictureStorage.GetPictureSynchronously(
|
||||
new FileManager.PictureDefinition(
|
||||
libraryBook.Book.PictureId,
|
||||
FileManager.PictureSize._500x500));
|
||||
|
||||
//Set default values from library
|
||||
OnTitleDiscovered(null, libraryBook.Book.Title);
|
||||
OnAuthorsDiscovered(null, string.Join(", ", libraryBook.Book.Authors));
|
||||
OnNarratorsDiscovered(null, string.Join(", ", libraryBook.Book.NarratorNames));
|
||||
OnCoverImageFilepathDiscovered(null,
|
||||
FileManager.PictureStorage.GetPictureSynchronously(
|
||||
new FileManager.PictureDefinition(
|
||||
libraryBook.Book.PictureId,
|
||||
FileManager.PictureSize._80x80)));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IStreamable event handler overrides
|
||||
|
||||
public override void OnStreamingProgressChanged(object sender, DownloadProgress downloadProgress)
|
||||
{
|
||||
if (!downloadProgress.ProgressPercentage.HasValue)
|
||||
return;
|
||||
|
||||
if (downloadProgress.ProgressPercentage == 0)
|
||||
updateRemainingTime(0);
|
||||
else
|
||||
progressBar1.UIThread(() => progressBar1.Value = (int)downloadProgress.ProgressPercentage);
|
||||
}
|
||||
|
||||
public override void OnStreamingTimeRemaining(object sender, TimeSpan timeRemaining)
|
||||
=> updateRemainingTime((int)timeRemaining.TotalSeconds);
|
||||
|
||||
#endregion
|
||||
|
||||
#region IAudioDecodable event handlers
|
||||
|
||||
public virtual void OnRequestCoverArt(object sender, Action<byte[]> setCoverArtDelegate)
|
||||
=> setCoverArtDelegate(GetCoverArtDelegate?.Invoke());
|
||||
|
||||
public virtual void OnTitleDiscovered(object sender, string title)
|
||||
{
|
||||
this.UIThread(() => this.Text = DecodeActionName + " " + title);
|
||||
this.title = title;
|
||||
updateBookInfo();
|
||||
}
|
||||
|
||||
public virtual void OnAuthorsDiscovered(object sender, string authors)
|
||||
{
|
||||
authorNames = authors;
|
||||
updateBookInfo();
|
||||
}
|
||||
|
||||
public virtual void OnNarratorsDiscovered(object sender, string narrators)
|
||||
{
|
||||
narratorNames = narrators;
|
||||
updateBookInfo();
|
||||
}
|
||||
|
||||
public virtual void OnCoverImageFilepathDiscovered(object sender, byte[] coverArt)
|
||||
=> pictureBox1.UIThread(() => pictureBox1.Image = Dinah.Core.Drawing.ImageReader.ToImage(coverArt));
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
// thread-safe UI updates
|
||||
private void updateBookInfo()
|
||||
=> bookInfoLbl.UIThread(() => bookInfoLbl.Text = $"{title}\r\nBy {authorNames}\r\nNarrated by {narratorNames}");
|
||||
|
||||
private void updateRemainingTime(int remaining)
|
||||
=> remainingTimeLbl.UIThread(() => remainingTimeLbl.Text = $"ETA:\r\n{remaining} sec");
|
||||
|
||||
}
|
||||
}
|
||||
24
LibationWinForms/BookLiberation/AudioDecryptForm.cs
Normal file
24
LibationWinForms/BookLiberation/AudioDecryptForm.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using DataLayer;
|
||||
using System;
|
||||
|
||||
namespace LibationWinForms.BookLiberation
|
||||
{
|
||||
class AudioDecryptForm : AudioDecodeBaseForm
|
||||
{
|
||||
#region AudioDecodeBaseForm overrides
|
||||
public override string DecodeActionName => "Decrypting";
|
||||
#endregion
|
||||
|
||||
#region IProcessable event handler overrides
|
||||
public override void OnBegin(object sender, LibraryBook libraryBook)
|
||||
{
|
||||
InfoLogAction($"Download & Decrypt Step, Begin: {libraryBook.Book}");
|
||||
|
||||
base.OnBegin(sender, libraryBook);
|
||||
}
|
||||
public override void OnCompleted(object sender, LibraryBook libraryBook)
|
||||
=> InfoLogAction($"Download & Decrypt Step, Completed: {libraryBook.Book}{Environment.NewLine}");
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
using Dinah.Core.Windows.Forms;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LibationWinForms.BookLiberation
|
||||
{
|
||||
public partial class DecryptForm : Form
|
||||
{
|
||||
public DecryptForm() => InitializeComponent();
|
||||
|
||||
// book info
|
||||
private string title;
|
||||
private string authorNames;
|
||||
private string narratorNames;
|
||||
|
||||
public void SetTitle(string actionName, string title)
|
||||
{
|
||||
this.UIThread(() => this.Text = actionName + " " + title);
|
||||
this.title = title;
|
||||
updateBookInfo();
|
||||
}
|
||||
public void SetAuthorNames(string authorNames)
|
||||
{
|
||||
this.authorNames = authorNames;
|
||||
updateBookInfo();
|
||||
}
|
||||
public void SetNarratorNames(string narratorNames)
|
||||
{
|
||||
this.narratorNames = narratorNames;
|
||||
updateBookInfo();
|
||||
}
|
||||
|
||||
// thread-safe UI updates
|
||||
private void updateBookInfo()
|
||||
=> bookInfoLbl.UIThread(() => bookInfoLbl.Text = $"{title}\r\nBy {authorNames}\r\nNarrated by {narratorNames}");
|
||||
|
||||
public void SetCoverImage(System.Drawing.Image coverImage)
|
||||
=> pictureBox1.UIThread(() => pictureBox1.Image = coverImage);
|
||||
|
||||
public void UpdateProgress(int percentage)
|
||||
{
|
||||
if (percentage == 0)
|
||||
updateRemainingTime(0);
|
||||
else
|
||||
progressBar1.UIThread(() => progressBar1.Value = percentage);
|
||||
}
|
||||
|
||||
public void UpdateRemainingTime(TimeSpan remaining)
|
||||
=> updateRemainingTime((int)remaining.TotalSeconds);
|
||||
|
||||
private void updateRemainingTime(int remaining)
|
||||
=> remainingTimeLbl.UIThread(() => remainingTimeLbl.Text = $"ETA:\r\n{remaining} sec");
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,13 @@
|
||||
using Dinah.Core.Windows.Forms;
|
||||
using DataLayer;
|
||||
using Dinah.Core.Net.Http;
|
||||
using Dinah.Core.Windows.Forms;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LibationWinForms.BookLiberation
|
||||
{
|
||||
public partial class DownloadForm : Form
|
||||
public partial class DownloadForm : ProcessBaseForm
|
||||
{
|
||||
public DownloadForm()
|
||||
{
|
||||
@ -15,23 +17,27 @@ namespace LibationWinForms.BookLiberation
|
||||
filenameLbl.Text = "";
|
||||
}
|
||||
|
||||
// thread-safe UI updates
|
||||
public void UpdateFilename(string title) => filenameLbl.UIThread(() => filenameLbl.Text = title);
|
||||
|
||||
public void DownloadProgressChanged(long BytesReceived, long? TotalBytesToReceive)
|
||||
#region IStreamable event handler overrides
|
||||
public override void OnStreamingBegin(object sender, string beginString)
|
||||
{
|
||||
filenameLbl.UIThread(() => filenameLbl.Text = beginString);
|
||||
base.OnStreamingBegin(sender, beginString);
|
||||
}
|
||||
public override void OnStreamingProgressChanged(object sender, DownloadProgress downloadProgress)
|
||||
{
|
||||
// this won't happen with download file. it will happen with download string
|
||||
if (!TotalBytesToReceive.HasValue || TotalBytesToReceive.Value <= 0)
|
||||
if (!downloadProgress.TotalBytesToReceive.HasValue || downloadProgress.TotalBytesToReceive.Value <= 0)
|
||||
return;
|
||||
|
||||
progressLbl.UIThread(() => progressLbl.Text = $"{BytesReceived:#,##0} of {TotalBytesToReceive.Value:#,##0}");
|
||||
progressLbl.UIThread(() => progressLbl.Text = $"{downloadProgress.BytesReceived:#,##0} of {downloadProgress.TotalBytesToReceive.Value:#,##0}");
|
||||
|
||||
var d = double.Parse(BytesReceived.ToString()) / double.Parse(TotalBytesToReceive.Value.ToString()) * 100.0;
|
||||
var d = double.Parse(downloadProgress.BytesReceived.ToString()) / double.Parse(downloadProgress.TotalBytesToReceive.Value.ToString()) * 100.0;
|
||||
var i = int.Parse(Math.Truncate(d).ToString());
|
||||
progressBar1.UIThread(() => progressBar1.Value = i);
|
||||
|
||||
lastDownloadProgress = DateTime.Now;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region timer
|
||||
private Timer timer { get; } = new Timer { Interval = 1000 };
|
||||
|
||||
16
LibationWinForms/BookLiberation/PdfDownloadForm.cs
Normal file
16
LibationWinForms/BookLiberation/PdfDownloadForm.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using DataLayer;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationWinForms.BookLiberation
|
||||
{
|
||||
internal class PdfDownloadForm : DownloadForm
|
||||
{
|
||||
public override void OnBegin(object sender, LibraryBook libraryBook) => InfoLogAction($"PDF Step, Begin: {libraryBook.Book}");
|
||||
public override void OnCompleted(object sender, LibraryBook libraryBook) => InfoLogAction($"PDF Step, Completed: {libraryBook.Book}");
|
||||
|
||||
}
|
||||
}
|
||||
45
LibationWinForms/BookLiberation/ProcessBaseForm.cs
Normal file
45
LibationWinForms/BookLiberation/ProcessBaseForm.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using DataLayer;
|
||||
using Dinah.Core.Net.Http;
|
||||
using FileLiberator;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LibationWinForms.BookLiberation
|
||||
{
|
||||
public class ProcessBaseForm : StreamBaseForm
|
||||
{
|
||||
protected Action<string> InfoLogAction { get; private set; }
|
||||
public virtual void SetProcessable(IStreamable streamable, Action<string> infoLog)
|
||||
{
|
||||
InfoLogAction = infoLog;
|
||||
SetStreamable(streamable);
|
||||
|
||||
if (Streamable is not null && Streamable is IProcessable processable)
|
||||
{
|
||||
OnUnsubscribeAll(this, EventArgs.Empty);
|
||||
|
||||
processable.Begin += OnBegin;
|
||||
processable.Completed += OnCompleted;
|
||||
processable.StatusUpdate += OnStatusUpdate;
|
||||
Disposed += OnUnsubscribeAll;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnsubscribeAll(object sender, EventArgs e)
|
||||
{
|
||||
Disposed -= OnUnsubscribeAll;
|
||||
if (Streamable is not null && Streamable is IProcessable processable)
|
||||
{
|
||||
processable.Begin -= OnBegin;
|
||||
processable.Completed -= OnCompleted;
|
||||
processable.StatusUpdate -= OnStatusUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
#region IProcessable event handlers
|
||||
public virtual void OnBegin(object sender, LibraryBook libraryBook) => InfoLogAction($"Begin: {libraryBook.Book}");
|
||||
public virtual void OnStatusUpdate(object sender, string statusUpdate) => InfoLogAction("- " + statusUpdate);
|
||||
public virtual void OnCompleted(object sender, LibraryBook libraryBook) => InfoLogAction($"Completed: {libraryBook.Book}{Environment.NewLine}");
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -55,153 +55,65 @@ namespace LibationWinForms.BookLiberation
|
||||
{
|
||||
Serilog.Log.Logger.Information("Begin backup single {@DebugInfo}", new { libraryBook?.Book?.AudibleProductId });
|
||||
|
||||
var backupBook = getWiredUpBackupBook(completedAction);
|
||||
|
||||
(Action unsubscribeEvents, LogMe logMe) = attachToBackupsForm(backupBook);
|
||||
LogMe logMe = LogMe.RegisterForm();
|
||||
var backupBook = CreateBackupBook(completedAction, logMe);
|
||||
|
||||
// continue even if libraryBook is null. we'll display even that in the processing box
|
||||
await new BackupSingle(logMe, backupBook, libraryBook).RunBackupAsync();
|
||||
|
||||
unsubscribeEvents();
|
||||
}
|
||||
|
||||
public static async Task BackupAllBooksAsync(EventHandler<LibraryBook> completedAction = null)
|
||||
{
|
||||
Serilog.Log.Logger.Information("Begin " + nameof(BackupAllBooksAsync));
|
||||
|
||||
var backupBook = getWiredUpBackupBook(completedAction);
|
||||
var automatedBackupsForm = new AutomatedBackupsForm();
|
||||
LogMe logMe = LogMe.RegisterForm(automatedBackupsForm);
|
||||
|
||||
(Action unsubscribeEvents, LogMe logMe) = attachToBackupsForm(backupBook, automatedBackupsForm);
|
||||
var backupBook = CreateBackupBook(completedAction, logMe);
|
||||
|
||||
await new BackupLoop(logMe, backupBook, automatedBackupsForm).RunBackupAsync();
|
||||
|
||||
unsubscribeEvents();
|
||||
}
|
||||
|
||||
public static async Task ConvertAllBooksAsync()
|
||||
{
|
||||
Serilog.Log.Logger.Information("Begin " + nameof(ConvertAllBooksAsync));
|
||||
|
||||
var convertBook = new ConvertToMp3();
|
||||
convertBook.Begin += (_, l) => wireUpEvents(convertBook, l, "Converting");
|
||||
|
||||
var automatedBackupsForm = new AutomatedBackupsForm();
|
||||
LogMe logMe = LogMe.RegisterForm(automatedBackupsForm);
|
||||
|
||||
var logMe = LogMe.RegisterForm(automatedBackupsForm);
|
||||
|
||||
void statusUpdate(object _, string str) => logMe.Info("- " + str);
|
||||
void convertBookBegin(object _, LibraryBook libraryBook) => logMe.Info($"Convert Step, Begin: {libraryBook.Book}");
|
||||
void convertBookCompleted(object _, LibraryBook libraryBook) => logMe.Info($"Convert Step, Completed: {libraryBook.Book}{Environment.NewLine}");
|
||||
convertBook.Begin += convertBookBegin;
|
||||
convertBook.StatusUpdate += statusUpdate;
|
||||
convertBook.Completed += convertBookCompleted;
|
||||
var convertBook = CreateStreamableProcessable<ConvertToMp3, AudioConvertForm>(null, logMe);
|
||||
|
||||
await new BackupLoop(logMe, convertBook, automatedBackupsForm).RunBackupAsync();
|
||||
|
||||
convertBook.Begin -= convertBookBegin;
|
||||
convertBook.StatusUpdate -= statusUpdate;
|
||||
convertBook.Completed -= convertBookCompleted;
|
||||
}
|
||||
|
||||
private static BackupBook getWiredUpBackupBook(EventHandler<LibraryBook> completedAction)
|
||||
private static BackupBook CreateBackupBook(EventHandler<LibraryBook> completedAction, LogMe logMe)
|
||||
{
|
||||
var backupBook = new BackupBook();
|
||||
|
||||
backupBook.DownloadDecryptBook.Begin += (_, l) => wireUpEvents(backupBook.DownloadDecryptBook, l);
|
||||
backupBook.DownloadPdf.Begin += (_, __) => wireUpEvents(backupBook.DownloadPdf);
|
||||
|
||||
if (completedAction != null)
|
||||
{
|
||||
backupBook.DownloadDecryptBook.Completed += completedAction;
|
||||
backupBook.DownloadPdf.Completed += completedAction;
|
||||
}
|
||||
|
||||
return backupBook;
|
||||
}
|
||||
|
||||
private static (Action unsubscribeEvents, LogMe) attachToBackupsForm(BackupBook backupBook, AutomatedBackupsForm automatedBackupsForm = null)
|
||||
{
|
||||
#region create logger
|
||||
var logMe = LogMe.RegisterForm(automatedBackupsForm);
|
||||
#endregion
|
||||
|
||||
#region define how model actions will affect form behavior
|
||||
void statusUpdate(object _, string str) => logMe.Info("- " + str);
|
||||
void decryptBookBegin(object _, LibraryBook libraryBook) => logMe.Info($"Decrypt Step, Begin: {libraryBook.Book}");
|
||||
// extra line after book is completely finished
|
||||
void decryptBookCompleted(object _, LibraryBook libraryBook) => logMe.Info($"Decrypt Step, Completed: {libraryBook.Book}{Environment.NewLine}");
|
||||
void downloadPdfBegin(object _, LibraryBook libraryBook) => logMe.Info($"PDF Step, Begin: {libraryBook.Book}");
|
||||
// extra line after book is completely finished
|
||||
void downloadPdfCompleted(object _, LibraryBook libraryBook) => logMe.Info($"PDF Step, Completed: {libraryBook.Book}{Environment.NewLine}");
|
||||
#endregion
|
||||
|
||||
#region subscribe new form to model's events
|
||||
backupBook.DownloadDecryptBook.Begin += decryptBookBegin;
|
||||
backupBook.DownloadDecryptBook.StatusUpdate += statusUpdate;
|
||||
backupBook.DownloadDecryptBook.Completed += decryptBookCompleted;
|
||||
backupBook.DownloadPdf.Begin += downloadPdfBegin;
|
||||
backupBook.DownloadPdf.StatusUpdate += statusUpdate;
|
||||
backupBook.DownloadPdf.Completed += downloadPdfCompleted;
|
||||
#endregion
|
||||
|
||||
#region when form closes, unsubscribe from model's events
|
||||
// unsubscribe so disposed forms aren't still trying to receive notifications
|
||||
Action unsubscribe = () =>
|
||||
{
|
||||
backupBook.DownloadDecryptBook.Begin -= decryptBookBegin;
|
||||
backupBook.DownloadDecryptBook.StatusUpdate -= statusUpdate;
|
||||
backupBook.DownloadDecryptBook.Completed -= decryptBookCompleted;
|
||||
backupBook.DownloadPdf.Begin -= downloadPdfBegin;
|
||||
backupBook.DownloadPdf.StatusUpdate -= statusUpdate;
|
||||
backupBook.DownloadPdf.Completed -= downloadPdfCompleted;
|
||||
};
|
||||
#endregion
|
||||
|
||||
return (unsubscribe, logMe);
|
||||
var downloadPdf = CreateStreamableProcessable<DownloadPdf, DownloadForm>(completedAction, logMe);
|
||||
var downloadDecryptBook = CreateStreamableProcessable<DownloadDecryptBook, AudioDecryptForm>(completedAction, logMe);
|
||||
return new BackupBook(downloadDecryptBook, downloadPdf);
|
||||
}
|
||||
|
||||
public static async Task BackupAllPdfsAsync(EventHandler<LibraryBook> completedAction = null)
|
||||
{
|
||||
Serilog.Log.Logger.Information("Begin " + nameof(BackupAllPdfsAsync));
|
||||
|
||||
var downloadPdf = getWiredUpDownloadPdf(completedAction);
|
||||
var automatedBackupsForm = new AutomatedBackupsForm();
|
||||
LogMe logMe = LogMe.RegisterForm(automatedBackupsForm);
|
||||
|
||||
var downloadPdf = CreateStreamableProcessable<DownloadPdf, DownloadForm>(completedAction, logMe);
|
||||
|
||||
(AutomatedBackupsForm automatedBackupsForm, LogMe logMe) = attachToBackupsForm(downloadPdf);
|
||||
await new BackupLoop(logMe, downloadPdf, automatedBackupsForm).RunBackupAsync();
|
||||
}
|
||||
|
||||
private static DownloadPdf getWiredUpDownloadPdf(EventHandler<LibraryBook> completedAction)
|
||||
{
|
||||
var downloadPdf = new DownloadPdf();
|
||||
|
||||
downloadPdf.Begin += (_, __) => wireUpEvents(downloadPdf);
|
||||
|
||||
if (completedAction != null)
|
||||
downloadPdf.Completed += completedAction;
|
||||
|
||||
return downloadPdf;
|
||||
}
|
||||
|
||||
public static void DownloadFile(string url, string destination, bool showDownloadCompletedDialog = false)
|
||||
{
|
||||
var downloadDialog = new DownloadForm();
|
||||
downloadDialog.UpdateFilename(destination);
|
||||
downloadDialog.Show();
|
||||
|
||||
new System.Threading.Thread(() =>
|
||||
{
|
||||
var downloadFile = new DownloadFile();
|
||||
(DownloadFile downloadFile, DownloadForm downloadForm) = CreateStreamable<DownloadFile, DownloadForm>();
|
||||
|
||||
downloadFile.DownloadProgressChanged += (_, progress) => downloadDialog.UIThread(() =>
|
||||
downloadDialog.DownloadProgressChanged(progress.BytesReceived, progress.TotalBytesToReceive)
|
||||
);
|
||||
downloadFile.DownloadCompleted += (_, __) => downloadDialog.UIThread(() =>
|
||||
{
|
||||
downloadDialog.Close();
|
||||
if (showDownloadCompletedDialog)
|
||||
MessageBox.Show("File downloaded");
|
||||
});
|
||||
downloadFile.StreamingCompleted += (_, __) => MessageBox.Show("File downloaded");
|
||||
|
||||
downloadFile.PerformDownloadFileAsync(url, destination).GetAwaiter().GetResult();
|
||||
})
|
||||
@ -209,171 +121,41 @@ namespace LibationWinForms.BookLiberation
|
||||
.Start();
|
||||
}
|
||||
|
||||
// subscribed to Begin event because a new form should be created+processed+closed on each iteration
|
||||
private static void wireUpEvents(IDownloadableProcessable downloadable)
|
||||
/// <summary>
|
||||
/// Create a new <see cref="IStreamProcessable"/> and which creates a new <see cref="ProcessBaseForm"/> on IProcessable.Begin.
|
||||
/// </summary>
|
||||
/// <typeparam name="TStrProc">The <see cref="IStreamProcessable"/> derrived type to create.</typeparam>
|
||||
/// <typeparam name="TForm">The <see cref="ProcessBaseForm"/> derrived form to create on Begin</typeparam>
|
||||
/// <param name="completedAction">An additional event handler to handle <typeparamref name="TStrProc"/>.Completed</param>
|
||||
/// <returns>A new <see cref="IStreamProcessable"/> of type <typeparamref name="TStrProc"/></returns>
|
||||
private static TStrProc CreateStreamableProcessable<TStrProc, TForm>(EventHandler<LibraryBook> completedAction = null, LogMe logMe = null)
|
||||
where TForm : ProcessBaseForm, new()
|
||||
where TStrProc : IStreamProcessable, new()
|
||||
{
|
||||
#region create form
|
||||
var downloadDialog = new DownloadForm();
|
||||
#endregion
|
||||
var strProc = new TStrProc();
|
||||
|
||||
// extra complexity for wiring up download form:
|
||||
// case 1: download is needed
|
||||
// dialog created. subscribe to events
|
||||
// downloadable.DownloadBegin fires. shows dialog
|
||||
// downloadable.DownloadCompleted fires. closes dialog. which fires FormClosing, FormClosed, Disposed
|
||||
// Disposed unsubscribe from events
|
||||
// case 2: download is not needed
|
||||
// dialog created. subscribe to events
|
||||
// dialog is never shown nor closed
|
||||
// downloadable.Completed fires. disposes dialog and unsubscribes from events
|
||||
|
||||
#region define how model actions will affect form behavior
|
||||
void downloadBegin(object _, string str)
|
||||
strProc.Begin += (sender, libraryBook) =>
|
||||
{
|
||||
downloadDialog.UpdateFilename(str);
|
||||
downloadDialog.Show();
|
||||
}
|
||||
|
||||
// close form on DOWNLOAD completed, not final Completed. Else for BackupBook this form won't close until DECRYPT is also complete
|
||||
void fileDownloadCompleted(object _, string __) => downloadDialog.Close();
|
||||
|
||||
void downloadProgressChanged(object _, Dinah.Core.Net.Http.DownloadProgress progress)
|
||||
=> downloadDialog.DownloadProgressChanged(progress.BytesReceived, progress.TotalBytesToReceive);
|
||||
|
||||
void unsubscribe(object _ = null, EventArgs __ = null)
|
||||
{
|
||||
downloadable.DownloadBegin -= downloadBegin;
|
||||
downloadable.DownloadCompleted -= fileDownloadCompleted;
|
||||
downloadable.DownloadProgressChanged -= downloadProgressChanged;
|
||||
downloadable.Completed -= dialogDispose;
|
||||
}
|
||||
|
||||
// unless we dispose, if the form is created but un-used/never-shown then weird UI stuff can happen
|
||||
// also, since event unsubscribe occurs on FormClosing and an unused form is never closed, then the events will never be unsubscribed
|
||||
void dialogDispose(object _, object __)
|
||||
{
|
||||
if (!downloadDialog.IsDisposed)
|
||||
downloadDialog.Dispose();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region subscribe new form to model's events
|
||||
downloadable.DownloadBegin += downloadBegin;
|
||||
downloadable.DownloadCompleted += fileDownloadCompleted;
|
||||
downloadable.DownloadProgressChanged += downloadProgressChanged;
|
||||
downloadable.Completed += dialogDispose;
|
||||
#endregion
|
||||
|
||||
#region when form closes, unsubscribe from model's events
|
||||
// unsubscribe so disposed forms aren't still trying to receive notifications
|
||||
// FormClosing is more UI safe but won't fire unless the form is shown and closed
|
||||
// if form was shown, Disposed will fire for FormClosing, FormClosed, and Disposed
|
||||
// if not shown, it will still fire for Disposed
|
||||
downloadDialog.Disposed += unsubscribe;
|
||||
#endregion
|
||||
}
|
||||
|
||||
// subscribed to Begin event because a new form should be created+processed+closed on each iteration
|
||||
private static void wireUpEvents(IDecryptable decryptBook, LibraryBook libraryBook, string actionName = "Decrypting")
|
||||
{
|
||||
#region create form
|
||||
var decryptDialog = new DecryptForm();
|
||||
#endregion
|
||||
|
||||
#region Set initially displayed book properties from library info.
|
||||
decryptDialog.SetTitle(actionName, libraryBook.Book.Title);
|
||||
decryptDialog.SetAuthorNames(string.Join(", ", libraryBook.Book.Authors));
|
||||
decryptDialog.SetNarratorNames(string.Join(", ", libraryBook.Book.NarratorNames));
|
||||
decryptDialog.SetCoverImage(
|
||||
Dinah.Core.Drawing.ImageReader.ToImage(
|
||||
FileManager.PictureStorage.GetPictureSynchronously(
|
||||
new FileManager.PictureDefinition(
|
||||
libraryBook.Book.PictureId,
|
||||
FileManager.PictureSize._80x80))));
|
||||
#endregion
|
||||
|
||||
#region define how model actions will affect form behavior
|
||||
void decryptBegin(object _, string __) => decryptDialog.Show();
|
||||
void titleDiscovered(object _, string title) => decryptDialog.SetTitle(actionName, title);
|
||||
void authorsDiscovered(object _, string authors) => decryptDialog.SetAuthorNames(authors);
|
||||
void narratorsDiscovered(object _, string narrators) => decryptDialog.SetNarratorNames(narrators);
|
||||
void coverImageFilepathDiscovered(object _, byte[] coverBytes) => decryptDialog.SetCoverImage(Dinah.Core.Drawing.ImageReader.ToImage(coverBytes));
|
||||
void updateProgress(object _, int percentage) => decryptDialog.UpdateProgress(percentage);
|
||||
void updateRemainingTime(object _, TimeSpan remaining) => decryptDialog.UpdateRemainingTime(remaining);
|
||||
void decryptCompleted(object _, string __) => decryptDialog.Close();
|
||||
void requestCoverArt(object _, Action<byte[]> setCoverArtDelegate)
|
||||
=> setCoverArtDelegate(
|
||||
FileManager.PictureStorage.GetPictureSynchronously(
|
||||
new FileManager.PictureDefinition(
|
||||
libraryBook.Book.PictureId,
|
||||
FileManager.PictureSize._500x500)));
|
||||
#endregion
|
||||
|
||||
#region subscribe new form to model's events
|
||||
decryptBook.DecryptBegin += decryptBegin;
|
||||
|
||||
decryptBook.TitleDiscovered += titleDiscovered;
|
||||
decryptBook.AuthorsDiscovered += authorsDiscovered;
|
||||
decryptBook.NarratorsDiscovered += narratorsDiscovered;
|
||||
decryptBook.CoverImageFilepathDiscovered += coverImageFilepathDiscovered;
|
||||
decryptBook.UpdateProgress += updateProgress;
|
||||
decryptBook.UpdateRemainingTime += updateRemainingTime;
|
||||
decryptBook.RequestCoverArt += requestCoverArt;
|
||||
|
||||
decryptBook.DecryptCompleted += decryptCompleted;
|
||||
#endregion
|
||||
|
||||
#region when form closes, unsubscribe from model's events
|
||||
// unsubscribe so disposed forms aren't still trying to receive notifications
|
||||
decryptDialog.FormClosing += (_, __) =>
|
||||
{
|
||||
decryptBook.DecryptBegin -= decryptBegin;
|
||||
|
||||
decryptBook.TitleDiscovered -= titleDiscovered;
|
||||
decryptBook.AuthorsDiscovered -= authorsDiscovered;
|
||||
decryptBook.NarratorsDiscovered -= narratorsDiscovered;
|
||||
decryptBook.CoverImageFilepathDiscovered -= coverImageFilepathDiscovered;
|
||||
decryptBook.UpdateProgress -= updateProgress;
|
||||
decryptBook.UpdateRemainingTime -= updateRemainingTime;
|
||||
decryptBook.RequestCoverArt -= requestCoverArt;
|
||||
|
||||
decryptBook.DecryptCompleted -= decryptCompleted;
|
||||
decryptBook.Cancel();
|
||||
var processForm = new TForm();
|
||||
processForm.SetProcessable(strProc, logMe.Info);
|
||||
processForm.OnBegin(sender, libraryBook);
|
||||
};
|
||||
#endregion
|
||||
|
||||
if (completedAction != null)
|
||||
strProc.Completed += completedAction;
|
||||
|
||||
return strProc;
|
||||
}
|
||||
|
||||
private static (AutomatedBackupsForm, LogMe) attachToBackupsForm(IDownloadableProcessable downloadable)
|
||||
private static (TStrProc, TForm) CreateStreamable<TStrProc, TForm>(EventHandler<LibraryBook> completedAction = null)
|
||||
where TForm : StreamBaseForm, new()
|
||||
where TStrProc : IStreamable, new()
|
||||
{
|
||||
#region create form and logger
|
||||
var automatedBackupsForm = new AutomatedBackupsForm();
|
||||
var logMe = LogMe.RegisterForm(automatedBackupsForm);
|
||||
#endregion
|
||||
var strProc = new TStrProc();
|
||||
|
||||
#region define how model actions will affect form behavior
|
||||
void begin(object _, LibraryBook libraryBook) => logMe.Info($"Begin: {libraryBook.Book}");
|
||||
void statusUpdate(object _, string str) => logMe.Info("- " + str);
|
||||
// extra line after book is completely finished
|
||||
void completed(object _, LibraryBook libraryBook) => logMe.Info($"Completed: {libraryBook.Book}{Environment.NewLine}");
|
||||
#endregion
|
||||
var streamForm = new TForm();
|
||||
streamForm.SetStreamable(strProc);
|
||||
|
||||
#region subscribe new form to model's events
|
||||
downloadable.Begin += begin;
|
||||
downloadable.StatusUpdate += statusUpdate;
|
||||
downloadable.Completed += completed;
|
||||
#endregion
|
||||
|
||||
#region when form closes, unsubscribe from model's events
|
||||
// unsubscribe so disposed forms aren't still trying to receive notifications
|
||||
automatedBackupsForm.FormClosing += (_, __) =>
|
||||
{
|
||||
downloadable.Begin -= begin;
|
||||
downloadable.StatusUpdate -= statusUpdate;
|
||||
downloadable.Completed -= completed;
|
||||
};
|
||||
#endregion
|
||||
|
||||
return (automatedBackupsForm, logMe);
|
||||
return (strProc, streamForm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
52
LibationWinForms/BookLiberation/StreamBaseForm.cs
Normal file
52
LibationWinForms/BookLiberation/StreamBaseForm.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using Dinah.Core.Net.Http;
|
||||
using FileLiberator;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LibationWinForms.BookLiberation
|
||||
{
|
||||
public class StreamBaseForm : Form
|
||||
{
|
||||
protected IStreamable Streamable { get; private set; }
|
||||
public void SetStreamable(IStreamable streamable)
|
||||
{
|
||||
Streamable = streamable;
|
||||
|
||||
if (Streamable is null) return;
|
||||
|
||||
OnUnsubscribeAll(this, EventArgs.Empty);
|
||||
|
||||
Streamable.StreamingBegin += OnStreamingBegin;
|
||||
Streamable.StreamingCompleted += OnStreamingCompleted;
|
||||
Streamable.StreamingProgressChanged += OnStreamingProgressChanged;
|
||||
Streamable.StreamingTimeRemaining += OnStreamingTimeRemaining;
|
||||
|
||||
Disposed += OnUnsubscribeAll;
|
||||
}
|
||||
|
||||
private void OnUnsubscribeAll(object sender, EventArgs e)
|
||||
{
|
||||
Disposed -= OnUnsubscribeAll;
|
||||
|
||||
Streamable.StreamingBegin -= OnStreamingBegin;
|
||||
Streamable.StreamingCompleted -= OnStreamingCompleted;
|
||||
Streamable.StreamingProgressChanged -= OnStreamingProgressChanged;
|
||||
Streamable.StreamingTimeRemaining -= OnStreamingTimeRemaining;
|
||||
}
|
||||
|
||||
#region IStreamable event handlers
|
||||
public virtual void OnStreamingBegin(object sender, string beginString) => Show();
|
||||
public virtual void OnStreamingProgressChanged(object sender, DownloadProgress downloadProgress) { }
|
||||
public virtual void OnStreamingTimeRemaining(object sender, TimeSpan timeRemaining) { }
|
||||
public virtual void OnStreamingCompleted(object sender, string completedString)
|
||||
{
|
||||
Close();
|
||||
Dispose();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
17
LibationWinForms/Form1.Designer.cs
generated
17
LibationWinForms/Form1.Designer.cs
generated
@ -61,6 +61,7 @@
|
||||
this.backupsCountsLbl = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.pdfsCountsLbl = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.addFilterBtn = new System.Windows.Forms.Button();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.menuStrip1.SuspendLayout();
|
||||
this.statusStrip1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
@ -177,14 +178,14 @@
|
||||
// removeAllAccountsToolStripMenuItem
|
||||
//
|
||||
this.removeAllAccountsToolStripMenuItem.Name = "removeAllAccountsToolStripMenuItem";
|
||||
this.removeAllAccountsToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
|
||||
this.removeAllAccountsToolStripMenuItem.Size = new System.Drawing.Size(157, 22);
|
||||
this.removeAllAccountsToolStripMenuItem.Text = "All Accounts";
|
||||
this.removeAllAccountsToolStripMenuItem.Click += new System.EventHandler(this.removeAllAccountsToolStripMenuItem_Click);
|
||||
//
|
||||
// removeSomeAccountsToolStripMenuItem
|
||||
//
|
||||
this.removeSomeAccountsToolStripMenuItem.Name = "removeSomeAccountsToolStripMenuItem";
|
||||
this.removeSomeAccountsToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
|
||||
this.removeSomeAccountsToolStripMenuItem.Size = new System.Drawing.Size(157, 22);
|
||||
this.removeSomeAccountsToolStripMenuItem.Text = "Some Accounts";
|
||||
this.removeSomeAccountsToolStripMenuItem.Click += new System.EventHandler(this.removeSomeAccountsToolStripMenuItem_Click);
|
||||
//
|
||||
@ -336,11 +337,22 @@
|
||||
this.addFilterBtn.UseVisualStyleBackColor = true;
|
||||
this.addFilterBtn.Click += new System.EventHandler(this.AddFilterBtn_Click);
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Location = new System.Drawing.Point(648, 0);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(123, 70);
|
||||
this.button1.TabIndex = 0;
|
||||
this.button1.Text = "button1";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// Form1
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(1007, 539);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Controls.Add(this.filterBtn);
|
||||
this.Controls.Add(this.addFilterBtn);
|
||||
this.Controls.Add(this.filterSearchTb);
|
||||
@ -398,5 +410,6 @@
|
||||
private System.Windows.Forms.ToolStripMenuItem removeLibraryBooksToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem removeAllAccountsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem removeSomeAccountsToolStripMenuItem;
|
||||
private System.Windows.Forms.Button button1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,6 +63,7 @@ namespace LibationWinForms
|
||||
|
||||
// also applies filter. ONLY call AFTER loading grid
|
||||
loadInitialQuickFilterState();
|
||||
|
||||
}
|
||||
|
||||
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
|
||||
@ -494,5 +495,13 @@ namespace LibationWinForms
|
||||
|
||||
private void basicSettingsToolStripMenuItem_Click(object sender, EventArgs e) => new SettingsDialog().ShowDialog();
|
||||
#endregion
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
BookLiberation.ProcessorAutomationController.DownloadFile(
|
||||
"https://github.com/rmcrackan/Libation/releases/download/v5.4.9/Libation.5.4.9.zip",
|
||||
@"C:\Users\mbuca\Downloads\libation test dl.zip");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<root>
|
||||
<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:element name="root" msdata:IsDataSet="true">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user