summaryrefslogtreecommitdiff
path: root/directory.py
diff options
context:
space:
mode:
authorJon Bergli Heier <snakebite@jvnv.net>2012-02-13 22:09:11 +0100
committerJon Bergli Heier <snakebite@jvnv.net>2012-02-13 22:09:11 +0100
commit0d8e86265f74ee36503bbcec445dcefdea208df0 (patch)
treed129e83b5913124ca4542ae6b5e6c40fcafc6517 /directory.py
Initial import.
Diffstat (limited to 'directory.py')
-rw-r--r--directory.py134
1 files changed, 134 insertions, 0 deletions
diff --git a/directory.py b/directory.py
new file mode 100644
index 0000000..67db83f
--- /dev/null
+++ b/directory.py
@@ -0,0 +1,134 @@
+import os, mimetypes, cuesheet, mutagen, db
+
+from config import config
+
+class DirectoryEntry(object):
+ '''Base class for directory entries.'''
+
+ def __init__(self, path, track = None, metadata = {}):
+ self.path = path
+ self.track = track
+ self.metadata = metadata
+
+ if '..' in path.split():
+ raise Exception('Invalid path')
+
+ self.rel_path = os.path.relpath(path, config.get('music_root'))
+
+ 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.rel_path, name = os.path.basename(self.path))
+
+ def json(self):
+ return {'type': self.entry_type, 'name': self.rel_path, 'track': self.track, 'metadata': self.metadata}
+
+class Directory(DirectoryEntry):
+ '''A directory entry inside a directory.'''
+
+ entry_type = 'dir'
+
+ def listdir(self):
+ directories = []
+ files = []
+
+ for f in os.listdir(self.path):
+ path = os.path.join(self.path, f)
+ if os.path.isdir(path):
+ directories.append(Directory(path))
+ elif os.path.isfile(path):
+ if os.path.splitext(f)[1] == '.cue':
+ cue = cuesheet.Cuesheet(path)
+ for t in cue.tracks:
+ metadata = {}
+ info = cue.info[0]
+ if info.performer:
+ metadata['artist'] = info.performer
+ if info.title:
+ metadata['album'] = info.title
+ if t.title:
+ metadata['title'] = t.title
+ files.append(File(path, track = t.track[0], metadata = metadata))
+ else:
+ metadata = {}
+ tags = mutagen.File(path) or []
+ if isinstance(tags, mutagen.mp3.MP3):
+ for id3, tn in (('TPE1', 'artist'), ('TALB', 'album'), ('TIT2', 'title')):
+ if id3 in tags:
+ metadata[tn] = tags[id3].text[0]
+ else:
+ for tn in ('artist', 'album', 'title'):
+ if tn in tags:
+ metadata[tn] = tags[tn][0].encode('utf-8')
+ files.append(File(path, metadata = metadata))
+ 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.path, strict = False)[0] or 'application/octet-stream'
+ size = os.path.getsize(self.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.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.path, 'rb')
+
+def rec_scan(session, root, parent_id = None):
+ directory = db.Directory.get(session, root, parent_id)
+
+ d = Directory(root)
+ for de in d.listdir():
+ if isinstance(de, Directory):
+ print 'id:', directory.id
+ rec_scan(session, de.path, directory.id)
+ else:
+ print de.metadata
+ artist = db.Artist.get(session, de.metadata['artist']) if 'artist' in de.metadata else None
+ album = db.Album.get(session, de.metadata['album'], artist.id if artist else None) if 'album' in de.metadata else None
+ track = db.Track.get(session, de.metadata['title'] if 'title' in de.metadata else None, None,
+ os.path.basename(de.path), de.track, directory.id, artist.id if artist else None, album.id if album else None)
+
+def scan(root = None):
+ if not root:
+ root = config.get('music_root')
+
+ try:
+ session = db.Session()
+ rec_scan(session, root)
+ session.commit()
+ finally:
+ session.close()
+
+if __name__ == '__main__':
+ scan()