Class: Api::V1::OembedController

Inherits:
BaseController
  • Object
show all
Defined in:
app/controllers/api/v1/oembed_controller.rb

Overview

oEmbed API endpoint for Redactor 4 embed plugin
Converts URLs (YouTube, Vimeo, WarmlyYours videos, etc.) to embeddable HTML

For external providers: Uses ruby-oembed gem with built-in providers + Noembed fallback
For WY videos: Uses custom Oembed::WyVideoProvider for proper Shaka player support

Providers are registered in config/initializers/oembed.rb
Examples:
GET /api/v1/oembed?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ
=> { html: "<iframe ...>", type: "video", provider_name: "YouTube" }

GET /api/v1/oembed?url=https://www.warmlyyours.com/videos/my-video-slug
=> { html: "...", type: "video", provider_name: "WarmlyYours" }

Instance Method Summary collapse

Methods inherited from BaseController

#catalog_for_request, #error!, #locale_for_request, #logger, #render_bad_request_response, #render_internal_server_error, #render_not_found_response, #render_result, #render_unprocessable_entity_response, #set_locale, #store_for_request, #underscore_params

Instance Method Details

#faqObject

GET /api/v1/oembed/faq
Returns rendered FAQ HTML for embedding in Redactor editor

Required params (one of):
uuid - EmbeddedAsset UUID (new flow, preferred)
ids - Comma-separated FAQ IDs (legacy flow)

Optional params:
title - Custom section title
sort - Sort option: 'popularity' for most helpful first

Example:
GET /api/v1/oembed/faq?uuid=abc123-...
GET /api/v1/oembed/faq?ids=123,456&title=Custom%20Title&sort=popularity
=> { html: "...", type: "rich", faq_ids: [123, 456] }



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'app/controllers/api/v1/oembed_controller.rb', line 63

def faq
  Rails.logger.debug { "[oEmbed] FAQ params received: #{params.to_unsafe_h.inspect}" }

  # Support UUID-based lookup (new flow) or direct ids (legacy flow)
  return render_faq_by_uuid(params[:uuid]) if params[:uuid].present?

  ids = params[:ids].to_s.split(',').map(&:strip).map(&:to_i).reject(&:zero?)

  return render json: { error: 'ids or uuid parameter is required' }, status: :bad_request if ids.empty?

  provider = Oembed::FaqProvider.new
  result = provider.get(
    ids: ids,
    title: params[:title],
    sort: params[:sort]
  )

  render json: {
    html: result[:html],
    type: result[:type],
    provider_name: result[:provider_name],
    title: result[:title],
    faq_ids: result[:faq_ids],
    faq_count: result[:faq_count]
  }
rescue Oembed::FaqProvider::FaqNotFoundError => e
  Rails.logger.warn("[oEmbed] FAQ not found: #{ids.join(',')} - #{e.message}")
  render json: { error: e.message }, status: :not_found
rescue StandardError => e
  Rails.logger.error("[oEmbed] Error fetching FAQ embed: #{ids.join(',')} - #{e.class}: #{e.message}")
  render json: { error: 'Failed to fetch FAQ embed data' }, status: :internal_server_error
end

#imageObject

GET /api/v1/oembed/image
Returns rendered image HTML for embedding in Redactor editor
Uses image_asset_tag for consistent rendering with the rest of the site

Required params:
image_id - The image ID from the library

Optional params (ImageKit transformations):
width, height - Target dimensions
crop_x, crop_y, crop_w, crop_h - Crop area
crop_mode - Crop mode (pad_resize, force, etc.)
rotate - Rotation in degrees
blur - Blur amount (1-100)
background - Background color for padding
encode_format - Output format (jpeg, png, webp)

Optional params (display):
caption - Image caption text
alt - Alt text override
link - URL to wrap image in link
link_target - Link target (_blank, etc.)
lazyload - Enable lazy loading (default: true)
include_srcset - Include responsive srcset (default: true)
wrapper_tag - Wrapper element: 'figure' (default), 'p', or 'div' (for email compatibility)

Example:
GET /api/v1/oembed/image?image_id=123&width=800&caption=My%20caption
=> { html: "...", type: "photo", ... }



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'app/controllers/api/v1/oembed_controller.rb', line 124

def image
  Rails.logger.debug { "[oEmbed] Image params received: #{params.to_unsafe_h.inspect}" }

  # Support UUID-based lookup (new flow) or direct image_id (legacy flow)
  return render_image_by_uuid(params[:uuid]) if params[:uuid].present?

  image_id = params[:image_id]

  return render json: { error: 'image_id or uuid parameter is required' }, status: :bad_request if image_id.blank?

  # Build options from permitted params
  options = image_params.merge(image_id: image_id)
  Rails.logger.debug { "[oEmbed] Image options after merge: #{options.inspect}" }

  provider = Oembed::ImageProvider.new
  result = provider.get(options)

  render json: {
    html: result[:html],
    type: result[:type],
    provider_name: result[:provider_name],
    title: result[:title],
    thumbnail_url: result[:thumbnail_url],
    width: result[:width],
    height: result[:height],
    # Custom fields for editing
    image_id: result[:image_id],
    image_slug: result[:image_slug],
    alt: result[:alt],
    caption: result[:caption],
    options: result[:options]
  }
rescue Oembed::ImageProvider::ImageNotFoundError => e
  Rails.logger.warn("[oEmbed] Image not found: #{image_id} - #{e.message}")
  render json: { error: e.message }, status: :not_found
rescue StandardError => e
  Rails.logger.error("[oEmbed] Error fetching image embed: #{image_id} - #{e.class}: #{e.message}")
  Rails.logger.error("[oEmbed] Backtrace: #{e.backtrace.first(10).join("\n")}")
  render json: { error: "Failed to fetch image embed data: #{e.message}" }, status: :internal_server_error
end

#productObject

GET /api/v1/oembed/product
Returns rendered product HTML for embedding in Redactor editor
Shows a magazine-style product card with image, title, SKU, price, and add-to-cart

Required params (one of):
uuid - EmbeddedAsset UUID (new flow, preferred)
sku - The product SKU (legacy flow)

Optional params:
locale - Locale for pricing ('en-US' or 'en-CA', defaults to 'en-US')

Example:
GET /api/v1/oembed/product?uuid=abc123-...
GET /api/v1/oembed/product?sku=TRT512-KIT&locale=en-US
=> { html: "...", type: "rich", sku: "TRT512-KIT" }



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'app/controllers/api/v1/oembed_controller.rb', line 234

def product
  Rails.logger.debug { "[oEmbed] Product params received: #{params.to_unsafe_h.inspect}" }

  # Support UUID-based lookup (new flow) or direct sku (legacy flow)
  return render_product_by_uuid(params[:uuid]) if params[:uuid].present?

  sku = params[:sku].to_s.strip.upcase
  locale = params[:locale].presence || I18n.locale.to_s

  return render json: { error: 'sku or uuid parameter is required' }, status: :bad_request if sku.blank?

  provider = Oembed::ProductProvider.new
  result = provider.get(sku: sku, locale: locale)

  render json: {
    html: result[:html],
    type: result[:type],
    provider_name: result[:provider_name],
    title: result[:title],
    sku: result[:sku],
    image_url: result[:image_url]
  }
rescue Oembed::ProductProvider::ProductNotFoundError => e
  Rails.logger.warn("[oEmbed] Product not found: #{sku} - #{e.message}")
  render json: { error: e.message }, status: :not_found
rescue Oembed::ProductProvider::ProductUnavailableError => e
  Rails.logger.warn("[oEmbed] Product unavailable: #{sku} - #{e.message}")
  render json: { error: e.message }, status: :gone
rescue StandardError => e
  Rails.logger.error("[oEmbed] Error fetching product embed: #{sku} - #{e.class}: #{e.message}")
  Rails.logger.error("[oEmbed] Backtrace: #{e.backtrace.first(10).join("\n")}")
  render json: { error: 'Failed to fetch product embed data' }, status: :internal_server_error
end

#showObject

GET /api/v1/oembed
Required params:
url - The URL to convert to embed HTML
Optional params:
maxwidth - Maximum width for the embed (default: 800)
maxheight - Maximum height for the embed (default: 600)



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'app/controllers/api/v1/oembed_controller.rb', line 26

def show
  url = params[:url]

  return render json: { error: 'URL parameter is required' }, status: :bad_request if url.blank?

  # Request oEmbed data with optional size constraints
  options = {}
  options[:maxwidth] = params[:maxwidth].to_i if params[:maxwidth].present?
  options[:maxheight] = params[:maxheight].to_i if params[:maxheight].present?

  # Set reasonable defaults for embed size
  options[:maxwidth] ||= 800
  options[:maxheight] ||= 450 # 16:9 aspect ratio

  # Check if this is a WarmlyYours video URL first
  wy_provider = Oembed::WyVideoProvider.new
  return handle_wy_video(url, options) if wy_provider.handles?(url)

  # Fall through to standard oEmbed providers for external URLs
  handle_external_oembed(url, options)
end

#videoObject

GET /api/v1/oembed/video
Returns rendered video HTML for embedding in Redactor editor
Uses Shaka player with HLS/captions support

Required params (one of):
uuid - EmbeddedAsset UUID (new flow, preferred)
video_id - Video ID (legacy flow)

Optional params:
player_type - 'html5' (default) or 'iframe'

Example:
GET /api/v1/oembed/video?uuid=abc123-...
=> { html: "<video ...data-embedded-asset-uuid='...'...", type: "video", ... }



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'app/controllers/api/v1/oembed_controller.rb', line 179

def video
  Rails.logger.debug { "[oEmbed] Video params received: #{params.to_unsafe_h.inspect}" }

  # Support UUID-based lookup (new flow) or direct video_id (legacy flow)
  return render_video_by_uuid(params[:uuid]) if params[:uuid].present?

  video_id = params[:video_id]
  return render json: { error: 'video_id or uuid parameter is required' }, status: :bad_request if video_id.blank?

  video_record = Video.find_by(id: video_id)
  return render json: { error: 'Video not found' }, status: :not_found unless video_record

  # Build video URL and use existing handler
  # include_wrapper: true so the HTML has the ratio-16x9 wrapper + play button overlay
  video_url = "https://www.warmlyyours.com/videos/#{video_record.slug}"
  options = { player_type: params[:player_type] || 'html5', include_wrapper: true }

  provider = Oembed::WyVideoProvider.new
  result = provider.get(video_url, options)

  render json: {
    html: result[:html],
    type: result[:type],
    provider_name: result[:provider_name],
    title: result[:title],
    thumbnail_url: result[:thumbnail_url],
    width: result[:width],
    height: result[:height],
    video_id: result[:video_id],
    video_slug: result[:video_slug]
  }
rescue Oembed::WyVideoProvider::VideoNotFoundError => e
  Rails.logger.warn("[oEmbed] Video not found: #{video_id} - #{e.message}")
  render json: { error: e.message }, status: :not_found
rescue StandardError => e
  Rails.logger.error("[oEmbed] Error fetching video embed: #{video_id} - #{e.class}: #{e.message}")
  Rails.logger.error("[oEmbed] Backtrace: #{e.backtrace.first(10).join("\n")}")
  render json: { error: "Failed to fetch video embed data: #{e.message}" }, status: :internal_server_error
end