1 from PyQt4
import QtCore
2 from clSong
import Song
3 from traceback
import print_exc
6 from threading
import Thread
8 class Monty(QtCore
.QObject
):
9 """The Monty class offers another layer above pympd, with usefull events."""
10 _client
=None # MPD client
11 _listeners
=None # array of listeners: { event: (listeners)* }
18 # objects used for comparison with previous value
29 'beforeSongChange':'curSongID',
30 'onSongChange':'oldSongID, newSongID',
31 'onTimeChange':'oldTime, newTime',
32 'onStateChange':'oldState, newState',
33 'onVolumeChange':'oldVolume, newVolume',
36 'onReady':'', # when connected, and initialisation is ready
37 'onUpdateDBStart':'', # start updating database
38 'onUpdateDBFinish':'', # when updating database has finished
42 QtCore
.QObject
.__init
__(self
)
53 for event
in self
.events
:
54 self
._listeners
[event
]=[]
56 def connect(self
, host
, port
):
57 """Connect to MPD@$host:$port. Returns true at success, false otherwise."""
61 self
._client
= mpd
.MPDClient()
62 self
._client
.connect(host
, port
)
67 self
._raiseEvent
('onConnect', None)
70 self
._updatePlaylist
()
71 self
._updateCurrentSong
()
72 self
._timerID
=self
.startTimer(500)
75 self
._raiseEvent
('onStateChange', {'oldState':'stop', 'newState':self
.getStatus()['state']})
76 self
._raiseEvent
('onReady', None)
81 """Disconnect from MPD."""
84 self
._client
.disconnect()
86 # don't kill timer, as it'll happen in timerEvent
88 def isConnected(self
):
89 """Returns true if we're connected to MPD, false otherwise."""
90 return self
._client
!=None
92 def listPlaylist(self
):
93 """Returns the current playlist."""
94 if self
.isConnected()==False:
96 return self
._curPlaylist
98 def listLibrary(self
):
99 """Returns the library."""
100 if self
.isConnected()==False:
104 def getCurrentSong(self
):
105 """Returns the current playing song."""
106 if self
.isConnected()==False:
110 def updateDB(self
, paths
):
111 self
._client
.command_list_ok_begin()
113 self
._client
.update(path
)
114 self
._client
.command_list_end()
117 """Returns the status."""
119 if self
.isConnected()==False:
121 ret
=self
._retrieve
(self
._client
.status
)
123 len=int(ret
['time'][ret
['time'].find(':')+1:])
124 cur
=int(ret
['time'][:ret
['time'].find(':')])
132 def repeat(self
,val
):
133 self
._client
.repeat(val
)
134 def random(self
,val
):
135 self
._client
.random(val
)
137 _retrMutex
=QtCore
.QMutex()
138 def _retrieve(self
, method
):
139 """Makes sure only one call is made at a time to MPD."""
140 self
._retrMutex
.lock()
144 self
._retrMutex
.unlock()
147 self
._retrMutex
.unlock()
151 return self
.getStatus()['state']=='play'
154 """Play song with ID $id."""
155 self
._playCalled
=True
157 self
._client
.playid(id)
159 self
._client
.playid()
163 self
._client
.pause(1)
165 """Resume playing."""
166 self
._client
.pause(0)
168 """Move on to the next song in the playlist."""
169 self
._playCalled
=False
170 self
._raiseEvent
('beforeSongChange', {'curSongID': self
._curSongID
})
171 # we only switch to the next song, if some of beforeSongChange's listeners
172 # didn't explicitly call play. If it did, then it ain't good to immediatly
173 # skip to the next song!
174 if not self
._playCalled
:
177 """Move back to the previous song in the playlist."""
178 self
._client
.previous()
183 def seek(self
, time
):
184 """Move the current playing time to $time."""
185 self
._client
.seekid(self
._curSongID
, time
)
187 def deleteFromPlaylist(self
, list):
188 """Remove all songIDs in $list from the playlist."""
189 self
._client
.command_list_ok_begin()
191 self
._client
.deleteid(id)
192 self
._client
.command_list_end()
193 self
._updatePlaylist
()
194 def clear_playlist(self
):
195 """Removes all songs from current playlist."""
197 self
._updatePlaylist
()
199 def addToPlaylist(self
, paths
):
200 """Add all files in $paths to the current playlist."""
201 self
._client
.command_list_ok_begin()
203 self
._client
.addid(path
)
204 ret
= self
._client
.command_list_end()
205 self
._updatePlaylist
()
208 def setVolume(self
, volume
):
209 """Set volumne to $volume."""
210 volume
=min(100, max(0, volume
))
211 self
._client
.setvol(volume
)
213 return int(self
.getStatus()['volume'])
215 def addListener(self
, event
, callback
):
216 """Add $callback to the listeners for $event."""
217 if not(event
in self
.events
):
218 raise Exception("Unknown event "+event
)
219 self
._listeners
[event
].append(callback
)
220 def removeListener(self
, event
, callback
):
221 if not(event
in self
.events
):
222 raise Exception("Unknown event "+event
)
223 self
._listeners
[event
].remove(callback
)
226 def _updateLib(self
):
227 """Update the library."""
228 self
._curLib
=self
._arrayToSongArray
(self
._retrieve
(self
._client
.listallinfo
))
230 for song
in self
._curLib
:
233 def _updatePlaylist(self
):
234 """Update the playlist."""
235 self
._curPlaylist
=self
._arrayToSongArray
(self
._retrieve
(self
._client
.playlistinfo
))
236 def _arrayToSongArray(self
, array
):
237 """Convert an array to an array of Songs."""
238 return map(lambda entry
: Song(entry
)
239 , filter(lambda entry
: not('directory' in entry
), array
)
241 def _updateCurrentSong(self
):
242 """Update the current song."""
243 song
= self
._retrieve
(self
._client
.currentsong
)
247 self
._curSong
= Song(song
)
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()
281 self
._raiseEvent
('onDisconnect', None)
282 self
.killTimer(self
._timerID
)
285 " check if song has changed"
292 if curID
!= self
._curSongID
:
293 self
._raiseEvent
('onSongChange', {'oldSongID':self
._curSongID
, 'newSongID':curID
})
294 self
._curSongID
= curID
296 " check if the time has changed"
298 curTime
=status
['time']
299 if curTime
!=self
._curTime
:
300 self
._raiseEvent
('onTimeChange', {'oldTime':self
._curTime
, 'newTime':curTime
})
301 self
._curTime
=curTime
302 if curTime
>=status
['length']-1:
303 self
._raiseEvent
('beforeSongChange', {'curSongID':curID
})
305 " check if the playing state has changed"
306 if 'state' in status
:
307 curState
=status
['state']
308 if curState
!=self
._curState
:
309 self
._raiseEvent
('onStateChange', {'oldState':self
._curState
, 'newState':curState
})
310 self
._curState
=curState
312 " check if the volume has changed"
313 if 'volume' in status
:
314 curVolume
=int(status
['volume'])
315 if curVolume
!=self
._curVolume
:
316 self
._raiseEvent
('onVolumeChange', {'oldVolume':self
._curVolume
, 'newVolume':curVolume
})
317 self
._curVolume
=curVolume
320 " update has started"
321 if 'updatings_db' in status
and self
._updatings
_db
==None:
322 self
._updatings
_db
=status
['updatings_db']
323 self
._raiseEvent
('onUpdateDBStart', {})
324 if not('updatings_db' in status
) and self
._updatings
_db
:
325 self
._updatings
_db
=None
326 self
._raiseEvent
('onUpdateDBFinish')