Parse Open Graph images when parsing metadata from an HTML page.

This commit is contained in:
Brent Simmons 2017-11-26 11:38:03 -08:00
parent e08acc9837
commit ff7695c290
9 changed files with 2644 additions and 53 deletions

View File

@ -10,19 +10,22 @@
@class RSHTMLMetadataFeedLink;
@class RSHTMLMetadataAppleTouchIcon;
@class RSHTMLOpenGraphProperties;
@class RSHTMLOpenGraphImage;
@class RSHTMLTag;
@interface RSHTMLMetadata : NSObject
- (instancetype)initWithURLString:(NSString *)urlString dictionaries:(NSArray <NSDictionary *> *)dictionaries;
- (instancetype)initWithURLString:(NSString *)urlString tags:(NSArray <RSHTMLTag *> *)tags;
@property (nonatomic, readonly) NSString *baseURLString;
@property (nonatomic, readonly) NSArray <NSDictionary *> *dictionaries;
@property (nonatomic, readonly) NSArray <RSHTMLTag *> *tags;
@property (nonatomic, readonly) NSString *faviconLink;
@property (nonatomic, readonly) NSArray <RSHTMLMetadataAppleTouchIcon *> *appleTouchIcons;
@property (nonatomic, readonly) NSArray <RSHTMLMetadataFeedLink *> *feedLinks;
@property (nonatomic, readonly) RSHTMLOpenGraphProperties *openGraphProperties;
@end
@ -43,3 +46,24 @@
@end
@interface RSHTMLOpenGraphProperties : NSObject
// TODO: the rest. At this writing (Nov. 26, 2017) I just care about og:image.
// See http://ogp.me/
- (instancetype)initWithURLString:(NSString *)urlString tags:(NSArray <RSHTMLTag *> *)tags;
@property (nonatomic, readonly) NSArray <RSHTMLOpenGraphImage *> *images;
@end
@interface RSHTMLOpenGraphImage : NSObject
@property (nonatomic, readonly) NSString *url;
@property (nonatomic, readonly) NSString *secureURL;
@property (nonatomic, readonly) NSString *mimeType;
@property (nonatomic, readonly) CGFloat width;
@property (nonatomic, readonly) CGFloat height;
@property (nonatomic, readonly) NSString *altText;
@end

View File

@ -8,11 +8,12 @@
#import <RSParser/RSHTMLMetadata.h>
#import <RSParser/RSParserInternal.h>
#import <RSParser/RSHTMLTag.h>
static NSString *urlStringFromDictionary(NSDictionary *d);
static NSString *absoluteURLStringWithRelativeURLString(NSString *relativeURLString, NSString *baseURLString);
static NSString *absoluteURLStringWithDictionary(NSDictionary *d, NSString *baseURLString);
static NSArray *objectsOfClassWithDictionaries(Class class, NSArray *dictionaries, NSString *baseURLString);
static NSArray *objectsOfClassWithTags(Class class, NSArray *tags, NSString *baseURLString);
static NSString *relValue(NSDictionary *d);
static BOOL typeIsFeedType(NSString *type);
@ -33,24 +34,23 @@ static NSString *kTypeKey = @"type";
@interface RSHTMLMetadataAppleTouchIcon ()
- (instancetype)initWithDictionary:(NSDictionary *)d baseURLString:(NSString *)baseURLString;
- (instancetype)initWithTag:(RSHTMLTag *)tag baseURLString:(NSString *)baseURLString;
@end
@interface RSHTMLMetadataFeedLink ()
- (instancetype)initWithDictionary:(NSDictionary *)d baseURLString:(NSString *)baseURLString;
- (instancetype)initWithTag:(RSHTMLTag *)tag baseURLString:(NSString *)baseURLString;
@end
@implementation RSHTMLMetadata
#pragma mark - Init
- (instancetype)initWithURLString:(NSString *)urlString dictionaries:(NSArray <NSDictionary *> *)dictionaries {
- (instancetype)initWithURLString:(NSString *)urlString tags:(NSArray <RSHTMLTag *> *)tags {
self = [super init];
if (!self) {
@ -58,17 +58,19 @@ static NSString *kTypeKey = @"type";
}
_baseURLString = urlString;
_dictionaries = dictionaries;
_faviconLink = [self resolvedLinkFromFirstDictionaryWithMatchingRel:kShortcutIconRelValue];
_tags = tags;
_faviconLink = [self resolvedLinkFromFirstLinkTagWithMatchingRel:kShortcutIconRelValue];
if (_faviconLink == nil) {
_faviconLink = [self resolvedLinkFromFirstDictionaryWithMatchingRel:kIconRelValue];
_faviconLink = [self resolvedLinkFromFirstLinkTagWithMatchingRel:kIconRelValue];
}
NSArray *appleTouchIconDictionaries = [self appleTouchIconDictionaries];
_appleTouchIcons = objectsOfClassWithDictionaries([RSHTMLMetadataAppleTouchIcon class], appleTouchIconDictionaries, urlString);
NSArray *appleTouchIconTags = [self appleTouchIconTags];
_appleTouchIcons = objectsOfClassWithTags([RSHTMLMetadataAppleTouchIcon class], appleTouchIconTags, urlString);
NSArray *feedLinkDictionaries = [self feedLinkDictionaries];
_feedLinks = objectsOfClassWithDictionaries([RSHTMLMetadataFeedLink class], feedLinkDictionaries, urlString);
NSArray *feedLinkTags = [self feedLinkTags];
_feedLinks = objectsOfClassWithTags([RSHTMLMetadataFeedLink class], feedLinkTags, urlString);
_openGraphProperties = [[RSHTMLOpenGraphProperties alloc] initWithURLString:urlString tags:tags];
return self;
}
@ -76,15 +78,18 @@ static NSString *kTypeKey = @"type";
#pragma mark - Private
- (NSDictionary *)firstDictionaryWithMatchingRel:(NSString *)valueToMatch {
- (RSHTMLTag *)firstLinkTagWithMatchingRel:(NSString *)valueToMatch {
// Case-insensitive.
for (NSDictionary *oneDictionary in self.dictionaries) {
for (RSHTMLTag *tag in self.tags) {
NSString *oneRelValue = relValue(oneDictionary);
if (tag.type != RSHTMLTagTypeLink) {
continue;
}
NSString *oneRelValue = relValue(tag.attributes);
if (oneRelValue && [oneRelValue compare:valueToMatch options:NSCaseInsensitiveSearch] == NSOrderedSame) {
return oneDictionary;
return tag;
}
}
@ -92,28 +97,36 @@ static NSString *kTypeKey = @"type";
}
- (NSArray *)appleTouchIconDictionaries {
- (NSArray *)appleTouchIconTags {
NSMutableArray *dictionaries = [NSMutableArray new];
NSMutableArray *tags = [NSMutableArray new];
for (NSDictionary *oneDictionary in self.dictionaries) {
for (RSHTMLTag *tag in self.tags) {
NSString *oneRelValue = relValue(oneDictionary).lowercaseString;
if (tag.type != RSHTMLTagTypeLink) {
continue;
}
NSString *oneRelValue = relValue(tag.attributes).lowercaseString;
if ([oneRelValue isEqualToString:kAppleTouchIconValue] || [oneRelValue isEqualToString:kAppleTouchIconPrecomposedValue]) {
[dictionaries addObject:oneDictionary];
[tags addObject:tag];
}
}
return dictionaries;
return tags;
}
- (NSArray *)feedLinkDictionaries {
- (NSArray *)feedLinkTags {
NSMutableArray *dictionaries = [NSMutableArray new];
NSMutableArray *tags = [NSMutableArray new];
for (NSDictionary *oneDictionary in self.dictionaries) {
for (RSHTMLTag *tag in self.tags) {
if (tag.type != RSHTMLTagTypeLink) {
continue;
}
NSDictionary *oneDictionary = tag.attributes;
NSString *oneRelValue = relValue(oneDictionary).lowercaseString;
if (![oneRelValue isEqualToString:kAlternateKey]) {
continue;
@ -128,20 +141,19 @@ static NSString *kTypeKey = @"type";
continue;
}
[dictionaries addObject:oneDictionary];
[tags addObject:tag];
}
return dictionaries;
return tags;
}
- (NSString *)resolvedLinkFromFirstDictionaryWithMatchingRel:(NSString *)relValue {
- (NSString *)resolvedLinkFromFirstLinkTagWithMatchingRel:(NSString *)relValue {
NSDictionary *d = [self firstDictionaryWithMatchingRel:relValue];
return absoluteURLStringWithDictionary(d, self.baseURLString);
RSHTMLTag *tag = [self firstLinkTagWithMatchingRel:relValue];
return absoluteURLStringWithDictionary(tag.attributes, self.baseURLString);
}
@end
@ -184,19 +196,19 @@ static NSString *absoluteURLStringWithDictionary(NSDictionary *d, NSString *base
}
static NSArray *objectsOfClassWithDictionaries(Class class, NSArray *dictionaries, NSString *baseURLString) {
static NSArray *objectsOfClassWithTags(Class class, NSArray *tags, NSString *baseURLString) {
NSMutableArray *objects = [NSMutableArray new];
for (NSDictionary *oneDictionary in dictionaries) {
for (RSHTMLTag *tag in tags) {
id oneObject = [[class alloc] initWithDictionary:oneDictionary baseURLString:baseURLString];
id oneObject = [[class alloc] initWithTag:tag baseURLString:baseURLString];
if (oneObject) {
[objects addObject:oneObject];
}
}
return [objects copy];
return objects;
}
@ -209,14 +221,14 @@ static BOOL typeIsFeedType(NSString *type) {
@implementation RSHTMLMetadataAppleTouchIcon
- (instancetype)initWithDictionary:(NSDictionary *)d baseURLString:(NSString *)baseURLString {
- (instancetype)initWithTag:(RSHTMLTag *)tag baseURLString:(NSString *)baseURLString {
self = [super init];
if (!self) {
return nil;
}
NSDictionary *d = tag.attributes;
_urlString = absoluteURLStringWithDictionary(d, baseURLString);
_sizes = [d rsparser_objectForCaseInsensitiveKey:kSizesKey];
_rel = [d rsparser_objectForCaseInsensitiveKey:kRelKey];
@ -224,20 +236,19 @@ static BOOL typeIsFeedType(NSString *type) {
return self;
}
@end
@implementation RSHTMLMetadataFeedLink
- (instancetype)initWithDictionary:(NSDictionary *)d baseURLString:(NSString *)baseURLString {
- (instancetype)initWithTag:(RSHTMLTag *)tag baseURLString:(NSString *)baseURLString {
self = [super init];
if (!self) {
return nil;
}
NSDictionary *d = tag.attributes;
_urlString = absoluteURLStringWithDictionary(d, baseURLString);
_title = [d rsparser_objectForCaseInsensitiveKey:kTitleKey];
_type = [d rsparser_objectForCaseInsensitiveKey:kTypeKey];
@ -245,6 +256,130 @@ static BOOL typeIsFeedType(NSString *type) {
return self;
}
@end
@interface RSHTMLOpenGraphImage ()
@property (nonatomic, readwrite) NSString *url;
@property (nonatomic, readwrite) NSString *secureURL;
@property (nonatomic, readwrite) NSString *mimeType;
@property (nonatomic, readwrite) CGFloat width;
@property (nonatomic, readwrite) CGFloat height;
@property (nonatomic, readwrite) NSString *altText;
@end
@implementation RSHTMLOpenGraphImage
@end
@interface RSHTMLOpenGraphProperties ()
@property (nonatomic) NSMutableArray *ogImages;
@end
@implementation RSHTMLOpenGraphProperties
- (instancetype)initWithURLString:(NSString *)urlString tags:(NSArray <RSHTMLTag *> *)tags {
self = [super init];
if (!self) {
return nil;
}
_ogImages = [NSMutableArray new];
[self parseTags:tags];
return self;
}
- (RSHTMLOpenGraphImage *)currentImage {
return self.ogImages.lastObject;
}
- (RSHTMLOpenGraphImage *)pushImage {
RSHTMLOpenGraphImage *image = [RSHTMLOpenGraphImage new];
[self.ogImages addObject:image];
return image;
}
- (RSHTMLOpenGraphImage *)ensureImage {
RSHTMLOpenGraphImage *image = [self currentImage];
if (image != nil) {
return image;
}
return [self pushImage];
}
- (NSArray *)images {
return self.ogImages;
}
static NSString *ogPrefix = @"og:";
static NSString *ogImage = @"og:image";
static NSString *ogImageURL = @"og:image:url";
static NSString *ogImageSecureURL = @"og:image:secure_url";
static NSString *ogImageType = @"og:image:type";
static NSString *ogImageWidth = @"og:image:width";
static NSString *ogImageHeight = @"og:image:height";
static NSString *ogImageAlt = @"og:image:alt";
static NSString *ogPropertyKey = @"property";
static NSString *ogContentKey = @"content";
- (void)parseTags:(NSArray *)tags {
for (RSHTMLTag *tag in tags) {
if (tag.type != RSHTMLTagTypeMeta) {
continue;
}
NSString *propertyName = tag.attributes[ogPropertyKey];
if (!propertyName || ![propertyName hasPrefix:ogPrefix]) {
continue;
}
NSString *content = tag.attributes[ogContentKey];
if (!content) {
continue;
}
if ([propertyName isEqualToString:ogImage]) {
RSHTMLOpenGraphImage *image = [self currentImage];
if (!image || image.url) { // Most likely case, since og:image will probably appear before other image attributes.
image = [self pushImage];
}
image.url = content;
}
else if ([propertyName isEqualToString:ogImageURL]) {
[self ensureImage].url = content;
}
else if ([propertyName isEqualToString:ogImageSecureURL]) {
[self ensureImage].secureURL = content;
}
else if ([propertyName isEqualToString:ogImageType]) {
[self ensureImage].mimeType = content;
}
else if ([propertyName isEqualToString:ogImageAlt]) {
[self ensureImage].altText = content;
}
else if ([propertyName isEqualToString:ogImageWidth]) {
[self ensureImage].width = [content floatValue];
}
else if ([propertyName isEqualToString:ogImageHeight]) {
[self ensureImage].height = [content floatValue];
}
}
}
@end

View File

@ -13,13 +13,13 @@
#import <RSParser/RSSAXParser.h>
#import <RSParser/RSParserInternal.h>
#import <RSParser/ParserData.h>
#import <RSParser/RSHTMLTag.h>
@interface RSHTMLMetadataParser () <RSSAXHTMLParserDelegate>
@property (nonatomic, readonly) ParserData *parserData;
@property (nonatomic, readwrite) RSHTMLMetadata *metadata;
@property (nonatomic) NSMutableArray *dictionaries;
@property (nonatomic) NSMutableArray *tags;
@property (nonatomic) BOOL didFinishParsing;
@end
@ -50,7 +50,7 @@
}
_parserData = parserData;
_dictionaries = [NSMutableArray new];
_tags = [NSMutableArray new];
[self parse];
@ -66,7 +66,7 @@
[parser parseData:self.parserData.data];
[parser finishParsing];
self.metadata = [[RSHTMLMetadata alloc] initWithURLString:self.parserData.url dictionaries:[self.dictionaries copy]];
self.metadata = [[RSHTMLMetadata alloc] initWithURLString:self.parserData.url tags:self.tags];
}
@ -84,7 +84,6 @@ static NSString *kRelKey = @"rel";
return [d rsparser_objectForCaseInsensitiveKey:kSrcKey];
}
- (void)handleLinkAttributes:(NSDictionary *)d {
if (RSParserStringIsEmpty([d rsparser_objectForCaseInsensitiveKey:kRelKey])) {
@ -94,9 +93,15 @@ static NSString *kRelKey = @"rel";
return;
}
[self.dictionaries addObject:d];
RSHTMLTag *tag = [RSHTMLTag linkTagWithAttributes:d];
[self.tags addObject:tag];
}
- (void)handleMetaAttributes:(NSDictionary *)d {
RSHTMLTag *tag = [RSHTMLTag metaTagWithAttributes:d];
[self.tags addObject:tag];
}
#pragma mark - RSSAXHTMLParserDelegate
@ -104,6 +109,8 @@ static const char *kBody = "body";
static const NSInteger kBodyLength = 5;
static const char *kLink = "link";
static const NSInteger kLinkLength = 5;
static const char *kMeta = "meta";
static const NSInteger kMetaLength = 5;
- (void)saxParser:(RSSAXHTMLParser *)SAXParser XMLStartElement:(const xmlChar *)localName attributes:(const xmlChar **)attributes {
@ -116,13 +123,19 @@ static const NSInteger kLinkLength = 5;
return;
}
if (!RSSAXEqualTags(localName, kLink, kLinkLength)) {
if (RSSAXEqualTags(localName, kLink, kLinkLength)) {
NSDictionary *d = [SAXParser attributesDictionary:attributes];
if (!RSParserObjectIsEmpty(d)) {
[self handleLinkAttributes:d];
}
return;
}
NSDictionary *d = [SAXParser attributesDictionary:attributes];
if (!RSParserObjectIsEmpty(d)) {
[self handleLinkAttributes:d];
if (RSSAXEqualTags(localName, kMeta, kMetaLength)) {
NSDictionary *d = [SAXParser attributesDictionary:attributes];
if (!RSParserObjectIsEmpty(d)) {
[self handleMetaAttributes:d];
}
}
}

View File

@ -0,0 +1,29 @@
//
// RSHTMLTag.h
// RSParser
//
// Created by Brent Simmons on 11/26/17.
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
//
@import Foundation;
extern NSString *RSHTMLTagNameLink; // @"link"
extern NSString *RSHTMLTagNameMeta; // @"meta"
typedef NS_ENUM(NSInteger, RSHTMLTagType) {
RSHTMLTagTypeLink,
RSHTMLTagTypeMeta
};
@interface RSHTMLTag : NSObject
- (instancetype)initWithType:(RSHTMLTagType)type attributes:(NSDictionary *)attributes;
+ (RSHTMLTag *)linkTagWithAttributes:(NSDictionary *)attributes;
+ (RSHTMLTag *)metaTagWithAttributes:(NSDictionary *)attributes;
@property (nonatomic, readonly) RSHTMLTagType type;
@property (nonatomic, readonly) NSDictionary *attributes;
@end

View File

@ -0,0 +1,39 @@
//
// RSHTMLTag.m
// RSParser
//
// Created by Brent Simmons on 11/26/17.
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
//
#import "RSHTMLTag.h"
NSString *RSHTMLTagNameLink = @"link";
NSString *RSHTMLTagNameMeta = @"meta";
@implementation RSHTMLTag
- (instancetype)initWithType:(RSHTMLTagType)type attributes:(NSDictionary *)attributes {
self = [super init];
if (!self) {
return nil;
}
_type = type;
_attributes = attributes;
return self;
}
+ (RSHTMLTag *)linkTagWithAttributes:(NSDictionary *)attributes {
return [[self alloc] initWithType:RSHTMLTagTypeLink attributes:attributes];
}
+ (RSHTMLTag *)metaTagWithAttributes:(NSDictionary *)attributes {
return [[self alloc] initWithType:RSHTMLTagTypeMeta attributes:attributes];
}
@end

View File

@ -44,6 +44,7 @@
#import <RSParser/RSHTMLMetadata.h>
#import <RSParser/RSHTMLLinkParser.h>
#import <RSParser/RSSAXHTMLParser.h> // For writing your own HTML parser.
#import <RSParser/RSHTMLTag.h>
// Utilities

View File

@ -60,6 +60,9 @@
84469D401EFF29A9004A6B28 /* FeedParserError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84469D3F1EFF29A9004A6B28 /* FeedParserError.swift */; };
84469D421EFF2B2D004A6B28 /* JSONTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84469D411EFF2B2D004A6B28 /* JSONTypes.swift */; };
84469D441F002CEF004A6B28 /* JSONFeedParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84469D431F002CEF004A6B28 /* JSONFeedParser.swift */; };
845213251FCB3C76003B6E93 /* coco.html in Resources */ = {isa = PBXBuildFile; fileRef = 845213241FCB3C75003B6E93 /* coco.html */; };
845213281FCB4042003B6E93 /* RSHTMLTag.h in Headers */ = {isa = PBXBuildFile; fileRef = 845213261FCB4042003B6E93 /* RSHTMLTag.h */; settings = {ATTRIBUTES = (Public, ); }; };
845213291FCB4042003B6E93 /* RSHTMLTag.m in Sources */ = {isa = PBXBuildFile; fileRef = 845213271FCB4042003B6E93 /* RSHTMLTag.m */; };
84628AAD1FCA10AE00566A9B /* allthis.atom in Resources */ = {isa = PBXBuildFile; fileRef = 84628AAC1FCA10AE00566A9B /* allthis.atom */; };
849A03D01F0081EA00122600 /* DaringFireball.html in Resources */ = {isa = PBXBuildFile; fileRef = 849A03C51F0081EA00122600 /* DaringFireball.html */; };
849A03D11F0081EA00122600 /* DaringFireball.rss in Resources */ = {isa = PBXBuildFile; fileRef = 849A03C61F0081EA00122600 /* DaringFireball.rss */; };
@ -157,6 +160,9 @@
84469D3F1EFF29A9004A6B28 /* FeedParserError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedParserError.swift; path = Feeds/FeedParserError.swift; sourceTree = "<group>"; };
84469D411EFF2B2D004A6B28 /* JSONTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONTypes.swift; path = Feeds/JSON/JSONTypes.swift; sourceTree = "<group>"; };
84469D431F002CEF004A6B28 /* JSONFeedParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONFeedParser.swift; path = Feeds/JSON/JSONFeedParser.swift; sourceTree = "<group>"; };
845213241FCB3C75003B6E93 /* coco.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = coco.html; sourceTree = "<group>"; };
845213261FCB4042003B6E93 /* RSHTMLTag.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RSHTMLTag.h; sourceTree = "<group>"; };
845213271FCB4042003B6E93 /* RSHTMLTag.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RSHTMLTag.m; sourceTree = "<group>"; };
84628AAC1FCA10AE00566A9B /* allthis.atom */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = allthis.atom; sourceTree = "<group>"; };
849A03C51F0081EA00122600 /* DaringFireball.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = DaringFireball.html; sourceTree = "<group>"; };
849A03C61F0081EA00122600 /* DaringFireball.rss */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = DaringFireball.rss; sourceTree = "<group>"; };
@ -285,6 +291,8 @@
84469D041EFA307E004A6B28 /* RSHTMLMetadataParser.m */,
84469D051EFA307E004A6B28 /* RSSAXHTMLParser.h */,
84469D061EFA307E004A6B28 /* RSSAXHTMLParser.m */,
845213261FCB4042003B6E93 /* RSHTMLTag.h */,
845213271FCB4042003B6E93 /* RSHTMLTag.m */,
);
path = HTML;
sourceTree = "<group>";
@ -348,6 +356,7 @@
849A03CC1F0081EA00122600 /* OneFootTsunami.atom */,
849A03E71F01F88600122600 /* ScriptingNews.json */,
849A03CD1F0081EA00122600 /* scriptingNews.rss */,
845213241FCB3C75003B6E93 /* coco.html */,
849A03CE1F0081EA00122600 /* sixcolors.html */,
84628AAC1FCA10AE00566A9B /* allthis.atom */,
849A03CF1F0081EA00122600 /* Subs.opml */,
@ -420,6 +429,7 @@
84D81BDC1EFA28E700652332 /* RSParser.h in Headers */,
84469D0B1EFA307E004A6B28 /* RSHTMLMetadataParser.h in Headers */,
84469CFC1EFA3069004A6B28 /* RSSAXParser.h in Headers */,
845213281FCB4042003B6E93 /* RSHTMLTag.h in Headers */,
84E7E69F1F85780D0046719D /* ParserData.h in Headers */,
84469D071EFA307E004A6B28 /* RSHTMLLinkParser.h in Headers */,
84469D0D1EFA307E004A6B28 /* RSSAXHTMLParser.h in Headers */,
@ -522,6 +532,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
845213251FCB3C76003B6E93 /* coco.html in Resources */,
849A03D51F0081EA00122600 /* KatieFloyd.rss in Resources */,
849A03D81F0081EA00122600 /* scriptingNews.rss in Resources */,
840FDCBA1F02186D0041F61B /* DaringFireball.json in Resources */,
@ -569,6 +580,7 @@
84469D0C1EFA307E004A6B28 /* RSHTMLMetadataParser.m in Sources */,
84469D0A1EFA307E004A6B28 /* RSHTMLMetadata.m in Sources */,
84469D171EFA30A2004A6B28 /* NSString+RSParser.m in Sources */,
845213291FCB4042003B6E93 /* RSHTMLTag.m in Sources */,
84469D2C1EFA3134004A6B28 /* RSParsedArticle.m in Sources */,
84285AAA1F006456002E8708 /* RSParsedFeedTransformer.swift in Sources */,
84469D2E1EFA3134004A6B28 /* RSParsedFeed.m in Sources */,

View File

@ -111,4 +111,13 @@ class HTMLMetadataTests: XCTestCase {
let _ = RSHTMLMetadataParser.htmlMetadata(with: d)
}
}
func testCocoOGImage() {
let d = parserData("coco", "html", "https://www.theatlantic.com/entertainment/archive/2017/11/coco-is-among-pixars-best-movies-in-years/546695/")
let metadata = RSHTMLMetadataParser.htmlMetadata(with: d)
let openGraphData = metadata.openGraphProperties!
let image = openGraphData.images.first!
XCTAssert(image.url == "https://cdn.theatlantic.com/assets/media/img/mt/2017/11/1033101_first_full_length_trailer_arrives_pixars_coco/facebook.jpg?1511382177")
}
}

File diff suppressed because one or more lines are too long