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