From 97d7c9014855449fe04162308feac66a35e007ea Mon Sep 17 00:00:00 2001 From: Jon Bergli Heier Date: Mon, 8 Aug 2011 00:11:01 +0200 Subject: Initial commit. app.py - WSGI application and handlers. config.py - Config helper class. directory.py - Directory and file helper classes. events.py - zeromq event publisher and subscriber. recode.py - Codecs and stuff static/ - Web interface --- directory.py | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 directory.py (limited to 'directory.py') diff --git a/directory.py b/directory.py new file mode 100644 index 0000000..a93e00c --- /dev/null +++ b/directory.py @@ -0,0 +1,112 @@ +import os, mimetypes, recode, events + +from config import config + +class DirectoryEntry(object): + '''Base class for directory entries.''' + + def __init__(self, path, isabs = False): + self.path = path + + if isabs: + self.abs_path = path + else: + path_list = os.path.split(config.get('music_root')) + os.path.split(self.path) + if '..' in path_list: + raise Exception('Invalid path') + + self.abs_path = os.path.normpath(os.path.sep.join(path_list)) + + def __cmp__(self, other): + return cmp(self.path, other.path) + + def __lt__(self, other): + return self.path < other.path + + def __str__(self): + return '{name}
'.format(path = self.path, name = os.path.basename(self.path)) + + def json(self): + return {'type': self.entry_type, 'name': self.path} + +class Directory(DirectoryEntry): + '''A directory entry inside a directory.''' + + entry_type = 'dir' + + def listdir(self): + directories = [] + files = [] + + for f in os.listdir(self.abs_path): + abs_path = os.path.join(self.abs_path, f) + rel_path = os.path.relpath(abs_path, config.get('music_root')) + if os.path.isdir(abs_path): + directories.append(Directory(rel_path)) + elif os.path.isfile(abs_path): + files.append(File(rel_path)) + return sorted(directories) + sorted(files) + +class File(DirectoryEntry): + '''A file entry inside a directory.''' + + entry_type = 'file' + + def send(self, environ, start_response): + do_range = 'HTTP_RANGE' in environ + if do_range: + file_range = environ['HTTP_RANGE'].split('bytes=')[1] + + mime = mimetypes.guess_type(self.abs_path, strict = False)[0] or 'application/octet-stream' + size = os.path.getsize(self.abs_path) + if do_range: + start, end = [int(x or 0) for x in file_range.split('-')] + if end == 0: + end = size-1 + + write_out = start_response('206 Partial Content', [ + ('Content-Type', mime), + ('Content-Range', 'bytes {start}-{end}/{size}'.format(start = start, end = end, size = size)), + ('Content-Length', str(end - start + 1))]) + + f = open(self.abs_path, 'rb') + f.seek(start) + remaining = end-start+1 + s = f.read(min(remaining, 1024)) + while s: + write_out(s) + remaining -= len(s) + s = f.read(min(remaining, 1024)) + return [] + + start_response('200 OK', [ + ('Content-Type', mime), + ('Content-Length', str(size))]) + return open(self.abs_path, 'rb') + + 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 + + def recode(self, decoder, encoder): + 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) + # check and create cache directory + if not os.path.exists(cache_file_dir): + os.mkdir(cache_file_dir) + # check if file is cached + if not os.path.exists(cache_file): + events.event_pub.recoding(self.path) + recoder.recode(self.abs_path, cache_file) + events.event_pub.cached(self.path) + + def json(self): + cache_file = self.get_cache_file() + d = DirectoryEntry.json(self) + d.update({'cached': os.path.exists(cache_file)}) + return d -- cgit v1.2.3