summaryrefslogtreecommitdiff
path: root/plugins/PlayControl.py
blob: abe45473b6549cd93d7fd888c686f7d147bb5666 (plain)
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
from PyQt4 import QtGui, QtSvg, QtCore

from misc import *
from clMonty import monty
from clPlugin import *
from thread import start_new_thread
from random import randint

# Some predefined constants.
# Note that REPEAT precedes RANDOM. E.g. if repeat
# is ALBUM, and random is SONG, then it'll take a random song
# from the current album ...
PC_RANDOM_NO=0		# no randomness
PC_RANDOM_SONG=1	# choose a song random from the playlist
PC_RANDOM_ALBUM=2	# choose a random album, and play that fully

PC_REPEAT_NO=0		# no repeat
PC_REPEAT_SONG=1	# repeat current song
PC_REPEAT_ALBUM=2	# repeat current album
PC_REPEAT_PLAYLIST=3	# repeat playlist


class wgPlayControl(QtGui.QWidget):
	"""Displays controls for interacting with playing, like play, volume ..."""
	" control buttons"
	btnPlayPause=None
	btnStop=None
	btnPrevious=None
	btnNext=None
	" slider for current time"
	slrTime=None
	" slider for volume"
	slrVolume=None
	" indicator for volume"
	svgVolume=None
	" all objects in this widget"
	objects=None

	cmbRepeat=None
	cmbShuffle=None
	
	" contains the songs of the album the current song is playing. None, if the album is not set"
	curAlbumSongs=None

	def __init__(self, parent=None):
		QtGui.QWidget.__init__(self, parent)
		

		class wgSvgSwitcher(QtSvg.QSvgWidget):
			"""Widget showing an svg-image, which, when clicked, will (un)hide an element."""
			# the element we wish to hide/show
			scroller=None
			def __init__(self,scroller,parent=None):
				QtSvg.QSvgWidget.__init__(self,parent)
				self.scroller=scroller
			def mousePressEvent(self,event):
				self.scroller.setVisible(not self.scroller.isVisible())
			def wheelEvent(self, event):
				event.accept()
				numDegrees=event.delta() / 8
				numSteps=5*numDegrees/15
				self.scroller.setValue(self.scroller.value()+numSteps)

		self.slrTime=QtGui.QSlider(QtCore.Qt.Horizontal, self)
		self.slrTime.setMinimumWidth(100)
		self.slrVolume=QtGui.QSlider(QtCore.Qt.Vertical, self)
		self.slrVolume.setMaximum(100)
		self.slrVolume.setMinimumWidth(100)
		self.slrVolume.setMaximumWidth(350)
		# set to some value that'll never be chosen, that way onChange will be called automatically :)
		self.slrVolume.setValue(3.141595)
		self.slrVolume.setVisible(False)
		self.svgVolume=wgSvgSwitcher(self.slrVolume)
		self.svgVolume.setMaximumSize(64,64)
		self.svgVolume.setMinimumSize(64,64)
		self.btnPlayPause=Button("play", self.onBtnPlayPauseClick, 'gfx/media-playback-start.svg', True)
		self.btnStop=Button("stop", self.onBtnStopClick, 'gfx/media-playback-stop.svg', True)
		self.btnPrevious=Button("prev", self.onBtnPreviousClick, 'gfx/media-skip-backward.svg', True)
		self.btnNext=Button("next", self.onBtnNextClick, 'gfx/media-skip-forward.svg', True)

		self.cmbShuffle=QtGui.QComboBox(self)
		self.cmbShuffle.addItem('Don\'t play dices')
		self.cmbShuffle.addItem('Random song')
		self.cmbShuffle.addItem('Random album')
		self.cmbShuffle.setCurrentIndex(int(settings.get('playcontrol.shuffle', PC_RANDOM_SONG)))
		
		self.cmbRepeat=QtGui.QComboBox(self)
		self.cmbRepeat.addItem('No repeat')
		self.cmbRepeat.addItem('Repeat current song')
		self.cmbRepeat.addItem('Repeat album')
		self.cmbRepeat.addItem('Playlist')
		self.cmbRepeat.setCurrentIndex(int(settings.get('playcontrol.repeat', PC_REPEAT_PLAYLIST)))

		self.objects=[self.slrVolume, self.slrTime, 
			self.btnStop, self.btnNext, self.btnPrevious]

		layout=QtGui.QHBoxLayout(parent)
		layout2=QtGui.QHBoxLayout(parent)
		layoutWidget=QtGui.QVBoxLayout(parent)
		layoutWidget.addLayout(layout)
		layoutWidget.addLayout(layout2)
		self.setLayout(layoutWidget)
		
		layout.addWidget(self.btnPrevious)
		layout.addWidget(self.btnPlayPause)
		layout.addWidget(self.btnStop)
		layout.addWidget(self.btnNext)
		layout.addWidget(self.slrTime)
		layout.addWidget(self.slrVolume)
		layout.addWidget(self.svgVolume)

		layout2.addWidget(self.cmbRepeat)
		layout2.addWidget(self.cmbShuffle)
		
		self.connect(self.slrVolume, QtCore.SIGNAL('valueChanged(int)'),self.onVolumeSliderChange)
		self.connect(self.slrTime, QtCore.SIGNAL('sliderReleased()'),self.onTimeSliderChange)
		
		self.connect(self.cmbRepeat, QtCore.SIGNAL('currentIndexChanged(int)'),self.onCmbRepeatChanged)
		self.connect(self.cmbShuffle, QtCore.SIGNAL('currentIndexChanged(int)'),self.onCmbShuffleChanged)
		
		monty.addListener('onStateChange', self.onStateChange)
		monty.addListener('beforeSongChange', self.beforeSongChange)
		monty.addListener('onSongChange', self.onSongChange)
		monty.addListener('onVolumeChange', self.onVolumeChange)
		monty.addListener('onReady', self.onStateChange)
		monty.addListener('onDisconnect', self.onDisconnect)
		monty.addListener('onTimeChange', self.onTimeChange)
	
	def onStateChange(self, params):
		newState=monty.getStatus()['state']

		map(lambda o: o.setEnabled(newState!='stop'), self.objects)

		if newState=='play':
			self.btnPlayPause.changeIcon('gfx/media-playback-pause.svg')
			self.btnPlayPause.setText('pauze')
		elif newState=='pause' or newState=='stop':
			self.btnPlayPause.changeIcon('gfx/media-playback-start.svg')
			self.btnPlayPause.setText('play')
	def onVolumeChange(self, params):
		self.slrVolume.setValue(params['newVolume'])
	def onDisconnect(self, params):
		map(lambda o: o.setEnabled(False), self.objects)
	def onTimeChange(self, params):
		if not self.slrTime.isSliderDown():
			self.slrTime.setValue(params['newTime'])
	def onSongChange(self, params):
		try:
			self.slrTime.setMaximum(monty.getStatus()['length'])
			self.slrTime.setEnabled(True)
		except:
			pass
		# look in another thread for the songs in the current album!
		params=()
		start_new_thread(self.findAlbumSongs, params)

	def beforeSongChange(self, params):
		nextID=None
		song=monty.getCurrentSong()
		# decide here what next song to play!
		repeat=self.cmbRepeat.currentIndex()
		random=self.cmbShuffle.currentIndex()
		# is the current song the last of the album?
		eofAlbum=int(song.getTrack())==int(self.curAlbumSongs[-1].getTrack())
		if repeat==PC_REPEAT_NO:
			# no repeat, nothing to see here! Pass on!
			pass
		elif repeat==PC_REPEAT_SONG:
			# we must repeat the previous song!
			nextID=params['curSongID']
		elif repeat==PC_REPEAT_ALBUM:
			# check if we are at the last track, if it is, we must start a new!
			if eofAlbum:
				nextID=self.curAlbumSongs[0].getID()
		elif repeat==PC_REPEAT_PLAYLIST:
			# repeating the playlist is handled by monty itself;
			# it is set in onCmbRepeatChanged.
			pass
		
		if random==PC_RANDOM_NO:
			# just follow our leader Monty.
			pass
		elif random==PC_RANDOM_SONG:
			# pick a random song! This depends on what repeat-mode we're in.
			if repeat==PC_REPEAT_NO or repeat==PC_REPEAT_PLAYLIST:
				# we don't repeat anything, so we can just let monty pick the
				# next random one!
				pass
			elif repeat==PC_REPEAT_SONG:
				# song repeat; don't choose the next one!
				nextID=params['curSongID']
			elif repeat==PC_REPEAT_ALBUM and self.curAlbumSongs:
				# pick random song from current album
				nextID=self.curAlbumSongs[randint(0,len(self.curAlbumSongs)-1)].getID()
		elif random==PC_RANDOM_ALBUM:
			# pick a random album! This means, we pick the first song of a random
			# album.
			if eofAlbum and (repeat==PC_REPEAT_PLAYLIST or repeat==PC_REPEAT_NO):
				# all first songs of an album
				albums=filter(lambda s: s.getAlbum() and s.getTrack()==1, monty.listPlaylist())
				nextID=albums[randint(0,len(albums)-1)].getID()

		if nextID!=None:
			monty.play(nextID)
		
	def findAlbumSongs(self):
		"""This method looks for the songs in the album of current playing song."""
		song=monty.getCurrentSong()
		self.curAlbumSongs=None
		if not song or not song.getAlbum():
			return
		self.curAlbumSongs=filter(lambda s: s.getArtist()==song.getArtist() 
				and s.getAlbum()==song.getAlbum(), monty.listPlaylist())
		self.curAlbumSongs.sort(numeric_compare)

	def onBtnPlayPauseClick(self):
		status=monty.getStatus()
		if status['state']=='play':
			monty.pause()
		elif status['state']=='stop':
			monty.play(None)
			#if self.lstPlaylist.getSelItemID()==-1:
				 #if nothing selected, set it on first row
				#self.lstPlaylist.selectRow(0)
			#self.onPlaylistDoubleClick()
		else:
			monty.resume()
	def onBtnStopClick(self):
		monty.stop()
	def onBtnPreviousClick(self):
		monty.previous()
	def onBtnNextClick(self):
		monty.next()
	def onTimeSliderChange(self):
		monty.seek(self.slrTime.value())
	def onVolumeSliderChange(self):
		v=self.slrVolume.value()
		monty.setVolume(v)
		if v<=1:
			mode='mute'
		else:
			mode=('0', 'min', 'med', 'max')[int(3*v/100)]
		self.svgVolume.load('gfx/stock_volume-%s.svg'%(mode))
	
	def onCmbRepeatChanged(self, newval):
		settings.set('playcontrol.repeat', newval)
		if newval==PC_REPEAT_PLAYLIST:
			monty.repeat(1)
		else:
			monty.repeat(0)
	def onCmbShuffleChanged(self, newval):
		settings.set('playcontrol.shuffle', newval)
		if newval==PC_RANDOM_SONG:
			monty.random(1)
		else:
			monty.random(0)


class pluginPlayControl(Plugin):
	o=None
	def __init__(self, winMain):
		Plugin.__init__(self, winMain, 'PlayControl')
	def _load(self):
		self.o=wgPlayControl(None)
	def getInfo(self):
		return "Have total control over the playing!"
	
	def _getDockWidget(self):
		return self._createDock(self.o)