diff --git a/BookmarkManager.csproj b/BookmarkManager.csproj index 5cefae5..f7a4438 100644 --- a/BookmarkManager.csproj +++ b/BookmarkManager.csproj @@ -14,7 +14,7 @@ Ray Lam 1.0.0.0 1.0.0.0 - 0.6.1.0249 + 0.6.2.092 bukkubuddy True 8.0 diff --git a/EditBookmarkForm.cs b/EditBookmarkForm.cs index 08d53f3..19b1e89 100644 --- a/EditBookmarkForm.cs +++ b/EditBookmarkForm.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Drawing; using System.Threading.Tasks; using System.Windows.Forms; +using System.Xml; using BookmarkManager.Services; using bzit.bomg.Models; using RyzStudio.Windows.Forms; @@ -414,7 +415,7 @@ namespace FizzyLauncher { try { - pictureBox1.Image = await _webProvider.RetrieveImage(document); + pictureBox1.Image = await _webProvider.RetrieveImage(url, document); if (pictureBox1.Image != null) { if (pictureBox1.Image.Width > 16) diff --git a/Services/WebProvider.cs b/Services/WebProvider.cs index c592dd3..602364d 100644 --- a/Services/WebProvider.cs +++ b/Services/WebProvider.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Generic; using System.Drawing; +using System.Linq; using System.Net; +using System.Security.Policy; using System.Threading.Tasks; using HtmlAgilityPack; using RyzStudio.Net; @@ -148,7 +151,7 @@ namespace BookmarkManager.Services return result; } - public async Task RetrieveImage(HtmlAgilityPack.HtmlDocument document) + public async Task RetrieveImage(string url, HtmlAgilityPack.HtmlDocument document) { var iconUrl = this.ParseFavicon(document); if (string.IsNullOrWhiteSpace(iconUrl)) @@ -156,6 +159,18 @@ namespace BookmarkManager.Services return null; } + try + { + var baseUri = new Uri(url); + var absoluteUri = new Uri(baseUri, iconUrl); + + iconUrl = absoluteUri.AbsoluteUri; + } + catch + { + return null; + } + return await this.RetrieveImage(iconUrl); } @@ -169,25 +184,25 @@ namespace BookmarkManager.Services return result; } - result = ParseTagValue_Attr(document, "//meta[@property='og:title']", "content", string.Empty)?.Trim(); + result = FindNodeAttrValue(document, "//meta[@property='og:title']", "content", string.Empty)?.Trim(); if (!string.IsNullOrWhiteSpace(result)) { return result; } - result = ParseTagValue_Attr(document, "//meta[@name='twitter:title']", "content", string.Empty)?.Trim(); + result = FindNodeAttrValue(document, "//meta[@name='twitter:title']", "content", string.Empty)?.Trim(); if (!string.IsNullOrWhiteSpace(result)) { return result; } - result = ParseTagValue_Attr(document, "//meta[@property='og:site_name']", "content", string.Empty)?.Trim(); + result = FindNodeAttrValue(document, "//meta[@property='og:site_name']", "content", string.Empty)?.Trim(); if (!string.IsNullOrWhiteSpace(result)) { return result; } - result = ParseTagValue_Attr(document, "//meta[@itemprop='name']", "content", string.Empty)?.Trim(); + result = FindNodeAttrValue(document, "//meta[@itemprop='name']", "content", string.Empty)?.Trim(); if (!string.IsNullOrWhiteSpace(result)) { return result; @@ -200,31 +215,31 @@ namespace BookmarkManager.Services { string result = null; - result = ParseTagValue_Attr(document, "//meta[@name='description']", "content", string.Empty)?.Trim(); + result = FindNodeAttrValue(document, "//meta[@name='description']", "content", string.Empty)?.Trim(); if (!string.IsNullOrWhiteSpace(result)) { return result; } - result = ParseTagValue_Attr(document, "//meta[@property='og:description']", "content", string.Empty)?.Trim(); + result = FindNodeAttrValue(document, "//meta[@property='og:description']", "content", string.Empty)?.Trim(); if (!string.IsNullOrWhiteSpace(result)) { return result; } - result = ParseTagValue_Attr(document, "//meta[@name='twitter:description']", "content", string.Empty)?.Trim(); + result = FindNodeAttrValue(document, "//meta[@name='twitter:description']", "content", string.Empty)?.Trim(); if (!string.IsNullOrWhiteSpace(result)) { return result; } - result = ParseTagValue_Attr(document, "//meta[@property='og:description']", "content", string.Empty)?.Trim(); + result = FindNodeAttrValue(document, "//meta[@property='og:description']", "content", string.Empty)?.Trim(); if (!string.IsNullOrWhiteSpace(result)) { return result; } - result = ParseTagValue_Attr(document, "//meta[@itemprop='description']", "content", string.Empty)?.Trim(); + result = FindNodeAttrValue(document, "//meta[@itemprop='description']", "content", string.Empty)?.Trim(); if (!string.IsNullOrWhiteSpace(result)) { return result; @@ -237,49 +252,85 @@ namespace BookmarkManager.Services { string result = null; - result = ParseTagValue_Attr(document, "//link[translate(@rel, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'shortcut icon']", "href", string.Empty)?.Trim(); + //var tt1 = FindNode_AtrributeContains(document, "//link[contains(@rel, 'icon')]", "href", string.Empty); + //var tt1 = FindNode_AtrributeContains(document, "link", "rel", "icon"); + + // Find link-rel contains "icon" + var linkNodes = FindNode(document, "link", "rel"); + foreach (var item in linkNodes) + { + var relValue = item.Attributes["rel"].Value?.Trim() ?? string.Empty; + if (!ContainsWord(relValue, "icon")) + { + continue; + } + + var hrefValue = item.Attributes["href"].Value?.Trim() ?? string.Empty; + if (string.IsNullOrWhiteSpace(hrefValue)) + { + continue; + } + + return System.Web.HttpUtility.HtmlDecode(hrefValue); + } + + // Find link-rel contains apple-icon + var appleIconPatterns = new List() { "apple-touch-icon", "apple-touch-icon-precomposed" }; + + foreach (var item in linkNodes) + { + var relValue = item.Attributes["rel"].Value?.Trim() ?? string.Empty; + if (!appleIconPatterns.Contains(relValue?.ToLower() ?? string.Empty)) + { + continue; + } + + var hrefValue = item.Attributes["href"].Value?.Trim() ?? string.Empty; + if (string.IsNullOrWhiteSpace(hrefValue)) + { + continue; + } + + return System.Web.HttpUtility.HtmlDecode(hrefValue); + } + + //result = ParseTagValue_Attr(document, "//link[translate(@rel, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'shortcut icon']", "href", string.Empty)?.Trim(); + //if (!string.IsNullOrWhiteSpace(result)) + //{ + // return result; + //} + + //result = ParseTagValue_Attr(document, "//link[translate(@rel, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'icon']", "href", string.Empty)?.Trim(); + //if (!string.IsNullOrWhiteSpace(result)) + //{ + // return result; + //} + + //result = ParseTagValue_Attr(document, "//link[translate(@rel, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'apple-touch-icon']", "href", string.Empty)?.Trim(); + //if (!string.IsNullOrWhiteSpace(result)) + //{ + // return result; + //} + + //result = ParseTagValue_Attr(document, "//link[translate(@rel, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'apple-touch-icon-precomposed']", "href", string.Empty)?.Trim(); + //if (!string.IsNullOrWhiteSpace(result)) + //{ + // return result; + //} + + result = FindNodeAttrValue(document, "//meta[@property='og:image']", "content", string.Empty)?.Trim(); if (!string.IsNullOrWhiteSpace(result)) { return result; } - result = ParseTagValue_Attr(document, "//link[translate(@rel, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'icon']", "href", string.Empty)?.Trim(); + result = FindNodeAttrValue(document, "//meta[@name='twitter:image']", "content", string.Empty)?.Trim(); if (!string.IsNullOrWhiteSpace(result)) { return result; } - result = ParseTagValue_Attr(document, "//link[translate(@rel, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'apple-touch-icon']", "href", string.Empty)?.Trim(); - if (!string.IsNullOrWhiteSpace(result)) - { - return result; - } - - result = ParseTagValue_Attr(document, "//link[translate(@rel, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'apple-touch-icon-precomposed']", "href", string.Empty)?.Trim(); - if (!string.IsNullOrWhiteSpace(result)) - { - return result; - } - - result = ParseTagValue_Attr(document, "//meta[translate(@property, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'og:image']", "content", string.Empty)?.Trim(); - if (!string.IsNullOrWhiteSpace(result)) - { - return result; - } - - result = ParseTagValue_Attr(document, "//meta[translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'twitter:image']", "content", string.Empty)?.Trim(); - if (!string.IsNullOrWhiteSpace(result)) - { - return result; - } - - result = ParseTagValue_Attr(document, "//meta[translate(@property, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'og:image']", "content", string.Empty)?.Trim(); - if (!string.IsNullOrWhiteSpace(result)) - { - return result; - } - - result = ParseTagValue_Attr(document, "//meta[translate(@itemprop, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'image']", "content", string.Empty)?.Trim(); + result = FindNodeAttrValue(document, "//meta[@itemprop='image']", "content", string.Empty)?.Trim(); if (!string.IsNullOrWhiteSpace(result)) { return result; @@ -320,7 +371,7 @@ namespace BookmarkManager.Services return defaultValue; } - private string ParseTagValue_Attr(HtmlAgilityPack.HtmlDocument document, string xPath, string attr, string defaultValue = "") + private string FindNodeAttrValue(HtmlAgilityPack.HtmlDocument document, string xPath, string attr, string defaultValue = "") { var hnc = document.DocumentNode.SelectNodes(xPath); if (hnc == null) @@ -351,5 +402,82 @@ namespace BookmarkManager.Services return defaultValue; } + //private List FindNode_AtrributeContains(HtmlAgilityPack.HtmlDocument document, string nodeName, string attrName, string findValue) + //{ + // var response = new List(); + + // var xPath = $"//{nodeName}[@{attrName}]"; + // var hnc = document.DocumentNode.SelectNodes(xPath); + // if (hnc == null) + // { + // return response; + // } + + // if (hnc.Count <= 0) + // { + // return response; + // } + + // foreach (HtmlNode item in hnc) + // { + // if (!item.Attributes.Contains(attrName)) + // { + // continue; + // } + + // if (!ContainsWord(item.Attributes[attrName].Value ?? string.Empty, findValue)) + // { + // continue; + // } + + // response.Add(item); + // } + + // return response; + //} + + private List FindNode(HtmlAgilityPack.HtmlDocument document, string nodeName, string attrName) + { + var xPath = (string.IsNullOrWhiteSpace(attrName) ? $"//{nodeName}" : $"//{nodeName}[@{attrName}]"); + var hnc = document.DocumentNode.SelectNodes(xPath); + if (hnc == null) + { + return new List(); + } + + if (hnc.Count <= 0) + { + return new List(); + } + + return hnc.ToList(); + } + + private bool ContainsWord(string haystack, string needle) + { + haystack = haystack?.Trim() ?? string.Empty; + + if (!haystack.Contains(" ")) + { + return haystack.Equals(needle, StringComparison.CurrentCultureIgnoreCase); + } + + foreach (var item in haystack.Split(" ")) + { + if (string.IsNullOrWhiteSpace(item)) + { + continue; + } + + if (item.Equals(needle, StringComparison.CurrentCultureIgnoreCase)) + { + return true; + } + } + + return false; + } + + } } \ No newline at end of file diff --git a/UpdateIconsForm.cs b/UpdateIconsForm.cs index 91477cc..db85498 100644 --- a/UpdateIconsForm.cs +++ b/UpdateIconsForm.cs @@ -360,7 +360,7 @@ namespace FizzyLauncher try { - var image = await _webProvider.RetrieveImage(document); + var image = await _webProvider.RetrieveImage(item.Value.Address, document); if (image != null) { if (image.Width > 16) diff --git a/build-installer.iss b/build-installer.iss index ce48110..341125e 100644 --- a/build-installer.iss +++ b/build-installer.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "BukkuBuddy Bookmark Manager" -#define MyAppVersion "0.6.0.716" +#define MyAppVersion "0.6.2.092" #define MyAppPublisher "Hi, I'm Ray" #define MyAppURL "https://www.hiimray.co.uk/software-bookmark-manager" #define MyAppExeName "bukkubuddy.exe"