lib/onebox/engine/youtube_onebox.rb in onebox-1.5.50 vs lib/onebox/engine/youtube_onebox.rb in onebox-1.5.60

- old
+ new

@@ -5,139 +5,148 @@ include StandardEmbed matches_regexp(/^https?:\/\/(?:www\.)?(?:m\.)?(?:youtube\.com|youtu\.be)\/.+$/) always_https - # 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 - 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 + WIDTH ||= 480 + HEIGHT ||= 360 - if uri.path =~ /\/embed\// - # A slash, then embed, then another 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='https://i1.ytimg.com/vi/#{video_id}/hqdefault.jpg' width='480' height='270'>" + "<img src='https://i.ytimg.com/vi/#{video_id}/hqdefault.jpg' width='#{WIDTH}' height='#{HEIGHT}'>" + elsif list_id + "<img src='#{list_thumbnail_url}' width='#{WIDTH}' height='#{HEIGHT}'>" 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}?#{embed_params}\" frameborder=\"0\" allowfullscreen></iframe>" - elsif params['list'] - # YouTube Playlist URL (https://www.youtube.com/playlist?list=PLBsP89CPrMeOwWHwmD6FzkKIca-GjAD_f) - # in case of cast_sender.js console errors, see: http://stackoverflow.com/q/25814914 - html = "<iframe width=\"480\" height=\"270\" src=\"https://www.youtube.com/embed/videoseries?list=#{params['list']}&wmode=transparent&rel=0&autohide=1&showinfo=1&enablejsapi=1\" frameborder=\"0\" allowfullscreen></iframe>" + <<-HTML + <iframe width="#{WIDTH}" + height="#{HEIGHT}" + src="https://www.youtube.com/embed/#{video_id}?#{embed_params}" + frameborder="0" + allowfullscreen> + </iframe> + HTML + elsif list_id + <<-HTML + <iframe width="#{WIDTH}" + height="#{HEIGHT}" + src="https://www.youtube.com/embed/videoseries?list=#{list_id}&wmode=transparent&rel=0&autohide=1&showinfo=1&enablejsapi=1" + frameborder="0" + allowfullscreen> + </iframe> + HTML else # 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://" + return if Onebox::Helpers.blank?(html) + html.gsub!("http:", "https:") + html.gsub!(/['"]\/\//, "https://") + html end - - html end def video_title - yt_oembed_url = "https://www.youtube.com/oembed?format=json&url=https://www.youtube.com/watch?v=#{video_id.split('?')[0]}" - yt_oembed_data = Onebox::Helpers.symbolize_keys(::MultiJson.load(Onebox::Helpers.fetch_response(yt_oembed_url).body)) - yt_oembed_data[:title] - rescue - return nil + @video_title ||= begin + url = "https://www.youtube.com/oembed?format=json&url=https://www.youtube.com/watch?v=#{video_id}" + data = Onebox::Helpers.symbolize_keys(::MultiJson.load(Onebox::Helpers.fetch_response(url).body)) + data[:title] + rescue + nil + end end - # Regex to parse strings like "1h3m2s". Also accepts bare numbers (which are seconds). - TIMESTR_REGEX = /(\d+h)?(\d+m)?(\d+s?)?/ + private - def embed_params - p = {'feature' => 'oembed', 'wmode' => 'opaque'} + def video_id + @video_id ||= begin + # http://youtu.be/afyK1HSFfgw + if uri.host["youtu.be"] + id = uri.path[/\/([\w\-]+)\//, 1] + return id if id + end - p['list'] = params['list'] if params['list'] + # https://www.youtube.com/embed/vsF0K3Ou1v0 + if uri.path["/embed/"] + id = uri.path[/\/embed\/([\w\-]+)/, 1] + return id if id + end - # 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] + # https://www.youtube.com/watch?v=Z0UISCEe52Y + params['v'] + end end - 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 + def list_id + @list_id ||= params['list'] end - URI.encode_www_form(p) - end + def list_thumbnail_url + @list_thumbnail_url ||= begin + url = "https://www.youtube.com/oembed?format=json&url=https://www.youtube.com/playlist?list=#{list_id}" + data = Onebox::Helpers.symbolize_keys(::MultiJson.load(Onebox::Helpers.fetch_response(url).body)) + data[:thumbnail_url] + rescue + nil + end + end - private + def embed_params + p = {'feature' => 'oembed', 'wmode' => 'opaque'} - # 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 + p['list'] = list_id if list_id - (h * 60 * 60) + (m * 60) + s - else - nil + # Parse timestrings, and assign the result as a start= parameter + start = if params['start'] + params['start'] + elsif params['t'] + 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 + uri.fragment[2..-1] + end + + 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 - 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 + def parse_timestring(string) + if string =~ /(\d+h)?(\d+m)?(\d+s?)?/ + ($1.to_i * 3600) + ($2.to_i * 60) + $3.to_i + end end - rescue - return {} - 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 + p = {} + CGI.parse(uri.query).each { |k, v| p[k] = v.first } + p + end + rescue + {} + end end end end