summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xapp.py27
-rw-r--r--codec.py31
-rw-r--r--db.py5
-rw-r--r--static/icons/loading.gifbin0 -> 1737 bytes
-rw-r--r--static/icons/music_nocache.pngbin0 -> 646 bytes
-rw-r--r--static/icons/music_playing.pngbin0 -> 633 bytes
-rw-r--r--static/init.js29
-rw-r--r--static/style.css3
8 files changed, 84 insertions, 11 deletions
diff --git a/app.py b/app.py
index e947f80..7d88ebe 100755
--- a/app.py
+++ b/app.py
@@ -1,9 +1,26 @@
#!/usr/bin/env python2
-import db, os, json, mimetypes, datetime
+import db, codec, os, json, mimetypes, datetime
from config import config
class JSONApplication(object):
+ @staticmethod
+ def cache_check(track):
+ ext = os.path.splitext(track.filename)[-1][1:]
+ if ext in config.get('allow_extensions').split():
+ return True
+ try:
+ r = codec.Recoder(track.get_path(), config.get('encoder'), track.file_index, os.path.join(config.get('cache_dir'), str(track.id)))
+ except codec.DecoderNotFoundError:
+ return False
+ return os.path.exists(r.enc_destination)
+
+ @staticmethod
+ def format_track(track):
+ d = track.dict()
+ d['cache'] = JSONApplication.cache_check(track)
+ return d
+
def list(self, environ, start_response, path):
root_id = int(path[1]) if len(path) > 1 and len(path[1]) else 0
session = db.Session()
@@ -15,7 +32,7 @@ class JSONApplication(object):
directories = directory.children
tracks = directory.tracks
contents = json.dumps([x.dict() for x in directories] +
- [x.dict() for x in tracks])
+ [self.format_track(x) for x in tracks])
finally:
session.close()
start_response('200 OK', [('Content-Type', 'application/json'), ('Content-Length', str(len(contents)))])
@@ -88,6 +105,12 @@ class Application(object):
try:
track = db.Track.get_by_id(session, track)
filename = track.get_path()
+ ext = os.path.splitext(filename)[-1][1:]
+ if not ext in config.get('allow_extensions').split():
+ r = codec.Recoder(filename, config.get('encoder'), track.file_index, os.path.join(config.get('cache_dir'), str(track.id)))
+ filename = r.enc_destination
+ if not os.path.exists(filename):
+ r.recode()
except db.NoResultFound:
start_response('404 Not Found', [])
return []
diff --git a/codec.py b/codec.py
index 8d8c3d6..417e9e3 100644
--- a/codec.py
+++ b/codec.py
@@ -1,4 +1,4 @@
-import subprocess, os
+import subprocess, os, cuesheet
from config import config
decoders = {}
@@ -54,8 +54,12 @@ class FFmpeg(Decoder):
test_exists = test_executable('ffmpeg')
- def decode(self):
+ def decode(self, **kwargs):
cmd = 'ffmpeg -loglevel quiet'.split()
+ if 'start_time' in kwargs and kwargs['start_time']:
+ cmd += ['-ss', str(kwargs['start_time'])]
+ if 'end_time' in kwargs and kwargs['end_time']:
+ cmd += ['-t', str(kwargs['end_time'] - kwargs['start_time'])]
cmd += ['-i', self.source, '-y', self.destination]
devnull = open('/dev/null', 'a+')
p = subprocess.Popen(cmd, stderr = devnull, close_fds = True)
@@ -80,7 +84,16 @@ class DecoderNotFoundError(Exception): pass
class EncoderNotFoundError(Exception): pass
class Recoder(object):
- def __init__(self, source, encoder):
+ def __init__(self, source, encoder, track = None, destination = None):
+ if track:
+ cue = cuesheet.Cuesheet(source)
+ source = os.path.join(os.path.dirname(source), cue.info[0].file[0])
+ track = cue.tracks[track-1]
+ self.start_time = track.get_start_time()
+ track = cue.get_next(track)
+ self.end_time = track.get_start_time() if track else None
+ else:
+ self.start_time, self.end_time = None, None
# TODO: Python 3 breakage (must be str)
if isinstance(encoder, basestring):
if not encoder in encoders:
@@ -89,9 +102,15 @@ class Recoder(object):
self.dec_source = source
# Boldly assume all decoders can convert to wave format.
- self.dec_destination = os.path.join(config.get('cache_dir'), os.path.splitext(os.path.basename(source))[0] + '.wav')
+ if destination:
+ self.dec_destination = os.path.splitext(destination)[0] + '.wav'
+ else:
+ self.dec_destination = os.path.join(config.get('cache_dir'), os.path.splitext(os.path.basename(source))[0] + '.wav')
self.enc_source = self.dec_destination
- self.enc_destination = os.path.splitext(self.dec_destination)[0] + encoder.extension
+ if destination:
+ self.enc_destination = os.path.splitext(destination)[0] + encoder.extension
+ else:
+ self.enc_destination = os.path.splitext(self.dec_destination)[0] + encoder.extension
self.decoder = None
for decoder in decoders.itervalues():
@@ -103,7 +122,7 @@ class Recoder(object):
self.encoder = encoder(self.enc_source, self.enc_destination)
def recode(self):
- self.decoder.decode()
+ self.decoder.decode(start_time = self.start_time, end_time = self.end_time)
self.encoder.encode()
os.unlink(self.dec_destination)
diff --git a/db.py b/db.py
index aa7ea6a..248d56a 100644
--- a/db.py
+++ b/db.py
@@ -165,7 +165,10 @@ class Track(Base):
return r.all()
def get_path(self):
- return os.path.join(self.directory.path, self.filename)
+ s = os.path.join(self.directory.path, self.filename)
+ if isinstance(s, unicode):
+ s = s.encode('utf-8')
+ return s
def get_relpath(self):
return os.path.relpath(self.get_path(), config.get('music_root'))
diff --git a/static/icons/loading.gif b/static/icons/loading.gif
new file mode 100644
index 0000000..1560b64
--- /dev/null
+++ b/static/icons/loading.gif
Binary files differ
diff --git a/static/icons/music_nocache.png b/static/icons/music_nocache.png
new file mode 100644
index 0000000..5034c06
--- /dev/null
+++ b/static/icons/music_nocache.png
Binary files differ
diff --git a/static/icons/music_playing.png b/static/icons/music_playing.png
new file mode 100644
index 0000000..6802f99
--- /dev/null
+++ b/static/icons/music_playing.png
Binary files differ
diff --git a/static/init.js b/static/init.js
index 0dd0af6..cdb806d 100644
--- a/static/init.js
+++ b/static/init.js
@@ -13,6 +13,16 @@ function pause() {
sound.togglePause();
}
+function preload_images() {
+ var cache_images = new Array(
+ 'loading.gif',
+ 'music_playing.png'
+ );
+ $.each(cache_images, function() {
+ (new Image()).src = '/static/icons/' + this;
+ });
+}
+
Handlebars.registerHelper('trackname', function() {
var item = this;
if(!item.metadata)
@@ -33,7 +43,7 @@ Handlebars.registerHelper('trackname', function() {
});
var templates = new (function Templates() {
- this.directory_item = Handlebars.compile('<li class="{{type}}"><a href="#">{{trackname}}</a>');
+ this.directory_item = Handlebars.compile('<li id="{{type}}-{{id}}" class="{{type}}{{#if nocache}} nocache{{/if}}"><a href="#">{{trackname}}</a>');
})();
function load_directory(dir_id, dir_item) {
@@ -54,11 +64,15 @@ function load_directory(dir_id, dir_item) {
);
}
$.each(data, function(i, item) {
+ if(item.type == "track")
+ item.nocache = !item.cache;
var el = $(templates.directory_item(item));
+ var id = el.attr('id');
if(item.type == "track") {
$(el, 'a').click(function() {
- console.log(item);
+ el.addClass('loading');
if(sound) {
+ sound.stop();
sound.destruct();
}
sound = soundManager.createSound({
@@ -67,7 +81,11 @@ function load_directory(dir_id, dir_item) {
whileloading: function() {
$('#status').text('Loading... ' + this.bytesLoaded);
},
+ onload: function(success) {
+ el.removeClass('loading').removeClass('nocache');
+ },
whileplaying: function() {
+ $('#' + id).addClass('playing');
var seconds = (this.position / 1000).toFixed(0);
var minutes = Math.floor(seconds / 60).toFixed(0);
seconds %= 60;
@@ -75,6 +93,12 @@ function load_directory(dir_id, dir_item) {
seconds = '0' + seconds;
var pos = minutes + ':' + seconds;
$('#status').text(pos);
+ },
+ onstop: function() {
+ $('#' + id).removeClass('playing');
+ },
+ onfinish: function() {
+ $('#' + id).removeClass('playing');
}
});
sound.play();
@@ -93,5 +117,6 @@ function load_directory(dir_id, dir_item) {
}
$(document).ready(function() {
+ preload_images();
load_directory(0);
});
diff --git a/static/style.css b/static/style.css
index 28fea31..a7ea139 100644
--- a/static/style.css
+++ b/static/style.css
@@ -5,3 +5,6 @@
#directory-list .dir, #directory-list .track { background-repeat: no-repeat; padding-left: 20px; }
#directory-list .dir { background-image: url('/static/icons/folder.png'); }
#directory-list .track { background-image: url('/static/icons/music.png'); }
+#directory-list .nocache { background-image: url('/static/icons/music_nocache.png'); }
+#directory-list .loading { background-image: url('/static/icons/loading.gif'); }
+#directory-list .playing { background-image: url('/static/icons/music_playing.png'); }