message: Move reading time to a service.

diff:
using XRayBuilder.Core.Libraries.Language.Localization;
using XRayBuilder.Core.Libraries.SimpleInjector.Extensions;
using XRayBuilder.Core.Logic;
+using XRayBuilder.Core.Logic.ReadingTime;
namespace XRayBuilder.Core.Bootstrap
{
@@ -17,6 +18,7 @@ public void Register(Container container)
{
container.RegisterSingleton<IDirectoryService, DirectoryService>();
container.RegisterSingleton<IBookSearchService, BookSearchService>();
+ container.RegisterSingleton<IReadingTimeService, ReadingTimeService>();
container.AutoregisterConcreteCollectionFromInterface<ILanguage>(Lifestyle.Singleton);
}
}

diff:
using XRayBuilder.Core.Libraries.Logging;
using XRayBuilder.Core.Libraries.Primitives.Extensions;
using XRayBuilder.Core.Libraries.Progress;
+using XRayBuilder.Core.Logic.ReadingTime;
using XRayBuilder.Core.Model;
using XRayBuilder.Core.Unpack;
using XRayBuilder.Core.XRay.Artifacts;
@@ -30,6 +31,7 @@ public sealed class SecondarySourceGoodreads : SecondarySource
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
private readonly IAmazonClient _amazonClient;
+ private readonly IReadingTimeService _readingTimeService;
private const int MaxConcurrentRequests = 10;
@@ -42,11 +44,12 @@ public sealed class SecondarySourceGoodreads : SecondarySource
private readonly Regex _regexEditions = new(@"editions/(?<editions>[0-9]*)-", RegexOptions.Compiled);
private readonly Regex _regexPages = new(@"(?<pages>(\d+)|(\d+,\d+)) pages", RegexOptions.Compiled);
- public SecondarySourceGoodreads(ILogger logger, IHttpClient httpClient, IAmazonClient amazonClient)
+ public SecondarySourceGoodreads(ILogger logger, IHttpClient httpClient, IAmazonClient amazonClient, IReadingTimeService readingTimeService)
{
_logger = logger;
_httpClient = httpClient;
_amazonClient = amazonClient;
+ _readingTimeService = readingTimeService;
}
private string ParseBookIdFromUrl(string input) => _regexBookId.Match(input).Groups["id"].Value;
@@ -248,15 +251,14 @@ public override async Task<bool> GetPageCountAsync(BookInfo curBook, Cancellatio
if (!match.Success)
return false;
- var minutes = int.Parse(match.Groups[1].Value, NumberStyles.AllowThousands) * 1.098507462686567;
- var span = TimeSpan.FromMinutes(minutes);
- var p = match.Groups["pages"].Value;
+ var pages = int.Parse(match.Groups["pages"].Value, NumberStyles.AllowThousands);
+ var readingTime = _readingTimeService.GetReadingTime(pages);
- curBook.PageCount = int.Parse(p, NumberStyles.AllowThousands, CultureInfo.InvariantCulture);
- curBook.ReadingHours = span.Hours;
- curBook.ReadingMinutes = span.Minutes;
+ curBook.PageCount = pages;
+ curBook.ReadingHours = readingTime.Hours;
+ curBook.ReadingMinutes = readingTime.Minutes;
- _logger.Log(Functions.GetReadingTime(curBook));
+ _logger.Log(_readingTimeService.GetFormattedReadingTime(pages));
return true;
}

diff:
using XRayBuilder.Core.Libraries.Http;
using XRayBuilder.Core.Libraries.Logging;
using XRayBuilder.Core.Libraries.Progress;
+using XRayBuilder.Core.Logic.ReadingTime;
using XRayBuilder.Core.Model;
using XRayBuilder.Core.Unpack;
using XRayBuilder.Core.XRay.Artifacts;
@@ -23,13 +24,15 @@ public sealed class SecondarySourceShelfari : SecondarySource
{
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
+ private readonly IReadingTimeService _readingTimeService;
private HtmlDocument sourceHtmlDoc;
- public SecondarySourceShelfari(ILogger logger, IHttpClient httpClient)
+ public SecondarySourceShelfari(ILogger logger, IHttpClient httpClient, IReadingTimeService readingTimeService)
{
_logger = logger;
_httpClient = httpClient;
+ _readingTimeService = readingTimeService;
}
public override string Name => "Shelfari";
@@ -106,12 +109,14 @@ public override async Task<bool> GetPageCountAsync(BookInfo curBook, Cancellatio
var match1 = Regex.Match(node1.InnerText, @"Page Count: ((\d+)|(\d+,\d+))");
if (match1.Success)
{
- var minutes = int.Parse(match1.Groups[1].Value, NumberStyles.AllowThousands) * 1.098507462686567;
- var span = TimeSpan.FromMinutes(minutes);
- curBook.PageCount = int.Parse(match1.Groups[1].Value);
- curBook.ReadingHours = span.Hours;
- curBook.ReadingMinutes = span.Minutes;
- _logger.Log(Functions.GetReadingTime(curBook));
+ var pages = int.Parse(match1.Groups[1].Value, NumberStyles.AllowThousands);
+ var readingTime = _readingTimeService.GetReadingTime(pages);
+
+ curBook.PageCount = pages;
+ curBook.ReadingHours = readingTime.Hours;
+ curBook.ReadingMinutes = readingTime.Minutes;
+
+ _logger.Log(_readingTimeService.GetFormattedReadingTime(pages));
return true;
}
return false;

diff:
using HtmlAgilityPack;
using JetBrains.Annotations;
using XRayBuilder.Core.Libraries.Language.Pluralization;
+using XRayBuilder.Core.Logic.ReadingTime;
using XRayBuilder.Core.Model;
namespace XRayBuilder.Core.Libraries
{
public static class Functions
{
+ private static readonly IReadingTimeService _readingTimeService;
+
public static string ReadFromFile(string file)
{
using var streamReader = new StreamReader(file, Encoding.UTF8);
@@ -76,26 +79,6 @@ public static string TimeStamp()
return $"Running X-Ray Builder GUI v{version}. Log started on {date} at {time}.\r\n";
}
- public static string GetReadingTime(BookInfo bookInfo)
- {
- if (bookInfo.PageCount == 0)
- return null;
-
- var minutes = bookInfo.PageCount * 1.098507462686567;
- var span = TimeSpan.FromMinutes(minutes);
-
- var d = PluralUtil.Pluralize($"{span.Days:day}");
- var h = PluralUtil.Pluralize($"{span.Hours:hour}");
- var m = PluralUtil.Pluralize($"{span.Minutes:minute}");
- var p = bookInfo.PageCount;
-
- bookInfo.ReadingHours = span.Hours;
- bookInfo.ReadingMinutes = span.Minutes;
-
- var formatted = $"Typical time to read: {(span.Days > 1 ? $"{d}, " : string.Empty)}{(span.Hours > 1 ? $"{h}" : string.Empty)}{(span.Hours > 1 ? " and " : ", ")}{(span.Minutes > 1 ? $"{m}" : string.Empty)} ({p} pages)";
- return formatted.StartsWith(", ") ? formatted.Substring(2, formatted.Length) : formatted;
- }
-
public static string GetPageCount(string rawMl, BookInfo bookInfo)
{
string output;
@@ -129,12 +112,14 @@ public static string GetPageCount(string rawMl, BookInfo bookInfo)
output = "An error occurred while estimating page count!";
return output;
}
- var minutes = pageCount * 1.098507462686567;
- var span = TimeSpan.FromMinutes(minutes);
+
+ var readingTime = _readingTimeService.GetReadingTime(pageCount);
+
bookInfo.PageCount = pageCount;
- bookInfo.ReadingHours = span.Hours;
- bookInfo.ReadingMinutes = span.Minutes;
- return GetReadingTime(bookInfo);
+ bookInfo.ReadingHours = readingTime.Hours;
+ bookInfo.ReadingMinutes = readingTime.Minutes;
+
+ return _readingTimeService.GetFormattedReadingTime(pageCount);
}
public static void RunNotepad(string filename)

original_message: Move reading time to a service.


message: Update current book reading hours and minutes when using ReadingTimeService.

change_type: MODIFY

diff:
@@ -389,7 +389,12 @@ async Task<BookInfo> FromApOrSearch(BookInfo book, CancellationToken ct)
{
var readingTimeFromPageCount = "";
if (curBook.PageCount != 0)
+ {
+ var readingTime = _readingTimeService.GetReadingTime(curBook.PageCount);
+ curBook.ReadingHours = readingTime.Hours;
+ curBook.ReadingMinutes = readingTime.Minutes;
readingTimeFromPageCount = _readingTimeService.GetFormattedReadingTime(curBook.PageCount);
+ }
if (string.IsNullOrEmpty(readingTimeFromPageCount))
{

license: MIT License


message: Add setting for Next in Series download.

diff:
@@ -12,7 +12,7 @@ namespace XRayBuilderGUI.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.7.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -502,5 +502,17 @@ internal sealed partial class Settings : global::System.Configuration.Applicatio
this["editDescription"] = value;
}
}
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool downloadNIS {
+ get {
+ return ((bool)(this["downloadNIS"]));
+ }
+ set {
+ this["downloadNIS"] = value;
+ }
+ }
}
}

diff:
<Setting Name="editDescription" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
- <Setting Name="Language" Type="System.String" Scope="User">
- <Value Profile="(Default)" />
+ <Setting Name="downloadNIS" Type="System.Boolean" Scope="User">
+ <Value Profile="(Default)">False</Value>
</Setting>
</Settings>
</SettingsFile>
\ No newline at end of file

diff:
<setting name="editDescription" serializeAs="String">
<value>True</value>
</setting>
- <setting name="Language" serializeAs="String">
- <value />
+ <setting name="downloadNIS" serializeAs="String">
+ <value>False</value>
</setting>
</XRayBuilderGUI.Properties.Settings>
</userSettings>

diff:
@@ -586,7 +586,8 @@ string AsinPrompt(string title, string author)
UseNewVersion = _settings.useNewVersion,
PromptAsin = _settings.promptASIN,
EstimatePageCount = _settings.pageCount,
- EditDescription = _settings.editDescription
+ EditDescription = _settings.editDescription,
+ DownloadNis = _settings.downloadNIS
};
var endActionsDataGenerator = _diContainer.GetInstance<IEndActionsDataGenerator>();

diff:
@@ -105,6 +105,7 @@ private void InitializeComponent()
this.chkRoentgenStartActions = new System.Windows.Forms.CheckBox();
this.label4 = new System.Windows.Forms.Label();
this.listSettings = new System.Windows.Forms.ListBox();
+ this.chkRoentgenNextInSeries = new System.Windows.Forms.CheckBox();
this.tabSettings.SuspendLayout();
this.tabPgGeneral.SuspendLayout();
this.cbLanguage.SuspendLayout();
@@ -827,7 +828,7 @@ private void InitializeComponent()
this.groupBox1.Controls.Add(this.pictureBox3);
this.groupBox1.Controls.Add(this.label5);
this.groupBox1.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.groupBox1.Location = new System.Drawing.Point(6, 154);
+ this.groupBox1.Location = new System.Drawing.Point(6, 181);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(414, 58);
this.groupBox1.TabIndex = 41;
@@ -868,7 +869,7 @@ private void InitializeComponent()
//
this.groupBox9.Controls.Add(this.chkIncludeTopics);
this.groupBox9.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.groupBox9.Location = new System.Drawing.Point(6, 103);
+ this.groupBox9.Location = new System.Drawing.Point(6, 130);
this.groupBox9.Name = "groupBox9";
this.groupBox9.Size = new System.Drawing.Size(414, 48);
this.groupBox9.TabIndex = 28;
@@ -888,6 +889,7 @@ private void InitializeComponent()
//
// groupBox7
//
+ this.groupBox7.Controls.Add(this.chkRoentgenNextInSeries);
this.groupBox7.Controls.Add(this.pictureBox2);
this.groupBox7.Controls.Add(this.label3);
this.groupBox7.Controls.Add(this.chkRoentgenAuthorProfile);
@@ -896,7 +898,7 @@ private void InitializeComponent()
this.groupBox7.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.groupBox7.Location = new System.Drawing.Point(6, 4);
this.groupBox7.Name = "groupBox7";
- this.groupBox7.Size = new System.Drawing.Size(414, 98);
+ this.groupBox7.Size = new System.Drawing.Size(414, 123);
this.groupBox7.TabIndex = 27;
this.groupBox7.TabStop = false;
this.groupBox7.Text = "Pre-built Downloads";
@@ -917,17 +919,17 @@ private void InitializeComponent()
this.label3.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(110)))), ((int)(((byte)(210)))));
this.label3.Location = new System.Drawing.Point(157, 17);
this.label3.Name = "label3";
- this.label3.Size = new System.Drawing.Size(242, 54);
+ this.label3.Size = new System.Drawing.Size(246, 42);
this.label3.TabIndex = 36;
this.label3.Text = "When these settings are enabled, pre-built versions of the files will be download" +
- "ed if they are available.\r\nRequires a valid ASIN.";
+ "ed if they are available. Requires a valid ASIN.";
//
// chkRoentgenAuthorProfile
//
this.chkRoentgenAuthorProfile.AutoSize = true;
this.chkRoentgenAuthorProfile.Enabled = false;
this.chkRoentgenAuthorProfile.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.chkRoentgenAuthorProfile.Location = new System.Drawing.Point(13, 69);
+ this.chkRoentgenAuthorProfile.Location = new System.Drawing.Point(13, 19);
this.chkRoentgenAuthorProfile.Name = "chkRoentgenAuthorProfile";
this.chkRoentgenAuthorProfile.Size = new System.Drawing.Size(100, 19);
this.chkRoentgenAuthorProfile.TabIndex = 33;
@@ -938,7 +940,7 @@ private void InitializeComponent()
//
this.chkRoentgenEndActions.AutoSize = true;
this.chkRoentgenEndActions.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.chkRoentgenEndActions.Location = new System.Drawing.Point(13, 19);
+ this.chkRoentgenEndActions.Location = new System.Drawing.Point(13, 44);
this.chkRoentgenEndActions.Name = "chkRoentgenEndActions";
this.chkRoentgenEndActions.Size = new System.Drawing.Size(89, 19);
this.chkRoentgenEndActions.TabIndex = 29;
@@ -949,7 +951,7 @@ private void InitializeComponent()
//
this.chkRoentgenStartActions.AutoSize = true;
this.chkRoentgenStartActions.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.chkRoentgenStartActions.Location = new System.Drawing.Point(13, 44);
+ this.chkRoentgenStartActions.Location = new System.Drawing.Point(13, 69);
this.chkRoentgenStartActions.Name = "chkRoentgenStartActions";
this.chkRoentgenStartActions.Size = new System.Drawing.Size(93, 19);
this.chkRoentgenStartActions.TabIndex = 24;
@@ -987,6 +989,17 @@ private void InitializeComponent()
this.listSettings.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(this.listSettings_MeasureItem);
this.listSettings.SelectedIndexChanged += new System.EventHandler(this.listSettings_SelectedIndexChanged);
//
+ // chkRoentgenNextInSeries
+ //
+ this.chkRoentgenNextInSeries.AutoSize = true;
+ this.chkRoentgenNextInSeries.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.chkRoentgenNextInSeries.Location = new System.Drawing.Point(13, 94);
+ this.chkRoentgenNextInSeries.Name = "chkRoentgenNextInSeries";
+ this.chkRoentgenNextInSeries.Size = new System.Drawing.Size(97, 19);
+ this.chkRoentgenNextInSeries.TabIndex = 38;
+ this.chkRoentgenNextInSeries.Text = "Next in Series";
+ this.chkRoentgenNextInSeries.UseVisualStyleBackColor = true;
+ //
// frmSettings
//
this.AcceptButton = this.btnSave;
@@ -1132,5 +1145,7 @@ private void InitializeComponent()
private System.Windows.Forms.CheckBox chkEditDescription;
#endregion
+
+ private System.Windows.Forms.CheckBox chkRoentgenNextInSeries;
}
}
\ No newline at end of file

diff:
@@ -90,6 +90,7 @@ private void frmSettingsNew_Load(object sender, EventArgs e)
chkRoentgenStartActions.Checked = Settings.Default.downloadSA;
chkRoentgenEndActions.Checked = Settings.Default.downloadEA;
chkRoentgenAuthorProfile.Checked = Settings.Default.downloadAP;
+ chkRoentgenNextInSeries.Checked = Settings.Default.downloadNIS;
chkIncludeTopics.Checked = Settings.Default.includeTopics;
@@ -205,6 +206,7 @@ private void btnSave_Click(object sender, EventArgs e)
Settings.Default.downloadSA = chkRoentgenStartActions.Checked;
Settings.Default.downloadEA = chkRoentgenEndActions.Checked;
Settings.Default.downloadAP = chkRoentgenAuthorProfile.Checked;
+ Settings.Default.downloadNIS = chkRoentgenNextInSeries.Checked;
Settings.Default.roentgenRegion = cmbRoentgenRegion.SelectedValue.ToString();
Settings.Default.includeTopics = chkIncludeTopics.Checked;
Settings.Default.Save();


message: Return response if page not found on Amazon.com.

diff:
@@ -246,7 +246,9 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
};
request.RequestUri = builder.Uri;
var response = await base.SendAsync(request, cancellationToken);
- Logger?.Log($"Not available from {originalHost}, but found on Amazon US ({request.RequestUri})");
+ if (response.StatusCode == HttpStatusCode.NotFound)
+ return response;
+ Logger?.Log($"Not available from {originalHost}, but found on Amazon.com ({request.RequestUri})");
return response;
}
}


message: Update Amazon page description parsing.

diff:
@@ -17,7 +17,7 @@ public class AmazonInfoParser : IAmazonInfoParser
private readonly IHttpClient _httpClient;
private readonly Regex _numbersRegex = new(@"(\d+|\d{1,3}([,\.]\d{3})*)(?=\s)", RegexOptions.Compiled);
- private readonly Regex _regex404 = new(@"(cs_404_logo|cs_404_link)", RegexOptions.Compiled);
+ private readonly Regex _regex404 = new(@"(cs_404_logo|cs_404_link|Page Not Found)", RegexOptions.Compiled);
public AmazonInfoParser(ILogger logger, IHttpClient httpClient)
{
@@ -111,22 +111,21 @@ public InfoResponse ParseAmazonDocument(HtmlDocument bookDoc)
?? bookDoc.DocumentNode.SelectSingleNode("//*[@class='a-size-medium series-detail-description-text']");
if (descNode != null && descNode.InnerText != "")
{
- var description = descNode.InnerText.Trim();
+ var description = descNode.InnerHtml.Clean();
// Following the example of Amazon, cut off desc around 1000 characters.
// If conveniently trimmed at the end of the sentence, let it end with the punctuation.
// If the sentence continues, cut it off and replace the space with an ellipsis
if (description.Length > 1000)
{
description = description.Substring(0, 1000);
- var lastPunc = description.LastIndexOfAny(new[] { '.', '!', '?' });
+ var lastPunc = description.LastIndexOfAny(new[] { '.', '!', '?', ',' });
var lastSpace = description.LastIndexOf(' ');
if (lastPunc > lastSpace)
description = description.Substring(0, lastPunc + 1);
else
description = $"{description.Substring(0, lastSpace)}{'\u2026'}";
}
- description = System.Net.WebUtility.HtmlDecode(description);
- response.Description = description.Clean();
+ response.Description = description;
}
#endregion

original_message: Update Amazon page description parsing.


message: Rework series parsing.

diff:
@@ -11,5 +11,6 @@ public interface IXRayBuilderConfig
/// When the old version is being built, enables excerpt shortening
/// </summary>
bool ShortenExcerptsLegacy { get; }
+ string AmazonTld { get; }
}
}
\ No newline at end of file

diff:
@@ -8,5 +8,6 @@ public sealed class XRayBuilderConfig : IXRayBuilderConfig
public bool UseSubdirectories { get; set; }
public bool UseNewVersion { get; set; }
public bool ShortenExcerptsLegacy { get; set; }
+ public string AmazonTld { get; set; }
}
}
\ No newline at end of file

diff:
using System.Threading.Tasks;
using Dasync.Collections;
using HtmlAgilityPack;
+using XRayBuilder.Core.Config;
using XRayBuilder.Core.DataSources.Amazon;
using XRayBuilder.Core.DataSources.Secondary.Model;
using XRayBuilder.Core.Libraries;
@@ -32,6 +33,7 @@ public sealed class SecondarySourceGoodreads : SecondarySource
private readonly IHttpClient _httpClient;
private readonly IAmazonClient _amazonClient;
private readonly IReadingTimeService _readingTimeService;
+ private readonly IXRayBuilderConfig _config;
private const int MaxConcurrentRequests = 10;
@@ -44,12 +46,13 @@ public sealed class SecondarySourceGoodreads : SecondarySource
private readonly Regex _regexEditions = new(@"editions/(?<editions>[0-9]*)-", RegexOptions.Compiled);
private readonly Regex _regexPages = new(@"(?<pages>(\d+)|(\d+,\d+)) pages", RegexOptions.Compiled);
- public SecondarySourceGoodreads(ILogger logger, IHttpClient httpClient, IAmazonClient amazonClient, IReadingTimeService readingTimeService)
+ public SecondarySourceGoodreads(ILogger logger, IHttpClient httpClient, IAmazonClient amazonClient, IReadingTimeService readingTimeService, IXRayBuilderConfig config)
{
_logger = logger;
_httpClient = httpClient;
_amazonClient = amazonClient;
_readingTimeService = readingTimeService;
+ _config = config;
}
private string ParseBookIdFromUrl(string input) => _regexBookId.Match(input).Groups["id"].Value;
@@ -167,6 +170,11 @@ public override async Task<SeriesInfo> GetSeriesInfoAsync(string dataUrl, Cancel
if (series.Total == 0)
return series;
+ _logger.Log((int) Convert.ToDouble(series.Position) == series.Total
+ ? $"This is the latest book in the {series.Name} series."
+ : $"This is book {series.Position} of {series.Total} in the {series.Name} series.");
+ _logger.Log($"Series URL: {series.Url}");
+
var positionInt = (int)Convert.ToDouble(series.Position, CultureInfo.InvariantCulture.NumberFormat);
var prevSearch = $"book {positionInt - 1}";
@@ -200,9 +208,17 @@ async Task<BookInfo> ParseSeriesBook(HtmlNode bookNode)
var bookIndexText = bookIndex.InnerText.ToLower();
if (bookIndexText == prevSearch && series.Previous == null)
+ {
series.Previous = await ParseSeriesBook(bookNode);
+ if(series.Previous != null)
+ _logger.Log($"Preceded by {series.Previous.Title}.");
+ }
else if (bookIndexText == nextSearch)
+ {
series.Next = await ParseSeriesBook(bookNode);
+ if(series.Next != null)
+ _logger.Log($"Followed by {series.Next.Title}.");
+ }
if (series.Previous != null && (series.Next != null || positionInt == series.Total))
break; // next and prev found or prev found and latest in series

diff:
@@ -10,5 +10,6 @@ public sealed class XRayBuilderConfig : IXRayBuilderConfig
public bool UseSubdirectories => Properties.Settings.Default.useSubDirectories;
public bool UseNewVersion => Properties.Settings.Default.useNewVersion;
public bool ShortenExcerptsLegacy => false;
+ public string AmazonTld => Properties.Settings.Default.amazonTLD;
}
}
\ No newline at end of file

original_message: Rework series parsing.


message: Minor change to height on Amazon region dropdown.

diff:
@@ -98,6 +98,7 @@ private void InitializeComponent()
this.groupBox9 = new System.Windows.Forms.GroupBox();
this.chkIncludeTopics = new System.Windows.Forms.CheckBox();
this.groupBox7 = new System.Windows.Forms.GroupBox();
+ this.chkRoentgenNextInSeries = new System.Windows.Forms.CheckBox();
this.pictureBox2 = new System.Windows.Forms.PictureBox();
this.label3 = new System.Windows.Forms.Label();
this.chkRoentgenAuthorProfile = new System.Windows.Forms.CheckBox();
@@ -105,7 +106,6 @@ private void InitializeComponent()
this.chkRoentgenStartActions = new System.Windows.Forms.CheckBox();
this.label4 = new System.Windows.Forms.Label();
this.listSettings = new System.Windows.Forms.ListBox();
- this.chkRoentgenNextInSeries = new System.Windows.Forms.CheckBox();
this.tabSettings.SuspendLayout();
this.tabPgGeneral.SuspendLayout();
this.cbLanguage.SuspendLayout();
@@ -790,7 +790,7 @@ private void InitializeComponent()
//
// cmbRegion
//
- this.cmbRegion.DropDownHeight = 162;
+ this.cmbRegion.DropDownHeight = 152;
this.cmbRegion.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmbRegion.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.cmbRegion.FormattingEnabled = true;
@@ -903,6 +903,17 @@ private void InitializeComponent()
this.groupBox7.TabStop = false;
this.groupBox7.Text = "Pre-built Downloads";
//
+ // chkRoentgenNextInSeries
+ //
+ this.chkRoentgenNextInSeries.AutoSize = true;
+ this.chkRoentgenNextInSeries.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.chkRoentgenNextInSeries.Location = new System.Drawing.Point(13, 94);
+ this.chkRoentgenNextInSeries.Name = "chkRoentgenNextInSeries";
+ this.chkRoentgenNextInSeries.Size = new System.Drawing.Size(97, 19);
+ this.chkRoentgenNextInSeries.TabIndex = 38;
+ this.chkRoentgenNextInSeries.Text = "Next in Series";
+ this.chkRoentgenNextInSeries.UseVisualStyleBackColor = true;
+ //
// pictureBox2
//
this.pictureBox2.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox2.Image")));
@@ -989,17 +1000,6 @@ private void InitializeComponent()
this.listSettings.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(this.listSettings_MeasureItem);
this.listSettings.SelectedIndexChanged += new System.EventHandler(this.listSettings_SelectedIndexChanged);
//
- // chkRoentgenNextInSeries
- //
- this.chkRoentgenNextInSeries.AutoSize = true;
- this.chkRoentgenNextInSeries.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.chkRoentgenNextInSeries.Location = new System.Drawing.Point(13, 94);
- this.chkRoentgenNextInSeries.Name = "chkRoentgenNextInSeries";
- this.chkRoentgenNextInSeries.Size = new System.Drawing.Size(97, 19);
- this.chkRoentgenNextInSeries.TabIndex = 38;
- this.chkRoentgenNextInSeries.Text = "Next in Series";
- this.chkRoentgenNextInSeries.UseVisualStyleBackColor = true;
- //
// frmSettings
//
this.AcceptButton = this.btnSave;


message: Fix for book position 1 in a series.

diff:
@@ -49,6 +49,12 @@ public async Task Populate(string inputFile, CancellationToken cancellationToken
if (startActions.Data.SeriesPosition != null)
{
var seriesInfo = startActions.Data.SeriesPosition;
+ if (seriesInfo.PositionInSeries == 1)
+ {
+ lblSeries.Text = $@"This is the first book in {seriesInfo.SeriesName}";
+ lblPreviousHeading.Visible = false;
+ lblPreviousTitle.Visible = false;
+ }
if (seriesInfo.PositionInSeries > 1)
{
lblSeries.Text = $@"This is book {seriesInfo.PositionInSeries} of {seriesInfo.TotalInSeries} in {seriesInfo.SeriesName}";

original_message: Fix for book position 1 in a series.


message: Default download next in series changed to enabled.

diff:
@@ -505,7 +505,7 @@ internal sealed partial class Settings : global::System.Configuration.Applicatio
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool downloadNIS {
get {
return ((bool)(this["downloadNIS"]));

diff:
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="downloadNIS" Type="System.Boolean" Scope="User">
- <Value Profile="(Default)">False</Value>
+ <Value Profile="(Default)">True</Value>
</Setting>
</Settings>
</SettingsFile>
\ No newline at end of file

diff:
<value>True</value>
</setting>
<setting name="downloadNIS" serializeAs="String">
- <value>False</value>
+ <value>True</value>
</setting>
</XRayBuilderGUI.Properties.Settings>
</userSettings>


message: More work on description parsing (WIP).

diff:
@@ -206,7 +206,7 @@ private string GetAuthorBiography(HtmlDocument authorHtmlDoc)
?? authorHtmlDoc.DocumentNode.SelectSingleNode("//span[@id='author_biography']")
?? throw new FormatChangedException(nameof(AmazonClient), "author bio");
- return bioNode.InnerText;
+ return bioNode.InnerHtml.Clean();
}
private string GetAuthorImageUrl(HtmlDocument authorHtmlDoc)

original_message: More work on description parsing (WIP).


message:
Fix Goodreads extras when Amazon rating is zero.
Add Goodreads author info parsing as a backup when Amazon has no picture or bio.

diff:
@@ -390,26 +390,59 @@ public override async Task GetExtrasAsync(BookInfo curBook, IProgressBar progres
//Add rating and reviews count if missing from Amazon book info
var metaNode = grDoc.DocumentNode.SelectSingleNode("//div[@id='bookMeta']");
- if (metaNode != null && !curBook.AmazonRating.HasValue)
+ if (metaNode != null && curBook.AmazonRating == 0)
{
- var goodreadsRating = metaNode.SelectSingleNode("//span[@class='value rating']")
+ var ratingNode = metaNode.SelectSingleNode("//span[@class='value rating']")
?? metaNode.SelectSingleNode(".//span[@itemprop='ratingValue']");
- if (goodreadsRating != null)
- curBook.AmazonRating = Math.Round(float.Parse(goodreadsRating.InnerText), 2);
- var passagesNode = metaNode.SelectSingleNode(".//a[@class='actionLinkLite votes' and @href='#other_reviews']")
- ?? metaNode.SelectSingleNode(".//span[@class='count value-title']");
- if (passagesNode != null)
- {
- var match = Regex.Match(passagesNode.InnerText, @"(\d+|\d{1,3}([,\.]\d{3})*)(?=\s)");
+ if (ratingNode != null)
+ curBook.AmazonRating = Math.Round(float.Parse(ratingNode.InnerText), 2);
+ var reviewsNode = metaNode.SelectSingleNode(".//a[@class='actionLinkLite votes' and @href='#other_reviews']")
+ ?? metaNode.SelectSingleNode(".//span[@class='count value-title']")
+ ?? metaNode.SelectSingleNode(".//a[@href='#other_reviews']");
+ if (reviewsNode != null)
+ {
+ var match = Regex.Match(reviewsNode.InnerText, @"(\d+|\d{1,3}([,\.]\d{3})*)(?=\s)");
if (match.Success)
curBook.Reviews = int.Parse(match.Value.Replace(",", "").Replace(".", ""));
}
- passagesNode = metaNode.SelectSingleNode(".//meta[@itemprop='reviewCount']");
- if (passagesNode != null && int.TryParse(passagesNode.GetAttributeValue("content", null), out var reviews))
+ reviewsNode = metaNode.SelectSingleNode(".//meta[@itemprop='reviewCount']");
+ if (reviewsNode != null && int.TryParse(reviewsNode.GetAttributeValue("content", null), out var reviews))
{
- curBook.Reviews = reviews;
+ curBook.Reviews += reviews;
+ }
}
}
+
+ /// <summary>
+ /// Scrape author info from Goodreads
+ /// </summary>
+ public async Task<AuthorSearchResults> GetAuthorAsync(string dataUrl, CancellationToken cancellationToken = default)
+ {
+ var result = new AuthorSearchResults();
+
+ var grDoc = await _httpClient.GetPageAsync(dataUrl, cancellationToken);
+
+ var aboutNode = grDoc.DocumentNode.SelectSingleNode("//div[@class='bookAuthorProfile']");
+ if (aboutNode != null)
+ {
+ var bioNodes = aboutNode.SelectNodes("//div[@class='bookAuthorProfile__about']/span/text()[normalize-space(.) != '']");
+ if (bioNodes != null)
+ {
+ var nodes = bioNodes.Select(n => HtmlEntity.DeEntitize(n.InnerText.Trim())).ToList();
+ result.Biography = string.Join(" ", nodes);
+ }
+
+ var imgNode = aboutNode.SelectSingleNode(".//div[@class='bookAuthorProfile__photo']");
+ if (imgNode != null)
+ {
+ var attr = imgNode.GetAttributeValue("style", "");
+ var match = Regex.Match(attr, @"background-image: url\((.*)\)");
+ if (match.Success)
+ result.ImageUrl = match.Groups[1].Value.Replace("p3/", "p8/");
+ }
+ }
+
+ return result;
}
}
}


diff:
@@ -66,11 +66,15 @@ public async Task<AuthorSearchResults> SearchAuthor(string author, string TLD, C
var plusAuthorName = newAuthor.Replace(" ", "+");
//Updated to match Search "all" Amazon
var amazonAuthorSearchUrl = $"https://www.amazon.{TLD}/s/ref=nb_sb_noss_2?url=search-alias%3Dstripbooks&field-keywords={plusAuthorName}";
+
+ // Changed to Amazon Kindle store only search
+ var amazonKindleAuthorSearchUrl = $"https://www.amazon.{TLD}/s?k={plusAuthorName}&i=digital-text&ref=nb_sb_noss";
+
if(enableLog)
_logger.Log($"Searching for author's page on Amazon.{TLD}...");
// Search Amazon for Author
- var authorSearchDoc = await _httpClient.GetPageAsync(amazonAuthorSearchUrl, cancellationToken);
+ var authorSearchDoc = await _httpClient.GetPageAsync(amazonKindleAuthorSearchUrl, cancellationToken);
// Check for captcha
try
@@ -240,7 +244,7 @@ private List<BookInfo> GetAuthorBooksNew(HtmlDocument authorHtmlDoc, string auth
continue;
var bookNodes = result.SelectNodes(".//div[@class='a-fixed-right-grid-inner']/div/div")
?? throw new FormatChangedException(nameof(AmazonClient), "book results - title nodes");
- var name = bookNodes.FirstOrDefault()?.SelectSingleNode("./a")?.InnerText.Trim()
+ var name = HtmlEntity.DeEntitize(bookNodes.FirstOrDefault()?.SelectSingleNode("./a")?.InnerText.Trim())
?? throw new FormatChangedException(nameof(AmazonClient), "book results - title");
// Skip known-bad things like lists and series and stuff
@@ -282,7 +286,7 @@ private List<BookInfo> GetAuthorBooks(HtmlDocument authorHtmlDoc, string author,
// Skip known-bad things like lists and series and stuff
if (_regexIgnoreHeaders.IsMatch(otherBook.InnerText))
continue;
- var name = otherBook.InnerText.Trim().ToTitleCase();
+ var name = HtmlEntity.DeEntitize(otherBook.InnerText.Trim().ToTitleCase());
otherBook = result.SelectSingleNode(".//*[@title='Kindle Edition']");
if (otherBook == null)
continue;

original_message: - Change author search URL to Kindle only.


message: Add bottomSheetEnabled and authorBiosBSE to End Actions and Start Actions.

diff:
@@ -18,6 +18,9 @@ public class EndActions
[JsonProperty("data")]
public DataJson Data { get; set; }
+ [JsonProperty("bottomSheetEnabled")]
+ public bool? BottomSheetEnabled { get; set; }
+
public class BookInformation
{
[JsonProperty("class")]
@@ -200,6 +203,9 @@ public class DataJson
[JsonProperty("customerProfile")]
public CustomerProfile CustomerProfile { get; set; }
+ [JsonProperty("authorBiosBSE")]
+ public AuthorBios AuthorBiosBSE { get; set; }
+
[JsonProperty("rating")]
public Rating Rating { get; set; }

diff:
@@ -17,6 +17,9 @@ public sealed class StartActions
[JsonProperty("data")]
public DataJson Data { get; set; }
+ [JsonProperty("bottomSheetEnabled")]
+ public bool? BottomSheetEnabled { get; set; }
+
public sealed class BookInformation
{
[JsonProperty("class")]
@@ -313,6 +316,9 @@ public sealed class DataJson
[JsonProperty("followSubscriptions")]
public AuthorSubscriptions FollowSubscriptions { get; set; }
+ [JsonProperty("authorBiosBSE")]
+ public AuthorBios AuthorBiosBSE { get; set; }
+
[JsonProperty("welcomeText")]
public WelcomeText WelcomeText { get; set; }

diff:
@@ -48,10 +48,11 @@ public string GenerateNew(Request request)
Asin = request.BookAsin,
ContentType = "EBOK",
Timestamp = Functions.UnixTimestampMilliseconds(),
- RefTagSuffix = "AAATAAB",
+ RefTagSuffix = "AAATAAA",
ImageUrl = request.BookImageUrl,
EmbeddedID = $"{request.BookDatabaseName}:{request.BookGuid}",
- Erl = request.BookErl
+ FictionStatus = "unknown",
+ Erl = -1 //request.BookErl
};
endActions.Data.FollowSubscriptions = new Extras.Artifacts.EndActions.AuthorSubscriptions
{
@@ -93,6 +94,19 @@ public string GenerateNew(Request request)
}
}
};
+ endActions.Data.AuthorBiosBSE = new AuthorBios
+ {
+ Authors = new[]
+ {
+ new Author
+ {
+ Asin = request.AuthorAsin,
+ Name = request.Author,
+ Bio = request.AuthorBiography,
+ ImageUrl = request.AuthorImageUrl
+ }
+ }
+ };
endActions.Data.AuthorRecs = new Recs
{
Class = "featuredRecommendationList",
@@ -110,6 +124,8 @@ public string GenerateNew(Request request)
.ToArray()
};
+ endActions.BottomSheetEnabled = false;
+
try
{
return Functions.ExpandUnicode(JsonConvert.SerializeObject(endActions));

diff:
@@ -45,6 +45,7 @@ public Artifacts.StartActions GenerateStartActions(BookInfo curBook, AuthorProfi
Timestamp = Functions.UnixTimestampMilliseconds(),
RefTagSuffix = "AAATAAA",
ImageUrl = curBook.ImageUrl,
+ FictionStatus = "unknown",
Erl = -1
};
if (!string.IsNullOrEmpty(curBook.Series?.Position))
@@ -102,6 +103,19 @@ public Artifacts.StartActions GenerateStartActions(BookInfo curBook, AuthorProfi
}
}
};
+ startActions.Data.AuthorBiosBSE = new AuthorBios
+ {
+ Authors = new[]
+ {
+ new Author
+ {
+ Asin = authorProfile.Asin,
+ Name = curBook.Author,
+ Bio = authorProfile.Biography,
+ ImageUrl = authorProfile.ImageUrl
+ }
+ }
+ };
startActions.Data.AuthorRecs = new Recs
{
Class = "recommendationList",
@@ -114,6 +128,8 @@ public Artifacts.StartActions GenerateStartActions(BookInfo curBook, AuthorProfi
startActions.Data.PreviousBookInTheSeries = Extensions.BookInfoToBook(curBook.Series?.Previous, true);
startActions.Data.ReadingPages.PagesInBook = curBook.PageCount;
+ startActions.BottomSheetEnabled = true;
+
return startActions;
}
}


message: Copy DownloadArtifactAsync to DownloadStartActionsAsync and DownloadNextInSeriesAsync as GetStringAsync was constantly failing (more investigation required).

diff:
@@ -54,18 +54,51 @@ public Task<StartActions> DownloadStartActionsAsync(string asin, string regionTl
{
return HandleDownloadExceptionsAsync(async () =>
{
- var response = await _httpClient.GetStringAsync($"{BaseUrl}{StartActionsEndpoint(asin)}", cancellationToken);
- return JsonUtil.Deserialize<StartActions>(response, false);
+ var requestContent = new StringContent(JsonUtil.Serialize(new DownloadRequest
+ {
+ Asin = asin,
+ RegionTld = regionTld
+ }), Encoding.UTF8, "application/json");
+ var request = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}{StartActionsEndpoint(asin)}")
+ {
+ Content = requestContent
+ };
+ var response = await _httpClient.SendAsync(request, cancellationToken);
+ var responseStream = await response.Content.ReadAsStreamAsync();
+ var responseString = await new StreamReader(responseStream, Encoding.UTF8).ReadToEndAsync();
+ return JsonUtil.Deserialize<StartActions>(responseString);
});
+
+ //return HandleDownloadExceptionsAsync(async () =>
+ //{
+ // var response = await _httpClient.GetStringAsync($"{BaseUrl}{StartActionsEndpoint(asin)}", cancellationToken);
+ // return JsonUtil.Deserialize<StartActions>(response, false);
+ //});
}
public Task<NextBookResult> DownloadNextInSeriesAsync(string asin, CancellationToken cancellationToken)
{
return HandleDownloadExceptionsAsync(async () =>
{
- var response = await _httpClient.GetStringAsync($"{BaseUrl}{SeriesEndpoint(asin)}", cancellationToken);
- return JsonConvert.DeserializeObject<NextBookResult>(response);
+ var requestContent = new StringContent(JsonUtil.Serialize(new DownloadRequest
+ {
+ Asin = asin,
+ }), Encoding.UTF8, "application/json");
+ var request = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}{SeriesEndpoint(asin)}")
+ {
+ Content = requestContent
+ };
+ var response = await _httpClient.SendAsync(request, cancellationToken);
+ var responseStream = await response.Content.ReadAsStreamAsync();
+ var responseString = await new StreamReader(responseStream, Encoding.UTF8).ReadToEndAsync();
+ return JsonUtil.Deserialize<NextBookResult>(responseString);
});
+
+ //return HandleDownloadExceptionsAsync(async () =>
+ //{
+ // var response = await _httpClient.GetStringAsync($"{BaseUrl}{SeriesEndpoint(asin)}", cancellationToken);
+ // return JsonConvert.DeserializeObject<NextBookResult>(response);
+ //});
}
public async Task PreloadAsync(string asin, string regionTld, CancellationToken cancellationToken)


message: Add missing (new?) BSE properties to StartActions.

diff:
@@ -126,6 +126,9 @@ public sealed class WidgetOptions
[JsonProperty("showBadges", NullValueHandling = NullValueHandling.Ignore)]
public bool? ShowBadges { get; set; }
+
+ [JsonProperty("BSEDataKey", NullValueHandling = NullValueHandling.Ignore)]
+ public string BseDataKey { get; set; }
}
public sealed class WidgetStrings
@@ -150,6 +153,15 @@ public sealed class WidgetStrings
[JsonProperty("panelRowTitle", NullValueHandling = NullValueHandling.Ignore)]
public Dictionary<string, string> PanelRowTitle { get; set; }
+
+ [JsonProperty("BSE_series_text", NullValueHandling = NullValueHandling.Ignore)]
+ public Dictionary<string, string> BseSeriesText { get; set; }
+
+ [JsonProperty("BSE_text", NullValueHandling = NullValueHandling.Ignore)]
+ public Dictionary<string, string> BseText { get; set; }
+
+ [JsonProperty("BSE_title", NullValueHandling = NullValueHandling.Ignore)]
+ public Dictionary<string, string> BseTitle { get; set; }
}
public sealed class Widget


message: Fix missing string format.

diff:
@@ -465,7 +465,7 @@ private async Task btnKindleExtras_Run()
}
catch (Exception e)
{
- _logger.Log($@"{MainStrings.NoPremadeActionsAvailable}:{Environment.NewLine}{e.Message}");
+ _logger.Log($@"{string.Format(MainStrings.NoPremadeActionsAvailable, type)}:{Environment.NewLine}{e.Message}");
return null;
}
}

original_message: Fix missing string format.


message: Remove br encoding from Accept-Encoding for compatibility.

diff:
@@ -100,7 +100,7 @@ public async Task<Stream> GetStreamAsync(string url, CancellationToken cancellat
private static void SetDefaultSettings(HttpRequestMessage request)
{
request.Headers.Add(KnownHeaders.UserAgent, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
- request.Headers.Add(KnownHeaders.AcceptEncoding,"gzip, deflate, br");
+ request.Headers.Add(KnownHeaders.AcceptEncoding,"gzip, deflate");
}
}


message: Change ProcessCmdKey to match other forms that use it.

diff:
@@ -25,7 +25,7 @@ public frmGR(BookInfo[] bookList, ISecondarySource source)
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
- if (keyData != Keys.Enter)
+ if (keyData != Keys.Escape)
return base.ProcessCmdKey(ref msg, keyData);
Close();
return true;


message: Add new form for displaying author search results - not implemented yet.

diff:
</Compile>
<Compile Update="src\UI\frmASIN.Designer.cs">
<DependentUpon>frmASIN.cs</DependentUpon>
+ </Compile>
+ <Compile Update="src\UI\frmAuthorList.cs">
+ <SubType>Form</SubType>
+ </Compile>
+ <Compile Update="src\UI\frmAuthorList.Designer.cs">
+ <DependentUpon>frmAuthorList.cs</DependentUpon>
</Compile>
<Compile Update="src\UI\frmCreateXR.cs">
<SubType>Form</SubType>
<EmbeddedResource Update="src\UI\frmASIN.resx">
<DependentUpon>frmASIN.cs</DependentUpon>
</EmbeddedResource>
+ <EmbeddedResource Update="src\UI\frmAuthorList.resx">
+ <DependentUpon>frmAuthorList.cs</DependentUpon>
+ </EmbeddedResource>
<EmbeddedResource Update="src\UI\frmBookInfo.resx">
<DependentUpon>frmBookInfo.cs</DependentUpon>
</EmbeddedResource>


message: Use asin for author results listing.

diff:
@@ -49,7 +49,7 @@ private void frmAuthorList_Load(object sender, EventArgs e)
lblMessage1.Text = $@"{PluralUtil.Pluralize($"{_authorList.Count:author}")} were found on Amazon with this name.";
cbResults.Items.Clear();
foreach (var author in _authorList)
- cbResults.Items.Add(author.Name);
+ cbResults.Items.Add(author.Asin);
cbResults.SelectedIndex = 0;
_tooltip.SetToolTip(linkStore, "Visit this author's page on Amazon.");
}

original_message: Use asin for author results listing.


message: Improve first author result matching on Amazon.

diff:
@@ -58,7 +58,6 @@ public AmazonClient(IHttpClient httpClient, IAmazonInfoParser amazonInfoParser,
public string Url(string tld, string asin) => $"https://www.amazon.{tld}/dp/{asin}";
- // TODO fix the need to have an enableLog param
public async Task<AuthorSearchResults> SearchAuthor(string author, string TLD, CancellationToken cancellationToken, bool enableLog = true)
{
//Generate Author search URL from author's name
@@ -78,7 +77,8 @@ public async Task<AuthorSearchResults> SearchAuthor(string author, string TLD, C
// Try to find Author's page from Amazon search
var properAuthor = "";
HtmlNode[] possibleNodes = null;
- var node = authorSearchDoc?.DocumentNode.SelectSingleNode("//*[@id='result_1']");
+ var node = authorSearchDoc?.DocumentNode.SelectSingleNode("//*[@id='result_1']")
+ ?? authorSearchDoc?.DocumentNode.SelectSingleNode(".//div/div[@data-index='0']");
if (node == null || !node.OuterHtml.Contains("/e/B"))
{
if(enableLog)
@@ -101,36 +101,44 @@ public async Task<AuthorSearchResults> SearchAuthor(string author, string TLD, C
}
string authorAsin = null;
+ string authorUrl = null;
+ var authorNode = node.Descendants("a").FirstOrDefault(d => d.Attributes["href"].Value.Contains("/e/B"));
+
if (possibleNodes != null && possibleNodes.Any())
{
// TODO Present a list of these to choose from
node = possibleNodes.First();
- properAuthor = node.GetAttributeValue("href", "");
- authorAsin = ParseAsinFromUrl(properAuthor);
+ authorUrl = node.GetAttributeValue("href", "");
+ authorAsin = ParseAsinFromUrl(authorUrl);
}
// Check for typical search results, second item is the author page
+ else if (authorNode != null)
+ {
+ authorUrl = authorNode.GetAttributeValue("href", "");
+ authorAsin = ParseAsinFromUrl(authorUrl);
+ }
else if ((node = node.SelectSingleNode("//*[@id='result_1']/div/div/div/div/a")) != null)
{
- properAuthor = node.GetAttributeValue("href", "");
+ authorUrl = node.GetAttributeValue("href", "");
authorAsin = node.GetAttributeValue("data-asin", null)
- ?? ParseAsinFromUrl(properAuthor);
+ ?? ParseAsinFromUrl(authorUrl);
}
// otherwise check for "by so-and-so" text beneath the titles for a possible match
else if ((node = authorSearchDoc.DocumentNode.SelectSingleNode($"//div[@id='resultsCol']//li[@class='s-result-item celwidget ']//a[text()=\"{newAuthor}\"]")) != null)
{
- properAuthor = node.GetAttributeValue("href", "");
- authorAsin = ParseAsinFromUrl(properAuthor);
+ authorUrl = node.GetAttributeValue("href", "");
+ authorAsin = ParseAsinFromUrl(authorUrl);
}
- if (node == null || string.IsNullOrEmpty(properAuthor) || properAuthor.IndexOf('/', 1) < 3 || string.IsNullOrEmpty(authorAsin))
+ if (node == null || string.IsNullOrEmpty(authorUrl) || authorUrl.IndexOf('/', 1) < 3 || string.IsNullOrEmpty(authorAsin))
{
if (enableLog)
_logger.Log($"Unable to parse author's page URL properly. Try again later or report this URL on the MobileRead thread: {amazonKindleAuthorSearchUrl}");
return null;
}
- properAuthor = properAuthor.Substring(1, properAuthor.IndexOf('/', 1) - 1);
+ properAuthor = authorUrl.Substring(1, authorUrl.IndexOf('/', 1) - 1);
var authorAmazonWebsiteLocationLog = $"https://www.amazon.{TLD}/{properAuthor}/e/{authorAsin}";
- var authorAmazonWebsiteLocation = $"https://www.amazon.{TLD}{node.GetAttributeValue("href", "")}";
+ var authorAmazonWebsiteLocation = $"https://www.amazon.{TLD}{authorUrl}";
if(enableLog)
_logger.Log($"Author page found on Amazon!\r\nAuthor's Amazon Page URL: {authorAmazonWebsiteLocationLog}");
@@ -194,7 +202,8 @@ public async Task<AuthorSearchResults> SearchAuthor(string author, string TLD, C
Biography = biography,
ImageUrl = authorImageUrl,
Books = bookList.ToArray(),
- Url = authorAmazonWebsiteLocationLog
+ Url = authorAmazonWebsiteLocationLog,
+ Name = newAuthor
};
}
@@ -202,10 +211,10 @@ public async Task<AuthorSearchResults> SearchAuthor(string author, string TLD, C
private string GetAuthorBiography(HtmlDocument authorHtmlDoc)
{
var bioNode = authorHtmlDoc.DocumentNode.SelectSingleNode("//div[@id='ap-bio' and @class='a-row']/div/div/span")
- ?? authorHtmlDoc.DocumentNode.SelectSingleNode("//span[@id='author_biography']")
- ?? throw new FormatChangedException(nameof(AmazonClient), "author bio");
+ ?? authorHtmlDoc.DocumentNode.SelectSingleNode("//span[@id='author_biography']");
+ //?? throw new FormatChangedException(nameof(AmazonClient), "author bio");
- return bioNode.InnerHtml.Clean();
+ return bioNode != null ? bioNode.InnerHtml.Clean() : string.Empty;
}
private string GetAuthorImageUrl(HtmlDocument authorHtmlDoc)


message: Remove missed name from AuthorSearchResults.

diff:
@@ -202,8 +202,7 @@ public async Task<AuthorSearchResults> SearchAuthor(string author, string TLD, C
Biography = biography,
ImageUrl = authorImageUrl,
Books = bookList.ToArray(),
- Url = authorAmazonWebsiteLocationLog,
- Name = newAuthor
+ Url = authorAmazonWebsiteLocationLog
};
}


message: Skip books in ParseBookList that don't appear to be Kindle editions.

diff:
@@ -173,6 +173,9 @@ private IEnumerable<BookInfo> ParseBookList(HtmlNodeCollection books)
{
foreach (var book in books.Where(item => item != null))
{
+ if (!book.InnerHtml.Contains("Kindle Edition"))
+ continue;
+
string author = null;
string title = null;


message: Fix missing ASIN request when enabled. Default to true.

diff:
@@ -12,7 +12,7 @@ namespace XRayBuilderGUI.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -313,7 +313,7 @@ internal sealed partial class Settings : global::System.Configuration.Applicatio
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool promptASIN {
get {
return ((bool)(this["promptASIN"]));

diff:
<Value Profile="(Default)">com</Value>
</Setting>
<Setting Name="promptASIN" Type="System.Boolean" Scope="User">
- <Value Profile="(Default)">False</Value>
+ <Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="minClipLen" Type="System.Int32" Scope="User">
<Value Profile="(Default)">25</Value>

diff:
<value>com</value>
</setting>
<setting name="promptASIN" serializeAs="String">
- <value>False</value>
+ <value>True</value>
</setting>
<setting name="minClipLen" serializeAs="String">
<value>25</value>

diff:
@@ -42,9 +42,9 @@ private void InitializeComponent()
// btnOK
//
this.btnOK.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.btnOK.Location = new System.Drawing.Point(146, 88);
+ this.btnOK.Location = new System.Drawing.Point(148, 88);
this.btnOK.Name = "btnOK";
- this.btnOK.Size = new System.Drawing.Size(100, 26);
+ this.btnOK.Size = new System.Drawing.Size(75, 26);
this.btnOK.TabIndex = 1;
this.btnOK.Text = "OK";
this.btnOK.UseVisualStyleBackColor = true;
@@ -55,7 +55,7 @@ private void InitializeComponent()
this.tbAsin.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.tbAsin.Location = new System.Drawing.Point(65, 54);
this.tbAsin.Name = "tbAsin";
- this.tbAsin.Size = new System.Drawing.Size(180, 23);
+ this.tbAsin.Size = new System.Drawing.Size(157, 23);
this.tbAsin.TabIndex = 0;
//
// lblTitle
@@ -64,24 +64,24 @@ private void InitializeComponent()
this.lblTitle.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lblTitle.Location = new System.Drawing.Point(66, 9);
this.lblTitle.Name = "lblTitle";
- this.lblTitle.Size = new System.Drawing.Size(182, 15);
+ this.lblTitle.Size = new System.Drawing.Size(160, 15);
this.lblTitle.TabIndex = 3;
this.lblTitle.Text = "Title";
//
// lblTitleHead
//
this.lblTitleHead.AutoSize = true;
- this.lblTitleHead.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.lblTitleHead.Location = new System.Drawing.Point(19, 9);
+ this.lblTitleHead.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblTitleHead.Location = new System.Drawing.Point(24, 9);
this.lblTitleHead.Name = "lblTitleHead";
- this.lblTitleHead.Size = new System.Drawing.Size(35, 15);
+ this.lblTitleHead.Size = new System.Drawing.Size(33, 15);
this.lblTitleHead.TabIndex = 4;
this.lblTitleHead.Text = "Title:";
//
// lblAsin
//
this.lblAsin.AutoSize = true;
- this.lblAsin.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblAsin.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lblAsin.Location = new System.Drawing.Point(19, 57);
this.lblAsin.Name = "lblAsin";
this.lblAsin.Size = new System.Drawing.Size(38, 15);
@@ -91,10 +91,10 @@ private void InitializeComponent()
// label1
//
this.label1.AutoSize = true;
- this.label1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.label1.Location = new System.Drawing.Point(8, 30);
+ this.label1.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label1.Location = new System.Drawing.Point(10, 30);
this.label1.Name = "label1";
- this.label1.Size = new System.Drawing.Size(49, 15);
+ this.label1.Size = new System.Drawing.Size(47, 15);
this.label1.TabIndex = 7;
this.label1.Text = "Author:";
//
@@ -104,7 +104,7 @@ private void InitializeComponent()
this.lblAuthor.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lblAuthor.Location = new System.Drawing.Point(66, 30);
this.lblAuthor.Name = "lblAuthor";
- this.lblAuthor.Size = new System.Drawing.Size(182, 15);
+ this.lblAuthor.Size = new System.Drawing.Size(160, 15);
this.lblAuthor.TabIndex = 6;
this.lblAuthor.Text = "Author";
//
@@ -113,7 +113,7 @@ private void InitializeComponent()
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(11, 88);
this.btnCancel.Name = "btnCancel";
- this.btnCancel.Size = new System.Drawing.Size(100, 26);
+ this.btnCancel.Size = new System.Drawing.Size(75, 26);
this.btnCancel.TabIndex = 8;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
@@ -126,7 +126,7 @@ private void InitializeComponent()
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.btnCancel;
- this.ClientSize = new System.Drawing.Size(257, 125);
+ this.ClientSize = new System.Drawing.Size(234, 125);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.label1);
this.Controls.Add(this.lblAuthor);
@@ -146,7 +146,6 @@ private void InitializeComponent()
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Amazon ASIN";
- this.TopMost = true;
this.ResumeLayout(false);
this.PerformLayout();

diff:
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
- AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLlrQRXavJNF6uyzZGm7gSAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAbr3VSoXR6J2L1u7WlOPz96Dr/P+g6/z/l+Pz+IrZ69KC0uaabLvVRAAA
- AABks85eAAAAAAAAAABsvdMvfs3juoTT6MyB0OWUecjfcWq91VZitM9LYbXOSWy91ll0xNxpfcrhjHvI
- 4MBzwNqvcsLZnG6+11Nfss1TbbvWnma30jkAAAAAkZFtB66WhFW2nYxpqJN9R39/fwIAAAAAm41/EpaH
- eBFhs84vbLzWloDL5K2C0OePAAAAAAAAAAAAAAAAo498J8Gtmc7UwK//1MGw/9K/rv+9qZi/oYx7PMGu
- msvCsJ/SqpaCM2q61UN0xNxpaLfRJwAAAAAAAAAAAAAAALymlazUwbD/1MGw/8+7p/XCr53T0b6s/Me0
- pvTUwbD/1MGw/8Crl88AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC/rZnd1MGw/9TBsP+znot3AAAAALOe
- jYTUwK//1MGw/9TBsP+2oY+FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwK2e2NTBsP/UwbD/rpaEVQAA
- AACZf38KxrOh5dTBsP/UwbD/p5SBTwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALmllZrUwbD/1MGw/72p
- l7OflHQYAAAAAMGunMfUwbD/1MGw/6aNeEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdkXkVvamXvtTA
- r//UwbD/ybOi7cGunMfLtqPo1MGw/9TBsP+mjXhIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH9/
- fwKvm4lkvaeXo8CuncTBsJzczreq99TBsP/UwbD/po14SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmW
- iSexm4lmsp6Lap2ReRUAAAAAAAAAAMGunMfUwbD/0r+u/5+OeTsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAC1pJF51MGw/9TBsP+2oZCbkX9tDqeSfT3MuKfx1MGw/826p/yci3MfAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAoZB3HsWyoOPUwbD/1MCv/8mzpPfPuav61MGw/9TBsP/AqpjHAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAACvmIlDwKqbx829rP3UwbD/1MGw/9K/rv/ArZrLsJiENAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACnjn09sZqIY7KbiWSmjn9Cf39/AgAAAAAAAAAAAAAAAAAA
- AAAAAAAA/D8AAOAFAACAAAAAEEAAAOAAAADgBwAA4QcAAOEHAADghwAA4AcAAPAHAADhhwAA4AcAAOAP
- AADwDwAA/B8AAA==
+ AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKQXi7WnHB22aB1jtmgdY7WnHB2wpBeLgAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAzo1nL92jdq/ssob58LeL//C3i//wt4v/8LeL/+uzhvrco3avzYtpLgAA
+ AAAAAAAAAAAAAAAAAAAAAAAA0ZhqTeaugeTwt4v/8LeL//C3i//wt4v/8LeL//C3i//wt4v/8LeL/+as
+ guTTlmlLAAAAAAAAAAAAAAAAz49lMOitguTwt4v/8LeL//C3i//wt4v/9Mqp//TJqf/wt4v/8LeL//C3
+ i//wt4v/5qyC5M6NZy8AAAAAAAAAANyid7Dwt4v/8LeL//C3i//wt4v/8LeL//TKqf/0yqn/8LeL//C3
+ i//wt4v/8LeL//C3i//coXWuAAAAAMWKYDDss4b68LeL//C3i//wt4v/8LeL//C3i//xvZT/8b2U//C3
+ i//wt4v/8LeL//C3i//wt4v/67OG+sKQXi7WnG938LeL//C3i//wt4v/8LeL//C3i//wt4v/9tK3//fX
+ v//wt4v/8LeL//C3i//wt4v/8LeL//C3i//Wm2912qF1kPC3i//wt4v/8LeL//C3i//wt4v/8LeL//LA
+ mv/88Of/8buR//C3i//wt4v/8LeL//C3i//wt4v/2aB1jtyhdZDwt4v/8LeL//C3i//wt4v/8LeL//C3
+ i//wt4v/9c6x//ro2//wt4v/8LeL//C3i//wt4v/8LeL/9qhdZDXm2948LeL//C3i//wt4v/8LeL//LD
+ nv/32sP/8LeL//C3i//76tz/8sCZ//C3i//wt4v/8LeL//C3i//WnHB2x4phMuyzhvrwt4v/8LeL//C3
+ i//xvpb/++zh//C3i//wt4v/++3i//G+lv/wt4v/8LeL//C3i//ssob5w41cLwAAAADdo3Wy8LeL//C3
+ i//wt4v/8LeL//jdyP/77OH/++zh//jcx//wt4v/8LeL//C3i//wt4v/3aN2rwAAAAAAAAAA0JJoMeit
+ guXwt4v/8LeL//C3i//wt4v/8r6X//K+l//wt4v/8LeL//C3i//wt4v/5q6B5M+PZTAAAAAAAAAAAAAA
+ AADOlmlO6K2C5fC3i//wt4v/8LeL//C3i//wt4v/8LeL//C3i//wt4v/5q6B5NGYak0AAAAAAAAAAAAA
+ AAAAAAAAAAAAANCSaDHco3ix7LOG+vC3i//wt4v/8LeL//C3i//ss4b63KJ3sM+PZTAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMeKYTLXm2942qB0j9mgdY7Xm294xYpgMAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA+B8AAOAHAADAAwAAgAEAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAAgAEAAMAD
+ AADgBwAA+B8AAA==
</value>
</data>
</root>
\ No newline at end of file


message: Return response not exception.

diff:
@@ -228,14 +228,7 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
try
{
var response = await base.SendAsync(request, cancellationToken);
- if (!response.IsSuccessStatusCode)
- throw new HttpClientException(response.StatusCode.ToString())
- {
- Response = response
- };
- return response;
- }
- catch (HttpClientException ex) when (ex.Response.StatusCode == HttpStatusCode.NotFound
+ if (!response.IsSuccessStatusCode
&& request.RequestUri.Host.Contains(".amazon.")
&& !request.RequestUri.Host.EndsWith(".com"))
{
@@ -245,12 +238,18 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
Host = "www.amazon.com"
};
request.RequestUri = builder.Uri;
- var response = await base.SendAsync(request, cancellationToken);
+ response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.NotFound)
return response;
Logger?.Log($"Not available from {originalHost}, but found on Amazon.com ({request.RequestUri})");
return response;
}
+ return response;
+ }
+ catch (HttpClientException)
+ {
+ return new HttpResponseMessage();
+ }
}
}

original_message: Return response not exception.


message: Additional book description node.

diff:
@@ -111,6 +111,7 @@ public InfoResponse ParseAmazonDocument(HtmlDocument bookDoc)
#region Description
var descNode = bookDoc.DocumentNode.SelectSingleNode("//*[@id='bookDescription_feature_div']/noscript")
+ ?? bookDoc.DocumentNode.SelectSingleNode("//*[@id='bookDescription_feature_div']/div[@data-a-expander-name='book_description_expander']")
?? bookDoc.DocumentNode.SelectSingleNode("//*[@class='a-size-medium series-detail-description-text']");
if (descNode != null && descNode.InnerText != "")
{

original_message: Additional book description node.


message: Fix System.ComponentModel.Win32Exception with Process.Start

diff:
@@ -171,13 +171,13 @@ public new void ShowDialog()
private void btnFollow_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(_authorUrl)) return;
- Process.Start(_authorUrl);
+ Process.Start(new ProcessStartInfo(_authorUrl) { UseShellExecute = true });
}
private void linkStore_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
if (!string.IsNullOrEmpty(_asin))
- Process.Start($"http://www.amazon.{Settings.Default.amazonTLD}/dp/{_asin}");
+ Process.Start(new ProcessStartInfo($"http://www.amazon.{Settings.Default.amazonTLD}/dp/{_asin}") { UseShellExecute = true });
}
}
}
\ No newline at end of file

diff:
@@ -174,7 +174,7 @@ private void ListViewItem_SetSpacing(ListView listview, short leftPadding, short
private void linkStore_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
if (!string.IsNullOrEmpty(_asin))
- Process.Start($"http://www.amazon.{Settings.Default.amazonTLD}/dp/{_asin}");
+ Process.Start(new ProcessStartInfo($"http://www.amazon.{Settings.Default.amazonTLD}/dp/{_asin}") { UseShellExecute = true });
}
}
}

diff:
@@ -23,7 +23,7 @@ public static void OpenDirectory(string dir)
if (!Directory.Exists(dir))
MessageBox.Show("Specified directory does not exist.", "Directory Not Found...", MessageBoxButtons.OK, MessageBoxIcon.Warning);
else
- Process.Start(dir);
+ Process.Start(new ProcessStartInfo(dir) { UseShellExecute = true });
}
public static string GetFile([Localizable(true)] string title, string defaultFile, string filter = "All files (*.*)|*.*", string initialDir = "")

diff:
@@ -37,7 +37,7 @@ private void lnklblIcons_LinkClicked(object sender, LinkLabelLinkClickedEventArg
{
try
{
- Process.Start("https://icons8.com");
+ Process.Start(new ProcessStartInfo("https://icons8.com") { UseShellExecute = true });
}
catch
{

diff:
@@ -56,13 +56,13 @@ private void frmAuthorList_Load(object sender, EventArgs e)
private void linkStore_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
- Process.Start(_authorList[cbResults.SelectedIndex].Url);
+ Process.Start(new ProcessStartInfo(_authorList[cbResults.SelectedIndex].Url) { UseShellExecute = true });
}
private void dgvOtherBooks_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex >= 0 && _authorList[cbResults.SelectedIndex].Books != null)
- Process.Start(_authorList[cbResults.SelectedIndex].Books[e.RowIndex].AmazonUrl);
+ Process.Start(new ProcessStartInfo(_authorList[cbResults.SelectedIndex].Books[e.RowIndex].AmazonUrl) { UseShellExecute = true });
}
}
}
\ No newline at end of file

diff:
@@ -242,7 +242,7 @@ private void OpenLink(string url)
try
{
- Process.Start(url);
+ Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
}
catch (Exception ex)
{

diff:
@@ -51,7 +51,7 @@ private void cbResults_SelectedIndexChanged(object sender, EventArgs e)
private void linkID_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
- Process.Start(_toolTip1.GetToolTip(linkID));
+ Process.Start(new ProcessStartInfo(_toolTip1.GetToolTip(linkID)) { UseShellExecute = true });
}
private void frmGR_Load(object sender, EventArgs e)


message: Improve Amazon author page search

diff:
@@ -75,7 +75,8 @@ public async Task<AuthorSearchResults> SearchAuthor(string author, string TLD, C
// Try to find Author's page from Amazon search
var properAuthor = "";
HtmlNode[] possibleNodes = null;
- var node = authorSearchDoc?.DocumentNode.SelectSingleNode("//*[@id='result_1']")
+ var node = authorSearchDoc?.DocumentNode.SelectSingleNode(".//div[@data-asin and @data-index and @data-component-type]")
+ ?? authorSearchDoc?.DocumentNode.SelectSingleNode("//*[@id='result_1']")
?? authorSearchDoc?.DocumentNode.SelectSingleNode(".//div/div[@data-index='0']");
if (node == null || !node.OuterHtml.Contains("/e/B"))
{

original_message: Improve Amazon author page search


message: Improve book description parsing

diff:
using XRayBuilder.Core.Libraries.Logging;
using XRayBuilder.Core.Model;
-namespace XRayBuilder.Core.DataSources.Amazon
-{
+namespace XRayBuilder.Core.DataSources.Amazon;
+
public class AmazonInfoParser : IAmazonInfoParser
{
private readonly ILogger _logger;
@@ -72,6 +72,7 @@ public InfoResponse ParseAmazonDocument(HtmlDocument bookDoc)
CheckCaptcha(bookDoc);
#region Image URL
+
var bookImageLoc = bookDoc.DocumentNode.SelectSingleNode("//*[@id='imgBlkFront']")
?? bookDoc.DocumentNode.SelectSingleNode("//*[@id='imageBlock']")
?? bookDoc.DocumentNode.SelectSingleNode("//*[@class='series-detail-product-image']")
@@ -80,7 +81,9 @@ public InfoResponse ParseAmazonDocument(HtmlDocument bookDoc)
?? bookDoc.DocumentNode.SelectSingleNode("//*[@id='main-image']");
if (bookImageLoc == null)
+ {
_logger.Log("Error finding book image.");
+ }
else
{
var imageUrl = Regex.Replace(bookImageLoc.GetAttributeValue("src", ""), @"_.*?_\.", string.Empty);
@@ -110,6 +113,7 @@ public InfoResponse ParseAmazonDocument(HtmlDocument bookDoc)
#endregion
#region Description
+
var descNode = bookDoc.DocumentNode.SelectSingleNode("//*[@id='bookDescription_feature_div']/noscript")
?? bookDoc.DocumentNode.SelectSingleNode("//*[@id='bookDescription_feature_div']/div[@data-a-expander-name='book_description_expander']")
?? bookDoc.DocumentNode.SelectSingleNode("//*[@class='a-size-medium series-detail-description-text']");
@@ -119,37 +123,49 @@ public InfoResponse ParseAmazonDocument(HtmlDocument bookDoc)
// Match all p nodes in the description node.
// Filter out any node starting and ending with a html style tag
// "Should" remove single bold, italic and heading lines to leave a cleaner description.
- // Then replace known entities with characters using HtmlEntity.DeEntitize and join strings to form description.
- var nodes = descNode.SelectNodes(".//p/text()[normalize-space(.) != '']") ?? descNode.SelectNodes(".//div/text()[normalize-space(.) != '']");
+ // Then replace known entities with characters using HtmlEntity.DeEntitize and join strings (with spaces if needed) to form description.
+ var nodes = descNode.SelectNodes(".//p/span[not(contains(@class,'bold'))]")
+ ?? descNode.SelectNodes(".//div/span[not(contains(@class,'bold'))]");
- string description;
+ var description = string.Empty;
if (nodes != null)
{
var filteredNodes = nodes
- .Where(node => !_dirtyParas.IsMatch(node.InnerHtml.Trim()))
+ //.Where(node => !_dirtyParas.IsMatch(node.InnerHtml.Trim()))
.Select(node => HtmlEntity.DeEntitize(node.InnerText.Trim()))
+ .Where(node => node.Length > 1)
.ToArray();
- description = filteredNodes.Any()
- ? string.Join(" ", filteredNodes)
- : descNode.InnerHtml.Clean();
+ foreach (var node in filteredNodes)
+ if (char.IsUpper(node[0])) // | (description.Length != 0 && char.IsLower(description[^1])))
+ description += " " + node;
+ else
+ description += node;
+
+ description = description.Clean();
+
+ //description = filteredNodes.Any()
+ // ? filteredNodes.Aggregate(description, (current, s) => current + (char.IsLower(s[0]) ? s : s + " ")).Trim()
+ // : descNode.InnerHtml.Clean();
}
else
- description = descNode.InnerHtml.Clean();
+ {
+ description = descNode.InnerText.Clean();
+ }
// Following the example of Amazon, cut off desc around 1000 characters.
// If conveniently trimmed at the end of the sentence, let it end with the punctuation.
// If the sentence continues, cut it off and replace the space with an ellipsis
- if (description.Length > 1000)
- {
- description = description.Substring(0, 1000);
- var lastPunc = description.LastIndexOfAny(new[] { '.', '!', '?' });
- var lastSpace = description.LastIndexOf(' ');
- if (lastPunc > lastSpace)
- description = description.Substring(0, lastPunc + 1);
- else
- description = $"{description.Substring(0, lastSpace - 1)}{'\u2026'}";
- }
+ //if (description.Length > 1000)
+ //{
+ // description = description.Substring(0, 1000);
+ // var lastPunc = description.LastIndexOfAny(new[] { '.', '!', '?' });
+ // var lastSpace = description.LastIndexOf(' ');
+ // if (lastPunc > lastSpace)
+ // description = description.Substring(0, lastPunc + 1);
+ // else
+ // description = $"{description.Substring(0, lastSpace - 1)}{'\u2026'}";
+ //}
// Used to test output from Amazon description parsing
//var descsCheckFile = AppDomain.CurrentDomain.BaseDirectory + @"\descs.txt";
@@ -157,9 +173,11 @@ public InfoResponse ParseAmazonDocument(HtmlDocument bookDoc)
response.Description = description;
}
+
#endregion
#region Reviews
+
try
{
var ratingNode = bookDoc.DocumentNode.SelectSingleNode("//*[@id='acrPopover']")
@@ -182,6 +200,7 @@ public InfoResponse ParseAmazonDocument(HtmlDocument bookDoc)
{
throw new AggregateException($"Error finding book ratings. If you want, you can report the book's Amazon URL to help with parsing.\r\nError: {ex.Message}\r\n{ex.StackTrace}", ex);
}
+
#endregion
#region Pages
@@ -222,4 +241,3 @@ public InfoResponse ParseAmazonDocument(HtmlDocument bookDoc)
return response;
}
}
\ No newline at end of file
-}

original_message: Improve book description parsing


message:
Fix System.ComponentModel.Win32Exception with Process.Start
Add button link to GitHub

diff:
@@ -98,6 +98,7 @@ private void InitializeComponent()
this.pbFile2 = new System.Windows.Forms.PictureBox();
this.pbFile1 = new System.Windows.Forms.PictureBox();
this.pbCover = new System.Windows.Forms.PictureBox();
+ this.btnVisitGithub = new System.Windows.Forms.ToolStripMenuItem();
this.groupBox1.SuspendLayout();
this.groupBox3.SuspendLayout();
this.cmsLog.SuspendLayout();
@@ -114,7 +115,7 @@ private void InitializeComponent()
// lblGoodreads
//
this.lblGoodreads.AutoSize = true;
- this.lblGoodreads.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblGoodreads.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.lblGoodreads.Location = new System.Drawing.Point(9, 24);
this.lblGoodreads.Name = "lblGoodreads";
this.lblGoodreads.Size = new System.Drawing.Size(91, 15);
@@ -126,7 +127,7 @@ private void InitializeComponent()
//
this.txtMobi.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
- this.txtMobi.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtMobi.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.txtMobi.Location = new System.Drawing.Point(13, 21);
this.txtMobi.Name = "txtMobi";
this.txtMobi.Size = new System.Drawing.Size(927, 23);
@@ -139,7 +140,7 @@ private void InitializeComponent()
| System.Windows.Forms.AnchorStyles.Right)));
this.groupBox1.Controls.Add(this.lblGoodreads);
this.groupBox1.Controls.Add(this.txtGoodreads);
- this.groupBox1.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.groupBox1.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this.groupBox1.Location = new System.Drawing.Point(12, 143);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(953, 58);
@@ -151,7 +152,7 @@ private void InitializeComponent()
//
this.txtGoodreads.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
- this.txtGoodreads.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtGoodreads.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.txtGoodreads.Location = new System.Drawing.Point(107, 21);
this.txtGoodreads.Name = "txtGoodreads";
this.txtGoodreads.Size = new System.Drawing.Size(833, 23);
@@ -163,7 +164,7 @@ private void InitializeComponent()
this.groupBox3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.groupBox3.Controls.Add(this.txtMobi);
- this.groupBox3.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.groupBox3.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this.groupBox3.Location = new System.Drawing.Point(12, 82);
this.groupBox3.Name = "groupBox3";
this.groupBox3.Size = new System.Drawing.Size(953, 58);
@@ -248,7 +249,7 @@ private void InitializeComponent()
//
this.lblFiles.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.lblFiles.AutoSize = true;
- this.lblFiles.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblFiles.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this.lblFiles.Location = new System.Drawing.Point(771, 639);
this.lblFiles.Name = "lblFiles";
this.lblFiles.Size = new System.Drawing.Size(33, 13);
@@ -261,7 +262,7 @@ private void InitializeComponent()
this.txtAsin.ActiveLinkColor = System.Drawing.Color.MediumBlue;
this.txtAsin.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.txtAsin.AutoSize = true;
- this.txtAsin.Font = new System.Drawing.Font("Segoe UI", 7.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtAsin.Font = new System.Drawing.Font("Segoe UI", 7.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.txtAsin.LinkBehavior = System.Windows.Forms.LinkBehavior.NeverUnderline;
this.txtAsin.LinkColor = System.Drawing.Color.MediumBlue;
this.txtAsin.Location = new System.Drawing.Point(851, 603);
@@ -276,7 +277,7 @@ private void InitializeComponent()
// txtAuthor
//
this.txtAuthor.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
- this.txtAuthor.Font = new System.Drawing.Font("Segoe UI", 7.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtAuthor.Font = new System.Drawing.Font("Segoe UI", 7.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.txtAuthor.Location = new System.Drawing.Point(851, 585);
this.txtAuthor.Name = "txtAuthor";
this.txtAuthor.Size = new System.Drawing.Size(120, 13);
@@ -288,7 +289,7 @@ private void InitializeComponent()
// txtTitle
//
this.txtTitle.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
- this.txtTitle.Font = new System.Drawing.Font("Segoe UI", 7.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtTitle.Font = new System.Drawing.Font("Segoe UI", 7.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.txtTitle.Location = new System.Drawing.Point(851, 567);
this.txtTitle.Name = "txtTitle";
this.txtTitle.Size = new System.Drawing.Size(120, 13);
@@ -301,7 +302,7 @@ private void InitializeComponent()
//
this.lblAsin.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.lblAsin.AutoSize = true;
- this.lblAsin.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblAsin.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this.lblAsin.Location = new System.Drawing.Point(772, 603);
this.lblAsin.Name = "lblAsin";
this.lblAsin.Size = new System.Drawing.Size(36, 13);
@@ -313,7 +314,7 @@ private void InitializeComponent()
//
this.lblAuthor.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.lblAuthor.AutoSize = true;
- this.lblAuthor.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblAuthor.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this.lblAuthor.Location = new System.Drawing.Point(772, 585);
this.lblAuthor.Name = "lblAuthor";
this.lblAuthor.Size = new System.Drawing.Size(47, 13);
@@ -325,7 +326,7 @@ private void InitializeComponent()
//
this.lblTitle.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.lblTitle.AutoSize = true;
- this.lblTitle.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblTitle.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this.lblTitle.Location = new System.Drawing.Point(772, 567);
this.lblTitle.Name = "lblTitle";
this.lblTitle.Size = new System.Drawing.Size(32, 13);
@@ -341,7 +342,7 @@ private void InitializeComponent()
this.groupBox4.Controls.Add(this.rdoFile);
this.groupBox4.Controls.Add(this.rdoGoodreads);
this.groupBox4.Controls.Add(this.txtXMLFile);
- this.groupBox4.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.groupBox4.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this.groupBox4.Location = new System.Drawing.Point(12, 204);
this.groupBox4.Name = "groupBox4";
this.groupBox4.Size = new System.Drawing.Size(953, 58);
@@ -352,7 +353,7 @@ private void InitializeComponent()
// rdoRoentgen
//
this.rdoRoentgen.AutoSize = true;
- this.rdoRoentgen.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.rdoRoentgen.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.rdoRoentgen.Location = new System.Drawing.Point(113, 22);
this.rdoRoentgen.Name = "rdoRoentgen";
this.rdoRoentgen.Size = new System.Drawing.Size(76, 19);
@@ -364,7 +365,7 @@ private void InitializeComponent()
// rdoFile
//
this.rdoFile.AutoSize = true;
- this.rdoFile.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.rdoFile.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.rdoFile.Location = new System.Drawing.Point(213, 22);
this.rdoFile.Name = "rdoFile";
this.rdoFile.Size = new System.Drawing.Size(43, 19);
@@ -377,7 +378,7 @@ private void InitializeComponent()
//
this.rdoGoodreads.AutoSize = true;
this.rdoGoodreads.Checked = true;
- this.rdoGoodreads.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.rdoGoodreads.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.rdoGoodreads.Location = new System.Drawing.Point(13, 22);
this.rdoGoodreads.Name = "rdoGoodreads";
this.rdoGoodreads.Size = new System.Drawing.Size(82, 19);
@@ -392,7 +393,7 @@ private void InitializeComponent()
this.txtXMLFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.txtXMLFile.Enabled = false;
- this.txtXMLFile.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtXMLFile.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.txtXMLFile.Location = new System.Drawing.Point(262, 21);
this.txtXMLFile.Name = "txtXMLFile";
this.txtXMLFile.Size = new System.Drawing.Size(678, 23);
@@ -593,28 +594,28 @@ private void InitializeComponent()
// tmiAuthorProfile
//
this.tmiAuthorProfile.Name = "tmiAuthorProfile";
- this.tmiAuthorProfile.Size = new System.Drawing.Size(180, 22);
+ this.tmiAuthorProfile.Size = new System.Drawing.Size(148, 22);
this.tmiAuthorProfile.Text = "Author Profile";
this.tmiAuthorProfile.Click += new System.EventHandler(this.tmiAuthorProfile_Click);
//
// tmiEndAction
//
this.tmiEndAction.Name = "tmiEndAction";
- this.tmiEndAction.Size = new System.Drawing.Size(180, 22);
+ this.tmiEndAction.Size = new System.Drawing.Size(148, 22);
this.tmiEndAction.Text = "End Actions";
this.tmiEndAction.Click += new System.EventHandler(this.tmiEndAction_Click);
//
// tmiStartAction
//
this.tmiStartAction.Name = "tmiStartAction";
- this.tmiStartAction.Size = new System.Drawing.Size(180, 22);
+ this.tmiStartAction.Size = new System.Drawing.Size(148, 22);
this.tmiStartAction.Text = "Start Actions";
this.tmiStartAction.Click += new System.EventHandler(this.tmiStartAction_Click);
//
// tmiXray
//
this.tmiXray.Name = "tmiXray";
- this.tmiXray.Size = new System.Drawing.Size(180, 22);
+ this.tmiXray.Size = new System.Drawing.Size(148, 22);
this.tmiXray.Text = "X-Ray";
this.tmiXray.Click += new System.EventHandler(this.tmiXray_Click);
//
@@ -726,6 +727,7 @@ private void InitializeComponent()
this.btnHelp.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.btnViewHelp,
this.btnVisitForum,
+ this.btnVisitGithub,
this.toolStripSeparator6,
this.btnAbout});
this.btnHelp.Image = ((System.Drawing.Image)(resources.GetObject("btnHelp.Image")));
@@ -769,7 +771,7 @@ private void InitializeComponent()
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.Enabled = false;
- this.btnCancel.Font = new System.Drawing.Font("Segoe UI", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.btnCancel.Font = new System.Drawing.Font("Segoe UI", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.btnCancel.Location = new System.Drawing.Point(774, 661);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(192, 23);
@@ -783,7 +785,7 @@ private void InitializeComponent()
this.txtDatasource.ActiveLinkColor = System.Drawing.Color.MediumBlue;
this.txtDatasource.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.txtDatasource.AutoSize = true;
- this.txtDatasource.Font = new System.Drawing.Font("Segoe UI", 7.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtDatasource.Font = new System.Drawing.Font("Segoe UI", 7.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.txtDatasource.LinkBehavior = System.Windows.Forms.LinkBehavior.NeverUnderline;
this.txtDatasource.LinkColor = System.Drawing.Color.MediumBlue;
this.txtDatasource.Location = new System.Drawing.Point(851, 621);
@@ -799,7 +801,7 @@ private void InitializeComponent()
//
this.lblDatasource.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.lblDatasource.AutoSize = true;
- this.lblDatasource.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblDatasource.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this.lblDatasource.Location = new System.Drawing.Point(771, 621);
this.lblDatasource.Name = "lblDatasource";
this.lblDatasource.Size = new System.Drawing.Size(68, 13);
@@ -876,6 +878,13 @@ private void InitializeComponent()
this.pbCover.MouseEnter += new System.EventHandler(this.pbCover_MouseEnter);
this.pbCover.MouseLeave += new System.EventHandler(this.pbCover_MouseLeave);
//
+ // btnVisitGithub
+ //
+ this.btnVisitGithub.Name = "btnVisitGithub";
+ this.btnVisitGithub.Size = new System.Drawing.Size(180, 22);
+ this.btnVisitGithub.Text = "Visit Github";
+ this.btnVisitGithub.Click += new System.EventHandler(this.btnVisitGithub_Click);
+ //
// frmMain
//
this.AllowDrop = true;
@@ -904,7 +913,7 @@ private void InitializeComponent()
this.Controls.Add(this.pbCover);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.prgBar);
- this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
@@ -1003,6 +1012,7 @@ private void InitializeComponent()
private System.Windows.Forms.ToolStripMenuItem btnViewHelp;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator6;
private System.Windows.Forms.ToolStripMenuItem btnAbout;
+ private System.Windows.Forms.ToolStripMenuItem btnVisitGithub;
}
}

diff:
@@ -807,6 +807,7 @@ private void SetDatasourceLink(string url)
if (string.IsNullOrEmpty(url))
{
txtDatasource.Text = "Search datasource...";
+ _tooltip.SetToolTip(txtDatasource, null);
return;
}
@@ -898,6 +899,7 @@ private void ClearInterface(bool bookOpened)
txtXMLFile.Text = "";
// todo another copy
txtDatasource.Text = "Search datasource...";
+ _tooltip.SetToolTip(txtDatasource, null);
prgBar.Value = 0;
_openedMetadata = null;
CheckFiles("","","","","","");
@@ -1027,7 +1029,7 @@ private void btnUnpack_Click(object sender, EventArgs e)
private void txtOutput_LinkClicked(object sender, LinkClickedEventArgs e)
{
- Process.Start(e.LinkText);
+ Process.Start(new ProcessStartInfo(e.LinkText) { UseShellExecute = true });
}
private async void btnOneClick_Click(object sender, EventArgs e)
@@ -1042,7 +1044,7 @@ private void txtAsin_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
if (string.Equals(txtAsin.Text, "ASIN"))
return;
- Process.Start(_amazonClient.Url(_settings.amazonTLD, txtAsin.Text));
+ Process.Start(new ProcessStartInfo(_amazonClient.Url(_settings.amazonTLD, txtAsin.Text)) { UseShellExecute = true });
}
private void btnExtractTerms_Click(object sender, EventArgs e)
@@ -1207,11 +1209,16 @@ private void clearAndSaveToolStripMenuItem_Click(object sender, EventArgs e)
txtOutput.Text = string.Empty;
}
- private void txtDatasource_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
+ private async void txtDatasource_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
- if (txtDatasource.Text.Contains("Search"))
- return;
- Process.Start(txtGoodreads.Text);
+ if (!Regex.IsMatch(txtDatasource.Text, "\\d+"))
+ {
+ ToggleInterface(false);
+ await btnSearchGoodreads_Run();
+ ToggleInterface(true);
+ }
+ else
+ Process.Start(new ProcessStartInfo(txtGoodreads.Text) { UseShellExecute = true });
}
private void txtGoodreads_TextChanged(object sender, EventArgs e)
@@ -1246,7 +1253,8 @@ private void btnViewHelp_Click(object sender, EventArgs e)
{
try
{
- Process.Start(Environment.CurrentDirectory + @"\doc\help.pdf");
+ Process.Start(new ProcessStartInfo(Environment.CurrentDirectory + @"\doc\help.pdf") { UseShellExecute = true });
+
}
catch
{
@@ -1256,12 +1264,17 @@ private void btnViewHelp_Click(object sender, EventArgs e)
private void btnVisitForum_Click(object sender, EventArgs e)
{
- Process.Start("http://www.mobileread.com/forums/showthread.php?t=245754");
+ Process.Start(new ProcessStartInfo("http://www.mobileread.com/forums/showthread.php?t=245754") { UseShellExecute = true });
}
private void btnAbout_Click(object sender, EventArgs e)
{
new frmAbout().ShowDialog();
}
+
+ private void btnVisitGithub_Click(object sender, EventArgs e)
+ {
+ Process.Start(new ProcessStartInfo("https://github.com/Ephemerality/xray-builder.gui") { UseShellExecute = true });
+ }
}
}
\ No newline at end of file


message: Fallback to title and author search if ASIN not found on Goodreads.

diff:
@@ -72,15 +72,20 @@ public override bool IsMatchingUrl(string url)
public override async Task<IEnumerable<BookInfo>> SearchBookAsync(IMetadata metadata, CancellationToken cancellationToken = default)
{
+ var title = Uri.EscapeDataString(metadata.Title);
+ var author = Uri.EscapeDataString(Functions.FixAuthor(metadata.Author));
+
// Try by ASIN first, then fall back on author/title
if (!string.IsNullOrEmpty(metadata.Asin))
{
- return ParseSearchResults(await _httpClient.GetPageAsync(SearchUrlAsin(Uri.EscapeDataString(metadata.Asin)), cancellationToken));
+ _logger.Log($"Searching by ASIN...");
+ var bookList = ParseSearchResults(await _httpClient.GetPageAsync(SearchUrlAsin(Uri.EscapeDataString(metadata.Asin)), cancellationToken));
+ var searchBookAsync = bookList as BookInfo[] ?? bookList.ToArray();
+ if (searchBookAsync.Any())
+ return searchBookAsync;
}
- var title = Uri.EscapeDataString(metadata.Title);
- var author = Uri.EscapeDataString(Functions.FixAuthor(metadata.Author));
-
+ _logger.Log($"Searching by Author and Title...");
return ParseSearchResults(await _httpClient.GetPageAsync(SearchUrl(author, title), cancellationToken));
}


message: Consolidate packages.

diff:
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" />
- <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
+ <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Pluralize.NET" Version="1.0.2" />
<PackageReference Include="SimpleInjector" Version="5.3.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.0" />
- <PackageReference Include="System.Data.SQLite.Core" Version="1.0.113.7" />
+ <PackageReference Include="System.Data.SQLite.Core" Version="1.0.114.2" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
<PackageReference Include="Pluralize.NET.Core" Version="1.0.0" />

diff:
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
- <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
+ <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NSubstitute" Version="4.3.0" />
<PackageReference Include="SimpleInjector" Version="5.3.3" />
- <PackageReference Include="System.Data.SQLite.Core" Version="1.0.113.7" />
+ <PackageReference Include="System.Data.SQLite.Core" Version="1.0.114.2" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
</ItemGroup>

diff:
@@ -19,7 +19,7 @@ Content can be unpacked in the form of RAWML for Mobi/AZW3 and plain text for KF
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" />
- <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
+ <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.0" />
<PackageReference Include="System.Memory" Version="4.5.4" />
</ItemGroup>
@@ -30,6 +30,12 @@ Content can be unpacked in the form of RAWML for Mobi/AZW3 and plain text for KF
</Reference>
</ItemGroup>
+ <ItemGroup>
+ <None Update="Amazon.IonDotnet.Ephemerality.dll">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </None>
+ </ItemGroup>
+
<Choose>
<When Condition="$(TargetFramework.StartsWith('net4'))">
<ItemGroup>

original_message: Consolidate packages.


message:
fix(provider/kubernetes): v2 Fix kubectl label selector flag
Currently we set `-l=` when `labelSelectors` is null or is not empty,
causing all commands that pass a null value for `labelSelectors` to
fail.
The `-l=somelabel` flag should be set if `labelSelectors` is not null
and is not empty.

diff:
@@ -404,7 +404,7 @@ public class KubectlJobExecutor {
command.add(kind.toString());
}
- if (labelSelectors == null || !labelSelectors.isEmpty()) {
+ if (labelSelectors != null && !labelSelectors.isEmpty()) {
command.add("-l=" + labelSelectors);
}

license: Apache License 2.0

repo: spinnaker/clouddriver

original_message:
fix(provider/kubernetes): v2 Fix kubectl label selector flag (#2252)

Currently we set `-l=` when `labelSelectors` is null or is not empty,
causing all commands that pass a null value for `labelSelectors` to
fail.

The `-l=somelabel` flag should be set if `labelSelectors` is not null
and is not empty.


message:
fix(provider/kubernetes): Replace artifact only if target found
Do not attempt artifact replacement if no target was found.
Reading a non-existing path can return an empty ArrayNode object,
in which case we should not attempt any replacement.
Add checks for bound artifacts in artifact replacement tests.

diff:
@@ -153,7 +153,7 @@ public class ArtifactReplacer {
String jsonPath = processPath(replacePath, artifact);
Object get = obj.read(jsonPath);
- if (get == null) {
+ if (get == null || (get instanceof ArrayNode && ((ArrayNode) get).size() == 0)) {
return false;
}

diff:
@@ -76,6 +76,8 @@ spec:
then:
result.manifest.spec.template.spec.containers[0].image == target
+ result.boundArtifacts.size() == 1
+ result.boundArtifacts.contains(artifact) == true
}
void "check that image isn't replaced by the artifact replacer"() {
@@ -91,6 +93,7 @@ spec:
then:
result.manifest.spec.template.spec.containers[0].image == IMAGE
+ result.boundArtifacts.isEmpty() == true
}
void "check that image is found"() {

original_message:
fix(provider/kubernetes): Replace artifact only if target found (#2259)

Do not attempt artifact replacement if no target was found.
Reading a non-existing path can return an empty ArrayNode object,
in which case we should not attempt any replacement.
Add checks for bound artifacts in artifact replacement tests.


message:
feat(provider/kubernetes): v2 Extract artifact name from reference
Allow for for using a regex to set the artifact name after finding
artifacts in a manifest. This is used to add support in orca for
Currently only done for Docker images, but should be possible for
configmaps and secrets as well.

diff:
@@ -42,6 +42,8 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -104,6 +106,7 @@ public class ArtifactReplacer {
.map(s -> Artifact.builder()
.type(r.getType().toString())
.reference(s)
+ .name(r.getNameFromReference(s))
.build()
);
} catch (Exception e) {
@@ -122,6 +125,8 @@ public class ArtifactReplacer {
public static class Replacer {
private final String replacePath;
private final String findPath;
+ private final Pattern namePattern; // the first group should be the artifact name
+
@Getter
private final ArtifactTypes type;
@@ -142,6 +147,19 @@ public class ArtifactReplacer {
return obj.read(findPath);
}
+ String getNameFromReference(String reference) {
+ if (namePattern == null) {
+ return null;
+ }
+
+ Matcher m = namePattern.matcher(reference);
+ if (m.find() && m.groupCount() > 0 && StringUtils.isNotEmpty(m.group(1))) {
+ return m.group(1);
+ } else {
+ return null;
+ }
+ }
+
boolean replaceIfPossible(DocumentContext obj, Artifact artifact) {
if (artifact == null || StringUtils.isEmpty(artifact.getType())) {
throw new IllegalArgumentException("Artifact and artifact type must be set.");

diff:
@@ -19,11 +19,14 @@ package com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.ArtifactReplacer.Replacer;
+import java.util.regex.Pattern;
+
public class ArtifactReplacerFactory {
public static Replacer dockerImageReplacer() {
return Replacer.builder()
.replacePath("$.spec.template.spec.containers.[?( @.image == \"{%name%}\" )].image")
.findPath("$.spec.template.spec.containers.*.image")
+ .namePattern(Pattern.compile("([0-9A-Za-z./]+).*"))
.type(ArtifactTypes.DOCKER_IMAGE)
.build();
}

original_message:
feat(provider/kubernetes): v2 Extract artifact name from reference (#2322)

Allow for for using a regex to set the artifact name after finding
artifacts in a manifest. This is used to add support in orca for
https://github.com/spinnaker/spinnaker/issues/2267

Currently only done for Docker images, but should be possible for
configmaps and secrets as well.