4 import pymumble.pymumble_py3 as pymumble
10 import variables as var
11 from librb import radiobrowser
12 from database import SettingsDatabase, MusicDatabase
13 from media.item import item_builders, item_loaders, item_id_generators, dict_to_item, dicts_to_items
14 from media.playlist import get_item_wrapper, get_item_wrapper_by_id, get_item_wrappers_by_tags
15 from media.file import FileItem
16 from media.url_from_playlist import PlaylistURLItem, get_playlist_info
17 from media.url import URLItem
18 from media.radio import RadioItem
20 log = logging.getLogger("bot")
22 def register_all_commands(bot):
23 bot.register_command(constants.commands('joinme'), cmd_joinme)
24 bot.register_command(constants.commands('user_ban'), cmd_user_ban)
25 bot.register_command(constants.commands('user_unban'), cmd_user_unban)
26 bot.register_command(constants.commands('url_ban'), cmd_url_ban)
27 bot.register_command(constants.commands('url_unban'), cmd_url_unban)
28 bot.register_command(constants.commands('play'), cmd_play)
29 bot.register_command(constants.commands('pause'), cmd_pause)
30 bot.register_command(constants.commands('play_file'), cmd_play_file)
31 bot.register_command(constants.commands('play_file_match'), cmd_play_file_match)
32 bot.register_command(constants.commands('play_url'), cmd_play_url)
33 bot.register_command(constants.commands('play_playlist'), cmd_play_playlist)
34 bot.register_command(constants.commands('play_radio'), cmd_play_radio)
35 bot.register_command(constants.commands('play_tag'), cmd_play_tags)
36 bot.register_command(constants.commands('rb_query'), cmd_rb_query)
37 bot.register_command(constants.commands('rb_play'), cmd_rb_play)
38 bot.register_command(constants.commands('yt_search'), cmd_yt_search)
39 bot.register_command(constants.commands('yt_play'), cmd_yt_play)
40 bot.register_command(constants.commands('help'), cmd_help)
41 bot.register_command(constants.commands('stop'), cmd_stop)
42 bot.register_command(constants.commands('clear'), cmd_clear)
43 bot.register_command(constants.commands('kill'), cmd_kill)
44 bot.register_command(constants.commands('update'), cmd_update)
45 bot.register_command(constants.commands('stop_and_getout'), cmd_stop_and_getout)
46 bot.register_command(constants.commands('volume'), cmd_volume)
47 bot.register_command(constants.commands('ducking'), cmd_ducking)
48 bot.register_command(constants.commands('ducking_threshold'), cmd_ducking_threshold)
49 bot.register_command(constants.commands('ducking_volume'), cmd_ducking_volume)
50 bot.register_command(constants.commands('current_music'), cmd_current_music)
51 bot.register_command(constants.commands('skip'), cmd_skip)
52 bot.register_command(constants.commands('last'), cmd_last)
53 bot.register_command(constants.commands('remove'), cmd_remove)
54 bot.register_command(constants.commands('list_file'), cmd_list_file)
55 bot.register_command(constants.commands('queue'), cmd_queue)
56 bot.register_command(constants.commands('random'), cmd_random)
57 bot.register_command(constants.commands('repeat'), cmd_repeat)
58 bot.register_command(constants.commands('mode'), cmd_mode)
59 bot.register_command(constants.commands('add_tag'), cmd_add_tag)
60 bot.register_command(constants.commands('remove_tag'), cmd_remove_tag)
61 bot.register_command(constants.commands('find_tagged'), cmd_find_tagged)
62 bot.register_command(constants.commands('search'), cmd_search_library)
63 bot.register_command(constants.commands('add_from_shortlist'), cmd_shortlist)
64 bot.register_command(constants.commands('drop_database'), cmd_drop_database, True)
65 bot.register_command(constants.commands('rescan'), cmd_refresh_cache, True)
68 bot.register_command('rtrms', cmd_real_time_rms, True)
69 bot.register_command('loop', cmd_loop_state, True)
70 bot.register_command('item', cmd_item, True)
72 def send_multi_lines(bot, lines, text, linebreak="<br />"):
80 if (len(msg) + len(newline)) > (bot.mumble.get_max_message_length() - 4) != 0: # 4 == len("<br>")
81 bot.send_msg(msg, text)
85 bot.send_msg(msg, text)
87 # ---------------- Variables -----------------
91 # ---------------- Commands ------------------
94 def cmd_joinme(bot, user, text, command, parameter):
97 bot.mumble.users.myself.move_in(
98 bot.mumble.users[text.actor]['channel_id'], token=parameter)
101 def cmd_user_ban(bot, user, text, command, parameter):
104 if bot.is_admin(user):
106 bot.mumble.users[text.actor].send_text_message(util.user_ban(parameter))
108 bot.mumble.users[text.actor].send_text_message(util.get_user_ban())
110 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
114 def cmd_user_unban(bot, user, text, command, parameter):
117 if bot.is_admin(user):
119 bot.mumble.users[text.actor].send_text_message(util.user_unban(parameter))
121 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
125 def cmd_url_ban(bot, user, text, command, parameter):
128 if bot.is_admin(user):
130 bot.mumble.users[text.actor].send_text_message(util.url_ban(util.get_url_from_input(parameter)))
132 bot.mumble.users[text.actor].send_text_message(util.get_url_ban())
134 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
138 def cmd_url_unban(bot, user, text, command, parameter):
141 if bot.is_admin(user):
143 bot.mumble.users[text.actor].send_text_message(util.url_unban(util.get_url_from_input(parameter)))
145 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
149 def cmd_play(bot, user, text, command, parameter):
152 if len(var.playlist) > 0:
154 if parameter.isdigit() and 1 <= int(parameter) <= len(var.playlist):
155 var.playlist.point_to(int(parameter) - 1 - 1) # First "-1" transfer 12345 to 01234, second "-1"
156 # point to the previous item. the loop will next to
160 bot.send_msg(constants.strings('invalid_index', index=parameter), text)
165 bot.send_msg(var.playlist.current_item().format_current_playing(), text)
168 bot.send_msg(constants.strings('queue_empty'), text)
171 def cmd_pause(bot, user, text, command, parameter):
175 bot.send_msg(constants.strings('paused'))
178 def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=False):
179 global log, song_shortlist
181 # if parameter is {index}
182 if parameter.isdigit():
183 files = var.cache.files
184 if int(parameter) < len(files):
185 music_wrapper = get_item_wrapper_by_id(bot, var.cache.file_id_lookup[files[int(parameter)]], user)
186 var.playlist.append(music_wrapper)
187 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
188 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
191 # if parameter is {path}
193 # sanitize "../" and so on
194 # path = os.path.abspath(os.path.join(var.music_folder, parameter))
195 # if not path.startswith(os.path.abspath(var.music_folder)):
196 # bot.send_msg(constants.strings('no_file'), text)
199 if parameter in var.cache.files:
200 music_wrapper = get_item_wrapper(bot, type='file', path=parameter, user=user)
201 var.playlist.append(music_wrapper)
202 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
203 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
206 # if parameter is {folder}
207 files = var.cache.dir.get_files(parameter)
209 msgs = [constants.strings('multiple_file_added')]
214 music_wrapper = get_item_wrapper_by_id(bot, var.cache.file_id_lookup[file], user)
215 var.playlist.append(music_wrapper)
216 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
217 msgs.append("{} ({})".format(music_wrapper.item().title, music_wrapper.item().path))
220 send_multi_lines(bot, msgs, text)
224 # try to do a partial match
225 files = var.cache.files
226 matches = [ file for file in files if parameter.lower() in file.lower()]
227 if len(matches) == 1:
229 music_wrapper = get_item_wrapper_by_id(bot, var.cache.file_id_lookup[file], user)
230 var.playlist.append(music_wrapper)
231 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
232 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
234 elif len(matches) > 1:
235 msgs = [ constants.strings('multiple_matches') ]
237 for index, match in enumerate(matches):
238 id = var.cache.file_id_lookup[match]
239 music_dict = var.music_db.query_music_by_id(id)
240 item = dict_to_item(bot, music_dict)
242 song_shortlist.append(music_dict)
244 msgs.append("<b>{:d}</b> - <b>{:s}</b> ({:s})".format(
245 index + 1, item.title, match))
246 send_multi_lines(bot, msgs, text)
249 if do_not_refresh_cache:
250 bot.send_msg(constants.strings("no_file"), text)
252 var.cache.build_dir_cache(bot)
253 cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=True)
256 def cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cache=False):
259 music_folder = var.music_folder
261 files = var.cache.files
262 msgs = [ constants.strings('multiple_file_added') + "<ul>"]
267 match = re.search(parameter, file)
268 if match and match[0]:
270 music_wrapper = get_item_wrapper_by_id(bot, var.cache.file_id_lookup[file], user)
271 music_wrappers.append(music_wrapper)
272 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
273 msgs.append("<li><b>{}</b> ({})</li>".format(music_wrapper.item().title,
274 file[:match.span()[0]]
275 + "<b style='color:pink'>"
276 + file[match.span()[0]: match.span()[1]]
278 + file[match.span()[1]:]
283 var.playlist.extend(music_wrappers)
284 send_multi_lines(bot, msgs, text, "")
286 if do_not_refresh_cache:
287 bot.send_msg(constants.strings("no_file"), text)
289 var.cache.build_dir_cache(bot)
290 cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cache=True)
292 except re.error as e:
293 msg = constants.strings('wrong_pattern', error=str(e))
294 bot.send_msg(msg, text)
296 bot.send_msg(constants.strings('bad_parameter', command=command))
299 def cmd_play_url(bot, user, text, command, parameter):
302 url = util.get_url_from_input(parameter)
304 music_wrapper = get_item_wrapper(bot, type='url', url=url, user=user)
305 var.playlist.append(music_wrapper)
307 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
308 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_short_string()), text)
309 if len(var.playlist) == 2:
310 # If I am the second item on the playlist. (I am the next one!)
311 bot.async_download_next()
313 bot.send_msg(constants.strings('bad_parameter', command=command))
317 def cmd_play_playlist(bot, user, text, command, parameter):
320 offset = 0 # if you want to start the playlist at a specific index
322 offset = int(parameter.split(" ")[-1])
326 url = util.get_url_from_input(parameter)
327 log.debug("cmd: fetching media info from playlist url %s" % url)
328 items = get_playlist_info(url=url, start_index=offset, user=user)
330 items = var.playlist.extend(list(map(
331 lambda item: get_item_wrapper(bot, **item), items)))
333 log.info("cmd: add to playlist: " + music.format_debug_string())
335 bot.send_msg(constants.strings("playlist_fetching_failed"), text)
338 def cmd_play_radio(bot, user, text, command, parameter):
342 all_radio = var.config.items('radio')
343 msg = constants.strings('preconfigurated_radio')
346 if len(i[1].split(maxsplit=1)) == 2:
347 comment = " - " + i[1].split(maxsplit=1)[1]
348 msg += "<br />" + i[0] + comment
349 bot.send_msg(msg, text)
351 if var.config.has_option('radio', parameter):
352 parameter = var.config.get('radio', parameter)
353 parameter = parameter.split()[0]
354 url = util.get_url_from_input(parameter)
356 music_wrapper = get_item_wrapper(bot, type='radio', url=url)
358 var.playlist.append(music_wrapper)
359 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
361 bot.send_msg(constants.strings('bad_url'))
364 def cmd_rb_query(bot, user, text, command, parameter):
367 log.info('cmd: Querying radio stations')
369 log.debug('rbquery without parameter')
370 msg = constants.strings('rb_query_empty')
371 bot.send_msg(msg, text)
373 log.debug('cmd: Found query parameter: ' + parameter)
374 # bot.send_msg('Searching for stations - this may take some seconds...', text)
375 rb_stations = radiobrowser.getstations_byname(parameter)
376 msg = constants.strings('rb_query_result')
377 msg += '\n<table><tr><th>!rbplay ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th></tr>'
379 log.debug('cmd: No matches found for rbquery ' + parameter)
380 bot.send_msg('Radio-Browser found no matches for ' + parameter, text)
382 for s in rb_stations:
384 stationname = s['stationname']
385 country = s['country']
387 bitrate = s['bitrate']
389 # msg += f'<tr><td>{stationid}</td><td>{stationname}</td><td>{genre}</td><td>{codec}/{bitrate}</td><td>{country}</td></tr>'
390 msg += '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s/%s</td><td>%s</td></tr>' % (
391 stationid, stationname, genre, codec, bitrate, country)
393 # Full message as html table
395 bot.send_msg(msg, text)
396 # Shorten message if message too long (stage I)
398 log.debug('Result too long stage I')
399 msg = constants.strings('rb_query_result') + ' (shortened L1)'
400 msg += '\n<table><tr><th>!rbplay ID</th><th>Station Name</th></tr>'
401 for s in rb_stations:
403 stationname = s['stationname']
404 # msg += f'<tr><td>{stationid}</td><td>{stationname}</td>'
405 msg += '<tr><td>%s</td><td>%s</td>' % (stationid, stationname)
408 bot.send_msg(msg, text)
409 # Shorten message if message too long (stage II)
411 log.debug('Result too long stage II')
412 msg = constants.strings('rb_query_result') + ' (shortened L2)'
413 msg += '!rbplay ID - Station Name'
414 for s in rb_stations:
416 stationname = s['stationname'][:12]
417 # msg += f'{stationid} - {stationname}'
418 msg += '%s - %s' % (stationid, stationname)
420 bot.send_msg(msg, text)
421 # Message still too long
423 bot.send_msg('Query result too long to post (> 5000 characters), please try another query.',
427 def cmd_rb_play(bot, user, text, command, parameter):
430 log.debug('cmd: Play a station by ID')
432 log.debug('rbplay without parameter')
433 msg = constants.strings('rb_play_empty')
434 bot.send_msg(msg, text)
436 log.debug('cmd: Retreiving url for station ID ' + parameter)
437 rstation = radiobrowser.getstationname_byid(parameter)
438 stationname = rstation[0]['name']
439 country = rstation[0]['country']
440 codec = rstation[0]['codec']
441 bitrate = rstation[0]['bitrate']
442 genre = rstation[0]['tags']
443 homepage = rstation[0]['homepage']
444 msg = 'Radio station added to playlist:'
445 # msg += '<table><tr><th>ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th><th>Homepage</th></tr>' + \
446 # f'<tr><td>{parameter}</td><td>{stationname}</td><td>{genre}</td><td>{codec}/{bitrate}</td><td>{country}</td><td>{homepage}</td></tr></table>'
447 msg += '<table><tr><th>ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th><th>Homepage</th></tr>' + \
448 '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s/%s</td><td>%s</td><td>%s</td></tr></table>' \
449 % (parameter, stationname, genre, codec, bitrate, country, homepage)
450 log.debug('cmd: Added station to playlist %s' % stationname)
451 bot.send_msg(msg, text)
452 url = radiobrowser.geturl_byid(parameter)
454 log.info('cmd: Found url: ' + url)
455 music_wrapper = get_item_wrapper(bot, type='radio', url=url, name=stationname, user=user)
456 var.playlist.append(music_wrapper)
457 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
458 bot.async_download_next()
460 log.info('cmd: No playable url found.')
461 msg += "No playable url found for this station, please try another station."
462 bot.send_msg(msg, text)
465 yt_last_page = 0 # TODO: if we keep adding global variables, we need to consider sealing all commands up into classes.
467 def cmd_yt_search(bot, user, text, command, parameter):
468 global log, yt_last_result, yt_last_page, song_shortlist
473 if parameter.startswith("-n"):
475 if len(yt_last_result) > yt_last_page * item_per_page:
476 song_shortlist = [{'type': 'url',
477 'url': "https://www.youtube.com/watch?v=" + result[0],
479 } for result in yt_last_result[yt_last_page * item_per_page: item_per_page]]
480 msg = _yt_format_result(yt_last_result, yt_last_page * item_per_page, item_per_page)
481 bot.send_msg(constants.strings('yt_result', result_table=msg), text)
483 bot.send_msg(constants.strings('yt_no_more'))
487 results = util.youtube_search(parameter)
489 yt_last_result = results
491 song_shortlist = [{'type': 'url', 'url': "https://www.youtube.com/watch?v=" + result[0]}
492 for result in results[0: item_per_page]]
493 msg = _yt_format_result(results, 0, item_per_page)
494 bot.send_msg(constants.strings('yt_result', result_table=msg), text)
496 bot.send_msg(constants.strings('yt_query_error'))
498 bot.send_msg(constants.strings('bad_parameter', command=command), text)
500 def _yt_format_result(results, start, count):
501 msg = '<table><tr><th width="10%">Index</th><th>Title</th><th width="20%">Uploader</th></tr>'
502 for index, item in enumerate(results[start:start+count]):
503 msg += '<tr><td>{index:d}</td><td>{title}</td><td>{uploader}</td></tr>'.format(
504 index=index + 1, title=item[1], uploader=item[2])
510 def cmd_yt_play(bot, user, text, command, parameter):
511 global log, yt_last_result, yt_last_page
514 results = util.youtube_search(parameter)
516 yt_last_result = results
518 url = "https://www.youtube.com/watch?v=" + yt_last_result[0][0]
519 cmd_play_url(bot, user, text, command, url)
521 bot.send_msg(constants.strings('yt_query_error'))
523 bot.send_msg(constants.strings('bad_parameter', command=command), text)
526 def cmd_help(bot, user, text, command, parameter):
529 bot.send_msg(constants.strings('help'), text)
530 if bot.is_admin(user):
531 bot.send_msg(constants.strings('admin_help'), text)
534 def cmd_stop(bot, user, text, command, parameter):
538 bot.send_msg(constants.strings('stopped'), text)
541 def cmd_clear(bot, user, text, command, parameter):
545 bot.send_msg(constants.strings('cleared'), text)
548 def cmd_kill(bot, user, text, command, parameter):
551 if bot.is_admin(user):
555 bot.mumble.users[text.actor].send_text_message(
556 constants.strings('not_admin'))
559 def cmd_update(bot, user, text, command, parameter):
562 if bot.is_admin(user):
563 bot.mumble.users[text.actor].send_text_message(
564 constants.strings('start_updating'))
565 msg = util.update(bot.version)
566 bot.mumble.users[text.actor].send_text_message(msg)
568 bot.mumble.users[text.actor].send_text_message(
569 constants.strings('not_admin'))
572 def cmd_stop_and_getout(bot, user, text, command, parameter):
577 bot.mumble.channels.find_by_name(bot.channel).move_in()
580 def cmd_volume(bot, user, text, command, parameter):
583 # The volume is a percentage
584 if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
585 bot.volume_set = float(float(parameter) / 100)
586 bot.send_msg(constants.strings('change_volume',
587 volume=int(bot.volume_set * 100), user=bot.mumble.users[text.actor]['name']), text)
588 var.db.set('bot', 'volume', str(bot.volume_set))
589 log.info('cmd: volume set to %d' % (bot.volume_set * 100))
591 bot.send_msg(constants.strings('current_volume', volume=int(bot.volume_set * 100)), text)
594 def cmd_ducking(bot, user, text, command, parameter):
597 if parameter == "" or parameter == "on":
598 bot.is_ducking = True
599 var.db.set('bot', 'ducking', True)
600 bot.ducking_volume = var.config.getfloat("bot", "ducking_volume", fallback=0.05)
601 bot.ducking_threshold = var.config.getint("bot", "ducking_threshold", fallback=5000)
602 bot.mumble.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED,
603 bot.ducking_sound_received)
604 bot.mumble.set_receive_sound(True)
605 log.info('cmd: ducking is on')
607 bot.send_msg(msg, text)
608 elif parameter == "off":
609 bot.is_ducking = False
610 bot.mumble.set_receive_sound(False)
611 var.db.set('bot', 'ducking', False)
613 log.info('cmd: ducking is off')
614 bot.send_msg(msg, text)
617 def cmd_ducking_threshold(bot, user, text, command, parameter):
620 if parameter and parameter.isdigit():
621 bot.ducking_threshold = int(parameter)
622 var.db.set('bot', 'ducking_threshold', str(bot.ducking_threshold))
623 msg = "Ducking threshold set to %d." % bot.ducking_threshold
624 bot.send_msg(msg, text)
626 msg = "Current ducking threshold is %d." % bot.ducking_threshold
627 bot.send_msg(msg, text)
630 def cmd_ducking_volume(bot, user, text, command, parameter):
633 # The volume is a percentage
634 if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
635 bot.ducking_volume = float(float(parameter) / 100)
636 bot.send_msg(constants.strings('change_ducking_volume',
637 volume=int(bot.ducking_volume * 100), user=bot.mumble.users[text.actor]['name']), text)
638 # var.db.set('bot', 'volume', str(bot.volume_set))
639 var.db.set('bot', 'ducking_volume', str(bot.ducking_volume))
640 log.info('cmd: volume on ducking set to %d' % (bot.ducking_volume * 100))
642 bot.send_msg(constants.strings('current_ducking_volume', volume=int(bot.ducking_volume * 100)), text)
645 def cmd_current_music(bot, user, text, command, parameter):
649 if len(var.playlist) > 0:
650 bot.send_msg(var.playlist.current_item().format_current_playing())
652 reply = constants.strings('not_playing')
653 bot.send_msg(reply, text)
656 def cmd_skip(bot, user, text, command, parameter):
661 if len(var.playlist) == 0:
662 bot.send_msg(constants.strings('queue_empty'), text)
665 def cmd_last(bot, user, text, command, parameter):
668 if len(var.playlist) > 0:
670 var.playlist.point_to(len(var.playlist) - 1)
672 bot.send_msg(constants.strings('queue_empty'), text)
675 def cmd_remove(bot, user, text, command, parameter):
678 # Allow to remove specific music into the queue with a number
679 if parameter and parameter.isdigit() and int(parameter) > 0 \
680 and int(parameter) <= len(var.playlist):
682 index = int(parameter) - 1
685 if index == var.playlist.current_index:
686 removed = var.playlist.remove(index)
688 if index < len(var.playlist):
691 var.playlist.current_index -= 1
692 # then the bot will move to next item
694 else: # if item deleted is the last item of the queue
695 var.playlist.current_index -= 1
699 removed = var.playlist.remove(index)
701 bot.send_msg(constants.strings('removing_item',
702 item=removed.format_short_string()), text)
704 log.info("cmd: delete from playlist: " + removed.format_debug_string())
706 bot.send_msg(constants.strings('bad_parameter', command=command))
709 def cmd_list_file(bot, user, text, command, parameter):
712 files = var.cache.files
713 msgs = [ "<br> <b>Files available:</b>" if not parameter else "<br> <b>Matched files:</b>" ]
716 for index, file in enumerate(files):
718 match = re.search(parameter, file)
723 msgs.append("<b>{:0>3d}</b> - {:s}".format(index, file))
726 send_multi_lines(bot, msgs, text)
728 bot.send_msg(constants.strings('no_file'), text)
730 except re.error as e:
731 msg = constants.strings('wrong_pattern', error=str(e))
732 bot.send_msg(msg, text)
735 def cmd_queue(bot, user, text, command, parameter):
738 if len(var.playlist) == 0:
739 msg = constants.strings('queue_empty')
740 bot.send_msg(msg, text)
742 msgs = [ constants.strings('queue_contents')]
743 for i, music in enumerate(var.playlist):
746 if len(music.item().tags) > 0:
747 tags = "<sup>{}</sup>".format(", ".join(music.item().tags))
748 if i == var.playlist.current_index:
749 newline = "<b style='color:orange'>{} ({}) {} </b> {}".format(i + 1, music.display_type(),
750 music.format_short_string(), tags)
752 newline = '<b>{}</b> ({}) {} {}'.format(i + 1, music.display_type(),
753 music.format_short_string(), tags)
757 send_multi_lines(bot, msgs, text)
759 def cmd_random(bot, user, text, command, parameter):
763 var.playlist.randomize()
765 def cmd_repeat(bot, user, text, command, parameter):
769 if parameter and parameter.isdigit():
770 repeat = int(parameter)
772 music = var.playlist.current_item()
773 for _ in range(repeat):
775 var.playlist.current_index + 1,
778 log.info("bot: add to playlist: " + music.format_debug_string)
780 bot.send_msg(constants.strings("repeat", song=music.format_song_string(), n=str(repeat)), text)
782 def cmd_mode(bot, user, text, command, parameter):
786 bot.send_msg(constants.strings("current_mode", mode=var.playlist.mode), text)
788 if not parameter in ["one-shot", "repeat", "random", "autoplay"]:
789 bot.send_msg(constants.strings('unknown_mode', mode=parameter), text)
791 var.db.set('playlist', 'playback_mode', parameter)
792 var.playlist = media.playlist.get_playlist(parameter, var.playlist)
793 log.info("command: playback mode changed to %s." % parameter)
794 bot.send_msg(constants.strings("change_mode", mode=var.playlist.mode,
795 user=bot.mumble.users[text.actor]['name']), text)
796 if parameter == "random":
800 def cmd_play_tags(bot, user, text, command, parameter):
802 bot.send_msg(constants.strings('bad_parameter', command=command))
805 msgs = [constants.strings('multiple_file_added') + "<ul>"]
808 tags = parameter.split(",")
809 tags = list(map(lambda t: t.strip(), tags))
810 music_wrappers = get_item_wrappers_by_tags(bot, tags, user)
811 for music_wrapper in music_wrappers:
813 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
814 msgs.append("<li><b>{}</b> (<i>{}</i>)</li>".format(music_wrapper.item().title, ", ".join(music_wrapper.item().tags)))
819 var.playlist.extend(music_wrappers)
820 send_multi_lines(bot, msgs, text, "")
822 bot.send_msg(constants.strings("no_file"), text)
825 def cmd_add_tag(bot, user, text, command, parameter):
828 params = parameter.split()
831 tags = list(map(lambda t: t.strip(), params[1].split(",")))
833 if index.isdigit() and 1 <= int(index) <= len(var.playlist):
834 var.playlist[int(index) - 1].add_tags(tags)
835 log.info("cmd: add tags %s to song %s" % (", ".join(tags),
836 var.playlist[int(index) - 1].format_debug_string()))
837 bot.send_msg(constants.strings("added_tags",
838 tags=", ".join(tags),
839 song=var.playlist[int(index) - 1].format_short_string()), text)
841 for item in var.playlist:
843 log.info("cmd: add tags %s to song %s" % (", ".join(tags),
844 item.format_debug_string()))
845 bot.send_msg(constants.strings("added_tags_to_all", tags=", ".join(tags)), text)
847 bot.send_msg(constants.strings('bad_parameter', command=command), text)
850 def cmd_remove_tag(bot, user, text, command, parameter):
853 params = parameter.split()
854 if len(params) == 2 and params[1]:
857 if index.isdigit() and 1 <= int(index) <= len(var.playlist):
859 tags = list(map(lambda t: t.strip(), params[1].split(",")))
860 var.playlist[int(index) - 1].remove_tags(tags)
861 log.info("cmd: remove tags %s from song %s" % (", ".join(tags),
862 var.playlist[int(index) - 1].format_debug_string()))
863 bot.send_msg(constants.strings("removed_tags",
864 tags=", ".join(tags),
865 song=var.playlist[int(index) - 1].format_short_string()), text)
868 var.playlist[int(index) - 1].clear_tags()
869 log.info("cmd: clear tags from song %s" % (var.playlist[int(index) - 1].format_debug_string()))
870 bot.send_msg(constants.strings("cleared_tags",
871 song=var.playlist[int(index) - 1].format_short_string()), text)
876 tags = list(map(lambda t: t.strip(), params[1].split(",")))
877 for item in var.playlist:
878 item.remove_tags(tags)
879 log.info("cmd: remove tags %s from song %s" % (", ".join(tags),
880 item.format_debug_string()))
881 bot.send_msg(constants.strings("removed_tags_from_all", tags=", ".join(tags)), text)
884 for item in var.playlist:
886 log.info("cmd: clear tags from song %s" % (item.format_debug_string()))
887 bot.send_msg(constants.strings("cleared_tags_from_all"), text)
890 bot.send_msg(constants.strings('bad_parameter', command=command), text)
892 def cmd_find_tagged(bot, user, text, command, parameter):
893 global song_shortlist
896 bot.send_msg(constants.strings('bad_parameter', command=command))
899 msgs = [constants.strings('multiple_file_found') + "<ul>"]
902 tags = parameter.split(",")
903 tags = list(map(lambda t: t.strip(), tags))
905 music_dicts = var.music_db.query_music_by_tags(tags)
906 song_shortlist = music_dicts
907 items = dicts_to_items(bot, music_dicts)
909 for i, item in enumerate(items):
911 msgs.append("<li><b>{:d}</b> - <b>{}</b> (<i>{}</i>)</li>".format(i+1, item.title, ", ".join(item.tags)))
915 msgs.append(constants.strings("shortlist_instruction"))
916 send_multi_lines(bot, msgs, text, "")
918 bot.send_msg(constants.strings("no_file"), text)
920 def cmd_search_library(bot, user, text, command, parameter):
921 global song_shortlist
923 bot.send_msg(constants.strings('bad_parameter', command=command))
926 msgs = [constants.strings('multiple_file_found') + "<ul>"]
929 _keywords = parameter.split(" ")
935 music_dicts = var.music_db.query_music_by_keywords(keywords)
936 items = dicts_to_items(bot, music_dicts)
937 song_shortlist = music_dicts
941 if len(item.tags) > 0:
942 msgs.append("<li><b>{:d}</b> - [{}] <b>{}</b> (<i>{}</i>)</li>".format(count, item.display_type(), item.title, ", ".join(item.tags)))
944 msgs.append("<li><b>{:d}</b> - [{}] <b>{}</b> </li>".format(count, item.display_type(), item.title, ", ".join(item.tags)))
948 msgs.append(constants.strings("shortlist_instruction"))
949 send_multi_lines(bot, msgs, text, "")
951 bot.send_msg(constants.strings("no_file"), text)
954 def cmd_shortlist(bot, user, text, command, parameter):
955 global song_shortlist
958 indexes = [ int(i) for i in parameter.split(" ") ]
960 bot.send_msg(constants.strings('bad_parameter', command=command), text)
964 msgs = [constants.strings('multiple_file_added') + "<ul>"]
965 for index in indexes:
966 if 1 <= index <= len(song_shortlist):
967 kwargs = song_shortlist[index - 1]
968 kwargs['user'] = user
969 music_wrapper = get_item_wrapper(bot, **kwargs)
970 var.playlist.append(music_wrapper)
971 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
972 msgs.append("<li><b>{}</b></li>".format(music_wrapper.item().title))
975 bot.send_msg(constants.strings('bad_parameter', command=command), text)
979 send_multi_lines(bot, msgs, text, "")
982 elif len(indexes) == 1:
984 if 1 <= index <= len(song_shortlist):
985 kwargs = song_shortlist[index - 1]
986 kwargs['user'] = user
987 music_wrapper = get_item_wrapper(bot, **kwargs)
988 var.playlist.append(music_wrapper)
989 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
990 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
994 bot.send_msg(constants.strings('bad_parameter', command=command), text)
997 def cmd_drop_database(bot, user, text, command, parameter):
1000 if bot.is_admin(user):
1002 var.db = SettingsDatabase(var.dbfile)
1003 var.music_db.drop_table()
1004 var.music_db = MusicDatabase(var.dbfile)
1005 log.info("command: database dropped.")
1006 bot.send_msg(constants.strings('database_dropped'), text)
1008 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
1010 def cmd_refresh_cache(bot, user, text, command, parameter):
1012 if bot.is_admin(user):
1013 var.cache.build_dir_cache(bot)
1014 log.info("command: Local file cache refreshed.")
1015 bot.send_msg(constants.strings('cache_refreshed'), text)
1017 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
1019 # Just for debug use
1020 def cmd_real_time_rms(bot, user, text, command, parameter):
1021 bot._display_rms = not bot._display_rms
1023 def cmd_loop_state(bot, user, text, command, parameter):
1024 print(bot._loop_status)
1026 def cmd_item(bot, user, text, command, parameter):
1027 print(bot.wait_for_downloading)
1028 print(var.playlist.current_item().to_dict())