-
Notifications
You must be signed in to change notification settings - Fork 0
/
webservice.py
116 lines (91 loc) · 3.7 KB
/
webservice.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import cyclone.web, json, logging, os, socket, sys
from twisted.internet.defer import maybeDeferred, inlineCallbacks, returnValue
from pithospandora import deferredCallWithReconnects
def stationProperties(s):
return dict((k, getattr(s, k)) for k in
"isCreator name idToken useQuickMix isQuickMix id".split())
def songProperties(s):
return dict((k, getattr(s, k, None)) for k in
'album artist artistMusicId audioUrl fileGain identity '
'musicId rating stationId title userSeed songDetailURL '
'albumDetailURL artRadio'.split())
def mpdCurrentSongProperties(s):
out = s.copy()
for intKey in ['id', 'pos', 'time']: # probably incomplete
if intKey in out:
out[intKey] = int(out[intKey])
return out
class Resource(cyclone.web.RequestHandler):
def get(self, *args):
self.set_header("Content-Type", "application/json")
return (maybeDeferred(self.getJson, *args)
.addCallback(lambda j: self.write(json.dumps(j))))
def mpd(self):
return self.settings.mpdConnection.currentConnection
def pandora(self):
return self.settings.pandora
def pandoraCall(self, method, *args):
m = getattr(self.pandora(), method)
return deferredCallWithReconnects(self.pandora(), m, *args)
def makeUri(self, rel):
return self.settings.baseUri + rel
class Index(Resource):
def getJson(self):
out = {'connectedToMpd' : bool(self.mpd()),
'stations' : self.makeUri('stations/'),
'currentStation' : self.makeUri('currentStation'),
'host' : socket.gethostname(),
'mpd' : self.settings.mpd,
}
out.update(self.settings.feeder.moreStatus())
return out
class Stations(Resource):
def getJson(self):
ret = []
for s in self.pandora().stations:
ret.append(stationProperties(s))
ret[-1]['uri'] = self.makeUri('stations/%s/' % ret[-1]['id'])
def lookup(station):
try:
return self.settings.preferred.index(station['name'])
except ValueError:
return sys.maxint
ret.sort(key=lookup)
return {'stations' : ret}
class Station(Resource):
def getJson(self, sid):
def stn(s):
return {'station' : stationProperties(s),
'play' : self.makeUri('currentStation')}
return self.pandoraCall('get_station_by_id', sid).addCallback(stn)
class CurrentStation(Resource):
def getJson(self):
cs = self.settings.feeder.currentStation
if cs is None:
raise cyclone.web.HTTPError(404, "no current station")
return {'station' : stationProperties(cs),
'currentSong' : self.makeUri('currentSong')}
def put(self):
body = json.loads(self.request.body)
return (self.pandoraCall('get_station_by_id', body['id'])
.addCallback(self.settings.feeder.setStation))
def delete(self):
self.settings.feeder.setStation(None)
class CurrentSong(Resource):
@inlineCallbacks
def getJson(self):
mpdSong = (yield self.mpd().currentsong())
out = {'song' : {'mpd' : mpdCurrentSongProperties(mpdSong)}}
try:
ps = self.settings.feeder.pandoraSong(mpdSong['file'])
except KeyError:
pass
else:
out['song']['pandora'] = songProperties(ps)
returnValue(out)
mapping = [(r'/', Index),
(r'/stations/', Stations),
(r'/stations/(\d+)(?:-[^/]+)?/', Station),
(r'/currentStation', CurrentStation),
(r'/currentSong', CurrentSong),
]