# encoding: utf-8 # vi:si:et:sw=4:sts=4:ts=4 import sys import inspect import os import json from urlparse import urlparse from twisted.web.resource import Resource from twisted.web.static import File from twisted.web.util import Redirect from version import __version__ def trim(docstring): if not docstring: return '' # Convert tabs to spaces (following the normal Python rules) # and split into a list of lines: lines = docstring.expandtabs().splitlines() # Determine minimum indentation (first line doesn't count): indent = sys.maxint for line in lines[1:]: stripped = line.lstrip() if stripped: indent = min(indent, len(line) - len(stripped)) # Remove indentation (first line is special): trimmed = [lines[0].strip()] if indent < sys.maxint: for line in lines[1:]: trimmed.append(line[indent:].rstrip()) # Strip off trailing and leading blank lines: while trimmed and not trimmed[-1]: trimmed.pop() while trimmed and not trimmed[0]: trimmed.pop(0) # Return a single string: return '\n'.join(trimmed) def json_response(data=None, status=200, text='ok'): if not data: data = {} return {'status': {'code': status, 'text': text}, 'data': data} class ApiActions(dict): properties = {} def __init__(self): def api(backend, site, data): ''' returns list of all known api actions param data { docs: bool } if docs is true, action properties contain docstrings return { status: {'code': int, 'text': string}, data: { actions: { 'api': { cache: true, doc: 'recursion' }, 'hello': { cache: true, .. } ... } } } ''' docs = data.get('docs', False) code = data.get('code', False) _actions = self.keys() _actions.sort() actions = {} for a in _actions: actions[a] = self.properties[a] if docs: actions[a]['doc'] = self.doc(a) if code: actions[a]['code'] = self.code(a) response = json_response({'actions': actions}) return response self.register(api) def doc(self, f): return trim(self[f].__doc__) def code(self, name): f = self[name] if name != 'api' and hasattr(f, 'func_closure') and f.func_closure: f = f.func_closure[0].cell_contents info = f.func_code.co_firstlineno return info, trim(inspect.getsource(f)) def register(self, method, action=None, cache=True): if not action: action = method.func_name self[action] = method self.properties[action] = {'cache': cache} def unregister(self, action): if action in self: del self[action] def render(self, backend, site, action, data): if action in self: result = self[action](backend, site, data) else: result = json_response(status=404, text='not found') #print result return json.dumps(result) actions = ApiActions() class Server(Resource): def __init__(self, base, backend): self.base = base self.backend = backend Resource.__init__(self) def static_path(self, path): return os.path.join(self.base, 'static', path) def get_site(self, request): headers = request.getAllHeaders() #print headers if 'origin' in headers: request.headers['Access-Control-Allow-Origin'] = headers['origin'] site = headers['origin'] elif 'referer' in headers: u = urlparse(headers['referer']) site = u.scheme + '://' + u.hostname else: site = 'http://' + headers['host'] return site def getChild(self, name, request): print request if name in ('icon.png', 'favicon.ico'): f = File(self.static_path('png/icon16.png')) f.isLeaf = True return f if request.path == '/api/': return self if request.path.endswith('.webm'): video = request.path site = self.get_site(request) itemId, filename = video[1:].split('/') path = self.backend.get_file(site, itemId, filename) if path: print itemId, filename, 'use', path request.headers['Access-Control-Allow-Origin'] = '*' f = File(path, 'video/webm') f.isLeaf = True return f else: url = site + video #url = 'http://padmo.local/B/240p.webm' print "redirect", url return Redirect(url) path = request.path path = path[1:] if not path: path = 'index.html' path = self.static_path(path) f = File(path) if not os.path.isdir(path): f.isLeaf = True return f def render_POST(self, request): print 'render_POST' request.headers['Server'] = 'pandoralocal/%s' % __version__ site = self.get_site(request) print "POST", request.args if 'action' in request.args: if 'data' in request.args: data = json.loads(request.args['data'][0]) else: data = {} action = request.args['action'][0] return actions.render(self.backend, site, action, data) def render_GET(self, request): print 'render_GET' request.headers['Server'] = 'pandoralocal/%s' % __version__ path = self.static_path('api.html') f = open(path) data = f.read() f.close() request.headers['Content-Type'] = 'text/html' site = self.get_site(request) data = data.replace('$name', site) return data