3 import pymumble.pymumble_py3 as pymumble
9 import variables as var
10 from librb import radiobrowser
11 from database import SettingsDatabase, MusicDatabase
12 from media.item import item_id_generators, dict_to_item, dicts_to_items
13 from media.cache import get_cached_wrapper_from_scrap, get_cached_wrapper_by_id, get_cached_wrappers_by_tags
14 from media.url_from_playlist import get_playlist_info
16 log = logging.getLogger("bot")
19 def register_all_commands(bot):
20 bot.register_command(constants.commands('joinme'), cmd_joinme, no_partial_match=False, access_outside_channel=True)
21 bot.register_command(constants.commands('user_ban'), cmd_user_ban, no_partial_match=True)
22 bot.register_command(constants.commands('user_unban'), cmd_user_unban, no_partial_match=True)
23 bot.register_command(constants.commands('url_ban_list'), cmd_url_ban_list, no_partial_match=True)
24 bot.register_command(constants.commands('url_ban'), cmd_url_ban, no_partial_match=True)
25 bot.register_command(constants.commands('url_unban'), cmd_url_unban, no_partial_match=True)
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('play_tag'), cmd_play_tags)
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, no_partial_match=False, access_outside_channel=True)
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, no_partial_match=True)
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('add_tag'), cmd_add_tag)
58 bot.register_command(constants.commands('remove_tag'), cmd_remove_tag)
59 bot.register_command(constants.commands('find_tagged'), cmd_find_tagged)
60 bot.register_command(constants.commands('search'), cmd_search_library)
61 bot.register_command(constants.commands('add_from_shortlist'), cmd_shortlist)
62 bot.register_command(constants.commands('delete_from_library'), cmd_delete_from_library)
63 bot.register_command(constants.commands('drop_database'), cmd_drop_database, no_partial_match=True)
64 bot.register_command(constants.commands('rescan'), cmd_refresh_cache, no_partial_match=True)
67 bot.register_command('rtrms', cmd_real_time_rms, True)
68 bot.register_command('loop', cmd_loop_state, True)
69 bot.register_command('item', cmd_item, True)
72 def send_multi_lines(bot, lines, text, linebreak="<br />"):
80 if bot.mumble.get_max_message_length()\
81 and (len(msg) + len(newline)) > (bot.mumble.get_max_message_length() - 4): # 4 == len("<br>")
82 bot.send_msg(msg, text)
86 bot.send_msg(msg, text)
89 # ---------------- Variables -----------------
94 # ---------------- Commands ------------------
96 def cmd_joinme(bot, user, text, command, parameter):
99 bot.mumble.users.myself.move_in(
100 bot.mumble.users[text.actor]['channel_id'], token=parameter)
103 def cmd_user_ban(bot, user, text, command, parameter):
106 if bot.is_admin(user):
108 bot.mumble.users[text.actor].send_text_message(util.user_ban(parameter))
110 bot.mumble.users[text.actor].send_text_message(util.get_user_ban())
112 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
116 def cmd_user_unban(bot, user, text, command, parameter):
119 if bot.is_admin(user):
121 bot.mumble.users[text.actor].send_text_message(util.user_unban(parameter))
123 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
127 def cmd_url_ban(bot, user, text, command, parameter):
130 if bot.is_admin(user):
132 bot.mumble.users[text.actor].send_text_message(util.url_ban(util.get_url_from_input(parameter)))
134 id = item_id_generators['url'](url=parameter)
135 var.cache.free_and_delete(id)
136 var.playlist.remove_by_id(id)
138 if var.playlist.current_item() and var.playlist.current_item().type == 'url':
139 item = var.playlist.current_item().item()
140 bot.mumble.users[text.actor].send_text_message(util.url_ban(util.get_url_from_input(item.url)))
141 var.cache.free_and_delete(item.id)
142 var.playlist.remove_by_id(item.id)
144 bot.send_msg(constants.strings('bad_parameter', command=command))
146 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
150 def cmd_url_ban_list(bot, user, text, command, parameter):
151 if bot.is_admin(user):
152 bot.mumble.users[text.actor].send_text_message(util.get_url_ban())
154 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
158 def cmd_url_unban(bot, user, text, command, parameter):
161 if bot.is_admin(user):
163 bot.mumble.users[text.actor].send_text_message(util.url_unban(util.get_url_from_input(parameter)))
165 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
169 def cmd_play(bot, user, text, command, parameter):
172 if len(var.playlist) > 0:
174 if parameter.isdigit() and 1 <= int(parameter) <= len(var.playlist):
175 # First "-1" transfer 12345 to 01234, second "-1"
176 # point to the previous item. the loop will next to
178 var.playlist.point_to(int(parameter) - 1 - 1)
185 bot.send_msg(constants.strings('invalid_index', index=parameter), text)
190 bot.send_msg(var.playlist.current_item().format_current_playing(), text)
193 bot.send_msg(constants.strings('queue_empty'), text)
196 def cmd_pause(bot, user, text, command, parameter):
200 bot.send_msg(constants.strings('paused'))
203 def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=False):
204 global log, song_shortlist
206 # if parameter is {index}
207 if parameter.isdigit():
208 files = var.cache.files
209 if int(parameter) < len(files):
210 music_wrapper = get_cached_wrapper_by_id(bot, var.cache.file_id_lookup[files[int(parameter)]], user)
211 var.playlist.append(music_wrapper)
212 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
213 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()))
216 # if parameter is {path}
218 # sanitize "../" and so on
219 # path = os.path.abspath(os.path.join(var.music_folder, parameter))
220 # if not path.startswith(os.path.abspath(var.music_folder)):
221 # bot.send_msg(constants.strings('no_file'), text)
224 if parameter in var.cache.files:
225 music_wrapper = get_cached_wrapper_from_scrap(bot, type='file', path=parameter, user=user)
226 var.playlist.append(music_wrapper)
227 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
228 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()))
231 # if parameter is {folder}
232 files = var.cache.dir.get_files(parameter)
234 msgs = [constants.strings('multiple_file_added')]
239 music_wrapper = get_cached_wrapper_by_id(bot, var.cache.file_id_lookup[file], user)
240 var.playlist.append(music_wrapper)
241 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
242 msgs.append("{} ({})".format(music_wrapper.item().title, music_wrapper.item().path))
245 send_multi_lines(bot, msgs, None)
249 # try to do a partial match
250 files = var.cache.files
251 matches = [file for file in files if parameter.lower() in file.lower()]
252 if len(matches) == 1:
254 music_wrapper = get_cached_wrapper_by_id(bot, var.cache.file_id_lookup[file], user)
255 var.playlist.append(music_wrapper)
256 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
257 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()))
259 elif len(matches) > 1:
260 msgs = [constants.strings('multiple_matches')]
262 for index, match in enumerate(matches):
263 id = var.cache.file_id_lookup[match]
264 music_dict = var.music_db.query_music_by_id(id)
265 item = dict_to_item(bot, music_dict)
267 song_shortlist.append(music_dict)
269 msgs.append("<b>{:d}</b> - <b>{:s}</b> ({:s})".format(
270 index + 1, item.title, match))
271 send_multi_lines(bot, msgs, text)
274 if do_not_refresh_cache:
275 bot.send_msg(constants.strings("no_file"), text)
277 var.cache.build_dir_cache(bot)
278 cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=True)
281 def cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cache=False):
285 files = var.cache.files
286 msgs = [constants.strings('multiple_file_added') + "<ul>"]
291 match = re.search(parameter, file)
292 if match and match[0]:
294 music_wrapper = get_cached_wrapper_by_id(bot, var.cache.file_id_lookup[file], user)
295 music_wrappers.append(music_wrapper)
296 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
297 msgs.append("<li><b>{}</b> ({})</li>".format(music_wrapper.item().title,
298 file[:match.span()[0]]
299 + "<b style='color:pink'>"
300 + file[match.span()[0]: match.span()[1]]
302 + file[match.span()[1]:]
307 var.playlist.extend(music_wrappers)
308 send_multi_lines(bot, msgs, None, "")
310 if do_not_refresh_cache:
311 bot.send_msg(constants.strings("no_file"), text)
313 var.cache.build_dir_cache(bot)
314 cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cache=True)
316 except re.error as e:
317 msg = constants.strings('wrong_pattern', error=str(e))
318 bot.send_msg(msg, text)
320 bot.send_msg(constants.strings('bad_parameter', command=command))
323 def cmd_play_url(bot, user, text, command, parameter):
326 url = util.get_url_from_input(parameter)
328 music_wrapper = get_cached_wrapper_from_scrap(bot, type='url', url=url, user=user)
329 var.playlist.append(music_wrapper)
331 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
332 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()))
333 if len(var.playlist) == 2:
334 # If I am the second item on the playlist. (I am the next one!)
335 bot.async_download_next()
337 bot.send_msg(constants.strings('bad_parameter', command=command))
340 def cmd_play_playlist(bot, user, text, command, parameter):
343 offset = 0 # if you want to start the playlist at a specific index
345 offset = int(parameter.split(" ")[-1])
349 url = util.get_url_from_input(parameter)
350 log.debug("cmd: fetching media info from playlist url %s" % url)
351 items = get_playlist_info(url=url, start_index=offset, user=user)
353 items = var.playlist.extend(list(map(
354 lambda item: get_cached_wrapper_from_scrap(bot, **item), items)))
356 log.info("cmd: add to playlist: " + music.format_debug_string())
358 bot.send_msg(constants.strings("playlist_fetching_failed"), text)
361 def cmd_play_radio(bot, user, text, command, parameter):
365 all_radio = var.config.items('radio')
366 msg = constants.strings('preconfigurated_radio')
369 if len(i[1].split(maxsplit=1)) == 2:
370 comment = " - " + i[1].split(maxsplit=1)[1]
371 msg += "<br />" + i[0] + comment
372 bot.send_msg(msg, text)
374 if var.config.has_option('radio', parameter):
375 parameter = var.config.get('radio', parameter)
376 parameter = parameter.split()[0]
377 url = util.get_url_from_input(parameter)
379 music_wrapper = get_cached_wrapper_from_scrap(bot, type='radio', url=url, user=user)
381 var.playlist.append(music_wrapper)
382 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
383 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()))
385 bot.send_msg(constants.strings('bad_url'))
388 def cmd_rb_query(bot, user, text, command, parameter):
391 log.info('cmd: Querying radio stations')
393 log.debug('rbquery without parameter')
394 msg = constants.strings('rb_query_empty')
395 bot.send_msg(msg, text)
397 log.debug('cmd: Found query parameter: ' + parameter)
398 # bot.send_msg('Searching for stations - this may take some seconds...', text)
399 rb_stations = radiobrowser.getstations_byname(parameter)
400 msg = constants.strings('rb_query_result')
401 msg += '\n<table><tr><th>!rbplay ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th></tr>'
403 log.debug('cmd: No matches found for rbquery ' + parameter)
404 bot.send_msg('Radio-Browser found no matches for ' + parameter, text)
406 for s in rb_stations:
408 stationname = s['stationname']
409 country = s['country']
411 bitrate = s['bitrate']
413 # msg += f'<tr><td>{stationid}</td><td>{stationname}</td><td>{genre}</td><td>{codec}/{bitrate}</td><td>{country}</td></tr>'
414 msg += '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s/%s</td><td>%s</td></tr>' % (
415 stationid, stationname, genre, codec, bitrate, country)
417 # Full message as html table
419 bot.send_msg(msg, text)
420 # Shorten message if message too long (stage I)
422 log.debug('Result too long stage I')
423 msg = constants.strings('rb_query_result') + ' (shortened L1)'
424 msg += '\n<table><tr><th>!rbplay ID</th><th>Station Name</th></tr>'
425 for s in rb_stations:
427 stationname = s['stationname']
428 # msg += f'<tr><td>{stationid}</td><td>{stationname}</td>'
429 msg += '<tr><td>%s</td><td>%s</td>' % (stationid, stationname)
432 bot.send_msg(msg, text)
433 # Shorten message if message too long (stage II)
435 log.debug('Result too long stage II')
436 msg = constants.strings('rb_query_result') + ' (shortened L2)'
437 msg += '!rbplay ID - Station Name'
438 for s in rb_stations:
440 stationname = s['stationname'][:12]
441 # msg += f'{stationid} - {stationname}'
442 msg += '%s - %s' % (stationid, stationname)
444 bot.send_msg(msg, text)
445 # Message still too long
447 bot.send_msg('Query result too long to post (> 5000 characters), please try another query.',
451 def cmd_rb_play(bot, user, text, command, parameter):
454 log.debug('cmd: Play a station by ID')
456 log.debug('rbplay without parameter')
457 msg = constants.strings('rb_play_empty')
458 bot.send_msg(msg, text)
460 log.debug('cmd: Retreiving url for station ID ' + parameter)
461 rstation = radiobrowser.getstationname_byid(parameter)
462 stationname = rstation[0]['name']
463 country = rstation[0]['country']
464 codec = rstation[0]['codec']
465 bitrate = rstation[0]['bitrate']
466 genre = rstation[0]['tags']
467 homepage = rstation[0]['homepage']
468 msg = 'Radio station added to playlist:'
469 # msg += '<table><tr><th>ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th><th>Homepage</th></tr>' + \
470 # f'<tr><td>{parameter}</td><td>{stationname}</td><td>{genre}</td><td>{codec}/{bitrate}</td><td>{country}</td><td>{homepage}</td></tr></table>'
471 msg += '<table><tr><th>ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th><th>Homepage</th></tr>' + \
472 '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s/%s</td><td>%s</td><td>%s</td></tr></table>' \
473 % (parameter, stationname, genre, codec, bitrate, country, homepage)
474 log.debug('cmd: Added station to playlist %s' % stationname)
475 bot.send_msg(msg, text)
476 url = radiobrowser.geturl_byid(parameter)
478 log.info('cmd: Found url: ' + url)
479 music_wrapper = get_cached_wrapper_from_scrap(bot, type='radio', url=url, name=stationname, user=user)
480 var.playlist.append(music_wrapper)
481 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
482 bot.async_download_next()
484 log.info('cmd: No playable url found.')
485 msg += "No playable url found for this station, please try another station."
486 bot.send_msg(msg, text)
490 yt_last_page = 0 # TODO: if we keep adding global variables, we need to consider sealing all commands up into classes.
493 def cmd_yt_search(bot, user, text, command, parameter):
494 global log, yt_last_result, yt_last_page, song_shortlist
499 if parameter.startswith("-n"):
501 if len(yt_last_result) > yt_last_page * item_per_page:
502 song_shortlist = [{'type': 'url',
503 'url': "https://www.youtube.com/watch?v=" + result[0],
505 } for result in yt_last_result[yt_last_page * item_per_page: item_per_page]]
506 msg = _yt_format_result(yt_last_result, yt_last_page * item_per_page, item_per_page)
507 bot.send_msg(constants.strings('yt_result', result_table=msg), text)
509 bot.send_msg(constants.strings('yt_no_more'))
513 results = util.youtube_search(parameter)
515 yt_last_result = results
517 song_shortlist = [{'type': 'url', 'url': "https://www.youtube.com/watch?v=" + result[0]}
518 for result in results[0: item_per_page]]
519 msg = _yt_format_result(results, 0, item_per_page)
520 bot.send_msg(constants.strings('yt_result', result_table=msg), text)
522 bot.send_msg(constants.strings('yt_query_error'))
524 bot.send_msg(constants.strings('bad_parameter', command=command), text)
527 def _yt_format_result(results, start, count):
528 msg = '<table><tr><th width="10%">Index</th><th>Title</th><th width="20%">Uploader</th></tr>'
529 for index, item in enumerate(results[start:start+count]):
530 msg += '<tr><td>{index:d}</td><td>{title}</td><td>{uploader}</td></tr>'.format(
531 index=index + 1, title=item[1], uploader=item[2])
537 def cmd_yt_play(bot, user, text, command, parameter):
538 global log, yt_last_result, yt_last_page
541 results = util.youtube_search(parameter)
543 yt_last_result = results
545 url = "https://www.youtube.com/watch?v=" + yt_last_result[0][0]
546 cmd_play_url(bot, user, text, command, url)
548 bot.send_msg(constants.strings('yt_query_error'))
550 bot.send_msg(constants.strings('bad_parameter', command=command), text)
553 def cmd_help(bot, user, text, command, parameter):
555 bot.send_msg(constants.strings('help'), text)
556 if bot.is_admin(user):
557 bot.send_msg(constants.strings('admin_help'), text)
560 def cmd_stop(bot, user, text, command, parameter):
564 bot.send_msg(constants.strings('stopped'), text)
567 def cmd_clear(bot, user, text, command, parameter):
571 bot.send_msg(constants.strings('cleared'), text)
574 def cmd_kill(bot, user, text, command, parameter):
577 if bot.is_admin(user):
581 bot.mumble.users[text.actor].send_text_message(
582 constants.strings('not_admin'))
585 def cmd_update(bot, user, text, command, parameter):
588 if bot.is_admin(user):
589 bot.mumble.users[text.actor].send_text_message(
590 constants.strings('start_updating'))
591 msg = util.update(bot.version)
592 bot.mumble.users[text.actor].send_text_message(msg)
594 bot.mumble.users[text.actor].send_text_message(
595 constants.strings('not_admin'))
598 def cmd_stop_and_getout(bot, user, text, command, parameter):
602 if var.playlist.mode == "one-shot":
606 bot.mumble.channels.find_by_name(bot.channel).move_in()
609 def cmd_volume(bot, user, text, command, parameter):
612 # The volume is a percentage
613 if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
614 bot.volume_set = float(float(parameter) / 100)
615 bot.send_msg(constants.strings('change_volume',
616 volume=int(bot.volume_set * 100), user=bot.mumble.users[text.actor]['name']))
617 var.db.set('bot', 'volume', str(bot.volume_set))
618 log.info('cmd: volume set to %d' % (bot.volume_set * 100))
620 bot.send_msg(constants.strings('current_volume', volume=int(bot.volume_set * 100)), text)
623 def cmd_ducking(bot, user, text, command, parameter):
626 if parameter == "" or parameter == "on":
627 bot.is_ducking = True
628 var.db.set('bot', 'ducking', True)
629 bot.ducking_volume = var.config.getfloat("bot", "ducking_volume", fallback=0.05)
630 bot.ducking_threshold = var.config.getint("bot", "ducking_threshold", fallback=5000)
631 bot.mumble.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED,
632 bot.ducking_sound_received)
633 bot.mumble.set_receive_sound(True)
634 log.info('cmd: ducking is on')
636 bot.send_msg(msg, text)
637 elif parameter == "off":
638 bot.is_ducking = False
639 bot.mumble.set_receive_sound(False)
640 var.db.set('bot', 'ducking', False)
642 log.info('cmd: ducking is off')
643 bot.send_msg(msg, text)
646 def cmd_ducking_threshold(bot, user, text, command, parameter):
649 if parameter and parameter.isdigit():
650 bot.ducking_threshold = int(parameter)
651 var.db.set('bot', 'ducking_threshold', str(bot.ducking_threshold))
652 msg = "Ducking threshold set to %d." % bot.ducking_threshold
653 bot.send_msg(msg, text)
655 msg = "Current ducking threshold is %d." % bot.ducking_threshold
656 bot.send_msg(msg, text)
659 def cmd_ducking_volume(bot, user, text, command, parameter):
662 # The volume is a percentage
663 if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
664 bot.ducking_volume = float(float(parameter) / 100)
665 bot.send_msg(constants.strings('change_ducking_volume',
666 volume=int(bot.ducking_volume * 100), user=bot.mumble.users[text.actor]['name']), text)
667 # var.db.set('bot', 'volume', str(bot.volume_set))
668 var.db.set('bot', 'ducking_volume', str(bot.ducking_volume))
669 log.info('cmd: volume on ducking set to %d' % (bot.ducking_volume * 100))
671 bot.send_msg(constants.strings('current_ducking_volume', volume=int(bot.ducking_volume * 100)), text)
674 def cmd_current_music(bot, user, text, command, parameter):
677 if len(var.playlist) > 0:
678 bot.send_msg(var.playlist.current_item().format_current_playing(), text)
680 bot.send_msg(constants.strings('not_playing'), text)
683 def cmd_skip(bot, user, text, command, parameter):
688 if len(var.playlist) == 0:
689 bot.send_msg(constants.strings('queue_empty'), text)
692 def cmd_last(bot, user, text, command, parameter):
695 if len(var.playlist) > 0:
697 var.playlist.point_to(len(var.playlist) - 1 - 1)
699 bot.send_msg(constants.strings('queue_empty'), text)
702 def cmd_remove(bot, user, text, command, parameter):
705 # Allow to remove specific music into the queue with a number
706 if parameter and parameter.isdigit() and 0 < int(parameter) <= len(var.playlist):
708 index = int(parameter) - 1
710 if index == var.playlist.current_index:
711 removed = var.playlist[index]
712 bot.send_msg(constants.strings('removing_item',
713 item=removed.format_short_string()), text)
714 log.info("cmd: delete from playlist: " + removed.format_debug_string())
716 var.playlist.remove(index)
718 if index < len(var.playlist):
721 var.playlist.current_index -= 1
722 # then the bot will move to next item
724 else: # if item deleted is the last item of the queue
725 var.playlist.current_index -= 1
729 var.playlist.remove(index)
732 bot.send_msg(constants.strings('bad_parameter', command=command), text)
735 def cmd_list_file(bot, user, text, command, parameter):
738 files = var.cache.files
739 msgs = [constants.strings("multiple_file_found")]
742 for index, file in enumerate(files):
744 match = re.search(parameter, file)
749 msgs.append("<b>{:0>3d}</b> - {:s}".format(index, file))
752 send_multi_lines(bot, msgs, text)
754 bot.send_msg(constants.strings('no_file'), text)
756 except re.error as e:
757 msg = constants.strings('wrong_pattern', error=str(e))
758 bot.send_msg(msg, text)
761 def cmd_queue(bot, user, text, command, parameter):
764 if len(var.playlist) == 0:
765 msg = constants.strings('queue_empty')
766 bot.send_msg(msg, text)
768 msgs = [constants.strings('queue_contents')]
769 for i, music in enumerate(var.playlist):
771 if len(music.item().tags) > 0:
772 tags = "<sup>{}</sup>".format(", ".join(music.item().tags))
773 if i == var.playlist.current_index:
774 newline = "<b style='color:orange'>{} ({}) {} </b> {}".format(i + 1, music.display_type(),
775 music.format_short_string(), tags)
777 newline = '<b>{}</b> ({}) {} {}'.format(i + 1, music.display_type(),
778 music.format_short_string(), tags)
782 send_multi_lines(bot, msgs, text)
785 def cmd_random(bot, user, text, command, parameter):
789 var.playlist.randomize()
792 def cmd_repeat(bot, user, text, command, parameter):
796 if parameter and parameter.isdigit():
797 repeat = int(parameter)
799 music = var.playlist.current_item()
800 for _ in range(repeat):
802 var.playlist.current_index + 1,
805 log.info("bot: add to playlist: " + music.format_debug_string())
807 bot.send_msg(constants.strings("repeat", song=music.format_song_string(), n=str(repeat)), text)
810 def cmd_mode(bot, user, text, command, parameter):
814 bot.send_msg(constants.strings("current_mode", mode=var.playlist.mode), text)
816 if parameter not in ["one-shot", "repeat", "random", "autoplay"]:
817 bot.send_msg(constants.strings('unknown_mode', mode=parameter), text)
819 var.db.set('playlist', 'playback_mode', parameter)
820 var.playlist = media.playlist.get_playlist(parameter, var.playlist)
821 log.info("command: playback mode changed to %s." % parameter)
822 bot.send_msg(constants.strings("change_mode", mode=var.playlist.mode,
823 user=bot.mumble.users[text.actor]['name']), text)
824 if parameter == "random":
829 def cmd_play_tags(bot, user, text, command, parameter):
831 bot.send_msg(constants.strings('bad_parameter', command=command), text)
834 msgs = [constants.strings('multiple_file_added') + "<ul>"]
837 tags = parameter.split(",")
838 tags = list(map(lambda t: t.strip(), tags))
839 music_wrappers = get_cached_wrappers_by_tags(bot, tags, user)
840 for music_wrapper in music_wrappers:
842 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
843 msgs.append("<li><b>{}</b> (<i>{}</i>)</li>".format(music_wrapper.item().title, ", ".join(music_wrapper.item().tags)))
847 var.playlist.extend(music_wrappers)
848 send_multi_lines(bot, msgs, None, "")
850 bot.send_msg(constants.strings("no_file"), text)
853 def cmd_add_tag(bot, user, text, command, parameter):
856 params = parameter.split()
859 tags = list(map(lambda t: t.strip(), params[1].split(",")))
860 elif len(params) == 1:
861 index = str(var.playlist.current_index + 1)
862 tags = list(map(lambda t: t.strip(), params[0].split(",")))
864 bot.send_msg(constants.strings('bad_parameter', command=command), text)
868 if index.isdigit() and 1 <= int(index) <= len(var.playlist):
869 var.playlist[int(index) - 1].add_tags(tags)
870 log.info("cmd: add tags %s to song %s" % (", ".join(tags),
871 var.playlist[int(index) - 1].format_debug_string()))
872 bot.send_msg(constants.strings("added_tags",
873 tags=", ".join(tags),
874 song=var.playlist[int(index) - 1].format_short_string()), text)
878 for item in var.playlist:
880 log.info("cmd: add tags %s to song %s" % (", ".join(tags),
881 item.format_debug_string()))
882 bot.send_msg(constants.strings("added_tags_to_all", tags=", ".join(tags)), text)
885 bot.send_msg(constants.strings('bad_parameter', command=command), text)
888 def cmd_remove_tag(bot, user, text, command, parameter):
891 params = parameter.split()
895 tags = list(map(lambda t: t.strip(), params[1].split(",")))
896 elif len(params) == 1:
897 index = str(var.playlist.current_index + 1)
898 tags = list(map(lambda t: t.strip(), params[0].split(",")))
900 bot.send_msg(constants.strings('bad_parameter', command=command), text)
904 if index.isdigit() and 1 <= int(index) <= len(var.playlist):
906 var.playlist[int(index) - 1].remove_tags(tags)
907 log.info("cmd: remove tags %s from song %s" % (", ".join(tags),
908 var.playlist[int(index) - 1].format_debug_string()))
909 bot.send_msg(constants.strings("removed_tags",
910 tags=", ".join(tags),
911 song=var.playlist[int(index) - 1].format_short_string()), text)
914 var.playlist[int(index) - 1].clear_tags()
915 log.info("cmd: clear tags from song %s" % (var.playlist[int(index) - 1].format_debug_string()))
916 bot.send_msg(constants.strings("cleared_tags",
917 song=var.playlist[int(index) - 1].format_short_string()), text)
922 for item in var.playlist:
923 item.remove_tags(tags)
924 log.info("cmd: remove tags %s from song %s" % (", ".join(tags),
925 item.format_debug_string()))
926 bot.send_msg(constants.strings("removed_tags_from_all", tags=", ".join(tags)), text)
929 for item in var.playlist:
931 log.info("cmd: clear tags from song %s" % (item.format_debug_string()))
932 bot.send_msg(constants.strings("cleared_tags_from_all"), text)
935 bot.send_msg(constants.strings('bad_parameter', command=command), text)
938 def cmd_find_tagged(bot, user, text, command, parameter):
939 global song_shortlist
942 bot.send_msg(constants.strings('bad_parameter', command=command), text)
945 msgs = [constants.strings('multiple_file_found') + "<ul>"]
948 tags = parameter.split(",")
949 tags = list(map(lambda t: t.strip(), tags))
951 music_dicts = var.music_db.query_music_by_tags(tags)
952 song_shortlist = music_dicts
953 items = dicts_to_items(bot, music_dicts)
955 for i, item in enumerate(items):
957 msgs.append("<li><b>{:d}</b> - <b>{}</b> (<i>{}</i>)</li>".format(i+1, item.title, ", ".join(item.tags)))
961 msgs.append(constants.strings("shortlist_instruction"))
962 send_multi_lines(bot, msgs, text, "")
964 bot.send_msg(constants.strings("no_file"), text)
967 def cmd_search_library(bot, user, text, command, parameter):
968 global song_shortlist
970 bot.send_msg(constants.strings('bad_parameter', command=command), text)
973 msgs = [constants.strings('multiple_file_found') + "<ul>"]
976 _keywords = parameter.split(" ")
982 music_dicts = var.music_db.query_music_by_keywords(keywords)
984 items = dicts_to_items(bot, music_dicts)
985 song_shortlist = music_dicts
989 if len(item.tags) > 0:
990 msgs.append("<li><b>{:d}</b> - [{}] <b>{}</b> (<i>{}</i>)</li>".format(count, item.display_type(), item.title, ", ".join(item.tags)))
992 msgs.append("<li><b>{:d}</b> - [{}] <b>{}</b> </li>".format(count, item.display_type(), item.title, ", ".join(item.tags)))
996 msgs.append(constants.strings("shortlist_instruction"))
997 send_multi_lines(bot, msgs, text, "")
999 bot.send_msg(constants.strings("no_file"), text)
1001 bot.send_msg(constants.strings("no_file"), text)
1004 def cmd_shortlist(bot, user, text, command, parameter):
1005 global song_shortlist, log
1007 indexes = [int(i) for i in parameter.split(" ")]
1009 bot.send_msg(constants.strings('bad_parameter', command=command), text)
1012 if len(indexes) > 1:
1013 msgs = [constants.strings('multiple_file_added') + "<ul>"]
1014 for index in indexes:
1015 if 1 <= index <= len(song_shortlist):
1016 kwargs = song_shortlist[index - 1]
1017 kwargs['user'] = user
1018 music_wrapper = get_cached_wrapper_from_scrap(bot, **kwargs)
1019 var.playlist.append(music_wrapper)
1020 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
1021 msgs.append("<li>[{}] <b>{}</b></li>".format(music_wrapper.item().type, music_wrapper.item().title))
1023 bot.send_msg(constants.strings('bad_parameter', command=command), text)
1026 msgs.append("</ul>")
1027 send_multi_lines(bot, msgs, None, "")
1029 elif len(indexes) == 1:
1031 if 1 <= index <= len(song_shortlist):
1032 kwargs = song_shortlist[index - 1]
1033 kwargs['user'] = user
1034 music_wrapper = get_cached_wrapper_from_scrap(bot, **kwargs)
1035 var.playlist.append(music_wrapper)
1036 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
1037 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()))
1040 bot.send_msg(constants.strings('bad_parameter', command=command), text)
1043 def cmd_delete_from_library(bot, user, text, command, parameter):
1044 global song_shortlist, log
1046 indexes = [int(i) for i in parameter.split(" ")]
1048 bot.send_msg(constants.strings('bad_parameter', command=command), text)
1051 if len(indexes) > 1:
1052 msgs = [constants.strings('multiple_file_added') + "<ul>"]
1054 for index in indexes:
1055 if 1 <= index <= len(song_shortlist):
1056 music_dict = song_shortlist[index - 1]
1057 if 'id' in music_dict:
1058 music_wrapper = get_cached_wrapper_by_id(bot, music_dict['id'], user)
1059 log.info("cmd: remove from library: " + music_wrapper.format_debug_string())
1060 msgs.append("<li>[{}] <b>{}</b></li>".format(music_wrapper.item().type, music_wrapper.item().title))
1061 var.playlist.remove_by_id(music_dict['id'])
1062 var.cache.free_and_delete(music_dict['id'])
1065 bot.send_msg(constants.strings('bad_parameter', command=command), text)
1069 bot.send_msg(constants.strings('bad_parameter', command=command), text)
1072 msgs.append("</ul>")
1073 send_multi_lines(bot, msgs, None, "")
1075 elif len(indexes) == 1:
1077 if 1 <= index <= len(song_shortlist):
1078 music_dict = song_shortlist[index - 1]
1079 if 'id' in music_dict:
1080 music_wrapper = get_cached_wrapper_by_id(bot, music_dict['id'], user)
1081 bot.send_msg(constants.strings('file_deleted', item=music_wrapper.format_song_string()), text)
1082 log.info("cmd: remove from library: " + music_wrapper.format_debug_string())
1083 var.playlist.remove_by_id(music_dict['id'])
1084 var.cache.free_and_delete(music_dict['id'])
1087 bot.send_msg(constants.strings('bad_parameter', command=command), text)
1090 def cmd_drop_database(bot, user, text, command, parameter):
1093 if bot.is_admin(user):
1095 var.db = SettingsDatabase(var.dbfile)
1096 var.music_db.drop_table()
1097 var.music_db = MusicDatabase(var.dbfile)
1098 log.info("command: database dropped.")
1099 bot.send_msg(constants.strings('database_dropped'), text)
1101 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
1104 def cmd_refresh_cache(bot, user, text, command, parameter):
1106 if bot.is_admin(user):
1107 var.cache.build_dir_cache(bot)
1108 log.info("command: Local file cache refreshed.")
1109 bot.send_msg(constants.strings('cache_refreshed'), text)
1111 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
1114 # Just for debug use
1115 def cmd_real_time_rms(bot, user, text, command, parameter):
1116 bot._display_rms = not bot._display_rms
1119 def cmd_loop_state(bot, user, text, command, parameter):
1120 print(bot._loop_status)
1123 def cmd_item(bot, user, text, command, parameter):
1124 print(bot.wait_for_downloading)
1125 print(var.playlist.current_item().to_dict())