Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Initial commit

  • Loading branch information...
commit e1845cedc5812093e784e7cd69f6ae86871b23a5 0 parents
Vidal Gonzalez authored
3  .gitignore
... ... @@ -0,0 +1,3 @@
  1 +pkg
  2 +doc
  3 +Manifest
0  README.rdoc
Source Rendered
No changes.
14 Rakefile
... ... @@ -0,0 +1,14 @@
  1 +require 'rubygems'
  2 +require 'rake'
  3 +require 'echoe'
  4 +
  5 +Echoe.new('ooyala_api', '0.1.0') do |p|
  6 + p.description = "Ooyala REST API client classes."
  7 + p.url = "http://github.com/vidalon/ooyala_api"
  8 + p.author = "Ooyala"
  9 + p.email = "support@ooyala.com"
  10 + p.ignore_pattern = ["tmp/*", "script/*"]
  11 + p.development_dependencies = []
  12 +end
  13 +
  14 +Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
214 lib/ooyala_api.rb
... ... @@ -0,0 +1,214 @@
  1 +require 'rubygems'
  2 +
  3 +require 'base64'
  4 +require 'cgi'
  5 +require 'digest/sha2'
  6 +require 'logger'
  7 +require 'net/http'
  8 +require 'net/https'
  9 +require 'rest-client'
  10 +require 'rexml/document'
  11 +require 'thread'
  12 +
  13 +module Ooyala
  14 + module API
  15 +
  16 + class Client
  17 + attr_reader :partner, :ingestion
  18 + def initialize(partner_code, secret_code, default_expires=15, logger=Logger.new(STDERR))
  19 + @partner = Partner.new(partner_code, secret_code, default_expires, logger)
  20 + @ingestion = Ingestion.new(partner_code, secret_code, default_expires, logger)
  21 + end
  22 + end
  23 +
  24 +
  25 + class Base
  26 + def initialize(partner_code, secret_code, default_expires=15, logger=Logger.new(STDERR))
  27 + @partner_code = partner_code
  28 + @secret_code = secret_code
  29 + @default_expires = default_expires
  30 + @logger = logger
  31 + end
  32 +
  33 + private
  34 +
  35 + def signed_params(params, post_data='')
  36 + params = {
  37 + 'pcode' => @partner_code,
  38 + 'expires' => Time.now.gmtime.to_i + @default_expires,
  39 + }.merge(params)
  40 +
  41 + string_to_sign = @secret_code
  42 + param_string = ''
  43 +
  44 + params.keys.sort{ |a,b| a.to_s <=> b.to_s }.each do |key|
  45 + string_to_sign += "#{key}=#{params[key]}" if key != 'pcode'
  46 + param_string += "&#{CGI.escape(key.to_s)}=#{CGI.escape(params[key].to_s)}"
  47 + end
  48 +
  49 + string_to_sign += post_data
  50 +
  51 + digest = Digest::SHA256.digest(string_to_sign)
  52 + signature = Base64::encode64(digest).chomp.gsub(/=+$/, '')
  53 +
  54 + param_string += "&signature=#{CGI.escape(signature)}"
  55 +
  56 + return param_string.sub(/^&/, '')
  57 + end
  58 +
  59 + def create_http
  60 + http = Net::HTTP::new('api.ooyala.com', 443)
  61 + http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  62 + http.use_ssl = true
  63 + return http
  64 + end
  65 +
  66 + def get(path, params, headers={})
  67 + http = create_http()
  68 + url = path + '?' + signed_params(params)
  69 +
  70 + STDERR.puts url
  71 +
  72 + @logger.info "Calling API: #{url}"
  73 + response, body = http.get(url, headers)
  74 + return body
  75 + end
  76 +
  77 + def post(path, params, post_data, headers={})
  78 + http = create_http()
  79 + response, = http.post(path + '?' + signed_params(params, post_data),
  80 + post_data,
  81 + headers)
  82 + return body
  83 + end
  84 + end
  85 +
  86 +
  87 + class Partner < Ooyala::API::Base
  88 + def query(params)
  89 + return get('/partner/query', params)
  90 + end
  91 +
  92 + def thumbnails(params)
  93 + return get('/partner/thumbnails', params)
  94 + end
  95 +
  96 + def edit(params)
  97 + return get('/partner/edit', params)
  98 + end
  99 +
  100 + def labels(params)
  101 + return get('/partner/labels', params)
  102 + end
  103 +
  104 + def set_metadata(params)
  105 + return get('/partner/set_metadata', params)
  106 + end
  107 +
  108 + def metadata(params)
  109 + return get('/partner/metadata', params)
  110 + end
  111 +
  112 + def channels(params)
  113 + return get('/partner/channels', params)
  114 + end
  115 +
  116 + def assign_ads(params, post_data)
  117 + return post('/partner/ads',
  118 + (params || {}).merge({ :mode => 'assign' }),
  119 + post_data,
  120 + { 'Content-Type' => 'application/xml' })
  121 + end
  122 + end
  123 +
  124 +
  125 + class Ingestion < Ooyala::API::Base
  126 + def upload_video(file_path, original_file_name=nil, title=nil, chunk_size=2**22, num_threads=10, uploaded_by_client='', &callback)
  127 + start_time = Time.now
  128 + size = File.size(file_path)
  129 + original_file_name ||= File.basename(file_path)
  130 + title ||= original_file_name
  131 + embed_code, urls = create_video({ :file_name => original_file_name,
  132 + :title => title,
  133 + :file_size => size,
  134 + :chunk_size => chunk_size,
  135 + :uploaded_by_client => uploaded_by_client })
  136 + callback.call(embed_code) if not callback.nil?
  137 + file = File.open(file_path)
  138 +
  139 + # Thread uploads
  140 + thread_pool = []
  141 + num_threads.times do # We'll do up to 10 concurrent upload threads
  142 + thread_pool << Thread.new do
  143 + while true
  144 + url = bytes = nil
  145 + # Here we need thread exclusivity to get the appropriate url and bytes from the file
  146 + Thread.exclusive do
  147 + url = urls.shift
  148 + bytes = file.read(chunk_size) if url
  149 + end
  150 + break if url.nil? # We're done
  151 +
  152 + success = true
  153 + uri = URI.parse(url)
  154 + 10.times do |i| # Try up to 3 times to upload
  155 + begin
  156 + http = Net::HTTP::new(uri.host, uri.port)
  157 + http.use_ssl = true if uri.scheme == 'https'
  158 + response, body = http.put(uri.request_uri, bytes)
  159 + break if (success = response.is_a?(Net::HTTPNoContent))
  160 + @logger.error "Error uploading #{uri}:\n#{body}"
  161 + rescue Timeout::Error
  162 + @logger.error "Timeout uploading #{uri}"
  163 + end
  164 + end
  165 + raise Exception.new("Error uploading #{uri}") if !success
  166 + end
  167 + end
  168 + end
  169 + thread_pool.each { |thread| thread.join(60*60*3) }
  170 +
  171 + upload_complete({ :embed_code => embed_code })
  172 +
  173 + run_time = Time.now - start_time
  174 + @logger.info "Uploaded #{size} bytes in #{run_time} seconds (#{size * 8 / 1024 / 1024 / run_time} Mbps)."
  175 +
  176 + return embed_code
  177 +
  178 + ensure
  179 + file.close if file
  180 + end
  181 +
  182 + def create_video(params)
  183 + result = get('/ingestion/create_video', params)
  184 + doc = REXML::Document.new(result)
  185 + video = doc.elements['video']
  186 + embed_code = video.elements['embedCode'].text.strip
  187 + upload_urls = video.elements.collect('urls/url') { |e| e.text.strip }
  188 + return embed_code, upload_urls
  189 + end
  190 +
  191 + def upload_complete(params)
  192 + return get('/ingestion/upload_complete', params)
  193 + end
  194 +
  195 + def upload_preview(embed_code, file_name)
  196 + file = File.open(file_name)
  197 + url = 'http://uploader.ooyala.com/api/upload/preview?' + signed_params({ :embed_code => embed_code })
  198 + @logger.debug url
  199 + begin
  200 + res = RestClient.post(url, :file => File.new(file_name))
  201 + return res
  202 + rescue => e
  203 + puts "Error uploading preview for #{embed_code} with file : #{file_name}. Error #{e}"
  204 + end
  205 + ensure
  206 + file.close if file
  207 + end
  208 +
  209 + def create_remote_asset(params)
  210 + return get('/ingestion/create_remote_asset', params)
  211 + end
  212 + end
  213 + end
  214 +end

0 comments on commit e1845ce

Please sign in to comment.
Something went wrong with that request. Please try again.