comparison src/browser/sat_browser/game_radiocol.py @ 672:b39a9eddfe56 frontends_multi_profiles

browser_side: fixes room games: - use quick_frontend.quick_games - rename the signals handlers to fit the convention (e.g.: tarotGameScoreHandler) - rename card_game to game_tarot, radiocol to game_radiocol, CardGame to TarotPanel
author souliane <souliane@mailoo.org>
date Wed, 11 Mar 2015 12:50:19 +0100
parents src/browser/sat_browser/radiocol.py@bbdc5357dc00
children 849ffb24d5bf
comparison
equal deleted inserted replaced
671:2201ff543a05 672:b39a9eddfe56
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 # Libervia: a Salut à Toi frontend
5 # Copyright (C) 2011, 2012, 2013, 2014 Jérôme Poisson <goffi@goffi.org>
6
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 import pyjd # this is dummy in pyjs
21 from sat.core.log import getLogger
22 log = getLogger(__name__)
23
24 from sat.core.i18n import _
25 from sat_frontends.tools.misc import DEFAULT_MUC
26 from constants import Const as C
27
28 from pyjamas.ui.VerticalPanel import VerticalPanel
29 from pyjamas.ui.HorizontalPanel import HorizontalPanel
30 from pyjamas.ui.FlexTable import FlexTable
31 from pyjamas.ui.FormPanel import FormPanel
32 from pyjamas.ui.Label import Label
33 from pyjamas.ui.Button import Button
34 from pyjamas.ui.ClickListener import ClickHandler
35 from pyjamas.ui.Hidden import Hidden
36 from pyjamas.ui.CaptionPanel import CaptionPanel
37 from pyjamas.media.Audio import Audio
38 from pyjamas import Window
39 from pyjamas.Timer import Timer
40
41 import html_tools
42 import file_tools
43
44
45 class MetadataPanel(FlexTable):
46
47 def __init__(self):
48 FlexTable.__init__(self)
49 title_lbl = Label("title:")
50 title_lbl.setStyleName('radiocol_metadata_lbl')
51 artist_lbl = Label("artist:")
52 artist_lbl.setStyleName('radiocol_metadata_lbl')
53 album_lbl = Label("album:")
54 album_lbl.setStyleName('radiocol_metadata_lbl')
55 self.title = Label("")
56 self.title.setStyleName('radiocol_metadata')
57 self.artist = Label("")
58 self.artist.setStyleName('radiocol_metadata')
59 self.album = Label("")
60 self.album.setStyleName('radiocol_metadata')
61 self.setWidget(0, 0, title_lbl)
62 self.setWidget(1, 0, artist_lbl)
63 self.setWidget(2, 0, album_lbl)
64 self.setWidget(0, 1, self.title)
65 self.setWidget(1, 1, self.artist)
66 self.setWidget(2, 1, self.album)
67 self.setStyleName("radiocol_metadata_pnl")
68
69 def setTitle(self, title):
70 self.title.setText(title)
71
72 def setArtist(self, artist):
73 self.artist.setText(artist)
74
75 def setAlbum(self, album):
76 self.album.setText(album)
77
78
79 class ControlPanel(FormPanel):
80 """Panel used to show controls to add a song, or vote for the current one"""
81
82 def __init__(self, parent):
83 FormPanel.__init__(self)
84 self.setEncoding(FormPanel.ENCODING_MULTIPART)
85 self.setMethod(FormPanel.METHOD_POST)
86 self.setAction("upload_radiocol")
87 self.timer_on = False
88 self._parent = parent
89 vPanel = VerticalPanel()
90
91 types = [('audio/ogg', '*.ogg', 'Ogg Vorbis Audio'),
92 ('video/ogg', '*.ogv', 'Ogg Vorbis Video'),
93 ('application/ogg', '*.ogx', 'Ogg Vorbis Multiplex'),
94 ('audio/mpeg', '*.mp3', 'MPEG-Layer 3'),
95 ('audio/mp3', '*.mp3', 'MPEG-Layer 3'),
96 ]
97 self.file_upload = file_tools.FilterFileUpload("song", 10, types)
98 vPanel.add(self.file_upload)
99
100 hPanel = HorizontalPanel()
101 self.upload_btn = Button("Upload song", getattr(self, "onBtnClick"))
102 hPanel.add(self.upload_btn)
103 self.status = Label()
104 self.updateStatus()
105 hPanel.add(self.status)
106 #We need to know the filename and the referee
107 self.filename_field = Hidden('filename', '')
108 hPanel.add(self.filename_field)
109 referee_field = Hidden('referee', self._parent.referee)
110 hPanel.add(self.filename_field)
111 hPanel.add(referee_field)
112 vPanel.add(hPanel)
113
114 self.add(vPanel)
115 self.addFormHandler(self)
116
117 def updateStatus(self):
118 if self.timer_on:
119 return
120 # TODO: the status should be different if a song is being played or not
121 queue = self._parent.getQueueSize()
122 queue_data = self._parent.queue_data
123 if queue < queue_data[0]:
124 left = queue_data[0] - queue
125 self.status.setText("[we need %d more song%s]" % (left, "s" if left > 1 else ""))
126 elif queue < queue_data[1]:
127 left = queue_data[1] - queue
128 self.status.setText("[%d available spot%s]" % (left, "s" if left > 1 else ""))
129 elif queue >= queue_data[1]:
130 self.status.setText("[The queue is currently full]")
131 self.status.setStyleName('radiocol_status')
132
133 def onBtnClick(self):
134 if self.file_upload.check():
135 self.status.setText('[Submitting, please wait...]')
136 self.filename_field.setValue(self.file_upload.getFilename())
137 if self.file_upload.getFilename().lower().endswith('.mp3'):
138 self._parent._parent.host.showWarning('STATUS', 'For a better support, it is recommended to submit Ogg Vorbis file instead of MP3. You can convert your files easily, ask for help if needed!', 5000)
139 self.submit()
140 self.file_upload.setFilename("")
141
142 def onSubmit(self, event):
143 pass
144
145 def blockUpload(self):
146 self.file_upload.setVisible(False)
147 self.upload_btn.setEnabled(False)
148
149 def unblockUpload(self):
150 self.file_upload.setVisible(True)
151 self.upload_btn.setEnabled(True)
152
153 def setTemporaryStatus(self, text, style):
154 self.status.setText(text)
155 self.status.setStyleName('radiocol_upload_status_%s' % style)
156 self.timer_on = True
157
158 def cb(timer):
159 self.timer_on = False
160 self.updateStatus()
161
162 Timer(5000, cb)
163
164 def onSubmitComplete(self, event):
165 result = event.getResults()
166 if result == C.UPLOAD_OK:
167 # the song can still be rejected (not readable, full queue...)
168 self.setTemporaryStatus('[Your song has been submitted to the radio]', "ok")
169 elif result == C.UPLOAD_KO:
170 self.setTemporaryStatus('[Something went wrong during your song upload]', "ko")
171 self._parent.radiocolSongRejectedHandler(_("The uploaded file has been rejected, only Ogg Vorbis and MP3 songs are accepted."))
172 # TODO: would be great to re-use the original Exception class and message
173 # but it is lost in the middle of the traceback and encapsulated within
174 # a DBusException instance --> extract the data from the traceback?
175 else:
176 Window.alert(_('Submit error: %s' % result))
177 self.status.setText('')
178
179
180 class Player(Audio):
181
182 def __init__(self, player_id, metadata_panel):
183 Audio.__init__(self)
184 self._id = player_id
185 self.metadata = metadata_panel
186 self.timestamp = ""
187 self.title = ""
188 self.artist = ""
189 self.album = ""
190 self.filename = None
191 self.played = False # True when the song is playing/has played, becomes False on preload
192 self.setAutobuffer(True)
193 self.setAutoplay(False)
194 self.setVisible(False)
195
196 def preload(self, timestamp, filename, title, artist, album):
197 """preload the song but doesn't play it"""
198 self.timestamp = timestamp
199 self.filename = filename
200 self.title = title
201 self.artist = artist
202 self.album = album
203 self.played = False
204 self.setSrc("radiocol/%s" % html_tools.html_sanitize(filename))
205 log.debug("preloading %s in %s" % (title, self._id))
206
207 def play(self, play=True):
208 """Play or pause the song
209 @param play: set to True to play or to False to pause
210 """
211 if play:
212 self.played = True
213 self.metadata.setTitle(self.title)
214 self.metadata.setArtist(self.artist)
215 self.metadata.setAlbum(self.album)
216 Audio.play(self)
217 else:
218 self.pause()
219
220
221 class RadioColPanel(HorizontalPanel, ClickHandler):
222
223 def __init__(self, parent, referee, players, queue_data):
224 """
225 @param parent
226 @param referee
227 @param players
228 @param queue_data: list of integers (queue to start, queue limit)
229 """
230 # We need to set it here and not in the CSS :(
231 HorizontalPanel.__init__(self, Height="90px")
232 ClickHandler.__init__(self)
233 self._parent = parent
234 self.referee = referee
235 self.queue_data = queue_data
236 self.setStyleName("radiocolPanel")
237
238 # Now we set up the layout
239 self.metadata_panel = MetadataPanel()
240 self.add(CaptionPanel("Now playing", self.metadata_panel))
241 self.playlist_panel = VerticalPanel()
242 self.add(CaptionPanel("Songs queue", self.playlist_panel))
243 self.control_panel = ControlPanel(self)
244 self.add(CaptionPanel("Controls", self.control_panel))
245
246 self.next_songs = []
247 self.players = [Player("player_%d" % i, self.metadata_panel) for i in xrange(queue_data[1] + 1)]
248 self.current_player = None
249 for player in self.players:
250 self.add(player)
251 self.addClickListener(self)
252
253 help_msg = """Accepted file formats: Ogg Vorbis (recommended), MP3.<br />
254 Please do not submit files that are protected by copyright.<br />
255 Click <a style="color: red;">here</a> if you need some support :)"""
256 link_cb = lambda: self._parent.host.bridge.call('joinMUC', None, DEFAULT_MUC, self._parent.nick)
257 self._parent.printInfo(help_msg, type_='link', link_cb=link_cb)
258
259 def pushNextSong(self, title):
260 """Add a song to the left panel's next songs queue"""
261 next_song = Label(title)
262 next_song.setStyleName("radiocol_next_song")
263 self.next_songs.append(next_song)
264 self.playlist_panel.append(next_song)
265 self.control_panel.updateStatus()
266
267 def popNextSong(self):
268 """Remove the first song of next songs list
269 should be called when the song is played"""
270 #FIXME: should check that the song we remove is the one we play
271 next_song = self.next_songs.pop(0)
272 self.playlist_panel.remove(next_song)
273 self.control_panel.updateStatus()
274
275 def getQueueSize(self):
276 return len(self.playlist_panel.getChildren())
277
278 def radiocolCheckPreload(self, timestamp):
279 for player in self.players:
280 if player.timestamp == timestamp:
281 return False
282 return True
283
284 def radiocolPreloadHandler(self, timestamp, filename, title, artist, album, sender):
285 if not self.radiocolCheckPreload(timestamp):
286 return # song already preloaded
287 preloaded = False
288 for player in self.players:
289 if not player.filename or \
290 (player.played and player != self.current_player):
291 #if player has no file loaded, or it has already played its song
292 #we use it to preload the next one
293 player.preload(timestamp, filename, title, artist, album)
294 preloaded = True
295 break
296 if not preloaded:
297 log.warning("Can't preload song, we are getting too many songs to preload, we shouldn't have more than %d at once" % self.queue_data[1])
298 else:
299 self.pushNextSong(title)
300 self._parent.printInfo(_('%(user)s uploaded %(artist)s - %(title)s') % {'user': sender, 'artist': artist, 'title': title})
301
302 def radiocolPlayHandler(self, filename):
303 found = False
304 for player in self.players:
305 if not found and player.filename == filename:
306 player.play()
307 self.popNextSong()
308 self.current_player = player
309 found = True
310 else:
311 player.play(False) # in case the previous player was not sync
312 if not found:
313 log.error("Song not found in queue, can't play it. This should not happen")
314
315 def radiocolNoUploadHandler(self):
316 self.control_panel.blockUpload()
317
318 def radiocolUploadOkHandler(self):
319 self.control_panel.unblockUpload()
320
321 def radiocolSongRejectedHandler(self, reason):
322 Window.alert("Song rejected: %s" % reason)