PlayControl: remove volume slider switch
[nephilim.git] / clMonty.py
blobb2d35dfc478127b152591ee914f4b87d966bad97
1 from PyQt4 import QtCore
2 from traceback import *
3 from clSong import Song
4 from traceback import print_exc
5 from misc import *
6 import mpd
7 from threading import Thread
9 class Monty(QtCore.QObject):
10 """The Monty class offers another layer above pympd, with usefull events."""
11 _client=None # MPD client
12 _listeners=None # array of listeners: { event: (listeners)* }
14 # cached objects
15 _curLib=None
16 _curPlaylist=None
17 _curSong=None
19 # objects used for comparison with previous value
20 _curSongID=None
21 _curTime=None
22 _curState=None
23 _curVolume=None
24 _updatings_db=None
26 _timerID=None
29 events={
30 'beforeSongChange':'curSongID',
31 'onSongChange':'oldSongID, newSongID',
32 'onTimeChange':'oldTime, newTime',
33 'onStateChange':'oldState, newState',
34 'onVolumeChange':'oldVolume, newVolume',
35 'onConnect':'',
36 'onDisconnect':'',
37 'onReady':'', # when connected, and initialisation is ready
38 'onUpdateDBStart':'', # start updating database
39 'onUpdateDBFinish':'', # when updating database has finished
42 def __init__(self):
43 QtCore.QObject.__init__(self)
44 self._client=None
45 self._listeners={}
47 self._curSongID=-1
48 self._curTime=-1
49 self._curState=-1
50 self._curVolume=-1
51 self._curLib=[]
52 self._curPlaylist=[]
54 for event in self.events:
55 self._listeners[event]=[]
57 def connect(self, host, port):
58 """Connect to MPD@$host:$port. Returns true at success, false otherwise."""
59 if self._client:
60 return
61 try:
62 self._client = mpd.MPDClient()
63 self._client.connect(host, port)
64 except:
65 self._client=None
66 return False
68 self._raiseEvent('onConnect', None)
69 try:
70 self._updateLib()
71 self._updatePlaylist()
72 self._updateCurrentSong()
73 self._timerID=self.startTimer(500)
74 except Exception:
75 print_exc()
76 self._raiseEvent('onStateChange', {'oldState':'stop', 'newState':self.getStatus()['state']})
77 self._raiseEvent('onReady', None)
78 doEvents()
79 return True
81 def disconnect(self):
82 """Disconnect from MPD."""
83 if self._client:
84 self._client.close()
85 self._client.disconnect()
86 self._client=None
87 # don't kill timer, as it'll happen in timerEvent
89 def isConnected(self):
90 """Returns true if we're connected to MPD, false otherwise."""
91 return self._client!=None
93 def listPlaylist(self):
94 """Returns the current playlist."""
95 if self.isConnected()==False:
96 return None
97 return self._curPlaylist
99 def listLibrary(self):
100 """Returns the library."""
101 if self.isConnected()==False:
102 return None
103 return self._curLib
105 def getCurrentSong(self):
106 """Returns the current playing song."""
107 if self.isConnected()==False:
108 return None
109 return self._curSong
111 def updateDB(self, paths):
112 self._client.command_list_ok_begin()
113 for path in paths:
114 self._client.update(path)
115 self._client.command_list_end()
117 def getStatus(self):
118 """Returns the status."""
119 try:
120 if self.isConnected()==False:
121 return None
122 ret=self._retrieve(self._client.status)
123 if 'time' in ret:
124 len=int(ret['time'][ret['time'].find(':')+1:])
125 cur=int(ret['time'][:ret['time'].find(':')])
126 ret['length']=len
127 ret['time']=cur
128 return ret
129 except Exception, d:
130 print_exc()
131 return None
133 def repeat(self,val):
134 self._client.repeat(val)
135 def random(self,val):
136 self._client.random(val)
138 _retrMutex=QtCore.QMutex()
139 def _retrieve(self, method):
140 """Makes sure only one call is made at a time to MPD."""
141 self._retrMutex.lock()
142 try:
143 ret=method()
144 except:
145 self._retrMutex.unlock()
146 raise
148 self._retrMutex.unlock()
149 return ret
151 def isPlaying(self):
152 return self.getStatus()['state']=='play'
154 def play(self, id):
155 """Play song with ID $id."""
156 self._playCalled=True
157 if id!=None:
158 self._client.playid(id)
159 else:
160 self._client.playid()
162 def pause(self):
163 """Pause playing."""
164 self._client.pause(1)
165 def resume(self):
166 """Resume playing."""
167 self._client.pause(0)
168 def next(self):
169 """Move on to the next song in the playlist."""
170 self._playCalled=False
171 self._raiseEvent('beforeSongChange', {'curSongID': self._curSongID})
172 # we only switch to the next song, if some of beforeSongChange's listeners
173 # didn't explicitly call play. If it did, then it ain't good to immediatly
174 # skip to the next song!
175 if not self._playCalled:
176 self._client.next()
177 def previous(self):
178 """Move back to the previous song in the playlist."""
179 self._client.previous()
180 def stop(self):
181 """Stop playing."""
182 self._client.stop()
184 def seek(self, time):
185 """Move the current playing time to $time."""
186 self._client.seekid(self._curSongID, time)
188 def deleteFromPlaylist(self, list):
189 """Remove all songIDs in $list from the playlist."""
190 self._client.command_list_ok_begin()
191 for id in list:
192 self._client.deleteid(id)
193 self._client.command_list_end()
194 self._updatePlaylist()
195 def clear_playlist(self):
196 """Removes all songs from current playlist."""
197 self._client.clear()
198 self._updatePlaylist()
200 def addToPlaylist(self, paths):
201 """Add all files in $paths to the current playlist."""
202 self._client.command_list_ok_begin()
203 for path in paths:
204 self._client.addid(path)
205 ret = self._client.command_list_end()
206 self._updatePlaylist()
207 return ret
209 def setVolume(self, volume):
210 """Set volumne to $volume."""
211 volume=min(100, max(0, volume))
212 self._client.setvol(volume)
213 def getVolume(self):
214 return int(self.getStatus()['volume'])
216 def addListener(self, event, callback):
217 """Add $callback to the listeners for $event."""
218 if not(event in self.events):
219 raise Exception("Unknown event "+event)
220 self._listeners[event].append(callback)
221 def removeListener(self, event, callback):
222 if not(event in self.events):
223 raise Exception("Unknown event "+event)
224 self._listeners[event].remove(callback)
227 def _updateLib(self):
228 """Update the library."""
229 self._curLib=self._arrayToSongArray(self._retrieve(self._client.listallinfo))
230 id=0
231 for song in self._curLib:
232 song._data['id']=id
233 id+=1
234 def _updatePlaylist(self):
235 """Update the playlist."""
236 self._curPlaylist=self._arrayToSongArray(self._retrieve(self._client.playlistinfo))
237 def _arrayToSongArray(self, array):
238 """Convert an array to an array of Songs."""
239 return map(lambda entry: Song(entry)
240 , filter(lambda entry: not('directory' in entry), array)
242 def _updateCurrentSong(self):
243 """Update the current song."""
244 self._curSong=self._retrieve(self._client.currentsong)
245 if self._curSong==None:
246 return
247 self._curSong=Song(self._curSong)
249 class simpleThread(Thread):
250 callback=None
251 params=None
252 def __init__(self,callback,params):
253 Thread.__init__(self)
254 self.callback=callback
255 self.params=params
256 def run(self):
257 self.callback(self.params)
259 def _raiseEvent(self, event, params):
260 """Call all listeners for $event with parameters $params."""
261 if not(event in self.events):
262 raise Exception("Unknown raised event "+event)
264 for listener in self._listeners[event]:
265 try:
266 self.simpleThread(listener, params).run()
267 except:
268 print_exc()
270 def timerEvent(self, event):
271 "Check for changes since last check."
272 try:
273 self._updateCurrentSong()
274 status=self.getStatus()
275 except:
276 self._curSong=None
278 song=self._curSong
279 if song==None or status==None:
280 self._client=None
281 self._raiseEvent('onDisconnect', None)
282 self.killTimer(self._timerID)
283 return
285 " check if song has changed"
286 if song.getID()>=0:
287 curID=song.getID()
288 if curID!=self._curSongID:
289 self._raiseEvent('onSongChange', {'oldSongID':self._curSongID, 'newSongID':curID})
290 self._curSongID=curID
292 " check if the time has changed"
293 if 'time' in status:
294 curTime=status['time']
295 if curTime!=self._curTime:
296 self._raiseEvent('onTimeChange', {'oldTime':self._curTime, 'newTime':curTime})
297 self._curTime=curTime
298 if curTime>=status['length']-1:
299 self._raiseEvent('beforeSongChange', {'curSongID':curID})
301 " check if the playing state has changed"
302 if 'state' in status:
303 curState=status['state']
304 if curState!=self._curState:
305 self._raiseEvent('onStateChange', {'oldState':self._curState, 'newState':curState})
306 self._curState=curState
308 " check if the volume has changed"
309 if 'volume' in status:
310 curVolume=int(status['volume'])
311 if curVolume!=self._curVolume:
312 self._raiseEvent('onVolumeChange', {'oldVolume':self._curVolume, 'newVolume':curVolume})
313 self._curVolume=curVolume
316 " update has started"
317 if 'updatings_db' in status and self._updatings_db==None:
318 self._updatings_db=status['updatings_db']
319 self._raiseEvent('onUpdateDBStart', {})
320 if not('updatings_db' in status) and self._updatings_db:
321 self._updatings_db=None
322 self._raiseEvent('onUpdateDBFinish')