diff options
-rwxr-xr-x | app.py | 59 | ||||
-rw-r--r-- | directory.py | 33 | ||||
-rw-r--r-- | static/index.html | 17 | ||||
-rw-r--r-- | static/player.js | 36 |
4 files changed, 82 insertions, 63 deletions
@@ -1,6 +1,6 @@ #!/usr/bin/env python2 -import os, mimetypes, json, cgi, recode, time, urllib, events, threading +import os, mimetypes, json, cgi, recode, time, urllib, events from config import config from directory import Directory, File @@ -11,26 +11,22 @@ class Application(object): 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 [] + rel_path = os.path.join(*path[1:] or '.') if os.path.isdir(full_path): start_response('200 OK', [('Content-Type', 'text/html; charset=UTF-8')]) return (str(x) for x in Directory(rel_path).listdir()) else: - args = cgi.FieldStorage(environ = environ) - - decoder = args.getvalue('decoder') if 'decoder' in args else None - encoder = args.getvalue('encoder') if 'encoder' in args else None - - if decoder and encoder: - cache_file = File(rel_path).get_cache_file() - return File(cache_file, True).send(environ, start_response) - else: - return File(rel_path).send(environ, start_response) + return File(rel_path).send(environ, start_response) def static(self, environ, start_response, path): filename = os.path.join('static', *path[1:]) - if not os.access(filename, os.F_OK) or '..' in path: + if not os.path.exists(filename) or '..' in path: start_response('404 Not Found', []) return [] @@ -38,6 +34,16 @@ class Application(object): start_response('200 OK', [('Content-Type', mime)]) return open(filename, 'rb') + def cache(self, environ, start_response, path): + path = os.path.join(*path[1:]) + cache_path = File(path).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 @@ -52,15 +58,34 @@ class Application(object): s = json.dumps([x.json() for x in contents]) return s - def json_cache(self, environ, start_response, path): + def json_recode(self, environ, start_response, path): args = cgi.FieldStorage(environ = environ) path = args.getvalue('path') if 'path' in args else None decoder = args.getvalue('decoder') if 'decoder' in args else None encoder = args.getvalue('encoder') if 'encoder' in args else None f = File(path) - t = threading.Thread(target = f.recode, args = (decoder, encoder, environ['sessionid'])) - t.start() + f.start_recode(decoder, encoder, environ['sessionid']) + + start_response('200 OK', [('Content-Type', 'text/plain')]) + return [] + + def json_play(self, environ, start_response, path): + args = cgi.FieldStorage(environ = environ) + + path = args.getvalue('path') + + f = File(path) + # TODO: replace this with some sane logic + if not os.path.splitext(path)[1] in ('.mp3', '.ogg'): + cache_path = f.get_cache_path() + decoder, encoder = ('ffmpeg',)*2 + if not os.path.exists(cache_path): + f.start_recode(decoder, encoder, environ['sessionid']) + else: + events.event_pub.play(environ['sessionid'], '/cache/{0}'.format(path)) + else: + events.event_pub.play(environ['sessionid'], '/files/{0}'.format(path)) start_response('200 OK', [('Content-Type', 'text/plain')]) return [] @@ -68,8 +93,10 @@ class Application(object): handlers = { 'files': files, 'static': static, + 'cache': cache, 'list': json_list, - 'cache': json_cache, + 'recode': json_recode, + 'play': json_play, 'events': events.EventSubscriber, } diff --git a/directory.py b/directory.py index 33391c6..48dfaa9 100644 --- a/directory.py +++ b/directory.py @@ -1,4 +1,4 @@ -import os, mimetypes, recode, events +import os, mimetypes, recode, events, threading from config import config @@ -84,32 +84,39 @@ class File(DirectoryEntry): ('Content-Length', str(size))]) return open(self.abs_path, 'rb') + def get_cache_path(self): + cache_path = os.path.join(config.get('cache_dir'), self.path) + cache_path = os.path.splitext(cache_path)[0] + '.mp3' + return cache_path + def get_cache_file(self): - cache_file = os.path.join(config.get('cache_dir'), self.path) - cache_file = os.path.splitext(cache_file)[0] + '.mp3' - return cache_file + return File(self.get_cache_path(), True) def recode(self, decoder, encoder, sessionid = None): decoder = recode.decoders[decoder]() encoder = recode.encoders[encoder]() recoder = recode.Recoder(decoder, encoder) - cache_file = self.get_cache_file() - cache_file_dir = os.path.dirname(cache_file) + cache_path = self.get_cache_path() + cache_path_dir = os.path.dirname(cache_path) # check and create cache directory - if not os.path.exists(cache_file_dir): - os.mkdir(cache_file_dir) + if not os.path.exists(cache_path_dir): + os.mkdir(cache_path_dir) # check if file is cached - if not os.path.exists(cache_file): + if not os.path.exists(cache_path): events.event_pub.recoding(self.path) - recoder.recode(self.abs_path, cache_file) + recoder.recode(self.abs_path, cache_path) events.event_pub.cached(self.path) if sessionid: - events.event_pub.play(sessionid, '/files/{0}'.format(self.path)) + events.event_pub.play(sessionid, '/cache/{0}'.format(self.path)) + + def start_recode(self, decoder, encoder, sessionid = None): + t = threading.Thread(target = self.recode, args = (decoder, encoder, sessionid)) + t.start() def json(self): - cache_file = self.get_cache_file() + cache_path = self.get_cache_path() d = DirectoryEntry.json(self) - d.update({'cached': os.path.exists(cache_file)}) + d.update({'cached': os.path.exists(cache_path)}) return d diff --git a/static/index.html b/static/index.html index ea20792..15384aa 100644 --- a/static/index.html +++ b/static/index.html @@ -7,19 +7,12 @@ <link rel="stylesheet" href="/static/style.css" type="text/css" /> </head> <body> - <div id="transcode-div"> - <label for="trans_enabled"><input type="checkbox" id="trans_enabled" /> Transcode</label> - from - <select id="trans_from"> - <option value="ffmpeg">FFmpeg</option> - </select> - to - <select id="trans_to"> - <option value="ffmpeg">FFmpeg</option> - </select> - </div> <textarea id="logbox" readonly="readonly" rows="10" cols="50"></textarea> - <div><input type="button" value="Close event source" onclick="source.close()" /></div> + <div> + <input type="button" value="Close event source" onclick="source.close()" /> + <input type="button" value="Open event source" onclick="event_open()" /> + <input type="button" value="Refresh directory" onclick="list(document.getElementById('current-dir').innerHTML.split(': ', 2)[1])" /> + </div> <div id="current-dir"></div> <ul id="song-links"></ul> <div id="audio-src-url">none</div> diff --git a/static/player.js b/static/player.js index ce75249..c0cf6ff 100644 --- a/static/player.js +++ b/static/player.js @@ -19,19 +19,14 @@ function MusicListing(type, path, name, cached) { this.a.tag = path; this.play = function() { - var transcode = document.getElementById('trans_enabled').checked; - var trans_from = document.getElementById('trans_from').value; - var trans_to = document.getElementById('trans_to').value; - var p = path; - if(transcode) - p += '?decoder=' + trans_from + '&encoder=' + trans_to; - log('playing ' + p); - change_url('/files/' + p); - audio.play(); + log('playing ' + path); + xmlhttp = new XMLHttpRequest(); + xmlhttp.open('GET', '/play?path=' + encodeURIComponent(path)); + xmlhttp.send(null); } - this.cache = function() { - var path = '/cache?path=' + encodeURIComponent(this.path) + + this.recode = function() { + var path = '/recode?path=' + encodeURIComponent(this.path) + '&decoder=' + document.getElementById('trans_from').value + '&encoder=' + document.getElementById('trans_to').value; var a = this.a; @@ -56,16 +51,7 @@ function MusicListing(type, path, name, cached) { if(type == 'dir') { list(path); } else if(type == 'file') { - var transcode = document.getElementById('trans_enabled').checked; - var trans_from = document.getElementById('trans_from').value; - var trans_to = document.getElementById('trans_to').value; - - var p = path; - if(transcode) { - ml.cache(p); - } else { - ml.play(); - } + ml.play(); } return false; } @@ -163,12 +149,18 @@ function event_handler(event) { } } -window.onload = function() { +function event_open() { + if(source) + source.close(); source = new EventSource('/events'); source.onopen = function() { log('event source opened'); } source.onmessage = event_handler; source.onerror = function(event) { log('event source error'); } log('event source status: ' + source.readyState); +} + +window.onload = function() { + event_open(); audio = new Audio(); list('/'); |