Merge pull request #187 from azlux/translate
i18n support, with traduora
This commit is contained in:
commit
0da652da10
38
.drone.yml
38
.drone.yml
@ -3,18 +3,22 @@ type: docker
|
||||
name: default
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
- name: build-web
|
||||
image: node
|
||||
commands:
|
||||
- cd web
|
||||
- npm install
|
||||
- npm run build
|
||||
- cd ..
|
||||
- (cd web && npm install && npm run build)
|
||||
|
||||
- name: translate-web
|
||||
image: python:3
|
||||
commands:
|
||||
- pip3 install jinja2
|
||||
- (cd templates/ && ./translate.py)
|
||||
|
||||
- name: deploy-testing
|
||||
image: debian
|
||||
commands:
|
||||
- apt-get -qq update && apt-get -qq install git > /dev/null
|
||||
- apt-get -qq update && apt-get -qq install git python3-requests > /dev/null
|
||||
- (cd lang && ./sync_translation.py --client $TRADUORA_W_CLIENT --secret $TRADUORA_W_SECRET --push)
|
||||
- sed -i 's/target_version = git/target_version = testing/' configuration.default.ini
|
||||
- version=$(git rev-parse HEAD)
|
||||
- echo "current git commit is $version"
|
||||
@ -53,7 +57,8 @@ steps:
|
||||
- name: deploy-stable
|
||||
image: debian
|
||||
commands:
|
||||
- apt-get -qq update && apt-get -qq install jq curl git pandoc > /dev/null
|
||||
- apt-get -qq update && apt-get -qq install jq curl git pandoc python3-requests > /dev/null
|
||||
- (cd lang && ./sync_translation.py --client $TRADUORA_W_CLIENT --secret $TRADUORA_W_SECRET --push)
|
||||
- sed -i 's/target_version = git/target_version = stable/' configuration.default.ini
|
||||
- git fetch --tags
|
||||
- version=$(git describe --abbrev=0 --tags)
|
||||
@ -85,6 +90,25 @@ steps:
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
|
||||
- name: fetch-translate
|
||||
image: debian
|
||||
commands:
|
||||
- apt update && apt install -y git python3-requests hub
|
||||
- git branch bot-traduora && git checkout bot-traduora
|
||||
- (cd lang/ && ./sync_translation.py --client $TRADUORA_R_CLIENT --secret $TRADUORA_R_SECRET --fetch)
|
||||
- git add lang/* && git status
|
||||
- git config --global user.email "github@azlux.fr" && git config --global user.name "azlux"
|
||||
- "git commit -a -m 'Bot: update strings'"
|
||||
- git remote set-url origin https://azlux:$GITHUB_API@github.com/azlux/botamusique/
|
||||
- git push --set-upstream origin new-translations && git push
|
||||
- "GITHUB_USER=\"azlux\" GITHUB_TOKEN=\"$GITHUB_API\" hub pull-request -m \"Bot: TRADUORA Update\""
|
||||
when:
|
||||
event:
|
||||
- cron
|
||||
cron:
|
||||
- auto-fetch-lang
|
||||
|
||||
|
||||
volumes:
|
||||
- name: repo
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -116,7 +116,7 @@ tmp/
|
||||
|
||||
*.db
|
||||
|
||||
templates/*
|
||||
templates/*.html
|
||||
|
||||
# Pycharm
|
||||
.idea/
|
||||
|
@ -91,9 +91,8 @@ cd botamusique
|
||||
python3 -m venv venv
|
||||
venv/bin/pip install wheel
|
||||
venv/bin/pip install -r requirements.txt
|
||||
cd web
|
||||
npm install
|
||||
npm run build
|
||||
(cd web && npm install && npm run build)
|
||||
(cd templates/ && ./translate.py)
|
||||
```
|
||||
</details>
|
||||
|
||||
|
339
command.py
339
command.py
@ -6,7 +6,8 @@ import json
|
||||
import re
|
||||
import pymumble_py3 as pymumble
|
||||
|
||||
import constants
|
||||
from constants import tr_cli as tr
|
||||
from constants import commands
|
||||
import interface
|
||||
import media.system
|
||||
import util
|
||||
@ -22,56 +23,56 @@ log = logging.getLogger("bot")
|
||||
|
||||
|
||||
def register_all_commands(bot):
|
||||
bot.register_command(constants.commands('joinme'), cmd_joinme, access_outside_channel=True)
|
||||
bot.register_command(constants.commands('user_ban'), cmd_user_ban, no_partial_match=True, admin=True)
|
||||
bot.register_command(constants.commands('user_unban'), cmd_user_unban, no_partial_match=True, admin=True)
|
||||
bot.register_command(constants.commands('url_ban_list'), cmd_url_ban_list, no_partial_match=True, admin=True)
|
||||
bot.register_command(constants.commands('url_ban'), cmd_url_ban, no_partial_match=True, admin=True)
|
||||
bot.register_command(constants.commands('url_unban'), cmd_url_unban, no_partial_match=True, admin=True)
|
||||
bot.register_command(constants.commands('play'), cmd_play)
|
||||
bot.register_command(constants.commands('pause'), cmd_pause)
|
||||
bot.register_command(constants.commands('play_file'), cmd_play_file)
|
||||
bot.register_command(constants.commands('play_file_match'), cmd_play_file_match)
|
||||
bot.register_command(constants.commands('play_url'), cmd_play_url)
|
||||
bot.register_command(constants.commands('play_playlist'), cmd_play_playlist)
|
||||
bot.register_command(constants.commands('play_radio'), cmd_play_radio)
|
||||
bot.register_command(constants.commands('play_tag'), cmd_play_tags)
|
||||
bot.register_command(constants.commands('rb_query'), cmd_rb_query)
|
||||
bot.register_command(constants.commands('rb_play'), cmd_rb_play)
|
||||
bot.register_command(constants.commands('yt_search'), cmd_yt_search)
|
||||
bot.register_command(constants.commands('yt_play'), cmd_yt_play)
|
||||
bot.register_command(constants.commands('help'), cmd_help, no_partial_match=False, access_outside_channel=True)
|
||||
bot.register_command(constants.commands('stop'), cmd_stop)
|
||||
bot.register_command(constants.commands('clear'), cmd_clear)
|
||||
bot.register_command(constants.commands('kill'), cmd_kill, admin=True)
|
||||
bot.register_command(constants.commands('update'), cmd_update, no_partial_match=True, admin=True)
|
||||
bot.register_command(constants.commands('stop_and_getout'), cmd_stop_and_getout)
|
||||
bot.register_command(constants.commands('volume'), cmd_volume)
|
||||
bot.register_command(constants.commands('ducking'), cmd_ducking)
|
||||
bot.register_command(constants.commands('ducking_threshold'), cmd_ducking_threshold)
|
||||
bot.register_command(constants.commands('ducking_volume'), cmd_ducking_volume)
|
||||
bot.register_command(constants.commands('current_music'), cmd_current_music)
|
||||
bot.register_command(constants.commands('skip'), cmd_skip)
|
||||
bot.register_command(constants.commands('last'), cmd_last)
|
||||
bot.register_command(constants.commands('remove'), cmd_remove)
|
||||
bot.register_command(constants.commands('list_file'), cmd_list_file)
|
||||
bot.register_command(constants.commands('queue'), cmd_queue)
|
||||
bot.register_command(constants.commands('random'), cmd_random)
|
||||
bot.register_command(constants.commands('repeat'), cmd_repeat)
|
||||
bot.register_command(constants.commands('mode'), cmd_mode)
|
||||
bot.register_command(constants.commands('add_tag'), cmd_add_tag)
|
||||
bot.register_command(constants.commands('remove_tag'), cmd_remove_tag)
|
||||
bot.register_command(constants.commands('find_tagged'), cmd_find_tagged)
|
||||
bot.register_command(constants.commands('search'), cmd_search_library)
|
||||
bot.register_command(constants.commands('add_from_shortlist'), cmd_shortlist)
|
||||
bot.register_command(constants.commands('delete_from_library'), cmd_delete_from_library)
|
||||
bot.register_command(constants.commands('drop_database'), cmd_drop_database, no_partial_match=True, admin=True)
|
||||
bot.register_command(constants.commands('rescan'), cmd_refresh_cache, no_partial_match=True)
|
||||
bot.register_command(constants.commands('requests_webinterface_access'), cmd_web_access)
|
||||
bot.register_command(constants.commands('add_webinterface_user'), cmd_web_user_add, admin=True)
|
||||
bot.register_command(constants.commands('remove_webinterface_user'), cmd_web_user_remove, admin=True)
|
||||
bot.register_command(constants.commands('list_webinterface_user'), cmd_web_user_list, admin=True)
|
||||
bot.register_command(constants.commands('change_user_password'), cmd_user_password, no_partial_match=True)
|
||||
bot.register_command(commands('joinme'), cmd_joinme, access_outside_channel=True)
|
||||
bot.register_command(commands('user_ban'), cmd_user_ban, no_partial_match=True, admin=True)
|
||||
bot.register_command(commands('user_unban'), cmd_user_unban, no_partial_match=True, admin=True)
|
||||
bot.register_command(commands('url_ban_list'), cmd_url_ban_list, no_partial_match=True, admin=True)
|
||||
bot.register_command(commands('url_ban'), cmd_url_ban, no_partial_match=True, admin=True)
|
||||
bot.register_command(commands('url_unban'), cmd_url_unban, no_partial_match=True, admin=True)
|
||||
bot.register_command(commands('play'), cmd_play)
|
||||
bot.register_command(commands('pause'), cmd_pause)
|
||||
bot.register_command(commands('play_file'), cmd_play_file)
|
||||
bot.register_command(commands('play_file_match'), cmd_play_file_match)
|
||||
bot.register_command(commands('play_url'), cmd_play_url)
|
||||
bot.register_command(commands('play_playlist'), cmd_play_playlist)
|
||||
bot.register_command(commands('play_radio'), cmd_play_radio)
|
||||
bot.register_command(commands('play_tag'), cmd_play_tags)
|
||||
bot.register_command(commands('rb_query'), cmd_rb_query)
|
||||
bot.register_command(commands('rb_play'), cmd_rb_play)
|
||||
bot.register_command(commands('yt_search'), cmd_yt_search)
|
||||
bot.register_command(commands('yt_play'), cmd_yt_play)
|
||||
bot.register_command(commands('help'), cmd_help, no_partial_match=False, access_outside_channel=True)
|
||||
bot.register_command(commands('stop'), cmd_stop)
|
||||
bot.register_command(commands('clear'), cmd_clear)
|
||||
bot.register_command(commands('kill'), cmd_kill, admin=True)
|
||||
bot.register_command(commands('update'), cmd_update, no_partial_match=True, admin=True)
|
||||
bot.register_command(commands('stop_and_getout'), cmd_stop_and_getout)
|
||||
bot.register_command(commands('volume'), cmd_volume)
|
||||
bot.register_command(commands('ducking'), cmd_ducking)
|
||||
bot.register_command(commands('ducking_threshold'), cmd_ducking_threshold)
|
||||
bot.register_command(commands('ducking_volume'), cmd_ducking_volume)
|
||||
bot.register_command(commands('current_music'), cmd_current_music)
|
||||
bot.register_command(commands('skip'), cmd_skip)
|
||||
bot.register_command(commands('last'), cmd_last)
|
||||
bot.register_command(commands('remove'), cmd_remove)
|
||||
bot.register_command(commands('list_file'), cmd_list_file)
|
||||
bot.register_command(commands('queue'), cmd_queue)
|
||||
bot.register_command(commands('random'), cmd_random)
|
||||
bot.register_command(commands('repeat'), cmd_repeat)
|
||||
bot.register_command(commands('mode'), cmd_mode)
|
||||
bot.register_command(commands('add_tag'), cmd_add_tag)
|
||||
bot.register_command(commands('remove_tag'), cmd_remove_tag)
|
||||
bot.register_command(commands('find_tagged'), cmd_find_tagged)
|
||||
bot.register_command(commands('search'), cmd_search_library)
|
||||
bot.register_command(commands('add_from_shortlist'), cmd_shortlist)
|
||||
bot.register_command(commands('delete_from_library'), cmd_delete_from_library)
|
||||
bot.register_command(commands('drop_database'), cmd_drop_database, no_partial_match=True, admin=True)
|
||||
bot.register_command(commands('rescan'), cmd_refresh_cache, no_partial_match=True)
|
||||
bot.register_command(commands('requests_webinterface_access'), cmd_web_access)
|
||||
bot.register_command(commands('add_webinterface_user'), cmd_web_user_add, admin=True)
|
||||
bot.register_command(commands('remove_webinterface_user'), cmd_web_user_remove, admin=True)
|
||||
bot.register_command(commands('list_webinterface_user'), cmd_web_user_list, admin=True)
|
||||
bot.register_command(commands('change_user_password'), cmd_user_password, no_partial_match=True)
|
||||
# Just for debug use
|
||||
bot.register_command('rtrms', cmd_real_time_rms, True)
|
||||
# bot.register_command('loop', cmd_loop_state, True)
|
||||
@ -160,7 +161,7 @@ def cmd_url_ban(bot, user, text, command, parameter):
|
||||
var.cache.free_and_delete(item.id)
|
||||
var.playlist.remove_by_id(item.id)
|
||||
else:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
|
||||
|
||||
def cmd_url_ban_list(bot, user, text, command, parameter):
|
||||
@ -184,14 +185,14 @@ def cmd_play(bot, user, text, command, parameter):
|
||||
if params[0].isdigit() and 1 <= int(params[0]) <= len(var.playlist):
|
||||
index = int(params[0])
|
||||
else:
|
||||
bot.send_msg(constants.strings('invalid_index', index=parameter), text)
|
||||
bot.send_msg(tr('invalid_index', index=parameter), text)
|
||||
return
|
||||
|
||||
if len(params) > 1:
|
||||
try:
|
||||
start_at = util.parse_time(params[1])
|
||||
except ValueError:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
return
|
||||
|
||||
if len(var.playlist) > 0:
|
||||
@ -204,14 +205,14 @@ def cmd_play(bot, user, text, command, parameter):
|
||||
bot.send_msg(var.playlist.current_item().format_current_playing(), text)
|
||||
else:
|
||||
bot.is_pause = False
|
||||
bot.send_msg(constants.strings('queue_empty'), text)
|
||||
bot.send_msg(tr('queue_empty'), text)
|
||||
|
||||
|
||||
def cmd_pause(bot, user, text, command, parameter):
|
||||
global log
|
||||
|
||||
bot.pause()
|
||||
bot.send_channel_msg(constants.strings('paused'))
|
||||
bot.send_channel_msg(tr('paused'))
|
||||
|
||||
|
||||
def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=False):
|
||||
@ -222,7 +223,7 @@ def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=Fals
|
||||
if music_wrappers:
|
||||
var.playlist.append(music_wrappers[0])
|
||||
log.info("cmd: add to playlist: " + music_wrappers[0].format_debug_string())
|
||||
bot.send_msg(constants.strings('file_added', item=music_wrappers[0].format_song_string()), text)
|
||||
bot.send_msg(tr('file_added', item=music_wrappers[0].format_song_string()), text)
|
||||
return
|
||||
|
||||
# assume parameter is a folder
|
||||
@ -230,7 +231,7 @@ def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=Fals
|
||||
.and_equal('type', 'file')
|
||||
.and_like('path', parameter + '%')), user)
|
||||
if music_wrappers:
|
||||
msgs = [constants.strings('multiple_file_added')]
|
||||
msgs = [tr('multiple_file_added')]
|
||||
|
||||
for music_wrapper in music_wrappers:
|
||||
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
|
||||
@ -249,20 +250,20 @@ def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=Fals
|
||||
music_wrapper = get_cached_wrapper_from_dict(matches[0], user)
|
||||
var.playlist.append(music_wrapper)
|
||||
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
|
||||
bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
|
||||
bot.send_msg(tr('file_added', item=music_wrapper.format_song_string()), text)
|
||||
return
|
||||
elif len(matches) > 1:
|
||||
song_shortlist = matches
|
||||
msgs = [constants.strings('multiple_matches')]
|
||||
msgs = [tr('multiple_matches')]
|
||||
for index, match in enumerate(matches):
|
||||
msgs.append("<b>{:d}</b> - <b>{:s}</b> ({:s})".format(
|
||||
index + 1, match['title'], match['path']))
|
||||
msgs.append(constants.strings("shortlist_instruction"))
|
||||
msgs.append(tr("shortlist_instruction"))
|
||||
send_multi_lines(bot, msgs, text)
|
||||
return
|
||||
|
||||
if do_not_refresh_cache:
|
||||
bot.send_msg(constants.strings("no_file"), text)
|
||||
bot.send_msg(tr("no_file"), text)
|
||||
else:
|
||||
var.cache.build_dir_cache()
|
||||
cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=True)
|
||||
@ -273,7 +274,7 @@ def cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cach
|
||||
|
||||
if parameter:
|
||||
file_dicts = var.music_db.query_music(Condition().and_equal('type', 'file'))
|
||||
msgs = [constants.strings('multiple_file_added') + "<ul>"]
|
||||
msgs = [tr('multiple_file_added') + "<ul>"]
|
||||
try:
|
||||
count = 0
|
||||
music_wrappers = []
|
||||
@ -299,16 +300,16 @@ def cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cach
|
||||
send_multi_lines_in_channel(bot, msgs, "")
|
||||
else:
|
||||
if do_not_refresh_cache:
|
||||
bot.send_msg(constants.strings("no_file"), text)
|
||||
bot.send_msg(tr("no_file"), text)
|
||||
else:
|
||||
var.cache.build_dir_cache()
|
||||
cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cache=True)
|
||||
|
||||
except re.error as e:
|
||||
msg = constants.strings('wrong_pattern', error=str(e))
|
||||
msg = tr('wrong_pattern', error=str(e))
|
||||
bot.send_msg(msg, text)
|
||||
else:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
|
||||
|
||||
def cmd_play_url(bot, user, text, command, parameter):
|
||||
@ -320,12 +321,12 @@ def cmd_play_url(bot, user, text, command, parameter):
|
||||
var.playlist.append(music_wrapper)
|
||||
|
||||
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
|
||||
bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
|
||||
bot.send_msg(tr('file_added', item=music_wrapper.format_song_string()), text)
|
||||
if len(var.playlist) == 2:
|
||||
# If I am the second item on the playlist. (I am the next one!)
|
||||
bot.async_download_next()
|
||||
else:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
|
||||
|
||||
def cmd_play_playlist(bot, user, text, command, parameter):
|
||||
@ -345,7 +346,7 @@ def cmd_play_playlist(bot, user, text, command, parameter):
|
||||
for music in items:
|
||||
log.info("cmd: add to playlist: " + music.format_debug_string())
|
||||
else:
|
||||
bot.send_msg(constants.strings("playlist_fetching_failed"), text)
|
||||
bot.send_msg(tr("playlist_fetching_failed"), text)
|
||||
|
||||
|
||||
def cmd_play_radio(bot, user, text, command, parameter):
|
||||
@ -353,7 +354,7 @@ def cmd_play_radio(bot, user, text, command, parameter):
|
||||
|
||||
if not parameter:
|
||||
all_radio = var.config.items('radio')
|
||||
msg = constants.strings('preconfigurated_radio')
|
||||
msg = tr('preconfigurated_radio')
|
||||
for i in all_radio:
|
||||
comment = ""
|
||||
if len(i[1].split(maxsplit=1)) == 2:
|
||||
@ -370,9 +371,9 @@ def cmd_play_radio(bot, user, text, command, parameter):
|
||||
|
||||
var.playlist.append(music_wrapper)
|
||||
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
|
||||
bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
|
||||
bot.send_msg(tr('file_added', item=music_wrapper.format_song_string()), text)
|
||||
else:
|
||||
bot.send_msg(constants.strings('bad_url'), text)
|
||||
bot.send_msg(tr('bad_url'), text)
|
||||
|
||||
|
||||
def cmd_rb_query(bot, user, text, command, parameter):
|
||||
@ -381,13 +382,13 @@ def cmd_rb_query(bot, user, text, command, parameter):
|
||||
log.info('cmd: Querying radio stations')
|
||||
if not parameter:
|
||||
log.debug('rbquery without parameter')
|
||||
msg = constants.strings('rb_query_empty')
|
||||
msg = tr('rb_query_empty')
|
||||
bot.send_msg(msg, text)
|
||||
else:
|
||||
log.debug('cmd: Found query parameter: ' + parameter)
|
||||
rb = RadioBrowser()
|
||||
rb_stations = rb.search(name=parameter, name_exact=False)
|
||||
msg = constants.strings('rb_query_result')
|
||||
msg = tr('rb_query_result')
|
||||
msg += '\n<table><tr><th>!rbplay ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th></tr>'
|
||||
if not rb_stations:
|
||||
log.debug(f"cmd: No matches found for rbquery {parameter}")
|
||||
@ -408,7 +409,7 @@ def cmd_rb_query(bot, user, text, command, parameter):
|
||||
# Shorten message if message too long (stage I)
|
||||
else:
|
||||
log.debug('Result too long stage I')
|
||||
msg = constants.strings('rb_query_result') + ' (shortened L1)'
|
||||
msg = tr('rb_query_result') + ' (shortened L1)'
|
||||
msg += '\n<table><tr><th>!rbplay ID</th><th>Station Name</th></tr>'
|
||||
for s in rb_stations:
|
||||
station_id = s['stationuuid']
|
||||
@ -420,7 +421,7 @@ def cmd_rb_query(bot, user, text, command, parameter):
|
||||
# Shorten message if message too long (stage II)
|
||||
else:
|
||||
log.debug('Result too long stage II')
|
||||
msg = constants.strings('rb_query_result') + ' (shortened L2)'
|
||||
msg = tr('rb_query_result') + ' (shortened L2)'
|
||||
msg += '!rbplay ID - Station Name'
|
||||
for s in rb_stations:
|
||||
station_id = s['stationuuid']
|
||||
@ -439,7 +440,7 @@ def cmd_rb_play(bot, user, text, command, parameter):
|
||||
log.debug('cmd: Play a station by ID')
|
||||
if not parameter:
|
||||
log.debug('rbplay without parameter')
|
||||
msg = constants.strings('rb_play_empty')
|
||||
msg = tr('rb_play_empty')
|
||||
bot.send_msg(msg, text)
|
||||
else:
|
||||
log.debug('cmd: Retreiving url for station ID ' + parameter)
|
||||
@ -488,9 +489,9 @@ def cmd_yt_search(bot, user, text, command, parameter):
|
||||
'title': result[1]
|
||||
} for result in yt_last_result[yt_last_page * item_per_page: item_per_page]]
|
||||
msg = _yt_format_result(yt_last_result, yt_last_page * item_per_page, item_per_page)
|
||||
bot.send_msg(constants.strings('yt_result', result_table=msg), text)
|
||||
bot.send_msg(tr('yt_result', result_table=msg), text)
|
||||
else:
|
||||
bot.send_msg(constants.strings('yt_no_more'), text)
|
||||
bot.send_msg(tr('yt_no_more'), text)
|
||||
|
||||
# if query
|
||||
else:
|
||||
@ -501,11 +502,11 @@ def cmd_yt_search(bot, user, text, command, parameter):
|
||||
song_shortlist = [{'type': 'url', 'url': "https://www.youtube.com/watch?v=" + result[0]}
|
||||
for result in results[0: item_per_page]]
|
||||
msg = _yt_format_result(results, 0, item_per_page)
|
||||
bot.send_msg(constants.strings('yt_result', result_table=msg), text)
|
||||
bot.send_msg(tr('yt_result', result_table=msg), text)
|
||||
else:
|
||||
bot.send_msg(constants.strings('yt_query_error'), text)
|
||||
bot.send_msg(tr('yt_query_error'), text)
|
||||
else:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
|
||||
|
||||
def _yt_format_result(results, start, count):
|
||||
@ -529,16 +530,16 @@ def cmd_yt_play(bot, user, text, command, parameter):
|
||||
url = "https://www.youtube.com/watch?v=" + yt_last_result[0][0]
|
||||
cmd_play_url(bot, user, text, command, url)
|
||||
else:
|
||||
bot.send_msg(constants.strings('yt_query_error'), text)
|
||||
bot.send_msg(tr('yt_query_error'), text)
|
||||
else:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
|
||||
|
||||
def cmd_help(bot, user, text, command, parameter):
|
||||
global log
|
||||
bot.send_msg(constants.strings('help'), text)
|
||||
bot.send_msg(tr('help'), text)
|
||||
if bot.is_admin(user):
|
||||
bot.send_msg(constants.strings('admin_help'), text)
|
||||
bot.send_msg(tr('admin_help'), text)
|
||||
|
||||
|
||||
def cmd_stop(bot, user, text, command, parameter):
|
||||
@ -549,14 +550,14 @@ def cmd_stop(bot, user, text, command, parameter):
|
||||
cmd_clear(bot, user, text, command, parameter)
|
||||
else:
|
||||
bot.stop()
|
||||
bot.send_msg(constants.strings('stopped'), text)
|
||||
bot.send_msg(tr('stopped'), text)
|
||||
|
||||
|
||||
def cmd_clear(bot, user, text, command, parameter):
|
||||
global log
|
||||
|
||||
bot.clear()
|
||||
bot.send_msg(constants.strings('cleared'), text)
|
||||
bot.send_msg(tr('cleared'), text)
|
||||
|
||||
|
||||
def cmd_kill(bot, user, text, command, parameter):
|
||||
@ -571,12 +572,12 @@ def cmd_update(bot, user, text, command, parameter):
|
||||
|
||||
if bot.is_admin(user):
|
||||
bot.mumble.users[text.actor].send_text_message(
|
||||
constants.strings('start_updating'))
|
||||
tr('start_updating'))
|
||||
msg = util.update(bot.version)
|
||||
bot.mumble.users[text.actor].send_text_message(msg)
|
||||
else:
|
||||
bot.mumble.users[text.actor].send_text_message(
|
||||
constants.strings('not_admin'))
|
||||
tr('not_admin'))
|
||||
|
||||
|
||||
def cmd_stop_and_getout(bot, user, text, command, parameter):
|
||||
@ -595,11 +596,11 @@ def cmd_volume(bot, user, text, command, parameter):
|
||||
# The volume is a percentage
|
||||
if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
|
||||
bot.volume_helper.set_volume(float(parameter) / 100.0)
|
||||
bot.send_msg(constants.strings('change_volume', volume=parameter, user=bot.mumble.users[text.actor]['name']), text)
|
||||
bot.send_msg(tr('change_volume', volume=parameter, user=bot.mumble.users[text.actor]['name']), text)
|
||||
var.db.set('bot', 'volume', str(float(parameter) / 100.0))
|
||||
log.info(f'cmd: volume set to {float(parameter) / 100.0}')
|
||||
else:
|
||||
bot.send_msg(constants.strings('current_volume', volume=int(bot.volume_helper.plain_volume_set * 100)), text)
|
||||
bot.send_msg(tr('current_volume', volume=int(bot.volume_helper.plain_volume_set * 100)), text)
|
||||
|
||||
|
||||
def cmd_ducking(bot, user, text, command, parameter):
|
||||
@ -608,7 +609,7 @@ def cmd_ducking(bot, user, text, command, parameter):
|
||||
if parameter == "" or parameter == "on":
|
||||
bot.is_ducking = True
|
||||
var.db.set('bot', 'ducking', True)
|
||||
bot.mumble.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED, bot.ducking_sound_received)
|
||||
bot.mumble.callbacks.set_callback(pymumble.c.PYMUMBLE_CLBK_SOUNDRECEIVED, bot.ducking_sound_received)
|
||||
bot.mumble.set_receive_sound(True)
|
||||
log.info('cmd: ducking is on')
|
||||
msg = "Ducking on."
|
||||
@ -641,11 +642,11 @@ def cmd_ducking_volume(bot, user, text, command, parameter):
|
||||
# The volume is a percentage
|
||||
if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
|
||||
bot.volume_helper.set_ducking_volume(float(parameter) / 100.0)
|
||||
bot.send_msg(constants.strings('change_ducking_volume', volume=parameter, user=bot.mumble.users[text.actor]['name']), text)
|
||||
bot.send_msg(tr('change_ducking_volume', volume=parameter, user=bot.mumble.users[text.actor]['name']), text)
|
||||
var.db.set('bot', 'ducking_volume', float(parameter) / 100.0)
|
||||
log.info(f'cmd: volume on ducking set to {parameter}')
|
||||
else:
|
||||
bot.send_msg(constants.strings('current_ducking_volume', volume=int(bot.volume_helper.plain_ducking_volume_set * 100)), text)
|
||||
bot.send_msg(tr('current_ducking_volume', volume=int(bot.volume_helper.plain_ducking_volume_set * 100)), text)
|
||||
|
||||
|
||||
def cmd_current_music(bot, user, text, command, parameter):
|
||||
@ -654,7 +655,7 @@ def cmd_current_music(bot, user, text, command, parameter):
|
||||
if len(var.playlist) > 0:
|
||||
bot.send_msg(var.playlist.current_item().format_current_playing(), text)
|
||||
else:
|
||||
bot.send_msg(constants.strings('not_playing'), text)
|
||||
bot.send_msg(tr('not_playing'), text)
|
||||
|
||||
|
||||
def cmd_skip(bot, user, text, command, parameter):
|
||||
@ -667,7 +668,7 @@ def cmd_skip(bot, user, text, command, parameter):
|
||||
bot.wait_for_ready = True
|
||||
|
||||
if len(var.playlist) == 0:
|
||||
bot.send_msg(constants.strings('queue_empty'), text)
|
||||
bot.send_msg(tr('queue_empty'), text)
|
||||
|
||||
|
||||
def cmd_last(bot, user, text, command, parameter):
|
||||
@ -677,7 +678,7 @@ def cmd_last(bot, user, text, command, parameter):
|
||||
bot.interrupt()
|
||||
var.playlist.point_to(len(var.playlist) - 1 - 1)
|
||||
else:
|
||||
bot.send_msg(constants.strings('queue_empty'), text)
|
||||
bot.send_msg(tr('queue_empty'), text)
|
||||
|
||||
|
||||
def cmd_remove(bot, user, text, command, parameter):
|
||||
@ -690,8 +691,8 @@ def cmd_remove(bot, user, text, command, parameter):
|
||||
|
||||
if index == var.playlist.current_index:
|
||||
removed = var.playlist[index]
|
||||
bot.send_msg(constants.strings('removing_item',
|
||||
item=removed.format_title()), text)
|
||||
bot.send_msg(tr('removing_item',
|
||||
item=removed.format_title()), text)
|
||||
log.info("cmd: delete from playlist: " + removed.format_debug_string())
|
||||
|
||||
var.playlist.remove(index)
|
||||
@ -710,7 +711,7 @@ def cmd_remove(bot, user, text, command, parameter):
|
||||
var.playlist.remove(index)
|
||||
|
||||
else:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
|
||||
|
||||
def cmd_list_file(bot, user, text, command, parameter):
|
||||
@ -722,7 +723,7 @@ def cmd_list_file(bot, user, text, command, parameter):
|
||||
|
||||
song_shortlist = files
|
||||
|
||||
msgs = [constants.strings("multiple_file_found") + "<ul>"]
|
||||
msgs = [tr("multiple_file_found") + "<ul>"]
|
||||
try:
|
||||
count = 0
|
||||
for index, file in enumerate(files):
|
||||
@ -739,14 +740,14 @@ def cmd_list_file(bot, user, text, command, parameter):
|
||||
if count != 0:
|
||||
msgs.append("</ul>")
|
||||
if count > ITEMS_PER_PAGE:
|
||||
msgs.append(constants.strings("records_omitted"))
|
||||
msgs.append(constants.strings("shortlist_instruction"))
|
||||
msgs.append(tr("records_omitted"))
|
||||
msgs.append(tr("shortlist_instruction"))
|
||||
send_multi_lines(bot, msgs, text, "")
|
||||
else:
|
||||
bot.send_msg(constants.strings("no_file"), text)
|
||||
bot.send_msg(tr("no_file"), text)
|
||||
|
||||
except re.error as e:
|
||||
msg = constants.strings('wrong_pattern', error=str(e))
|
||||
msg = tr('wrong_pattern', error=str(e))
|
||||
bot.send_msg(msg, text)
|
||||
|
||||
|
||||
@ -754,10 +755,10 @@ def cmd_queue(bot, user, text, command, parameter):
|
||||
global log
|
||||
|
||||
if len(var.playlist) == 0:
|
||||
msg = constants.strings('queue_empty')
|
||||
msg = tr('queue_empty')
|
||||
bot.send_msg(msg, text)
|
||||
else:
|
||||
msgs = [constants.strings('queue_contents')]
|
||||
msgs = [tr('queue_contents')]
|
||||
for i, music in enumerate(var.playlist):
|
||||
tags = ''
|
||||
if len(music.item().tags) > 0:
|
||||
@ -797,25 +798,25 @@ def cmd_repeat(bot, user, text, command, parameter):
|
||||
)
|
||||
log.info("bot: add to playlist: " + music.format_debug_string())
|
||||
|
||||
bot.send_channel_msg(constants.strings("repeat", song=music.format_song_string(), n=str(repeat)))
|
||||
bot.send_channel_msg(tr("repeat", song=music.format_song_string(), n=str(repeat)))
|
||||
else:
|
||||
bot.send_msg(constants.strings("queue_empty"), text)
|
||||
bot.send_msg(tr("queue_empty"), text)
|
||||
|
||||
|
||||
def cmd_mode(bot, user, text, command, parameter):
|
||||
global log
|
||||
|
||||
if not parameter:
|
||||
bot.send_msg(constants.strings("current_mode", mode=var.playlist.mode), text)
|
||||
bot.send_msg(tr("current_mode", mode=var.playlist.mode), text)
|
||||
return
|
||||
if parameter not in ["one-shot", "repeat", "random", "autoplay"]:
|
||||
bot.send_msg(constants.strings('unknown_mode', mode=parameter), text)
|
||||
bot.send_msg(tr('unknown_mode', mode=parameter), text)
|
||||
else:
|
||||
var.db.set('playlist', 'playback_mode', parameter)
|
||||
var.playlist = media.playlist.get_playlist(parameter, var.playlist)
|
||||
log.info(f"command: playback mode changed to {parameter}.")
|
||||
bot.send_msg(constants.strings("change_mode", mode=var.playlist.mode,
|
||||
user=bot.mumble.users[text.actor]['name']), text)
|
||||
bot.send_msg(tr("change_mode", mode=var.playlist.mode,
|
||||
user=bot.mumble.users[text.actor]['name']), text)
|
||||
if parameter == "random":
|
||||
bot.interrupt()
|
||||
bot.launch_music()
|
||||
@ -823,10 +824,10 @@ def cmd_mode(bot, user, text, command, parameter):
|
||||
|
||||
def cmd_play_tags(bot, user, text, command, parameter):
|
||||
if not parameter:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
return
|
||||
|
||||
msgs = [constants.strings('multiple_file_added') + "<ul>"]
|
||||
msgs = [tr('multiple_file_added') + "<ul>"]
|
||||
count = 0
|
||||
|
||||
tags = parameter.split(",")
|
||||
@ -842,7 +843,7 @@ def cmd_play_tags(bot, user, text, command, parameter):
|
||||
var.playlist.extend(music_wrappers)
|
||||
send_multi_lines_in_channel(bot, msgs, "")
|
||||
else:
|
||||
bot.send_msg(constants.strings("no_file"), text)
|
||||
bot.send_msg(tr("no_file"), text)
|
||||
|
||||
|
||||
def cmd_add_tag(bot, user, text, command, parameter):
|
||||
@ -866,19 +867,19 @@ def cmd_add_tag(bot, user, text, command, parameter):
|
||||
if index.isdigit() and 1 <= int(index) <= len(var.playlist):
|
||||
var.playlist[int(index) - 1].add_tags(tags)
|
||||
log.info(f"cmd: add tags {', '.join(tags)} to song {var.playlist[int(index) - 1].format_debug_string()}")
|
||||
bot.send_msg(constants.strings("added_tags",
|
||||
tags=", ".join(tags),
|
||||
song=var.playlist[int(index) - 1].format_title()), text)
|
||||
bot.send_msg(tr("added_tags",
|
||||
tags=", ".join(tags),
|
||||
song=var.playlist[int(index) - 1].format_title()), text)
|
||||
return
|
||||
|
||||
elif index == "*":
|
||||
for item in var.playlist:
|
||||
item.add_tags(tags)
|
||||
log.info(f"cmd: add tags {', '.join(tags)} to song {item.format_debug_string()}")
|
||||
bot.send_msg(constants.strings("added_tags_to_all", tags=", ".join(tags)), text)
|
||||
bot.send_msg(tr("added_tags_to_all", tags=", ".join(tags)), text)
|
||||
return
|
||||
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
|
||||
|
||||
def cmd_remove_tag(bot, user, text, command, parameter):
|
||||
@ -903,15 +904,15 @@ def cmd_remove_tag(bot, user, text, command, parameter):
|
||||
if tags[0] != "*":
|
||||
var.playlist[int(index) - 1].remove_tags(tags)
|
||||
log.info(f"cmd: remove tags {', '.join(tags)} from song {var.playlist[int(index) - 1].format_debug_string()}")
|
||||
bot.send_msg(constants.strings("removed_tags",
|
||||
tags=", ".join(tags),
|
||||
song=var.playlist[int(index) - 1].format_title()), text)
|
||||
bot.send_msg(tr("removed_tags",
|
||||
tags=", ".join(tags),
|
||||
song=var.playlist[int(index) - 1].format_title()), text)
|
||||
return
|
||||
else:
|
||||
var.playlist[int(index) - 1].clear_tags()
|
||||
log.info(f"cmd: clear tags from song {var.playlist[int(index) - 1].format_debug_string()}")
|
||||
bot.send_msg(constants.strings("cleared_tags",
|
||||
song=var.playlist[int(index) - 1].format_title()), text)
|
||||
bot.send_msg(tr("cleared_tags",
|
||||
song=var.playlist[int(index) - 1].format_title()), text)
|
||||
return
|
||||
|
||||
elif index == "*":
|
||||
@ -919,26 +920,26 @@ def cmd_remove_tag(bot, user, text, command, parameter):
|
||||
for item in var.playlist:
|
||||
item.remove_tags(tags)
|
||||
log.info(f"cmd: remove tags {', '.join(tags)} from song {item.format_debug_string()}")
|
||||
bot.send_msg(constants.strings("removed_tags_from_all", tags=", ".join(tags)), text)
|
||||
bot.send_msg(tr("removed_tags_from_all", tags=", ".join(tags)), text)
|
||||
return
|
||||
else:
|
||||
for item in var.playlist:
|
||||
item.clear_tags()
|
||||
log.info(f"cmd: clear tags from song {item.format_debug_string()}")
|
||||
bot.send_msg(constants.strings("cleared_tags_from_all"), text)
|
||||
bot.send_msg(tr("cleared_tags_from_all"), text)
|
||||
return
|
||||
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
|
||||
|
||||
def cmd_find_tagged(bot, user, text, command, parameter):
|
||||
global song_shortlist
|
||||
|
||||
if not parameter:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
return
|
||||
|
||||
msgs = [constants.strings('multiple_file_found') + "<ul>"]
|
||||
msgs = [tr('multiple_file_found') + "<ul>"]
|
||||
count = 0
|
||||
|
||||
tags = parameter.split(",")
|
||||
@ -957,20 +958,20 @@ def cmd_find_tagged(bot, user, text, command, parameter):
|
||||
if count != 0:
|
||||
msgs.append("</ul>")
|
||||
if count > ITEMS_PER_PAGE:
|
||||
msgs.append(constants.strings("records_omitted"))
|
||||
msgs.append(constants.strings("shortlist_instruction"))
|
||||
msgs.append(tr("records_omitted"))
|
||||
msgs.append(tr("shortlist_instruction"))
|
||||
send_multi_lines(bot, msgs, text, "")
|
||||
else:
|
||||
bot.send_msg(constants.strings("no_file"), text)
|
||||
bot.send_msg(tr("no_file"), text)
|
||||
|
||||
|
||||
def cmd_search_library(bot, user, text, command, parameter):
|
||||
global song_shortlist
|
||||
if not parameter:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
return
|
||||
|
||||
msgs = [constants.strings('multiple_file_found') + "<ul>"]
|
||||
msgs = [tr('multiple_file_found') + "<ul>"]
|
||||
count = 0
|
||||
|
||||
_keywords = parameter.split(" ")
|
||||
@ -988,7 +989,7 @@ def cmd_search_library(bot, user, text, command, parameter):
|
||||
music_wrapper = get_cached_wrapper(items[0], user)
|
||||
var.playlist.append(music_wrapper)
|
||||
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
|
||||
bot.send_channel_msg(constants.strings('file_added', item=music_wrapper.format_song_string()))
|
||||
bot.send_channel_msg(tr('file_added', item=music_wrapper.format_song_string()))
|
||||
else:
|
||||
for item in items:
|
||||
count += 1
|
||||
@ -1002,19 +1003,19 @@ def cmd_search_library(bot, user, text, command, parameter):
|
||||
if count != 0:
|
||||
msgs.append("</ul>")
|
||||
if count > ITEMS_PER_PAGE:
|
||||
msgs.append(constants.strings("records_omitted"))
|
||||
msgs.append(constants.strings("shortlist_instruction"))
|
||||
msgs.append(tr("records_omitted"))
|
||||
msgs.append(tr("shortlist_instruction"))
|
||||
send_multi_lines(bot, msgs, text, "")
|
||||
else:
|
||||
bot.send_msg(constants.strings("no_file"), text)
|
||||
bot.send_msg(tr("no_file"), text)
|
||||
else:
|
||||
bot.send_msg(constants.strings("no_file"), text)
|
||||
bot.send_msg(tr("no_file"), text)
|
||||
|
||||
|
||||
def cmd_shortlist(bot, user, text, command, parameter):
|
||||
global song_shortlist, log
|
||||
if parameter.strip() == "*":
|
||||
msgs = [constants.strings('multiple_file_added') + "<ul>"]
|
||||
msgs = [tr('multiple_file_added') + "<ul>"]
|
||||
music_wrappers = []
|
||||
for kwargs in song_shortlist:
|
||||
kwargs['user'] = user
|
||||
@ -1032,11 +1033,11 @@ def cmd_shortlist(bot, user, text, command, parameter):
|
||||
try:
|
||||
indexes = [int(i) for i in parameter.split(" ")]
|
||||
except ValueError:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
return
|
||||
|
||||
if len(indexes) > 1:
|
||||
msgs = [constants.strings('multiple_file_added') + "<ul>"]
|
||||
msgs = [tr('multiple_file_added') + "<ul>"]
|
||||
music_wrappers = []
|
||||
for index in indexes:
|
||||
if 1 <= index <= len(song_shortlist):
|
||||
@ -1048,7 +1049,7 @@ def cmd_shortlist(bot, user, text, command, parameter):
|
||||
msgs.append("<li>[{}] <b>{}</b></li>".format(music_wrapper.item().type, music_wrapper.item().title))
|
||||
else:
|
||||
var.playlist.extend(music_wrappers)
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
return
|
||||
|
||||
var.playlist.extend(music_wrappers)
|
||||
@ -1064,10 +1065,10 @@ def cmd_shortlist(bot, user, text, command, parameter):
|
||||
music_wrapper = get_cached_wrapper_from_scrap(**kwargs)
|
||||
var.playlist.append(music_wrapper)
|
||||
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
|
||||
bot.send_channel_msg(constants.strings('file_added', item=music_wrapper.format_song_string()))
|
||||
bot.send_channel_msg(tr('file_added', item=music_wrapper.format_song_string()))
|
||||
return
|
||||
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
|
||||
|
||||
def cmd_delete_from_library(bot, user, text, command, parameter):
|
||||
@ -1075,11 +1076,11 @@ def cmd_delete_from_library(bot, user, text, command, parameter):
|
||||
try:
|
||||
indexes = [int(i) for i in parameter.split(" ")]
|
||||
except ValueError:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
return
|
||||
|
||||
if len(indexes) > 1:
|
||||
msgs = [constants.strings('multiple_file_added') + "<ul>"]
|
||||
msgs = [tr('multiple_file_added') + "<ul>"]
|
||||
count = 0
|
||||
for index in indexes:
|
||||
if 1 <= index <= len(song_shortlist):
|
||||
@ -1092,11 +1093,11 @@ def cmd_delete_from_library(bot, user, text, command, parameter):
|
||||
var.cache.free_and_delete(music_dict['id'])
|
||||
count += 1
|
||||
else:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
return
|
||||
|
||||
if count == 0:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
return
|
||||
|
||||
msgs.append("</ul>")
|
||||
@ -1108,13 +1109,13 @@ def cmd_delete_from_library(bot, user, text, command, parameter):
|
||||
music_dict = song_shortlist[index - 1]
|
||||
if 'id' in music_dict:
|
||||
music_wrapper = get_cached_wrapper_by_id(music_dict['id'], user)
|
||||
bot.send_msg(constants.strings('file_deleted', item=music_wrapper.format_song_string()), text)
|
||||
bot.send_msg(tr('file_deleted', item=music_wrapper.format_song_string()), text)
|
||||
log.info("cmd: remove from library: " + music_wrapper.format_debug_string())
|
||||
var.playlist.remove_by_id(music_dict['id'])
|
||||
var.cache.free_and_delete(music_dict['id'])
|
||||
return
|
||||
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
|
||||
|
||||
def cmd_drop_database(bot, user, text, command, parameter):
|
||||
@ -1126,9 +1127,9 @@ def cmd_drop_database(bot, user, text, command, parameter):
|
||||
var.music_db.drop_table()
|
||||
var.music_db = MusicDatabase(var.settings_db_path)
|
||||
log.info("command: database dropped.")
|
||||
bot.send_msg(constants.strings('database_dropped'), text)
|
||||
bot.send_msg(tr('database_dropped'), text)
|
||||
else:
|
||||
bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
|
||||
bot.mumble.users[text.actor].send_text_message(tr('not_admin'))
|
||||
|
||||
|
||||
def cmd_refresh_cache(bot, user, text, command, parameter):
|
||||
@ -1136,9 +1137,9 @@ def cmd_refresh_cache(bot, user, text, command, parameter):
|
||||
if bot.is_admin(user):
|
||||
var.cache.build_dir_cache()
|
||||
log.info("command: Local file cache refreshed.")
|
||||
bot.send_msg(constants.strings('cache_refreshed'), text)
|
||||
bot.send_msg(tr('cache_refreshed'), text)
|
||||
else:
|
||||
bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
|
||||
bot.mumble.users[text.actor].send_text_message(tr('not_admin'))
|
||||
|
||||
|
||||
def cmd_web_access(bot, user, text, command, parameter):
|
||||
@ -1164,12 +1165,12 @@ def cmd_web_access(bot, user, text, command, parameter):
|
||||
else:
|
||||
access_address = var.config.get("webinterface", "access_address")
|
||||
|
||||
bot.send_msg(constants.strings('webpage_address', address=access_address), text)
|
||||
bot.send_msg(tr('webpage_address', address=access_address), text)
|
||||
|
||||
|
||||
def cmd_user_password(bot, user, text, command, parameter):
|
||||
if not parameter:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
return
|
||||
|
||||
user_info = var.db.get("user", user, fallback='{}')
|
||||
@ -1178,12 +1179,12 @@ def cmd_user_password(bot, user, text, command, parameter):
|
||||
|
||||
var.db.set("user", user, json.dumps(user_dict))
|
||||
|
||||
bot.send_msg(constants.strings('user_password_set'), text)
|
||||
bot.send_msg(tr('user_password_set'), text)
|
||||
|
||||
|
||||
def cmd_web_user_add(bot, user, text, command, parameter):
|
||||
if not parameter:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
return
|
||||
|
||||
auth_method = var.config.get("webinterface", "auth_method")
|
||||
@ -1193,14 +1194,14 @@ def cmd_web_user_add(bot, user, text, command, parameter):
|
||||
if parameter not in web_users:
|
||||
web_users.append(parameter)
|
||||
var.db.set("privilege", "web_access", json.dumps(web_users))
|
||||
bot.send_msg(constants.strings('web_user_list', users=", ".join(web_users)), text)
|
||||
bot.send_msg(tr('web_user_list', users=", ".join(web_users)), text)
|
||||
else:
|
||||
bot.send_msg(constants.strings('command_disabled', command=command), text)
|
||||
bot.send_msg(tr('command_disabled', command=command), text)
|
||||
|
||||
|
||||
def cmd_web_user_remove(bot, user, text, command, parameter):
|
||||
if not parameter:
|
||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||
bot.send_msg(tr('bad_parameter', command=command), text)
|
||||
return
|
||||
|
||||
auth_method = var.config.get("webinterface", "auth_method")
|
||||
@ -1210,9 +1211,9 @@ def cmd_web_user_remove(bot, user, text, command, parameter):
|
||||
if parameter in web_users:
|
||||
web_users.remove(parameter)
|
||||
var.db.set("privilege", "web_access", json.dumps(web_users))
|
||||
bot.send_msg(constants.strings('web_user_list', users=", ".join(web_users)), text)
|
||||
bot.send_msg(tr('web_user_list', users=", ".join(web_users)), text)
|
||||
else:
|
||||
bot.send_msg(constants.strings('command_disabled', command=command), text)
|
||||
bot.send_msg(tr('command_disabled', command=command), text)
|
||||
|
||||
|
||||
def cmd_web_user_list(bot, user, text, command, parameter):
|
||||
@ -1220,9 +1221,9 @@ def cmd_web_user_list(bot, user, text, command, parameter):
|
||||
|
||||
if auth_method == 'password':
|
||||
web_users = json.loads(var.db.get("privilege", "web_access", fallback='[]'))
|
||||
bot.send_msg(constants.strings('web_user_list', users=", ".join(web_users)), text)
|
||||
bot.send_msg(tr('web_user_list', users=", ".join(web_users)), text)
|
||||
else:
|
||||
bot.send_msg(constants.strings('command_disabled', command=command), text)
|
||||
bot.send_msg(tr('command_disabled', command=command), text)
|
||||
|
||||
|
||||
# Just for debug use
|
||||
|
@ -195,159 +195,3 @@ list_webinterface_user = webuserlist
|
||||
add_webinterface_user = webuseradd
|
||||
remove_webinterface_user = webuserdel
|
||||
change_user_password = password
|
||||
|
||||
[strings]
|
||||
current_volume = Current volume: {volume}.
|
||||
current_ducking_volume = Volume on ducking: {volume}.
|
||||
change_volume = Volume set to {volume} by {user}.
|
||||
change_ducking_volume = Volume on ducking set to {volume} by {user}.
|
||||
bad_command = {command}: command not found.
|
||||
bad_parameter = {command}: invalid parameter.
|
||||
error_executing_command = {command}: Command failed with error: {error}.
|
||||
not_admin = You are not an admin!
|
||||
not_playing = Nothing is playing right now.
|
||||
no_file = File not found.
|
||||
wrong_pattern = Invalid regex: {error}.
|
||||
file_added = Added {item}.
|
||||
file_deleted = Deleted {item} from the library.
|
||||
multiple_file_added = Multiple items added:
|
||||
multiple_file_deleted = Multiple items deleted from the library:
|
||||
multiple_file_found = Found:
|
||||
page_instruction = Page {current}/{total}. Use <i>!{command} {{page}}</i> to navigate.
|
||||
records_omitted = ...
|
||||
bad_url = Bad URL requested.
|
||||
preconfigurated_radio = Preconfigurated Radio available:
|
||||
unable_download = Unable to download <b>{item}</b>. Removed from the library.
|
||||
unable_play = Unable to play <b>{item}</b>. Removed from the library.
|
||||
which_command = Do you mean <br /> {commands}
|
||||
multiple_matches = File not found! Possible candidates:
|
||||
queue_contents = Items on the playlist:
|
||||
queue_empty = Playlist is empty!
|
||||
invalid_index = Invalid index <i>{index}</i>. Use '!queue' to see your playlist.
|
||||
now_playing = Playing {item}
|
||||
radio = Radio
|
||||
file = File
|
||||
url_from_playlist = URL
|
||||
url = URL
|
||||
radio_item = <a href="{url}"><b>{title}</b></a> <i>from</i> {name} <i>added by</i> {user}
|
||||
file_item = <b>{artist} - {title}</b> <i>added by</i> {user}
|
||||
url_from_playlist_item = <a href="{url}"><b>{title}</b></a> <i>from playlist</i> <a href="{playlist_url}">{playlist}</a> <i>added by</i> {user}
|
||||
url_item = <a href="{url}"><b>{title}</b></a> <i>added by</i> {user}
|
||||
not_in_my_channel = You're not in my channel, command refused!
|
||||
pm_not_allowed = Private message aren't allowed.
|
||||
too_long = <b>{song}</b> is too long, removed from playlist!
|
||||
download_in_progress = Download of <b>{item}</b> in progress...
|
||||
removing_item = Removed entry {item} from playlist.
|
||||
user_ban = You are banned, not allowed to do that!
|
||||
url_ban = This url is banned!
|
||||
rb_query_result = This is the result of your query, send !rbplay 'ID' to play a station:
|
||||
rb_play_empty = Please specify a radio station ID!
|
||||
paused = Music paused.
|
||||
stopped = Music stopped.
|
||||
cleared = Playlist emptied.
|
||||
database_dropped = Database dropped. All records have gone.
|
||||
new_version_found = <h2>Update Available!</h2> Version {new_version} of botamusique is available! <hr />
|
||||
<h3>Changelog</h3> {changelog} <hr /> Send <i>!update</i> to update!
|
||||
update_successful = <h2>botamusique v{version} Installed!</h2><hr />
|
||||
<h3>Changelog</h3> {changelog} <hr /> Visit <a href="https://github.com/azlux/botamusique">our github repo</a> for more details!
|
||||
start_updating = Start updating...
|
||||
file_missed = Music file '{file}' missed! This item has been removed from the playlist.
|
||||
unknown_mode = Unknown playback mode '{mode}'. It should be one of <i>one-shot</i>, <i>repeat</i>, <i>random</i>.
|
||||
current_mode = Current playback mode is <i>{mode}</i>.
|
||||
change_mode = Playback mode set to <i>{mode}</i> by {user}.
|
||||
repeat = Repeat {song} for {n} times.
|
||||
yt_result = Youtube query result: {result_table} Use <i>!sl {{indexes}}</i> to play the item you want. <br />
|
||||
<i>!ytquery -n</i> for the next page.
|
||||
yt_no_more = No more results!
|
||||
yt_query_error = Unable to query youtube!
|
||||
playlist_fetching_failed = Unable to fetch the playlist!
|
||||
cache_refreshed = Cache refreshed!
|
||||
added_tags = Added tags <i>{tags}</i> to <b>{song}</b>.
|
||||
added_tags_to_all = Added tags <i>{tags}</i> to songs on the playlist.
|
||||
removed_tags = Removed tags <i>{tags}</i> from <b>{song}</b>.
|
||||
removed_tags_from_all = Removed tags <i>{tags}</i> from songs on the playlist.
|
||||
cleared_tags = Removed all tags from <b>{song}</b>.
|
||||
cleared_tags_from_all = Removed all tags from songs on the playlist.
|
||||
shortlist_instruction = Use <i>!sl {indexes}</i> to play the item you want.
|
||||
auto_paused = Use <i>!play</i> to resume music!
|
||||
webpage_address= Your own address to access the web interface is <a href="{address}">{address}</a>
|
||||
web_user_list = Following users has the privilege to access the web interface: <br /> {users}
|
||||
user_password_set = Your password has been updated.
|
||||
command_disabled = {command}: command disabled!
|
||||
|
||||
help = <h3>Commands</h3>
|
||||
<b>Control</b>
|
||||
<ul>
|
||||
<li> <b>!<u>w</u>eb</b> - get the URL of the web interface, if enabled. </li>
|
||||
<li> <b>!play </b> (or <b>!p</b>) [{num}] [{start_from}] - resume from pausing / start to play (the num-th song is num if given) </li>
|
||||
<li> <b>!<u>pa</u>use </b> - pause </li>
|
||||
<li> <b>!<u>st</u>op </b> - stop playing </li>
|
||||
<li> <b>!<u>sk</u>ip </b> - jump to the next song </li>
|
||||
<li> <b>!<u>la</u>st </b> - jump to the last song </li>
|
||||
<li> <b>!<u>v</u>olume </b> {volume} - get or change the volume (from 0 to 100) </li>
|
||||
<li> <b>!<u>m</u>ode </b> [{mode}] - get or set the playback mode, {mode} should be one of <i>one-shot</i> (remove
|
||||
item once played), <i>repeat</i> (looping through the playlist), <i>random</i> (randomize the playlist),
|
||||
<i>autoplay</i> (randomly grab something from the music library).</li>
|
||||
<li> <b>!duck </b> on/off - enable or disable ducking function </li>
|
||||
<li> <b>!duckv </b> - set the volume of the bot when ducking is activated </li>
|
||||
<li> <b>!<u>duckt</u>hres </b> - set the threshold of volume to activate ducking (3000 by default) </li>
|
||||
<li> <b>!<u>o</u>ust </b> - stop playing and go to default channel </li>
|
||||
</ul>
|
||||
<b>Playlist</b>
|
||||
<ul>
|
||||
<li> <b>!<u>n</u>ow </b> (or <b>!np</b>) - display the current song </li>
|
||||
<li> <b>!<u>q</u>ueue </b> - display items in the playlist </li>
|
||||
<li> <b>!<u>t</u>ag </b> {tags} - add all items with tags {tags}, tags separated by ",". </li>
|
||||
<li> <b>!file </b>(or <b>!f</b>) {path/folder/keyword} - add a single file to the playlist by its path or keyword in its path. </li>
|
||||
<li> <b>!<u>filem</u>atch </b>(or <b>!fm</b>) {pattern} - add all files that match regex {pattern} </li>
|
||||
<li> <b>!<u>ur</u>l </b> {url} - add Youtube or SoundCloud music </li>
|
||||
<li> <b>!<u>playl</u>ist </b> {url} [{offset}] - add all items in a Youtube or SoundCloud playlist, and start with the {offset}-th item </li>
|
||||
<li> <b>!<u>rad</u>io </b> {url} - append a radio {url} to the playlist </li>
|
||||
<li> <b>!<u>rbq</u>uery </b> {keyword} - query http://www.radio-browser.info for a radio station </li>
|
||||
<li> <b>!<u>rbp</u>lay </b> {id} - play a radio station with {id} (eg. !rbplay 96746) </li>
|
||||
<li> <b>!<u>ys</u>earch </b> {keywords} - query youtube. Use <i>!ysearch -n</i> to turn the page. </li>
|
||||
<li> <b>!<u>yp</u>lay </b> {keywords} - add the first search result of {keywords} into the playlist.</li>
|
||||
<li> <b>!<u>sh</u>ortlist </b> (or <b>!sl</b>) {indexes/*} - add {indexes}-th item (or all items if * is given) on the shortlist. </li>
|
||||
<li> <b>!rm </b> {num} - remove the num-th song on the playlist </li>
|
||||
<li> <b>!<u>rep</u>eat </b> [{num}] - repeat current song {num} (1 by default) times.</li>
|
||||
<li> <b>!<u>ran</u>dom </b> - randomize the playlist.</li>
|
||||
</ul>
|
||||
<b>Music Library</b>
|
||||
<ul>
|
||||
<li> <b>!<u>se</u>arch </b> {keywords} - find item with {keywords} in the music library, keywords separated by space.</li>
|
||||
<li> <b>!<u>li</u>stfile </b> [{pattern}] - display list of available files (whose paths match the regex pattern if {pattern} is given) </li>
|
||||
<li> <b>!<u>addt</u>ag </b> [{index}] {tags} - add {tags} to {index}-th(current song if {index} is omitted) item on the playlist, tags separated by ",". </li>
|
||||
<li> <b>!<u>addt</u>ag </b> * {tags} - add {tags} to all items on the playlist. </li>
|
||||
<li> <b>!<u>un</u>tag </b> [{index/*}] {tags}/* - remove {tags}/all tags from {index}-th(current song if {index} is omitted) item on the playlist. </li>
|
||||
<li> <b>!<u>fin</u>dtagged </b> (or <b>!ft</b>) {tags} - find item with {tags} in the music library. </li>
|
||||
<li> <b>!<u>del</u>ete </b> {index} - delete {index}-th item on the shortlist from the music library. </li>
|
||||
</ul>
|
||||
<b>Other</b>
|
||||
<ul>
|
||||
<li> <b>!<u>j</u>oinme {token} </b> - join your own channel with {token}.</li>
|
||||
<li> <b>!<u>password</u> {password} </b> - change your password, used to access the web interface.</li>
|
||||
</ul>
|
||||
|
||||
admin_help = <h3>Admin command</h3>
|
||||
<b>Bot</b>
|
||||
<ul>
|
||||
<li><b>!<u>k</u>ill </b> - kill the bot</li>
|
||||
<li><b>!update </b> - update the bot</li>
|
||||
<li><b>!userban </b> {user} - ban a user</li>
|
||||
<li><b>!userunban </b> {user} - unban a user</li>
|
||||
<li><b>!urlbanlist </b> - list banned url</li>
|
||||
<li><b>!urlban </b> [{url}] - ban {url} (or current item's url by default) and remove this url from the library.</li>
|
||||
<li><b>!urlunban </b> {url} - unban {url}</li>
|
||||
<li><b>!rescan </b> {url} - rebuild local music file cache</li>
|
||||
<li><b>!dropdatabase</b> - clear the entire database, you will lose all settings and music library.</li>
|
||||
</ul>
|
||||
<b>Web Interface</b>
|
||||
<ul>
|
||||
<li><b>!<u>webuserlist</u></b> - list all users that have the permission of accessing the web interface, if auth mode is 'password'.</li>
|
||||
<li><b>!<u>webuseradd</u> {nick name}</b> - grant the user with {nick name} the access to the web interface, if auth mode is 'password'.</li>
|
||||
<li><b>!<u>webuserdel</u> {nick name}</b> - revoke the access to the web interface of {nick name}, if auth mode is 'password'.</li>
|
||||
<li><b>!update </b> - update the bot</li>
|
||||
<li><b>!userban </b> {user} - ban a user</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
@ -177,18 +177,3 @@ port = 64738
|
||||
# see configuration.default.ini. Copy options you want to edit into this file.
|
||||
#play_file = file, f
|
||||
#play_file_match = filematch, fm
|
||||
|
||||
# [strings] is used to compose what the bot says. You can customize them to fit in
|
||||
# the style of your channel, or translate into your own language.
|
||||
# For a full list of strings, please see configuration.default.ini.
|
||||
# Copy options you want to edit into this file.
|
||||
# Note: please keep those {placeholder} of each string in your new string.
|
||||
[strings]
|
||||
# Some examples are:
|
||||
#current_volume = Current volume: {volume}
|
||||
#current_volume = 当前音量为{volume}
|
||||
#current_volume = よく聞いてね!今の音量は{volume}!<br />ちゃんと覚える:大音量で耳が悪くなる!
|
||||
#
|
||||
#bad_command = {command}: command not found
|
||||
#bad_command = {command}: 未知命令,键入'!help'以获取可用命令列表
|
||||
#bad_command = {command}がなに?食べれる?おいしいでしか?
|
||||
|
49
constants.py
49
constants.py
@ -1,24 +1,54 @@
|
||||
import json
|
||||
|
||||
import variables as var
|
||||
|
||||
default_lang_dict = {}
|
||||
lang_dict = {}
|
||||
|
||||
def strings(option, *argv, **kwargs):
|
||||
|
||||
def load_lang(lang):
|
||||
global lang_dict, default_lang_dict
|
||||
with open("lang/en_US.json", "r") as f:
|
||||
default_lang_dict = json.load(f)
|
||||
with open(f"lang/{lang}.json", "r") as f:
|
||||
lang_dict = json.load(f)
|
||||
|
||||
|
||||
def tr_cli(option, *argv, **kwargs):
|
||||
try:
|
||||
string = var.config.get("strings", option)
|
||||
if option in lang_dict['cli'] and lang_dict['cli'][option]:
|
||||
string = lang_dict['cli'][option]
|
||||
else:
|
||||
string = default_lang_dict['cli'][option]
|
||||
except KeyError:
|
||||
raise KeyError("Missed strings in configuration file: '{string}'. ".format(string=option)
|
||||
+ "Please restore you configuration file back to default if necessary.")
|
||||
raise KeyError("Missed strings in language file: '{string}'. ".format(string=option))
|
||||
return _tr(string, *argv, **kwargs)
|
||||
|
||||
|
||||
def tr_web(option, *argv, **kwargs):
|
||||
try:
|
||||
if option in lang_dict['web'] and lang_dict['web'][option]:
|
||||
string = lang_dict['web'][option]
|
||||
else:
|
||||
string = default_lang_dict['web'][option]
|
||||
except KeyError:
|
||||
raise KeyError("Missed strings in language file: '{string}'. ".format(string=option))
|
||||
return _tr(string, *argv, **kwargs)
|
||||
|
||||
|
||||
def _tr(string, *argv, **kwargs):
|
||||
if argv or kwargs:
|
||||
try:
|
||||
formatted = string.format(*argv, **kwargs)
|
||||
return formatted
|
||||
except KeyError as e:
|
||||
raise KeyError(
|
||||
"Missed/Unexpected placeholder {{{placeholder}}} in string '{string}'. ".format(placeholder=str(e).strip("'"), string=option)
|
||||
+ "Please restore you configuration file back to default if necessary.")
|
||||
"Missed/Unexpected placeholder {{{placeholder}}} in string "
|
||||
"'{string}'. ".format(placeholder=str(e).strip("'"),
|
||||
string=string))
|
||||
except TypeError:
|
||||
raise KeyError(
|
||||
"Missed placeholder in string '{string}'. ".format(string=option)
|
||||
+ "Please restore you configuration file back to default if necessary.")
|
||||
"Missed placeholder in string '{string}'. ".format(string=string))
|
||||
else:
|
||||
return string
|
||||
|
||||
@ -28,5 +58,4 @@ def commands(command):
|
||||
string = var.config.get("commands", command)
|
||||
return string
|
||||
except KeyError:
|
||||
raise KeyError("Missed command in configuration file: '{string}'. ".format(string=command)
|
||||
+ "Please restore you configuration file back to default if necessary.")
|
||||
raise KeyError("Missed command in configuration file: '{string}'. ".format(string=command))
|
||||
|
@ -169,7 +169,7 @@ def requires_auth(f):
|
||||
bad_access_count[request.remote_addr] = 1
|
||||
log.info(f"web: bad token from ip {request.remote_addr}.")
|
||||
|
||||
return render_template('need_token.html',
|
||||
return render_template(f'need_token.{var.language}.html',
|
||||
name=var.config.get('bot', 'username'),
|
||||
command=f"{var.config.get('commands', 'command_symbol')[0]}{var.config.get('commands', 'requests_webinterface_access')}")
|
||||
|
||||
@ -225,7 +225,7 @@ def get_all_dirs():
|
||||
@web.route("/", methods=['GET'])
|
||||
@requires_auth
|
||||
def index():
|
||||
return open('templates/index.html', "r").read()
|
||||
return open(f"templates/index.{var.language}.html", "r").read()
|
||||
|
||||
|
||||
@web.route("/playlist", methods=['GET'])
|
||||
@ -554,7 +554,6 @@ def library_info():
|
||||
tags = var.music_db.query_all_tags()
|
||||
max_upload_file_size = util.parse_file_size(var.config.get("webinterface", "max_upload_file_size", fallback="30MB"))
|
||||
|
||||
print(get_all_dirs())
|
||||
return jsonify(dict(
|
||||
dirs=get_all_dirs(),
|
||||
upload_enabled=var.config.getboolean("webinterface", "upload_enabled", fallback=True),
|
||||
|
157
lang/en_US.json
Normal file
157
lang/en_US.json
Normal file
@ -0,0 +1,157 @@
|
||||
{
|
||||
"cli": {
|
||||
"added_tags": "Added tags <i>{tags}</i> to <b>{song}</b>.",
|
||||
"added_tags_to_all": "Added tags <i>{tags}</i> to songs on the playlist.",
|
||||
"admin_help": "<h3>Admin command</h3>\n<b>Bot</b>\n<ul>\n<li><b>!<u>k</u>ill </b> - kill the bot</li>\n<li><b>!update </b> - update the bot</li>\n<li><b>!userban </b> {user} - ban a user</li>\n<li><b>!userunban </b> {user} - unban a user</li>\n<li><b>!urlbanlist </b> - list banned url</li>\n<li><b>!urlban </b> [{url}] - ban {url} (or current item's url by default) and remove this url from the library.</li>\n<li><b>!urlunban </b> {url} - unban {url}</li>\n<li><b>!rescan </b> {url} - rebuild local music file cache</li>\n<li><b>!dropdatabase</b> - clear the entire database, you will lose all settings and music library.</li>\n</ul>\n<b>Web Interface</b>\n<ul>\n<li><b>!<u>webuserlist</u></b> - list all users that have the permission of accessing the web interface, if auth mode is 'password'.</li>\n<li><b>!<u>webuseradd</u> {nick name}</b> - grant the user with {nick name} the access to the web interface, if auth mode is 'password'.</li>\n<li><b>!<u>webuserdel</u> {nick name}</b> - revoke the access to the web interface of {nick name}, if auth mode is 'password'.</li>\n<li><b>!update </b> - update the bot</li>\n<li><b>!userban </b> {user} - ban a user</li>\n</ul>",
|
||||
"auto_paused": "Use <i>!play</i> to resume music!",
|
||||
"bad_command": "<i>{command}</i>: command not found.",
|
||||
"bad_parameter": "<i>{command}</i>: invalid parameter.",
|
||||
"bad_url": "Bad URL requested.",
|
||||
"cache_refreshed": "Cache refreshed!",
|
||||
"change_ducking_volume": "Volume on ducking set to {volume} by {user}.",
|
||||
"change_mode": "Playback mode set to <i>{mode}</i> by {user}.",
|
||||
"change_volume": "Volume set to {volume} by {user}.",
|
||||
"cleared": "Playlist emptied.",
|
||||
"cleared_tags": "Removed all tags from <b>{song}</b>.",
|
||||
"cleared_tags_from_all": "Removed all tags from songs on the playlist.",
|
||||
"command_disabled": "{command}: command disabled!",
|
||||
"current_ducking_volume": "Volume on ducking: {volume}.",
|
||||
"current_mode": "Current playback mode is <i>{mode}</i>.",
|
||||
"current_volume": "Current volume: {volume}.",
|
||||
"database_dropped": "Database dropped. All records have gone.",
|
||||
"download_in_progress": "Download of <b>{item}</b> in progress...",
|
||||
"error_executing_command": "{command}: Command failed with error: {error}.",
|
||||
"file": "File",
|
||||
"file_added": "Added {item}.",
|
||||
"file_deleted": "Deleted {item} from the library.",
|
||||
"file_item": "<b>{artist} - {title}</b> <i>added by</i> {user}",
|
||||
"file_missed": "Music file '{file}' missed! This item has been removed from the playlist.",
|
||||
"help": "<h3>Commands</h3>\n<b>Control</b>\n<ul>\n<li> <b>!<u>w</u>eb</b> - get the URL of the web interface, if enabled. </li>\n<li> <b>!play </b> (or <b>!p</b>) [{num}] [{start_from}] - resume from pausing / start to play (the num-th song is num if given) </li>\n<li> <b>!<u>pa</u>use </b> - pause </li>\n<li> <b>!<u>st</u>op </b> - stop playing </li>\n<li> <b>!<u>sk</u>ip </b> - jump to the next song </li>\n<li> <b>!<u>la</u>st </b> - jump to the last song </li>\n<li> <b>!<u>v</u>olume </b> {volume} - get or change the volume (from 0 to 100) </li>\n<li> <b>!<u>m</u>ode </b> [{mode}] - get or set the playback mode, {mode} should be one of <i>one-shot</i> (remove\nitem once played), <i>repeat</i> (looping through the playlist), <i>random</i> (randomize the playlist),\n<i>autoplay</i> (randomly grab something from the music library).</li>\n<li> <b>!duck </b> on/off - enable or disable ducking function </li>\n<li> <b>!duckv </b> - set the volume of the bot when ducking is activated </li>\n<li> <b>!<u>duckt</u>hres </b> - set the threshold of volume to activate ducking (3000 by default) </li>\n<li> <b>!<u>o</u>ust </b> - stop playing and go to default channel </li>\n</ul>\n<b>Playlist</b>\n<ul>\n<li> <b>!<u>n</u>ow </b> (or <b>!np</b>) - display the current song </li>\n<li> <b>!<u>q</u>ueue </b> - display items in the playlist </li>\n<li> <b>!<u>t</u>ag </b> {tags} - add all items with tags {tags}, tags separated by \",\". </li>\n<li> <b>!file </b>(or <b>!f</b>) {path/folder/keyword} - add a single file to the playlist by its path or keyword in its path. </li>\n<li> <b>!<u>filem</u>atch </b>(or <b>!fm</b>) {pattern} - add all files that match regex {pattern} </li>\n<li> <b>!<u>ur</u>l </b> {url} - add Youtube or SoundCloud music </li>\n<li> <b>!<u>playl</u>ist </b> {url} [{offset}] - add all items in a Youtube or SoundCloud playlist, and start with the {offset}-th item </li>\n<li> <b>!<u>rad</u>io </b> {url} - append a radio {url} to the playlist </li>\n<li> <b>!<u>rbq</u>uery </b> {keyword} - query http://www.radio-browser.info for a radio station </li>\n<li> <b>!<u>rbp</u>lay </b> {id} - play a radio station with {id} (eg. !rbplay 96746) </li>\n<li> <b>!<u>ys</u>earch </b> {keywords} - query youtube. Use <i>!ysearch -n</i> to turn the page. </li>\n<li> <b>!<u>yp</u>lay </b> {keywords} - add the first search result of {keywords} into the playlist.</li>\n<li> <b>!<u>sh</u>ortlist </b> (or <b>!sl</b>) {indexes/*} - add {indexes}-th item (or all items if * is given) on the shortlist. </li>\n<li> <b>!rm </b> {num} - remove the num-th song on the playlist </li>\n<li> <b>!<u>rep</u>eat </b> [{num}] - repeat current song {num} (1 by default) times.</li>\n<li> <b>!<u>ran</u>dom </b> - randomize the playlist.</li>\n</ul>\n<b>Music Library</b>\n<ul>\n<li> <b>!<u>se</u>arch </b> {keywords} - find item with {keywords} in the music library, keywords separated by space.</li>\n<li> <b>!<u>li</u>stfile </b> [{pattern}] - display list of available files (whose paths match the regex pattern if {pattern} is given) </li>\n<li> <b>!<u>addt</u>ag </b> [{index}] {tags} - add {tags} to {index}-th(current song if {index} is omitted) item on the playlist, tags separated by \",\". </li>\n<li> <b>!<u>addt</u>ag </b> * {tags} - add {tags} to all items on the playlist. </li>\n<li> <b>!<u>un</u>tag </b> [{index/*}] {tags}/* - remove {tags}/all tags from {index}-th(current song if {index} is omitted) item on the playlist. </li>\n<li> <b>!<u>fin</u>dtagged </b> (or <b>!ft</b>) {tags} - find item with {tags} in the music library. </li>\n<li> <b>!<u>del</u>ete </b> {index} - delete {index}-th item on the shortlist from the music library. </li>\n</ul>\n<b>Other</b>\n<ul>\n<li> <b>!<u>j</u>oinme {token} </b> - join your own channel with {token}.</li>\n<li> <b>!<u>password</u> {password} </b> - change your password, used to access the web interface.</li>\n</ul>",
|
||||
"invalid_index": "Invalid index <i>{index}</i>. Use '!queue' to see the playlist.",
|
||||
"multiple_file_added": "Multiple items added:",
|
||||
"multiple_file_deleted": "Multiple items deleted from the library:",
|
||||
"multiple_file_found": "Found:",
|
||||
"multiple_matches": "File not found! Possible candidates:",
|
||||
"new_version_found": "<h2>Update Available!</h2> Version {new_version} of botamusique is available! <hr />\n<h3>Changelog</h3> {changelog} <hr /> Send <i>!update</i> to update!",
|
||||
"no_file": "File not found.",
|
||||
"not_admin": "You are not an admin!",
|
||||
"not_in_my_channel": "You're not in my channel!",
|
||||
"not_playing": "Nothing is playing right now.",
|
||||
"now_playing": "Playing {item}",
|
||||
"page_instruction": "Page {current}/{total}. Use <i>!{command} {{page}}</i> to navigate.",
|
||||
"paused": "Music paused.",
|
||||
"playlist_fetching_failed": "Unable to fetch the playlist!",
|
||||
"pm_not_allowed": "Private message aren't allowed.",
|
||||
"preconfigurated_radio": "Preconfigurated Radio available:",
|
||||
"queue_contents": "Items on the playlist:",
|
||||
"queue_empty": "Playlist is empty!",
|
||||
"radio": "Radio",
|
||||
"radio_item": "<a href=\"{url}\"><b>{title}</b></a> <i>from</i> {name} <i>added by</i> {user}",
|
||||
"rb_play_empty": "Please specify a radio station ID!",
|
||||
"rb_query_result": "This is the result of your query, send <i> !rbplay {ID} </i> to play a station:",
|
||||
"records_omitted": "...",
|
||||
"removed_tags": "Removed tags <i>{tags}</i> from <b>{song}</b>.",
|
||||
"removed_tags_from_all": "Removed tags <i>{tags}</i> from songs on the playlist.",
|
||||
"removing_item": "Removed entry {item} from playlist.",
|
||||
"repeat": "Repeat {song} for {n} times.",
|
||||
"shortlist_instruction": "Use <i>!sl {indexes}</i> to play the item you want.",
|
||||
"start_updating": "Start updating...",
|
||||
"stopped": "Music stopped.",
|
||||
"too_long": "<b>{song}</b> is too long, removed from playlist!",
|
||||
"unable_download": "Unable to download <b>{item}</b>. Removed from the library.",
|
||||
"unable_play": "Unable to play <b>{item}</b>. Removed from the library.",
|
||||
"unknown_mode": "Unknown playback mode '{mode}'. It should be one of <i>one-shot</i>, <i>repeat</i>, <i>random</i>.",
|
||||
"update_successful": "<h2>botamusique v{version} Installed!</h2><hr />\n<h3>Changelog</h3> {changelog} <hr /> Visit <a href=\"https://github.com/azlux/botamusique\">our github repo</a> for more details!",
|
||||
"url": "URL",
|
||||
"url_ban": "This URL is banned!",
|
||||
"url_from_playlist": "URL",
|
||||
"url_from_playlist_item": "<a href=\"{url}\"><b>{title}</b></a> <i>from playlist</i> <a href=\"{playlist_url}\">{playlist}</a> <i>added by</i> {user}",
|
||||
"url_item": "<a href=\"{url}\"><b>{title}</b></a> <i>added by</i> {user}",
|
||||
"user_ban": "You are banned, not allowed to do that!",
|
||||
"user_password_set": "Your password has been updated.",
|
||||
"web_user_list": "Following users has the privilege to access the web interface: <br /> {users}",
|
||||
"webpage_address": "Your own address to access the web interface is <a href=\"{address}\">{address}</a>",
|
||||
"which_command": "Do you mean <br /> {commands}",
|
||||
"wrong_pattern": "Invalid regex: {error}.",
|
||||
"yt_no_more": "No more results!",
|
||||
"yt_query_error": "Unable to query youtube!",
|
||||
"yt_result": "Youtube query result: {result_table} Use <i>!sl {{indexes}}</i> to play the item you want. <br />\n<i>!ytquery -n</i> for the next page."
|
||||
},
|
||||
"web": {
|
||||
"action": "Action",
|
||||
"add": "Add",
|
||||
"add_all": "Add All",
|
||||
"add_radio": "Add Radio",
|
||||
"add_radio_url": "Add Radio URL",
|
||||
"add_to_bottom": "Add to bottom",
|
||||
"add_to_bottom_of_current_playlist": "Add to bottom of current playlist",
|
||||
"add_to_playlist_next": "Add to playlist right after current song",
|
||||
"add_url": "Add URL",
|
||||
"add_youtube_or_soundcloud_url": "Add Youtube or Soundcloud URL",
|
||||
"are_you_really_sure": "Are you really sure?",
|
||||
"aria_botamusique_logo": "Botamusique Logo: a person with two headphones, enjoying the music",
|
||||
"aria_default_cover": "A black square with two eight notes beamed together.",
|
||||
"aria_empty_box": "A drawing of an empty box.",
|
||||
"aria_remove_this_song": "Remove this song from the current playlist",
|
||||
"aria_skip_current_song": "Skip current song and play this song right now",
|
||||
"aria_skip_to_next_track": "Skip to next track",
|
||||
"aria_spinner": "A loading spinner",
|
||||
"aria_warning_of_deletion": "Warning about deletion of files.",
|
||||
"autoplay": "Autoplay",
|
||||
"browse_music_file": "Browse Music file",
|
||||
"cancel": "Cancel",
|
||||
"cancel_upload_warning": "<strong>Are you really sure?</strong> <br /> Click again to abort uploading.",
|
||||
"change_playback_mode": "Change Playback Mode",
|
||||
"choose_file": "Choose file",
|
||||
"clear_playlist": "Clear Playlist",
|
||||
"close": "Close",
|
||||
"delete_all": "Delete All",
|
||||
"delete_all_files": "Delete All Listed Files",
|
||||
"delete_file_warning": "All files listed here, include files on other pages, will be deleted from your hard-drive.\n Is that what you want?",
|
||||
"directory": "Directory",
|
||||
"download_all": "Download All",
|
||||
"download_song_from_library": "Download song from library",
|
||||
"edit_submit": "Edit!",
|
||||
"edit_tags_for": "Edit tags for",
|
||||
"expand_playlist": "See item <span\n class=\"playlist-expand-item-range\"></span> on the playlist.",
|
||||
"file": "File",
|
||||
"filters": "Filters",
|
||||
"index": "#",
|
||||
"keywords": "Keywords",
|
||||
"keywords_placeholder": "Keywords...",
|
||||
"mini_player_title": "Now Playing...",
|
||||
"music_library": "Music Library",
|
||||
"next_to_play": "Next to play",
|
||||
"no_tag": "No tag",
|
||||
"oneshot": "One-shot",
|
||||
"open_volume_controls": "Open Volume Controls",
|
||||
"page_title": "botamusique Web Interface",
|
||||
"pause": "Pause",
|
||||
"play": "Play",
|
||||
"playlist_controls": "Playlist controls",
|
||||
"radio": "Radio",
|
||||
"radio_url_placeholder": "Radio URL...",
|
||||
"random": "Random",
|
||||
"remove_song_from_library": "Remove song from library",
|
||||
"repeat": "Repeat",
|
||||
"rescan_files": "Rescan Files",
|
||||
"skip_track": "Skip Track",
|
||||
"submit": "Submit",
|
||||
"tags": "Tags",
|
||||
"tags_to_add": "Tags to add",
|
||||
"title": "Title",
|
||||
"token": "Token",
|
||||
"token_required": "Token Required",
|
||||
"token_required_message": "You are accessing the web interface of {{ name }}.\nA token is needed to grant you access.<br />\nPlease send \"{{ command }}\" to the bot in mumble to acquire one.",
|
||||
"type": "Type",
|
||||
"upload_file": "Upload File",
|
||||
"upload_submit": "Upload!",
|
||||
"upload_to": "Upload To",
|
||||
"uploaded_finished": "Uploaded finished!",
|
||||
"uploading_files": "Uploading files...",
|
||||
"url": "URL",
|
||||
"url_path": "Url/Path",
|
||||
"url_placeholder": "URL...",
|
||||
"volume_slider": "Volume Slider"
|
||||
}
|
||||
}
|
95
lang/sync_translation.py
Executable file
95
lang/sync_translation.py
Executable file
@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import re
|
||||
import argparse
|
||||
import requests
|
||||
|
||||
base_url = "https://translate.azlux.fr/api/v1"
|
||||
project_id = "4aafb197-3282-47b3-a197-0ca870cf6ab2"
|
||||
|
||||
|
||||
def get_access_header(client, secret):
|
||||
data = {"grant_type": "client_credentials",
|
||||
"client_id": client,
|
||||
"client_secret": secret}
|
||||
|
||||
r = requests.post(f"{base_url}/auth/token", json=data)
|
||||
|
||||
if r.status_code != 200:
|
||||
print("Access denied! Please check your client ID or secret.")
|
||||
exit(1)
|
||||
|
||||
token = r.json()["access_token"]
|
||||
|
||||
headers = {"Authorization": "Bearer " + token,
|
||||
"Accept": "application/json, text/plain, */*"}
|
||||
|
||||
return headers
|
||||
|
||||
|
||||
def fetch_translation(r_client, r_secret):
|
||||
headers = get_access_header(r_client, r_secret)
|
||||
|
||||
r = requests.get(f"{base_url}/projects/{project_id}/translations", headers=headers)
|
||||
translations = r.json()['data']
|
||||
for translation in translations:
|
||||
lang_code = translation['locale']['code']
|
||||
print(f" - Fetching {lang_code}")
|
||||
params = {'locale': lang_code,
|
||||
'format': 'jsonnested'}
|
||||
r = requests.get(f"{base_url}/projects/{project_id}/exports", params=params, headers=headers)
|
||||
with open(lang_code + ".json", "wb") as f:
|
||||
f.write(r.content)
|
||||
|
||||
|
||||
def push_strings(w_client, w_secret):
|
||||
print("Pushing local translation files into the remote host...")
|
||||
headers = get_access_header(w_client, w_secret)
|
||||
|
||||
lang_files = os.listdir('.')
|
||||
lang_list = []
|
||||
for lang_file in lang_files:
|
||||
match = re.search("([a-z]{2}_[A-Z]{2})\.json", lang_file)
|
||||
if match:
|
||||
lang_list.append(match[1])
|
||||
|
||||
for lang in lang_list:
|
||||
print(f" - Pushing {lang}")
|
||||
|
||||
params = {'locale': lang,
|
||||
'format': 'jsonnested'}
|
||||
files = {'file': open(lang + ".json", 'r')}
|
||||
|
||||
r = requests.post(f"{base_url}/projects/{project_id}/imports", params=params, headers=headers, files=files)
|
||||
assert r.status_code == 200, f"Unable to push {lang} into remote host. {r.status_code}"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Sync translation files with azlux's traduora server.")
|
||||
|
||||
parser.add_argument("--client", dest="client",
|
||||
type=str, help="Client ID used to access the server.")
|
||||
parser.add_argument("--secret", dest="secret",
|
||||
type=str, help="Secret used to access the server.")
|
||||
|
||||
parser.add_argument("--fetch", dest='fetch', action="store_true",
|
||||
help='Fetch translation files from the server.')
|
||||
parser.add_argument("--push", dest='push', action="store_true",
|
||||
help='Push local translation files into the server.')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.client or not args.secret:
|
||||
print("Client ID and secret need to be provided!")
|
||||
exit(1)
|
||||
|
||||
if args.push:
|
||||
push_strings(args.client, args.secret)
|
||||
|
||||
if args.fetch:
|
||||
fetch_translation(args.client, args.secret)
|
||||
|
||||
print("Done.")
|
||||
|
@ -9,7 +9,7 @@ from PIL import Image
|
||||
import util
|
||||
import variables as var
|
||||
from media.item import BaseItem, item_builders, item_loaders, item_id_generators, ValidationFailedError
|
||||
import constants
|
||||
from constants import tr_cli as tr
|
||||
|
||||
'''
|
||||
type : file
|
||||
@ -75,7 +75,7 @@ class FileItem(BaseItem):
|
||||
if not os.path.exists(self.uri()):
|
||||
self.log.info(
|
||||
"file: music file missed for %s" % self.format_debug_string())
|
||||
raise ValidationFailedError(constants.strings('file_missed', file=self.path))
|
||||
raise ValidationFailedError(tr('file_missed', file=self.path))
|
||||
|
||||
if self.duration == 0:
|
||||
self.duration = util.get_media_duration(self.uri())
|
||||
@ -185,14 +185,14 @@ class FileItem(BaseItem):
|
||||
)
|
||||
|
||||
def format_song_string(self, user):
|
||||
return constants.strings("file_item",
|
||||
title=self.title,
|
||||
artist=self.artist if self.artist else '??',
|
||||
user=user
|
||||
)
|
||||
return tr("file_item",
|
||||
title=self.title,
|
||||
artist=self.artist if self.artist else '??',
|
||||
user=user
|
||||
)
|
||||
|
||||
def format_current_playing(self, user):
|
||||
display = constants.strings("now_playing", item=self.format_song_string(user))
|
||||
display = tr("now_playing", item=self.format_song_string(user))
|
||||
if self.thumbnail:
|
||||
thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
|
||||
self.thumbnail + '"/>'
|
||||
@ -208,4 +208,4 @@ class FileItem(BaseItem):
|
||||
return title
|
||||
|
||||
def display_type(self):
|
||||
return constants.strings("file")
|
||||
return tr("file")
|
||||
|
@ -7,7 +7,7 @@ import hashlib
|
||||
|
||||
from media.item import BaseItem
|
||||
from media.item import item_builders, item_loaders, item_id_generators
|
||||
import constants
|
||||
from constants import tr_cli as tr
|
||||
|
||||
log = logging.getLogger("bot")
|
||||
|
||||
@ -147,18 +147,18 @@ class RadioItem(BaseItem):
|
||||
)
|
||||
|
||||
def format_song_string(self, user):
|
||||
return constants.strings("radio_item",
|
||||
url=self.url,
|
||||
title=get_radio_title(self.url), # the title of current song
|
||||
name=self.title, # the title of radio station
|
||||
user=user
|
||||
)
|
||||
return tr("radio_item",
|
||||
url=self.url,
|
||||
title=get_radio_title(self.url), # the title of current song
|
||||
name=self.title, # the title of radio station
|
||||
user=user
|
||||
)
|
||||
|
||||
def format_current_playing(self, user):
|
||||
return constants.strings("now_playing", item=self.format_song_string(user))
|
||||
return tr("now_playing", item=self.format_song_string(user))
|
||||
|
||||
def format_title(self):
|
||||
return self.title if self.title else self.url
|
||||
|
||||
def display_type(self):
|
||||
return constants.strings("radio")
|
||||
return tr("radio")
|
||||
|
20
media/url.py
20
media/url.py
@ -9,7 +9,7 @@ import glob
|
||||
from io import BytesIO
|
||||
import base64
|
||||
|
||||
import constants
|
||||
from constants import tr_cli as tr
|
||||
import media
|
||||
import variables as var
|
||||
from media.item import BaseItem, item_builders, item_loaders, item_id_generators, ValidationFailedError, \
|
||||
@ -99,7 +99,7 @@ class URLItem(BaseItem):
|
||||
# Check the length, useful in case of playlist, it wasn't checked before)
|
||||
log.info(
|
||||
"url: " + self.url + " has a duration of " + str(self.duration / 60) + " min -- too long")
|
||||
raise ValidationFailedError(constants.strings('too_long', song=self.title))
|
||||
raise ValidationFailedError(tr('too_long', song=self.title))
|
||||
else:
|
||||
self.ready = "validated"
|
||||
self.version += 1 # notify wrapper to save me
|
||||
@ -138,7 +138,7 @@ class URLItem(BaseItem):
|
||||
if not succeed:
|
||||
self.ready = 'failed'
|
||||
self.log.error("url: error while fetching info from the URL")
|
||||
raise ValidationFailedError(constants.strings('unable_download', item=self.format_title()))
|
||||
raise ValidationFailedError(tr('unable_download', item=self.format_title()))
|
||||
|
||||
def _download(self):
|
||||
media.system.clear_tmp_folder(var.tmp_folder, var.config.getint('bot', 'tmp_folder_max_size'))
|
||||
@ -193,7 +193,7 @@ class URLItem(BaseItem):
|
||||
os.remove(f)
|
||||
self.ready = "failed"
|
||||
self.downloading = False
|
||||
raise PreparationFailedError(constants.strings('unable_download', item=self.format_title()))
|
||||
raise PreparationFailedError(tr('unable_download', item=self.format_title()))
|
||||
|
||||
def _read_thumbnail_from_file(self, path_thumbnail):
|
||||
if os.path.isfile(path_thumbnail):
|
||||
@ -226,14 +226,14 @@ class URLItem(BaseItem):
|
||||
|
||||
def format_song_string(self, user):
|
||||
if self.ready in ['validated', 'yes']:
|
||||
return constants.strings("url_item",
|
||||
title=self.title if self.title else "??",
|
||||
url=self.url,
|
||||
user=user)
|
||||
return tr("url_item",
|
||||
title=self.title if self.title else "??",
|
||||
url=self.url,
|
||||
user=user)
|
||||
return self.url
|
||||
|
||||
def format_current_playing(self, user):
|
||||
display = constants.strings("now_playing", item=self.format_song_string(user))
|
||||
display = tr("now_playing", item=self.format_song_string(user))
|
||||
|
||||
if self.thumbnail:
|
||||
thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
|
||||
@ -246,4 +246,4 @@ class URLItem(BaseItem):
|
||||
return self.title if self.title.strip() else self.url
|
||||
|
||||
def display_type(self):
|
||||
return constants.strings("url")
|
||||
return tr("url")
|
||||
|
@ -1,5 +1,5 @@
|
||||
import youtube_dl
|
||||
import constants
|
||||
from constants import tr_cli as tr
|
||||
import variables as var
|
||||
from media.item import item_builders, item_loaders, item_id_generators
|
||||
from media.url import URLItem, url_item_id_generator
|
||||
@ -96,15 +96,15 @@ class PlaylistURLItem(URLItem):
|
||||
)
|
||||
|
||||
def format_song_string(self, user):
|
||||
return constants.strings("url_from_playlist_item",
|
||||
title=self.title,
|
||||
url=self.url,
|
||||
playlist_url=self.playlist_url,
|
||||
playlist=self.playlist_title,
|
||||
user=user)
|
||||
return tr("url_from_playlist_item",
|
||||
title=self.title,
|
||||
url=self.url,
|
||||
playlist_url=self.playlist_url,
|
||||
playlist=self.playlist_title,
|
||||
user=user)
|
||||
|
||||
def format_current_playing(self, user):
|
||||
display = constants.strings("now_playing", item=self.format_song_string(user))
|
||||
display = tr("now_playing", item=self.format_song_string(user))
|
||||
|
||||
if self.thumbnail:
|
||||
thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
|
||||
@ -114,4 +114,4 @@ class PlaylistURLItem(URLItem):
|
||||
return display
|
||||
|
||||
def display_type(self):
|
||||
return constants.strings("url_from_playlist")
|
||||
return tr("url_from_playlist")
|
||||
|
56
mumbleBot.py
56
mumbleBot.py
@ -24,6 +24,7 @@ from packaging import version
|
||||
import util
|
||||
import command
|
||||
import constants
|
||||
from constants import tr_cli as tr
|
||||
from database import SettingsDatabase, MusicDatabase, DatabaseMigration
|
||||
import media.system
|
||||
from media.item import ValidationFailedError, PreparationFailedError
|
||||
@ -118,7 +119,7 @@ class MumbleBot:
|
||||
self.mumble.start() # start the mumble thread
|
||||
self.mumble.is_ready() # wait for the connection
|
||||
|
||||
if self.mumble.connected >= pymumble_py3.constants.PYMUMBLE_CONN_STATE_FAILED:
|
||||
if self.mumble.connected >= pymumble.constants.PYMUMBLE_CONN_STATE_FAILED:
|
||||
exit()
|
||||
|
||||
self.set_comment()
|
||||
@ -179,7 +180,7 @@ class MumbleBot:
|
||||
if not last_startup_version or version.parse(last_startup_version) < version.parse(self.version):
|
||||
var.db.set("bot", "version", self.version)
|
||||
changelog = util.fetch_changelog()
|
||||
self.send_channel_msg(constants.strings("update_successful", version=self.version, changelog=changelog))
|
||||
self.send_channel_msg(tr("update_successful", version=self.version, changelog=changelog))
|
||||
|
||||
# Set the CTRL+C shortcut
|
||||
def ctrl_caught(self, signal, frame):
|
||||
@ -206,7 +207,7 @@ class MumbleBot:
|
||||
self.log.info(f"update: new version {new_version} found, current installed version {self.version}.")
|
||||
self.log.info(f"update: changelog: {changelog}")
|
||||
changelog = changelog.replace("\n", "<br>")
|
||||
self.send_channel_msg(constants.strings('new_version_found', new_version=new_version, changelog=changelog))
|
||||
self.send_channel_msg(tr('new_version_found', new_version=new_version, changelog=changelog))
|
||||
else:
|
||||
self.log.debug("update: no new version found.")
|
||||
|
||||
@ -263,13 +264,13 @@ class MumbleBot:
|
||||
# Anti stupid guy function
|
||||
if not self.is_admin(user) and not var.config.getboolean('bot', 'allow_private_message') and text.session:
|
||||
self.mumble.users[text.actor].send_text_message(
|
||||
constants.strings('pm_not_allowed'))
|
||||
tr('pm_not_allowed'))
|
||||
return
|
||||
|
||||
for i in var.db.items("user_ban"):
|
||||
if user.lower() == i[0]:
|
||||
self.mumble.users[text.actor].send_text_message(
|
||||
constants.strings('user_ban'))
|
||||
tr('user_ban'))
|
||||
return
|
||||
|
||||
if not self.is_admin(user) and parameter:
|
||||
@ -278,7 +279,7 @@ class MumbleBot:
|
||||
for i in var.db.items("url_ban"):
|
||||
if input_url == i[0]:
|
||||
self.mumble.users[text.actor].send_text_message(
|
||||
constants.strings('url_ban'))
|
||||
tr('url_ban'))
|
||||
return
|
||||
|
||||
command_exc = ""
|
||||
@ -299,15 +300,15 @@ class MumbleBot:
|
||||
|
||||
elif len(matches) > 1:
|
||||
self.mumble.users[text.actor].send_text_message(
|
||||
constants.strings('which_command', commands="<br>".join(matches)))
|
||||
tr('which_command', commands="<br>".join(matches)))
|
||||
return
|
||||
else:
|
||||
self.mumble.users[text.actor].send_text_message(
|
||||
constants.strings('bad_command', command=command))
|
||||
tr('bad_command', command=command))
|
||||
return
|
||||
|
||||
if self.cmd_handle[command_exc]['admin'] and not self.is_admin(user):
|
||||
self.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
|
||||
self.mumble.users[text.actor].send_text_message(tr('not_admin'))
|
||||
return
|
||||
|
||||
if not self.cmd_handle[command_exc]['access_outside_channel'] \
|
||||
@ -315,7 +316,7 @@ class MumbleBot:
|
||||
and not var.config.getboolean('bot', 'allow_other_channel_message') \
|
||||
and self.mumble.users[text.actor]['channel_id'] != self.mumble.users.myself['channel_id']:
|
||||
self.mumble.users[text.actor].send_text_message(
|
||||
constants.strings('not_in_my_channel'))
|
||||
tr('not_in_my_channel'))
|
||||
return
|
||||
|
||||
self.cmd_handle[command_exc]['handle'](self, user, text, command_exc, parameter)
|
||||
@ -323,7 +324,7 @@ class MumbleBot:
|
||||
error_traceback = traceback.format_exc()
|
||||
error = error_traceback.rstrip().split("\n")[-1]
|
||||
self.log.error(f"bot: command {command_exc} failed with error: {error_traceback}\n")
|
||||
self.send_msg(constants.strings('error_executing_command', command=command_exc, error=error), text)
|
||||
self.send_msg(tr('error_executing_command', command=command_exc, error=error), text)
|
||||
|
||||
def send_msg(self, msg, text):
|
||||
msg = msg.encode('utf-8', 'ignore').decode('utf-8')
|
||||
@ -355,7 +356,7 @@ class MumbleBot:
|
||||
if var.config.get("bot", "when_nobody_in_channel") == "pause_resume":
|
||||
self.resume()
|
||||
elif var.config.get("bot", "when_nobody_in_channel") == "pause":
|
||||
self.send_channel_msg(constants.strings("auto_paused"))
|
||||
self.send_channel_msg(tr("auto_paused"))
|
||||
|
||||
elif len(own_channel.get_users()) == 1:
|
||||
# if the bot is the only user left in the channel
|
||||
@ -436,7 +437,7 @@ class MumbleBot:
|
||||
self.log.info("bot: current music isn't ready, start downloading.")
|
||||
self.async_download(item)
|
||||
self.send_channel_msg(
|
||||
constants.strings('download_in_progress', item=item.format_title()))
|
||||
tr('download_in_progress', item=item.format_title()))
|
||||
|
||||
def _download(self, item):
|
||||
ver = item.version
|
||||
@ -514,7 +515,7 @@ class MumbleBot:
|
||||
self.log.error("bot: with ffmpeg error: %s", self.last_ffmpeg_err)
|
||||
self.last_ffmpeg_err = ""
|
||||
|
||||
self.send_channel_msg(constants.strings('unable_play', item=current.format_title()))
|
||||
self.send_channel_msg(tr('unable_play', item=current.format_title()))
|
||||
var.playlist.remove_by_id(current.id)
|
||||
var.cache.free_and_delete(current.id)
|
||||
|
||||
@ -713,6 +714,8 @@ def start_web_interface(addr, port):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
supported_languages = util.get_supported_language()
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Bot for playing music on Mumble')
|
||||
|
||||
@ -720,9 +723,11 @@ if __name__ == '__main__':
|
||||
parser.add_argument("--config", dest='config', type=str, default='configuration.ini',
|
||||
help='Load configuration from this file. Default: configuration.ini')
|
||||
parser.add_argument("--db", dest='db', type=str,
|
||||
default=None, help='settings database file. Default: settings-{username_of_the_bot}.db')
|
||||
default=None, help='Settings database file')
|
||||
parser.add_argument("--music-db", dest='music_db', type=str,
|
||||
default=None, help='music library database file. Default: music.db')
|
||||
default=None, help='Music library database file')
|
||||
parser.add_argument("--lang", dest='lang', type=str, default=None,
|
||||
help='Preferred language. Support ' + ", ".join(supported_languages))
|
||||
|
||||
parser.add_argument("-q", "--quiet", dest="quiet",
|
||||
action="store_true", help="Only Error logs")
|
||||
@ -814,6 +819,25 @@ if __name__ == '__main__':
|
||||
|
||||
var.music_folder = util.solve_filepath(var.config.get('bot', 'music_folder'))
|
||||
var.tmp_folder = util.solve_filepath(var.config.get('bot', 'tmp_folder'))
|
||||
|
||||
# ======================
|
||||
# Translation
|
||||
# ======================
|
||||
|
||||
lang = ""
|
||||
if args.lang:
|
||||
lang = args.lang
|
||||
else:
|
||||
lang = var.config.get('bot', 'language', fallback='en_US')
|
||||
|
||||
if lang not in supported_languages:
|
||||
raise KeyError(f"Unsupported language {lang}")
|
||||
var.language = lang
|
||||
constants.load_lang(lang)
|
||||
|
||||
# ======================
|
||||
# Prepare Cache
|
||||
# ======================
|
||||
var.cache = MusicCache(var.music_db)
|
||||
|
||||
if var.config.getboolean("bot", "refresh_cache_on_startup", fallback=True):
|
||||
|
58
templates/translate.py
Executable file
58
templates/translate.py
Executable file
@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import json
|
||||
import re
|
||||
import jinja2
|
||||
|
||||
default_lang_dict = {}
|
||||
lang_dict = {}
|
||||
|
||||
|
||||
def load_lang(lang):
|
||||
with open(f"../lang/{lang}.json", "r") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def tr(option):
|
||||
try:
|
||||
if option in lang_dict['web'] and lang_dict['web'][option]:
|
||||
string = lang_dict['web'][option]
|
||||
else:
|
||||
string = default_lang_dict['web'][option]
|
||||
return string
|
||||
except KeyError:
|
||||
raise KeyError("Missed strings in language file: '{string}'. ".format(string=option))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
html_files = os.listdir('.')
|
||||
for html_file in html_files:
|
||||
match = re.search("(.+)\.template\.html", html_file)
|
||||
if match is None:
|
||||
continue
|
||||
|
||||
print(f"Populating {html_file} with translations...")
|
||||
basename = match[1]
|
||||
with open(html_file, "r") as f:
|
||||
html = f.read()
|
||||
|
||||
lang_files = os.listdir('../lang')
|
||||
lang_list = []
|
||||
|
||||
default_lang_dict = load_lang("en_US")
|
||||
|
||||
for lang_file in lang_files:
|
||||
match = re.search("([a-z]{2}_[A-Z]{2})\.json", lang_file)
|
||||
if match:
|
||||
lang_list.append(match[1])
|
||||
|
||||
template = jinja2.Template(html)
|
||||
|
||||
for lang in lang_list:
|
||||
print(f" - Populating {lang}...")
|
||||
lang_dict = load_lang(lang)
|
||||
|
||||
with open(f"{basename}.{lang}.html", "w") as f:
|
||||
f.write(template.render(tr=tr))
|
||||
print("Done.")
|
12
util.py
12
util.py
@ -8,7 +8,6 @@ import io
|
||||
import sys
|
||||
import variables as var
|
||||
import zipfile
|
||||
import requests
|
||||
import re
|
||||
import subprocess as sp
|
||||
import logging
|
||||
@ -416,6 +415,17 @@ def verify_password(password, salted_hash, salt):
|
||||
return False
|
||||
|
||||
|
||||
def get_supported_language():
|
||||
lang_files = os.listdir('lang')
|
||||
lang_list = []
|
||||
for lang_file in lang_files:
|
||||
match = re.search("([a-z]{2}_[A-Z]{2})\.json", lang_file)
|
||||
if match:
|
||||
lang_list.append(match[1])
|
||||
|
||||
return lang_list
|
||||
|
||||
|
||||
class LoggerIOWrapper(io.TextIOWrapper):
|
||||
def __init__(self, logger: logging.Logger, logging_level, fallback_io_buffer):
|
||||
super().__init__(fallback_io_buffer, write_through=True)
|
||||
|
@ -23,3 +23,5 @@ bot_logger = None
|
||||
|
||||
music_folder = ""
|
||||
tmp_folder = ""
|
||||
|
||||
language = ""
|
||||
|
@ -816,7 +816,7 @@ const modal_tag = $('.modal-tag');
|
||||
const modal_tag_text = $('.modal-tag-text');
|
||||
|
||||
function addTagModalShow(_id, _title, _tag_tuples) {
|
||||
add_tag_modal_title.html('Edit tags for ' + _title);
|
||||
add_tag_modal_title.html(_title);
|
||||
add_tag_modal_item_id.val(_id);
|
||||
add_tag_modal_tags.empty();
|
||||
_tag_tuples.forEach(function (tag_tuple) {
|
||||
|
@ -6,7 +6,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta charset="UTF-8">
|
||||
|
||||
<title>botamusique web interface</title>
|
||||
<title>{{ tr('page_title') }}</title>
|
||||
|
||||
<link rel="icon" href="static/image/favicon.ico" />
|
||||
|
||||
@ -18,46 +18,46 @@
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<img src="static/image/logo.png" height="200px"
|
||||
alt="Botamusique Logo: a person with two headphones, enjoying the music">
|
||||
alt="{{ tr('aria_botamusique_logo') }}">
|
||||
</div>
|
||||
<div class="col my-auto">
|
||||
<h1>botamusique Web Interface</h1>
|
||||
<h1>{{ tr('page_title') }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="playlist" class="container mb-5">
|
||||
<div class="btn-toolbar mb-2" role="toolbar" aria-label="Playlist controls">
|
||||
<button type="button" id="play-pause-btn" class="btn btn-info mb-2 btn-space" aria-label="Play">
|
||||
<div class="btn-toolbar mb-2" role="toolbar" aria-label="{{ tr('playlist_controls') }}">
|
||||
<button type="button" id="play-pause-btn" class="btn btn-info mb-2 btn-space" aria-label="{{ tr('play') }}">
|
||||
<i class="fas fa-play"></i>
|
||||
</button>
|
||||
<button type="button" id="fast-forward-btn" class="btn btn-info mb-2" aria-label="Skip Track">
|
||||
<button type="button" id="fast-forward-btn" class="btn btn-info mb-2" aria-label="{{ tr('skip_track') }}">
|
||||
<i class="fas fa-fast-forward"></i>
|
||||
</button>
|
||||
<div class="ml-auto">
|
||||
<div class="dropdown mr-2">
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" id="play-mode"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
|
||||
aria-label="Change Playback Mode">
|
||||
aria-label="{{ tr('change_playback_mode') }}">
|
||||
<i class="fas fa-tasks mr-2" aria-hidden="true" id="modeIndicator"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="play-mode">
|
||||
<a class="dropdown-item" href="#" id="one-shot-mode-btn">
|
||||
<i class="fas fa-tasks mr-2" aria-hidden="true"></i>One-shot
|
||||
<i class="fas fa-tasks mr-2" aria-hidden="true"></i> {{ tr('oneshot') }}
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" id="random-mode-btn">
|
||||
<i class="fas fa-random mr-2" aria-hidden="true"></i>Random
|
||||
<i class="fas fa-random mr-2" aria-hidden="true"></i> {{ tr('random') }}
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" id="repeat-mode-btn">
|
||||
<i class="fas fa-redo mr-2" aria-hidden="true"></i>Repeat
|
||||
<i class="fas fa-redo mr-2" aria-hidden="true"></i> {{ tr('repeat') }}
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" id="autoplay-mode-btn">
|
||||
<i class="fas fa-robot mr-2" aria-hidden="true"></i>Autoplay
|
||||
<i class="fas fa-robot mr-2" aria-hidden="true"></i> {{ tr('autoplay') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" id="volume-popover-btn" class="btn btn-warning ml-1"
|
||||
aria-label="Open Volume Controls">
|
||||
aria-label="{{ tr('open_volume_controls') }}">
|
||||
<i class="fa fa-volume-up" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
@ -67,7 +67,7 @@
|
||||
</a>
|
||||
|
||||
<input type="range" class="custom-range ml-1" id="volume-slider" min="0" max="1" step="0.01"
|
||||
value="0.5" aria-label="Volume Slider" />
|
||||
value="0.5" aria-label="{{ tr('volume_slider') }}" />
|
||||
|
||||
<a id="volume-up-btn">
|
||||
<i class="fa fa-volume-up" aria-hidden="true"></i>
|
||||
@ -81,29 +81,28 @@
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="d-none d-md-table-cell">#</th>
|
||||
<th scope="col" class="w-50">Title</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">Url/Path</th>
|
||||
<th scope="col">Action</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">{{ tr('index') }}</th>
|
||||
<th scope="col" class="w-50">{{ tr('title') }}</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">{{ tr('url_path') }}</th>
|
||||
<th scope="col">{{ tr('action') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="playlist-table" class="playlist-table">
|
||||
<tr id="playlist-loading">
|
||||
<td colspan="4" class="text-center">
|
||||
<img style="margin: auto; width: 35px;" src="static/image/loading.svg"
|
||||
alt="A loading spinner" />
|
||||
alt="{{ tr('aria_spinner') }}" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="playlist-empty" class="d-none">
|
||||
<td colspan="4" class="text-center">
|
||||
<img style="margin: auto; width: 35px;" src="static/image/empty_box.svg"
|
||||
alt="A drawing of an empty box." />
|
||||
alt="{{ tr('aria_empty_box') }}" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="playlist-expand table-dark d-none">
|
||||
<td colspan="4" class="text-center">
|
||||
<a class="text-muted" href="javascript:">See item <span
|
||||
class="playlist-expand-item-range"></span> on the playlist.</a>
|
||||
<a class="text-muted" href="javascript:">{{ tr('expand_playlist') }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="playlist-item-template d-none" aria-hidden="true">
|
||||
@ -112,7 +111,7 @@
|
||||
<input hidden type="hidden" class="playlist-item-id" value="" />
|
||||
<div class="float-left">
|
||||
<img width="80" class="playlist-item-thumbnail" src="static/image/unknown-album.png"
|
||||
alt="A black square with two eight notes beamed together." />
|
||||
alt="{{ tr('aria_default_cover') }}" />
|
||||
</div>
|
||||
<div class="playlist-artwork">
|
||||
<b class="playlist-item-title"></b>
|
||||
@ -134,11 +133,11 @@
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="playlist-item-play btn btn-info btn-sm"
|
||||
aria-label="Skip current song and play this song right now">
|
||||
aria-label="{{ tr('aria_skip_current_song') }}">
|
||||
<i class="fas fa-play" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="playlist-item-trash btn btn-danger btn-sm ml-1"
|
||||
aria-label="Remove this song from the current playlist">
|
||||
aria-label="{{ tr('aria_remove_this_song') }}">
|
||||
<i class="fas fa-trash-alt" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -150,52 +149,51 @@
|
||||
|
||||
<div class="btn-group">
|
||||
<button type="button" id="clear-playlist-btn" class="btn btn-danger mr-1">
|
||||
<i class="fas fa-trash-alt" aria-hidden="true"></i> Clear Playlist
|
||||
</button>
|
||||
<i class="fas fa-trash-alt" aria-hidden="true"></i> {{ tr('clear_playlist') }}</button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div class="container mb-3">
|
||||
<h2 id="forms">Music Library</h2>
|
||||
<h2 id="forms">{{ tr('music_library') }}</h2>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Filters</h3>
|
||||
<h3 class="card-title">{{ tr('filters') }}</h3>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<fieldset id="filter-type" class="mb-2">
|
||||
<legend>Type</legend>
|
||||
<legend>{{ tr('type') }}</legend>
|
||||
<div class="btn-group btn-group-sm btn-group-toggle">
|
||||
<label id="filter-type-file" class="btn btn-secondary">
|
||||
<input type="checkbox" name="options"> File
|
||||
<input type="checkbox" name="options">{{ tr('file') }}
|
||||
</label>
|
||||
<label id="filter-type-url" class="btn btn-secondary">
|
||||
<input type="checkbox" name="options"> URL
|
||||
<input type="checkbox" name="options">{{ tr('url') }}
|
||||
</label>
|
||||
<label id="filter-type-radio" class="btn btn-secondary">
|
||||
<input type="checkbox" name="options"> Radio
|
||||
<input type="checkbox" name="options">{{ tr('radio') }}
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<label for="filter-dir">Directory</label>
|
||||
<label for="filter-dir">{{ tr('directory') }}</label>
|
||||
<div id="filter-path" class="input-group mb-2">
|
||||
<select class="form-control form-control-sm" id="filter-dir" disabled>
|
||||
<option value="">.</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<label for="filter-keywords">Keywords</label>
|
||||
<label for="filter-keywords">{{ tr('keywords') }}</label>
|
||||
<div id="filter-path" class="input-group mb-2">
|
||||
<input class="form-control form-control-sm" id="filter-keywords" name="keywords"
|
||||
placeholder="Keywords..." style="margin-top:5px;" />
|
||||
placeholder="{{ tr('keywords_placeholder') }}" style="margin-top:5px;" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<fieldset id="filter-tags">
|
||||
<legend>Tags</legend>
|
||||
<legend>{{ tr('tags') }}</legend>
|
||||
<span class="filter-tag tag-unclicked tag-click badge"></span>
|
||||
</fieldset>
|
||||
</div>
|
||||
@ -205,11 +203,11 @@
|
||||
<div id="library-group" class="list-group library-group" style="overflow: auto;">
|
||||
<div id="library-item-loading" class="list-group-item library-item">
|
||||
<img style="margin: auto; width: 35px;" src="static/image/loading.svg"
|
||||
alt="A loading spinner" />
|
||||
alt="{{ tr('aria_spinner') }}" />
|
||||
</div>
|
||||
<div id="library-item-empty" style="display: none" class="list-group-item library-item">
|
||||
<img style="margin: auto; width: 35px;" src="static/image/empty_box.svg"
|
||||
alt="A drawing of an empty box." />
|
||||
alt="{{ tr('aria_empty_box') }}" />
|
||||
</div>
|
||||
<div id="library-item" style="display: none;" class="list-group-item library-item">
|
||||
<input hidden type="hidden" class="library-item-id" value="" />
|
||||
@ -217,10 +215,10 @@
|
||||
<div class="library-thumb-col">
|
||||
<div class="library-thumb-img">
|
||||
<img class="library-item-thumb library-thumb-img" src="static/image/unknown-album.png"
|
||||
alt="A black square with two eight notes beamed together." />
|
||||
alt="{{ tr('aria_default_cover') }}" />
|
||||
</div>
|
||||
<div class="btn-group-vertical library-thumb-grp">
|
||||
<div class="library-item-play btn btn-secondary library-thumb-btn-up" title="Play">
|
||||
<div class="library-item-play btn btn-secondary library-thumb-btn-up" title="{{ tr('play') }}">
|
||||
<i class="fas fa-play" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
@ -239,14 +237,14 @@
|
||||
<div class="library-item-tags">
|
||||
<a class="tag-space tag-click library-item-edit"><i class="fas fa-edit"
|
||||
style="color: #AAAAAA"></i></a>
|
||||
<span class="library-item-notag badge badge-light text-muted font-italic">No tag</span>
|
||||
<span class="library-item-notag badge badge-light text-muted font-italic">{{ tr('no_tag') }}</span>
|
||||
<span class="library-item-tag tag-space badge">Tag</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group library-action">
|
||||
<button class="library-item-add-next btn btn-info btn-sm btn-space" type="button"
|
||||
title="Next to play" aria-label="Add to playlist right after current song">
|
||||
title="{{ tr('next_to_play') }}" aria-label="{{ tr('add_to_playlist_next') }}">
|
||||
<svg class="library-btn-svg" style="width: 1rem; fill: currentColor;"
|
||||
viewBox="5 5 17 17">
|
||||
<path d="m5.700245,3.92964l0,14.150376l11.451127,-7.075188l-11.451127,-7.075188z">
|
||||
@ -257,7 +255,7 @@
|
||||
</svg>
|
||||
</button>
|
||||
<button class="library-item-add-bottom library-btn btn btn-info btn-sm btn-space"
|
||||
type="button" title="Add to bottom" aria-label="Add to bottom of current playlist">
|
||||
type="button" title="{{ tr('add_to_bottom') }}" aria-label="{{ tr('add_to_bottom_of_current_playlist') }}">
|
||||
<svg class="library-btn-svg" style="width: 1rem; fill: currentColor;"
|
||||
viewBox="2 2 20 20">
|
||||
<path
|
||||
@ -266,11 +264,11 @@
|
||||
</svg>
|
||||
</button>
|
||||
<button class="library-item-download btn btn-primary btn-sm btn-space" type="button"
|
||||
aria-label="Download song from library">
|
||||
aria-label="{{ tr('download_song_from_library') }}">
|
||||
<i class="fas fa-download" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button class="library-item-trash btn btn-danger btn-sm btn-space" type="button"
|
||||
aria-label="Remove song from library">
|
||||
aria-label="{{ tr('remove_song_from_library') }}">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -290,40 +288,36 @@
|
||||
|
||||
<div class="btn-group mb-2" role="group">
|
||||
<button id="add-to-playlist-btn" type="button" class="btn btn-secondary mr-1">
|
||||
<i class="fas fa-plus" aria-hidden="true"></i> Add All
|
||||
<i class="fas fa-plus" aria-hidden="true"></i>{{ tr('add_all') }}
|
||||
</button>
|
||||
<button id="library-rescan-btn" type="button" class="btn btn-secondary mr-1">
|
||||
<i class="fas fa-sync-alt" aria-hidden="true"></i> Rescan Files
|
||||
<i class="fas fa-sync-alt" aria-hidden="true"></i>{{ tr('rescan_files') }}
|
||||
</button>
|
||||
<button id="library-download-btn" type="button" class="btn btn-secondary mr-1">
|
||||
<i class="fas fa-download" aria-hidden="true"></i> Download All
|
||||
<i class="fas fa-download" aria-hidden="true"></i>{{ tr('download_all') }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger mr-1" data-toggle="modal"
|
||||
data-target="#deleteWarningModal">
|
||||
<i class="fas fa-trash-alt" aria-hidden="true"></i> Delete All
|
||||
<i class="fas fa-trash-alt" aria-hidden="true"></i>{{ tr('delete_all') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- QUESTION: should this div have aria-hidden as true?? -->
|
||||
<div class="modal fade" id="deleteWarningModal" tabindex="-1" role="dialog"
|
||||
aria-label="Modal Window for warning about deletion of files." aria-hidden="true">
|
||||
aria-label="{{ tr('aria_warning_of_deletion') }}" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" id="deleteWarningModalLabel">Are you really sure?</h3>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<h3 class="modal-title" id="deleteWarningModalLabel">{{ tr('are_you_really_sure') }}</h3>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="{{ tr('close') }}">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
All files listed here, include files on other pages, will be deleted from your
|
||||
hard-drive.
|
||||
Is that what you want?
|
||||
</div>
|
||||
{{ tr('delete_file_warning') }}</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ tr('close') }}</button>
|
||||
<button id="library-delete-btn" type="button" class="btn btn-danger"
|
||||
data-dismiss="modal">Delete All Listed Files</button>
|
||||
data-dismiss="modal">{{ tr('delete_all_files') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -336,7 +330,7 @@
|
||||
<input type="hidden" id="uploadDisabled" value="false" />
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Upload File</h3>
|
||||
<h3 class="card-title">{{ tr('upload_file') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="./upload" method="post" enctype="multipart/form-data">
|
||||
@ -345,8 +339,8 @@
|
||||
<div class="input-group mb-3">
|
||||
<div id="uploadField" style="display: flex; width: 100%">
|
||||
<div class="custom-file">
|
||||
<input type="file" name="file[]" class="custom-file-input" id="uploadSelectFile" aria-describedby="uploadSubmit" value="Browse Music file" multiple />
|
||||
<label class="custom-file-label" for="uploadSelectFile">Choose file</label>
|
||||
<input type="file" name="file[]" class="custom-file-input" id="uploadSelectFile" aria-describedby="uploadSubmit" value="{{ tr('browse_music_file') }}" multiple />
|
||||
<label class="custom-file-label" for="uploadSelectFile">{{ tr('choose_file') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -356,7 +350,7 @@
|
||||
<div class="col">
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<label for="uploadTargetDir" class="input-group-text">Upload To</label>
|
||||
<label for="uploadTargetDir" class="input-group-text">{{ tr('upload_to') }}</label>
|
||||
</div>
|
||||
<input class="form-control" list="upload-target-dirs" id="uploadTargetDir" name="upload-target-dirs" placeholder="uploads" />
|
||||
<datalist id="upload-target-dirs">
|
||||
@ -364,7 +358,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="btn btn-primary" type="button" id="uploadSubmit"><i class="fas fa-upload mr-1"></i>Upload!</button>
|
||||
<button class="btn btn-primary" type="button" id="uploadSubmit"><i class="fas fa-upload mr-1"></i>{{ tr('upload_submit') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -378,29 +372,29 @@
|
||||
<div class="card-deck">
|
||||
<div id="add-music-url" class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Add URL</h3>
|
||||
<h3 class="card-title">{{ tr('add_url') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<label for="music-url-input">Add Youtube or Soundcloud URL</label>
|
||||
<label for="music-url-input">{{ tr('add_youtube_or_soundcloud_url') }}</label>
|
||||
<div class="input-group mb-2">
|
||||
<input class="form-control" type="text" id="music-url-input" placeholder="URL...">
|
||||
<input class="form-control" type="text" id="music-url-input" placeholder="{{ tr('url_placeholder') }}">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Add URL
|
||||
{{ tr('add_url') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="add-radio-url" class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Add Radio</h3>
|
||||
<h3 class="card-title">{{ tr('add_radio') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<label for="radio-url-input">Add Radio URL</label>
|
||||
<label for="radio-url-input">{{ tr('add_radio_url') }}</label>
|
||||
<div class="input-group mb-2">
|
||||
<input id="radio-url-input" class="form-control" type="text" placeholder="Radio Address...">
|
||||
<input id="radio-url-input" class="form-control" type="text" placeholder="{{ tr('radio_url_placeholder') }}">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Add Radio
|
||||
{{ tr('add_radio') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -418,26 +412,26 @@
|
||||
<div id="playerToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
|
||||
<div class="toast-header">
|
||||
<i class="fas fa-play-circle mr-2 text-primary"></i>
|
||||
<strong class="mr-auto">Now Playing...</strong>
|
||||
<strong class="mr-auto">{{ tr('mini_player_title') }}</strong>
|
||||
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="toast-body" id="playerContainer">
|
||||
<img id="playerArtworkIdle" src="static/image/empty_box.svg" alt="A drawing of an empty box." />
|
||||
<img id="playerArtworkIdle" src="static/image/empty_box.svg" alt="{{ tr('aria_empty_box') }}" />
|
||||
<img id="playerArtwork" src="static/image/unknown-album.png" style="display: none;"
|
||||
alt="A black square with two eight notes beamed together." />
|
||||
alt="{{ tr('aria_default_cover') }}" />
|
||||
<div id="playerInfo">
|
||||
<div id="playerActionBox">
|
||||
<button id="playerPlayBtn" class="btn btn-primary btn-sm btn-space" style="display: none"
|
||||
aria-label="Play">
|
||||
aria-label="{{ tr('play') }}">
|
||||
<i class="fas fa-play"></i>
|
||||
</button>
|
||||
<button id="playerPauseBtn" class="btn btn-primary btn-sm btn-space" style="display: none"
|
||||
aria-label="Pause">
|
||||
aria-label="{{ tr('pause') }}">
|
||||
<i class="fas fa-pause"></i>
|
||||
</button>
|
||||
<button id="playerSkipBtn" class="btn btn-primary btn-sm" aria-label="Skip to next track">
|
||||
<button id="playerSkipBtn" class="btn btn-primary btn-sm" aria-label="{{ tr('aria_skip_to_next_track') }}">
|
||||
<i class="fas fa-fast-forward"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -470,7 +464,7 @@
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" id="addTagModalTitle">Edit tags for ?</h3>
|
||||
<h3 class="modal-title">{{ tr('edit_tags_for') }} <span id="addTagModalTitle">?</span></h3>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
@ -486,17 +480,17 @@
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input class="form-control form-control-sm btn-space" type="text" id="addTagModalInput"
|
||||
placeholder="tag1,tag2,..." aria-label="Tags to add">
|
||||
placeholder="tag1,tag2,..." aria-label="{{ tr('tags_to_add') }}">
|
||||
<button id="addTagModalAddBtn" type="button" class="btn btn-primary btn-sm">
|
||||
<i class="fas fa-plus" aria-hidden="true"></i>
|
||||
Add
|
||||
{{ tr('add') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ tr('close') }}</button>
|
||||
<button id="addTagModalSubmit" type="button" class="btn btn-success"
|
||||
data-dismiss="modal">Edit!</button>
|
||||
data-dismiss="modal">{{ tr('edit_submit') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -507,12 +501,12 @@
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" id="uploadTitle"><i class="fas fa-upload mr-1"></i>Uploading files...</h3>
|
||||
<h3 class="modal-title" id="uploadTitle"><i class="fas fa-upload mr-1"></i>{{ tr('uploading_files') }}</h3>
|
||||
</div>
|
||||
<div id="uploadModalBody" class="modal-body">
|
||||
<div id="uploadSuccessAlert" class="alert alert-success" role="alert" style="display: none">
|
||||
<i class="fas fa-check mr-1"></i>
|
||||
Uploaded finished!
|
||||
{{ tr('uploaded_finished') }}
|
||||
</div>
|
||||
<div id="uploadModalList" style="margin-left: 5px; margin-bottom: 10px;">
|
||||
<div class="uploadItem" style="display: none; width: 100%; padding-bottom: 8px;">
|
||||
@ -529,11 +523,11 @@
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" id="uploadClose" class="btn btn-success" data-dismiss="modal">
|
||||
<i class="fas fa-times mr-1"></i> Close</button>
|
||||
<i class="fas fa-times mr-1"></i> {{ tr('close') }}</button>
|
||||
<button type="button" id="uploadCancel" class="btn btn-danger" data-toggle="tooltip"
|
||||
data-html="true"
|
||||
title="<strong>Are you really sure?</strong> <br /> Click again to abort uploading.">
|
||||
<i class="fas fa-trash-alt mr-1" aria-hidden="true"></i> Cancel</button>
|
||||
title="{{ tr('cancel_upload_warning') }}">
|
||||
<i class="fas fa-trash-alt mr-1" aria-hidden="true"></i> {{ tr('cancel') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -4,7 +4,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta charset="UTF-8">
|
||||
|
||||
<title>botamusique web interface</title>
|
||||
<title>{{ tr('page_title') }}</title>
|
||||
|
||||
<link rel="icon" href="static/image/favicon.ico" />
|
||||
|
||||
@ -16,19 +16,17 @@
|
||||
<div class="col-8" style="margin: auto; padding-top: 50px;">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Token Required
|
||||
{{ tr('token_required') }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h3>Token Required!</h3>
|
||||
You are accessing the web interface of {{ name }}.
|
||||
A token is needed to grant you access.<br />
|
||||
Please send "{{ command }}" to the bot in mumble to acquire one.
|
||||
<h3>{{ tr('token_required') }}</h3>
|
||||
{{ tr('token_required_message') }}
|
||||
<form action="." method="get">
|
||||
<div class="form-group mt-3">
|
||||
<label for="token_input">Token</label>
|
||||
<label for="token_input">{{ tr('token') }}</label>
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control btn-space" id="token_input" name="token" placeholder="xxxxxxx">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
<button type="submit" class="btn btn-primary">{{ tr('submit') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -29,13 +29,13 @@ module.exports = {
|
||||
filename: 'static/css/[name].css',
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'templates/index.html',
|
||||
template: './templates/index.html',
|
||||
filename: 'templates/index.template.html',
|
||||
template: './templates/index.template.html',
|
||||
inject: false,
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'templates/need_token.html',
|
||||
template: './templates/need_token.html',
|
||||
filename: 'templates/need_token.template.html',
|
||||
template: './templates/need_token.template.html',
|
||||
inject: false,
|
||||
}),
|
||||
],
|
||||
|
Loading…
x
Reference in New Issue
Block a user