diff --git a/PortablePorts/NReadability/MetaExtractor.cs b/PortablePorts/NReadability/MetaExtractor.cs new file mode 100644 index 0000000..3632226 --- /dev/null +++ b/PortablePorts/NReadability/MetaExtractor.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; + +namespace ReadSharp.Ports.NReadability +{ + public class MetaExtractor + { + /// + /// Gets or sets a value indicating whether [has value]. + /// + /// + /// true if [has value]; otherwise, false. + /// + public bool HasValue { get; set; } + + /// + /// Gets or sets the tags. + /// + /// + /// The tags. + /// + public IEnumerable Tags { get; private set; } + + + + /// + /// Initializes a new instance of the class. + /// + /// The document. + public MetaExtractor(XDocument document) + { + var documentRoot = document.Root; + + if (documentRoot == null || documentRoot.Name == null || !"html".Equals(documentRoot.Name.LocalName, StringComparison.OrdinalIgnoreCase)) + { + HasValue = false; + return; + } + + var headElement = documentRoot.GetChildrenByTagName("head").FirstOrDefault(); + + if (headElement == null) + { + HasValue = false; + return; + } + + IEnumerable meta = headElement.GetChildrenByTagName("meta"); + IEnumerable link = headElement.GetChildrenByTagName("link"); + + Tags = meta != null ? meta.Concat(link) : link; + HasValue = Tags != null && Tags.Count() > 0; + } + + + /// + /// Gets the meta description. + /// + /// + public string GetMetaDescription() + { + return SearchCandidates(new Dictionary() + { + { "property|og:description", "content" }, + { "name|description", "content" } + }); + } + + + /// + /// Gets the meta image. + /// + /// + public string GetMetaImage() + { + return SearchCandidates(new Dictionary() + { + { "property|og:image", "content" }, + { "rel|apple-touch-icon", "href" }, + { "rel|apple-touch-icon-precomposed", "href"}, + { "name|msapplication-square310x310logo", "content" }, + { "name|msapplication-square150x150logo", "content" }, + { "name|msapplication-square70x70logo", "content" }, + { "name|msapplication-TileImage", "content" }, + { "rel|image_src", "href" } + }); + } + + + /// + /// Gets the meta favicon. + /// + /// + public string GetMetaFavicon() + { + return SearchCandidates(new Dictionary() + { + { "rel|icon", "href" }, + { "rel|shortcut icon", "href" } + }); + } + + + /// + /// Searches the candidates. + /// + /// The candidates. + /// + private string SearchCandidates(Dictionary candidates) + { + string result = null; + + foreach (var candidate in candidates) + { + string[] type = candidate.Key.Split('|'); + + XElement element = Tags + .Where(item => String.Equals(item.GetAttributeValue(type[0], null), type[1], StringComparison.OrdinalIgnoreCase)) + .FirstOrDefault(); + + if (element != null) + { + result = element.GetAttributeValue(candidate.Value, ""); + } + + if (result != null && result.Length > 1) + { + break; + } + } + + return result; + } + } +} diff --git a/PortablePorts/NReadability/NReadability.csproj b/PortablePorts/NReadability/NReadability.csproj index 026ac77..155b3d1 100644 --- a/PortablePorts/NReadability/NReadability.csproj +++ b/PortablePorts/NReadability/NReadability.csproj @@ -49,6 +49,7 @@ + diff --git a/PortablePorts/NReadability/NReadabilityTranscoder.cs b/PortablePorts/NReadability/NReadabilityTranscoder.cs index 1328dd9..a04a70d 100644 --- a/PortablePorts/NReadability/NReadabilityTranscoder.cs +++ b/PortablePorts/NReadability/NReadabilityTranscoder.cs @@ -159,6 +159,7 @@ namespace ReadSharp.Ports.NReadability private Func _imageSourceTranformer; private Func _anchorHrefTransformer; + private Func _metaTransformer; #endregion @@ -249,11 +250,38 @@ namespace ReadSharp.Ports.NReadability bool titleExtracted = !string.IsNullOrEmpty(extractedTitle); + MetaExtractor metaExtractor = new MetaExtractor(transcodedXmlDocument); + + string description = null; + Uri image = null; + Uri favicon = null; + + if (metaExtractor.HasValue) + { + description = metaExtractor.GetMetaDescription(); + string imageString = metaExtractor.GetMetaImage(); + string faviconString = metaExtractor.GetMetaFavicon(); + + if (imageString != null) + { + imageString = ResolveElementUrl(imageString, transcodingInput.Url); + Uri.TryCreate(imageString, UriKind.Absolute, out image); + } + if (faviconString != null) + { + faviconString = ResolveElementUrl(faviconString, transcodingInput.Url); + Uri.TryCreate(faviconString, UriKind.Absolute, out favicon); + } + } + return new TranscodingResult(contentExtracted, titleExtracted) { ExtractedContent = transcodedContent, ExtractedTitle = extractedTitle, + ExtractedDescription = description, + ExtractedFavicon = favicon, + ExtractedImage = image, NextPageUrl = nextPageUrl, Images = images }; @@ -1835,6 +1863,13 @@ namespace ReadSharp.Ports.NReadability set { _anchorHrefTransformer = value; } } + + public Func MetaTranformer + { + get { return _metaTransformer; } + set { _metaTransformer = value; } + } + #endregion } } diff --git a/PortablePorts/NReadability/TranscodingResult.cs b/PortablePorts/NReadability/TranscodingResult.cs index 49c07d9..a6bfff6 100644 --- a/PortablePorts/NReadability/TranscodingResult.cs +++ b/PortablePorts/NReadability/TranscodingResult.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Xml.Linq; @@ -21,6 +22,12 @@ namespace ReadSharp.Ports.NReadability public string NextPageUrl { get; set; } + public string ExtractedDescription { get; set; } + + public Uri ExtractedImage { get; set; } + + public Uri ExtractedFavicon { get; set; } + public XDocument RawDocument { get; set; } public IEnumerable Images { get; set; } diff --git a/ReadSharp/Models/Article.cs b/ReadSharp/Models/Article.cs index 5a7ba7e..5bd788d 100644 --- a/ReadSharp/Models/Article.cs +++ b/ReadSharp/Models/Article.cs @@ -8,6 +8,22 @@ namespace ReadSharp /// public class Article { + /// + /// Gets or sets the title. + /// + /// + /// The title. + /// + public string Title { get; set; } + + /// + /// Gets or sets the description. + /// + /// + /// The description. + /// + public string Description { get; set; } + /// /// Gets or sets the content. /// @@ -16,6 +32,22 @@ namespace ReadSharp /// public string Content { get; set; } + /// + /// Gets or sets the front image. + /// + /// + /// The front image. + /// + public Uri FrontImage { get; set; } + + /// + /// Gets or sets the favicon. + /// + /// + /// The favicon. + /// + public Uri Favicon { get; set; } + /// /// Gets or sets the images. /// @@ -24,14 +56,6 @@ namespace ReadSharp /// public List Images { get; set; } - /// - /// Gets or sets the title. - /// - /// - /// The title. - /// - public string Title { get; set; } - /// /// Gets or sets the next page URL. /// diff --git a/ReadSharp/Reader.cs b/ReadSharp/Reader.cs index 806cca4..8eadc50 100644 --- a/ReadSharp/Reader.cs +++ b/ReadSharp/Reader.cs @@ -68,7 +68,7 @@ namespace ReadSharp /// /// An URI to extract the content from. /// if set to true [only body is returned]. - /// if set to true [no headline (h1) is included]. + /// if set to true [no headline (h1) is included in generated HTML]. /// /// An article with extracted content and meta information. /// @@ -124,9 +124,12 @@ namespace ReadSharp // create article return new Article() { - Content = transcodingResult.ExtractedContent, - Images = images, Title = transcodingResult.ExtractedTitle, + Description = transcodingResult.ExtractedDescription, + Content = transcodingResult.ExtractedContent, + FrontImage = transcodingResult.ExtractedImage, + Images = images, + Favicon = transcodingResult.ExtractedFavicon, NextPage = transcodingResult.NextPageUrl != null ? new Uri(transcodingResult.NextPageUrl, UriKind.Absolute) : null }; }