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; using LibationFileManager; namespace FileLiberator { public abstract class Processable { public abstract string Name { get; } public event EventHandler Begin; /// General string message to display. DON'T rely on this for success, failure, or control logic public event EventHandler StatusUpdate; /// Fired when a file is successfully saved to disk public event EventHandler<(string id, string path)> FileCreated; public event EventHandler StreamingProgressChanged; public event EventHandler StreamingTimeRemaining; public event EventHandler Completed; /// True == Valid public abstract bool Validate(LibraryBook libraryBook); /// True == success public abstract Task ProcessAsync(LibraryBook libraryBook); // when used in foreach: stateful. deferred execution public IEnumerable GetValidLibraryBooks(IEnumerable library) => library.Where(libraryBook => Validate(libraryBook) && (!libraryBook.Book.IsEpisodeChild() || 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" }; GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); return status; } public async Task TryProcessAsync(LibraryBook libraryBook) => Validate(libraryBook) ? await ProcessAsync(libraryBook) : new StatusHandler(); protected void OnBegin(LibraryBook libraryBook) { Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(Begin), Book = libraryBook.LogFriendly() }); Begin?.Invoke(this, libraryBook); } protected void OnStatusUpdate(string statusUpdate) { Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(StatusUpdate), Status = statusUpdate }); StatusUpdate?.Invoke(this, statusUpdate); } protected void OnFileCreated(LibraryBook libraryBook, string path) { Serilog.Log.Logger.Information("File created {@DebugInfo}", new { Name = nameof(FileCreated), libraryBook.Book.AudibleProductId, path }); FilePathCache.Insert(libraryBook.Book.AudibleProductId, path); FileCreated?.Invoke(this, (libraryBook.Book.AudibleProductId, path)); } protected void OnStreamingProgressChanged(DownloadProgress progress) => OnStreamingProgressChanged(null, progress); protected void OnStreamingProgressChanged(object _, DownloadProgress progress) => StreamingProgressChanged?.Invoke(this, progress); protected void OnStreamingTimeRemaining(TimeSpan timeRemaining) => OnStreamingTimeRemaining(null, timeRemaining); protected void OnStreamingTimeRemaining(object _, TimeSpan timeRemaining) => StreamingTimeRemaining?.Invoke(this, timeRemaining); protected void OnCompleted(LibraryBook libraryBook) { Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(Completed), Book = libraryBook.LogFriendly() }); Completed?.Invoke(this, libraryBook); } } }