]> git.0d.be Git - botaradio.git/blob - command.py
fix: random will not delete current song. changed one-shot icon.#79
[botaradio.git] / command.py
1 # coding=utf-8
2 import logging
3 import os.path
4 import pymumble.pymumble_py3 as pymumble
5 import re
6
7 import constants
8 import media.file
9 import media.playlist
10 import media.radio
11 import media.system
12 import media.url
13 import util
14 import variables as var
15 from librb import radiobrowser
16 from database import Database
17
18
19 def register_all_commands(bot):
20     bot.register_command(constants.commands('joinme'), cmd_joinme)
21     bot.register_command(constants.commands('user_ban'), cmd_user_ban)
22     bot.register_command(constants.commands('user_unban'), cmd_user_unban)
23     bot.register_command(constants.commands('url_ban'), cmd_url_ban)
24     bot.register_command(constants.commands('url_unban'), cmd_url_unban)
25     bot.register_command(constants.commands('play'), cmd_play)
26     bot.register_command(constants.commands('pause'), cmd_pause)
27     bot.register_command(constants.commands('play_file'), cmd_play_file)
28     bot.register_command(constants.commands('play_file_match'), cmd_play_file_match)
29     bot.register_command(constants.commands('play_url'), cmd_play_url)
30     bot.register_command(constants.commands('play_playlist'), cmd_play_playlist)
31     bot.register_command(constants.commands('play_radio'), cmd_play_radio)
32     bot.register_command(constants.commands('rb_query'), cmd_rb_query)
33     bot.register_command(constants.commands('rb_play'), cmd_rb_play)
34     bot.register_command(constants.commands('help'), cmd_help)
35     bot.register_command(constants.commands('stop'), cmd_stop)
36     bot.register_command(constants.commands('clear'), cmd_clear)
37     bot.register_command(constants.commands('kill'), cmd_kill)
38     bot.register_command(constants.commands('update'), cmd_update)
39     bot.register_command(constants.commands('stop_and_getout'), cmd_stop_and_getout)
40     bot.register_command(constants.commands('volume'), cmd_volume)
41     bot.register_command(constants.commands('ducking'), cmd_ducking)
42     bot.register_command(constants.commands('ducking_threshold'), cmd_ducking_threshold)
43     bot.register_command(constants.commands('ducking_volume'), cmd_ducking_volume)
44     bot.register_command(constants.commands('current_music'), cmd_current_music)
45     bot.register_command(constants.commands('skip'), cmd_skip)
46     bot.register_command(constants.commands('remove'), cmd_remove)
47     bot.register_command(constants.commands('list_file'), cmd_list_file)
48     bot.register_command(constants.commands('queue'), cmd_queue)
49     bot.register_command(constants.commands('random'), cmd_random)
50     bot.register_command(constants.commands('mode'), cmd_mode)
51     bot.register_command(constants.commands('drop_database'), cmd_drop_database)
52
53 def send_multi_lines(bot, lines, text):
54     msg = ""
55     br = ""
56     for newline in lines:
57         msg += br
58         br = "<br>"
59         if len(msg) + len(newline) > 5000:
60             bot.send_msg(msg, text)
61             msg = ""
62         msg += newline
63
64     bot.send_msg(msg, text)
65
66 # ---------------- Commands ------------------
67
68
69 def cmd_joinme(bot, user, text, command, parameter):
70     bot.mumble.users.myself.move_in(
71         bot.mumble.users[text.actor]['channel_id'], token=parameter)
72
73
74 def cmd_user_ban(bot, user, text, command, parameter):
75     if bot.is_admin(user):
76         if parameter:
77             bot.mumble.users[text.actor].send_text_message(util.user_ban(parameter))
78         else:
79             bot.mumble.users[text.actor].send_text_message(util.get_user_ban())
80     else:
81         bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
82     return
83
84
85 def cmd_user_unban(bot, user, text, command, parameter):
86     if bot.is_admin(user):
87         if parameter:
88             bot.mumble.users[text.actor].send_text_message(util.user_unban(parameter))
89     else:
90         bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
91     return
92
93
94 def cmd_url_ban(bot, user, text, command, parameter):
95     if bot.is_admin(user):
96         if parameter:
97             bot.mumble.users[text.actor].send_text_message(util.url_ban(util.get_url_from_input(parameter)))
98         else:
99             bot.mumble.users[text.actor].send_text_message(util.get_url_ban())
100     else:
101         bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
102     return
103
104
105 def cmd_url_unban(bot, user, text, command, parameter):
106     if bot.is_admin(user):
107         if parameter:
108             bot.mumble.users[text.actor].send_text_message(util.url_unban(util.get_url_from_input(parameter)))
109     else:
110         bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
111     return
112
113
114 def cmd_play(bot, user, text, command, parameter):
115     if var.playlist.length() > 0:
116         if parameter is not None:
117             if parameter.isdigit() and int(parameter) > 0 and int(parameter) <= len(var.playlist):
118                 bot.interrupt_playing()
119                 bot.launch_music(int(parameter) - 1)
120             else:
121                 bot.send_msg(constants.strings('invalid_index', index=parameter), text)
122
123         elif bot.is_pause:
124             bot.resume()
125         else:
126             bot.send_msg(util.format_current_playing(), text)
127     else:
128         bot.is_pause = False
129         bot.send_msg(constants.strings('queue_empty'), text)
130
131
132 def cmd_pause(bot, user, text, command, parameter):
133     bot.pause()
134     bot.send_msg(constants.strings('paused'))
135
136
137 def cmd_play_file(bot, user, text, command, parameter):
138     music_folder = var.config.get('bot', 'music_folder')
139     # if parameter is {index}
140     if parameter.isdigit():
141         files = util.get_recursive_filelist_sorted(music_folder)
142         if int(parameter) < len(files):
143             filename = files[int(parameter)].replace(music_folder, '')
144             music = {'type': 'file',
145                      'path': filename,
146                      'user': user}
147             logging.info("cmd: add to playlist: " + filename)
148             music = var.playlist.append(music)
149             bot.send_msg(constants.strings('file_added', item=util.format_song_string(music)), text)
150
151     # if parameter is {path}
152     else:
153         # sanitize "../" and so on
154         path = os.path.abspath(os.path.join(music_folder, parameter))
155         if not path.startswith(os.path.abspath(music_folder)):
156             bot.send_msg(constants.strings('no_file'), text)
157             return
158
159         if os.path.isfile(path):
160             music = {'type': 'file',
161                      'path': parameter,
162                      'user': user}
163             logging.info("cmd: add to playlist: " + parameter)
164             music = var.playlist.append(music)
165             bot.send_msg(constants.strings('file_added', item=util.format_song_string(music)), text)
166             return
167
168         # if parameter is {folder}
169         elif os.path.isdir(path):
170             if parameter != '.' and parameter != './':
171                 if not parameter.endswith("/"):
172                     parameter += "/"
173             else:
174                 parameter = ""
175
176             files = util.get_recursive_filelist_sorted(music_folder)
177             music_library = util.Dir(music_folder)
178             for file in files:
179                 music_library.add_file(file)
180
181             files = music_library.get_files(parameter)
182             msgs = [constants.strings('multiple_file_added')]
183             count = 0
184
185             for file in files:
186                 count += 1
187                 music = {'type': 'file',
188                          'path': file,
189                          'user': user}
190                 logging.info("cmd: add to playlist: " + file)
191                 music = var.playlist.append(music)
192
193                 msgs.append("{} ({})".format(music['title'], music['path']))
194
195             if count != 0:
196                 send_multi_lines(bot, msgs, text)
197             else:
198                 bot.send_msg(constants.strings('no_file'), text)
199
200         else:
201             # try to do a partial match
202             files = util.get_recursive_filelist_sorted(music_folder)
203             matches = [(index, file) for index, file in enumerate(files) if parameter.lower() in file.lower()]
204             if len(matches) == 0:
205                 bot.send_msg(constants.strings('no_file'), text)
206             elif len(matches) == 1:
207                 music = {'type': 'file',
208                          'path': matches[0][1],
209                          'user': user}
210                 logging.info("cmd: add to playlist: " + matches[0][1])
211                 music = var.playlist.append(music)
212                 bot.send_msg(constants.strings('file_added', item=util.format_song_string(music)), text)
213             else:
214                 msgs = [ constants.strings('multiple_matches')]
215                 for match in matches:
216                     msgs.append("<b>{:0>3d}</b> - {:s}".format(match[0], match[1]))
217                 send_multi_lines(bot, msgs, text)
218
219
220 def cmd_play_file_match(bot, user, text, command, parameter):
221     music_folder = var.config.get('bot', 'music_folder')
222     if parameter is not None:
223         files = util.get_recursive_filelist_sorted(music_folder)
224         msgs = [ constants.strings('multiple_file_added')]
225         count = 0
226         try:
227             for file in files:
228                 match = re.search(parameter, file)
229                 if match:
230                     count += 1
231                     music = {'type': 'file',
232                              'path': file,
233                              'user': user}
234                     logging.info("cmd: add to playlist: " + file)
235                     music = var.playlist.append(music)
236
237                     msgs.append("{} ({})".format(music['title'], music['path']))
238
239             if count != 0:
240                 send_multi_lines(bot, msgs, text)
241             else:
242                 bot.send_msg(constants.strings('no_file'), text)
243
244         except re.error as e:
245             msg = constants.strings('wrong_pattern', error=str(e))
246             bot.send_msg(msg, text)
247     else:
248         bot.send_msg(constants.strings('bad_parameter', command))
249
250
251 def cmd_play_url(bot, user, text, command, parameter):
252     music = {'type': 'url',
253              # grab the real URL
254              'url': util.get_url_from_input(parameter),
255              'user': user,
256              'ready': 'validation'}
257
258     music = bot.validate_music(music)
259     if music:
260         var.playlist.append(music)
261         logging.info("cmd: add to playlist: " + music['url'])
262         bot.send_msg(constants.strings('file_added', item=util.format_song_string(music)), text)
263         if var.playlist.length() == 2:
264             # If I am the second item on the playlist. (I am the next one!)
265             bot.async_download_next()
266     else:
267         bot.send_msg(constants.strings('unable_download'), text)
268
269
270 def cmd_play_playlist(bot, user, text, command, parameter):
271     offset = 0  # if you want to start the playlist at a specific index
272     try:
273         offset = int(parameter.split(" ")[-1])
274     except ValueError:
275         pass
276
277     url = util.get_url_from_input(parameter)
278     logging.debug("cmd: fetching media info from playlist url %s" % url)
279     items = media.playlist.get_playlist_info(url=url, start_index=offset, user=user)
280     if len(items) > 0:
281         var.playlist.extend(items)
282         for music in items:
283             logging.info("cmd: add to playlist: " + util.format_debug_song_string(music))
284
285
286 def cmd_play_radio(bot, user, text, command, parameter):
287     if not parameter:
288         all_radio = var.config.items('radio')
289         msg = constants.strings('preconfigurated_radio')
290         for i in all_radio:
291             comment = ""
292             if len(i[1].split(maxsplit=1)) == 2:
293                 comment = " - " + i[1].split(maxsplit=1)[1]
294             msg += "<br />" + i[0] + comment
295         bot.send_msg(msg, text)
296     else:
297         if var.config.has_option('radio', parameter):
298             parameter = var.config.get('radio', parameter)
299             parameter = parameter.split()[0]
300         url = util.get_url_from_input(parameter)
301         if url:
302             music = {'type': 'radio',
303                      'url': url,
304                      'user': user}
305             var.playlist.append(music)
306             logging.info("cmd: add to playlist: " + music['url'])
307             bot.async_download_next()
308         else:
309             bot.send_msg(constants.strings('bad_url'))
310
311
312 def cmd_rb_query(bot, user, text, command, parameter):
313     logging.info('cmd: Querying radio stations')
314     if not parameter:
315         logging.debug('rbquery without parameter')
316         msg = constants.strings('rb_query_empty')
317         bot.send_msg(msg, text)
318     else:
319         logging.debug('cmd: Found query parameter: ' + parameter)
320         # bot.send_msg('Searching for stations - this may take some seconds...', text)
321         rb_stations = radiobrowser.getstations_byname(parameter)
322         msg = constants.strings('rb_query_result')
323         msg += '\n<table><tr><th>!rbplay ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th></tr>'
324         if not rb_stations:
325             logging.debug('cmd: No matches found for rbquery ' + parameter)
326             bot.send_msg('Radio-Browser found no matches for ' + parameter, text)
327         else:
328             for s in rb_stations:
329                 stationid = s['id']
330                 stationname = s['stationname']
331                 country = s['country']
332                 codec = s['codec']
333                 bitrate = s['bitrate']
334                 genre = s['genre']
335                 # msg += f'<tr><td>{stationid}</td><td>{stationname}</td><td>{genre}</td><td>{codec}/{bitrate}</td><td>{country}</td></tr>'
336                 msg += '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s/%s</td><td>%s</td></tr>' % (
337                     stationid, stationname, genre, codec, bitrate, country)
338             msg += '</table>'
339             # Full message as html table
340             if len(msg) <= 5000:
341                 bot.send_msg(msg, text)
342             # Shorten message if message too long (stage I)
343             else:
344                 logging.debug('Result too long stage I')
345                 msg = constants.strings('rb_query_result') + ' (shortened L1)'
346                 msg += '\n<table><tr><th>!rbplay ID</th><th>Station Name</th></tr>'
347                 for s in rb_stations:
348                     stationid = s['id']
349                     stationname = s['stationname']
350                     # msg += f'<tr><td>{stationid}</td><td>{stationname}</td>'
351                     msg += '<tr><td>%s</td><td>%s</td>' % (stationid, stationname)
352                 msg += '</table>'
353                 if len(msg) <= 5000:
354                     bot.send_msg(msg, text)
355                 # Shorten message if message too long (stage II)
356                 else:
357                     logging.debug('Result too long stage II')
358                     msg = constants.strings('rb_query_result') + ' (shortened L2)'
359                     msg += '!rbplay ID - Station Name'
360                     for s in rb_stations:
361                         stationid = s['id']
362                         stationname = s['stationname'][:12]
363                         # msg += f'{stationid} - {stationname}'
364                         msg += '%s - %s' % (stationid, stationname)
365                     if len(msg) <= 5000:
366                         bot.send_msg(msg, text)
367                     # Message still too long
368                     else:
369                         bot.send_msg('Query result too long to post (> 5000 characters), please try another query.',
370                                      text)
371
372
373 def cmd_rb_play(bot, user, text, command, parameter):
374     logging.debug('cmd: Play a station by ID')
375     if not parameter:
376         logging.debug('rbplay without parameter')
377         msg = constants.strings('rb_play_empty')
378         bot.send_msg(msg, text)
379     else:
380         logging.debug('cmd: Retreiving url for station ID ' + parameter)
381         rstation = radiobrowser.getstationname_byid(parameter)
382         stationname = rstation[0]['name']
383         country = rstation[0]['country']
384         codec = rstation[0]['codec']
385         bitrate = rstation[0]['bitrate']
386         genre = rstation[0]['tags']
387         homepage = rstation[0]['homepage']
388         msg = 'Radio station added to playlist:'
389         # msg += '<table><tr><th>ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th><th>Homepage</th></tr>' + \
390         #       f'<tr><td>{parameter}</td><td>{stationname}</td><td>{genre}</td><td>{codec}/{bitrate}</td><td>{country}</td><td>{homepage}</td></tr></table>'
391         msg += '<table><tr><th>ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th><th>Homepage</th></tr>' + \
392                '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s/%s</td><td>%s</td><td>%s</td></tr></table>' \
393                % (parameter, stationname, genre, codec, bitrate, country, homepage)
394         logging.debug('cmd: Added station to playlist %s' % stationname)
395         bot.send_msg(msg, text)
396         url = radiobrowser.geturl_byid(parameter)
397         if url != "-1":
398             logging.info('cmd: Found url: ' + url)
399             music = {'type': 'radio',
400                      'title': stationname,
401                      'artist': homepage,
402                      'url': url,
403                      'user': user}
404             var.playlist.append(music)
405             logging.info("cmd: add to playlist: " + music['url'])
406             bot.async_download_next()
407         else:
408             logging.info('cmd: No playable url found.')
409             msg += "No playable url found for this station, please try another station."
410             bot.send_msg(msg, text)
411
412
413 def cmd_help(bot, user, text, command, parameter):
414     bot.send_msg(constants.strings('help'), text)
415     if bot.is_admin(user):
416         bot.send_msg(constants.strings('admin_help'), text)
417
418
419 def cmd_stop(bot, user, text, command, parameter):
420     bot.stop()
421     bot.send_msg(constants.strings('stopped'), text)
422
423
424 def cmd_clear(bot, user, text, command, parameter):
425     bot.clear()
426     bot.send_msg(constants.strings('cleared'), text)
427
428
429 def cmd_kill(bot, user, text, command, parameter):
430     if bot.is_admin(user):
431         bot.pause()
432         bot.exit = True
433     else:
434         bot.mumble.users[text.actor].send_text_message(
435             constants.strings('not_admin'))
436
437
438 def cmd_update(bot, user, text, command, parameter):
439     if bot.is_admin(user):
440         bot.mumble.users[text.actor].send_text_message(
441             constants.strings('start_updating'))
442         msg = util.update(bot.version)
443         bot.mumble.users[text.actor].send_text_message(msg)
444     else:
445         bot.mumble.users[text.actor].send_text_message(
446             constants.strings('not_admin'))
447
448
449 def cmd_stop_and_getout(bot, user, text, command, parameter):
450     bot.stop()
451     if bot.channel:
452         bot.mumble.channels.find_by_name(bot.channel).move_in()
453
454
455 def cmd_volume(bot, user, text, command, parameter):
456     # The volume is a percentage
457     if parameter is not None and parameter.isdigit() and 0 <= int(parameter) <= 100:
458         bot.volume_set = float(float(parameter) / 100)
459         bot.send_msg(constants.strings('change_volume',
460             volume=int(bot.volume_set * 100), user=bot.mumble.users[text.actor]['name']), text)
461         var.db.set('bot', 'volume', str(bot.volume_set))
462         logging.info('cmd: volume set to %d' % (bot.volume_set * 100))
463     else:
464         bot.send_msg(constants.strings('current_volume', volume=int(bot.volume_set * 100)), text)
465
466
467 def cmd_ducking(bot, user, text, command, parameter):
468     if parameter == "" or parameter == "on":
469         bot.is_ducking = True
470         var.db.set('bot', 'ducking', True)
471         bot.ducking_volume = var.config.getfloat("bot", "ducking_volume", fallback=0.05)
472         bot.ducking_threshold = var.config.getint("bot", "ducking_threshold", fallback=5000)
473         bot.mumble.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED,
474                                           bot.ducking_sound_received)
475         bot.mumble.set_receive_sound(True)
476         logging.info('cmd: ducking is on')
477         msg = "Ducking on."
478         bot.send_msg(msg, text)
479     elif parameter == "off":
480         bot.is_ducking = False
481         bot.mumble.set_receive_sound(False)
482         var.db.set('bot', 'ducking', False)
483         msg = "Ducking off."
484         logging.info('cmd: ducking is off')
485         bot.send_msg(msg, text)
486
487
488 def cmd_ducking_threshold(bot, user, text, command, parameter):
489     if parameter is not None and parameter.isdigit():
490         bot.ducking_threshold = int(parameter)
491         var.db.set('bot', 'ducking_threshold', str(bot.ducking_threshold))
492         msg = "Ducking threshold set to %d." % bot.ducking_threshold
493         bot.send_msg(msg, text)
494     else:
495         msg = "Current ducking threshold is %d." % bot.ducking_threshold
496         bot.send_msg(msg, text)
497
498
499 def cmd_ducking_volume(bot, user, text, command, parameter):
500     # The volume is a percentage
501     if parameter is not None and parameter.isdigit() and 0 <= int(parameter) <= 100:
502         bot.ducking_volume = float(float(parameter) / 100)
503         bot.send_msg(constants.strings('change_ducking_volume',
504             volume=int(bot.ducking_volume * 100), user=bot.mumble.users[text.actor]['name']), text)
505         # var.db.set('bot', 'volume', str(bot.volume_set))
506         var.db.set('bot', 'ducking_volume', str(bot.ducking_volume))
507         logging.info('cmd: volume on ducking set to %d' % (bot.ducking_volume * 100))
508     else:
509         bot.send_msg(constants.strings('current_ducking_volume', volume=int(bot.ducking_volume * 100)), text)
510
511
512 def cmd_current_music(bot, user, text, command, parameter):
513     reply = ""
514     if var.playlist.length() > 0:
515         bot.send_msg(util.format_current_playing())
516     else:
517         reply = constants.strings('not_playing')
518     bot.send_msg(reply, text)
519
520
521 def cmd_skip(bot, user, text, command, parameter):
522     if var.playlist.length() > 0:
523         bot.stop()
524         bot.launch_music()
525         bot.async_download_next()
526     else:
527         bot.send_msg(constants.strings('queue_empty'), text)
528
529
530 def cmd_remove(bot, user, text, command, parameter):
531     # Allow to remove specific music into the queue with a number
532     if parameter is not None and parameter.isdigit() and int(parameter) > 0 \
533             and int(parameter) <= var.playlist.length():
534
535         index = int(parameter) - 1
536
537         removed = None
538         if index == var.playlist.current_index:
539             removed = var.playlist.remove(index)
540
541             if index < len(var.playlist):
542                 if not bot.is_pause:
543                     bot.interrupt_playing()
544                     var.playlist.current_index -= 1
545                     # then the bot will move to next item
546
547             else: # if item deleted is the last item of the queue
548                 var.playlist.current_index -= 1
549                 if not bot.is_pause:
550                     bot.interrupt_playing()
551         else:
552             removed = var.playlist.remove(index)
553
554         # the Title isn't here if the music wasn't downloaded
555         bot.send_msg(constants.strings('removing_item',
556             item=removed['title'] if 'title' in removed else removed['url']), text)
557
558         logging.info("cmd: delete from playlist: " + str(removed['path'] if 'path' in removed else removed['url']))
559     else:
560         bot.send_msg(constants.strings('bad_parameter', command=command))
561
562
563 def cmd_list_file(bot, user, text, command, parameter):
564     folder_path = var.config.get('bot', 'music_folder')
565
566     files = util.get_recursive_filelist_sorted(folder_path)
567     msgs = [ "<br> <b>Files available:</b>" if not parameter else "<br> <b>Matched files:</b>" ]
568     try:
569         count = 0
570         for index, file in enumerate(files):
571             if parameter:
572                 match = re.search(parameter, file)
573                 if not match:
574                     continue
575
576             count += 1
577             msgs.append("<b>{:0>3d}</b> - {:s}".format(index, file))
578
579         if count != 0:
580             send_multi_lines(bot, msgs, text)
581         else:
582             bot.send_msg(constants.strings('no_file'), text)
583
584     except re.error as e:
585         msg = constants.strings('wrong_pattern', error=str(e))
586         bot.send_msg(msg, text)
587
588
589 def cmd_queue(bot, user, text, command, parameter):
590     if len(var.playlist) == 0:
591         msg = constants.strings('queue_empty')
592         bot.send_msg(msg, text)
593     else:
594         msgs = [ constants.strings('queue_contents')]
595         for i, value in enumerate(var.playlist):
596             newline = ''
597             if i == var.playlist.current_index:
598                 newline = '<b>{} ▶ ({}) {} ◀</b>'.format(i + 1, value['type'],
599                                                            value['title'] if 'title' in value else value['url'])
600             else:
601                 newline = '<b>{}</b> ({}) {}'.format(i + 1, value['type'],
602                                                      value['title'] if 'title' in value else value['url'])
603
604             msgs.append(newline)
605
606         send_multi_lines(bot, msgs, text)
607
608 def cmd_random(bot, user, text, command, parameter):
609     bot.interrupt_playing()
610     var.playlist.randomize()
611
612 def cmd_mode(bot, user, text, command, parameter):
613     if not parameter:
614         bot.send_msg(constants.strings("current_mode", mode=var.playlist.mode), text)
615         return
616     if not parameter in ["one-shot", "repeat", "random"]:
617         bot.send_msg(constants.strings('unknown_mode', mode=parameter), text)
618     else:
619         var.db.set('playlist', 'playback_mode', parameter)
620         var.playlist.set_mode(parameter)
621         logging.info("command: playback mode changed to %s." % parameter)
622         bot.send_msg(constants.strings("change_mode", mode=var.playlist.mode,
623                                        user=bot.mumble.users[text.actor]['name']), text)
624         if parameter == "random":
625             bot.stop()
626             var.playlist.randomize()
627             bot.launch_music(0)
628
629
630 def cmd_drop_database(bot, user, text, command, parameter):
631     var.db.drop_table()
632     var.db = Database(var.dbfile)
633     bot.send_msg(constants.strings('database_dropped'), text)