// TODO: Review this... public async Task <Response> GenerateAsync(Request request, CancellationToken cancellationToken = default) { AuthorSearchResults searchResults = null; // Attempt to download from the alternate site, if present. If it fails in some way, try .com // If the .com search crashes, it will crash back to the caller in frmMain try { searchResults = await _amazonClient.SearchAuthor(request.Book.Author, request.Book.Asin, request.Settings.AmazonTld, request.Settings.SaveHtml, cancellationToken); } catch (Exception ex) { _logger.Log("Error searching Amazon." + request.Settings.AmazonTld + ": " + ex.Message + "\r\n" + ex.StackTrace); } finally { if (searchResults == null) { _logger.Log(string.Format("Failed to find {0} on Amazon." + request.Settings.AmazonTld, request.Book.Author)); if (request.Settings.AmazonTld != "com") { _logger.Log("Trying again with Amazon.com."); request.Settings.AmazonTld = "com"; searchResults = await _amazonClient.SearchAuthor(request.Book.Author, request.Book.Asin, request.Settings.AmazonTld, request.Settings.SaveHtml, cancellationToken); } } } if (searchResults == null) { return(null); // Already logged error in search function } var authorAsin = searchResults.Asin; //todo re-implement saving in a nicer way // if (Properties.Settings.Default.saveHtml) // { // try // { // _logger.Log("Saving author's Amazon webpage..."); // File.WriteAllText(Environment.CurrentDirectory + string.Format(@"\dmp\{0}.authorpageHtml.txt", request.Book.Asin), // searchResults.AuthorHtmlDoc.DocumentNode.InnerHtml); // } // catch (Exception ex) // { // _logger.Log(string.Format("An error occurred saving authorpageHtml.txt: {0}", ex.Message)); // } // } // Try to find author's biography string ReadBio(string file) { try { var fileText = Functions.ReadFromFile(file); if (string.IsNullOrEmpty(fileText)) { _logger.Log("Found biography file, but it is empty!\r\n" + file); } else { _logger.Log("Using biography from " + file + "."); } return(fileText); } catch (Exception ex) { _logger.Log("An error occurred while opening " + file + "\r\n" + ex.Message + "\r\n" + ex.StackTrace); } return(null); } // TODO: Separate out biography stuff string biography = null; var bioFile = Environment.CurrentDirectory + @"\ext\" + authorAsin + ".bio"; var readFromFile = false; if (request.Settings.SaveBio && File.Exists(bioFile)) { biography = ReadBio(bioFile); // if it's null, there was an error. if it's just empty, we'll parse it out instead if (biography == null) { return(null); } if (biography == "") { readFromFile = true; } } if (string.IsNullOrEmpty(biography) && !string.IsNullOrEmpty(searchResults.Biography)) { //Trim authour biography to less than 1000 characters and/or replace more problematic characters. if (searchResults.Biography.Trim().Length > 0) { if (searchResults.Biography.Length > 1000) { var lastPunc = searchResults.Biography.LastIndexOfAny(new [] { '.', '!', '?' }); var lastSpace = searchResults.Biography.LastIndexOf(' '); if (lastPunc > lastSpace) { biography = searchResults.Biography.Substring(0, lastPunc + 1); } else { biography = searchResults.Biography.Substring(0, lastSpace) + '\u2026'; } } else { biography = searchResults.Biography; } biography = biography.Clean(); if (request.Settings.SaveBio) { File.WriteAllText(bioFile, biography); } _logger.Log("Author biography found on Amazon!"); } } var message = biography == null ? "No author biography found on Amazon!\r\nWould you like to create one?" : readFromFile ? "Would you like to edit the existing biography?" : "Author biography found on Amazon! Would you like to edit it?"; // TODO: No dialogs here if (request.Settings.EditBiography && System.Windows.Forms.DialogResult.Yes == System.Windows.Forms.MessageBox.Show( message, "Biography", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question, System.Windows.Forms.MessageBoxDefaultButton.Button2)) { if (!File.Exists(bioFile)) { File.WriteAllText(bioFile, string.Empty); } Functions.RunNotepad(bioFile); biography = ReadBio(bioFile); } if (string.IsNullOrEmpty(biography)) { biography = "No author biography found locally or on Amazon!"; _logger.Log("An error occurred finding the author biography."); } if (request.Settings.SaveBio) { if (!File.Exists(bioFile)) { try { _logger.Log("Saving biography to " + bioFile); using var streamWriter = new StreamWriter(bioFile, false, System.Text.Encoding.UTF8); await streamWriter.WriteAsync(biography); } catch (Exception ex) { _logger.Log("An error occurred while writing biography.\r\n" + ex.Message + "\r\n" + ex.StackTrace); return(null); } } if (System.Windows.Forms.DialogResult.Yes == System.Windows.Forms.MessageBox.Show("Would you like to open the biography file in notepad for editing?", "Biography", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question, System.Windows.Forms.MessageBoxDefaultButton.Button2)) { Functions.RunNotepad(bioFile); biography = ReadBio(bioFile); if (string.IsNullOrEmpty(biography)) { return(null); } } } // Try to download Author image request.Book.AuthorImageUrl = searchResults.ImageUrl; Bitmap ApAuthorImage = null; try { _logger.Log("Downloading author image..."); ApAuthorImage = await _httpClient.GetImageAsync(request.Book.AuthorImageUrl, cancellationToken : cancellationToken); _logger.Log("Grayscale base64-encoded author image created!"); } catch (Exception ex) { _logger.Log(string.Format("An error occurred downloading the author image: {0}", ex.Message)); } var bookBag = new ConcurrentBag <BookInfo>(); if (searchResults.Books != null && request.Settings.UseNewVersion) { _logger.Log("Gathering metadata for author's other books..."); try { await foreach (var book in _amazonClient.EnhanceBookInfos(searchResults.Books, cancellationToken)) { // todo progress bookBag.Add(book); } } catch (Exception ex) { _logger.Log($"An error occurred gathering metadata for other books: {ex.Message}"); throw; } } else { _logger.Log("Unable to find other books by this author. If there should be some, check the Amazon URL to ensure it is correct."); } _logger.Log("Writing Author Profile to file..."); return(new Response { Asin = authorAsin, Name = request.Book.Author, OtherBooks = bookBag.ToArray(), Biography = biography, Image = ApAuthorImage, ImageUrl = searchResults.ImageUrl }); }
// TODO: Review this... public async Task <Response> GenerateAsync(Request request, Func <string, bool> editBioCallback, IProgressBar progress = null, CancellationToken cancellationToken = default) { AuthorSearchResults searchResults = null; // Attempt to download from the alternate site, if present. If it fails in some way, try .com // If the .com search crashes, it will crash back to the caller in frmMain try { searchResults = await _amazonClient.SearchAuthor(request.Book.Author, request.Settings.AmazonTld, cancellationToken); } catch (Exception ex) { _logger.Log($"Error searching Amazon.{request.Settings.AmazonTld}: {ex.Message}\r\n{ex.StackTrace}"); } finally { if (searchResults == null) { _logger.Log($"Failed to find {request.Book.Author} on Amazon.{request.Settings.AmazonTld}"); if (request.Settings.AmazonTld != "com") { _logger.Log("Trying again with Amazon.com."); request.Settings.AmazonTld = "com"; searchResults = await _amazonClient.SearchAuthor(request.Book.Author, request.Settings.AmazonTld, cancellationToken); } } } if (searchResults == null) { return(null); // Already logged error in search function } // Filter out any results that are the same title but not the same asin searchResults.Books = searchResults.Books .Where(book => !book.Title.ToLower().Contains(request.Book.Title.ToLower()) && book.Asin != request.Book.Asin) .ToArray(); var authorAsin = searchResults.Asin; //todo re-implement saving in a nicer way // if (Properties.Settings.Default.saveHtml) // { // try // { // _logger.Log("Saving author's Amazon webpage..."); // File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + string.Format(@"\dmp\{0}.authorpageHtml.txt", request.Book.Asin), // searchResults.AuthorHtmlDoc.DocumentNode.InnerHtml); // } // catch (Exception ex) // { // _logger.Log(string.Format("An error occurred saving authorpageHtml.txt: {0}", ex.Message)); // } // } // TODO: Separate out biography stuff // Try to find author's biography string biography = null; var bioFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ext", $"{authorAsin}.bio"); var readFromFile = false; var newBioFile = false; string ReadBio(string file) { try { var fileText = Functions.ReadFromFile(file); if (string.IsNullOrEmpty(fileText)) { _logger.Log($"Found biography file, but it is empty!\r\n{file}"); } else if (!string.Equals(biography, fileText)) { _logger.Log($"Using biography from {file}."); } // todo fix this if (fileText != null && fileText.Contains("No author biography found locally or on Amazon!")) { _logger.Log($"Warning: Local biography file contains an empty default biography.{Environment.NewLine}Delete {file} and try again"); } return(fileText); } catch (Exception ex) { _logger.Log($"An error occurred while opening {file}\r\n{ex.Message}\r\n{ex.StackTrace}"); } return(null); } string TrimBio(string bio) { try { //Trim author biography to less than 1000 characters and/or replace more problematic characters. if (string.IsNullOrWhiteSpace(bio)) { return(null); } if (bio.Length > 1000) { // todo culture invariant var lastPunc = bio.LastIndexOfAny(new[] { '.', '!', '?' }); var lastSpace = bio.LastIndexOf(' '); bio = lastPunc > lastSpace ? bio.Substring(0, lastPunc + 1) : $"{bio.Substring(0, lastSpace)}{'\u2026'}"; } return(bio.Clean()); } catch (Exception ex) { _logger.Log($"An error occurred while trimming the biography\r\n{ex.Message}\r\n{ex.StackTrace}"); } return(bio); } if (searchResults.Biography == null && !File.Exists(bioFile)) { if (request.Settings.AmazonTld != "com") { _logger.Log(@"Searching for biography on Amazon.com…"); request.Settings.AmazonTld = "com"; var tempSearchResults = await _amazonClient.SearchAuthor(request.Book.Author, request.Settings.AmazonTld, cancellationToken, false); if (tempSearchResults?.Biography != null) { searchResults.Biography = tempSearchResults.Biography; } } } if (searchResults.Biography != null) { biography = searchResults.Biography; } if (File.Exists(bioFile) && request.Settings.SaveBio) { biography = ReadBio(bioFile); // if it's null, there was an error. if it's just empty, we'll parse it out instead if (biography == null) { return(null); } if (!string.IsNullOrEmpty(biography)) { readFromFile = true; } } if (!string.IsNullOrEmpty(biography) && (request.Settings.SaveBio || request.Settings.EditBiography)) { if (!readFromFile) { biography = TrimBio(biography); } if (!File.Exists(bioFile) && !string.IsNullOrEmpty(biography)) { File.WriteAllText(bioFile, biography); newBioFile = true; _logger.Log(@"Author biography found!"); } } var message = biography == null ? $"No author biography found on Amazon!{Environment.NewLine}Would you like to create one?" : readFromFile ? "Would you like to edit the existing biography?" : $"Author biography found on Amazon!{Environment.NewLine}Would you like to edit it?"; if (editBioCallback != null && editBioCallback(message)) { if (!File.Exists(bioFile)) { File.WriteAllText(bioFile, string.Empty); } Functions.RunNotepad(bioFile); biography = ReadBio(bioFile); } if (string.IsNullOrEmpty(biography)) { biography = "No author biography found locally or on Amazon!"; _logger.Log("An error occurred finding the author biography."); } if (request.Settings.SaveBio) { if (!File.Exists(bioFile)) { try { _logger.Log($"Saving biography to {bioFile}"); using var streamWriter = new StreamWriter(bioFile, false, System.Text.Encoding.UTF8); await streamWriter.WriteAsync(biography); } catch (Exception ex) { _logger.Log($"An error occurred while writing biography.\r\n{ex.Message}\r\n{ex.StackTrace}"); return(null); } } if (newBioFile) { _logger.Log(@"New biography file opened in notepad for editing…"); Functions.RunNotepad(bioFile); biography = ReadBio(bioFile); if (string.IsNullOrEmpty(biography)) { return(null); } searchResults.Biography = biography; } if (!readFromFile && editBioCallback != null && editBioCallback("Would you like to open the biography file in notepad for editing?")) { Functions.RunNotepad(bioFile); biography = ReadBio(bioFile); if (string.IsNullOrEmpty(biography)) { return(null); } } } searchResults.Biography = biography; // Try to download Author image request.Book.AuthorImageUrl = searchResults.ImageUrl; Bitmap ApAuthorImage = null; try { _logger.Log("Downloading author image…"); ApAuthorImage = await _httpClient.GetImageAsync(request.Book.AuthorImageUrl, cancellationToken : cancellationToken); _logger.Log("Grayscale base64-encoded author image created!"); } catch (Exception ex) { _logger.Log($"An error occurred downloading the author image: {ex.Message}"); } var bookBag = new ConcurrentBag <BookInfo>(); if (searchResults.Books != null && request.Settings.UseNewVersion) { if (searchResults.Books.Length != 0) { // todo pluralize _logger.Log(searchResults.Books.Length > 1 ? $"Gathering metadata for {searchResults.Books.Length} other books by {request.Book.Author}…" : $"Gathering metadata for another book by {request.Book.Author}…"); } try { progress?.Set(0, searchResults.Books.Length); await _amazonClient .EnhanceBookInfos(searchResults.Books, cancellationToken) .ForEachAsync(book => { bookBag.Add(book); progress?.Add(1); }, cancellationToken); progress?.Set(0, 0); _logger.Log("Metadata gathering complete!"); } catch (Exception ex) { _logger.Log($"An error occurred gathering metadata for other books: {ex.Message}"); throw; } } else { _logger.Log($"Unable to find other books by {request.Book.Author}. If there should be some, check the Amazon URL to ensure it is correct."); } _logger.Log("Writing Author Profile to file…"); return(new Response { Asin = authorAsin, Name = request.Book.Author, OtherBooks = bookBag.ToArray(), Biography = biography, Image = ApAuthorImage, ImageUrl = searchResults.ImageUrl }); }