A Revver command line video upload tool

Update: It seems the script had gone missing at some point. It's back.

As you may or may not have noticed, I do a bunch of stuff for Revver. I ended up writing a sort of a tutorial to the Revver API, and as I like to collect all kinds of code samples here, I thought I should crossblog it here. The original is on the Revver developer blog.

One day, I was on a slow internet connection and wanted to upload a few files. I wanted something more batch-oriented than the web-based upload, and I have a personal bias against most current Java runtimes. So I decided to use the cool new API and write a video upload client, and will walk you through what it does in this blog entry. Feel free to "just" use the tool, but hopefully this will also help you in writing your own API clients.

First of all, I wanted to write something that's usable just about everywhere. I tend to use Python, so that's what the tool is written in. The Python standard library didn't seem to be able to do HTTP POST file upload (think web forms) of large files, so I ended up using curl for that. This should work on any Linux/OS X/etc box with Python and curl installed. All you Ubuntu/Debian people just get to say sudo apt-get install curl and that's it.

So, let's dive right in. The tool is imaginatively named revver-upload-video. The first bit is the command line parser. Don't be intimidated by the length, this is pretty much boilerplate code, the actual API-using bits are really small. The full file is 155 lines, total.

There are basically three kinds of options: mandatory, optional and for developer use. Mandatory options are enforced later on, and developer options are mostly meant for playing with the staging environment and reusing upload tokens from previous, failed, uploads.

#!/usr/bin/python
"""
Guerrilla command line video upload tool.
"""
import optparse, getpass, urlparse, urllib, xmlrpclib, subprocess

def getParser():
	parser = optparse.OptionParser(
		usage='%prog --title=TEXT --age-rating=NUM [OPTIONS] FILE..',
		description='Upload videos to Revver')

	parser.set_defaults(
		api_url='https://api.revver.com/xml/1.0',
		upload_url='http://httpupload.revver.com/',
		login=getpass.getuser(),
		)
	parser.add_option('--login',
					  help='login name to use (default: %s)' %
					  parser.defaults['login'])
	parser.add_option('--passphrase-file',
					  help='read passphrase from (prompt if not given)',
					  metavar='FILE')
	parser.add_option('--age-rating',
					  help='MPAA age rating (mandatory)',
					  type='int')
	parser.add_option('--title',
					  help='title for the video (mandatory)',
					  metavar='TEXT')
	parser.add_option('--tag',
					  help='tags (mandatory, repeat for more tags)',
					  metavar='KEYWORD',
					  action='append')

	parser.add_option('--author',
					  help='author of the video',
					  metavar='FULLNAME')
	parser.add_option('--url',
					  help='website for extra info')
	parser.add_option('--credits',
					  help='extra credits',
					  metavar='TEXT')
	parser.add_option('--description',
					  help='a brief description',
					  metavar='TEXT')

	parser.add_option('--api-url',
					  help='API URL to contact (developers only)')
	parser.add_option('--upload-url',
					  help='Upload URL to send the file to (developers only)')
	parser.add_option('--upload-token',
					  help='use preallocated token (developers only)',
					  metavar='HEX',
					  action='append')

	return parser

If you've used optparse before, there's not much interesting there. It just instantiates a parser object and returns it, for the main function to use. Nothing there touches the Revver API yet.

Next up, we have some utility functions. getPassphrase will read a passphrase form the file given to --passphrase-file=, or prompt the user for one. getAPI instantiates an XML-RPC client object with the login and passphrase, and caches it in options so if you call getAPI more than once, you're still only prompted for the passphrase at most once. Nothing in revver-upload-video uses that, but these are meant to be reusable functions.

def getPassphrase(filename=None):
	if filename is not None:
		f = file(filename)
		passphrase = f.readline().rstrip('\n')
		f.close()
	else:
		passphrase = getpass.getpass('Passphrase for video upload: ')

	return passphrase

def getAPI(options):
	api = getattr(options, 'api', None)
	if api is None:
		passphrase = getPassphrase(filename=options.passphrase_file)

		(scheme, netloc, path, query, fragment) = \
				urlparse.urlsplit(options.api_url,
								  allow_fragments=False)
		query = urllib.urlencode([('login', options.login),
								  ('passwd', passphrase)])
		url = urlparse.urlunsplit((scheme, netloc, path, query, fragment))
		api = xmlrpclib.Server(url)
		options.api = api
	return api

All right, now we're getting to the actual meat. getToken calls the API method video.getUploadTokens to allocate an upload token, that lets you upload a file to the Revver archive.

def getToken(api):
	url, tokens = api.video.getUploadTokens(1)
	assert len(tokens)==1
	token = tokens[0]
	return url, token

createMedia creates a new video in the archive from your uploaded file by calling video.create in the API. It also adds metadata like your website URL to the video. Finally, it returns the media id of the newly-created video.

def createMedia(options, token):
	data = {}
	if options.credits is not None:
		data['credits'] = options.credits
	if options.url is not None:
		data['url'] = options.url
	if options.description is not None:
		data['description'] = options.description
	if options.author is not None:
		data['author'] = options.author
	api = getAPI(options)
	media_id = api.video.create(token,
								options.title,
								options.tag,
								options.age_rating,
								data)
	return media_id

Finally, we have the main function, and the bits that call it when you run the tool. Here, we actually parse the command line arguments, enforce the presence of the mandatory options, and bail out unless you gave it actual files to upload.

For each file given on the command line, we either use one of the tokens given to us with --upload-token=, or get one from the API with getToken. Then we join the upload URL and the token to get the place to upload the file to, and run curl as a subprocess to do the actual upload. Checking that curl worked takes 8 lines, and then we use createMedia to actually create the video. And that's it!

def main(progname, args):
	parser = getParser()
	(options, args) = parser.parse_args()

	if options.login is None:
		parser.error('You must pass --login=LOGIN')
	if options.title is None:
		parser.error('You must pass --title=TEXT')
	if not options.tag:
		parser.error('You must pass --tag=KEYWORD')
	if options.age_rating is None:
		parser.error('You must pass --age-rating=NUM')
	if not args:
		parser.error('Pass files to upload on command line')

	for filename in args:
		if options.upload_token:
			token = options.upload_token.pop(0)
			url = options.upload_url
		else:
			api = getAPI(options)
			url, token = getToken(api)
			print '%s: allocated token %s' % (progname, token)

		upload_url = urlparse.urljoin(url, token)
		retcode = subprocess.call(['curl',
								   '-F', 'file=@%s' % filename,
								   '--',
								   upload_url,
								   ])
		if retcode < 0:
			print >>sys.stderr, '%s: upload aborted by signal %d' % (
				progname, -retcode)
			sys.exit(1)
		elif retcode > 0:
			print >>sys.stderr, '%s: upload failed with code %d' % (
				progname, retcode)
			sys.exit(1)
		print '%s: used token %s for %s' % (progname, token, filename)

		media_id = createMedia(options, token)
		print '%s: created media %r from %r' % (progname,
												media_id,
												filename)

if __name__ == '__main__':
	import os, sys
	main(progname=os.path.basename(sys.argv[0]),
		 args=sys.argv[1:])

At this point, we have all we need to do the same thing as the web-based upload, or the Java upload client. And you can do something similar, by yourself. This file is copyright Revver, Inc, but licensed under the MIT license -- that means you can use it as a base for writing your software, without any real restrictions. Download the whole thing here: revver-upload-video.

2008-07-20T13:33+03:00, originally published 2006-11-13T20:27-08:00