Source code for couchpotato.api

from functools import wraps
from threading import Thread
import json
import threading
import traceback
import urllib

from couchpotato.core.helpers.request import getParams
from couchpotato.core.logger import CPLog
from tornado.ioloop import IOLoop
from tornado.web import RequestHandler, asynchronous


log = CPLog(__name__)


api = {}
api_locks = {}
api_nonblock = {}

api_docs = {}
api_docs_missing = []


[docs]def run_async(func): @wraps(func) def async_func(*args, **kwargs): func_hl = Thread(target = func, args = args, kwargs = kwargs) func_hl.start() return async_func
@run_async
[docs]def run_handler(route, kwargs, callback = None): try: res = api[route](**kwargs) callback(res, route) except: log.error('Failed doing api request "%s": %s', (route, traceback.format_exc())) callback({'success': False, 'error': 'Failed returning results'}, route)
# NonBlock API handler
[docs]class NonBlockHandler(RequestHandler):
stopper = None @asynchronous def get(self, route, *args, **kwargs): route = route.strip('/') start, stop = api_nonblock[route] self.stopper = stop start(self.sendData, last_id = self.get_argument('last_id', None)) def sendData(self, response): if not self.request.connection.stream.closed(): try: self.finish(response) except: log.debug('Failed doing nonblock request, probably already closed: %s', (traceback.format_exc())) try: self.finish({'success': False, 'error': 'Failed returning results'}) except: pass self.removeStopper() def removeStopper(self): if self.stopper: self.stopper(self.sendData) self.stopper = None
[docs]def addNonBlockApiView(route, func_tuple, docs = None, **kwargs): api_nonblock[route] = func_tuple if docs: api_docs[route[4:] if route[0:4] == 'api.' else route] = docs else: api_docs_missing.append(route)
# Blocking API handler
[docs]class ApiHandler(RequestHandler): route = None @asynchronous
[docs] def get(self, route, *args, **kwargs): self.route = route = route.strip('/') if not api.get(route): self.write('API call doesn\'t seem to exist') self.finish() return # Create lock if it doesn't exist if route in api_locks and not api_locks.get(route): api_locks[route] = threading.Lock() api_locks[route].acquire() try: kwargs = {} for x in self.request.arguments: kwargs[x] = urllib.unquote(self.get_argument(x)) # Split array arguments kwargs = getParams(kwargs) kwargs['_request'] = self # Remove t random string try: del kwargs['t'] except: pass # Add async callback handler run_handler(route, kwargs, callback = self.taskFinished) except: log.error('Failed doing api request "%s": %s', (route, traceback.format_exc())) try: self.write({'success': False, 'error': 'Failed returning results'}) self.finish() except: log.error('Failed write error "%s": %s', (route, traceback.format_exc())) self.unlock()
post = get
[docs] def taskFinished(self, result, route): IOLoop.current().add_callback(self.sendData, result, route) self.unlock()
[docs] def sendData(self, result, route):
if not self.request.connection.stream.closed(): try: # Check JSONP callback jsonp_callback = self.get_argument('callback_func', default = None) if jsonp_callback: self.set_header('Content-Type', 'text/javascript') self.finish(str(jsonp_callback) + '(' + json.dumps(result) + ')') elif isinstance(result, tuple) and result[0] == 'redirect': self.redirect(result[1]) else: self.finish(result) except UnicodeDecodeError: log.error('Failed proper encode: %s', traceback.format_exc()) except: log.debug('Failed doing request, probably already closed: %s', (traceback.format_exc())) try: self.finish({'success': False, 'error': 'Failed returning results'}) except: pass
[docs] def unlock(self): try: api_locks[self.route].release() except: pass
[docs]def addApiView(route, func, static = False, docs = None, **kwargs):
if static: func(route) else: api[route] = func api_locks[route] = threading.Lock() if docs: api_docs[route[4:] if route[0:4] == 'api.' else route] = docs else: api_docs_missing.append(route)