#!/usr/bin/env python2 import os, mimetypes, json, cgi, recode, time, urllib, events, db, itertools, re from config import config from directory import Directory, File from session import Session class Application(object): # Application handlers def files(self, environ, start_response, path): full_path = os.path.join(config.get('music_root'), *path[1:]) if not os.path.exists(full_path) or '..' in path: start_response('404 Not Found', []) return [] if os.path.isdir(full_path): start_response('200 OK', [('Content-Type', 'text/html; charset=UTF-8')]) return (str(x) for x in Directory(full_path).listdir()) else: return File(full_path).send(environ, start_response) def static(self, environ, start_response, path): filename = os.path.join('static', *path[1:]) if not os.path.exists(filename) or '..' in path: start_response('404 Not Found', []) return [] mime = mimetypes.guess_type(filename, strict = False)[0] or 'application/octet-stream' start_response('200 OK', [('Content-Type', mime)]) return open(filename, 'rb') def cache(self, environ, start_response, path): args = cgi.FieldStorage(environ = environ) path = os.path.join(*path[1:]) path = os.path.join(config.get('music_root'), path) track = int(args.getvalue('track')) if 'track' in args else None try: session = db.Session() t = db.Track.find(session, path, track) except: start_response('404 Not Fonud', []) return [] finally: session.close() cache_path = File(path, track = track).get_cache_path() if not os.path.exists(cache_path) or '..' in path: start_response('404 Not Found', []) return [] f = File(cache_path, True) return f.send(environ, start_response) # JSON handlers def json_list(self, environ, start_response, path): args = cgi.FieldStorage(environ = environ) directory = args.getvalue('directory') if 'directory' in args else '' if directory[-1:] == '/': directory = directory[:-1] directory = os.path.join(config.get('music_root'), directory) if directory[-1:] == '/': directory = directory[:-1] contents = [] try: session = db.Session() directory = db.Directory.get(session, directory) directories = (Directory(d.path) for d in directory.children) tracks = (File(t.get_path(), track = t.file_index, metadata = t.get_metadata()) for t in directory.tracks) contents = [x.json() for x in itertools.chain(directories, tracks)] finally: session.close() start_response('200 OK', [('Content-Type', 'text/plain')]) return json.dumps(contents) def json_recode(self, environ, start_response, path): args = cgi.FieldStorage(environ = environ) path = args.getvalue('path') if 'path' in args else '' path = os.path.join(config.get('music_root'), path) track = int(args.getvalue('track')) if 'track' in args else None try: session = db.Session() t = db.Track.find(session, path, track) except: start_response('404 Not Fonud', []) return [] finally: session.close() f = File(path, track = track) # see json_play() if not os.path.splitext(path)[1] in ('.mp3', '.ogg'): decoder = 'ffmpeg' encoder = config.get('encoder') f.start_recode(decoder, encoder) start_response('200 OK', [('Content-Type', 'text/plain')]) return [] def json_play(self, environ, start_response, path): args = cgi.FieldStorage(environ = environ) rel_path = args.getvalue('path') path = os.path.join(config.get('music_root'), rel_path) track = int(args.getvalue('track')) if 'track' in args else None try: session = db.Session() t = db.Track.find(session, path, track) except: start_response('404 Not Fonud', []) return [] finally: session.close() f = File(path, track = track) # TODO: replace this with some sane logic if not os.path.splitext(path)[1] in ('.mp3', '.ogg'): cache_path = f.get_cache_path() decoder = 'ffmpeg' encoder = config.get('encoder') if not os.path.exists(cache_path): f.start_recode(decoder, encoder, environ['sessionid']) else: events.event_pub.play(environ['sessionid'], '/cache/{0}{1}'.format(rel_path, ('?track=' + str(track) if track else ''))) else: events.event_pub.play(environ['sessionid'], '/files/{0}'.format(rel_path)) start_response('200 OK', [('Content-Type', 'text/plain')]) return [] re_search = re.compile(r'(")?((?(1)[^"]|[^ ])+)(?(1)")') def json_search(self, environ, start_response, path): args = cgi.FieldStorage(environ = environ) query = args.getvalue('query') r = self.re_search.findall(query) d = {} l = [] for _, v in r: if ':' in v: k, v = v.split(':', 1) d[k] = v else: l.append(v) results = [] try: session = db.Session() r = db.Track.search(session, *l, **d) results = [File(x.get_path(), metadata = x.get_metadata(), track = x.file_index).json() for x in r] finally: session.close() start_response('200 OK', []) return json.dumps(results) handlers = { 'files': files, 'static': static, 'cache': cache, 'list': json_list, 'recode': json_recode, 'play': json_play, 'search': json_search, 'events': events.EventSubscriber, } # WSGI handler def __call__(self, environ, start_response): path = urllib.unquote(environ['PATH_INFO']) path = path.split('/')[1:] module = path[0] or None if not module: module = 'static' path = ['static', 'index.html'] if module in self.handlers: return Session(self.handlers[module])(self, environ, start_response, path) start_response('404 Not Found', [('Content-Type', 'text/plain')]) return [str(path)] if __name__ == '__main__': import sys if len(sys.argv) == 3: from flup.server.fcgi import WSGIServer WSGIServer(Application(), bindAddress = (sys.argv[1], int(sys.argv[2]))).run() else: from wsgiref.simple_server import make_server, WSGIServer # enable IPv6 WSGIServer.address_family |= 10 httpd = make_server('', 8000, Application()) httpd.serve_forever()