feedjira 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +8 -0
- data/.travis.yml +31 -12
- data/CHANGELOG.md +15 -1
- data/Dangerfile +1 -0
- data/Gemfile +2 -1
- data/Rakefile +6 -1
- data/feedjira.gemspec +16 -14
- data/fixtures/vcr_cassettes/fetch_failure.yml +62 -0
- data/fixtures/vcr_cassettes/parse_error.yml +222 -0
- data/fixtures/vcr_cassettes/success.yml +281 -0
- data/lib/feedjira.rb +9 -0
- data/lib/feedjira/core_ext.rb +3 -3
- data/lib/feedjira/core_ext/date.rb +2 -1
- data/lib/feedjira/core_ext/string.rb +1 -1
- data/lib/feedjira/core_ext/time.rb +19 -16
- data/lib/feedjira/date_time_utilities.rb +24 -0
- data/lib/feedjira/date_time_utilities/date_time_language_parser.rb +22 -0
- data/lib/feedjira/date_time_utilities/date_time_pattern_parser.rb +29 -0
- data/lib/feedjira/feed.rb +27 -18
- data/lib/feedjira/feed_entry_utilities.rb +15 -17
- data/lib/feedjira/feed_utilities.rb +26 -21
- data/lib/feedjira/parser/atom.rb +9 -8
- data/lib/feedjira/parser/atom_entry.rb +10 -13
- data/lib/feedjira/parser/atom_feed_burner.rb +8 -10
- data/lib/feedjira/parser/atom_feed_burner_entry.rb +11 -14
- data/lib/feedjira/parser/atom_youtube.rb +20 -0
- data/lib/feedjira/parser/atom_youtube_entry.rb +29 -0
- data/lib/feedjira/parser/google_docs_atom.rb +6 -6
- data/lib/feedjira/parser/google_docs_atom_entry.rb +11 -11
- data/lib/feedjira/parser/itunes_rss.rb +39 -22
- data/lib/feedjira/parser/itunes_rss_category.rb +38 -0
- data/lib/feedjira/parser/itunes_rss_item.rb +28 -20
- data/lib/feedjira/parser/itunes_rss_owner.rb +3 -4
- data/lib/feedjira/parser/podlove_chapter.rb +20 -0
- data/lib/feedjira/parser/rss.rb +10 -8
- data/lib/feedjira/parser/rss_entry.rb +17 -21
- data/lib/feedjira/parser/rss_feed_burner.rb +4 -6
- data/lib/feedjira/parser/rss_feed_burner_entry.rb +23 -28
- data/lib/feedjira/parser/rss_image.rb +15 -0
- data/lib/feedjira/preprocessor.rb +2 -2
- data/lib/feedjira/version.rb +1 -1
- data/spec/feedjira/date_time_utilities_spec.rb +41 -0
- data/spec/feedjira/feed_entry_utilities_spec.rb +23 -19
- data/spec/feedjira/feed_spec.rb +109 -74
- data/spec/feedjira/feed_utilities_spec.rb +65 -63
- data/spec/feedjira/parser/atom_entry_spec.rb +54 -34
- data/spec/feedjira/parser/atom_feed_burner_entry_spec.rb +27 -20
- data/spec/feedjira/parser/atom_feed_burner_spec.rb +32 -30
- data/spec/feedjira/parser/atom_spec.rb +50 -48
- data/spec/feedjira/parser/atom_youtube_entry_spec.rb +86 -0
- data/spec/feedjira/parser/atom_youtube_spec.rb +43 -0
- data/spec/feedjira/parser/google_docs_atom_entry_spec.rb +5 -4
- data/spec/feedjira/parser/google_docs_atom_spec.rb +6 -6
- data/spec/feedjira/parser/itunes_rss_item_spec.rb +33 -29
- data/spec/feedjira/parser/itunes_rss_owner_spec.rb +10 -9
- data/spec/feedjira/parser/itunes_rss_spec.rb +83 -30
- data/spec/feedjira/parser/podlove_chapter_spec.rb +37 -0
- data/spec/feedjira/parser/rss_entry_spec.rb +50 -33
- data/spec/feedjira/parser/rss_feed_burner_entry_spec.rb +55 -33
- data/spec/feedjira/parser/rss_feed_burner_spec.rb +31 -26
- data/spec/feedjira/parser/rss_spec.rb +56 -24
- data/spec/feedjira/preprocessor_spec.rb +11 -3
- data/spec/sample_feeds.rb +29 -21
- data/spec/sample_feeds/AmazonWebServicesBlog.xml +797 -797
- data/spec/sample_feeds/AtomEscapedHTMLInPreTag.xml +13 -0
- data/spec/sample_feeds/CRE.xml +5849 -0
- data/spec/sample_feeds/FeedBurnerXHTML.xml +400 -400
- data/spec/sample_feeds/ITunesWithSingleQuotedAttributes.xml +67 -0
- data/spec/sample_feeds/PaulDixExplainsNothing.xml +175 -175
- data/spec/sample_feeds/PaulDixExplainsNothingAlternate.xml +175 -175
- data/spec/sample_feeds/PaulDixExplainsNothingFirstEntryContent.xml +16 -16
- data/spec/sample_feeds/PaulDixExplainsNothingWFW.xml +174 -174
- data/spec/sample_feeds/TenderLovemaking.xml +12 -2
- data/spec/sample_feeds/TrotterCashionHome.xml +611 -611
- data/spec/sample_feeds/TypePadNews.xml +368 -368
- data/spec/sample_feeds/itunes.xml +18 -2
- data/spec/sample_feeds/pet_atom.xml +229 -229
- data/spec/sample_feeds/youtube_atom.xml +395 -0
- data/spec/spec_helper.rb +6 -0
- metadata +112 -27
data/lib/feedjira/feed.rb
CHANGED
@@ -5,16 +5,14 @@ module Feedjira
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def self.parse(xml, &block)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
raise NoParserAvailable.new("No valid parser for XML.")
|
12
|
-
end
|
8
|
+
parser = determine_feed_parser_for_xml(xml)
|
9
|
+
raise NoParserAvailable, 'No valid parser for XML.' unless parser
|
10
|
+
parse_with parser, xml, &block
|
13
11
|
end
|
14
12
|
|
15
13
|
def self.determine_feed_parser_for_xml(xml)
|
16
14
|
start_of_doc = xml.slice(0, 2000)
|
17
|
-
feed_classes.detect {|klass| klass.able_to_parse?(start_of_doc)}
|
15
|
+
feed_classes.detect { |klass| klass.able_to_parse?(start_of_doc) }
|
18
16
|
end
|
19
17
|
|
20
18
|
def self.add_feed_class(klass)
|
@@ -25,6 +23,7 @@ module Feedjira
|
|
25
23
|
@feed_classes ||= [
|
26
24
|
Feedjira::Parser::RSSFeedBurner,
|
27
25
|
Feedjira::Parser::GoogleDocsAtom,
|
26
|
+
Feedjira::Parser::AtomYoutube,
|
28
27
|
Feedjira::Parser::AtomFeedBurner,
|
29
28
|
Feedjira::Parser::Atom,
|
30
29
|
Feedjira::Parser::ITunesRSS,
|
@@ -53,10 +52,14 @@ module Feedjira
|
|
53
52
|
end
|
54
53
|
|
55
54
|
def self.call_on_each_feed_entry(method, *parameters)
|
56
|
-
feed_classes.each do |
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
feed_classes.each do |klass|
|
56
|
+
klass.sax_config.collection_elements.each_value do |value|
|
57
|
+
collection_configs = value.select do |v|
|
58
|
+
v.accessor == 'entries' && v.data_class.class == Class
|
59
|
+
end
|
60
|
+
|
61
|
+
collection_configs.each do |config|
|
62
|
+
config.data_class.send(method, *parameters)
|
60
63
|
end
|
61
64
|
end
|
62
65
|
end
|
@@ -64,15 +67,14 @@ module Feedjira
|
|
64
67
|
|
65
68
|
def self.fetch_and_parse(url)
|
66
69
|
response = connection(url).get
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
feed = parse_with parser_klass, xml
|
70
|
+
unless response.success?
|
71
|
+
raise FetchFailure, "Fetch failed - #{response.status}"
|
72
|
+
end
|
73
|
+
feed = parse response.body
|
73
74
|
feed.feed_url = url
|
74
|
-
feed.etag = response.headers['etag'].to_s.
|
75
|
-
|
75
|
+
feed.etag = response.headers['etag'].to_s.delete '"'
|
76
|
+
|
77
|
+
feed.last_modified = parse_last_modified(response)
|
76
78
|
feed
|
77
79
|
end
|
78
80
|
|
@@ -82,5 +84,12 @@ module Feedjira
|
|
82
84
|
conn.adapter :net_http
|
83
85
|
end
|
84
86
|
end
|
87
|
+
|
88
|
+
def self.parse_last_modified(response)
|
89
|
+
DateTime.parse(response.headers['last-modified']).to_time
|
90
|
+
rescue
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
private_class_method :parse_last_modified
|
85
94
|
end
|
86
95
|
end
|
@@ -1,23 +1,22 @@
|
|
1
1
|
module Feedjira
|
2
2
|
module FeedEntryUtilities
|
3
|
-
|
4
3
|
include Enumerable
|
4
|
+
include DateTimeUtilities
|
5
5
|
|
6
6
|
def published
|
7
7
|
@published ||= @updated
|
8
8
|
end
|
9
9
|
|
10
10
|
def parse_datetime(string)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
nil
|
16
|
-
end
|
11
|
+
DateTime.parse(string).feed_utils_to_gm_time
|
12
|
+
rescue
|
13
|
+
warn "Failed to parse date #{string.inspect}"
|
14
|
+
nil
|
17
15
|
end
|
18
16
|
|
19
17
|
##
|
20
|
-
# Returns the id of the entry or its url if not id is present, as some
|
18
|
+
# Returns the id of the entry or its url if not id is present, as some
|
19
|
+
# formats don't support it
|
21
20
|
def id
|
22
21
|
@entry_id ||= @url
|
23
22
|
end
|
@@ -37,30 +36,29 @@ module Feedjira
|
|
37
36
|
end
|
38
37
|
|
39
38
|
def sanitize!
|
40
|
-
%w
|
41
|
-
if
|
42
|
-
|
39
|
+
%w(title author summary content image).each do |name|
|
40
|
+
if respond_to?(name) && send(name).respond_to?(:sanitize!)
|
41
|
+
send(name).send :sanitize!
|
43
42
|
end
|
44
43
|
end
|
45
44
|
end
|
46
45
|
|
47
|
-
|
46
|
+
alias last_modified published
|
48
47
|
|
49
48
|
def each
|
50
|
-
@rss_fields ||=
|
49
|
+
@rss_fields ||= instance_variables
|
51
50
|
|
52
51
|
@rss_fields.each do |field|
|
53
|
-
yield(field.to_s.sub('@', ''),
|
52
|
+
yield(field.to_s.sub('@', ''), instance_variable_get(field))
|
54
53
|
end
|
55
54
|
end
|
56
55
|
|
57
56
|
def [](field)
|
58
|
-
|
57
|
+
instance_variable_get("@#{field}")
|
59
58
|
end
|
60
59
|
|
61
60
|
def []=(field, value)
|
62
|
-
|
61
|
+
instance_variable_set("@#{field}", value)
|
63
62
|
end
|
64
|
-
|
65
63
|
end
|
66
64
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Feedjira
|
2
2
|
module FeedUtilities
|
3
|
-
UPDATABLE_ATTRIBUTES = %w(title feed_url url last_modified etag)
|
3
|
+
UPDATABLE_ATTRIBUTES = %w(title feed_url url last_modified etag).freeze
|
4
4
|
|
5
5
|
attr_writer :new_entries, :updated, :last_modified
|
6
6
|
attr_accessor :etag
|
@@ -32,7 +32,8 @@ module Feedjira
|
|
32
32
|
|
33
33
|
def last_modified
|
34
34
|
@last_modified ||= begin
|
35
|
-
|
35
|
+
published = entries.reject { |e| e.published.nil? }
|
36
|
+
entry = published.sort_by { |e| e.published if e.published }.last
|
36
37
|
entry ? entry.published : nil
|
37
38
|
end
|
38
39
|
end
|
@@ -45,13 +46,13 @@ module Feedjira
|
|
45
46
|
@new_entries ||= []
|
46
47
|
end
|
47
48
|
|
48
|
-
def
|
49
|
-
new_entries.
|
49
|
+
def new_entries?
|
50
|
+
!new_entries.empty?
|
50
51
|
end
|
51
52
|
|
52
53
|
def update_from_feed(feed)
|
53
54
|
self.new_entries += find_new_entries_for(feed)
|
54
|
-
|
55
|
+
entries.unshift(*self.new_entries)
|
55
56
|
|
56
57
|
@updated = false
|
57
58
|
|
@@ -61,7 +62,8 @@ module Feedjira
|
|
61
62
|
end
|
62
63
|
|
63
64
|
def update_attribute(feed, name)
|
64
|
-
old_value
|
65
|
+
old_value = send(name)
|
66
|
+
new_value = feed.send(name)
|
65
67
|
|
66
68
|
if old_value != new_value
|
67
69
|
send("#{name}=", new_value)
|
@@ -72,33 +74,36 @@ module Feedjira
|
|
72
74
|
end
|
73
75
|
|
74
76
|
def sanitize_entries!
|
75
|
-
entries.each
|
77
|
+
entries.each(&:sanitize!)
|
76
78
|
end
|
77
79
|
|
78
80
|
private
|
79
81
|
|
82
|
+
# This implementation is a hack, which is why it's so ugly. It's to get
|
83
|
+
# around the fact that not all feeds have a published date. However,
|
84
|
+
# they're always ordered with the newest one first. So we go through the
|
85
|
+
# entries just parsed and insert each one as a new entry until we get to
|
86
|
+
# one that has the same id as the the newest for the feed.
|
80
87
|
def find_new_entries_for(feed)
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
# So we go through the entries just parsed and insert each one as a new entry
|
85
|
-
# until we get to one that has the same id as the the newest for the feed
|
86
|
-
return feed.entries if self.entries.length == 0
|
87
|
-
latest_entry = self.entries.first
|
88
|
+
return feed.entries if entries.length.zero?
|
89
|
+
|
90
|
+
latest_entry = entries.first
|
88
91
|
found_new_entries = []
|
92
|
+
|
89
93
|
feed.entries.each do |entry|
|
90
|
-
|
91
|
-
break if entry.url == latest_entry.url
|
92
|
-
else
|
93
|
-
break if entry.entry_id == latest_entry.entry_id || entry.url == latest_entry.url
|
94
|
-
end
|
94
|
+
break unless new_entry?(entry, latest_entry)
|
95
95
|
found_new_entries << entry
|
96
96
|
end
|
97
|
+
|
97
98
|
found_new_entries
|
98
99
|
end
|
99
100
|
|
100
|
-
def
|
101
|
-
|
101
|
+
def new_entry?(entry, latest)
|
102
|
+
nil_ids = entry.entry_id.nil? && latest.entry_id.nil?
|
103
|
+
new_id = entry.entry_id != latest.entry_id
|
104
|
+
new_url = entry.url != latest.url
|
105
|
+
|
106
|
+
(nil_ids || new_id) && new_url
|
102
107
|
end
|
103
108
|
end
|
104
109
|
end
|
data/lib/feedjira/parser/atom.rb
CHANGED
@@ -4,16 +4,17 @@ module Feedjira
|
|
4
4
|
class Atom
|
5
5
|
include SAXMachine
|
6
6
|
include FeedUtilities
|
7
|
+
|
7
8
|
element :title
|
8
|
-
element :subtitle, :
|
9
|
-
element :link, :
|
10
|
-
element :link, :
|
11
|
-
elements :link, :
|
12
|
-
elements :link, :
|
13
|
-
elements :entry, :
|
9
|
+
element :subtitle, as: :description
|
10
|
+
element :link, as: :url, value: :href, with: { type: 'text/html' }
|
11
|
+
element :link, as: :feed_url, value: :href, with: { rel: 'self' }
|
12
|
+
elements :link, as: :links, value: :href
|
13
|
+
elements :link, as: :hubs, value: :href, with: { rel: 'hub' }
|
14
|
+
elements :entry, as: :entries, class: AtomEntry
|
14
15
|
|
15
|
-
def self.able_to_parse?(xml)
|
16
|
-
|
16
|
+
def self.able_to_parse?(xml)
|
17
|
+
%r{\<feed[^\>]+xmlns\s?=\s?[\"\'](http://www\.w3\.org/2005/Atom|http://purl\.org/atom/ns\#)[\"\'][^\>]*\>} =~ xml # rubocop:disable Metrics/LineLength
|
17
18
|
end
|
18
19
|
|
19
20
|
def url
|
@@ -1,5 +1,4 @@
|
|
1
1
|
module Feedjira
|
2
|
-
|
3
2
|
module Parser
|
4
3
|
# Parser for dealing with Atom feed entries.
|
5
4
|
class AtomEntry
|
@@ -7,28 +6,26 @@ module Feedjira
|
|
7
6
|
include FeedEntryUtilities
|
8
7
|
|
9
8
|
element :title
|
10
|
-
element :link, :
|
11
|
-
element :name, :
|
9
|
+
element :link, as: :url, value: :href, with: { type: 'text/html', rel: 'alternate' } # rubocop:disable Metrics/LineLength
|
10
|
+
element :name, as: :author
|
12
11
|
element :content
|
13
12
|
element :summary
|
14
13
|
|
15
|
-
element :"media:content", :
|
16
|
-
element :enclosure, :
|
14
|
+
element :"media:content", as: :image, value: :url
|
15
|
+
element :enclosure, as: :image, value: :href
|
17
16
|
|
18
17
|
element :published
|
19
|
-
element :id, :
|
20
|
-
element :created, :
|
21
|
-
element :issued, :
|
18
|
+
element :id, as: :entry_id
|
19
|
+
element :created, as: :published
|
20
|
+
element :issued, as: :published
|
22
21
|
element :updated
|
23
|
-
element :modified, :
|
24
|
-
elements :category, :
|
25
|
-
elements :link, :
|
22
|
+
element :modified, as: :updated
|
23
|
+
elements :category, as: :categories, value: :term
|
24
|
+
elements :link, as: :links, value: :href
|
26
25
|
|
27
26
|
def url
|
28
27
|
@url ||= links.first
|
29
28
|
end
|
30
29
|
end
|
31
|
-
|
32
30
|
end
|
33
|
-
|
34
31
|
end
|
@@ -1,26 +1,24 @@
|
|
1
1
|
module Feedjira
|
2
|
-
|
3
2
|
module Parser
|
4
3
|
# Parser for dealing with Feedburner Atom feeds.
|
5
4
|
class AtomFeedBurner
|
6
5
|
include SAXMachine
|
7
6
|
include FeedUtilities
|
7
|
+
|
8
8
|
element :title
|
9
|
-
element :subtitle, :
|
10
|
-
element :link, :
|
11
|
-
element :link, :
|
12
|
-
elements :"atom10:link", :
|
13
|
-
elements :entry, :
|
9
|
+
element :subtitle, as: :description
|
10
|
+
element :link, as: :url, value: :href, with: { type: 'text/html' }
|
11
|
+
element :link, as: :feed_url, value: :href, with: { type: 'application/atom+xml' } # rubocop:disable Metrics/LineLength
|
12
|
+
elements :"atom10:link", as: :hubs, value: :href, with: { rel: 'hub' }
|
13
|
+
elements :entry, as: :entries, class: AtomFeedBurnerEntry
|
14
14
|
|
15
|
-
def self.able_to_parse?(xml)
|
16
|
-
((/Atom/ =~ xml) && (/feedburner/ =~ xml) && !(/\<rss|\<rdf/ =~ xml)) || false
|
15
|
+
def self.able_to_parse?(xml)
|
16
|
+
((/Atom/ =~ xml) && (/feedburner/ =~ xml) && !(/\<rss|\<rdf/ =~ xml)) || false # rubocop:disable Metrics/LineLength
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.preprocess(xml)
|
20
20
|
Preprocessor.new(xml).to_xml
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
24
23
|
end
|
25
|
-
|
26
24
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
module Feedjira
|
2
|
-
|
3
2
|
module Parser
|
4
3
|
# Parser for dealing with Feedburner Atom feed entries.
|
5
4
|
class AtomFeedBurnerEntry
|
@@ -7,29 +6,27 @@ module Feedjira
|
|
7
6
|
include FeedEntryUtilities
|
8
7
|
|
9
8
|
element :title
|
10
|
-
element :name, :
|
11
|
-
element :link, :
|
12
|
-
element :"feedburner:origLink", :
|
9
|
+
element :name, as: :author
|
10
|
+
element :link, as: :url, value: :href, with: { type: 'text/html', rel: 'alternate' } # rubocop:disable Metrics/LineLength
|
11
|
+
element :"feedburner:origLink", as: :url
|
13
12
|
element :summary
|
14
13
|
element :content
|
15
14
|
|
16
|
-
element :"media:content", :
|
17
|
-
element :enclosure, :
|
15
|
+
element :"media:content", as: :image, value: :url
|
16
|
+
element :enclosure, as: :image, value: :href
|
18
17
|
|
19
18
|
element :published
|
20
|
-
element :id, :
|
21
|
-
element :issued, :
|
22
|
-
element :created, :
|
19
|
+
element :id, as: :entry_id
|
20
|
+
element :issued, as: :published
|
21
|
+
element :created, as: :published
|
23
22
|
element :updated
|
24
|
-
element :modified, :
|
25
|
-
elements :category, :
|
26
|
-
elements :link, :
|
23
|
+
element :modified, as: :updated
|
24
|
+
elements :category, as: :categories, value: :term
|
25
|
+
elements :link, as: :links, value: :href
|
27
26
|
|
28
27
|
def url
|
29
28
|
@url ||= links.first
|
30
29
|
end
|
31
|
-
|
32
30
|
end
|
33
31
|
end
|
34
|
-
|
35
32
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Feedjira
|
2
|
+
module Parser
|
3
|
+
# Parser for dealing with RSS feeds.
|
4
|
+
class AtomYoutube
|
5
|
+
include SAXMachine
|
6
|
+
include FeedUtilities
|
7
|
+
element :title
|
8
|
+
element :link, as: :url, value: :href, with: { rel: 'alternate' }
|
9
|
+
element :link, as: :feed_url, value: :href, with: { rel: 'self' }
|
10
|
+
element :name, as: :author
|
11
|
+
element :"yt:channelId", as: :youtube_channel_id
|
12
|
+
|
13
|
+
elements :entry, as: :entries, class: AtomYoutubeEntry
|
14
|
+
|
15
|
+
def self.able_to_parse?(xml) #:nodoc:
|
16
|
+
%r{xmlns:yt="http://www.youtube.com/xml/schemas/2015"} =~ xml
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Feedjira
|
2
|
+
module Parser
|
3
|
+
class AtomYoutubeEntry
|
4
|
+
include SAXMachine
|
5
|
+
include FeedEntryUtilities
|
6
|
+
|
7
|
+
element :title
|
8
|
+
element :link, as: :url, value: :href, with: { rel: 'alternate' }
|
9
|
+
element :name, as: :author
|
10
|
+
element :"media:description", as: :content
|
11
|
+
element :summary
|
12
|
+
element :published
|
13
|
+
element :id, as: :entry_id
|
14
|
+
element :updated
|
15
|
+
element :"yt:videoId", as: :youtube_video_id
|
16
|
+
element :"media:title", as: :media_title
|
17
|
+
element :"media:content", as: :media_url, value: :url
|
18
|
+
element :"media:content", as: :media_type, value: :type
|
19
|
+
element :"media:content", as: :media_width, value: :width
|
20
|
+
element :"media:content", as: :media_height, value: :height
|
21
|
+
element :"media:thumbnail", as: :media_thumbnail_url, value: :url
|
22
|
+
element :"media:thumbnail", as: :media_thumbnail_width, value: :width
|
23
|
+
element :"media:thumbnail", as: :media_thumbnail_height, value: :height
|
24
|
+
element :"media:starRating", as: :media_star_count, value: :count
|
25
|
+
element :"media:starRating", as: :media_star_average, value: :average
|
26
|
+
element :"media:statistics", as: :media_views, value: :views
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|