Class: Api::V1::OembedController
- Inherits:
-
BaseController
- Object
- ActionController::API
- BaseController
- Api::V1::OembedController
- 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
-
#faq ⇒ Object
GET /api/v1/oembed/faq Returns rendered FAQ HTML for embedding in Redactor editor.
-
#image ⇒ Object
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.
-
#post ⇒ Object
GET /api/v1/oembed/post Consumer-facing oEmbed for blog-post pages.
-
#product ⇒ Object
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.
-
#show ⇒ Object
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).
-
#video ⇒ Object
GET /api/v1/oembed/video Returns rendered video HTML for embedding in Redactor editor Uses Shaka player with HLS/captions support.
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
#faq ⇒ Object
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
Usage:
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] }
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'app/controllers/api/v1/oembed_controller.rb', line 94 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.}") render json: { error: e. }, status: :not_found rescue StandardError => e Rails.logger.error("[oEmbed] Error fetching FAQ embed: #{ids.join(',')} - #{e.class}: #{e.}") render json: { error: 'Failed to fetch FAQ embed data' }, status: :internal_server_error end |
#image ⇒ Object
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)
Usage:
GET /api/v1/oembed/image?image_id=123&width=800&caption=My%20caption
=> { html: "...", type: "photo", ... }
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'app/controllers/api/v1/oembed_controller.rb', line 155 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 = image_params.merge(image_id: image_id) Rails.logger.debug { "[oEmbed] Image options after merge: #{.inspect}" } provider = Oembed::ImageProvider.new result = provider.get() 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.}") render json: { error: e. }, status: :not_found rescue StandardError => e Rails.logger.error("[oEmbed] Error fetching image embed: #{image_id} - #{e.class}: #{e.}") Rails.logger.error("[oEmbed] Backtrace: #{e.backtrace.first(10).join("\n")}") render json: { error: "Failed to fetch image embed data: #{e.}" }, status: :internal_server_error end |
#post ⇒ Object
GET /api/v1/oembed/post
Consumer-facing oEmbed for blog-post pages. Backs the
application/json+oembed discovery links so iframely/Gamma, Slack, Notion,
etc. resolve the canonical title/description/thumbnail instead of scraping
in-article Product JSON-LD.
Required params:
url - The blog-post URL
Optional params:
format - 'json' (default) or 'xml'
Usage:
GET /api/v1/oembed/post?url=https://www.warmlyyours.com/en-US/posts/my-slug
=> { version: "1.0", type: "link", title: "...", description: "...", ... }
72 73 74 75 76 77 |
# File 'app/controllers/api/v1/oembed_controller.rb', line 72 def post url = params[:url] return render json: { error: 'URL parameter is required' }, status: :bad_request if url.blank? handle_wy_post(url) end |
#product ⇒ Object
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')
Usage:
GET /api/v1/oembed/product?uuid=abc123-...
GET /api/v1/oembed/product?sku=TRT512-KIT&locale=en-US
=> { html: "...", type: "rich", sku: "TRT512-KIT" }
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'app/controllers/api/v1/oembed_controller.rb', line 266 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.}") render json: { error: e. }, status: :not_found rescue Oembed::ProductProvider::ProductUnavailableError => e Rails.logger.warn("[oEmbed] Product unavailable: #{sku} - #{e.}") render json: { error: e. }, status: :gone rescue StandardError => e Rails.logger.error("[oEmbed] Error fetching product embed: #{sku} - #{e.class}: #{e.}") 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 |
#show ⇒ Object
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)
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'app/controllers/api/v1/oembed_controller.rb', line 32 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 = {} [:maxwidth] = params[:maxwidth].to_i if params[:maxwidth].present? [:maxheight] = params[:maxheight].to_i if params[:maxheight].present? # Set reasonable defaults for embed size [:maxwidth] ||= 800 [:maxheight] ||= 450 # 16:9 aspect ratio # Check if this is a WarmlyYours blog-post URL first (consumer discovery may # hit the generic endpoint rather than the dedicated /oembed/post one). return handle_wy_post(url) if Oembed::PostProvider.new.handles?(url) # Then WarmlyYours videos. wy_provider = Oembed::WyVideoProvider.new return handle_wy_video(url, ) if wy_provider.handles?(url) # Fall through to standard oEmbed providers for external URLs (url, ) end |
#video ⇒ Object
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'
Usage:
GET /api/v1/oembed/video?uuid=abc123-...
=> { html: "<video ...data-embedded-asset-uuid='...'...", type: "video", ... }
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'app/controllers/api/v1/oembed_controller.rb', line 210 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}" = { player_type: params[:player_type] || 'html5', include_wrapper: true } provider = Oembed::WyVideoProvider.new result = provider.get(video_url, ) ({ version: '1.0', 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.}") render json: { error: e. }, status: :not_found rescue StandardError => e Rails.logger.error("[oEmbed] Error fetching video embed: #{video_id} - #{e.class}: #{e.}") Rails.logger.error("[oEmbed] Backtrace: #{e.backtrace.first(10).join("\n")}") render json: { error: "Failed to fetch video embed data: #{e.}" }, status: :internal_server_error end |