Mercurial > libervia-backend
comparison src/plugins/plugin_misc_radiocol.py @ 683:75e4f5e2cc65
plugins radiocol, card_game, quiz: code factorization
author | souliane <souliane@mailoo.org> |
---|---|
date | Wed, 23 Oct 2013 12:45:13 +0200 |
parents | 84a6e83157c2 |
children | f610864eb7a5 |
comparison
equal
deleted
inserted
replaced
682:2805fa3f4bdf | 683:75e4f5e2cc65 |
---|---|
27 from zope.interface import implements | 27 from zope.interface import implements |
28 | 28 |
29 import os.path | 29 import os.path |
30 from os import unlink | 30 from os import unlink |
31 from mutagen.oggvorbis import OggVorbis, OggVorbisHeaderError | 31 from mutagen.oggvorbis import OggVorbis, OggVorbisHeaderError |
32 | 32 from sat.tools.plugins.games import RoomGame |
33 try: | 33 try: |
34 from twisted.words.protocols.xmlstream import XMPPHandler | 34 from twisted.words.protocols.xmlstream import XMPPHandler |
35 except ImportError: | 35 except ImportError: |
36 from wokkel.subprotocols import XMPPHandler | 36 from wokkel.subprotocols import XMPPHandler |
37 | 37 |
52 } | 52 } |
53 | 53 |
54 QUEUE_LIMIT = 2 | 54 QUEUE_LIMIT = 2 |
55 | 55 |
56 | 56 |
57 class Radiocol(object): | 57 class Radiocol(RoomGame): |
58 | 58 |
59 def __init__(self, host): | 59 def __init__(self, host): |
60 info(_("Radio collective initialization")) | 60 info(_("Radio collective initialization")) |
61 RoomGame.__init__(self, host, PLUGIN_INFO, (NC_RADIOCOL, RADIOC_TAG), | |
62 options={'queue': [], 'upload': True, 'playing': False, 'to_delete': {}}) | |
61 self.host = host | 63 self.host = host |
62 self.radios = {} | 64 host.bridge.addMethod("radiocolLaunch", ".plugin", in_sign='ass', out_sign='', method=self.prepareRoom) |
63 host.bridge.addMethod("radiocolLaunch", ".plugin", in_sign='ass', out_sign='', method=self.radiocolLaunch) | 65 host.bridge.addMethod("radiocolCreate", ".plugin", in_sign='ss', out_sign='', method=self.createCollectiveGame) |
64 host.bridge.addMethod("radiocolCreate", ".plugin", in_sign='ss', out_sign='', method=self.radiocolCreate) | |
65 host.bridge.addMethod("radiocolSongAdded", ".plugin", in_sign='sss', out_sign='', method=self.radiocolSongAdded) | 66 host.bridge.addMethod("radiocolSongAdded", ".plugin", in_sign='sss', out_sign='', method=self.radiocolSongAdded) |
66 host.bridge.addSignal("radiocolStarted", ".plugin", signature='sss') # room_jid, referee, profile | 67 host.bridge.addSignal("radiocolStarted", ".plugin", signature='sss') # room_jid, referee, profile |
67 host.bridge.addSignal("radiocolSongRejected", ".plugin", signature='sss') # room_jid, reason, profile | 68 host.bridge.addSignal("radiocolSongRejected", ".plugin", signature='sss') # room_jid, reason, profile |
68 host.bridge.addSignal("radiocolPreload", ".plugin", signature='ssssss') # room_jid, filename, title, artist, album, profile | 69 host.bridge.addSignal("radiocolPreload", ".plugin", signature='ssssss') # room_jid, filename, title, artist, album, profile |
69 host.bridge.addSignal("radiocolPlay", ".plugin", signature='sss') # room_jid, filename, profile | 70 host.bridge.addSignal("radiocolPlay", ".plugin", signature='sss') # room_jid, filename, profile |
70 host.bridge.addSignal("radiocolNoUpload", ".plugin", signature='ss') # room_jid, profile | 71 host.bridge.addSignal("radiocolNoUpload", ".plugin", signature='ss') # room_jid, profile |
71 host.bridge.addSignal("radiocolUploadOk", ".plugin", signature='ss') # room_jid, profile | 72 host.bridge.addSignal("radiocolUploadOk", ".plugin", signature='ss') # room_jid, profile |
72 host.trigger.add("MUC user joined", self.userJoinedTrigger) | 73 host.trigger.add("MUC user joined", self.userJoinedTrigger) |
73 | 74 |
74 def createRadiocolElt(self, to_jid, type="normal"): | |
75 type = "normal" if to_jid.resource else "groupchat" | |
76 elt = domish.Element((None, 'message')) | |
77 elt["to"] = to_jid.full() | |
78 elt["type"] = type | |
79 elt.addElement((NC_RADIOCOL, RADIOC_TAG)) | |
80 return elt | |
81 | |
82 def __create_started_elt(self): | |
83 """Create a game_started domish element""" | |
84 started_elt = domish.Element((None, 'started')) | |
85 return started_elt | |
86 | |
87 def __create_preload_elt(self, sender, filename, title, artist, album): | 75 def __create_preload_elt(self, sender, filename, title, artist, album): |
88 preload_elt = domish.Element((None, 'preload')) | 76 preload_elt = domish.Element((None, 'preload')) |
89 preload_elt['sender'] = sender | 77 preload_elt['sender'] = sender |
90 preload_elt['filename'] = filename # XXX: the frontend should know the temporary directory where file is put | 78 preload_elt['filename'] = filename # XXX: the frontend should know the temporary directory where file is put |
91 preload_elt['title'] = title | 79 preload_elt['title'] = title |
92 preload_elt['artist'] = artist | 80 preload_elt['artist'] = artist |
93 preload_elt['album'] = album | 81 preload_elt['album'] = album |
94 return preload_elt | 82 return preload_elt |
95 | |
96 def userJoinedTrigger(self, room, user, profile): | |
97 """This trigger is used to check if we are waiting people in this room, | |
98 and to create a game if everybody is here""" | |
99 room_jid = room.occupantJID.userhost() | |
100 if room_jid in self.radios and self.radios[room_jid]["referee"] == room.occupantJID.full(): | |
101 #we are in a radiocol room, let's start the party ! | |
102 mess = self.createRadiocolElt(jid.JID(room_jid + '/' + user.nick)) | |
103 mess.firstChildElement().addChild(self.__create_started_elt()) | |
104 self.host.profiles[profile].xmlstream.send(mess) | |
105 return True | |
106 | |
107 def radiocolLaunch(self, occupants, profile_key='@DEFAULT@'): | |
108 """Launch a game: helper method to create a room, invite occupants, and create the radiocol | |
109 @param occupants: list for occupants jid""" | |
110 debug(_('Launching radiocol')) | |
111 profile = self.host.memory.getProfileName(profile_key) | |
112 if not profile: | |
113 error(_("Unknown profile")) | |
114 return | |
115 | |
116 def radiocolRoomJoined(room): | |
117 print "radiocolRoomJoined" | |
118 _room_jid = room.occupantJID.userhostJID() | |
119 self.radiocolCreate(_room_jid.userhost(), profile_key=profile) | |
120 for occupant in occupants: | |
121 self.host.plugins["XEP-0249"].invite(jid.JID(occupant), room.occupantJID.userhostJID(), {"game": "Radiocol"}, profile) | |
122 | |
123 def after_init(ignore): | |
124 room_name = "sat_radiocol_%s" % self.host.plugins["XEP-0045"].getUniqueName(profile_key) | |
125 print "\n\n===> room_name:", room_name | |
126 muc_service = None | |
127 for service in self.host.memory.getServerServiceEntities("conference", "text", profile): | |
128 if not ".irc." in service.userhost(): | |
129 #FIXME: | |
130 #This awfull ugly hack is here to avoid an issue with openfire: the irc gateway | |
131 #use "conference/text" identity (instead of "conference/irc"), there is certainly a better way | |
132 #to manage this, but this hack fill do it for test purpose | |
133 muc_service = service | |
134 break | |
135 if not muc_service: | |
136 error(_("Can't find a MUC service")) | |
137 return | |
138 | |
139 _jid, xmlstream = self.host.getJidNStream(profile) | |
140 d = self.host.plugins["XEP-0045"].join(jid.JID("%s@%s" % (room_name, muc_service.userhost())), _jid.user, {}, profile) | |
141 d.addCallback(radiocolRoomJoined) | |
142 | |
143 client = self.host.getClient(profile) | |
144 if not client: | |
145 error(_('No client for this profile key: %s') % profile_key) | |
146 return | |
147 client.client_initialized.addCallback(after_init) | |
148 | |
149 def radiocolCreate(self, room_jid_param, profile_key='@DEFAULT@'): | |
150 """Create a new game | |
151 @param room_jid_param: jid of the room | |
152 @param profile_key: %(doc_profile_key)s""" | |
153 debug(_("Creating Radiocol")) | |
154 room_jid = jid.JID(room_jid_param) | |
155 profile = self.host.memory.getProfileName(profile_key) | |
156 if not profile: | |
157 error(_("profile %s is unknown") % profile_key) | |
158 return | |
159 if room_jid in self.radios: | |
160 warning(_("Radiocol already started in room %s") % room_jid.userhost()) | |
161 else: | |
162 room_nick = self.host.plugins["XEP-0045"].getRoomNick(room_jid.userhost(), profile) | |
163 if not room_nick: | |
164 error('Internal error') | |
165 return | |
166 referee = room_jid.userhost() + '/' + room_nick | |
167 self.radios[room_jid.userhost()] = {'referee': referee, 'queue': [], 'upload': True, 'playing': False, 'occupants_data': {}, 'to_delete': {}} | |
168 mess = self.createRadiocolElt(jid.JID(room_jid.userhost())) | |
169 mess.firstChildElement().addChild(self.__create_started_elt()) | |
170 self.host.profiles[profile].xmlstream.send(mess) | |
171 | 83 |
172 def radiocolSongAdded(self, referee, song_path, profile): | 84 def radiocolSongAdded(self, referee, song_path, profile): |
173 """This method is called by libervia when a song has been uploaded | 85 """This method is called by libervia when a song has been uploaded |
174 @param room_jid_param: jid of the room | 86 @param room_jid_param: jid of the room |
175 @song_path: absolute path of the song added | 87 @song_path: absolute path of the song added |
187 except OggVorbisHeaderError: | 99 except OggVorbisHeaderError: |
188 #this file is not ogg vorbis, we reject it | 100 #this file is not ogg vorbis, we reject it |
189 unlink(song_path) # FIXME: same host trick (see note above) | 101 unlink(song_path) # FIXME: same host trick (see note above) |
190 self.host.bridge.radiocolSongRejected(jid.JID(referee).userhost(), | 102 self.host.bridge.radiocolSongRejected(jid.JID(referee).userhost(), |
191 "Uploaded file is not Ogg Vorbis song, only Ogg Vorbis songs are acceptable", profile) | 103 "Uploaded file is not Ogg Vorbis song, only Ogg Vorbis songs are acceptable", profile) |
192 """mess = self.createRadiocolElt(jid.JID(referee)) | 104 """mess = self.createGameElt(jid.JID(referee)) |
193 reject_elt = mess.firstChildElement().addElement(('','song_rejected')) | 105 reject_elt = mess.firstChildElement().addElement(('','song_rejected')) |
194 reject_elt['sender'] = client.jid | 106 reject_elt['sender'] = client.jid |
195 reject_elt['reason'] = "Uploaded file is not Ogg Vorbis song, only Ogg Vorbis songs are acceptable" | 107 reject_elt['reason'] = "Uploaded file is not Ogg Vorbis song, only Ogg Vorbis songs are acceptable" |
196 #FIXME: add an error code | 108 #FIXME: add an error code |
197 self.host.profiles[profile].xmlstream.send(mess)""" | 109 self.host.profiles[profile].xmlstream.send(mess)""" |
198 return | 110 return |
199 title = song.get("title", ["Unknown"])[0] | 111 title = song.get("title", ["Unknown"])[0] |
200 artist = song.get("artist", ["Unknown"])[0] | 112 artist = song.get("artist", ["Unknown"])[0] |
201 album = song.get("album", ["Unknown"])[0] | 113 album = song.get("album", ["Unknown"])[0] |
202 length = song.info.length | 114 length = song.info.length |
203 mess = self.createRadiocolElt(jid.JID(referee)) | 115 mess = self.createGameElt(jid.JID(referee)) |
204 added_elt = mess.firstChildElement().addElement(('', 'song_added')) | 116 added_elt = mess.firstChildElement().addElement(('', 'song_added')) |
205 added_elt['filename'] = filename = os.path.basename(song_path) | 117 added_elt['filename'] = filename = os.path.basename(song_path) |
206 added_elt['title'] = title | 118 added_elt['title'] = title |
207 added_elt['artist'] = artist | 119 added_elt['artist'] = artist |
208 added_elt['album'] = album | 120 added_elt['album'] = album |
209 added_elt['length'] = str(length) | 121 added_elt['length'] = str(length) |
210 self.host.profiles[profile].xmlstream.send(mess) | 122 self.host.profiles[profile].xmlstream.send(mess) |
211 | 123 |
212 radio_data = self.radios[jid.JID(referee).userhost()] # FIXME: referee comes from Libervia's client side, it's unsecure | 124 radio_data = self.games[jid.JID(referee).userhost()] # FIXME: referee comes from Libervia's client side, it's unsecure |
213 radio_data['to_delete'][filename] = song_path # FIXME: works only because of the same host trick, see the note under the docstring | 125 radio_data['to_delete'][filename] = song_path # FIXME: works only because of the same host trick, see the note under the docstring |
214 | 126 |
215 def playNext(self, room_jid, profile): | 127 def playNext(self, room_jid, profile): |
216 """"Play next sont in queue if exists, and put a timer | 128 """"Play next sont in queue if exists, and put a timer |
217 which trigger after the song has been played to play next one""" | 129 which trigger after the song has been played to play next one""" |
218 #TODO: need to check that there are still peoples in the room | 130 #TODO: need to check that there are still peoples in the room |
219 # and clean the datas/stop the playlist if it's not the case | 131 # and clean the datas/stop the playlist if it's not the case |
220 #TODO: songs need to be erased once played or found invalids | 132 #TODO: songs need to be erased once played or found invalids |
221 # ==> unlink done the Q&D way with the same host trick (see above) | 133 # ==> unlink done the Q&D way with the same host trick (see above) |
222 radio_data = self.radios[room_jid.userhost()] | 134 radio_data = self.games[room_jid.userhost()] |
223 queue = radio_data['queue'] | 135 queue = radio_data['queue'] |
224 if not queue: | 136 if not queue: |
225 #nothing left to play, we need to wait for uploads | 137 #nothing left to play, we need to wait for uploads |
226 radio_data['playing'] = False | 138 radio_data['playing'] = False |
227 return | 139 return |
228 | 140 |
229 filename, length = queue.pop(0) | 141 filename, length = queue.pop(0) |
230 mess = self.createRadiocolElt(room_jid) | 142 mess = self.createGameElt(room_jid) |
231 play_elt = mess.firstChildElement().addElement(('', 'play')) | 143 play_elt = mess.firstChildElement().addElement(('', 'play')) |
232 play_elt['filename'] = filename | 144 play_elt['filename'] = filename |
233 self.host.profiles[profile].xmlstream.send(mess) | 145 self.host.profiles[profile].xmlstream.send(mess) |
234 | 146 |
235 if not radio_data['upload'] and len(queue) < QUEUE_LIMIT: | 147 if not radio_data['upload'] and len(queue) < QUEUE_LIMIT: |
236 #upload is blocked and we now have resources to get more, we reactivate it | 148 #upload is blocked and we now have resources to get more, we reactivate it |
237 mess = self.createRadiocolElt(room_jid) | 149 mess = self.createGameElt(room_jid) |
238 no_upload_elt = mess.firstChildElement().addElement(('', 'upload_ok')) | 150 no_upload_elt = mess.firstChildElement().addElement(('', 'upload_ok')) |
239 self.host.profiles[profile].xmlstream.send(mess) | 151 self.host.profiles[profile].xmlstream.send(mess) |
240 radio_data['upload'] = True | 152 radio_data['upload'] = True |
241 | 153 |
242 reactor.callLater(length, self.playNext, room_jid, profile) | 154 reactor.callLater(length, self.playNext, room_jid, profile) |
252 def radiocol_game_cmd(self, mess_elt, profile): | 164 def radiocol_game_cmd(self, mess_elt, profile): |
253 #FIXME: we should check sender (is it referee ?) here before accepting commands | 165 #FIXME: we should check sender (is it referee ?) here before accepting commands |
254 from_jid = jid.JID(mess_elt['from']) | 166 from_jid = jid.JID(mess_elt['from']) |
255 room_jid = jid.JID(from_jid.userhost()) | 167 room_jid = jid.JID(from_jid.userhost()) |
256 radio_elt = mess_elt.firstChildElement() | 168 radio_elt = mess_elt.firstChildElement() |
257 radio_data = self.radios[room_jid.userhost()] | 169 radio_data = self.games[room_jid.userhost()] |
258 occupants_data = radio_data['occupants_data'] | |
259 queue = radio_data['queue'] | 170 queue = radio_data['queue'] |
260 | 171 |
261 for elt in radio_elt.elements(): | 172 for elt in radio_elt.elements(): |
262 | 173 |
263 if elt.name == 'started': # new game created | 174 if elt.name == 'started': # new game created |
276 #FIXME: we are KISS for the proof of concept: every song is added, to a limit of 3 in queue. | 187 #FIXME: we are KISS for the proof of concept: every song is added, to a limit of 3 in queue. |
277 # Need to manage some sort of rules to allow peoples to send songs | 188 # Need to manage some sort of rules to allow peoples to send songs |
278 | 189 |
279 if len(queue) >= QUEUE_LIMIT: | 190 if len(queue) >= QUEUE_LIMIT: |
280 #there are already too many songs in queue, we reject this one | 191 #there are already too many songs in queue, we reject this one |
281 mess = self.createRadiocolElt(room_jid) | 192 mess = self.createGameElt(room_jid) |
282 reject_elt = mess.firstChildElement().addElement(('', 'song_rejected')) | 193 reject_elt = mess.firstChildElement().addElement(('', 'song_rejected')) |
283 reject_elt['sender'] = from_jid.resource | 194 reject_elt['sender'] = from_jid.resource |
284 reject_elt['reason'] = "Too many songs in queue" | 195 reject_elt['reason'] = "Too many songs in queue" |
285 #FIXME: add an error code | 196 #FIXME: add an error code |
286 self.host.profiles[profile].xmlstream.send(mess) | 197 self.host.profiles[profile].xmlstream.send(mess) |
289 #The song is accepted and added in queue | 200 #The song is accepted and added in queue |
290 queue.append((elt['filename'], float(elt['length']))) | 201 queue.append((elt['filename'], float(elt['length']))) |
291 | 202 |
292 if len(queue) >= QUEUE_LIMIT: | 203 if len(queue) >= QUEUE_LIMIT: |
293 #We are at the limit, we refuse new upload until next play | 204 #We are at the limit, we refuse new upload until next play |
294 mess = self.createRadiocolElt(room_jid) | 205 mess = self.createGameElt(room_jid) |
295 no_upload_elt = mess.firstChildElement().addElement(('', 'no_upload')) | 206 no_upload_elt = mess.firstChildElement().addElement(('', 'no_upload')) |
296 #FIXME: add an error code | 207 #FIXME: add an error code |
297 self.host.profiles[profile].xmlstream.send(mess) | 208 self.host.profiles[profile].xmlstream.send(mess) |
298 radio_data['upload'] = False | 209 radio_data['upload'] = False |
299 | 210 |
300 mess = self.createRadiocolElt(room_jid) | 211 mess = self.createGameElt(room_jid) |
301 preload_elt = self.__create_preload_elt(from_jid.resource, | 212 preload_elt = self.__create_preload_elt(from_jid.resource, |
302 elt['filename'], | 213 elt['filename'], |
303 elt['title'], | 214 elt['title'], |
304 elt['artist'], | 215 elt['artist'], |
305 elt['album']) | 216 elt['album']) |