summaryrefslogtreecommitdiff
path: root/directory.py
diff options
context:
space:
mode:
Diffstat (limited to 'directory.py')
-rw-r--r--directory.py112
1 files changed, 112 insertions, 0 deletions
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 '<a href="/files/{path}">{name}</a><br />'.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