1 from PyQt4
import QtCore
2 from traceback
import *
3 from clSong
import Song
4 from traceback
import print_exc
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)* }
19 # objects used for comparison with previous value
30 'beforeSongChange':'curSongID',
31 'onSongChange':'oldSongID, newSongID',
32 'onTimeChange':'oldTime, newTime',
33 'onStateChange':'oldState, newState',
34 'onVolumeChange':'oldVolume, newVolume',
37 'onReady':'', # when connected, and initialisation is ready
38 'onUpdateDBStart':'', # start updating database
39 'onUpdateDBFinish':'', # when updating database has finished
43 QtCore
.QObject
.__init
__(self
)
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."""
62 self
._client
= mpd
.MPDClient()
63 self
._client
.connect(host
, port
)
68 self
._raiseEvent
('onConnect', None)
71 self
._updatePlaylist
()
72 self
._updateCurrentSong
()
73 self
._timerID
=self
.startTimer(500)
76 self
._raiseEvent
('onStateChange', {'oldState':'stop', 'newState':self
.getStatus()['state']})
77 self
._raiseEvent
('onReady', None)
82 """Disconnect from MPD."""
85 self
._client
.disconnect()
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:
97 return self
._curPlaylist
99 def listLibrary(self
):
100 """Returns the library."""
101 if self
.isConnected()==False:
105 def getCurrentSong(self
):
106 """Returns the current playing song."""
107 if self
.isConnected()==False:
111 def updateDB(self
, paths
):
112 self
._client
.command_list_ok_begin()
114 self
._client
.update(path
)
115 self
._client
.command_list_end()
118 """Returns the status."""
120 if self
.isConnected()==False:
122 ret
=self
._retrieve
(self
._client
.status
)
124 len=int(ret
['time'][ret
['time'].find(':')+1:])
125 cur
=int(ret
['time'][:ret
['time'].find(':')])
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()
145 self
._retrMutex
.unlock()
148 self
._retrMutex
.unlock()
152 return self
.getStatus()['state']=='play'
155 """Play song with ID $id."""
156 self
._playCalled
=True
158 self
._client
.playid(id)
160 self
._client
.playid()
164 self
._client
.pause(1)
166 """Resume playing."""
167 self
._client
.pause(0)
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
:
178 """Move back to the previous song in the playlist."""
179 self
._client
.previous()
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()
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."""
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()
204 self
._client
.addid(path
)
205 ret
= self
._client
.command_list_end()
206 self
._updatePlaylist
()
209 def setVolume(self
, volume
):
210 """Set volumne to $volume."""
211 volume
=min(100, max(0, volume
))
212 self
._client
.setvol(volume
)
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
))
231 for song
in self
._curLib
:
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:
247 self
._curSong
=Song(self
._curSong
)
249 class simpleThread(Thread
):
252 def __init__(self
,callback
,params
):
253 Thread
.__init
__(self
)
254 self
.callback
=callback
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
]:
266 self
.simpleThread(listener
, params
).run()
270 def timerEvent(self
, event
):
271 "Check for changes since last check."
273 self
._updateCurrentSong
()
274 status
=self
.getStatus()
279 if song
==None or status
==None:
281 self
._raiseEvent
('onDisconnect', None)
282 self
.killTimer(self
._timerID
)
285 " check if song has changed"
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"
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')