9

I've written a ruby youtube url parser. It's designed to take an input of a youtube url of one of the following structures (these are currently the youtube url structures that I could find, maybe there's more?):

http://youtu.be/sGE4HMvDe-Q
http://www.youtube.com/watch?v=Lp7E973zozc&feature=relmfu
http://www.youtube.com/p/A0C3C1D163BE880A?hl=en_US&fs=1

The aim is to save just the id of the clip or playlist so that it can be embedded, so if it's a clip: 'sGE4HMvDe-Q', or if it's a playlist: 'p/A0C3C1D163BE880A'

The parser I wrote works fine for these urls, but seems a bit brittle and long-winded, I'm just wondering if someone could suggest a nicer ruby approach to this problem?

def parse_youtube
    a = url.split('//').last.split('/')
    b = a.last.split('watch?v=').last.split('?').first.split('&').first
    if a[1] == 'p'
        url = "p/#{b}"
    else
        url = b
    end
end
sawa
  • 165,429
  • 45
  • 277
  • 381
Paul Nelligan
  • 2,015
  • 2
  • 18
  • 18

3 Answers3

19
def parse_youtube url
   regex = /(?:.be\/|\/watch\?v=|\/(?=p\/))([\w\/\-]+)/
   url.match(regex)[1]
end

urls = %w[http://youtu.be/sGE4HMvDe-Q 
          http://www.youtube.com/watch?v=Lp7E973zozc&feature=relmfu
          http://www.youtube.com/p/A0C3C1D163BE880A?hl=en_US&fs=1]

urls.each {|url| puts parse_youtube url }
# sGE4HMvDe-Q
# Lp7E973zozc
# p/A0C3C1D163BE880A

Depending on how you use this, you might want a better validation that the URL is indeed from youtube.

UPDATE:

Coming back to this a few years later. I've always been annoyed by how sloppy the original answer was. Since the validity of the Youtube domain wasn't validated anyway, I've removed some of the slop.

NODE                     EXPLANATION
--------------------------------------------------------------------------------
  (?:                      group, but do not capture:
--------------------------------------------------------------------------------
    .                        any character except \n
--------------------------------------------------------------------------------
    be                       'be'
--------------------------------------------------------------------------------
    \/                       '/'
--------------------------------------------------------------------------------
   |                        OR
--------------------------------------------------------------------------------
    \/                       '/'
--------------------------------------------------------------------------------
    watch                    'watch'
--------------------------------------------------------------------------------
    \?                       '?'
--------------------------------------------------------------------------------
    v=                       'v='
--------------------------------------------------------------------------------
   |                        OR
--------------------------------------------------------------------------------
    \/                       '/'
--------------------------------------------------------------------------------
    (?=                      look ahead to see if there is:
--------------------------------------------------------------------------------
      p                        'p'
--------------------------------------------------------------------------------
      \/                       '/'
--------------------------------------------------------------------------------
    )                        end of look-ahead
--------------------------------------------------------------------------------
  )                        end of grouping
--------------------------------------------------------------------------------
  (                        group and capture to \1:
--------------------------------------------------------------------------------
    [\w\/\-]+                any character of: word characters (a-z,
                             A-Z, 0-9, _), '\/', '\-' (1 or more
                             times (matching the most amount
                             possible))
--------------------------------------------------------------------------------
  )                        end of \1
dee-see
  • 23,668
  • 5
  • 58
  • 91
  • This is good - thank you. Any suggestions on how to change the regex to handle httpS:// prefixes as well? – mxs Aug 09 '13 at 05:52
  • Simply change the `http` for `https?`. – dee-see Aug 13 '13 at 02:12
  • Thanks Vache - I was wondering if the regex could be modified to handle both http and https in the same method. – mxs Aug 13 '13 at 03:56
  • Yeah that's what I'm saying too. The `?` is part of the regex. See [this page](http://www.regular-expressions.info/optional.html) to see what the `?` is all about. You should pick up some basic regex, it's not complicated and it will help you ! ;) – dee-see Aug 13 '13 at 11:55
  • This is the best explanation of a regex I have ever seen. Nice. – noomerikal Apr 11 '15 at 14:40
5

Using the Addressable gem, you can save yourself some work. There's also a URI module in stdlib, but Addressable is more powerful.

require 'addressable/uri'

uri = Addressable::URI.parse(youtube_url)
if uri.path == "/watch"
  uri.query_values["v"] if uri.query_values
else
  uri.path
end

EDIT | Removed madness. Didn't notice Addressable provides #query_values already.

d11wtq
  • 34,788
  • 19
  • 120
  • 195
4
require 'uri'
require 'cgi'

urls = %w[http://youtu.be/sGE4HMvDe-Q 
          http://www.youtube.com/watch?v=Lp7E973zozc&feature=relmfu
          http://www.youtube.com/p/A0C3C1D163BE880A?hl=en_US&fs=1]

def parse_youtube url
  u = URI.parse url
  if u.path =~ /watch/
    p CGI::parse(u.query)["v"].first
  else
    p u.path
  end
end

urls.each { |url| parse_youtube url }
#=> "/sGE4HMvDe-Q"
#=> "Lp7E973zozc"
#=> "/p/A0C3C1D163BE880A"
Vasiliy Ermolovich
  • 24,459
  • 5
  • 79
  • 77