Skip to content

Instantly share code, notes, and snippets.

@mikker
Last active June 25, 2019 08:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikker/001bcfd712c67d2482150462bca27a0d to your computer and use it in GitHub Desktop.
Save mikker/001bcfd712c67d2482150462bca27a0d to your computer and use it in GitHub Desktop.
# frozen_string_literal: true
class VideoUrlParser
VideoMatch = Struct.new(:type, :video_id, :matches)
def initialize(url)
@url = url
@match = match_url
@type = @match.type
@video_id = @match.video_id
end
attr_reader :type, :video_id, :url
def extras
@match.matches
end
def still_image_url
case type
when :youtube
"https://img.youtube.com/vi/#{video_id}/hqdefault.jpg"
when :facebook
"https://graph.facebook.com/#{video_id}/picture?type=normal"
end
end
# rubocop:disable Metrics/LineLength
def embed_html
case type
when :youtube
%(<iframe class='db' width="320" height="180" src="https://www.youtube.com/embed/#{video_id}?showinfo=0&amp;rel=0" frameborder="0" allowfullscreen></iframe>)
when :facebook
%(<div id="fb-root"></div><script>(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.3"; fjs.parentNode.insertBefore(js, fjs);}(document, 'script', 'facebook-jssdk'));</script><div class="fb-video" data-allowfullscreen="1" data-href="/#{extras[:username]}/videos/#{video_id}/?type=1"></div>)
when :vimeo
%(<iframe class='db' src="https://player.vimeo.com/video/148740221" width="500" height="281" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>)
else
"Unrecognized URL type '#{url}'"
end
end
# rubocop:enable Metrics/LineLength
private
# rubocop:disable Metrics/LineLength, Metrics/MethodLength
def match_url
types = {
youtube: [
%r{https?://(www\.)?youtube.com/watch\?v=(?<id>[A-Za-z0-9\-_]+)},
%r{https?://youtu.be/(?<id>[A-Za-z0-9\-_]+)}
],
facebook: %r{https?://(www\.)?facebook.com/(?<username>[A-Za-z0-9\-_\.]+)/videos/(?<id>\d+).*},
vimeo: %r{https?://(www\.)?vimeo.com/(?<id>[0-9]+)}
}
types.each_with_object(VideoMatch.new(:unknown, nil)) do |kv, _unknown|
key, patterns = kv
matches = Array(patterns).reduce(nil) do |_, pattern|
if (hit = url.match(pattern))
break hit
end
nil
end
break VideoMatch.new(key, matches[:id], matches) if matches
end
end
# rubocop:enable Metrics/LineLength, Metrics/MethodLength
end
# typed: strict
# frozen_string_literal: true
require 'sorbet-runtime'
class VideoUrlParser
extend T::Sig
class VideoMatch < T::Struct
extend T::Sig
prop :type, Symbol
prop :video_id, T.nilable(String)
prop :matches, T.nilable(MatchData)
sig do
params(
type: Symbol,
video_id: T.nilable(String),
matches: T.nilable(MatchData)
).void
end
def initialize(type = :unknown, video_id: nil, matches: nil)
@type = T.let(type, Symbol)
@video_id = T.let(video_id, T.nilable(String))
@matches = T.let(matches, T.nilable(MatchData))
end
end
# rubocop:disable Metrics/LineLength
TYPE_PATTERNS = T.let({
youtube: [
%r{https?://(www\.)?youtube.com/watch\?v=(?<id>[A-Za-z0-9\-_]+)},
%r{https?://youtu.be/(?<id>[A-Za-z0-9\-_]+)}
],
facebook: [%r{https?://(www\.)?facebook.com/(?<username>[A-Za-z0-9\-_\.]+)/videos/(?<id>\d+).*}],
vimeo: [%r{https?://(www\.)?vimeo.com/(?<id>[0-9]+)}]
}.freeze, T::Hash[Symbol, T::Array[Regexp]])
# rubocop:enable Metrics/LineLength
sig { params(url: String).void }
def initialize(url)
@url = T.let(url, String)
@match = T.let(match_url, VideoMatch)
end
sig { returns(T.nilable(String)) }
def still_image_url
case type
when :youtube
"https://img.youtube.com/vi/#{video_id}/hqdefault.jpg"
when :facebook
"https://graph.facebook.com/#{video_id}/picture?type=normal"
end
end
# rubocop:disable Metrics/LineLength, Metrics/MethodLength
sig { returns(String) }
def embed_html
case type
when :youtube
%(<iframe class='db' width="320" height="180" src="https://www.youtube.com/embed/#{video_id}?showinfo=0&amp;rel=0" frameborder="0" allowfullscreen></iframe>)
when :facebook
username = T.let(extras, T.untyped)[:username]
%(<div id="fb-root"></div><script>(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.3"; fjs.parentNode.insertBefore(js, fjs);}(document, 'script', 'facebook-jssdk'));</script><div class="fb-video" data-allowfullscreen="1" data-href="/#{username}/videos/#{video_id}/?type=1"></div>)
when :vimeo
%(<iframe class='db' src="https://player.vimeo.com/video/148740221" width="500" height="281" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>)
else
"Unrecognized URL type '#{url}'"
end
end
# rubocop:enable Metrics/LineLength, Metrics/MethodLength
private
# rubocop:disable Metrics/MethodLength
sig { returns(VideoMatch) }
def match_url
TYPE_PATTERNS.each_with_object(VideoMatch.new) do |(key, patterns), _|
matches = patterns.reduce(nil) do |_, pattern|
if (hit = url.match(pattern))
break hit
end
nil
end
if matches
break VideoMatch.new(key, video_id: matches[:id], matches: matches)
end
end
end
# rubocop:enable Metrics/MethodLength
sig { returns(String) }
attr_reader :url
sig { returns(VideoMatch) }
attr_reader :match
sig { returns(T.nilable(MatchData)) }
def extras
@match.matches
end
sig { returns(T.nilable(Symbol)) }
def type
@match.type
end
sig { returns(T.nilable(String)) }
def video_id
@match.video_id
end
end
# typed: strict
# frozen_string_literal: true
require 'sorbet-runtime'
class VideoUrlParser
extend T::Sig
class YouTubeType; end
class FacebookType; end
class VimeoType; end
class UnknownType; end
Type = T.type_alias(T.any(YouTubeType, FacebookType, VimeoType, UnknownType))
class VideoMatch < T::Struct
extend T::Sig
prop :type, Type
prop :video_id, T.nilable(String)
prop :username, T.nilable(String)
# rubocop:disable Metrics/LineLength
PATTERNS = T.let({
YouTubeType.new => [
%r{https?://(www\.)?youtube.com/watch\?v=(?<id>[A-Za-z0-9\-_]+)},
%r{https?://youtu.be/(?<id>[A-Za-z0-9\-_]+)}
],
FacebookType.new => [
%r{https?://(www\.)?facebook.com/(?<username>[A-Za-z0-9\-_\.]+)/videos/(?<id>\d+).*}
],
VimeoType.new => [
%r{https?://(www\.)?vimeo.com/(?<id>[0-9]+)}
]
}.freeze, T::Hash[Type, T::Array[Regexp]])
# rubocop:enable Metrics/LineLength
sig do
params(
type: Type,
video_id: T.nilable(String),
username: T.nilable(String)
).void
end
def initialize(type = UnknownType.new, video_id: nil, username: nil)
@type = T.let(type, Type)
@video_id = T.let(video_id, T.nilable(String))
@username = T.let(username, T.nilable(String))
end
sig { params(url: String).returns(VideoMatch) }
def self.parse(url)
type, match =
PATTERNS.each_with_object([]) do |(type, patterns), _not_found|
match = patterns.lazy
.map { |p| url.match(p) }
.find { |m| !m.nil? }
break [type, match] if match
end
return unknown unless match
username = match.names.include?('username') ? match['username'] : nil
new(type, video_id: match[:id], username: username)
end
sig { returns(VideoMatch) }
def self.unknown
new(UnknownType.new)
end
end
sig { params(url: String).void }
def initialize(url)
@url = T.let(url, String)
@match = T.let(VideoMatch.parse(url), VideoMatch)
end
sig { returns(String) }
attr_reader :url
sig { returns(VideoMatch) }
attr_reader :match
sig { returns(Type) }
def type
match.type
end
sig { returns(T.nilable(String)) }
def still_image_url
case match.type
when YouTubeType
"https://img.youtube.com/vi/#{match.video_id}/hqdefault.jpg"
when FacebookType
"https://graph.facebook.com/#{match.video_id}/picture?type=normal"
end
end
# rubocop:disable Metrics/LineLength
sig { returns(String) }
def embed_html
case match.type
when YouTubeType
%(<iframe class='db' width="320" height="180" src="https://www.youtube.com/embed/#{match.video_id}?showinfo=0&amp;rel=0" frameborder="0" allowfullscreen></iframe>)
when FacebookType
%(<div id="fb-root"></div><script>(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.3"; fjs.parentNode.insertBefore(js, fjs);}(document, 'script', 'facebook-jssdk'));</script><div class="fb-video" data-allowfullscreen="1" data-href="/#{match.username}/videos/#{match.video_id}/?type=1"></div>)
when VimeoType
%(<iframe class='db' src="https://player.vimeo.com/video/148740221" width="500" height="281" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>)
when UnknownType
"Unrecognized URL type '#{url}'"
end
end
# rubocop:enable Metrics/LineLength
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment