- gzip api response

- dont parse xml on client
- load xml in thread
This commit is contained in:
j 2012-09-07 13:49:41 +02:00
parent 10694d8d1e
commit 5b97b26692
4 changed files with 77 additions and 13 deletions

View File

@ -10,6 +10,7 @@ def init(backend, site, data):
actions.register(init, cache=False) actions.register(init, cache=False)
def library(backend, site, data): def library(backend, site, data):
response = backend.library response = {}
response['tracks'] = backend.tracks
return json_response(response) return json_response(response)
actions.register(library, cache=True) actions.register(library, cache=True)

View File

@ -3,13 +3,21 @@
from __future__ import with_statement, division from __future__ import with_statement, division
import os import os
import re
from urllib import unquote from urllib import unquote
from threading import Thread
from plistlib import readPlist from plistlib import readPlist
class iTunes(object): class iTunes(object):
tracks = []
def __init__(self, xml): 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']: for id in self.library['Tracks']:
self.library['Tracks'][id]['Location'] = unquote(self.library['Tracks'][id]['Location'].replace('file://localhost/', '/')) self.library['Tracks'][id]['Location'] = unquote(self.library['Tracks'][id]['Location'].replace('file://localhost/', '/'))
if self.library['Tracks'][id]['Location'].startswith('//'): 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/', '/')) self.library['Music Folder'] = unquote(self.library['Music Folder'].replace('file://localhost/', '/'))
if self.library['Music Folder'].startswith('//'): if self.library['Music Folder'].startswith('//'):
self.library['Music Folder'] = self.library['Music Folder'][1:] self.library['Music Folder'] = self.library['Music Folder'][1:]
self.xml = xml
self.root = self.library['Music Folder'] 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

View File

@ -14,10 +14,10 @@ from twisted.web.resource import Resource
from twisted.web.static import File from twisted.web.static import File
from twisted.web.util import Redirect from twisted.web.util import Redirect
from twisted.web.error import NoResource from twisted.web.error import NoResource
from twisted.web.server import NOT_DONE_YET
from version import __version__ from version import __version__
from gziprequest import GzipFile from gziprequest import GzipFile, GzipRequest
def trim(docstring): def trim(docstring):
if not docstring: if not docstring:
return '' return ''
@ -198,7 +198,13 @@ class Server(Resource):
else: else:
data = {} data = {}
action = request.args['action'][0] 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): def render_GET(self, request):
request.headers['Server'] = 'oxcd/%s' % __version__ request.headers['Server'] = 'oxcd/%s' % __version__

View File

@ -1,6 +1,11 @@
'use strict'; 'use strict';
Ox.load('UI', function() { Ox.load('UI', function() {
window.oxcd = Ox.App({
name: 'oxcd',
url: '/api/'
}).bindEvent({
load: function(data) {
var app = { var app = {
$ui: {}, $ui: {},
data: {}, data: {},
@ -82,7 +87,7 @@ Ox.load('UI', function() {
format: function(value) { format: function(value) {
return app.utils.formatTime(value); return app.utils.formatTime(value);
}, },
id: 'totalTime', id: 'duration',
operator: '-', operator: '-',
title: 'Time', title: 'Time',
visible: true, visible: true,
@ -156,8 +161,8 @@ Ox.load('UI', function() {
*/ */
scrollbarVisible: true, scrollbarVisible: true,
sort: app.user.sort, sort: app.user.sort,
sums: ['size', 'totalTime'], sums: ['size', 'duration'],
unique: 'trackID' unique: 'id'
}) })
.bindEvent({ .bindEvent({
init: function(data) { init: function(data) {
@ -205,10 +210,10 @@ Ox.load('UI', function() {
app.data.status = data; app.data.status = data;
} else if (data.ids && data.ids.length > 1) { } else if (data.ids && data.ids.length > 1) {
var items = app.user.music.tracks.filter(function(track) { 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; data.selected = data.ids.length;
['size', 'totalTime'].forEach(function(key) { ['size', 'duration'].forEach(function(key) {
data[key] = items.reduce(function(p, c) { data[key] = items.reduce(function(p, c) {
return p + c[key]; return p + c[key];
}, 0); }, 0);
@ -219,7 +224,7 @@ Ox.load('UI', function() {
data ? (data.selected ? data.selected + ' of ' : '') data ? (data.selected ? data.selected + ' of ' : '')
+ Ox.formatNumber(app.data.status.items) + ' item' + Ox.formatNumber(app.data.status.items) + ' item'
+ (app.data.status.items == 1 ? '' : 's') + ', ' + (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') + Ox.formatValue(data.size || app.data.status.size, 'B')
: 'Loading...' : 'Loading...'
); );
@ -237,6 +242,23 @@ Ox.load('UI', function() {
return key[0].toLowerCase() + key.substr(1).replace(/ /g, ''); return key[0].toLowerCase() + key.substr(1).replace(/ /g, '');
}; };
app.utils.parseLibrary = function(callback) { 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) { Ox.get(app.user.library, function(xml) {
var data = {playlists: [], tracks: []}, var data = {playlists: [], tracks: []},
library = app.utils.parseXML($(xml).children()[0]); library = app.utils.parseXML($(xml).children()[0]);
@ -254,6 +276,7 @@ Ox.load('UI', function() {
}); });
callback(data); callback(data);
}); });
*/
}; };
app.utils.parseXML = function(xml) { app.utils.parseXML = function(xml) {
var type = xml.nodeName.toLowerCase(), value; var type = xml.nodeName.toLowerCase(), value;
@ -293,5 +316,6 @@ Ox.load('UI', function() {
} }
}; };
app.load(); app.load();
window.app = app; }
});
}); });