diff --git a/oxcd/api.py b/oxcd/api.py index dcb172c..ed71600 100644 --- a/oxcd/api.py +++ b/oxcd/api.py @@ -10,6 +10,7 @@ def init(backend, site, data): actions.register(init, cache=False) def library(backend, site, data): - response = backend.library + response = {} + response['tracks'] = backend.tracks return json_response(response) actions.register(library, cache=True) diff --git a/oxcd/itunes.py b/oxcd/itunes.py index 538e9da..3d046e6 100644 --- a/oxcd/itunes.py +++ b/oxcd/itunes.py @@ -3,13 +3,21 @@ from __future__ import with_statement, division import os +import re from urllib import unquote +from threading import Thread from plistlib import readPlist class iTunes(object): + tracks = [] def __init__(self, xml): - self.library = readPlist(xml) + self.xml = xml + t = Thread(target=self.parse_xml, args=[]) + t.start() + + def parse_xml(self): + self.library = readPlist(self.xml) for id in self.library['Tracks']: self.library['Tracks'][id]['Location'] = unquote(self.library['Tracks'][id]['Location'].replace('file://localhost/', '/')) if self.library['Tracks'][id]['Location'].startswith('//'): @@ -18,6 +26,31 @@ class iTunes(object): self.library['Music Folder'] = unquote(self.library['Music Folder'].replace('file://localhost/', '/')) if self.library['Music Folder'].startswith('//'): self.library['Music Folder'] = self.library['Music Folder'][1:] - self.xml = xml self.root = self.library['Music Folder'] + self.tracks = self.load_tracks() + def load_tracks(self): + tracks = [] + keys = [ + 'id', 'name', 'artist', 'album', 'kind', 'year', 'duration', 'size', + 'sortArtist', 'albumArtist', 'sortAlbumArtist', 'compliation', + ] + for t in self.library['Tracks']: + track = self.library['Tracks'][t] + item = {} + for key in keys: + item[key] = track.get({ + 'id': 'Track ID', + 'duration': 'Total Time', + }.get( + key, + re.sub( + '^(.)', + lambda m: m.groups(0)[0].capitalize(), + re.sub('([A-Z])', ' \\1', key) + ) + ), None) + if item[key] == None: + del item[key] + tracks.append(item) + return tracks diff --git a/oxcd/server.py b/oxcd/server.py index 73cd318..0538737 100644 --- a/oxcd/server.py +++ b/oxcd/server.py @@ -14,10 +14,10 @@ from twisted.web.resource import Resource from twisted.web.static import File from twisted.web.util import Redirect from twisted.web.error import NoResource +from twisted.web.server import NOT_DONE_YET from version import __version__ -from gziprequest import GzipFile - +from gziprequest import GzipFile, GzipRequest def trim(docstring): if not docstring: return '' @@ -198,7 +198,13 @@ class Server(Resource): else: data = {} action = request.args['action'][0] - return actions.render(self.backend, site, action, data) + accept_encoding = request.getHeader('accept-encoding') + if 'gzip' in accept_encoding: + request = GzipRequest(request) + #FIXME: this should be done async + request.write(actions.render(self.backend, site, action, data)) + request.finish() + return NOT_DONE_YET def render_GET(self, request): request.headers['Server'] = 'oxcd/%s' % __version__ diff --git a/oxcd/static/js/index.js b/oxcd/static/js/index.js index a784fcc..a11ef19 100644 --- a/oxcd/static/js/index.js +++ b/oxcd/static/js/index.js @@ -1,6 +1,11 @@ 'use strict'; Ox.load('UI', function() { + window.oxcd = Ox.App({ + name: 'oxcd', + url: '/api/' + }).bindEvent({ + load: function(data) { var app = { $ui: {}, data: {}, @@ -82,7 +87,7 @@ Ox.load('UI', function() { format: function(value) { return app.utils.formatTime(value); }, - id: 'totalTime', + id: 'duration', operator: '-', title: 'Time', visible: true, @@ -156,8 +161,8 @@ Ox.load('UI', function() { */ scrollbarVisible: true, sort: app.user.sort, - sums: ['size', 'totalTime'], - unique: 'trackID' + sums: ['size', 'duration'], + unique: 'id' }) .bindEvent({ init: function(data) { @@ -205,10 +210,10 @@ Ox.load('UI', function() { app.data.status = data; } else if (data.ids && data.ids.length > 1) { var items = app.user.music.tracks.filter(function(track) { - return Ox.contains(data.ids, track.trackID); + return Ox.contains(data.ids, track.id); }); data.selected = data.ids.length; - ['size', 'totalTime'].forEach(function(key) { + ['size', 'duration'].forEach(function(key) { data[key] = items.reduce(function(p, c) { return p + c[key]; }, 0); @@ -219,7 +224,7 @@ Ox.load('UI', function() { data ? (data.selected ? data.selected + ' of ' : '') + Ox.formatNumber(app.data.status.items) + ' item' + (app.data.status.items == 1 ? '' : 's') + ', ' - + app.utils.formatTime(data.totalTime || app.data.status.totalTime) + ' total time, ' + + app.utils.formatTime(data.duration || app.data.status.duration) + ' total time, ' + Ox.formatValue(data.size || app.data.status.size, 'B') : 'Loading...' ); @@ -237,6 +242,23 @@ Ox.load('UI', function() { return key[0].toLowerCase() + key.substr(1).replace(/ /g, ''); }; app.utils.parseLibrary = function(callback) { + var data = {playlists: [], tracks: []}; + oxcd.api.library(function(result) { + Ox.forEach(result.data.tracks, function(track) { + if (track.kind == 'MPEG audio file' && !track.podcast) { + app.site.columns.map(function(column) { + return column.id; + }).forEach(function(key) { + if (!track[key]) { + track[key] = ''; + } + }) + data.tracks.push(track); + } + }); + callback(data); + }); + /* Ox.get(app.user.library, function(xml) { var data = {playlists: [], tracks: []}, library = app.utils.parseXML($(xml).children()[0]); @@ -254,6 +276,7 @@ Ox.load('UI', function() { }); callback(data); }); + */ }; app.utils.parseXML = function(xml) { var type = xml.nodeName.toLowerCase(), value; @@ -293,5 +316,6 @@ Ox.load('UI', function() { } }; app.load(); - window.app = app; + } + }); });