Skip to content

Commit

Permalink
upload-functionality restored
Browse files Browse the repository at this point in the history
  • Loading branch information
NDMarcel committed Nov 9, 2010
1 parent ca24ebd commit f0e6914
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 54 deletions.
23 changes: 12 additions & 11 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@ begin
gem.add_development_dependency "fakeweb", ">= 1.2.6"
gem.add_development_dependency "crack", ">= 0.1.4"
gem.add_development_dependency "ruby-prof", ">= 0.9.2"

gem.has_rdoc = true

gem.rdoc_options = ['--main', 'README.rdoc', '--inline-source', '--charset=UTF-8']
gem.extra_rdoc_files = ['README.rdoc', 'LICENSE', 'CHANGELOG.rdoc']

gem.add_dependency "httparty", ">= 0.4.5"
gem.add_dependency "json", ">= 1.1.9"
gem.add_dependency "oauth", ">= 0.3.6"
gem.add_dependency "oauth", ">= 0.4.3"
gem.add_dependency "httpclient", ">= 2.1.5.2"
gem.add_dependency "multipart-post", ">= 1.0.1"
end
Jeweler::GemcutterTasks.new
rescue LoadError
Expand Down Expand Up @@ -59,27 +60,27 @@ namespace :vimeo do
desc "Multi-step wizard to acquire an access_token. CONSUMER_KEY and CONSUMER_SECRET required."
task :auth do
require 'vimeo'

def ask(message)
print message
STDOUT.flush
STDIN.gets.chomp
end

consumer_key = ENV['CONSUMER_KEY']
consumer_secret = ENV['CONSUMER_SECRET']
base = Vimeo::Advanced::Base.new(consumer_key, consumer_secret)

request_token = base.get_request_token
oauth_secret = request_token.secret

puts "Please visit: #{base.authorize_url}"

oauth_token = ask("oauth_token=")
oauth_verifier = ask("oauth_verifier=")

access_token = base.get_access_token(oauth_token, oauth_secret, oauth_verifier)

puts "token: #{access_token.token}"
puts "secret: #{access_token.secret}"
end
Expand Down
2 changes: 2 additions & 0 deletions lib/vimeo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
require 'httparty'
require 'digest/md5'

require 'net/http/post/multipart'

$:.unshift(File.dirname(__FILE__))
require 'vimeo/simple'
require 'vimeo/advanced'
Expand Down
139 changes: 100 additions & 39 deletions lib/vimeo/advanced/upload.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
require 'httpclient'
require 'json'

module Vimeo
module Advanced

class Upload < Vimeo::Advanced::Base
class UploadError < RuntimeError; end

# 2 megabytes
# CHUNK_SIZE = 2 * 1024 * 1024
CHUNK_SIZE = 6000

BOUNDARY = "-----------RubyMultipartPost"

# Check to make sure an upload ticket is still valid.
create_api_method :check_ticket,
Expand All @@ -14,7 +20,7 @@ class Upload < Vimeo::Advanced::Base
# Complete the upload process.
create_api_method :complete,
"vimeo.videos.upload.complete",
:required => [:filename, :ticket_id]
:required => [:ticket_id, :filename]

# Returns an upload ticket.
create_api_method :get_ticket,
Expand All @@ -31,50 +37,105 @@ class Upload < Vimeo::Advanced::Base
:required => [:ticket_id]


def upload_chunk(data, endpoint, options = {})
pp @oauth_consumer.request(:post, endpoint, get_access_token, {}, options).body
# Uploads data (IO streams or files) to Vimeo.
def upload(uploadable)
case uploadable
when File
upload_file(uploadable)
when String
upload_file(File.new(uploadable))
else
upload_io(uploadable)
end
end

# Upload +file+ to vimeo with +ticket_id+ and +auth_token+
# Returns the json manifest necessary to confirm the upload.
def upload(file_path)
size = File.size(file_path)
basename = File.basename(file_path)
file = File.open(file_path)
file.binmode

chunk_size = 2 * 1024 * 1024 # 2 megabytes

protected

def upload_chunk(chunk_id, data, endpoint, filename)
endpoint += "&chunk_id=#{chunk_id}"

response = @oauth_consumer.request(:post, endpoint, get_access_token, {}, {}) do |req|
req.set_content_type("multipart/form-data", { "boundary" => BOUNDARY })

io = StringIO.new(data)
io.instance_variable_set :"@original_filename", filename
def io.original_filename; @original_filename; end
def io.content_type; "application/octet-stream"; end

parts = []
parts << Parts::FilePart.new(BOUNDARY, "file_data", io)
parts << Parts::EpiloguePart.new(BOUNDARY)

ios = parts.map{|p| p.to_io }
req.content_length = parts.inject(0) {|sum,i| sum + i.length }
req.body_stream = CompositeReadIO.new(*ios)

:continue
end

response.body
end

def upload_io(io, size, filename = 'io.data')
raise "#{io.inspect} must respond to #read" unless io.respond_to?(:read)

quota_response = get_quota
user = quota_response["user"]
upload_space = user["upload_space"]
free = upload_space["free"].to_i

raise UploadError.new, "file size exceeds quota. required: #{size}, free: #{free}" if size > free

ticket_response = get_ticket
pp ticket_response
ticket = ticket_response["ticket"]
ticket_id = ticket["id"]
endpoint = ticket["endpoint"]

chunk_count = (size.to_f / chunk_size.to_f).ceil
ticket = ticket_response["ticket"]
max_file_size = ticket["max_file_size"].to_i
ticket_id = ticket["id"]
endpoint = ticket["endpoint"]

raise UploadError.new, "file was too big: #{size}, maximum: #{max_file_size}" if size > max_file_size

chunk_sizes = {}

chunk_count.times do |chunk_index|
last = (chunk_index == chunk_count - 1)
data = last ? file.read : file.read(chunk_size)

chunk_sizes[chunk_index] = data.size
upload_chunk(data, endpoint, :chunk_id => chunk_index, :ticket_id => ticket_id)
chunk_index = 0

while (chunk = io.read(CHUNK_SIZE)) do

chunk_id = upload_chunk(chunk_index, chunk, endpoint, filename)
chunk_sizes[chunk_id] = chunk.length
chunk_index += 1
end

verification = verify_chunks(:ticket_id => ticket_id)["ticket"]
received_chunks = Hash[(verification["chunk"] || []).map do |chunk|
[chunk["id"], chunk["size"]]
end]

chunk_sizes.all? do |id, size|
received_chunks[id] == size

validate_chunks_after_upload(ticket_id, chunk_sizes)

complete(ticket_id, filename)
end

def upload_file(file)
file_path = file.path

size = File.size(file_path)
basename = File.basename(file_path)
io = File.open(file_path)
io.binmode

upload_io(io, size, basename).tap do
io.close
end

complete(:ticket_id => ticket_id, :filename => basename).tap do
file.close
end

def validate_chunks_after_upload(ticket_id, chunk_sizes)
verification = verify_chunks(ticket_id)
ticket = verification["ticket"]
chunk_list = Array(ticket["chunks"]["chunk"])
received_chunks = Hash[chunk_list.map { |chunk| [chunk["id"], chunk["size"].to_i] }]

chunk_sizes.each do |id, size|
vimeo_size = received_chunks[id]

if vimeo_size != size
raise UploadError.new, "Chunk (id: #{id}) was invalid - was: #{vimeo_size}, should be: #{size}."
end
end
end
end # Upload
end # Advanced
end # Vimeo
end # Vimeo
11 changes: 7 additions & 4 deletions vimeo.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Gem::Specification.new do |s|

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Matt Hooks"]
s.date = %q{2010-09-18}
s.date = %q{2010-11-09}
s.description = %q{A full featured Ruby implementation of the Vimeo API.}
s.email = %q{[email protected]}
s.extra_rdoc_files = [
Expand Down Expand Up @@ -227,17 +227,19 @@ Gem::Specification.new do |s|
s.add_development_dependency(%q<ruby-prof>, [">= 0.9.2"])
s.add_runtime_dependency(%q<httparty>, [">= 0.4.5"])
s.add_runtime_dependency(%q<json>, [">= 1.1.9"])
s.add_runtime_dependency(%q<oauth>, [">= 0.3.6"])
s.add_runtime_dependency(%q<oauth>, [">= 0.4.3"])
s.add_runtime_dependency(%q<httpclient>, [">= 2.1.5.2"])
s.add_runtime_dependency(%q<multipart-post>, [">= 1.0.1"])
else
s.add_dependency(%q<shoulda>, [">= 2.11.3"])
s.add_dependency(%q<fakeweb>, [">= 1.2.6"])
s.add_dependency(%q<crack>, [">= 0.1.4"])
s.add_dependency(%q<ruby-prof>, [">= 0.9.2"])
s.add_dependency(%q<httparty>, [">= 0.4.5"])
s.add_dependency(%q<json>, [">= 1.1.9"])
s.add_dependency(%q<oauth>, [">= 0.3.6"])
s.add_dependency(%q<oauth>, [">= 0.4.3"])
s.add_dependency(%q<httpclient>, [">= 2.1.5.2"])
s.add_dependency(%q<multipart-post>, [">= 1.0.1"])
end
else
s.add_dependency(%q<shoulda>, [">= 2.11.3"])
Expand All @@ -246,8 +248,9 @@ Gem::Specification.new do |s|
s.add_dependency(%q<ruby-prof>, [">= 0.9.2"])
s.add_dependency(%q<httparty>, [">= 0.4.5"])
s.add_dependency(%q<json>, [">= 1.1.9"])
s.add_dependency(%q<oauth>, [">= 0.3.6"])
s.add_dependency(%q<oauth>, [">= 0.4.3"])
s.add_dependency(%q<httpclient>, [">= 2.1.5.2"])
s.add_dependency(%q<multipart-post>, [">= 1.0.1"])
end
end

0 comments on commit f0e6914

Please sign in to comment.