]> git.0d.be Git - botaradio.git/blob - command.py
fix: message too long on some extreme cases
[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)) > (bot.mumble.get_max_message_length() - 4) != 0: # 4 == len("<br>")
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     if bot.download_in_progress:
672         bot.send_msg(constants.strings("cannot_change_when_download"))
673         return
674
675     # Allow to remove specific music into the queue with a number
676     if parameter and parameter.isdigit() and int(parameter) > 0 \
677             and int(parameter) <= var.playlist.length():
678
679         index = int(parameter) - 1
680
681         removed = None
682         if index == var.playlist.current_index:
683             removed = var.playlist.remove(index)
684
685             if index < len(var.playlist):
686                 if not bot.is_pause:
687                     bot.interrupt_playing()
688                     var.playlist.current_index -= 1
689                     # then the bot will move to next item
690
691             else: # if item deleted is the last item of the queue
692                 var.playlist.current_index -= 1
693                 if not bot.is_pause:
694                     bot.interrupt_playing()
695         else:
696             removed = var.playlist.remove(index)
697
698         # the Title isn't here if the music wasn't downloaded
699         bot.send_msg(constants.strings('removing_item',
700             item=removed['title'] if 'title' in removed else removed['url']), text)
701
702         log.info("cmd: delete from playlist: " + str(removed['path'] if 'path' in removed else removed['url']))
703     else:
704         bot.send_msg(constants.strings('bad_parameter', command=command))
705
706
707 def cmd_list_file(bot, user, text, command, parameter):
708     global log
709
710     folder_path = var.music_folder
711
712     files = util.get_recursive_file_list_sorted(folder_path)
713     msgs = [ "<br> <b>Files available:</b>" if not parameter else "<br> <b>Matched files:</b>" ]
714     try:
715         count = 0
716         for index, file in enumerate(files):
717             if parameter:
718                 match = re.search(parameter, file)
719                 if not match:
720                     continue
721
722             count += 1
723             msgs.append("<b>{:0>3d}</b> - {:s}".format(index, file))
724
725         if count != 0:
726             send_multi_lines(bot, msgs, text)
727         else:
728             bot.send_msg(constants.strings('no_file'), text)
729
730     except re.error as e:
731         msg = constants.strings('wrong_pattern', error=str(e))
732         bot.send_msg(msg, text)
733
734
735 def cmd_queue(bot, user, text, command, parameter):
736     global log
737
738     if len(var.playlist) == 0:
739         msg = constants.strings('queue_empty')
740         bot.send_msg(msg, text)
741     else:
742         msgs = [ constants.strings('queue_contents')]
743         for i, value in enumerate(var.playlist):
744             newline = ''
745             if i == var.playlist.current_index:
746                 newline = '<b>{} ▶ ({}) {} ◀</b>'.format(i + 1, value['type'],
747                                                            value['title'] if 'title' in value else value['url'])
748             else:
749                 newline = '<b>{}</b> ({}) {}'.format(i + 1, value['type'],
750                                                      value['title'] if 'title' in value else value['url'])
751
752             msgs.append(newline)
753
754         send_multi_lines(bot, msgs, text)
755
756 def cmd_random(bot, user, text, command, parameter):
757     global log
758
759     bot.interrupt_playing()
760     var.playlist.randomize()
761
762 def cmd_repeat(bot, user, text, command, parameter):
763     global log
764
765     repeat = 1
766     if parameter and parameter.isdigit():
767         repeat = int(parameter)
768
769     music = var.playlist.current_item()
770     for _ in range(repeat):
771         var.playlist.insert(
772             var.playlist.current_index + 1,
773             music
774         )
775         log.info("bot: add to playlist: " + util.format_debug_song_string(music))
776
777     bot.send_msg(constants.strings("repeat", song=util.format_song_string(music), n=str(repeat)), text)
778
779 def cmd_mode(bot, user, text, command, parameter):
780     global log
781
782     if not parameter:
783         bot.send_msg(constants.strings("current_mode", mode=var.playlist.mode), text)
784         return
785     if not parameter in ["one-shot", "repeat", "random"]:
786         bot.send_msg(constants.strings('unknown_mode', mode=parameter), text)
787     else:
788         var.db.set('playlist', 'playback_mode', parameter)
789         var.playlist.set_mode(parameter)
790         log.info("command: playback mode changed to %s." % parameter)
791         bot.send_msg(constants.strings("change_mode", mode=var.playlist.mode,
792                                        user=bot.mumble.users[text.actor]['name']), text)
793         if parameter == "random":
794             bot.stop()
795             var.playlist.randomize()
796             bot.launch_music(0)
797
798 def cmd_drop_database(bot, user, text, command, parameter):
799     global log
800
801     var.db.drop_table()
802     var.db = Database(var.dbfile)
803     bot.send_msg(constants.strings('database_dropped'), text)
804
805 # Just for debug use
806 def cmd_real_time_rms(bot, user, text, command, parameter):
807     bot._display_rms = not bot._display_rms
808
809 def cmd_loop_state(bot, user, text, command, parameter):
810     print(bot._loop_status)
811
812 def cmd_item(bot, user, text, command, parameter):
813     print(bot.wait_for_downloading)
814     print(var.playlist.current_item())