84 lines
2.4 KiB
Ruby
84 lines
2.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Banzai
|
|
module Filter
|
|
# Find every image that isn't already wrapped in an `a` tag, and that has
|
|
# a `src` attribute ending with an audio or video extension, add a new audio or video node and
|
|
# a "Download" link in the case the media cannot be played.
|
|
class PlayableLinkFilter < HTML::Pipeline::Filter
|
|
def call
|
|
doc.xpath('descendant-or-self::img[not(ancestor::a)]').each do |el|
|
|
el.replace(media_node(doc, el)) if has_media_extension?(el)
|
|
end
|
|
|
|
doc
|
|
end
|
|
|
|
private
|
|
|
|
def media_type
|
|
raise NotImplementedError
|
|
end
|
|
|
|
def safe_media_ext
|
|
raise NotImplementedError
|
|
end
|
|
|
|
def extra_element_attrs
|
|
{}
|
|
end
|
|
|
|
def has_media_extension?(element)
|
|
src = element.attr('data-canonical-src').presence || element.attr('src')
|
|
|
|
return unless src.present?
|
|
|
|
src_ext = File.extname(src).sub('.', '').downcase
|
|
safe_media_ext.include?(src_ext)
|
|
end
|
|
|
|
def media_element(doc, element)
|
|
media_element_attrs = {
|
|
src: element['src'],
|
|
controls: true,
|
|
'data-setup': '{}',
|
|
'data-title': element['title'] || element['alt']
|
|
}.merge!(extra_element_attrs)
|
|
|
|
if element['data-canonical-src']
|
|
media_element_attrs['data-canonical-src'] = element['data-canonical-src']
|
|
end
|
|
|
|
doc.document.create_element(media_type, media_element_attrs)
|
|
end
|
|
|
|
def download_link(doc, element)
|
|
link_content = element['title'] || element['alt']
|
|
|
|
link_element_attrs = {
|
|
href: element['src'],
|
|
target: '_blank',
|
|
rel: 'noopener noreferrer',
|
|
title: "Download '#{link_content}'"
|
|
}
|
|
|
|
# make sure the original non-proxied src carries over
|
|
if element['data-canonical-src']
|
|
link_element_attrs['data-canonical-src'] = element['data-canonical-src']
|
|
end
|
|
|
|
doc.document.create_element('a', link_content, link_element_attrs)
|
|
end
|
|
|
|
def media_node(doc, element)
|
|
container_element_attrs = { class: "media-container #{media_type}-container" }
|
|
|
|
doc.document.create_element('span', container_element_attrs).tap do |container|
|
|
container.add_child(media_element(doc, element))
|
|
container.add_child(download_link(doc, element))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|