Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions lib/simple-rss.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment thread
cardmagic marked this conversation as resolved.
]

# @rbs (untyped, ?Hash[Symbol, untyped]) -> void
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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?
Expand Down Expand Up @@ -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
Expand Down
84 changes: 84 additions & 0 deletions test/base/media_and_enclosure_helpers_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
require "test_helper"

class MediaAndEnclosureHelpersTest < Test::Unit::TestCase
def setup
@feed = SimpleRSS.parse <<~XML
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
<channel>
<title>Podcast Feed</title>
<item>
<title>Episode 1</title>
<enclosure url="https://example.com/audio-1.mp3" type="audio/mpeg" length="12345" />
<media:content url="https://example.com/image-1.jpg" type="image/jpeg" />
<media:description>Episode image</media:description>
<media:thumbnail url="https://example.com/thumb-1.jpg" />
<itunes:duration>00:42:00</itunes:duration>
<itunes:image href="https://example.com/itunes-1.jpg" />
</item>
<item>
<title>Episode 2</title>
<media:thumbnail url="https://example.com/thumb-2.jpg" />
</item>
<item>
<title>Episode 3</title>
<enclosure url="https://example.com/audio-3.mp3" type="audio/mpeg" length="999" />
</item>
<item>
<title>Episode 4</title>
<enclosure url="" type="audio/mpeg" length="111" />
<media:thumbnail url="" />
<itunes:image href="" />
</item>
</channel>
</rss>
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
Loading