Class FeedTools::FeedItem
In: lib/feed_tools/feed_item.rb
Parent: Object

The FeedTools::FeedItem class represents the structure of a single item within a web feed.

Methods

Public Class methods

Initialize the feed object

[Source]

    # File lib/feed_tools/feed_item.rb, line 31
31:     def initialize
32:       super
33:       @feed_data = nil
34:       @feed_data_type = :xml
35:       @xml_document = nil
36:       @root_node = nil
37:       @title = nil
38:       @id = nil
39:       @time = Time.now.gmtime
40:       @version = FeedTools::FEED_TOOLS_VERSION::STRING
41:     end

Public Instance methods

abstract()

Alias for summary

abstract=(new_summary)

Alias for summary=

Returns the feed item author

[Source]

      # File lib/feed_tools/feed_item.rb, line 1420
1420:     def author
1421:       if @author.nil?
1422:         @author = FeedTools::Author.new
1423:         author_node = FeedTools::XmlHelper.try_xpaths(self.root_node, [
1424:           "atom10:author",
1425:           "atom03:author",
1426:           "atom:author",
1427:           "author",
1428:           "managingEditor",
1429:           "dc:author",
1430:           "dc:creator",
1431:           "creator"
1432:         ])
1433:         unless author_node.nil?
1434:           @author.raw = FeedTools::XmlHelper.try_xpaths(
1435:             author_node, ["text()"], :select_result_value => true)
1436:           @author.raw = FeedTools::HtmlHelper.unescape_entities(@author.raw)
1437:           unless @author.raw.nil?
1438:             raw_scan = @author.raw.scan(
1439:               /(.*)\((\b[A-Z0-9._%-\+]+@[A-Z0-9._%-]+\.[A-Z]{2,4}\b)\)/i)
1440:             if raw_scan.nil? || raw_scan.size == 0
1441:               raw_scan = @author.raw.scan(
1442:                 /(\b[A-Z0-9._%-\+]+@[A-Z0-9._%-]+\.[A-Z]{2,4}\b)\s*\((.*)\)/i)
1443:               unless raw_scan.size == 0
1444:                 author_raw_pair = raw_scan.first.reverse
1445:               end
1446:             else
1447:               author_raw_pair = raw_scan.first
1448:             end
1449:             if raw_scan.nil? || raw_scan.size == 0
1450:               email_scan = @author.raw.scan(
1451:                 /\b[A-Z0-9._%-\+]+@[A-Z0-9._%-]+\.[A-Z]{2,4}\b/i)
1452:               if email_scan != nil && email_scan.size > 0
1453:                 @author.email = email_scan.first.strip
1454:               end
1455:             end
1456:             unless author_raw_pair.nil? || author_raw_pair.size == 0
1457:               @author.name = author_raw_pair.first.strip
1458:               @author.email = author_raw_pair.last.strip
1459:             else
1460:               unless @author.raw.include?("@")
1461:                 # We can be reasonably sure we are looking at something
1462:                 # that the creator didn't intend to contain an email address
1463:                 # if it got through the preceeding regexes and it doesn't
1464:                 # contain the tell-tale '@' symbol.
1465:                 @author.name = @author.raw
1466:               end
1467:             end
1468:           end
1469:           if @author.name.blank?
1470:             @author.name = FeedTools::HtmlHelper.unescape_entities(
1471:               FeedTools::XmlHelper.try_xpaths(author_node, [
1472:                 "atom10:name/text()",
1473:                 "atom03:name/text()",
1474:                 "atom:name/text()",
1475:                 "name/text()",
1476:                 "@name"
1477:               ], :select_result_value => true)
1478:             )
1479:           end
1480:           if @author.email.blank?
1481:             @author.email = FeedTools::HtmlHelper.unescape_entities(
1482:               FeedTools::XmlHelper.try_xpaths(author_node, [
1483:                 "atom10:email/text()",
1484:                 "atom03:email/text()",
1485:                 "atom:email/text()",
1486:                 "email/text()",
1487:                 "@email"
1488:               ], :select_result_value => true)
1489:             )
1490:           end
1491:           if @author.url.blank?
1492:             @author.url = FeedTools::HtmlHelper.unescape_entities(
1493:               FeedTools::XmlHelper.try_xpaths(author_node, [
1494:                 "atom10:url/text()",
1495:                 "atom03:url/text()",
1496:                 "atom:url/text()",
1497:                 "url/text()",
1498:                 "atom10:uri/text()",
1499:                 "atom03:uri/text()",
1500:                 "atom:uri/text()",
1501:                 "uri/text()",
1502:                 "@url",
1503:                 "@uri",
1504:                 "@href"
1505:               ], :select_result_value => true)
1506:             )
1507:           end
1508:           if @author.name.blank? && !@author.raw.blank? &&
1509:               !@author.email.blank?
1510:             name_scan = @author.raw.scan(
1511:               /"?([^"]*)"? ?[\(<].*#{@author.email}.*[\)>].*/)
1512:             if name_scan.flatten.size == 1
1513:               @author.name = name_scan.flatten[0].strip
1514:             end
1515:             if @author.name.blank?
1516:               name_scan = @author.raw.scan(
1517:                 /.*#{@author.email} ?[\(<]"?([^"]*)"?[\)>].*/)
1518:               if name_scan.flatten.size == 1
1519:                 @author.name = name_scan.flatten[0].strip
1520:               end
1521:             end
1522:           end
1523:           @author.name = nil if @author.name.blank?
1524:           @author.raw = nil if @author.raw.blank?
1525:           @author.email = nil if @author.email.blank?
1526:           @author.url = nil if @author.url.blank?
1527:           if @author.url != nil
1528:             begin
1529:               if !(@author.url =~ /^file:/) &&
1530:                   !FeedTools::UriHelper.is_uri?(@author.url)
1531:                 @author.url = FeedTools::UriHelper.resolve_relative_uri(
1532:                   @author.url, [author_node.base_uri, self.base_uri])
1533:               end
1534:             rescue
1535:             end
1536:           end
1537:           if FeedTools::XmlHelper.try_xpaths(author_node,
1538:               ["@gr:unknown-author"], :select_result_value => true) == "true"
1539:             if @author.name == "(author unknown)"
1540:               @author.name = nil
1541:             end
1542:           end
1543:         end
1544:         # Fallback on the itunes module if we didn't find an author name
1545:         begin
1546:           @author.name = self.itunes_author if @author.name.nil?
1547:         rescue
1548:           @author.name = nil
1549:         end
1550:         if @author.name.blank? && @author.email.blank? &&
1551:             @author.href.blank?
1552:           parent_feed = self.feed
1553:           if parent_feed != nil
1554:             @author = parent_feed.author.dup
1555:           end
1556:         end
1557:       end
1558:       return @author
1559:     end

Sets the feed item author

[Source]

      # File lib/feed_tools/feed_item.rb, line 1562
1562:     def author=(new_author)
1563:       if new_author.respond_to?(:name) &&
1564:           new_author.respond_to?(:email) &&
1565:           new_author.respond_to?(:url)
1566:         # It's a complete author object, just set it.
1567:         @author = new_author
1568:       else
1569:         # We're not looking at an author object, this is probably a string,
1570:         # default to setting the author's name.
1571:         if @author.nil?
1572:           @author = FeedTools::Author.new
1573:         end
1574:         @author.name = new_author
1575:       end
1576:     end

Returns the parent feed‘s base_uri if any.

[Source]

     # File lib/feed_tools/feed_item.rb, line 633
633:     def base_uri
634:       parent_feed = self.feed
635:       if parent_feed != nil
636:         return parent_feed.base_uri
637:       else
638:         return nil
639:       end
640:     end

Generates xml based on the content of the feed item

[Source]

      # File lib/feed_tools/feed_item.rb, line 2034
2034:     def build_xml(feed_type=(self.feed.feed_type or "atom"), version=nil,
2035:         xml_builder=Builder::XmlMarkup.new(
2036:           :indent => 2, :escape_attrs => false))
2037:           
2038:       parent_feed = self.feed
2039:       if parent_feed.find_node(
2040:           "access:restriction/@relationship").to_s == "deny"
2041:         raise StandardError,
2042:           "Operation not permitted.  This feed denies redistribution."
2043:       elsif parent_feed.find_node("@indexing:index").to_s == "no"
2044:         raise StandardError,
2045:           "Operation not permitted.  This feed denies redistribution."
2046:       end
2047:       if self.find_node(
2048:           "access:restriction/@relationship").to_s == "deny"
2049:         raise StandardError,
2050:           "Operation not permitted.  This feed item denies redistribution."
2051:       end
2052:       
2053:       self.full_parse()
2054:       
2055:       if feed_type == "rss" && (version == nil || version == 0.0)
2056:         version = 1.0
2057:       elsif feed_type == "atom" && (version == nil || version == 0.0)
2058:         version = 1.0
2059:       end
2060:       if feed_type == "rss" &&
2061:           (version == 0.9 || version == 1.0 || version == 1.1)
2062:         # RDF-based rss format
2063:         if link.nil?
2064:           raise "Cannot generate an rdf-based feed item with a " +
2065:             "nil link field."
2066:         end
2067:         return xml_builder.item("rdf:about" =>
2068:             FeedTools::HtmlHelper.escape_entities(link)) do
2069:           unless self.title.blank?
2070:             xml_builder.title(
2071:               FeedTools::HtmlHelper.strip_html_tags(self.title))
2072:           else
2073:             xml_builder.title
2074:           end
2075:           unless self.link.blank?
2076:             xml_builder.link(self.link)
2077:           else
2078:             xml_builder.link
2079:           end
2080:           unless self.author.nil? || self.author.name.nil?
2081:             xml_builder.tag!("dc:creator", self.author.name)
2082:           end
2083:           unless self.summary.blank?
2084:             xml_builder.description(self.summary)
2085:           else
2086:             xml_builder.description
2087:           end
2088:           unless self.content.blank?
2089:             xml_builder.tag!("content:encoded") do
2090:               xml_builder.cdata!(self.content)
2091:             end
2092:           end
2093:           unless time.nil?
2094:             xml_builder.tag!("dc:date", time.iso8601)            
2095:           end
2096:           unless self.rights.blank?
2097:             xml_builder.tag!("dc:rights", self.rights)
2098:           end
2099:           unless tags.nil? || tags.size == 0
2100:             for tag in tags
2101:               xml_builder.tag!("dc:subject", tag)
2102:             end
2103:             if self.feed.podcast?
2104:               xml_builder.tag!("itunes:keywords", tags.join(", "))
2105:             end
2106:           end
2107:           build_xml_hook(feed_type, version, xml_builder)
2108:         end
2109:       elsif feed_type == "rss"
2110:         # normal rss format
2111:         return xml_builder.item do
2112:           unless self.title.blank?
2113:             xml_builder.title(
2114:               FeedTools::HtmlHelper.strip_html_tags(self.title))
2115:           end
2116:           unless self.link.blank?
2117:             xml_builder.link(self.link)
2118:           end
2119:           unless self.author.nil? || self.author.name.nil?
2120:             xml_builder.tag!("dc:creator", self.author.name)
2121:           end
2122:           unless self.author.nil? || self.author.email.nil? ||
2123:               self.author.name.nil?
2124:             xml_builder.author("#{self.author.email} (#{self.author.name})")
2125:           end
2126:           unless self.summary.blank?
2127:             xml_builder.description(self.summary)
2128:           end
2129:           unless self.content.blank?
2130:             xml_builder.tag!("content:encoded") do
2131:               xml_builder.cdata!(self.content)
2132:             end
2133:           end
2134:           if !self.published.nil?
2135:             xml_builder.pubDate(self.published.rfc822)            
2136:           elsif !self.time.nil?
2137:             xml_builder.pubDate(self.time.rfc822)            
2138:           end
2139:           unless self.rights.blank?
2140:             xml_builder.tag!("dc:rights", self.rights)
2141:           end
2142:           unless self.guid.blank?
2143:             if FeedTools::UriHelper.is_uri?(self.guid) &&
2144:                 (self.guid =~ /^http/)
2145:               xml_builder.guid(self.guid, "isPermaLink" => "true")
2146:             else
2147:               xml_builder.guid(self.guid, "isPermaLink" => "false")
2148:             end
2149:           else
2150:             unless self.link.blank?
2151:               xml_builder.guid(self.link, "isPermaLink" => "true")
2152:             end
2153:           end
2154:           unless tags.nil? || tags.size == 0
2155:             for tag in tags
2156:               xml_builder.tag!("category", tag)
2157:             end
2158:             if self.feed.podcast?
2159:               xml_builder.tag!("itunes:keywords", tags.join(", "))
2160:             end
2161:           end
2162:           unless self.enclosures.blank? || self.enclosures.size == 0
2163:             for enclosure in self.enclosures
2164:               attribute_hash = {}
2165:               next if enclosure.url.blank?
2166:               begin
2167:                 if enclosure.file_size.blank? || enclosure.file_size.to_i == 0
2168:                   # We can't use this enclosure because it's missing the
2169:                   # required file size.  Check alternate versions for
2170:                   # file_size.
2171:                   if !enclosure.versions.blank? && enclosure.versions.size > 0
2172:                     for alternate in enclosure.versions
2173:                       if alternate.file_size != nil &&
2174:                           alternate.file_size.to_i > 0
2175:                         enclosure = alternate
2176:                         break
2177:                       end
2178:                     end
2179:                   end
2180:                 end
2181:               rescue
2182:               end
2183:               attribute_hash["url"] =
2184:                 FeedTools::UriHelper.normalize_url(enclosure.url)
2185:               if enclosure.type != nil
2186:                 attribute_hash["type"] = enclosure.type
2187:               end
2188:               if enclosure.file_size != nil && enclosure.file_size.to_i > 0
2189:                 attribute_hash["length"] = enclosure.file_size.to_s
2190:               else
2191:                 # We couldn't find an alternate and the problem is still
2192:                 # there.  Give up and go on.
2193:                 xml_builder.comment!(
2194:                   "*** Enclosure failed to include file size. Ignoring. ***")
2195:                 next
2196:               end
2197:               xml_builder.enclosure(attribute_hash)
2198:             end
2199:           end
2200:           build_xml_hook(feed_type, version, xml_builder)
2201:         end
2202:       elsif feed_type == "atom" && version == 0.3
2203:         raise "Atom 0.3 is obsolete."
2204:       elsif feed_type == "atom" && version == 1.0
2205:         # normal atom format
2206:         return xml_builder.entry("xmlns" =>
2207:             FEED_TOOLS_NAMESPACES['atom10']) do
2208:           unless title.nil? || title == ""
2209:             xml_builder.title(
2210:               FeedTools::HtmlHelper.strip_html_tags(self.title),
2211:                 "type" => "html")
2212:           end
2213:           xml_builder.author do
2214:             unless self.author.nil? || self.author.name.nil?
2215:               xml_builder.name(self.author.name)
2216:             else
2217:               xml_builder.name("n/a")
2218:             end
2219:             unless self.author.nil? || self.author.email.nil?
2220:               xml_builder.email(self.author.email)
2221:             end
2222:             unless self.author.nil? || self.author.url.nil?
2223:               xml_builder.uri(self.author.url)
2224:             end
2225:           end
2226:           unless link.nil? || link == ""
2227:             xml_builder.link(
2228:                 "href" =>
2229:                   FeedTools::HtmlHelper.escape_entities(self.link),
2230:                 "rel" => "alternate")
2231:           end
2232:           if !self.content.blank?
2233:             xml_builder.content(self.content,
2234:                 "type" => "html")
2235:           end
2236:           if !self.summary.blank?
2237:             xml_builder.summary(self.summary,
2238:                 "type" => "html")
2239:           end
2240:           if self.updated != nil
2241:             xml_builder.updated(self.updated.iso8601)
2242:           elsif self.time != nil
2243:             # Not technically correct, but a heck of a lot better
2244:             # than the Time.now fall-back.
2245:             xml_builder.updated(self.time.iso8601)
2246:           else
2247:             xml_builder.updated(Time.now.gmtime.iso8601)
2248:           end
2249:           unless self.published.nil?
2250:             xml_builder.published(self.published.iso8601)            
2251:           end
2252:           unless self.rights.blank?
2253:             xml_builder.rights(self.rights)
2254:           end
2255:           if self.id != nil
2256:             unless FeedTools::UriHelper.is_uri? self.id
2257:               if self.time != nil && self.link != nil
2258:                 xml_builder.id(FeedTools::UriHelper.build_tag_uri(
2259:                   self.link, self.time))
2260:               elsif self.link != nil
2261:                 xml_builder.id(FeedTools.build_urn_uuid_uri(self.link))
2262:               else
2263:                 raise "The unique id must be a URI. " +
2264:                   "(Attempted to generate id, but failed.)"
2265:               end
2266:             else
2267:               xml_builder.id(self.id)
2268:             end
2269:           elsif self.time != nil && self.link != nil
2270:             xml_builder.id(FeedTools::UriHelper.build_tag_uri(
2271:               self.link, self.time))
2272:           else
2273:             raise "Cannot build feed, missing feed unique id."
2274:           end
2275:           unless self.tags.nil? || self.tags.size == 0
2276:             for tag in self.tags
2277:               xml_builder.category("term" => tag)
2278:             end
2279:           end
2280:           unless self.enclosures.blank? || self.enclosures.size == 0
2281:             for enclosure in self.enclosures
2282:               attribute_hash = {}
2283:               next if enclosure.url.blank?
2284:               attribute_hash["rel"] = "enclosure"
2285:               attribute_hash["href"] =
2286:                 FeedTools::UriHelper.normalize_url(enclosure.url)
2287:               if enclosure.type != nil
2288:                 attribute_hash["type"] = enclosure.type
2289:               end
2290:               if enclosure.file_size != nil && enclosure.file_size.to_i > 0
2291:                 attribute_hash["length"] = enclosure.file_size.to_s
2292:               end
2293:               xml_builder.link(attribute_hash)
2294:             end
2295:           end
2296:           build_xml_hook(feed_type, version, xml_builder)
2297:         end
2298:       else
2299:         raise "Unsupported feed format/version."
2300:       end
2301:     end

A hook method that is called during the feed generation process. Overriding this method will enable additional content to be inserted into the feed.

[Source]

      # File lib/feed_tools/feed_item.rb, line 2029
2029:     def build_xml_hook(feed_type, version, xml_builder)
2030:       return nil
2031:     end

Returns a list of the feed item‘s categories

[Source]

     # File lib/feed_tools/feed_item.rb, line 743
743:     def categories
744:       if @categories.nil?
745:         @categories = []
746:         category_nodes = FeedTools::XmlHelper.try_xpaths_all(self.root_node, [
747:           "category",
748:           "dc:subject"
749:         ])
750:         for category_node in category_nodes
751:           category = FeedTools::Category.new
752:           category.term = FeedTools::XmlHelper.try_xpaths(
753:             category_node, ["@term", "text()"],
754:             :select_result_value => true)
755:           category.term.strip! unless category.term.nil?
756:           category.label = FeedTools::XmlHelper.try_xpaths(
757:             category_node, ["@label"],
758:             :select_result_value => true)
759:           category.label.strip! unless category.label.nil?
760:           category.scheme = FeedTools::XmlHelper.try_xpaths(
761:             category_node, [
762:               "@scheme",
763:               "@domain"
764:             ], :select_result_value => true)
765:           category.scheme.strip! unless category.scheme.nil?
766:           @categories << category
767:         end
768:       end
769:       return @categories
770:     end

Returns the url for posting comments

[Source]

     # File lib/feed_tools/feed_item.rb, line 643
643:     def comments
644:       if @comments.nil?
645:         @comments = FeedTools::XmlHelper.try_xpaths(
646:           self.root_node, ["comments/text()"],
647:           :select_result_value => true)
648:         begin
649:           if !(@comments =~ /^file:/) &&
650:               !FeedTools::UriHelper.is_uri?(@comments)
651:             root_base_uri = nil
652:             unless self.root_node.nil?
653:               root_base_uri = self.root_node.base_uri
654:             end
655:             @comments = FeedTools::UriHelper.resolve_relative_uri(
656:               @comments, [root_base_uri, self.base_uri])
657:           end
658:         rescue
659:         end
660:         if self.configurations[:url_normalization_enabled]
661:           @comments = FeedTools::UriHelper.normalize_url(@comments)
662:         end
663:       end
664:       return @comments
665:     end

Sets the url for posting comments

[Source]

     # File lib/feed_tools/feed_item.rb, line 668
668:     def comments=(new_comments)
669:       @comments = new_comments
670:     end

Returns the load options for this feed.

[Source]

     # File lib/feed_tools/feed_item.rb, line 139
139:     def configurations
140:       if @configurations.blank?
141:         parent_feed = self.feed
142:         if parent_feed != nil
143:           @configurations = parent_feed.configurations.dup
144:         else
145:           @configurations = FeedTools.configurations.dup
146:         end
147:       end
148:       return @configurations
149:     end

Sets the load options for this feed.

[Source]

     # File lib/feed_tools/feed_item.rb, line 152
152:     def configurations=(new_configurations)
153:       @configurations = new_configurations
154:     end

Returns the feed item content

[Source]

     # File lib/feed_tools/feed_item.rb, line 328
328:     def content
329:       if @content.nil?
330:         repair_entities = false
331:         content_node = FeedTools::XmlHelper.try_xpaths(self.root_node, [
332:           "atom10:content",
333:           "atom03:content",
334:           "atom:content",
335:           "body/datacontent",
336:           "xhtml:body",
337:           "body",
338:           "xhtml:div",
339:           "div",
340:           "p:payload",
341:           "payload",
342:           "content:encoded",
343:           "content",
344:           "fullitem",
345:           "encoded",
346:           "description",
347:           "tagline",
348:           "subtitle",
349:           "atom10:summary",
350:           "atom03:summary",
351:           "atom:summary",
352:           "summary",
353:           "abstract",
354:           "blurb",
355:           "info"
356:         ])
357:         @content = FeedTools::HtmlHelper.process_text_construct(content_node,
358:           self.feed_type, self.feed_version, [self.base_uri])
359:         if self.feed_type == "atom" ||
360:             self.configurations[:always_strip_wrapper_elements]
361:           @content = FeedTools::HtmlHelper.strip_wrapper_element(@content)
362:         end
363:         if @content.nil?
364:           @content = self.media_text
365:         end
366:         if @content.nil?
367:           @content = self.itunes_summary
368:         end
369:         if @content.nil?
370:           @content = self.itunes_subtitle
371:         end
372:       end
373:       return @content
374:     end

Sets the feed item content

[Source]

     # File lib/feed_tools/feed_item.rb, line 377
377:     def content=(new_content)
378:       @content = new_content
379:     end
copyright()

Alias for rights

copyright=(new_rights)

Alias for rights=

description()

Alias for summary

description=(new_summary)

Alias for summary=

Breaks any references that the feed entry may be keeping around, thus making the job of the garbage collector much, much easier. Call this method prior to feed entries going out of scope to prevent memory leaks.

[Source]

    # File lib/feed_tools/feed_item.rb, line 46
46:     def dispose()
47:       @feed_data = nil
48:       @feed_data_type = nil
49:       @xml_document = nil
50:       @root_node = nil
51:       @title = nil
52:       @id = nil
53:       @time = nil
54:     end

Returns all feed item enclosures

[Source]

      # File lib/feed_tools/feed_item.rb, line 937
 937:     def enclosures
 938:       if @enclosures.nil?
 939:         @enclosures = []
 940:         
 941:         # First, load up all the different possible sources of enclosures
 942:         rss_enclosures =
 943:           FeedTools::XmlHelper.try_xpaths_all(self.root_node, ["enclosure"])
 944:         atom_enclosures =
 945:           FeedTools::XmlHelper.try_xpaths_all(self.root_node, [
 946:             "atom10:link[@rel='enclosure']",
 947:             "atom03:link[@rel='enclosure']",
 948:             "atom:link[@rel='enclosure']",
 949:             "link[@rel='enclosure']"
 950:           ])
 951:         media_content_enclosures =
 952:           FeedTools::XmlHelper.try_xpaths_all(self.root_node,
 953:             ["media:content"])
 954:         media_group_enclosures =
 955:           FeedTools::XmlHelper.try_xpaths_all(self.root_node, ["media:group"])
 956:           
 957:         bogus_enclosures =
 958:           FeedTools::XmlHelper.try_xpaths_all(self.root_node, ["video"])
 959:           
 960:         # TODO: Implement this
 961:         bittorrent_enclosures =
 962:           FeedTools::XmlHelper.try_xpaths_all(self.root_node,
 963:             ["bitTorrent:torrent"])
 964:         
 965: 
 966:         # Parse RSS-type enclosures.  Thanks to a few buggy enclosures
 967:         # implementations, sometimes these also manage to show up in atom
 968:         # files.
 969:         for enclosure_node in rss_enclosures
 970:           enclosure = FeedTools::Enclosure.new
 971:           enclosure.url = FeedTools::HtmlHelper.unescape_entities(
 972:             enclosure_node.attributes["url"].to_s)
 973:           enclosure.type = enclosure_node.attributes["type"].to_s
 974:           enclosure.file_size = enclosure_node.attributes["length"].to_i
 975:           enclosure.credits = []
 976:           enclosure.explicit = false
 977:           @enclosures << enclosure
 978:         end
 979:         
 980:         # Parse atom-type enclosures.  If there are repeats of the same
 981:         # enclosure object, we merge the two together.
 982:         for enclosure_node in atom_enclosures
 983:           enclosure_url = FeedTools::HtmlHelper.unescape_entities(
 984:             enclosure_node.attributes["href"].to_s)
 985:           enclosure = nil
 986:           new_enclosure = false
 987:           for existing_enclosure in @enclosures
 988:             if existing_enclosure.url == enclosure_url
 989:               enclosure = existing_enclosure
 990:               break
 991:             end
 992:           end
 993:           if enclosure.nil?
 994:             new_enclosure = true
 995:             enclosure = FeedTools::Enclosure.new
 996:           end
 997:           enclosure.url = enclosure_url
 998:           enclosure.type = enclosure_node.attributes["type"].to_s
 999:           enclosure.file_size = enclosure_node.attributes["length"].to_i
1000:           enclosure.credits = []
1001:           enclosure.explicit = false
1002:           if new_enclosure
1003:             @enclosures << enclosure
1004:           end
1005:         end
1006:         
1007:         # Parse atom-type enclosures.  If there are repeats of the same
1008:         # enclosure object, we merge the two together.
1009:         for enclosure_node in bogus_enclosures
1010:           enclosure_url = FeedTools::HtmlHelper.unescape_entities(
1011:             enclosure_node.attributes["url"].to_s)
1012:           enclosure = nil
1013:           new_enclosure = false
1014:           for existing_enclosure in @enclosures
1015:             if existing_enclosure.url == enclosure_url
1016:               enclosure = existing_enclosure
1017:               break
1018:             end
1019:           end
1020:           if enclosure.nil?
1021:             new_enclosure = true
1022:             enclosure = FeedTools::Enclosure.new
1023:           end
1024:           enclosure.url = enclosure_url
1025:           if File.extname(enclosure_url) == ".wmv"
1026:             enclosure.type = "video/x-ms-wmv"
1027:           end
1028:           enclosure.explicit = false
1029:           if new_enclosure
1030:             @enclosures << enclosure
1031:           end
1032:         end
1033: 
1034:         # Creates an anonymous method to parse content objects from the media
1035:         # module.  We do this to avoid excessive duplication of code since we
1036:         # have to do identical processing for content objects within group
1037:         # objects.
1038:         parse_media_content = lambda do |media_content_nodes|
1039:           affected_enclosures = []
1040:           for enclosure_node in media_content_nodes
1041:             enclosure_url = FeedTools::HtmlHelper.unescape_entities(
1042:               enclosure_node.attributes["url"].to_s)
1043:             enclosure = nil
1044:             new_enclosure = false
1045:             for existing_enclosure in @enclosures
1046:               if existing_enclosure.url == enclosure_url
1047:                 enclosure = existing_enclosure
1048:                 break
1049:               end
1050:             end
1051:             if enclosure.nil?
1052:               new_enclosure = true
1053:               enclosure = FeedTools::Enclosure.new
1054:             end
1055:             enclosure.url = enclosure_url
1056:             enclosure.type = enclosure_node.attributes["type"].to_s
1057:             enclosure.file_size = enclosure_node.attributes["fileSize"].to_i
1058:             enclosure.duration = enclosure_node.attributes["duration"].to_s
1059:             enclosure.height = enclosure_node.attributes["height"].to_i
1060:             enclosure.width = enclosure_node.attributes["width"].to_i
1061:             enclosure.bitrate = enclosure_node.attributes["bitrate"].to_i
1062:             enclosure.framerate = enclosure_node.attributes["framerate"].to_i
1063:             enclosure.expression =
1064:               enclosure_node.attributes["expression"].to_s
1065:             enclosure.is_default =
1066:               (enclosure_node.attributes["isDefault"].to_s.downcase == "true")
1067:             enclosure_thumbnail_url =
1068:               FeedTools::XmlHelper.try_xpaths(enclosure_node,
1069:                 ["media:thumbnail/@url"], :select_result_value => true)
1070:             if !enclosure_thumbnail_url.blank?
1071:               enclosure.thumbnail = FeedTools::EnclosureThumbnail.new(
1072:                 FeedTools::HtmlHelper.unescape_entities(
1073:                   enclosure_thumbnail_url),
1074:                 FeedTools::HtmlHelper.unescape_entities(
1075:                   FeedTools::XmlHelper.