33

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 asking 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?

sscirrus
  • 50,379
  • 41
  • 125
  • 211

10 Answers10

69

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.

Douglas F Shearer
  • 23,408
  • 2
  • 45
  • 48
  • 11
    Note to others: if you use `youtube_embed` as .erb in a view, add `raw`, i.e. `

    `. Thanks @PrestonSpratt.
    – sscirrus Jan 22 '13 at 22:11
  • awesome, love this solution, saved me hours and learned something too :-) – Richlewis May 01 '14 at 18:59
  • 1
    I would [sanitize](http://api.rubyonrails.org/classes/ActionView/Helpers/SanitizeHelper.html#method-i-sanitize) the url first and then use either of `raw` or `html_safe` methods! – Yan Foto Jan 07 '15 at 21:40
7

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>
Preston Spratt
  • 111
  • 1
  • 2
7

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

Andrew Kuklewicz
  • 10,291
  • 1
  • 31
  • 42
4

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
schiza
  • 1,870
  • 1
  • 15
  • 18
  • hey, I can confirm that this code works perfectly. Very nice, thanks a lot it helped me out. In case anyone is following this, I had to replace `Model.video_id` with an instance variable `@video.video_id` and I also had to replace `source_url` with `self.youtube_url`. Was dreading having to figure all this out and you saved me a lot of time. – stephenmurdoch Aug 17 '11 at 10:03
  • yeah, ```Model``` and ```source_url``` where just examples ;) great you could use it :) – schiza Aug 18 '11 at 06:32
3

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:

And here's a proof of concept regexp for matching youtu.be links:

Note that the embed url can also be loaded in the browser which opens a page where the video is fullscreen.

rzetterberg
  • 9,544
  • 3
  • 40
  • 54
  • here's a random link I just got from Youtube: http://youtu.be/jJrzIdDUfT4. What can I do with this? – sscirrus May 06 '11 at 09:09
  • Take out jJrzIdDUfT4 and insert after embed :) "youtu.be/JrzldDUft4" is the same as "http://www.youtube.com/watch?v=jJrzIdDUfT4" – rzetterberg May 06 '11 at 09:12
3

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 + "&amp;hl=en_US&amp;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
Syed Aslam
  • 8,387
  • 5
  • 39
  • 53
2

You might prefer to roll your own solution, but it's worth considering the Embedly API. http://embed.ly/

Michael Hellein
  • 3,468
  • 1
  • 23
  • 21
1

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) %>
MrJacket
  • 391
  • 3
  • 14
  • This doesn't work for URLs that fall outside that format - there are lots of them out there! – sscirrus Sep 13 '12 at 03:04
  • Right, in that case you'll have to replace the regular expression by the right one till you get just the video_id – MrJacket Sep 13 '12 at 22:21
1

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
Yan Foto
  • 8,951
  • 4
  • 45
  • 79
  • To get **YouTube** or **Vimeo** iframe from a url, I created a [gist](https://gist.github.com/a-barbieri/a93281cd5c01025664e5cbd781cd917c) which is based on your code. – a.barbieri Jun 06 '17 at 09:26
  • @a.barbieri great! Thanks for linking :) – Yan Foto Jun 06 '17 at 16:22
0

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 output, 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):

  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>"
      ## 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>"
      ### 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.html_safe %>
    </div><!-- .embed-responsive -->
  <% end %>

If you want to grab the thumbnail for the embed, this method works the same way:

  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='http://img.youtube.com/vi/#{self.embed[32..42]}/0.jpg' />"
      elsif self.embed =~ /^(https?:\/\/)?(www\.)?youtu.be\//
        "<img alt='Media' class='card-img-top' src='http://img.youtube.com/vi/#{self.embed[17..27]}/0.jpg' />"
      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.html_safe, article_path(article) %>
<% end # consider else.. fallback image ..end %>

Working Example: https://rails-react-bootstrap.herokuapp.com/articles/45