lib/onebox/engine/youtube_onebox.rb in onebox-1.4.4 vs lib/onebox/engine/youtube_onebox.rb in onebox-1.4.5

- old
+ new

@@ -9,51 +9,127 @@ # Try to get the video ID. Works for URLs of the form: # * https://www.youtube.com/watch?v=Z0UISCEe52Y # * http://youtu.be/afyK1HSFfgw # * https://www.youtube.com/embed/vsF0K3Ou1v0 def video_id - match = @url.match(/^https?:\/\/(?:www\.)?(?:m\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_\-]{11})(?:[#&\?]t=(([0-9]+[smh]?)+))?$/) - match && match[1] + if uri.host =~ /youtu.be/ + # A slash, then capture all non-slash characters remaining + match = uri.path.match(/\/([^\/]+)/) + return match[1] if match && match[1] + end + + if uri.path =~ /\/embed\// + # A slash, then embed, then anotther slash, then capture all remaining non-slash characters + match = uri.path.match(/\/embed\/([^\/]+)/) + return match[1] if match && match[1] + end + + if params['v'] + return params['v'] + end + + nil + rescue + return nil end def placeholder_html if video_id - "<img src='http://i1.ytimg.com/vi/#{video_id}/hqdefault.jpg' width='480' height='270'>" + "<img src='https://i1.ytimg.com/vi/#{video_id}/hqdefault.jpg' width='480' height='270'>" else to_html end end def to_html if video_id # Avoid making HTTP requests if we are able to get the video ID from the # URL. - html = "<iframe width=\"480\" height=\"270\" src=\"https://www.youtube.com/embed/#{video_id}?feature=oembed\" frameborder=\"0\" allowfullscreen></iframe>" + html = "<iframe width=\"480\" height=\"270\" src=\"https://www.youtube.com/embed/#{video_id}?#{embed_params}\" frameborder=\"0\" allowfullscreen></iframe>" else - # Fall back to making HTTP requests. - html = raw[:html] || "" + # for channel pages + html = Onebox::Engine::WhitelistedGenericOnebox.new(@url, @cache, @timeout).to_html + return nil unless html + html = html.gsub /http:/, 'https:' + html = html.gsub /"\/\//, '"https://' + html = html.gsub /'\/\//, "'https://" end - rewrite_agnostic(append_params(html)) + html end - def append_params(html) - result = html.dup - result.gsub! /(src="[^"]+)/, '\1&wmode=opaque' - if url =~ /t=(\d+h)?(\d+m)?(\d+s?)?/ - h = Regexp.last_match[1].to_i - m = Regexp.last_match[2].to_i - s = Regexp.last_match[3].to_i + # Regex to parse strings like "1h3m2s". Also accepts bare numbers (which are seconds). + TIMESTR_REGEX = /(\d+h)?(\d+m)?(\d+s?)?/ - total = (h * 60 * 60) + (m * 60) + s + def embed_params + p = {'feature' => 'oembed', 'wmode' => 'opaque'} - result.gsub! /(src="[^"]+)/, '\1&start=' + total.to_s + p['list'] = params['list'] if params['list'] + + # Parse timestrings, and assign the result as a start= parameter + start = nil + if params['start'] + start = params['start'] + elsif params['t'] + start = params['t'] + elsif uri.fragment && uri.fragment.start_with?('t=') + # referencing uri is safe here because any throws were already caught by video_id returning nil + # remove the t= from the start + start = uri.fragment[2..-1] end - result + p['start'] = parse_timestring(start) if start + p['end'] = parse_timestring params['end'] if params['end'] + + # Official workaround for looping videos + # https://developers.google.com/youtube/player_parameters#loop + # use params.include? so that you can just add "&loop" + if params.include? 'loop' + p['loop'] = 1 + p['playlist'] = video_id + end + + URI.encode_www_form(p) end - def rewrite_agnostic(html) - html.gsub(/https?:\/\//, '//') + private + + # Takes a timestring and returns the number of seconds it represents. + def parse_timestring(string) + tm = string.match TIMESTR_REGEX + if tm && !tm[0].empty? + h = tm[1].to_i + m = tm[2].to_i + s = tm[3].to_i + + (h * 60 * 60) + (m * 60) + s + else + nil + end end + + # Note: May throw! Make sure to recue. + def uri + @_uri ||= URI(@url) + end + + def params + return {} unless uri.query + # This mapping is necessary because CGI.parse returns a hash of keys to arrays. + # And *that* is necessary because querystrings support arrays, so they + # force you to deal with it to avoid security issues that would pop up + # if one day it suddenly gave you an array. + # + # However, we aren't interested. Just take the first one. + @_params ||= begin + params = {} + CGI.parse(uri.query).each do |k, v| + params[k] = v.first + end + params + end + rescue + return {} + end + end end end