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