diff --git a/FileLiberator/ConvertToMp3.cs b/FileLiberator/ConvertToMp3.cs index ab99927a..10465727 100644 --- a/FileLiberator/ConvertToMp3.cs +++ b/FileLiberator/ConvertToMp3.cs @@ -12,39 +12,32 @@ using System.Threading.Tasks; namespace FileLiberator { - public class ConvertToMp3 : IAudioDecodable + public class ConvertToMp3 : Processable, IAudioDecodable { private Mp4File m4bBook; - public event EventHandler StreamingTimeRemaining; public event EventHandler> RequestCoverArt; public event EventHandler TitleDiscovered; public event EventHandler AuthorsDiscovered; public event EventHandler NarratorsDiscovered; public event EventHandler CoverImageDiscovered; - public event EventHandler StreamingBegin; - public event EventHandler StreamingProgressChanged; - public event EventHandler StreamingCompleted; - public event EventHandler Begin; - public event EventHandler StatusUpdate; - public event EventHandler Completed; private long fileSize; private string Mp3FileName(string m4bPath) => m4bPath is null ? string.Empty : PathLib.ReplaceExtension(m4bPath, ".mp3"); public void Cancel() => m4bBook?.Cancel(); - public bool Validate(LibraryBook libraryBook) + public override bool Validate(LibraryBook libraryBook) { var path = AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId); return path?.ToLower()?.EndsWith(".m4b") == true && !File.Exists(Mp3FileName(path)); } - public async Task ProcessAsync(LibraryBook libraryBook) + public override async Task ProcessAsync(LibraryBook libraryBook) { - Begin?.Invoke(this, libraryBook); + OnBegin(libraryBook); - StreamingBegin?.Invoke(this, $"Begin converting {libraryBook} to mp3"); + OnStreamingBegin($"Begin converting {libraryBook} to mp3"); try { @@ -78,8 +71,8 @@ namespace FileLiberator } finally { - StreamingCompleted?.Invoke(this, $"Completed converting to mp3: {libraryBook.Book.Title}"); - Completed?.Invoke(this, libraryBook); + OnStreamingCompleted($"Completed converting to mp3: {libraryBook.Book.Title}"); + OnCompleted(libraryBook); } } @@ -90,11 +83,11 @@ namespace FileLiberator double estTimeRemaining = remainingSecsToProcess / e.ProcessSpeed; if (double.IsNormal(estTimeRemaining)) - StreamingTimeRemaining?.Invoke(this, TimeSpan.FromSeconds(estTimeRemaining)); + OnStreamingTimeRemaining(TimeSpan.FromSeconds(estTimeRemaining)); double progressPercent = 100 * e.ProcessPosition.TotalSeconds / duration.TotalSeconds; - StreamingProgressChanged?.Invoke(this, + OnStreamingProgressChanged( new DownloadProgress { ProgressPercentage = progressPercent, diff --git a/FileLiberator/DownloadDecryptBook.cs b/FileLiberator/DownloadDecryptBook.cs index 3ed348b7..c1b3f619 100644 --- a/FileLiberator/DownloadDecryptBook.cs +++ b/FileLiberator/DownloadDecryptBook.cs @@ -13,26 +13,19 @@ using FileManager; namespace FileLiberator { - public class DownloadDecryptBook : IAudioDecodable + public class DownloadDecryptBook : Processable, IAudioDecodable { private AudiobookDownloadBase aaxcDownloader; - public event EventHandler StreamingTimeRemaining; public event EventHandler> RequestCoverArt; public event EventHandler TitleDiscovered; public event EventHandler AuthorsDiscovered; public event EventHandler NarratorsDiscovered; public event EventHandler CoverImageDiscovered; - public event EventHandler StreamingBegin; - public event EventHandler StreamingProgressChanged; - public event EventHandler StreamingCompleted; - public event EventHandler Begin; - public event EventHandler StatusUpdate; - public event EventHandler Completed; - public async Task ProcessAsync(LibraryBook libraryBook) + public override async Task ProcessAsync(LibraryBook libraryBook) { - Begin?.Invoke(this, libraryBook); + OnBegin(libraryBook); try { @@ -57,13 +50,13 @@ namespace FileLiberator } finally { - Completed?.Invoke(this, libraryBook); + OnCompleted(libraryBook); } } private async Task downloadAudiobookAsync(string cacheDir, string destinationDir, LibraryBook libraryBook) { - StreamingBegin?.Invoke(this, $"Begin decrypting {libraryBook}"); + OnStreamingBegin($"Begin decrypting {libraryBook}"); try { @@ -101,8 +94,8 @@ namespace FileLiberator aaxcDownloader = contentLic.DrmType == AudibleApi.Common.DrmType.Adrm ? new AaxcDownloadConverter(outFileName, cacheDir, audiobookDlLic, outputFormat, Configuration.Instance.SplitFilesByChapter) { AppName = "Libation" } : new UnencryptedAudiobookDownloader(outFileName, cacheDir, audiobookDlLic); - aaxcDownloader.DecryptProgressUpdate += (s, progress) => StreamingProgressChanged?.Invoke(this, progress); - aaxcDownloader.DecryptTimeRemaining += (s, remaining) => StreamingTimeRemaining?.Invoke(this, remaining); + aaxcDownloader.DecryptProgressUpdate += (s, progress) => OnStreamingProgressChanged(progress); + aaxcDownloader.DecryptTimeRemaining += (s, remaining) => OnStreamingTimeRemaining(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); @@ -119,7 +112,7 @@ namespace FileLiberator } finally { - StreamingCompleted?.Invoke(this, $"Completed downloading and decrypting {libraryBook.Book.Title}"); + OnStreamingCompleted($"Completed downloading and decrypting {libraryBook.Book.Title}"); } } @@ -214,7 +207,7 @@ namespace FileLiberator throw new Exception(errorString("Locale")); } - public bool Validate(LibraryBook libraryBook) => !libraryBook.Book.Audio_Exists; + public override bool Validate(LibraryBook libraryBook) => !libraryBook.Book.Audio_Exists; public void Cancel() { diff --git a/FileLiberator/DownloadPdf.cs b/FileLiberator/DownloadPdf.cs index 5295b3aa..7a6fa039 100644 --- a/FileLiberator/DownloadPdf.cs +++ b/FileLiberator/DownloadPdf.cs @@ -11,25 +11,15 @@ using FileManager; namespace FileLiberator { - public class DownloadPdf : IProcessable + public class DownloadPdf : Processable { - public event EventHandler Begin; - public event EventHandler Completed; - - public event EventHandler StreamingBegin; - public event EventHandler StreamingProgressChanged; - public event EventHandler StreamingCompleted; - - public event EventHandler StatusUpdate; - public event EventHandler StreamingTimeRemaining; - - public bool Validate(LibraryBook libraryBook) + public override bool Validate(LibraryBook libraryBook) => !string.IsNullOrWhiteSpace(getdownloadUrl(libraryBook)) && !libraryBook.Book.PDF_Exists; - public async Task ProcessAsync(LibraryBook libraryBook) + public override async Task ProcessAsync(LibraryBook libraryBook) { - Begin?.Invoke(this, libraryBook); + OnBegin(libraryBook); try { @@ -43,7 +33,7 @@ namespace FileLiberator } finally { - Completed?.Invoke(this, libraryBook); + OnCompleted(libraryBook); } } @@ -69,7 +59,7 @@ namespace FileLiberator private async Task downloadPdfAsync(LibraryBook libraryBook, string proposedDownloadFilePath) { - StreamingBegin?.Invoke(this, proposedDownloadFilePath); + OnStreamingBegin(proposedDownloadFilePath); try { @@ -77,17 +67,17 @@ namespace FileLiberator var downloadUrl = await api.GetPdfDownloadLinkAsync(libraryBook.Book.AudibleProductId); var progress = new Progress(); - progress.ProgressChanged += (_, e) => StreamingProgressChanged?.Invoke(this, e); + progress.ProgressChanged += (_, e) => OnStreamingProgressChanged(e); var client = new HttpClient(); var actualDownloadedFilePath = await client.DownloadFileAsync(downloadUrl, proposedDownloadFilePath, progress); - StatusUpdate?.Invoke(this, actualDownloadedFilePath); + OnStatusUpdate(actualDownloadedFilePath); return actualDownloadedFilePath; } finally { - StreamingCompleted?.Invoke(this, proposedDownloadFilePath); + OnStreamingCompleted(proposedDownloadFilePath); } } diff --git a/FileLiberator/IAudioDecodable.cs b/FileLiberator/IAudioDecodable.cs index 128242ef..ff5fc3ed 100644 --- a/FileLiberator/IAudioDecodable.cs +++ b/FileLiberator/IAudioDecodable.cs @@ -2,7 +2,7 @@ namespace FileLiberator { - public interface IAudioDecodable : IProcessable + public interface IAudioDecodable { event EventHandler> RequestCoverArt; event EventHandler TitleDiscovered; diff --git a/FileLiberator/IProcessable.cs b/FileLiberator/IProcessable.cs deleted file mode 100644 index 6def9301..00000000 --- a/FileLiberator/IProcessable.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Threading.Tasks; -using DataLayer; -using Dinah.Core.ErrorHandling; - -namespace FileLiberator -{ - public interface IProcessable : IStreamable - { - event EventHandler Begin; - - /// General string message to display. DON'T rely on this for success, failure, or control logic - event EventHandler StatusUpdate; - - event EventHandler Completed; - - /// True == Valid - bool Validate(LibraryBook libraryBook); - - /// True == success - Task ProcessAsync(LibraryBook libraryBook); - } -} diff --git a/FileLiberator/IProcessableExt.cs b/FileLiberator/IProcessableExt.cs deleted file mode 100644 index 2e6a7b9e..00000000 --- a/FileLiberator/IProcessableExt.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DataLayer; -using Dinah.Core; -using Dinah.Core.ErrorHandling; - -namespace FileLiberator -{ - public static class IProcessableExt - { - // when used in foreach: stateful. deferred execution - public static IEnumerable GetValidLibraryBooks(this IProcessable processable, IEnumerable library) - => library.Where(libraryBook => - processable.Validate(libraryBook) - && (libraryBook.Book.ContentType != ContentType.Episode || FileManager.Configuration.Instance.DownloadEpisodes) - ); - - public static async Task ProcessSingleAsync(this IProcessable processable, LibraryBook libraryBook, bool validate) - { - if (validate && !processable.Validate(libraryBook)) - return new StatusHandler { "Validation failed" }; - - Serilog.Log.Logger.Information("Begin " + nameof(ProcessSingleAsync) + " {@DebugInfo}", new - { - libraryBook.Book.Title, - libraryBook.Book.AudibleProductId, - libraryBook.Book.Locale, - Account = libraryBook.Account?.ToMask() ?? "[empty]" - }); - - var status - = (await processable.ProcessAsync(libraryBook)) - ?? new StatusHandler { "Processable should never return a null status" }; - - return status; - } - - public static async Task TryProcessAsync(this IProcessable processable, LibraryBook libraryBook) - => processable.Validate(libraryBook) - ? await processable.ProcessAsync(libraryBook) - : new StatusHandler(); - } -} diff --git a/FileLiberator/Processable.cs b/FileLiberator/Processable.cs new file mode 100644 index 00000000..e7a68e1f --- /dev/null +++ b/FileLiberator/Processable.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DataLayer; +using Dinah.Core; +using Dinah.Core.ErrorHandling; +using Dinah.Core.Net.Http; + +namespace FileLiberator +{ + public abstract class Processable : IStreamable + { + public event EventHandler Begin; + + /// General string message to display. DON'T rely on this for success, failure, or control logic + public event EventHandler StatusUpdate; + + public event EventHandler Completed; + public event EventHandler StreamingBegin; + public event EventHandler StreamingProgressChanged; + public event EventHandler StreamingTimeRemaining; + public event EventHandler StreamingCompleted; + + + + // when used in foreach: stateful. deferred execution + public IEnumerable GetValidLibraryBooks(IEnumerable library) + => library.Where(libraryBook => + Validate(libraryBook) + && (libraryBook.Book.ContentType != ContentType.Episode || FileManager.Configuration.Instance.DownloadEpisodes) + ); + + public async Task ProcessSingleAsync(LibraryBook libraryBook, bool validate) + { + if (validate && !Validate(libraryBook)) + return new StatusHandler { "Validation failed" }; + + Serilog.Log.Logger.Information("Begin " + nameof(ProcessSingleAsync) + " {@DebugInfo}", new + { + libraryBook.Book.Title, + libraryBook.Book.AudibleProductId, + libraryBook.Book.Locale, + Account = libraryBook.Account?.ToMask() ?? "[empty]" + }); + + var status + = (await ProcessAsync(libraryBook)) + ?? new StatusHandler { "Processable should never return a null status" }; + + return status; + } + + public async Task TryProcessAsync( LibraryBook libraryBook) + => Validate(libraryBook) + ? await ProcessAsync(libraryBook) + : new StatusHandler(); + + /// True == Valid + public abstract bool Validate(LibraryBook libraryBook); + + /// True == success + public abstract Task ProcessAsync(LibraryBook libraryBook); + + public virtual void OnBegin(LibraryBook libraryBook) + { + Begin?.Invoke(this, libraryBook); + } + public virtual void OnCompleted(LibraryBook libraryBook) + { + Completed?.Invoke(this, libraryBook); + } + public virtual void OnStatusUpdate(string status) + { + StatusUpdate?.Invoke(this, status); + } + public virtual void OnStreamingBegin(string filePath) + { + StreamingBegin?.Invoke(this, filePath); + } + public virtual void OnStreamingCompleted(string filePath) + { + StreamingCompleted?.Invoke(this, filePath); + } + public virtual void OnStreamingProgressChanged(DownloadProgress progress) + { + StreamingProgressChanged?.Invoke(this, progress); + } + public virtual void OnStreamingTimeRemaining(TimeSpan timeRemaining) + { + StreamingTimeRemaining?.Invoke(this, timeRemaining); + } + } +} diff --git a/LibationCli/Options/LiberateOptions.cs b/LibationCli/Options/LiberateOptions.cs index 0115ae3a..6563c778 100644 --- a/LibationCli/Options/LiberateOptions.cs +++ b/LibationCli/Options/LiberateOptions.cs @@ -20,7 +20,7 @@ namespace LibationCli ? RunAsync(CreateProcessable()) : RunAsync(CreateBackupBook()); - private static IProcessable CreateBackupBook() + private static Processable CreateBackupBook() { var downloadPdf = CreateProcessable(); diff --git a/LibationCli/Options/_ProcessableOptionsBase.cs b/LibationCli/Options/_ProcessableOptionsBase.cs index e95e15d1..6daab32b 100644 --- a/LibationCli/Options/_ProcessableOptionsBase.cs +++ b/LibationCli/Options/_ProcessableOptionsBase.cs @@ -13,7 +13,7 @@ namespace LibationCli public abstract class ProcessableOptionsBase : OptionsBase { protected static TProcessable CreateProcessable(EventHandler completedAction = null) - where TProcessable : IProcessable, new() + where TProcessable : Processable, new() { var strProc = new TProcessable(); @@ -25,7 +25,7 @@ namespace LibationCli return strProc; } - protected static async Task RunAsync(IProcessable Processable) + protected static async Task RunAsync(Processable Processable) { foreach (var libraryBook in Processable.GetValidLibraryBooks(DbContexts.GetLibrary_Flat_NoTracking())) await ProcessOneAsync(Processable, libraryBook, false); @@ -35,7 +35,7 @@ namespace LibationCli Serilog.Log.Logger.Information(done); } - private static async Task ProcessOneAsync(IProcessable Processable, LibraryBook libraryBook, bool validate) + private static async Task ProcessOneAsync(Processable Processable, LibraryBook libraryBook, bool validate) { try { diff --git a/LibationWinForms/BookLiberation/BaseForms/LiberationBaseForm.cs b/LibationWinForms/BookLiberation/BaseForms/LiberationBaseForm.cs index 0b8eeef7..514db24a 100644 --- a/LibationWinForms/BookLiberation/BaseForms/LiberationBaseForm.cs +++ b/LibationWinForms/BookLiberation/BaseForms/LiberationBaseForm.cs @@ -30,7 +30,7 @@ namespace LibationWinForms.BookLiberation.BaseForms Subscribe(streamable); - if (Streamable is IProcessable processable) + if (Streamable is Processable processable) Subscribe(processable); if (Streamable is IAudioDecodable audioDecodable) Subscribe(audioDecodable); @@ -50,7 +50,7 @@ namespace LibationWinForms.BookLiberation.BaseForms Disposed += UnsubscribeStreamable; } - private void Subscribe(IProcessable processable) + private void Subscribe(Processable processable) { UnsubscribeProcessable(this, null); @@ -92,7 +92,7 @@ namespace LibationWinForms.BookLiberation.BaseForms } private void UnsubscribeProcessable(object sender, LibraryBook e) { - if (Streamable is not IProcessable processable) + if (Streamable is not Processable processable) return; processable.Completed -= UnsubscribeProcessable; @@ -148,11 +148,11 @@ namespace LibationWinForms.BookLiberation.BaseForms #region IProcessable event handlers public virtual void OnBegin(object sender, LibraryBook libraryBook) - => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(IProcessable.Begin), Book = libraryBook.LogFriendly() }); + => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(Processable.Begin), Book = libraryBook.LogFriendly() }); public virtual void OnStatusUpdate(object sender, string statusUpdate) - => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(IProcessable.StatusUpdate), Status = statusUpdate }); + => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(Processable.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() }); + => Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(Processable.Completed), Book = libraryBook.LogFriendly() }); #endregion diff --git a/LibationWinForms/BookLiberation/ProcessorAutomationController.cs b/LibationWinForms/BookLiberation/ProcessorAutomationController.cs index 7582d790..edab5e66 100644 --- a/LibationWinForms/BookLiberation/ProcessorAutomationController.cs +++ b/LibationWinForms/BookLiberation/ProcessorAutomationController.cs @@ -96,7 +96,7 @@ namespace LibationWinForms.BookLiberation await new BackupLoop(logMe, downloadPdf, automatedBackupsForm).RunBackupAsync(); } - private static IProcessable CreateBackupBook(LogMe logMe) + private static Processable CreateBackupBook(LogMe logMe) { var downloadPdf = CreateProcessable(logMe); @@ -132,16 +132,16 @@ namespace LibationWinForms.BookLiberation } /// - /// Create a new and links it to a new . + /// Create a new and links it to a new . /// - /// The derived type to create. - /// The derived Form to create on , Show on , Close on , and Dispose on + /// The derived type to create. + /// The derived Form to create on , Show on , Close on , and Dispose on /// The logger - /// An additional event handler to handle - /// A new of type + /// An additional event handler to handle + /// A new of type private static TProcessable CreateProcessable(LogMe logMe, EventHandler completedAction = null) where TForm : LiberationBaseForm, new() - where TProcessable : IProcessable, new() + where TProcessable : Processable, new() { var strProc = new TProcessable(); @@ -161,10 +161,10 @@ namespace LibationWinForms.BookLiberation internal abstract class BackupRunner { protected LogMe LogMe { get; } - protected IProcessable Processable { get; } + protected Processable Processable { get; } protected AutomatedBackupsForm AutomatedBackupsForm { get; } - protected BackupRunner(LogMe logMe, IProcessable processable, AutomatedBackupsForm automatedBackupsForm = null) + protected BackupRunner(LogMe logMe, Processable processable, AutomatedBackupsForm automatedBackupsForm = null) { LogMe = logMe; Processable = processable; @@ -278,7 +278,7 @@ An error occurred while trying to process this book. Skip this book permanently? protected override MessageBoxDefaultButton SkipDialogDefaultButton => MessageBoxDefaultButton.Button2; protected override DialogResult SkipResult => DialogResult.Yes; - public BackupSingle(LogMe logMe, IProcessable processable, LibraryBook libraryBook) + public BackupSingle(LogMe logMe, Processable processable, LibraryBook libraryBook) : base(logMe, processable) { _libraryBook = libraryBook; @@ -307,7 +307,7 @@ An error occurred while trying to process this book. protected override MessageBoxDefaultButton SkipDialogDefaultButton => MessageBoxDefaultButton.Button1; protected override DialogResult SkipResult => DialogResult.Ignore; - public BackupLoop(LogMe logMe, IProcessable processable, AutomatedBackupsForm automatedBackupsForm) + public BackupLoop(LogMe logMe, Processable processable, AutomatedBackupsForm automatedBackupsForm) : base(logMe, processable, automatedBackupsForm) { } protected override async Task RunAsync()