diff --git a/couchpotato/api.py b/couchpotato/api.py index f251841c..cd0bb046 100644 --- a/couchpotato/api.py +++ b/couchpotato/api.py @@ -1,3 +1,5 @@ +from functools import wraps +from threading import Thread import json import threading import traceback @@ -5,7 +7,6 @@ import urllib from couchpotato.core.helpers.request import getParams from couchpotato.core.logger import CPLog -from tornado.gen import coroutine from tornado.web import RequestHandler, asynchronous @@ -20,6 +21,24 @@ api_docs = {} api_docs_missing = [] +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 +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 class NonBlockHandler(RequestHandler): @@ -65,7 +84,7 @@ def addNonBlockApiView(route, func_tuple, docs = None, **kwargs): # Blocking API handler class ApiHandler(RequestHandler): - @coroutine + @asynchronous def get(self, route, *args, **kwargs): route = route.strip('/') if not api.get(route): @@ -88,32 +107,43 @@ class ApiHandler(RequestHandler): try: del kwargs['t'] except: pass - # Fire api handler(s) - try: - result = api[route](**kwargs) - except: - log.error('Failed doing api request "%s": %s', (route, traceback.format_exc())) - result = {'success': False, 'error': 'Failed returning results'} + # Add async callback handler + run_handler(route, kwargs, callback = self.taskFinished) + except: + log.error('Failed doing api request "%s": %s', (route, traceback.format_exc())) + self.write({'success': False, 'error': 'Failed returning results'}) + self.finish() + + api_locks[route].release() + + post = get + + def taskFinished(self, result, route): + + if self.request.connection.stream.closed(): + return + + try: # Check JSONP callback jsonp_callback = self.get_argument('callback_func', default = None) if jsonp_callback: self.write(str(jsonp_callback) + '(' + json.dumps(result) + ')') self.set_header("Content-Type", "text/javascript") + self.finish() elif isinstance(result, tuple) and result[0] == 'redirect': self.redirect(result[1]) else: self.write(result) - + self.finish() except: - log.error('Failed doing api request "%s": %s', (route, traceback.format_exc())) - self.write({'success': False, 'error': 'Failed returning results'}) + log.debug('Failed doing request, probably already closed: %s', (traceback.format_exc())) + try: self.finish({'success': False, 'error': 'Failed returning results'}) + except: pass api_locks[route].release() - post = get - def addApiView(route, func, static = False, docs = None, **kwargs): diff --git a/couchpotato/core/database.py b/couchpotato/core/database.py index 40b29285..1b9501c2 100644 --- a/couchpotato/core/database.py +++ b/couchpotato/core/database.py @@ -372,10 +372,10 @@ class Database(object): m = medias[x] status = statuses.get(m['status_id']).get('identifier') - l = libraries[m['library_id']] + l = libraries.get(m['library_id']) # Only migrate wanted movies, Skip if no identifier present - if not getImdb(l.get('identifier')): continue + if not l or not getImdb(l.get('identifier')): continue profile_id = profile_link.get(m['profile_id']) category_id = category_link.get(m['category_id'])