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