Converting a regular Youtube 'link' into an embedded video
My goal: I am trying to allow users to embed a link to a Youtube video in my site, while giving me control over the player's settings.
I would like to do this by only askin开发者_如何学Cg the user to supply the link (not the entire embed code), from where I can somehow paste that link into the embed code.
I've tried doing a simple substitution with a few Youtube links (http://youtu.be/...
) but they don't work, saying 'movie not loaded'. Is there a dependable way to do this?
I do this quite often for clients, the gist of it is that you parse out the ID from the URL, then generate the iframe
HTML using this.
def youtube_embed(youtube_url)
if youtube_url[/youtu\.be\/([^\?]*)/]
youtube_id = $1
else
# Regex from # http://stackoverflow.com/questions/3452546/javascript-regex-how-to-get-youtube-video-id-from-url/4811367#4811367
youtube_url[/^.*((v\/)|(embed\/)|(watch\?))\??v?=?([^\&\?]*).*/]
youtube_id = $5
end
%Q{<iframe title="YouTube video player" width="640" height="390" src="http://www.youtube.com/embed/#{ youtube_id }" frameborder="0" allowfullscreen></iframe>}
end
youtube_embed('youtu.be/jJrzIdDUfT4')
# => <iframe title="YouTube video player" width="640" height="390" src="http://www.youtube.com/embed/jJrzIdDUfT4" frameborder="0" allowfullscreen></iframe>
I put this in a helper. Change the height, width and options to taste.
Another answer is to use this gem which handles youtube and vimeo, and could be expanded to more. It also integrates well with AR so you can cache the resulting html instead of filtering on each render:
https://github.com/dejan/auto_html
I used the highest rated answer about with the function youtube_embed but when I implemented in my view I was seeing the iframe code appear in my page, but no video. I added raw before the function call and all is good with the page now.
Inside my view.html.erb
<p><%= raw(youtube_embed(@experiment.researchsection.videolink)) %></p>
something like this (for ex. in model):
@@video_regexp = [ /^(?:https?:\/\/)?(?:www\.)?youtube\.com(?:\/v\/|\/watch\?v=)([A-Za-z0-9_-]{11})/,
/^(?:https?:\/\/)?(?:www\.)?youtu\.be\/([A-Za-z0-9_-]{11})/,
/^(?:https?:\/\/)?(?:www\.)?youtube\.com\/user\/[^\/]+\/?#(?:[^\/]+\/){1,4}([A-Za-z0-9_-]{11})/
]
def video_id
@@video_regexp.each { |m| return m.match(source_url)[1] unless m.nil? }
end
where source_url
is the full link to video.
then a helper:
def youtube_video(video_id)
render :partial => 'youtube_video', :locals => { :id => video_id }
end
and sample partial (haml):
%iframe{:allowfullscreen => "", :frameborder => "0", :height => "349",
:src => "http://www.youtube.com/embed/#{id}", :width => "560"}
and in view as simple as:
= youtube_video Model.video_id
This is what youtube uses:
<iframe width="425" height="349" src="http://www.youtube.com/embed/zb-gmJVW5lw" frameborder="0" allowfullscreen></iframe>
Then just use a regexp to change the link from:
http://www.youtube.com/watch?v=zb-gmJVW5lw
into:
http://www.youtube.com/embed/zb-gmJVW5lw
Here's a proof of concept regexp for matching regular youtube links:
- http://rubular.com/r/Fitt7PPJW1
And here's a proof of concept regexp for matching youtu.be links:
- http://rubular.com/r/MTgd9nXzFA
Note that the embed url can also be loaded in the browser which opens a page where the video is fullscreen.
I had to incorporate this functionality in one of my recent projects. I had to support linking both YouTube and Vimeo videos. I am using the 'uri' module of Ruby and the HTTParty. Basically I came with the following:
class LinkVideo < ActiveRecord::Base
require 'uri'
include HTTParty
cattr_reader :per_page
@@per_page = 12
belongs_to :user
validates :weblink, :presence => true, :domain => true
def embed(width = "640", height = "390")
embed_code = nil
case base_uri
when "www.youtube.com"
embed_code = "<object width='#{width}' height='#{height}'>" +
"<param name='movie' value='#{url}'></param>" +
"<param name='allowFullScreen' value='false'></param>" +
"<param name='allowscriptaccess' value='always'></param>" +
"<embed src='#{url}' type='application/x-shockwave-flash' allowscriptaccess='always' allowfullscreen='false'
width='#{width}' height='#{height}'> </embed>" +
"</object>"
when "www.vimeo.com"
embed_code = "<iframe src='#{url}' width='#{width}' height='#{height}' frameborder='0'></iframe>"
end
embed_code
end
def url
url = nil
case base_uri
when "www.youtube.com"
url = "http://www.youtube.com/v/" + video_id + "&hl=en_US&fs=1"
when "www.vimeo.com"
url = "http://player.vimeo.com/video/" + video_id
end
url
end
def thumbnail
url = nil
case base_uri
when "www.youtube.com"
url = "http://img.youtube.com/vi/" + video_id + "/2.jpg"
when "www.vimeo.com"
url = thumbnail_path( image_base_uri, video_id )
end
url
end
# Video Paths:
# http://www.youtube.com/watch?v=Gqraan6sBjk
# http://www.vimeo.com/21618919
# Thumbnail Paths:
# http://img.youtube.com/vi/Gqraan6sBjk/2.jpg
private
def image_base_uri
image_base_uri = nil
case base_uri
when "www.youtube.com"
image_base_uri = "http://img.youtube.com/vi/"
when "www.vimeo.com"
image_base_uri = "http://vimeo.com/api/v2/video/"
end
image_base_uri
end
def thumbnail_path(base_uri, videoid = nil, format = 'xml')
path = nil
return path if base_uri.nil?
xml = HTTParty.get( base_uri + ( videoid.nil? ? video_id : videoid ) + format.insert(0, '.') )
values = xml.parsed_response.values_at("videos").first.fetch('video')
if values["user_portrait_medium"].include?('100')
path = values["user_portrait_medium"]
else values["user_portrait_large"].include?('100')
path = values["user_portrait_large"]
end
path
end
def base_uri
@uri ||= parse_it
@uri.host
end
def video_id
video_id = nil
case base_uri
when "www.youtube.com"
video_id = @uri.query.split('=')[1].slice(0, 11)
when "www.vimeo.com"
video_id = @uri.path.delete('/')
end
video_id
end
def parse_it
@uri = URI.parse( weblink )
end
end
You might prefer to roll your own solution, but it's worth considering the Embedly API. http://embed.ly/
I think the simplest way to achieve what you're trying to do is the following:
You're trying to get from this:
http://www.youtube.com/watch?v=zb-gmJVW5lw
To this:
<iframe width="640" height="360" src="http://www.youtube.com/embed/zb-gmJVW5l" frameborder="0" allowfullscreen></iframe>
So you can just simply do this:
#### Use a regular expression to extract the video code
@video_id = (/([\w-]{11})/.match(@v_url)).to_s
#### Put this information in the right format
@embed_code = "<iframe width='640' height='360' src='http://www.youtube.com/embed/#{@video_id}' frameborder='0' allowfullscreen></iframe>"
And then in your view, just do:
<%= raw(@embed_code) %>
I have created a simple helper to embed YouTube videos:
# Helpers for better embedding and manipulation of videos
module VideosHelper
# Regex to find YouTube's video ID
YOUTUBE_REGEX = %r(^(http[s]*:\/\/)?(www.)?(youtube.com|youtu.be)\/(watch\?v=){0,1}([a-zA-Z0-9_-]{11}))
# Embeds YouTube video of given URL in an iframe
#
# @param url [String] URL of desired video
# @param width [String] width of embedded video. Can be any valid CSS unit
# @param height [String] height of embedded video. Can be any valid CSS unit
# @return [String] HTML string of embedded video
def youtube_embed(url, width = "100%", height = "250px")
youtube_id = find_youtube_id(url)
result = %(<iframe title="YouTube video player" width="#{width}"
height="#{height}" src="//www.youtube.com/embed/#{ youtube_id }"
frameborder="0" allowfullscreen></iframe>)
result.html_safe
end
# Finds YouTube's video ID from given URL or [nil] if URL is invalid
# The video ID matches the RegEx \[a-zA-Z0-9_-]{11}\
#
# @param url [String] URL of desired video
# @return [String] video ID of given URL
def find_youtube_id(url)
url = sanitize(url).to_str
matches = YOUTUBE_REGEX.match url
if matches
matches[6] || matches[5]
end
end
end
Let's say you have an Article model with a field (string) called embed :
YouTube examples to handle:
https://www.youtube.com/watch?v=u75Zsl1ECPQ&list=PLu9lbDbw-S8zyBwu9_aA2nE-3QocgyzRE&index=4
https://www.youtube.com/watch?v=u75Zsl1ECPQ
https://youtu.be/u75Zsl1ECPQ
https://youtu.be/u75Zsl1ECPQ?t=12
etc..
In the model (note.. I'm not applying width and height in the iframe, because I'll handle it globally in a stylesheet. Also, you can remove that regex and uncomment self.embed.include? .. to achieve the same validation:
#show (before_save)
# Returns a string (html_safe) .. iframe element from the embed string field
def iframe
if self.embed.present?
### YouTube
## Browser link --- use array to handle most playlist links, etc
if self.embed =~ /^(https?:\/\/)?(www\.)?youtube.com\/watch\?v=/ # self.embed.include? 'https://www.youtube.com/watch?v='
("<iframe src='https://www.youtube.com/embed/#{self.embed[32..42]}' frameborder='0' allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture' allowfullscreen></iframe>").html_safe
## YouTube share link --- using array, because .split('https://youtu.be/').last wouldn't handle start at option ()?t=12)
elsif self.embed =~ /^(https?:\/\/)?(www\.)?youtu.be\// # self.embed.include? 'https://youtu.be/'
("<iframe src='https://www.youtube.com/embed/#{self.embed[17..27]}' frameborder='0' allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture' allowfullscreen></iframe>").html_safe
### Validate + Generate iframe for whatever other embeds you want to allow (Google Maps, Vimeo, etc)
# elsif
else
self.embed = nil
end
end
end
In the articles#show view (note.. bootstrap classes for handling responsiveness):
<% if @article.embed.present? # no markup if nil %>
<div class="embed-responsive embed-responsive-16by9">
<%= @article.iframe %>
</div><!-- .embed-responsive -->
<% end %>
If you want to grab the thumbnail for the embed, this method works the same way:
#_articles (before_save)
# Returns a string (html_safe) .. img element from the embed string field
def thumb
if self.embed.present?
### YouTube
## Each YouTube video has 4 generated images [ /0 .. /3 ]
if self.embed =~ /^(https?:\/\/)?(www\.)?youtube.com\/watch\?v=/
("<img alt='Media' class='card-img-top' src='https://img.youtube.com/vi/#{self.embed[32..42]}/0.jpg' />").html_safe
elsif self.embed =~ /^(https?:\/\/)?(www\.)?youtu.be\//
("<img alt='Media' class='card-img-top' src='https://img.youtube.com/vi/#{self.embed[17..27]}/0.jpg' />").html_safe
else
self.embed = nil
end
end
end
So, in the articles#index view, you could call on the thumb method:
<% if article.embed.present? %>
<%= link_to article.thumb, article_path(article) %>
<% end %>
In each case, you'll use an activerecord callback in the article.rb model:
before_save :iframe, :thumb
精彩评论