├── .coveralls.yml ├── .gitignore ├── .travis.yml ├── Gemfile ├── MIT-LICENSE ├── README.markdown ├── Rakefile ├── acts_as_unvlogable.gemspec ├── lib ├── acts_as_unvlogable.rb └── acts_as_unvlogable │ ├── flickr.rb │ ├── object_base.rb │ ├── string_base.rb │ ├── string_extend.rb │ ├── version.rb │ ├── vg_11870.rb │ ├── vg_collegehumor.rb │ ├── vg_dailymotion.rb │ ├── vg_dalealplay.rb │ ├── vg_flickr.rb │ ├── vg_gfycat.rb │ ├── vg_giphy.rb │ ├── vg_metacafe.rb │ ├── vg_myspace.rb │ ├── vg_pleer.rb │ ├── vg_rutube.rb │ ├── vg_ted.rb │ ├── vg_vimeo.rb │ ├── vg_vine.rb │ ├── vg_wistia.rb │ ├── vg_youtu.rb │ └── vg_youtube.rb ├── spec ├── acts_as_unvlogable_spec.rb └── spec_helper.rb └── unvlogable_sample.yml /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle 3 | Gemfile.lock 4 | pkg/* 5 | coverage 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | cache: bundler 3 | 4 | rvm: 5 | - 2.3.1 6 | 7 | branches: 8 | only: 9 | - master 10 | - refactor 11 | 12 | script: "bundle exec rspec" 13 | 14 | notifications: 15 | email: 16 | recipients: 17 | - mamuso@mamuso.net 18 | on_failure: change 19 | on_success: never -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | gemspec 3 | 4 | group :development do 5 | gem "bundler", "> 1.3" 6 | end 7 | 8 | group :test do 9 | gem 'coveralls', require: false 10 | end -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008 unvlog.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Acts as unvlogable [![Build Status](https://travis-ci.org/mamuso/acts_as_unvlogable.svg?branch=master)](https://travis-ci.org/mamuso/acts_as_unvlogable) [![Coverage Status](https://coveralls.io/repos/mamuso/acts_as_unvlogable/badge.png?branch=master)](https://coveralls.io/r/mamuso/acts_as_unvlogable?branch=master) 2 | 3 | 4 | What the hell is this! 5 | ---------------------- 6 | 7 | This is the plugin that we use in [unvlog.com](http://unvlog.com) to manage the supported video services. It is an easy way to obtain a few basics about a video only through its url. 8 | 9 | A quick example: 10 | 11 | To include [this video](http://www.youtube.com/watch?v=GPQnbtldFyo) in [this post](http://unvlog.com/blat/2008/3/10/otro-pelotazo) we need to know its title, the correct way to embed it and its thumbnail url. With this plugin we have an easy access to this data: 12 | 13 | @aha = UnvlogIt.new("http://www.youtube.com/watch?v=GPQnbtldFyo") 14 | @aha.title => "paradon del portero" 15 | @aha.thumbnail => "http://i4.ytimg.com/vi/GPQnbtldFyo/default.jpg" 16 | @aha.embed_url => "http://www.youtube.com/v/GPQnbtldFyo" 17 | @aha.embed_html(width, height) => "" 18 | # all together :) 19 | @aha.video_details(width, height) => { 20 | :title => ..., 21 | :thumbnail => ..., 22 | :embed_url => ..., 23 | :embed_html => ..., 24 | } 25 | 26 | With this plugin we have an unique way to manage multiple services :) 27 | 28 | 29 | Install it! 30 | ----------- 31 | 32 | 1. Install it as a gem: 33 | 34 | gem "acts_as_unvlogable" 35 | 36 | 2. Optionally you can create the `config/unvlogable.yml` to store keys for the different services. You have in the plugin a [sample file](http://github.com/mamuso/acts_as_unvlogable/tree/master/unvlogable_sample.yml). 37 | 38 | 2.1. 'yt' gem will need an API key from google in order to get the youtube video information properly, without dealing with API limits. They explain it better than me [here](https://github.com/Fullscreen/yt#apps-that-do-not-require-user-interactions) 39 | 40 | Use it! 41 | ------- 42 | 43 | 44 | The idea is make it as simple as possible. For a given video URL as : 45 | 46 | videotron = UnvlogIt.new("http://vimeo.com/1785993") 47 | 48 | Then we have methods to know the 'basics' for use this video on your application. 49 | 50 | - __title:__ A method to know the title of the video on the service. 51 | 52 | videotron.title 53 | => "Beached" 54 | 55 | - __service:__ A method to know the name of the video provider service. 56 | 57 | videotron.service 58 | => "Vimeo" 59 | 60 | - __thumbnail:__ An image representation of the video. Each service has a different size, but... it works :) 61 | 62 | videotron.thumbnail 63 | => "http://bc1.vimeo.com/vimeo/thumbs/143104745_640.jpg" 64 | 65 | - __embed\_url:__ The url (with flashvars) of the video player. 66 | 67 | videotron.embed_url 68 | => "http://vimeo.com/moogaloop.swf?clip_id=1785993 [...] &show_portrait=1" 69 | 70 | - __embed\_html(width, height):__ Uses the embed\_url to build an oembed string. The default width x height is 425 x 344, but we can specify a different one. 71 | 72 | videotron.embed_html(400, 300) 73 | => "" 74 | 75 | - __flv:__ **DEPRECATED** 76 | 77 | - __video\_details(width, height):__ All together :), returns all the previous elements in a hash. Width and height can be specified to build the embed\_html. 78 | 79 | videotron.video_details 80 | => "{ [...] }" 81 | 82 | 83 | Supported services 84 | ------------------ 85 | 86 | At this moment we support the following video services: 87 | 88 | - [Youtube](http://www.youtube.com/) 89 | - [Vimeo](http://vimeo.com/) 90 | - [Vine](http://vine.co/) 91 | - [Giphy](http://giphy.com/) 92 | - [Gfycat](http://gfycat.com/) 93 | - [Flickr (videos)](http://flickr.com/) 94 | - [Metacafe](http://metacafe.com/) 95 | - [Dailymotion](http://dailymotion.com/) 96 | - [Collegehumor](http://collegehumor.com/) 97 | - [Myspace](http://vids.myspace.com/) 98 | - [Ted Talks](http://www.ted.com/talks/) 99 | - [11870.com](http://11870.com/) 100 | - [Dalealplay](http://www.dalealplay.com/) 101 | - [RuTube](http://www.rutube.ru/) 102 | - [Wistia](http://wistia.com/) 103 | - [Pleer — Audio](http://pleer.com/) 104 | 105 | 106 | And... what else? 107 | ----------------- 108 | If you find a bug or want to suggest a new video service, please tell it to us in [a ticket](http://github.com/mamuso/acts_as_unvlogable/issues). 109 | 110 | Thanks!! 111 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks -------------------------------------------------------------------------------- /acts_as_unvlogable.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "acts_as_unvlogable/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "acts_as_unvlogable" 7 | s.version = ActsAsUnvlogable::VERSION 8 | s.platform = Gem::Platform::RUBY 9 | s.required_ruby_version = '>= 1.9.3' 10 | s.authors = ["Manuel Muñoz", "Fernando Blat", "Alberto Romero"] 11 | s.email = ["mamuso@mamuso.net", "ferblape@gmail.com", "denegro@gmail.com"] 12 | s.homepage = "https://github.com/mamuso/acts_as_unvlogable" 13 | s.summary = %q{An easy way to include external video services in a rails app} 14 | s.description = %q{An easy way to include external video services in a rails app. This gem provides you wrappers for the most common video services, such as Youtube, Vimeo, Flickr, and so on...} 15 | 16 | s.rubyforge_project = "acts_as_unvlogable" 17 | 18 | s.files = `git ls-files`.split("\n") 19 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 20 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 21 | s.require_paths = ["lib"] 22 | 23 | s.add_development_dependency 'bundler', '~> 1.3' 24 | s.add_development_dependency 'rake' 25 | s.add_development_dependency 'rspec' 26 | s.add_runtime_dependency("activesupport") 27 | s.add_runtime_dependency("nokogiri") 28 | s.add_runtime_dependency("xml-simple") 29 | s.add_runtime_dependency("yt") 30 | end 31 | -------------------------------------------------------------------------------- /lib/acts_as_unvlogable.rb: -------------------------------------------------------------------------------- 1 | require "bundler/setup" 2 | require "cgi" 3 | require "json" 4 | require "net/http" 5 | require 'rexml/document' 6 | require 'active_support' 7 | require 'active_support/core_ext/hash' 8 | require "yt" 9 | require "nokogiri" 10 | require "acts_as_unvlogable/flickr" 11 | 12 | 13 | if defined?(ActiveSupport).nil? 14 | require "acts_as_unvlogable/string_base" 15 | require "acts_as_unvlogable/object_base" 16 | end 17 | require "acts_as_unvlogable/string_extend" 18 | 19 | 20 | # Video Services 21 | videolibs = File.join(File.dirname(__FILE__), "acts_as_unvlogable", "vg_*.rb") 22 | Dir.glob(videolibs).each {|file| require file} 23 | 24 | 25 | class UnvlogIt 26 | 27 | def initialize(url=nil, options={}) 28 | @object = VideoFactory.new(url, options).load_service 29 | end 30 | 31 | def title 32 | @object.title rescue nil 33 | end 34 | 35 | def thumbnail 36 | @object.thumbnail rescue nil 37 | end 38 | 39 | def duration # duration is in seconds 40 | @object.duration rescue nil 41 | end 42 | 43 | def embed_url 44 | @object.embed_url rescue nil 45 | end 46 | 47 | def video_id 48 | @object.video_id rescue nil 49 | end 50 | 51 | def embed_html(width=425, height=344, options={}, params={}) 52 | @object.embed_html(width, height, options, params) rescue nil 53 | end 54 | 55 | def service 56 | @object.service rescue nil 57 | end 58 | 59 | # Deprecated 60 | def flv 61 | end 62 | 63 | # Deprecated 64 | def download_url 65 | end 66 | 67 | def video_details(width=425, height=344) 68 | { 69 | :title => @object.title, 70 | :thumbnail => @object.thumbnail, 71 | :embed_url => @object.embed_url, 72 | :embed_html => @object.embed_html(width, height), 73 | :flv => nil, # Deprecated 74 | :download_url => nil, # Deprecated 75 | :service => @object.service, 76 | :duration => @object.duration 77 | } 78 | end 79 | 80 | class VideoFactory 81 | def initialize(url, options = {}) 82 | raise ArgumentError.new("We need a video url") if url.blank? 83 | @url = url 84 | @options = options 85 | end 86 | 87 | def load_service 88 | @object = service_object 89 | validate_embed(@object) 90 | end 91 | 92 | private 93 | 94 | def validate_embed(object) 95 | unless object.instance_variable_get("@details").nil? || !object.instance_variable_get("@details").respond_to?("noembed") 96 | if !object.instance_variable_get("@details").embeddable? 97 | raise ArgumentError.new("Embedding disabled by request") 98 | end 99 | end 100 | object 101 | end 102 | 103 | def service_object 104 | class_name = "vg_#{get_domain.downcase}".camelize 105 | class_name.constantize.new(@url, @options) 106 | rescue NameError 107 | raise ArgumentError.new("Unsuported url or service") 108 | end 109 | 110 | def get_domain 111 | host = URI::parse(@url).host.split(".") 112 | unless host.size == 1 113 | host[host.size-2] 114 | else 115 | host[0] 116 | end 117 | end 118 | end 119 | end 120 | -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/flickr.rb: -------------------------------------------------------------------------------- 1 | # included here to skip the gem dependency and modified to manage video capabilities. 2 | # this is the updated version from http://github.com/ctagg/flickr/tree/master/lib/flickr.rb 3 | # Modified and simplified to keep only the used methods 4 | 5 | require 'cgi' 6 | require 'net/http' 7 | require 'xmlsimple' 8 | require 'digest/md5' 9 | 10 | # Flickr client class. Requires an API key 11 | class Flickr 12 | attr_reader :api_key, :auth_token 13 | attr_accessor :user 14 | 15 | HOST_URL = 'https://flickr.com' 16 | API_PATH = '/services/rest' 17 | 18 | # Flickr, annoyingly, uses a number of representations to specify the size 19 | # of a photo, depending on the context. It gives a label such a "Small" or 20 | # "Medium" to a size of photo, when returning all possible sizes. However, 21 | # when generating the uri for the page that features that size of photo, or 22 | # the source url for the image itself it uses a single letter. Bizarrely, 23 | # these letters are different depending on whether you want the Flickr page 24 | # for the photo or the source uri -- e.g. a "Small" photo (240 pixels on its 25 | # longest side) may be viewed at 26 | # "http://www.flickr.com/photos/sco/2397458775/sizes/s/" 27 | # but its source is at 28 | # "http://farm4.static.flickr.com/3118/2397458775_2ec2ddc324_m.jpg". 29 | # The VALID_SIZES hash associates the correct letter with a label 30 | VALID_SIZES = { "Square" => ["s", "sq"], 31 | "Thumbnail" => ["t", "t"], 32 | "Small" => ["m", "s"], 33 | "Medium" => [nil, "m"], 34 | "Large" => ["b", "l"] 35 | } 36 | 37 | # To use the Flickr API you need an api key 38 | # (see http://www.flickr.com/services/api/misc.api_keys.html), and the flickr 39 | # client object shuld be initialized with this. You'll also need a shared 40 | # secret code if you want to use authentication (e.g. to get a user's 41 | # private photos) 42 | # There are two ways to initialize the Flickr client. The preferred way is with 43 | # a hash of params, e.g. 'api_key' => 'your_api_key', 'shared_secret' => 44 | # 'shared_secret_code'. The older (deprecated) way is to pass an ordered series of 45 | # arguments. This is provided for continuity only, as several of the arguments 46 | # are no longer usable ('email', 'password') 47 | def initialize(api_key_or_params=nil, email=nil, password=nil, shared_secret=nil) 48 | @host = HOST_URL 49 | @api = API_PATH 50 | if api_key_or_params.is_a?(Hash) 51 | @api_key = api_key_or_params['api_key'] 52 | @shared_secret = api_key_or_params['shared_secret'] 53 | @auth_token = api_key_or_params['auth_token'] 54 | else 55 | @api_key = api_key_or_params 56 | @shared_secret = shared_secret 57 | login(email, password) if email and password 58 | end 59 | end 60 | 61 | 62 | # Implements everything else. 63 | # Any method not defined explicitly will be passed on to the Flickr API, 64 | # and return an XmlSimple document. For example, Flickr#test_echo is not 65 | # defined, so it will pass the call to the flickr.test.echo method. 66 | def method_missing(method_id, params={}) 67 | request(method_id.id2name.gsub(/_/, '.'), params) 68 | end 69 | 70 | # Takes a Flickr API method name and set of parameters; returns an XmlSimple object with the response 71 | def request(method, params={}) 72 | url = request_url(method, params) 73 | response = XmlSimple.xml_in(open(url), { 'ForceArray' => false }) 74 | raise response['err']['msg'] if response['stat'] != 'ok' 75 | response 76 | end 77 | 78 | # Builds url for Flickr API REST request from given the flickr method name 79 | # (exclusing the 'flickr.' that begins each method call) and params (where 80 | # applicable) which should be supplied as a Hash (e.g 'user_id' => "foo123") 81 | def request_url(method, params={}) 82 | method = 'flickr.' + method 83 | url = "#{@host}#{@api}/?api_key=#{@api_key}&method=#{method}" 84 | params.merge!('api_key' => @api_key, 'method' => method, 'auth_token' => @auth_token) 85 | signature = signature_from(params) 86 | 87 | url = "#{@host}#{@api}/?" + params.merge('api_sig' => signature).collect { |k,v| "#{k}=" + CGI::escape(v.to_s) unless v.nil? }.compact.join("&") 88 | end 89 | 90 | def signature_from(params={}) 91 | return unless @shared_secret # don't both getting signature if no shared_secret 92 | request_str = params.reject {|k,v| v.nil?}.collect {|p| "#{p[0].to_s}#{p[1]}"}.sort.join # build key value pairs, sort in alpha order then join them, ignoring those with nil value 93 | return Digest::MD5.hexdigest("#{@shared_secret}#{request_str}") 94 | end 95 | 96 | # A collection of photos is returned as a PhotoCollection, a subclass of Array. 97 | # This allows us to retain the pagination info returned by Flickr and make it 98 | # accessible in a friendly way 99 | class PhotoCollection < Array 100 | attr_reader :page, :pages, :perpage, :total 101 | 102 | # builds a PhotoCollection from given params, such as those returned from 103 | # photos.search API call 104 | def initialize(photos_api_response={}, api_key=nil) 105 | end 106 | end 107 | 108 | # Todo: 109 | # logged_in? 110 | # if logged in: 111 | # flickr.blogs.getList 112 | # flickr.favorites.add 113 | # flickr.favorites.remove 114 | # flickr.groups.browse 115 | # flickr.photos.getCounts 116 | # flickr.photos.getNotInSet 117 | # flickr.photos.getUntagged 118 | # flickr.photosets.create 119 | # flickr.photosets.orderSets 120 | # flickr.tags.getListUserPopular 121 | # flickr.test.login 122 | # uploading 123 | class User 124 | 125 | attr_reader :client, :id, :name, :location, :photos_url, :url, :count, :firstdate, :firstdatetaken 126 | 127 | # A Flickr::User can be instantiated in two ways. The old (deprecated) 128 | # method is with an ordered series of values. The new method is with a 129 | # params Hash, which is easier when a variable number of params are 130 | # supplied, which is the case here, and also avoids having to constantly 131 | # supply nil values for the email and password, which are now irrelevant 132 | # as authentication is no longer done this way. 133 | # An associated flickr client will also be generated if an api key is 134 | # passed among the arguments or in the params hash. Alternatively, and 135 | # most likely, an existing client object may be passed in the params hash 136 | # (e.g. 'client' => some_existing_flickr_client_object), and this is 137 | # what happends when users are initlialized as the result of a method 138 | # called on the flickr client (e.g. flickr.users) 139 | def initialize(id_or_params_hash=nil, username=nil, email=nil, password=nil, api_key=nil) 140 | if id_or_params_hash.is_a?(Hash) 141 | id_or_params_hash.each { |k,v| self.instance_variable_set("@#{k}", v) } # convert extra_params into instance variables 142 | else 143 | @id = id_or_params_hash 144 | @username = username 145 | @email = email 146 | @password = password 147 | @api_key = api_key 148 | end 149 | @client ||= Flickr.new('api_key' => @api_key, 'shared_secret' => @shared_secret, 'auth_token' => @auth_token) if @api_key 150 | @client.login(@email, @password) if @email and @password # this is now irrelevant as Flickr API no longer supports authentication this way 151 | end 152 | 153 | end 154 | 155 | class Photo 156 | 157 | attr_reader :id, :client, :title 158 | 159 | def initialize(id=nil, api_key=nil, extra_params={}) 160 | @id = id 161 | @api_key = api_key 162 | extra_params.each { |k,v| self.instance_variable_set("@#{k}", v) } # convert extra_params into instance variables 163 | @client = Flickr.new @api_key 164 | end 165 | 166 | def title 167 | @title.nil? ? getInfo("title") : @title 168 | end 169 | 170 | # converts string or symbol size to a capitalized string 171 | def normalize_size(size) 172 | size ? size.to_s.capitalize : size 173 | end 174 | 175 | # Returns the URL for the image (default or any specified size) 176 | def source(size='Medium') 177 | image_source_uri_from_self(size) || sizes(size)['source'] 178 | end 179 | 180 | # unvlog 181 | def media 182 | @media || getInfo("media") 183 | end 184 | 185 | def secret 186 | @secret || getInfo("secret") 187 | end 188 | 189 | 190 | private 191 | 192 | # Implements flickr.photos.getInfo 193 | def getInfo(attrib="") 194 | return instance_variable_get("@#{attrib}") if @got_info 195 | info = @client.photos_getInfo('photo_id'=>@id)['photo'] 196 | @got_info = true 197 | info.each { |k,v| instance_variable_set("@#{k}", v)} 198 | @media = info['media'] 199 | @secret = info['secret'] 200 | @owner = User.new(info['owner']['nsid'], info['owner']['username'], nil, nil, @api_key) 201 | @tags = info['tags']['tag'] 202 | @notes = info['notes']['note']#.collect { |note| Note.new(note.id) } 203 | @url = info['urls']['url']['content'] # assumes only one url 204 | instance_variable_get("@#{attrib}") 205 | end 206 | 207 | # Builds source uri of image from params (often returned from other 208 | # methods, e.g. User#photos). As specified at: 209 | # http://www.flickr.com/services/api/misc.urls.html. If size is given 210 | # should be one the keys in the VALID_SIZES hash, i.e. 211 | # "Square", "Thumbnail", "Medium", "Large", "Original", "Small" (These 212 | # are the values returned by flickr.photos.getSizes). 213 | # If no size is given the uri for "Medium"-size image, i.e. with width 214 | # of 500 is returned 215 | # TODO: Handle "Original" size 216 | def image_source_uri_from_self(size=nil) 217 | return unless @farm&&@server&&@id&&@secret 218 | s_size = VALID_SIZES[normalize_size(size)] # get the short letters array corresponding to the size 219 | s_size = s_size&&s_size[0] # the first element of this array is used to build the source uri 220 | if s_size.nil? 221 | "http://farm#{@farm}.static.flickr.com/#{@server}/#{@id}_#{@secret}.jpg" 222 | else 223 | "http://farm#{@farm}.static.flickr.com/#{@server}/#{@id}_#{@secret}_#{s_size}.jpg" 224 | end 225 | end 226 | 227 | end 228 | 229 | end 230 | -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/object_base.rb: -------------------------------------------------------------------------------- 1 | # This removes the activesupport dependency 2 | 3 | class Object 4 | def blank? 5 | respond_to?(:empty?) ? empty? : !self 6 | end 7 | 8 | def instance_values #:nodoc: 9 | instance_variables.inject({}) do |values, name| 10 | values[name.to_s[1..-1]] = instance_variable_get(name) 11 | values 12 | end 13 | end 14 | 15 | end -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/string_base.rb: -------------------------------------------------------------------------------- 1 | # This removes the activesupport dependency 2 | 3 | class String 4 | def camelize(first_letter_in_uppercase = true) 5 | if first_letter_in_uppercase 6 | self.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } 7 | else 8 | self.first + camelize(self)[1..-1] 9 | end 10 | end 11 | 12 | def constantize 13 | camel_cased_word = self 14 | unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word 15 | raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!" 16 | end 17 | 18 | Object.module_eval("::#{$1}", __FILE__, __LINE__) 19 | end 20 | 21 | def humanize 22 | self.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize 23 | end 24 | end -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/string_extend.rb: -------------------------------------------------------------------------------- 1 | class String 2 | def query_param(param_name) 3 | raise ArgumentError.new("param name can't be nil") if param_name.blank? 4 | uri = URI.parse(self) 5 | (CGI::parse(uri.query)[param_name].first.to_s if uri.query) || nil 6 | end 7 | end -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/version.rb: -------------------------------------------------------------------------------- 1 | module ActsAsUnvlogable 2 | VERSION = "1.0.5" 3 | end 4 | -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_11870.rb: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------- 2 | # Class for 11870 (11870.com) 3 | # http://11870.com/pro/chic-basic-born/media/b606abfe 4 | # ---------------------------------------------- 5 | 6 | 7 | class Vg11870 8 | 9 | def initialize(url=nil, options={}) 10 | @url = url.sub "http:", "https:" 11 | @page = Nokogiri::HTML(open(@url)) 12 | end 13 | 14 | def title 15 | CGI::unescapeHTML @page.xpath("//h2[@class='fn p-name']/a/span").first.text.strip 16 | end 17 | 18 | def thumbnail 19 | @page.xpath("//img[@itemprop='image']").first["src"] 20 | end 21 | 22 | def duration 23 | nil 24 | end 25 | 26 | def embed_url 27 | "https://s3-eu-west-1.amazonaws.com/static.11870.com/11870/player.swf?netstreambasepath=#{@url}&id=video&file=#{flv}&image=#{thumbnail}" 28 | end 29 | 30 | def embed_html(width=425, height=344, options={}, params={}) 31 | " " 32 | end 33 | 34 | def flv 35 | @page.xpath("//div[@id='video']/span").first.text.strip 36 | end 37 | 38 | def download_url 39 | nil 40 | end 41 | 42 | def service 43 | "11870.com" 44 | end 45 | 46 | end -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_collegehumor.rb: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------- 2 | # Class for Collegehumor (www.collegehumor.com) 3 | # http://www.collegehumor.com/video:1781938 4 | # ---------------------------------------------- 5 | 6 | 7 | class VgCollegehumor 8 | 9 | def initialize(url=nil, options={}) 10 | @url = url 11 | @video_id = parse_url(url) 12 | res = Net::HTTP.get(URI.parse("http://www.collegehumor.com/moogaloop/video/#{@video_id}")) 13 | @feed = REXML::Document.new(res) 14 | end 15 | 16 | def title 17 | REXML::XPath.first(@feed, "//video/caption")[0].to_s 18 | end 19 | 20 | def thumbnail 21 | REXML::XPath.first(@feed, "//video/thumbnail")[0] 22 | end 23 | 24 | def embed_url 25 | "http://www.collegehumor.com/e/#{@video_id}" 26 | end 27 | 28 | def embed_html(width=425, height=344, options={}, params={}) 29 | "" 30 | end 31 | 32 | def download_url 33 | nil 34 | end 35 | 36 | def duration 37 | nil 38 | end 39 | 40 | def service 41 | "CollegeHumor" 42 | end 43 | 44 | private 45 | 46 | def parse_url(url) 47 | uri = URI.parse(url) 48 | path = uri.path 49 | videoargs = path.split("/") 50 | raise unless videoargs.size > 0 && videoargs[1] == 'video' 51 | videoargs[2] 52 | end 53 | 54 | end -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_dailymotion.rb: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------- 2 | # Class for Dailymotion (www.dailymotion.com) 3 | # http://www.dailymotion.com/visited-week/lang/es/video/x7u5kn_parkour-dayyy_sport 4 | # ---------------------------------------------- 5 | 6 | 7 | class VgDailymotion 8 | 9 | def initialize(url=nil, options={}) 10 | @url = url 11 | @video_id = parse_url(url) 12 | res = Net::HTTP.get(URI.parse("http://www.dailymotion.com/rss/video/#{@video_id}")) 13 | @feed = REXML::Document.new(res) 14 | end 15 | 16 | def title 17 | REXML::XPath.first(@feed, "//item/title")[0].to_s 18 | end 19 | 20 | def thumbnail 21 | REXML::XPath.first(@feed, "//media:thumbnail").attributes['url'].gsub("preview_large", "preview_medium") 22 | end 23 | 24 | def embed_url 25 | REXML::XPath.first(@feed, "//media:content[@type='text/html']").attributes['url'] 26 | end 27 | 28 | def embed_html(width=425, height=344, options={}, params={}) 29 | "" 30 | end 31 | 32 | def download_url 33 | nil 34 | end 35 | 36 | def duration 37 | nil 38 | end 39 | 40 | def service 41 | "Dailymotion" 42 | end 43 | 44 | private 45 | 46 | def parse_url(url) 47 | uri = URI.parse(url) 48 | path = uri.path 49 | videoargs = '' 50 | if path 51 | videoargs = path.split('/video/')[1].split("/")[0] 52 | raise unless videoargs.size > 0 53 | else 54 | raise 55 | end 56 | videoargs 57 | rescue 58 | nil 59 | end 60 | 61 | 62 | end -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_dalealplay.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # ---------------------------------------------- 3 | # Class for dalealplay (dalealplay.com) 4 | # http://www.dalealplay.com/informaciondecontenido.php?con=80280 5 | # 6 | # Crappy without api 7 | # ---------------------------------------------- 8 | 9 | 10 | class VgDalealplay 11 | 12 | def initialize(url=nil, options={}) 13 | @url = url 14 | @video_id = parse_url(@url) 15 | @page = Nokogiri::HTML(open(@url)) 16 | end 17 | 18 | def title 19 | @page.xpath("//h1").first.text 20 | end 21 | 22 | def thumbnail 23 | @page.xpath("//meta[@itemprop='thumbnailUrl']").first["content"] 24 | end 25 | 26 | def embed_url 27 | @page.xpath("//meta[@itemprop='embedUrl']").first["content"] 28 | end 29 | 30 | def duration 31 | nil 32 | end 33 | 34 | def embed_html(width=425, height=344, options={}, params={}) 35 | "" 36 | end 37 | 38 | def download_url 39 | nil 40 | end 41 | 42 | def service 43 | "dalealplay" 44 | end 45 | 46 | def parse_url(url) 47 | uri = URI.parse(url) 48 | path = uri.path 49 | videoargs = '' 50 | if path 51 | videoargs = path.split('_')[-1] 52 | raise unless videoargs.size > 0 53 | else 54 | raise 55 | end 56 | videoargs 57 | rescue 58 | nil 59 | end 60 | 61 | 62 | end -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_flickr.rb: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------- 2 | # Class for Flickr (flickr.com) 3 | # http://www.flickr.com/photos/andina/3158762127/in/photostream/ 4 | # ---------------------------------------------- 5 | 6 | 7 | class VgFlickr 8 | 9 | def initialize(url=nil, options={}) 10 | # general settings 11 | @url = url 12 | @video_id = parse_url(url) 13 | settings ||= YAML.load_file(RAILS_ROOT + '/config/unvlogable.yml') rescue {} 14 | @details = Flickr::Photo.new(@video_id, options.nil? || options[:key].nil? ? settings['flickr_key'] : options[:key]) 15 | raise if @details.media != "video" 16 | end 17 | 18 | def title 19 | @details.title 20 | end 21 | 22 | def thumbnail 23 | @details.source('Small') 24 | end 25 | 26 | def embed_url 27 | "http://www.flickr.com/apps/video/stewart.swf?v=63881&intl_lang=en-us&photo_secret=#{@details.secret}&photo_id=#{@video_id}" 28 | end 29 | 30 | def embed_html(width=425, height=344, options={}, params={}) 31 | " " 32 | end 33 | 34 | def download_url 35 | nil 36 | end 37 | 38 | def duration 39 | nil 40 | end 41 | 42 | def service 43 | "Flickr" 44 | end 45 | 46 | private 47 | 48 | def parse_url(url) 49 | video_id = URI::parse(url).path.split("/")[3] 50 | raise unless video_id 51 | video_id 52 | rescue 53 | nil 54 | end 55 | 56 | end -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_gfycat.rb: -------------------------------------------------------------------------------- 1 | class VgGfycat 2 | attr_accessor :video_id 3 | 4 | def initialize(url, options={}) 5 | @url = url 6 | @uri = URI.parse(url) 7 | @video_id = @uri.path.split("/")[-1] 8 | json_endpoint = "https://gfycat.com/cajax/get/#{@video_id}" 9 | @json = JSON.parse(Net::HTTP.get(URI.parse(json_endpoint))) 10 | raise ArgumentError unless @video_id or @json["error"] 11 | end 12 | 13 | def title 14 | @title ||= @json["gfyItem"]["title"] 15 | end 16 | 17 | def thumbnail 18 | @thumbnail ||= @json["gfyItem"]["posterUrl"] 19 | end 20 | 21 | def embed_url 22 | @embed_url ||= @json["gfyItem"]["mp4Url"] 23 | end 24 | 25 | def embed_html(width=425, height=344, options={}, params={}) 26 | "" 27 | end 28 | 29 | def download_url 30 | nil 31 | end 32 | 33 | def duration 34 | nil 35 | end 36 | 37 | def service 38 | "Gfycat" 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_giphy.rb: -------------------------------------------------------------------------------- 1 | class VgGiphy 2 | attr_accessor :video_id 3 | 4 | def initialize(url, options={}) 5 | @url = url 6 | @uri = URI.parse(url) 7 | @video_id = @uri.path.match(/gifs\/([\w\-\d]+)/)[1].split("-")[-1] 8 | json_endpoint = "http://api.giphy.com/v1/gifs?api_key=dc6zaTOxFJmzC&ids=#{@video_id}" 9 | @json = JSON.parse(Net::HTTP.get(URI.parse(json_endpoint))) 10 | raise ArgumentError unless @video_id 11 | end 12 | 13 | def title 14 | nil 15 | end 16 | 17 | def thumbnail 18 | @thumbnail ||= @json["data"][0]["images"]["fixed_height_still"]["url"] 19 | end 20 | 21 | def embed_url 22 | @embed_url ||= @json["data"][0]["images"]["looping"]["mp4"] 23 | end 24 | 25 | def embed_html(width=425, height=344, options={}, params={}) 26 | "" 27 | end 28 | 29 | def download_url 30 | nil 31 | end 32 | 33 | def duration 34 | nil 35 | end 36 | 37 | def service 38 | "Giphy" 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_metacafe.rb: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------- 2 | # Class for Metacafe (www.metacafe.com) 3 | # http://www.metacafe.com/watch/476621/experiments_with_the_myth_busters_with_diet_coke_and_mentos_dry/ 4 | # ---------------------------------------------- 5 | 6 | 7 | class VgMetacafe 8 | 9 | def initialize(url=nil, options={}) 10 | @url = url 11 | @args = parse_url(url) 12 | @youtubed = @args[1].index("yt-").nil? ? false : true 13 | @yt = @youtubed ? VgYoutube.new("http://www.youtube.com/watch?v=#{@args[1].sub('yt-', '')}", options) : nil 14 | end 15 | 16 | def title 17 | @youtubed ? @yt.title : (@args[2].humanize unless @args[2].blank?) 18 | end 19 | 20 | def thumbnail 21 | "http://www.metacafe.com/thumb/#{@args[1]}.jpg" 22 | end 23 | 24 | def embed_url 25 | "http://www.metacafe.com/fplayer/#{@args[1]}/#{@args[2]}.swf" 26 | end 27 | 28 | def embed_html(width=425, height=344, options={}, params={}) 29 | "" 30 | end 31 | 32 | def duration 33 | nil 34 | end 35 | 36 | def service 37 | "Metacafe" 38 | end 39 | 40 | private 41 | 42 | def parse_url(url) 43 | uri = URI.parse(url) 44 | path = uri.path 45 | @args = '' 46 | if path and path.split("/").size >=1 47 | @args = path.split("/") 48 | @args.delete("watch") 49 | raise unless @args.size > 0 50 | else 51 | raise 52 | end 53 | @args 54 | rescue 55 | nil 56 | end 57 | 58 | end -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_myspace.rb: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------- 2 | # Class for Myspace (vids.myspace.com) 3 | # http://vids.myspace.com/index.cfm?fuseaction=vids.individual&VideoID=27111431 4 | # ---------------------------------------------- 5 | 6 | 7 | class VgMyspace 8 | 9 | def initialize(url=nil, options={}) 10 | @url = url 11 | @page = Nokogiri::HTML(open(@url)) 12 | end 13 | 14 | def title 15 | @page.xpath("//meta[@property='og:title']").first["content"].split("Video by")[0].strip 16 | end 17 | 18 | def thumbnail 19 | @page.xpath("//meta[@property='og:image']").first["content"].strip 20 | end 21 | 22 | def duration 23 | nil 24 | end 25 | 26 | def embed_url 27 | @page.xpath("//meta[@name='twitter:player']").first["content"].strip 28 | end 29 | 30 | def embed_html(width=425, height=344, options={}, params={}) 31 | "" 32 | end 33 | 34 | def download_url 35 | nil 36 | end 37 | 38 | def service 39 | "Myspace" 40 | end 41 | 42 | end -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_pleer.rb: -------------------------------------------------------------------------------- 1 | class VgPleer 2 | attr_accessor :track_id 3 | 4 | def initialize(url, options={}) 5 | url = url.sub(/pleer.com/, 'pleer.net') #addressing permanent redirection 6 | @uri = URI.parse(url) 7 | @track_id = @uri.path.match(/tracks\/([\w\d]+)/)[1] 8 | @url = url 9 | raise ArgumentError unless @track_id 10 | end 11 | 12 | def title 13 | @title ||= [pp_data[:singer], pp_data[:song]].join(' - ') 14 | end 15 | 16 | def embed_html(width=425, height=344, options={}, params={}) 17 | "" 18 | end 19 | 20 | def service 21 | "Pleer" 22 | end 23 | 24 | private 25 | def pp_data 26 | return @pp_data if defined? @pp_data 27 | page = Nokogiri::HTML(Net::HTTP.get(@uri)) 28 | info = page.xpath('//li[@singer]').first 29 | @pp_data = { 30 | :singer => info['singer'], # artist name 31 | :song => info['song'], # song title 32 | :file_id => info['file_id'], # wtf 33 | :link => info['link'], # same as @track_id 34 | :duration => info['duration'], # duration of the song in seconds 35 | :size => info['size'], # file size 36 | :rate => info['rate'] # bit rate 37 | } 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_rutube.rb: -------------------------------------------------------------------------------- 1 | class VgRutube 2 | 3 | def initialize(url=nil, options={}) 4 | @url = url 5 | @page = Nokogiri::HTML(open(@url)) rescue nil 6 | raise ArgumentError.new("Unsuported url or service") if @page.xpath("//meta[@property='og:video:iframe']").blank? 7 | end 8 | 9 | def title 10 | @page.xpath("//meta[@property='og:title']").first["content"].strip 11 | end 12 | 13 | def thumbnail 14 | @page.xpath("//meta[@property='og:image']").first["content"].strip 15 | end 16 | 17 | def embed_url 18 | @page.xpath("//meta[@property='og:video:iframe']").first["content"].strip 19 | end 20 | 21 | def embed_html(width=425, height=344, options={}, params={}) 22 | "" 23 | end 24 | 25 | def download_url 26 | nil 27 | end 28 | 29 | def duration 30 | nil 31 | end 32 | 33 | def service 34 | "Rutube" 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_ted.rb: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------- 2 | # Class for Ted Talks (www.ted.com/talks) 3 | # http://www.ted.com/talks/benjamin_wallace_on_the_price_of_happiness.html 4 | # ---------------------------------------------- 5 | 6 | 7 | class VgTed 8 | 9 | def initialize(url=nil, options={}) 10 | @url = url 11 | raise ArgumentError.new("Unsuported url or service") unless URI::parse(url).path.split("/").include? "talks" 12 | @page = Nokogiri::HTML(open(@url)) 13 | end 14 | 15 | def title 16 | @page.xpath("//meta[@property='og:title']").first["content"].strip 17 | end 18 | 19 | def thumbnail 20 | @page.xpath("//meta[@property='og:image']").first["content"].strip 21 | end 22 | 23 | def duration 24 | nil 25 | end 26 | 27 | def embed_url 28 | @page.xpath("//link[@itemprop='embedURL']").first["href"].strip 29 | end 30 | 31 | def embed_html(width=425, height=344, options={}, params={}) 32 | "" 33 | end 34 | 35 | def download_url 36 | nil 37 | end 38 | 39 | def service 40 | "Ted Talks" 41 | end 42 | 43 | end -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_vimeo.rb: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------- 2 | # Class for Vimeo (vimeo.com) 3 | # http://vimeo.com/5362441 4 | # ---------------------------------------------- 5 | 6 | 7 | class VgVimeo 8 | 9 | def initialize(url=nil, options={}) 10 | # general settings 11 | @url = url 12 | @video_id = parse_url(url) 13 | res = Net::HTTP.get(URI.parse("http://vimeo.com/api/v2/video/#{@video_id}.xml")) 14 | @feed = REXML::Document.new(res) 15 | end 16 | 17 | def video_id 18 | @video_id 19 | end 20 | 21 | def title 22 | REXML::XPath.first( @feed, "//title" )[0].to_s 23 | end 24 | 25 | def thumbnail 26 | REXML::XPath.first( @feed, "//thumbnail_medium" )[0].to_s 27 | end 28 | 29 | def duration 30 | REXML::XPath.first( @feed, "//duration" )[0].to_s.to_i 31 | end 32 | 33 | def embed_url 34 | "http://player.vimeo.com/video/#{@video_id}" 35 | end 36 | 37 | def embed_html(width=425, height=344, options={}, params={}) 38 | "" 39 | end 40 | 41 | def download_url 42 | nil 43 | end 44 | 45 | def service 46 | "Vimeo" 47 | end 48 | 49 | protected 50 | 51 | # formats: http://vimeo.com/ or http://vimeo.com/channels/hd# 52 | def parse_url(url) 53 | uri = URI.parse(url) 54 | path = uri.path 55 | videoargs = '' 56 | 57 | return uri.fragment if uri.fragment 58 | 59 | if uri.path and path.split("/").size > 0 60 | videoargs = path.split("/") 61 | raise unless videoargs.size > 0 62 | else 63 | raise 64 | end 65 | videoargs[1] 66 | rescue 67 | nil 68 | end 69 | 70 | end 71 | -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_vine.rb: -------------------------------------------------------------------------------- 1 | class VgVine 2 | attr_accessor :video_id 3 | 4 | def initialize(url, options={}) 5 | @url = url 6 | @uri = URI.parse(url) 7 | @video_id = @uri.path.match(/v\/([\w\d]+)/)[1] 8 | json_endpoint = "https://vine.co/oembed/#{@video_id}.json" 9 | @json = JSON.parse(Net::HTTP.get(URI.parse(json_endpoint))) 10 | raise ArgumentError unless @video_id 11 | end 12 | 13 | def title 14 | @title ||= @json["title"] 15 | end 16 | 17 | def thumbnail 18 | @thumbnail ||= @json["thumbnail_url"] 19 | end 20 | 21 | def embed_url 22 | @embed_url ||= "#{@url}/embed/simple" 23 | end 24 | 25 | def embed_html(width=600, height=600, options={}, params={}) 26 | "" 22 | end 23 | 24 | private 25 | 26 | def fetch_video_details! 27 | begin 28 | res = Net::HTTP.get(URI.parse(wistia_oembed_endpoint)) 29 | @details = JSON.parse(res) 30 | rescue JSON::ParserError 31 | raise ArgumentError.new("Unsuported url or service") 32 | end 33 | end 34 | 35 | def assign_properties! 36 | @title = @details["title"] 37 | @thumbnail = @details["thumbnail_url"] 38 | @duration = @details["duration"] 39 | @service = @details["provider_name"] 40 | end 41 | 42 | def encoded_url 43 | CGI::escape(@url) 44 | end 45 | 46 | def wistia_oembed_endpoint 47 | "http://fast.wistia.com/oembed.json?url=#{encoded_url}" 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_youtu.rb: -------------------------------------------------------------------------------- 1 | require "acts_as_unvlogable/vg_youtube" 2 | 3 | class VgYoutu < VgYoutube 4 | def initialize(url=nil, options={}) 5 | url = URI(url) 6 | url.host = 'www.youtube.com' 7 | url.query = "#{url.query}&v=#{url.path[1..-1]}" 8 | url.path = '/watch' 9 | super(url.to_s, options) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/acts_as_unvlogable/vg_youtube.rb: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------- 2 | # Class for Youtube (youtube.com) 3 | # http://www.youtube.com/watch?v=MVa4q-YVjD8 4 | # ---------------------------------------------- 5 | 6 | class VgYoutube 7 | 8 | def initialize(url=nil, options={}) 9 | settings ||= YAML.load_file(RAILS_ROOT + '/config/unvlogable.yml') rescue {} 10 | Yt.configure do |config| 11 | config.api_key = options.nil? || options[:key].nil? ? settings['youtube_key'] : options[:key] 12 | end 13 | 14 | @url = url 15 | @video_id = @url.query_param('v') 16 | begin 17 | @details = Yt::Video.new id: @video_id 18 | raise if @details.blank? || !@details.embeddable? 19 | rescue 20 | raise ArgumentError, "Unsuported url or service" 21 | end 22 | end 23 | 24 | def title 25 | @details.title 26 | end 27 | 28 | def thumbnail 29 | @details.thumbnail_url 30 | end 31 | 32 | def duration 33 | @details.duration 34 | end 35 | 36 | def embed_url 37 | "http://www.youtube.com/embed/#{@video_id}" if @details.embeddable? 38 | end 39 | 40 | # iframe embed — https://developers.google.com/youtube/player_parameters#Manual_IFrame_Embeds 41 | def embed_html(width=425, height=344, options={}, params={}) 42 | "