You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

71 lines
1.7 KiB

  1. # frozen_string_literal: true
  2. class FetchOEmbedService
  3. attr_reader :url, :options, :format, :endpoint_url
  4. def call(url, options = {})
  5. @url = url
  6. @options = options
  7. discover_endpoint!
  8. fetch!
  9. end
  10. private
  11. def discover_endpoint!
  12. return if html.nil?
  13. @format = @options[:format]
  14. page = Nokogiri::HTML(html)
  15. if @format.nil? || @format == :json
  16. @endpoint_url ||= page.at_xpath('//link[@type="application/json+oembed"]')&.attribute('href')&.value
  17. @format ||= :json if @endpoint_url
  18. end
  19. if @format.nil? || @format == :xml
  20. @endpoint_url ||= page.at_xpath('//link[@type="text/xml+oembed"]')&.attribute('href')&.value
  21. @format ||= :xml if @endpoint_url
  22. end
  23. return if @endpoint_url.blank?
  24. @endpoint_url = Addressable::URI.parse(@endpoint_url).to_s
  25. rescue Addressable::URI::InvalidURIError
  26. @endpoint_url = nil
  27. end
  28. def fetch!
  29. return if @endpoint_url.blank?
  30. body = Request.new(:get, @endpoint_url).perform do |res|
  31. res.code != 200 ? nil : res.body_with_limit
  32. end
  33. validate(parse_for_format(body)) unless body.nil?
  34. rescue Oj::ParseError, Ox::ParseError
  35. nil
  36. end
  37. def parse_for_format(body)
  38. case @format
  39. when :json
  40. Oj.load(body, mode: :strict)&.with_indifferent_access
  41. when :xml
  42. Ox.load(body, mode: :hash_no_attrs)&.with_indifferent_access&.dig(:oembed)
  43. end
  44. end
  45. def validate(oembed)
  46. oembed if oembed[:version] == '1.0' && oembed[:type].present?
  47. end
  48. def html
  49. return @html if defined?(@html)
  50. @html = @options[:html] || Request.new(:get, @url).perform do |res|
  51. res.code != 200 || res.mime_type != 'text/html' ? nil : res.body_with_limit
  52. end
  53. end
  54. end