add rhythmbox backend

This commit is contained in:
j 2012-09-08 19:37:51 +02:00
parent 1f2dd79517
commit 304675110a
6 changed files with 129 additions and 45 deletions

View file

@ -11,14 +11,24 @@ if os.path.exists(os.path.join(root, 'oxcd')):
sys.path.insert(0, root) sys.path.insert(0, root)
import oxcd import oxcd
import oxcd.itunes
import oxcd.rhythmbox
if __name__ == '__main__': if __name__ == '__main__':
parser = OptionParser() parser = OptionParser()
parser.add_option('-p', '--port', dest='port', help='port', default=2681) parser.add_option('-p', '--port', dest='port', help='port', default=2681)
parser.add_option('-i', '--itunes', dest='itunes', help='iTunes xml', default=oxcd.itunes_path()) parser.add_option('-i', '--itunes', dest='itunes', help='iTunes xml', default=oxcd.itunes.path())
parser.add_option('-r', '--rhythmbox', dest='rhythmbox', help='Rhythmbox xml', default=oxcd.rhythmbox.path())
(opts, args) = parser.parse_args() (opts, args) = parser.parse_args()
if None in (opts.port, opts.itunes): print (opts.port, )
print not filter(None, (opts.itunes, opts.rhythmbox))
if None in (opts.port, ) or not filter(None, (opts.itunes, opts.rhythmbox)):
parser.print_help() parser.print_help()
sys.exit() sys.exit()
oxcd.main(opts.port, opts.itunes) if opts.itunes:
backend = oxcd.itunes.iTunes(opts.itunes)
elif opts.rhythmbox:
backend = oxcd.rhythmbox.Rhythmbox(opts.rhythmbox)
oxcd.main(opts.port, backend)

View file

@ -13,19 +13,9 @@ import api
from version import __version__ from version import __version__
def itunes_path(): def main(port, backend):
if sys.platform == 'darwin':
path = os.path.expanduser('~/Music/iTunes/iTunes Music Library.xml')
elif sys.platform == 'win32':
path = os.path.expanduser('~\\Music\\iTunes\\iTunes Music Library.xml')
else:
path = None
return path
def main(port, itunes):
base = os.path.abspath(os.path.dirname(__file__)) base = os.path.abspath(os.path.dirname(__file__))
print 'loading', itunes print 'loading', backend
backend = iTunes(itunes)
root = Server(base, backend) root = Server(base, backend)
site = Site(root) site = Site(root)
reactor.listenTCP(port, site) reactor.listenTCP(port, site)

View file

@ -6,6 +6,7 @@ import os
import re import re
from urllib import unquote from urllib import unquote
from threading import Thread from threading import Thread
import sys
from plistlib import readPlist from plistlib import readPlist
@ -16,6 +17,9 @@ class iTunes(object):
t = Thread(target=self.parse_xml, args=[]) t = Thread(target=self.parse_xml, args=[])
t.start() t.start()
def __repr__(self):
return self.xml
def parse_xml(self): def parse_xml(self):
self.library = readPlist(self.xml) self.library = readPlist(self.xml)
for id in self.library['Tracks']: for id in self.library['Tracks']:
@ -37,20 +41,36 @@ class iTunes(object):
] ]
for t in self.library['Tracks']: for t in self.library['Tracks']:
track = self.library['Tracks'][t] track = self.library['Tracks'][t]
item = {} if track.get('kind') in ('MPEG audio file') and not track.get('podcast'):
for key in keys: item = {}
item[key] = track.get({ for key in keys:
'id': 'Track ID', item[key] = track.get({
'duration': 'Total Time', 'id': 'Track ID',
}.get( 'duration': 'Total Time',
key, }.get(
re.sub( key,
'^(.)', re.sub(
lambda m: m.groups(0)[0].capitalize(), '^(.)',
re.sub('([A-Z])', ' \\1', key) lambda m: m.groups(0)[0].capitalize(),
) re.sub('([A-Z])', ' \\1', key)
), None) )
if item[key] == None: ), None)
del item[key] if key == 'duration' and item[key]:
tracks.append(item) item[key] = item[key] / 1000
if item[key] == None:
del item[key]
tracks.append(item)
return tracks return tracks
def track(self, track_id):
track = self.library['Tracks'].get(track_id)
return track and track['Location'] or None
def path():
if sys.platform == 'darwin':
path = os.path.expanduser('~/Music/iTunes/iTunes Music Library.xml')
elif sys.platform == 'win32':
path = os.path.expanduser('~\\Music\\iTunes\\iTunes Music Library.xml')
else:
path = None
return path

67
oxcd/rhythmbox.py Normal file
View file

@ -0,0 +1,67 @@
# encoding: utf-8
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import with_statement, division
import os
import re
from urllib import unquote
from threading import Thread
import sys
import ox
class Rhythmbox(object):
tracks = []
locations = {}
def __init__(self, xml):
self.xml = os.path.expanduser(xml)
t = Thread(target=self.parse_xml, args=[])
t.start()
def __repr__(self):
return self.xml
def parse_xml(self):
from lxml import etree
with open(self.xml) as f:
self.library = etree.fromstring(f.read())
self.tracks = self.load_tracks()
def load_tracks(self):
tracks = []
keys = [
'id', 'name', 'artist', 'album', 'kind', 'year', 'duration', 'size',
'sortArtist', 'albumArtist', 'sortAlbumArtist', 'compliation',
]
key_map = {
'title': 'name',
'file-size': 'size',
}
for e in self.library.xpath('//entry[@type="song"]'):
item = {}
path = e.find('location').text.split('file://')[-1]
path = unquote(path)
item['id'] = ox.oshash(path)
self.locations[item['id']] = path
for c in e:
if c.tag in keys + key_map.keys():
key = key_map.get(c.tag, c.tag)
if key == 'duration':
item[key] = int(c.text)
else:
item[key] = c.text
info = ox.avinfo(path)
metadata = info.get('metadata', {})
if 'date' in metadata:
item['year'] = metadata['date']
tracks.append(item)
return tracks
def track(self, track_id):
return self.locations.get(track_id)
def path():
path = os.path.expanduser('~/.local/share/rhythmbox/rhythmdb.xml')
if not os.path.exists(path):
path = None
return path

View file

@ -168,9 +168,8 @@ class Server(Resource):
if request.path.startswith('/track/'): if request.path.startswith('/track/'):
track_id = request.path.split('/')[-1].split('.')[0] track_id = request.path.split('/')[-1].split('.')[0]
track = self.backend.library['Tracks'].get(track_id) path = self.backend.track(track_id)
if track: if path:
path = track['Location']
if os.path.exists(path): if os.path.exists(path):
request.headers['Access-Control-Allow-Origin'] = '*' request.headers['Access-Control-Allow-Origin'] = '*'
f = File(path, 'audio/mpeg') f = File(path, 'audio/mpeg')

View file

@ -233,7 +233,7 @@ Ox.load('UI', function() {
return $element; return $element;
}; };
app.utils.formatTime = function(duration) { app.utils.formatTime = function(duration) {
return Ox.formatDuration(duration / 1000).replace(/^00:/, '').replace(/^0/, ''); return Ox.formatDuration(duration).replace(/^00:/, '').replace(/^0/, '');
}; };
app.utils.formatTitle = function(title) { app.utils.formatTitle = function(title) {
return title.replace(/\[(.+)\]$/, '<span class="OxLight">($1)</span>'); return title.replace(/\[(.+)\]$/, '<span class="OxLight">($1)</span>');
@ -245,16 +245,14 @@ Ox.load('UI', function() {
var data = {playlists: [], tracks: []}; var data = {playlists: [], tracks: []};
oxcd.api.library(function(result) { oxcd.api.library(function(result) {
Ox.forEach(result.data.tracks, function(track) { Ox.forEach(result.data.tracks, function(track) {
if (track.kind == 'MPEG audio file' && !track.podcast) { app.site.columns.map(function(column) {
app.site.columns.map(function(column) { return column.id;
return column.id; }).forEach(function(key) {
}).forEach(function(key) { if (!track[key]) {
if (!track[key]) { track[key] = '';
track[key] = ''; }
} })
}) data.tracks.push(track);
data.tracks.push(track);
}
}); });
callback(data); callback(data);
}); });