- gzip api response
- dont parse xml on client - load xml in thread
This commit is contained in:
parent
10694d8d1e
commit
5b97b26692
4 changed files with 77 additions and 13 deletions
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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__
|
||||||
|
|
|
@ -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;
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue