comparison src/plugins/plugin_misc_radiocol.py @ 457:fbe7c9118ce4

plugin radiocol: working Radio Collective \o/
author Goffi <goffi@goffi.org>
date Sun, 29 Jan 2012 03:00:25 +0100
parents ba6e1feda03e
children cf005701624b
comparison
equal deleted inserted replaced
456:ba6e1feda03e 457:fbe7c9118ce4
56 "main": "Radiocol", 56 "main": "Radiocol",
57 "handler": "yes", 57 "handler": "yes",
58 "description": _("""Implementation of radio collective""") 58 "description": _("""Implementation of radio collective""")
59 } 59 }
60 60
61 QUEUE_LIMIT = 2
62
61 63
62 class Radiocol(): 64 class Radiocol():
63 65
64 def __init__(self, host): 66 def __init__(self, host):
65 info(_("Radio collective initialization")) 67 info(_("Radio collective initialization"))
69 host.bridge.addMethod("radiocolCreate", ".plugin", in_sign='ss', out_sign='', method=self.radiocolCreate) 71 host.bridge.addMethod("radiocolCreate", ".plugin", in_sign='ss', out_sign='', method=self.radiocolCreate)
70 host.bridge.addMethod("radiocolSongAdded", ".plugin", in_sign='sss', out_sign='', method=self.radiocolSongAdded) 72 host.bridge.addMethod("radiocolSongAdded", ".plugin", in_sign='sss', out_sign='', method=self.radiocolSongAdded)
71 host.bridge.addSignal("radiocolStarted", ".plugin", signature='sss') #room_jid, referee, profile 73 host.bridge.addSignal("radiocolStarted", ".plugin", signature='sss') #room_jid, referee, profile
72 host.bridge.addSignal("radiocolSongRejected", ".plugin", signature='sss') #room_jid, reason, profile 74 host.bridge.addSignal("radiocolSongRejected", ".plugin", signature='sss') #room_jid, reason, profile
73 host.bridge.addSignal("radiocolPreload", ".plugin", signature='ssssss') #room_jid, filename, title, artist, album, profile 75 host.bridge.addSignal("radiocolPreload", ".plugin", signature='ssssss') #room_jid, filename, title, artist, album, profile
76 host.bridge.addSignal("radiocolPlay", ".plugin", signature='sss') #room_jid, filename, profile
77 host.bridge.addSignal("radiocolNoUpload", ".plugin", signature='ss') #room_jid, profile
78 host.bridge.addSignal("radiocolUploadOk", ".plugin", signature='ss') #room_jid, profile
74 host.trigger.add("MUC user joined", self.userJoinedTrigger) 79 host.trigger.add("MUC user joined", self.userJoinedTrigger)
75 80
76 def createRadiocolElt(self, to_jid, type="normal"): 81 def createRadiocolElt(self, to_jid, type="normal"):
77 type = "normal" if to_jid.resource else "groupchat" 82 type = "normal" if to_jid.resource else "groupchat"
78 elt = domish.Element(('jabber:client','message')) 83 elt = domish.Element(('jabber:client','message'))
83 88
84 def __create_started_elt(self): 89 def __create_started_elt(self):
85 """Create a game_started domish element""" 90 """Create a game_started domish element"""
86 started_elt = domish.Element(('','started')) 91 started_elt = domish.Element(('','started'))
87 return started_elt 92 return started_elt
93
94 def __create_preload_elt(self, sender, filename, title, artist, album):
95 preload_elt = domish.Element(('','preload'))
96 preload_elt['sender'] = sender
97 preload_elt['filename'] = filename #XXX: the frontend should know the temporary directory where file is put
98 preload_elt['title'] = title
99 preload_elt['artist'] = artist
100 preload_elt['album'] = album
101 return preload_elt
102
88 103
89 def userJoinedTrigger(self, room, user, profile): 104 def userJoinedTrigger(self, room, user, profile):
90 """This trigger is used to check if we are waiting people in this room, 105 """This trigger is used to check if we are waiting people in this room,
91 and to create a game if everybody is here""" 106 and to create a game if everybody is here"""
92 room_jid = room.occupantJID.userhost() 107 room_jid = room.occupantJID.userhost()
157 error ('Internal error') 172 error ('Internal error')
158 return 173 return
159 referee = room_jid.userhost() + '/' + room_nick 174 referee = room_jid.userhost() + '/' + room_nick
160 status = {} 175 status = {}
161 occupants_data = {} 176 occupants_data = {}
162 self.radios[room_jid.userhost()] = {'referee':referee, 'occupants_data':occupants_data} 177 self.radios[room_jid.userhost()] = {'referee':referee, 'queue':[], 'upload':True, 'playing': False, 'occupants_data':occupants_data}
163 mess = self.createRadiocolElt(jid.JID(room_jid.userhost())) 178 mess = self.createRadiocolElt(jid.JID(room_jid.userhost()))
164 mess.firstChildElement().addChild(self.__create_started_elt()) 179 mess.firstChildElement().addChild(self.__create_started_elt())
165 self.host.profiles[profile].xmlstream.send(mess) 180 self.host.profiles[profile].xmlstream.send(mess)
166 181
167 def radiocolSongAdded(self, referee, song_path, profile): 182 def radiocolSongAdded(self, referee, song_path, profile):
179 return 194 return
180 try: 195 try:
181 song = OggVorbis(song_path) 196 song = OggVorbis(song_path)
182 except OggVorbisHeaderError: 197 except OggVorbisHeaderError:
183 #this file is not ogg vorbis, we reject it 198 #this file is not ogg vorbis, we reject it
184 import pdb 199 self.host.bridge.radiocolSongRejected(jid.JID(referee).userhost(), \
185 pdb.set_trace() 200 "Uploaded file is not Ogg Vorbis song, only Ogg Vorbis songs are acceptable", profile)
186 """mess = self.createRadiocolElt(jid.JID(referee)) 201 """mess = self.createRadiocolElt(jid.JID(referee))
187 reject_elt = mess.firstChildElement().addElement(('','song_rejected')) 202 reject_elt = mess.firstChildElement().addElement(('','song_rejected'))
188 reject_elt['sender'] = client.jid 203 reject_elt['sender'] = client.jid
189 reject_elt['reason'] = _("Uploaded file is not Ogg Vorbis song, only Ogg Vorbis songs are acceptable") 204 reject_elt['reason'] = "Uploaded file is not Ogg Vorbis song, only Ogg Vorbis songs are acceptable"
190 #FIXME: add an error code 205 #FIXME: add an error code
191 self.host.profiles[profile].xmlstream.send(mess)""" 206 self.host.profiles[profile].xmlstream.send(mess)"""
192 return 207 return
193 title = song.get("title", ["Unknown"])[0] 208 title = song.get("title", ["Unknown"])[0]
194 artist = song.get("artist", ["Unknown"])[0] 209 artist = song.get("artist", ["Unknown"])[0]
202 added_elt['album'] = album 217 added_elt['album'] = album
203 added_elt['length'] = str(length) 218 added_elt['length'] = str(length)
204 self.host.profiles[profile].xmlstream.send(mess) 219 self.host.profiles[profile].xmlstream.send(mess)
205 return 220 return
206 221
207 222 def playNext(self, room_jid, profile):
223 """"Play next sont in queue if exists, and put a timer
224 which trigger after the song has been played to play next one"""
225 #TODO: need to check that there are still peoples in the room
226 # and clean the datas/stop the playlist if it's not the case
227 radio_data = self.radios[room_jid.userhost()]
228 queue = radio_data['queue']
229 if not queue:
230 #nothing left to play, we need to wait for uploads
231 radio_data['playing'] = False
232 return
233
234 filename, length = queue.pop(0)
235 mess = self.createRadiocolElt(room_jid)
236 play_elt = mess.firstChildElement().addElement(('','play'))
237 play_elt['filename'] = filename
238 self.host.profiles[profile].xmlstream.send(mess)
239
240 if not radio_data['upload'] and len(queue) < QUEUE_LIMIT:
241 #upload is blocked and we now have resources to get more, we reactivate it
242 mess = self.createRadiocolElt(room_jid)
243 no_upload_elt = mess.firstChildElement().addElement(('','upload_ok'))
244 self.host.profiles[profile].xmlstream.send(mess)
245 radio_data['upload'] = True
246
247 print ("Playing next song in %s s" % length)
248 reactor.callLater(length, self.playNext, room_jid, profile)
249
208 250
209 def radiocol_game_cmd(self, mess_elt, profile): 251 def radiocol_game_cmd(self, mess_elt, profile):
252 #FIXME: we should check sender (is it referee ?) here before accepting commands
210 from_jid = jid.JID(mess_elt['from']) 253 from_jid = jid.JID(mess_elt['from'])
211 room_jid = jid.JID(from_jid.userhost()) 254 room_jid = jid.JID(from_jid.userhost())
212 radio_elt = mess_elt.firstChildElement() 255 radio_elt = mess_elt.firstChildElement()
213 radio_data = self.radios[room_jid.userhost()] 256 radio_data = self.radios[room_jid.userhost()]
214 occupants_data = radio_data['occupants_data'] 257 occupants_data = radio_data['occupants_data']
258 queue = radio_data['queue']
215 259
216 for elt in radio_elt.elements(): 260 for elt in radio_elt.elements():
217 261
218 if elt.name == 'started': #new game created 262 if elt.name == 'started': #new game created
219 self.host.bridge.radiocolStarted(room_jid.userhost(), from_jid.full(), profile) 263 self.host.bridge.radiocolStarted(room_jid.userhost(), from_jid.full(), profile)
220 elif elt.name == 'preload': #a song is in queue and must be preloaded 264 elif elt.name == 'preload': #a song is in queue and must be preloaded
221 self.host.bridge.radiocolPreload(room_jid.userhost(), elt['filename'], elt['title'], elt['artist'], elt['album'], profile) 265 self.host.bridge.radiocolPreload(room_jid.userhost(), elt['filename'], elt['title'], elt['artist'], elt['album'], profile)
266 elif elt.name == 'play':
267 self.host.bridge.radiocolPlay(room_jid.userhost(), elt['filename'], profile)
222 elif elt.name == 'song_rejected': #a song has been refused 268 elif elt.name == 'song_rejected': #a song has been refused
223 import pdb 269 self.host.bridge.radiocolSongRejected(jid.JID(referee).userhost(), elt['reason'], profile)
224 pdb.set_trace() 270 elif elt.name == 'no_upload':
225 elif elt.name == 'song_added': #a song has been refused 271 self.host.bridge.radiocolNoUpload(room_jid.userhost(), profile)
226 #FIXME: we are KISS for the PoC: every song is added. Need to manage some sort of rules to allow peoples to send songs 272 elif elt.name == 'upload_ok':
273 self.host.bridge.radiocolUploadOk(room_jid.userhost(), profile)
274 elif elt.name == 'song_added': #a song has been added
275 #FIXME: we are KISS for the proof of concept: every song is added, to a limit of 3 in queue.
276 # Need to manage some sort of rules to allow peoples to send songs
277
278 if len(queue) >= QUEUE_LIMIT:
279 #there are already too many songs in queue, we reject this one
280 mess = self.createRadiocolElt(room_jid)
281 reject_elt = mess.firstChildElement().addElement(('','song_rejected'))
282 reject_elt['sender'] = from_jid.resource
283 reject_elt['reason'] = "Too many songs in queue"
284 #FIXME: add an error code
285 self.host.profiles[profile].xmlstream.send(mess)
286 return
287
288 #The song is accepted and added in queue
289 queue.append((elt['filename'], float(elt['length'])))
290
291 if len(queue) >= QUEUE_LIMIT:
292 #We are at the limit, we refuse new upload until next play
293 mess = self.createRadiocolElt(room_jid)
294 no_upload_elt = mess.firstChildElement().addElement(('','no_upload'))
295 #FIXME: add an error code
296 self.host.profiles[profile].xmlstream.send(mess)
297 radio_data['upload'] = False
298
299
227 mess = self.createRadiocolElt(room_jid) 300 mess = self.createRadiocolElt(room_jid)
228 preload_elt = mess.firstChildElement().addElement(('','preload')) 301 preload_elt = self.__create_preload_elt(from_jid.resource,
229 preload_elt['sender'] = from_jid.resource 302 elt['filename'],
230 preload_elt['filename'] = elt['filename'] #XXX: the frontend should know the temporary directory where file is put 303 elt['title'],
231 preload_elt['title'] = elt['title'] 304 elt['artist'],
232 preload_elt['artist'] = elt['artist'] 305 elt['album'])
233 preload_elt['album'] = elt['album'] 306 mess.firstChildElement().addChild(preload_elt)
234 preload_elt['length'] = elt['length']
235 self.host.profiles[profile].xmlstream.send(mess) 307 self.host.profiles[profile].xmlstream.send(mess)
308 if not radio_data['playing'] and len(queue) == 2:
309 #we have not started playing yet, and we have 2 songs in queue
310 #we can now start the party :)
311 radio_data['playing'] = True
312 self.playNext(room_jid, profile)
236 else: 313 else:
237 error (_('Unmanaged game element: %s') % elt.name) 314 error (_('Unmanaged game element: %s') % elt.name)
238 315
239 def getHandler(self, profile): 316 def getHandler(self, profile):
240 return RadiocolHandler(self) 317 return RadiocolHandler(self)