remoteobjects/examples/netflix.py

179 строки
5.7 KiB
Python

#!/usr/bin/env python
# Copyright (c) 2009-2010 Six Apart Ltd.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of Six Apart Ltd. nor the names of its contributors may
# be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""
An example Netflix API client, implemented using remoteobjects.
"""
__version__ = '1.0'
__date__ = '25 August 2009'
__author__ = 'Mark Paschal'
import cgi
from optparse import OptionParser
import sys
from urllib import urlencode
import urlparse
from xml.etree import ElementTree
import httplib2
from oauth.oauth import OAuthConsumer, OAuthRequest, OAuthSignatureMethod_HMAC_SHA1
from remoteobjects import RemoteObject, fields, PageObject
class Flixject(RemoteObject):
content_types = ('text/xml', 'application/xml')
api_token = None
def get_request(self, headers=None, **kwargs):
request = super(Flixject, self).get_request(headers=headers, **kwargs)
method = request.get('method', 'GET')
# Apply OAuthness.
csr = OAuthConsumer(*self.api_token)
orq = OAuthRequest.from_consumer_and_token(csr, http_method=method,
http_url=request['uri'])
# OAuthRequest will strip our query parameters, so add them back in.
parts = list(urlparse.urlparse(self._location))
queryargs = cgi.parse_qs(parts[4], keep_blank_values=True)
for key, value in queryargs.iteritems():
orq.set_parameter(key, value[0])
# Sign the request.
osm = OAuthSignatureMethod_HMAC_SHA1()
orq.set_parameter('oauth_signature_method', osm.get_name())
orq.sign_request(osm, csr, None)
if method == 'GET':
request['uri'] = orq.to_url()
else:
request['headers'].update(orq.to_header())
return request
def update_from_tree(self, tree):
data = dict((k, v(tree)) for k, v in self.decoder_ring.items())
self.update_from_dict(data)
return self
def update_from_response(self, url, response, content):
self.raise_for_response(url, response, content)
tree = ElementTree.fromstring(content)
self.update_from_tree(tree)
class Title(Flixject):
api_url = fields.Field()
title = fields.Field()
link = fields.Field()
thumb = fields.Field()
#synopsis = fields.Link(...)
decoder_ring = {
'title': lambda x: x.find('title').get('regular'),
'link': lambda x: [j for j in x.findall('link') if j.get('rel') == 'alternate'][0].get('href'),
'thumb': lambda x: x.find('box_art').get('large'),
'api_url': lambda x: x.find('id'),
}
class Catalog(Flixject):
results = fields.List(fields.Field())
total = fields.Field()
offset = fields.Field()
limit = fields.Field()
decoder_ring = {
'results': lambda x: [Title().update_from_tree(tree) for tree in x.findall('catalog_title')],
'total': lambda x: int(x.find('number_of_results').text),
'offset': lambda x: int(x.find('start_index').text),
'limit': lambda x: int(x.find('results_per_page').text),
}
def do_search(opts, args):
query = ' '.join(args)
search = Catalog.get('http://api.netflix.com/catalog/titles').filter(term=query)
search.deliver()
if len(search.results) == 0:
print "No results for %r" % query
elif len(search.results) == 1:
result = search.results[0]
print "## %s ##" % result.title
else:
print "## Results for %r ##" % query
print
for title in search.results:
if title is None:
print "(oops, none)"
else:
print title.title
return 0
def main(argv=None):
if argv is None:
argv = sys.argv
parser = OptionParser()
parser.add_option("-k", "--key", dest="key",
help="Netflix API key (required)")
parser.add_option("-s", "--secret", dest="secret",
help="Netflix API shared secret (required)")
parser.add_option("--search", action="store_const", const=do_search,
dest="action", default=do_search,
help="Search for an item by title (default)")
opts, args = parser.parse_args()
if opts.key is None or opts.secret is None:
print >>sys.stderr, "Options --key and --secret are required"
return 1
Flixject.api_token = (opts.key, opts.secret)
return opts.action(opts, args)
if __name__ == '__main__':
sys.exit(main(sys.argv))