diff --git a/FileLiberator/DecryptBook.cs b/FileLiberator/DecryptBook.cs
deleted file mode 100644
index 47ef3e3a..00000000
--- a/FileLiberator/DecryptBook.cs
+++ /dev/null
@@ -1,207 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using AaxDecrypter;
-using AudibleApi;
-using DataLayer;
-using Dinah.Core;
-using Dinah.Core.ErrorHandling;
-using FileManager;
-using InternalUtilities;
-
-namespace FileLiberator
-{
- ///
- /// Decrypt audiobook files
- ///
- /// Processes:
- /// Download: download aax file: the DRM encrypted audiobook
- /// Decrypt: remove DRM encryption from audiobook. Store final book
- /// Backup: perform all steps (downloaded, decrypt) still needed to get final book
- ///
- public class DecryptBook : IDecryptable
- {
- public event EventHandler Begin;
- public event EventHandler StatusUpdate;
- public event EventHandler DecryptBegin;
-
- public event EventHandler TitleDiscovered;
- public event EventHandler AuthorsDiscovered;
- public event EventHandler NarratorsDiscovered;
- public event EventHandler CoverImageFilepathDiscovered;
- public event EventHandler UpdateProgress;
-
- public event EventHandler DecryptCompleted;
- public event EventHandler Completed;
-
- public bool Validate(LibraryBook libraryBook)
- => AudibleFileStorage.AAX.Exists(libraryBook.Book.AudibleProductId)
- && !AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId);
-
- // do NOT use ConfigureAwait(false) on ProcessAsync()
- // often calls events which prints to forms in the UI context
- public async Task ProcessAsync(LibraryBook libraryBook)
- {
- Begin?.Invoke(this, libraryBook);
-
- try
- {
- var aaxFilename = AudibleFileStorage.AAX.GetPath(libraryBook.Book.AudibleProductId);
-
- if (aaxFilename == null)
- return new StatusHandler { "aaxFilename parameter is null" };
- if (!File.Exists(aaxFilename))
- return new StatusHandler { $"Cannot find AAX file: {aaxFilename}" };
- if (AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId))
- return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
-
- var chapters = await downloadChapterNamesAsync(libraryBook);
-
- var outputAudioFilename = await aaxToM4bConverterDecryptAsync(aaxFilename, libraryBook, chapters);
-
- // decrypt failed
- if (outputAudioFilename == null)
- return new StatusHandler { "Decrypt failed" };
-
- // moves files and returns dest dir. Do not put inside of if(RetainAaxFiles)
- var destinationDir = moveFilesToBooksDir(libraryBook.Book, outputAudioFilename);
-
- var jsonFilename = PathLib.ReplaceExtension(aaxFilename, "json");
- if (Configuration.Instance.RetainAaxFiles)
- {
- var newAaxFilename = FileUtility.GetValidFilename(
- destinationDir,
- Path.GetFileNameWithoutExtension(aaxFilename),
- "aax");
- File.Move(aaxFilename, newAaxFilename);
-
- var newJsonFilename = PathLib.ReplaceExtension(newAaxFilename, "json");
- File.Move(jsonFilename, newJsonFilename);
- }
- else
- {
- Dinah.Core.IO.FileExt.SafeDelete(aaxFilename);
- Dinah.Core.IO.FileExt.SafeDelete(jsonFilename);
- }
-
- var finalAudioExists = AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId);
- if (!finalAudioExists)
- return new StatusHandler { "Cannot find final audio file after decryption" };
-
- return new StatusHandler();
- }
- finally
- {
- Completed?.Invoke(this, libraryBook);
- }
- }
-
- private static async Task downloadChapterNamesAsync(LibraryBook libraryBook)
- {
- try
- {
- var api = await AudibleApiActions.GetApiAsync(libraryBook.Account, libraryBook.Book.Locale);
- var contentMetadata = await api.GetLibraryBookMetadataAsync(libraryBook.Book.AudibleProductId);
- if (contentMetadata?.ChapterInfo is null)
- return null;
-
- return new DownloadedChapters(contentMetadata.ChapterInfo);
- }
- catch
- {
- return null;
- }
- }
-
- private async Task aaxToM4bConverterDecryptAsync(string aaxFilename, LibraryBook libraryBook, Chapters chapters)
- {
- DecryptBegin?.Invoke(this, $"Begin decrypting {aaxFilename}");
-
- try
- {
- var jsonPath = PathLib.ReplaceExtension(aaxFilename, "json");
- var jsonContents = File.ReadAllText(jsonPath);
- var dlLic = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonContents);
-
- var converter = await AaxToM4bConverter.CreateAsync(aaxFilename, dlLic.AudibleKey, dlLic.AudibleIV, chapters);
- converter.AppName = "Libation";
-
- TitleDiscovered?.Invoke(this, converter.tags.title);
- AuthorsDiscovered?.Invoke(this, converter.tags.author);
- NarratorsDiscovered?.Invoke(this, converter.tags.narrator);
- CoverImageFilepathDiscovered?.Invoke(this, converter.coverBytes);
-
- // override default which was set in CreateAsync
- var proposedOutputFile = Path.Combine(AudibleFileStorage.DecryptInProgress, $"[{libraryBook.Book.AudibleProductId}].m4b");
- converter.SetOutputFilename(proposedOutputFile);
- converter.DecryptProgressUpdate += (s, progress) => UpdateProgress?.Invoke(this, progress);
-
- // REAL WORK DONE HERE
- var success = await Task.Run(() => converter.Run());
-
- // decrypt failed
- if (!success)
- return null;
-
- return converter.outputFileName;
- }
- finally
- {
- DecryptCompleted?.Invoke(this, $"Completed decrypting {aaxFilename}");
- }
- }
-
- private static string moveFilesToBooksDir(Book product, string outputAudioFilename)
- {
- // create final directory. move each file into it. MOVE AUDIO FILE LAST
- // new dir: safetitle_limit50char + " [" + productId + "]"
-
- var destinationDir = AudibleFileStorage.Audio.GetDestDir(product.Title, product.AudibleProductId);
- Directory.CreateDirectory(destinationDir);
-
- var sortedFiles = getProductFilesSorted(product, outputAudioFilename);
-
- var musicFileExt = Path.GetExtension(outputAudioFilename).Trim('.');
-
- // audio filename: safetitle_limit50char + " [" + productId + "]." + audio_ext
- var audioFileName = FileUtility.GetValidFilename(destinationDir, product.Title, musicFileExt, product.AudibleProductId);
-
- foreach (var f in sortedFiles)
- {
- var dest
- = AudibleFileStorage.Audio.IsFileTypeMatch(f)
- ? audioFileName
- // non-audio filename: safetitle_limit50char + " [" + productId + "][" + audio_ext +"]." + non_audio_ext
- : FileUtility.GetValidFilename(destinationDir, product.Title, f.Extension, product.AudibleProductId, musicFileExt);
-
- if (Path.GetExtension(dest).Trim('.').ToLower() == "cue")
- Cue.UpdateFileName(f, audioFileName);
-
- File.Move(f.FullName, dest);
- }
-
- return destinationDir;
- }
-
- private static List getProductFilesSorted(Book product, string outputAudioFilename)
- {
- // files are: temp path\author\[asin].ext
- var m4bDir = new FileInfo(outputAudioFilename).Directory;
- var files = m4bDir
- .EnumerateFiles()
- .Where(f => f.Name.ContainsInsensitive(product.AudibleProductId))
- .ToList();
-
- // move audio files to the end of the collection so these files are moved last
- var musicFiles = files.Where(f => AudibleFileStorage.Audio.IsFileTypeMatch(f));
- var sortedFiles = files
- .Except(musicFiles)
- .Concat(musicFiles)
- .ToList();
-
- return sortedFiles;
- }
- }
-}
diff --git a/FileLiberator/DownloadBook.cs b/FileLiberator/DownloadBook.cs
deleted file mode 100644
index c32aa51e..00000000
--- a/FileLiberator/DownloadBook.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using System;
-using System.IO;
-using System.Threading.Tasks;
-using AudibleApi;
-using DataLayer;
-using Dinah.Core;
-using Dinah.Core.ErrorHandling;
-using FileManager;
-using System.Net.Http;
-using Dinah.Core.Net.Http;
-
-namespace FileLiberator
-{
- ///
- /// Download DRM book
- ///
- /// Processes:
- /// Download: download aax file: the DRM encrypted audiobook
- /// Decrypt: remove DRM encryption from audiobook. Store final book
- /// Backup: perform all steps (downloaded, decrypt) still needed to get final book
- ///
- public class DownloadBook : DownloadableBase
- {
- private const string SERVICE_UNAVAILABLE = "Content Delivery Companion Service is not available.";
-
- public override bool Validate(LibraryBook libraryBook)
- => !AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId)
- && !AudibleFileStorage.AAX.Exists(libraryBook.Book.AudibleProductId);
-
- public override async Task ProcessItemAsync(LibraryBook libraryBook)
- {
- var tempAaxFilename = getDownloadPath(libraryBook);
- var actualFilePath = await downloadAaxcBookAsync(libraryBook, tempAaxFilename);
- moveBook(libraryBook, actualFilePath);
- return verifyDownload(libraryBook);
- }
-
- private static string getDownloadPath(LibraryBook libraryBook)
- => FileUtility.GetValidFilename(
- AudibleFileStorage.DownloadsInProgress,
- libraryBook.Book.Title,
- "aaxc",
- libraryBook.Book.AudibleProductId);
-
- private async Task downloadAaxcBookAsync(LibraryBook libraryBook, string tempAaxFilename)
- {
- validate(libraryBook);
-
- var api = await GetApiAsync(libraryBook);
-
- var dlLic = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId);
-
- var client = new HttpClient();
- client.DefaultRequestHeaders.Add("User-Agent", Resources.UserAgent);
-
- var actualFilePath = await PerformDownloadAsync(
- tempAaxFilename,
- (p) => client.DownloadFileAsync(new Uri(dlLic.DownloadUrl).AbsoluteUri, tempAaxFilename, p));
-
- System.Threading.Thread.Sleep(100);
- // if bad file download, a 0-33 byte file will be created
- // if service unavailable, a 52 byte string will be saved as file
- var length = new FileInfo(actualFilePath).Length;
-
- // success. save json and return
- if (length > 100)
- {
- // save along side book
- var jsonPath = PathLib.ReplaceExtension(actualFilePath, "json");
- var jsonContents = Newtonsoft.Json.JsonConvert.SerializeObject(dlLic, Newtonsoft.Json.Formatting.Indented);
- File.WriteAllText(jsonPath, jsonContents);
-
- return actualFilePath;
- }
-
- // else: failure. clean up and throw
- var contents = File.ReadAllText(actualFilePath);
- File.Delete(actualFilePath);
-
- var exMsg = contents.StartsWithInsensitive(SERVICE_UNAVAILABLE)
- ? SERVICE_UNAVAILABLE
- : "Error downloading file";
-
- var ex = new Exception(exMsg);
- Serilog.Log.Logger.Error(ex, "Download error {@DebugInfo}", new
- {
- libraryBook.Book.Title,
- libraryBook.Book.AudibleProductId,
- libraryBook.Book.Locale,
- Account = libraryBook.Account?.ToMask() ?? "[empty]",
- tempAaxFilename,
- actualFilePath,
- length,
- contents
- });
- throw ex;
- }
-
- private static void validate(LibraryBook libraryBook)
- {
- string errorString(string field)
- => $"{errorTitle()}\r\nCannot download book. {field} is not known. Try re-importing the account which owns this book.";
-
- string errorTitle()
- {
- var title
- = (libraryBook.Book.Title.Length > 53)
- ? $"{libraryBook.Book.Title.Truncate(50)}..."
- : libraryBook.Book.Title;
- var errorBookTitle = $"{title} [{libraryBook.Book.AudibleProductId}]";
- return errorBookTitle;
- };
-
- if (string.IsNullOrWhiteSpace(libraryBook.Account))
- throw new Exception(errorString("Account"));
-
- if (string.IsNullOrWhiteSpace(libraryBook.Book.Locale))
- throw new Exception(errorString("Locale"));
- }
-
- private void moveBook(LibraryBook libraryBook, string actualFilePath)
- {
- var newAaxFilename = FileUtility.GetValidFilename(
- AudibleFileStorage.DownloadsFinal,
- libraryBook.Book.Title,
- "aax",
- libraryBook.Book.AudibleProductId);
- File.Move(actualFilePath, newAaxFilename);
-
- // also move DownloadLicense json file
- var jsonPathOld = PathLib.ReplaceExtension(actualFilePath, "json");
- var jsonPathNew = PathLib.ReplaceExtension(newAaxFilename, "json");
- File.Move(jsonPathOld, jsonPathNew);
-
- Invoke_StatusUpdate($"Successfully downloaded. Moved to: {newAaxFilename}");
- }
-
- private static StatusHandler verifyDownload(LibraryBook libraryBook)
- => !AudibleFileStorage.AAX.Exists(libraryBook.Book.AudibleProductId)
- ? new StatusHandler { "Downloaded AAX file cannot be found" }
- : new StatusHandler();
- }
-}
diff --git a/FileLiberator/DownloadedChapters.cs b/FileLiberator/DownloadedChapters.cs
deleted file mode 100644
index 7a95f83c..00000000
--- a/FileLiberator/DownloadedChapters.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Globalization;
-using System.Linq;
-using System.Text;
-using AaxDecrypter;
-using AudibleApiDTOs;
-using Dinah.Core.Diagnostics;
-
-
-namespace FileLiberator
-{
- public class DownloadedChapters : Chapters
- {
- public DownloadedChapters(ChapterInfo chapterInfo)
- {
- AddChapters(chapterInfo.Chapters
- .Select(c => new AaxDecrypter.Chapter(c.StartOffsetMs / 1000d, (c.StartOffsetMs + c.LengthMs) / 1000d, c.Title)));
- }
- }
-}