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.playlist import get_item_wrapper, get_item_wrapper_by_id, get_item_wrappers_by_tags
14 from media.file import FileItem
15 from media.url_from_playlist import PlaylistURLItem, get_playlist_info
16 from media.url import URLItem
17 from media.radio import RadioItem
19 log = logging.getLogger("bot")
21 def register_all_commands(bot):
22 bot.register_command(constants.commands('joinme'), cmd_joinme)
23 bot.register_command(constants.commands('user_ban'), cmd_user_ban)
24 bot.register_command(constants.commands('user_unban'), cmd_user_unban)
25 bot.register_command(constants.commands('url_ban'), cmd_url_ban)
26 bot.register_command(constants.commands('url_unban'), cmd_url_unban)
27 bot.register_command(constants.commands('play'), cmd_play)
28 bot.register_command(constants.commands('pause'), cmd_pause)
29 bot.register_command(constants.commands('play_file'), cmd_play_file)
30 bot.register_command(constants.commands('play_file_match'), cmd_play_file_match)
31 bot.register_command(constants.commands('play_url'), cmd_play_url)
32 bot.register_command(constants.commands('play_playlist'), cmd_play_playlist)
33 bot.register_command(constants.commands('play_radio'), cmd_play_radio)
34 bot.register_command(constants.commands('play_tag'), cmd_play_tags)
35 bot.register_command(constants.commands('rb_query'), cmd_rb_query)
36 bot.register_command(constants.commands('rb_play'), cmd_rb_play)
37 bot.register_command(constants.commands('yt_search'), cmd_yt_search)
38 bot.register_command(constants.commands('yt_play'), cmd_yt_play)
39 bot.register_command(constants.commands('help'), cmd_help)
40 bot.register_command(constants.commands('stop'), cmd_stop)
41 bot.register_command(constants.commands('clear'), cmd_clear)
42 bot.register_command(constants.commands('kill'), cmd_kill)
43 bot.register_command(constants.commands('update'), cmd_update)
44 bot.register_command(constants.commands('stop_and_getout'), cmd_stop_and_getout)
45 bot.register_command(constants.commands('volume'), cmd_volume)
46 bot.register_command(constants.commands('ducking'), cmd_ducking)
47 bot.register_command(constants.commands('ducking_threshold'), cmd_ducking_threshold)
48 bot.register_command(constants.commands('ducking_volume'), cmd_ducking_volume)
49 bot.register_command(constants.commands('current_music'), cmd_current_music)
50 bot.register_command(constants.commands('skip'), cmd_skip)
51 bot.register_command(constants.commands('last'), cmd_last)
52 bot.register_command(constants.commands('remove'), cmd_remove)
53 bot.register_command(constants.commands('list_file'), cmd_list_file)
54 bot.register_command(constants.commands('queue'), cmd_queue)
55 bot.register_command(constants.commands('random'), cmd_random)
56 bot.register_command(constants.commands('repeat'), cmd_repeat)
57 bot.register_command(constants.commands('mode'), cmd_mode)
58 bot.register_command(constants.commands('add_tag'), cmd_add_tag)
59 bot.register_command(constants.commands('remove_tag'), cmd_remove_tag)
60 bot.register_command(constants.commands('find_tagged'), cmd_find_tagged)
61 bot.register_command(constants.commands('drop_database'), cmd_drop_database, True)
62 bot.register_command(constants.commands('rescan'), cmd_refresh_cache, True)
65 bot.register_command('rtrms', cmd_real_time_rms, True)
66 bot.register_command('loop', cmd_loop_state, True)
67 bot.register_command('item', cmd_item, True)
69 def send_multi_lines(bot, lines, text, linebreak="<br />"):
77 if (len(msg) + len(newline)) > (bot.mumble.get_max_message_length() - 4) != 0: # 4 == len("<br>")
78 bot.send_msg(msg, text)
82 bot.send_msg(msg, text)
84 # ---------------- Commands ------------------
87 def cmd_joinme(bot, user, text, command, parameter):
90 bot.mumble.users.myself.move_in(
91 bot.mumble.users[text.actor]['channel_id'], token=parameter)
94 def cmd_user_ban(bot, user, text, command, parameter):
97 if bot.is_admin(user):
99 bot.mumble.users[text.actor].send_text_message(util.user_ban(parameter))
101 bot.mumble.users[text.actor].send_text_message(util.get_user_ban())
103 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
107 def cmd_user_unban(bot, user, text, command, parameter):
110 if bot.is_admin(user):
112 bot.mumble.users[text.actor].send_text_message(util.user_unban(parameter))
114 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
118 def cmd_url_ban(bot, user, text, command, parameter):
121 if bot.is_admin(user):
123 bot.mumble.users[text.actor].send_text_message(util.url_ban(util.get_url_from_input(parameter)))
125 bot.mumble.users[text.actor].send_text_message(util.get_url_ban())
127 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
131 def cmd_url_unban(bot, user, text, command, parameter):
134 if bot.is_admin(user):
136 bot.mumble.users[text.actor].send_text_message(util.url_unban(util.get_url_from_input(parameter)))
138 bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
142 def cmd_play(bot, user, text, command, parameter):
145 if len(var.playlist) > 0:
147 if parameter.isdigit() and 1 <= int(parameter) <= len(var.playlist):
148 var.playlist.point_to(int(parameter) - 1 - 1) # First "-1" transfer 12345 to 01234, second "-1"
149 # point to the previous item. the loop will next to
153 bot.send_msg(constants.strings('invalid_index', index=parameter), text)
158 bot.send_msg(var.playlist.current_item().format_current_playing(), text)
161 bot.send_msg(constants.strings('queue_empty'), text)
164 def cmd_pause(bot, user, text, command, parameter):
168 bot.send_msg(constants.strings('paused'))
171 def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=False):
174 # if parameter is {index}
175 if parameter.isdigit():
176 files = var.library.files
177 if int(parameter) < len(files):
178 music_wrapper = get_item_wrapper_by_id(bot, var.library.file_id_lookup[files[int(parameter)]], user)
179 var.playlist.append(music_wrapper)
180 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
181 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
184 # if parameter is {path}
186 # sanitize "../" and so on
187 # path = os.path.abspath(os.path.join(var.music_folder, parameter))
188 # if not path.startswith(os.path.abspath(var.music_folder)):
189 # bot.send_msg(constants.strings('no_file'), text)
192 if parameter in var.library.files:
193 music_wrapper = get_item_wrapper(bot, type='file', path=parameter, user=user)
194 var.playlist.append(music_wrapper)
195 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
196 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
199 # if parameter is {folder}
200 files = var.library.dir.get_files(parameter)
202 msgs = [constants.strings('multiple_file_added')]
207 music_wrapper = get_item_wrapper_by_id(bot, var.library.file_id_lookup[file],user)
208 var.playlist.append(music_wrapper)
209 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
210 msgs.append("{} ({})".format(music_wrapper.item().title, music_wrapper.item().path))
213 send_multi_lines(bot, msgs, text)
217 # try to do a partial match
218 files = var.library.files
219 matches = [(index, file) for index, file in enumerate(files) if parameter.lower() in file.lower()]
220 if len(matches) == 1:
222 music_wrapper = get_item_wrapper_by_id(bot, var.library.file_id_lookup[file],user)
223 var.playlist.append(music_wrapper)
224 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
225 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
227 elif len(matches) > 1:
228 msgs = [ constants.strings('multiple_matches') ]
229 for match in matches:
230 music_wrapper = get_item_wrapper_by_id(bot, var.library.file_id_lookup[match[1]], user)
231 msgs.append("<b>{:0>3d}</b> - <b>{:s}</b> ({:s})".format(
232 match[0], music_wrapper.item().title, match[1]))
233 send_multi_lines(bot, msgs, text)
236 if do_not_refresh_cache:
237 bot.send_msg(constants.strings("no_file"), text)
239 var.library.build_dir_cache(bot)
240 cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=True)
243 def cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cache=False):
246 music_folder = var.music_folder
248 files = var.library.files
249 msgs = [ constants.strings('multiple_file_added') + "<ul>"]
254 match = re.search(parameter, file)
255 if match and match[0]:
257 music_wrapper = get_item_wrapper_by_id(bot, var.library.file_id_lookup[file], user)
258 music_wrappers.append(music_wrapper)
259 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
260 msgs.append("<li><b>{}</b> ({})</li>".format(music_wrapper.item().title,
261 file[:match.span()[0]]
262 + "<b style='color:pink'>"
263 + file[match.span()[0]: match.span()[1]]
265 + file[match.span()[1]:]
270 var.playlist.extend(music_wrappers)
271 send_multi_lines(bot, msgs, text, "")
273 if do_not_refresh_cache:
274 bot.send_msg(constants.strings("no_file"), text)
276 var.library.build_dir_cache(bot)
277 cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cache=True)
279 except re.error as e:
280 msg = constants.strings('wrong_pattern', error=str(e))
281 bot.send_msg(msg, text)
283 bot.send_msg(constants.strings('bad_parameter', command=command))
286 def cmd_play_url(bot, user, text, command, parameter):
289 url = util.get_url_from_input(parameter)
290 music_wrapper = get_item_wrapper(bot, type='url', url=url, user=user)
291 var.playlist.append(music_wrapper)
293 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
294 bot.send_msg(constants.strings('file_added', item=music_wrapper.format_short_string()), text)
295 if len(var.playlist) == 2:
296 # If I am the second item on the playlist. (I am the next one!)
297 bot.async_download_next()
300 def cmd_play_playlist(bot, user, text, command, parameter):
303 offset = 0 # if you want to start the playlist at a specific index
305 offset = int(parameter.split(" ")[-1])
309 url = util.get_url_from_input(parameter)
310 log.debug("cmd: fetching media info from playlist url %s" % url)
311 items = get_playlist_info(url=url, start_index=offset, user=user)
313 items = var.playlist.extend(list(map(
314 lambda item: get_item_wrapper(bot, **item), items)))
316 log.info("cmd: add to playlist: " + music.format_debug_string())
318 bot.send_msg(constants.strings("playlist_fetching_failed"), text)
321 def cmd_play_radio(bot, user, text, command, parameter):
325 all_radio = var.config.items('radio')
326 msg = constants.strings('preconfigurated_radio')
329 if len(i[1].split(maxsplit=1)) == 2:
330 comment = " - " + i[1].split(maxsplit=1)[1]
331 msg += "<br />" + i[0] + comment
332 bot.send_msg(msg, text)
334 if var.config.has_option('radio', parameter):
335 parameter = var.config.get('radio', parameter)
336 parameter = parameter.split()[0]
337 url = util.get_url_from_input(parameter)
339 music_wrapper = get_item_wrapper(bot, type='radio', url=url)
341 var.playlist.append(music_wrapper)
342 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
344 bot.send_msg(constants.strings('bad_url'))
347 def cmd_rb_query(bot, user, text, command, parameter):
350 log.info('cmd: Querying radio stations')
352 log.debug('rbquery without parameter')
353 msg = constants.strings('rb_query_empty')
354 bot.send_msg(msg, text)
356 log.debug('cmd: Found query parameter: ' + parameter)
357 # bot.send_msg('Searching for stations - this may take some seconds...', text)
358 rb_stations = radiobrowser.getstations_byname(parameter)
359 msg = constants.strings('rb_query_result')
360 msg += '\n<table><tr><th>!rbplay ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th></tr>'
362 log.debug('cmd: No matches found for rbquery ' + parameter)
363 bot.send_msg('Radio-Browser found no matches for ' + parameter, text)
365 for s in rb_stations:
367 stationname = s['stationname']
368 country = s['country']
370 bitrate = s['bitrate']
372 # msg += f'<tr><td>{stationid}</td><td>{stationname}</td><td>{genre}</td><td>{codec}/{bitrate}</td><td>{country}</td></tr>'
373 msg += '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s/%s</td><td>%s</td></tr>' % (
374 stationid, stationname, genre, codec, bitrate, country)
376 # Full message as html table
378 bot.send_msg(msg, text)
379 # Shorten message if message too long (stage I)
381 log.debug('Result too long stage I')
382 msg = constants.strings('rb_query_result') + ' (shortened L1)'
383 msg += '\n<table><tr><th>!rbplay ID</th><th>Station Name</th></tr>'
384 for s in rb_stations:
386 stationname = s['stationname']
387 # msg += f'<tr><td>{stationid}</td><td>{stationname}</td>'
388 msg += '<tr><td>%s</td><td>%s</td>' % (stationid, stationname)
391 bot.send_msg(msg, text)
392 # Shorten message if message too long (stage II)
394 log.debug('Result too long stage II')
395 msg = constants.strings('rb_query_result') + ' (shortened L2)'
396 msg += '!rbplay ID - Station Name'
397 for s in rb_stations:
399 stationname = s['stationname'][:12]
400 # msg += f'{stationid} - {stationname}'
401 msg += '%s - %s' % (stationid, stationname)
403 bot.send_msg(msg, text)
404 # Message still too long
406 bot.send_msg('Query result too long to post (> 5000 characters), please try another query.',
410 def cmd_rb_play(bot, user, text, command, parameter):
413 log.debug('cmd: Play a station by ID')
415 log.debug('rbplay without parameter')
416 msg = constants.strings('rb_play_empty')
417 bot.send_msg(msg, text)
419 log.debug('cmd: Retreiving url for station ID ' + parameter)
420 rstation = radiobrowser.getstationname_byid(parameter)
421 stationname = rstation[0]['name']
422 country = rstation[0]['country']
423 codec = rstation[0]['codec']
424 bitrate = rstation[0]['bitrate']
425 genre = rstation[0]['tags']
426 homepage = rstation[0]['homepage']
427 msg = 'Radio station added to playlist:'
428 # msg += '<table><tr><th>ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th><th>Homepage</th></tr>' + \
429 # f'<tr><td>{parameter}</td><td>{stationname}</td><td>{genre}</td><td>{codec}/{bitrate}</td><td>{country}</td><td>{homepage}</td></tr></table>'
430 msg += '<table><tr><th>ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th><th>Homepage</th></tr>' + \
431 '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s/%s</td><td>%s</td><td>%s</td></tr></table>' \
432 % (parameter, stationname, genre, codec, bitrate, country, homepage)
433 log.debug('cmd: Added station to playlist %s' % stationname)
434 bot.send_msg(msg, text)
435 url = radiobrowser.geturl_byid(parameter)
437 log.info('cmd: Found url: ' + url)
438 music_wrapper = get_item_wrapper(bot, type='radio', url=url, name=stationname)
439 var.playlist.append(music_wrapper)
440 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
441 bot.async_download_next()
443 log.info('cmd: No playable url found.')
444 msg += "No playable url found for this station, please try another station."
445 bot.send_msg(msg, text)
448 yt_last_page = 0 # TODO: if we keep adding global variables, we need to consider sealing all commands up into classes.
450 def cmd_yt_search(bot, user, text, command, parameter):
451 global log, yt_last_result, yt_last_page
456 if parameter.startswith("-n"):
458 if len(yt_last_result) > yt_last_page * item_per_page:
459 msg = _yt_format_result(yt_last_result, yt_last_page * item_per_page, item_per_page)
460 bot.send_msg(constants.strings('yt_result', result_table=msg), text)
462 bot.send_msg(constants.strings('yt_no_more'))
466 results = util.youtube_search(parameter)
468 yt_last_result = results
470 msg = _yt_format_result(results, 0, item_per_page)
471 bot.send_msg(constants.strings('yt_result', result_table=msg), text)
473 bot.send_msg(constants.strings('yt_query_error'))
475 bot.send_msg(constants.strings('bad_parameter', command=command), text)
477 def _yt_format_result(results, start, count):
478 msg = '<table><tr><th width="10%">Index</th><th>Title</th><th width="20%">Uploader</th></tr>'
479 for index, item in enumerate(results[start:start+count]):
480 msg += '<tr><td>{index:d}</td><td>{title}</td><td>{uploader}</td></tr>'.format(
481 index=index + 1 + start, title=item[1], uploader=item[2])
487 def cmd_yt_play(bot, user, text, command, parameter):
488 global log, yt_last_result, yt_last_page
491 if parameter.isdigit() and 0 <= int(parameter) - 1 < len(yt_last_result):
492 url = "https://www.youtube.com/watch?v=" + yt_last_result[int(parameter) - 1][0]
493 cmd_play_url(bot, user, text, command, url)
495 results = util.youtube_search(parameter)
497 yt_last_result = results
499 url = "https://www.youtube.com/watch?v=" + yt_last_result[0][0]
500 cmd_play_url(bot, user, text, command, url)
502 bot.send_msg(constants.strings('yt_query_error'))
504 bot.send_msg(constants.strings('bad_parameter', command=command), text)
507 def cmd_help(bot, user, text, command, parameter):
510 bot.send_msg(constants.strings('help'), text)
511 if bot.is_admin(user):
512 bot.send_msg(constants.strings('admin_help'), text)
515 def cmd_stop(bot, user, text, command, parameter):
519 bot.send_msg(constants.strings('stopped'), text)
522 def cmd_clear(bot, user, text, command, parameter):
526 bot.send_msg(constants.strings('cleared'), text)
529 def cmd_kill(bot, user, text, command, parameter):
532 if bot.is_admin(user):
536 bot.mumble.users[text.actor].send_text_message(
537 constants.strings('not_admin'))
540 def cmd_update(bot, user, text, command, parameter):
543 if bot.is_admin(user):
544 bot.mumble.users[text.actor].send_text_message(
545 constants.strings('start_updating'))
546 msg = util.update(bot.version)
547 bot.mumble.users[text.actor].send_text_message(msg)
549 bot.mumble.users[text.actor].send_text_message(
550 constants.strings('not_admin'))
553 def cmd_stop_and_getout(bot, user, text, command, parameter):
558 bot.mumble.channels.find_by_name(bot.channel).move_in()
561 def cmd_volume(bot, user, text, command, parameter):
564 # The volume is a percentage
565 if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
566 bot.volume_set = float(float(parameter) / 100)
567 bot.send_msg(constants.strings('change_volume',
568 volume=int(bot.volume_set * 100), user=bot.mumble.users[text.actor]['name']), text)
569 var.db.set('bot', 'volume', str(bot.volume_set))
570 log.info('cmd: volume set to %d' % (bot.volume_set * 100))
572 bot.send_msg(constants.strings('current_volume', volume=int(bot.volume_set * 100)), text)
575 def cmd_ducking(bot, user, text, command, parameter):
578 if parameter == "" or parameter == "on":
579 bot.is_ducking = True
580 var.db.set('bot', 'ducking', True)
581 bot.ducking_volume = var.config.getfloat("bot", "ducking_volume", fallback=0.05)
582 bot.ducking_threshold = var.config.getint("bot", "ducking_threshold", fallback=5000)
583 bot.mumble.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED,
584 bot.ducking_sound_received)
585 bot.mumble.set_receive_sound(True)
586 log.info('cmd: ducking is on')
588 bot.send_msg(msg, text)
589 elif parameter == "off":
590 bot.is_ducking = False
591 bot.mumble.set_receive_sound(False)
592 var.db.set('bot', 'ducking', False)
594 log.info('cmd: ducking is off')
595 bot.send_msg(msg, text)
598 def cmd_ducking_threshold(bot, user, text, command, parameter):
601 if parameter and parameter.isdigit():
602 bot.ducking_threshold = int(parameter)
603 var.db.set('bot', 'ducking_threshold', str(bot.ducking_threshold))
604 msg = "Ducking threshold set to %d." % bot.ducking_threshold
605 bot.send_msg(msg, text)
607 msg = "Current ducking threshold is %d." % bot.ducking_threshold
608 bot.send_msg(msg, text)
611 def cmd_ducking_volume(bot, user, text, command, parameter):
614 # The volume is a percentage
615 if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
616 bot.ducking_volume = float(float(parameter) / 100)
617 bot.send_msg(constants.strings('change_ducking_volume',
618 volume=int(bot.ducking_volume * 100), user=bot.mumble.users[text.actor]['name']), text)
619 # var.db.set('bot', 'volume', str(bot.volume_set))
620 var.db.set('bot', 'ducking_volume', str(bot.ducking_volume))
621 log.info('cmd: volume on ducking set to %d' % (bot.ducking_volume * 100))
623 bot.send_msg(constants.strings('current_ducking_volume', volume=int(bot.ducking_volume * 100)), text)
626 def cmd_current_music(bot, user, text, command, parameter):
630 if len(var.playlist) > 0:
631 bot.send_msg(var.playlist.current_item().format_current_playing())
633 reply = constants.strings('not_playing')
634 bot.send_msg(reply, text)
637 def cmd_skip(bot, user, text, command, parameter):
642 if len(var.playlist) == 0:
643 bot.send_msg(constants.strings('queue_empty'), text)
646 def cmd_last(bot, user, text, command, parameter):
649 if len(var.playlist) > 0:
651 var.playlist.point_to(len(var.playlist) - 1)
653 bot.send_msg(constants.strings('queue_empty'), text)
656 def cmd_remove(bot, user, text, command, parameter):
659 # Allow to remove specific music into the queue with a number
660 if parameter and parameter.isdigit() and int(parameter) > 0 \
661 and int(parameter) <= len(var.playlist):
663 index = int(parameter) - 1
666 if index == var.playlist.current_index:
667 removed = var.playlist.remove(index)
669 if index < len(var.playlist):
672 var.playlist.current_index -= 1
673 # then the bot will move to next item
675 else: # if item deleted is the last item of the queue
676 var.playlist.current_index -= 1
680 removed = var.playlist.remove(index)
682 bot.send_msg(constants.strings('removing_item',
683 item=removed.format_short_string()), text)
685 log.info("cmd: delete from playlist: " + removed.format_debug_string())
687 bot.send_msg(constants.strings('bad_parameter', command=command))
690 def cmd_list_file(bot, user, text, command, parameter):
693 files = var.library.files
694 msgs = [ "<br> <b>Files available:</b>" if not parameter else "<br> <b>Matched files:</b>" ]
697 for index, file in enumerate(files):
699 match = re.search(parameter, file)
704 msgs.append("<b>{:0>3d}</b> - {:s}".format(index, file))
707 send_multi_lines(bot, msgs, text)
709 bot.send_msg(constants.strings('no_file'), text)
711 except re.error as e:
712 msg = constants.strings('wrong_pattern', error=str(e))
713 bot.send_msg(msg, text)
716 def cmd_queue(bot, user, text, command, parameter):
719 if len(var.playlist) == 0:
720 msg = constants.strings('queue_empty')
721 bot.send_msg(msg, text)
723 msgs = [ constants.strings('queue_contents')]
724 for i, music in enumerate(var.playlist):
727 if len(music.item().tags) > 0:
728 tags = "<sup>{}</sup>".format(", ".join(music.item().tags))
729 if i == var.playlist.current_index:
730 newline = "<b style='color:orange'>{} ({}) {} </b> {}".format(i + 1, music.display_type(),
731 music.format_short_string(), tags)
733 newline = '<b>{}</b> ({}) {} {}'.format(i + 1, music.display_type(),
734 music.format_short_string(), tags)
738 send_multi_lines(bot, msgs, text)
740 def cmd_random(bot, user, text, command, parameter):
744 var.playlist.randomize()
746 def cmd_repeat(bot, user, text, command, parameter):
750 if parameter and parameter.isdigit():
751 repeat = int(parameter)
753 music = var.playlist.current_item()
754 for _ in range(repeat):
756 var.playlist.current_index + 1,
759 log.info("bot: add to playlist: " + music.format_debug_string)
761 bot.send_msg(constants.strings("repeat", song=music.format_song_string(), n=str(repeat)), text)
763 def cmd_mode(bot, user, text, command, parameter):
767 bot.send_msg(constants.strings("current_mode", mode=var.playlist.mode), text)
769 if not parameter in ["one-shot", "repeat", "random", "autoplay"]:
770 bot.send_msg(constants.strings('unknown_mode', mode=parameter), text)
772 var.db.set('playlist', 'playback_mode', parameter)
773 var.playlist = media.playlist.get_playlist(parameter, var.playlist)
774 log.info("command: playback mode changed to %s." % parameter)
775 bot.send_msg(constants.strings("change_mode", mode=var.playlist.mode,
776 user=bot.mumble.users[text.actor]['name']), text)
777 if parameter == "random":
781 def cmd_play_tags(bot, user, text, command, parameter):
783 bot.send_msg(constants.strings('bad_parameter', command=command))
786 msgs = [constants.strings('multiple_file_added') + "<ul>"]
789 tags = parameter.split(",")
790 tags = list(map(lambda t: t.strip(), tags))
791 music_wrappers = get_item_wrappers_by_tags(bot, tags, user)
792 for music_wrapper in music_wrappers:
794 log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
795 msgs.append("<li><b>{}</b> (<i>{}</i>)</li>".format(music_wrapper.item().title, ", ".join(music_wrapper.item().tags)))
800 var.playlist.extend(music_wrappers)
801 send_multi_lines(bot, msgs, text, "")
803 bot.send_msg(constants.strings("no_file"), text)
806 def cmd_add_tag(bot, user, text, command, parameter):
809 params = parameter.split()
812 tags = list(map(lambda t: t.strip(), params[1].split(",")))
814 if index.isdigit() and 1 <= int(index) <= len(var.playlist):
815 var.playlist[int(index) - 1].add_tags(tags)
816 log.info("cmd: add tags %s to song %s" % (", ".join(tags),
817 var.playlist[int(index) - 1].format_debug_string()))
818 bot.send_msg(constants.strings("added_tags",
819 tags=", ".join(tags),
820 song=var.playlist[int(index) - 1].format_short_string()), text)
822 for item in var.playlist:
824 log.info("cmd: add tags %s to song %s" % (", ".join(tags),
825 item.format_debug_string()))
826 bot.send_msg(constants.strings("added_tags_to_all", tags=", ".join(tags)), text)
828 bot.send_msg(constants.strings('bad_parameter', command=command), text)
831 def cmd_remove_tag(bot, user, text, command, parameter):
834 params = parameter.split()
835 if len(params) == 2 and params[1]:
838 if index.isdigit() and 1 <= int(index) <= len(var.playlist):
840 tags = list(map(lambda t: t.strip(), params[1].split(",")))
841 var.playlist[int(index) - 1].remove_tags(tags)
842 log.info("cmd: remove tags %s from song %s" % (", ".join(tags),
843 var.playlist[int(index) - 1].format_debug_string()))
844 bot.send_msg(constants.strings("removed_tags",
845 tags=", ".join(tags),
846 song=var.playlist[int(index) - 1].format_short_string()), text)
849 var.playlist[int(index) - 1].clear_tags()
850 log.info("cmd: clear tags from song %s" % (var.playlist[int(index) - 1].format_debug_string()))
851 bot.send_msg(constants.strings("cleared_tags",
852 song=var.playlist[int(index) - 1].format_short_string()), text)
857 tags = list(map(lambda t: t.strip(), params[1].split(",")))
858 for item in var.playlist:
859 item.remove_tags(tags)
860 log.info("cmd: remove tags %s from song %s" % (", ".join(tags),
861 item.format_debug_string()))
862 bot.send_msg(constants.strings("removed_tags_from_all", tags=", ".join(tags)), text)
865 for item in var.playlist:
867 log.info("cmd: clear tags from song %s" % (item.format_debug_string()))
868 bot.send_msg(constants.strings("cleared_tags_from_all"), text)
871 bot.send_msg(constants.strings('bad_parameter', command=command), text)
873 def cmd_find_tagged(bot, user, text, command, parameter):
875 bot.send_msg(constants.strings('bad_parameter', command=command))
878 msgs = [constants.strings('multiple_file_found') + "<ul>"]
881 tags = parameter.split(",")
882 tags = list(map(lambda t: t.strip(), tags))
883 music_wrappers = get_item_wrappers_by_tags(bot, tags, user)
884 for music_wrapper in music_wrappers:
886 msgs.append("<li><b>{}</b> (<i>{}</i>)</li>".format(music_wrapper.item().title, ", ".join(music_wrapper.item().tags)))
890 send_multi_lines(bot, msgs, text, "")
892 bot.send_msg(constants.strings("no_file"), text)
894 def cmd_drop_database(bot, user, text, command, parameter):
898 var.db = SettingsDatabase(var.dbfile)
899 var.music_db.drop_table()
900 var.music_db = MusicDatabase(var.dbfile)
901 log.info("command: database dropped.")
902 bot.send_msg(constants.strings('database_dropped'), text)
904 def cmd_refresh_cache(bot, user, text, command, parameter):
906 var.library.build_dir_cache(bot)
907 log.info("command: Local file cache refreshed.")
908 bot.send_msg(constants.strings('cache_refreshed'), text)
911 def cmd_real_time_rms(bot, user, text, command, parameter):
912 bot._display_rms = not bot._display_rms
914 def cmd_loop_state(bot, user, text, command, parameter):
915 print(bot._loop_status)
917 def cmd_item(bot, user, text, command, parameter):
918 print(bot.wait_for_downloading)
919 print(var.playlist.current_item().to_dict())