diff --git a/lib/simple-rss.rb b/lib/simple-rss.rb index af1b592..6339f68 100644 --- a/lib/simple-rss.rb +++ b/lib/simple-rss.rb @@ -52,6 +52,9 @@ class SimpleRSS # rubocop:disable Metrics/ClassLength media:title media:thumbnail#url media:thumbnail#height media:thumbnail#width media:credit media:credit#role media:category media:category#scheme + media:description + enclosure#url enclosure#type enclosure#length + itunes:duration itunes:image#href ] # @rbs (untyped, ?Hash[Symbol, untyped]) -> void @@ -169,6 +172,26 @@ def dedupe self end + # @rbs () -> Array[Hash[Symbol, untyped]] + def enclosures + items.filter_map do |item| + enclosure_url = item[:enclosure_url] + next if blank_value?(enclosure_url) + + { + url: enclosure_url, + type: item[:enclosure_type], + length: item[:enclosure_length], + item: item + } + end + end + + # @rbs () -> Array[String] + def images + items.flat_map { |item| item_image_urls(item) }.uniq + end + # @rbs (?Hash[Symbol, untyped]) -> Hash[Symbol, untyped] def as_json(_options = {}) hash = {} #: Hash[Symbol, untyped] @@ -381,10 +404,32 @@ def parse parse_item_tag(item, tag, match[3], match[2]) end item.define_singleton_method(:method_missing) { |name, *_args| self[name] } + add_item_media_helpers(item) @items << item end end + # @rbs (Hash[Symbol, untyped]) -> void + def add_item_media_helpers(item) + item.define_singleton_method(:has_media?) do + [ + self[:media_content_url], + self[:media_thumbnail_url], + self[:enclosure_url], + self[:itunes_image_href] + ].any? { |value| !value.nil? && !value.to_s.strip.empty? } + end + + item.define_singleton_method(:media_url) do + [ + self[:media_content_url], + self[:media_thumbnail_url], + self[:enclosure_url], + self[:itunes_image_href] + ].find { |value| !value.nil? && !value.to_s.strip.empty? } + end + end + # @rbs (Hash[Symbol, untyped], Symbol, String?, String?) -> void def parse_item_tag(item, tag, content, item_attrs = nil) return if content.nil? @@ -573,6 +618,18 @@ def item_date(item) nil end + # @rbs (Hash[Symbol, untyped]) -> Array[String] + def item_image_urls(item) + [item[:media_thumbnail_url], item[:media_content_url], item[:itunes_image_href]] + .compact + .reject { |url| blank_value?(url) } + end + + # @rbs (untyped) -> bool + def blank_value?(value) + value.to_s.strip.empty? + end + # @rbs (untyped) -> untyped def serialize_value(value) case value diff --git a/test/base/media_and_enclosure_helpers_test.rb b/test/base/media_and_enclosure_helpers_test.rb new file mode 100644 index 0000000..eec90c2 --- /dev/null +++ b/test/base/media_and_enclosure_helpers_test.rb @@ -0,0 +1,84 @@ +require "test_helper" + +class MediaAndEnclosureHelpersTest < Test::Unit::TestCase + def setup + @feed = SimpleRSS.parse <<~XML + + + + Podcast Feed + + Episode 1 + + + Episode image + + 00:42:00 + + + + Episode 2 + + + + Episode 3 + + + + Episode 4 + + + + + + + XML + end + + def test_enclosures_extracts_podcast_enclosures + enclosures = @feed.enclosures + + assert_equal 2, enclosures.size + assert_equal "https://example.com/audio-1.mp3", enclosures.first[:url] + assert_equal "audio/mpeg", enclosures.first[:type] + assert_equal "12345", enclosures.first[:length] + assert_equal "Episode 1", enclosures.first[:item][:title] + end + + def test_images_collects_unique_media_urls + images = @feed.images + + assert_equal [ + "https://example.com/thumb-1.jpg", + "https://example.com/image-1.jpg", + "https://example.com/itunes-1.jpg", + "https://example.com/thumb-2.jpg" + ], images + end + + def test_item_media_helpers + first_item = @feed.items.first + second_item = @feed.items[1] + third_item = @feed.items[2] + fourth_item = @feed.items.last + + assert_equal true, first_item.has_media? + assert_equal "https://example.com/image-1.jpg", first_item.media_url + + assert_equal true, second_item.has_media? + assert_equal "https://example.com/thumb-2.jpg", second_item.media_url + + assert_equal true, third_item.has_media? + assert_equal "https://example.com/audio-3.mp3", third_item.media_url + + assert_equal false, fourth_item.has_media? + assert_nil fourth_item.media_url + end + + def test_media_description_and_itunes_duration_are_parsed + first_item = @feed.items.first + + assert_equal "Episode image", first_item[:media_description] + assert_equal "00:42:00", first_item[:itunes_duration] + end +end