Merge upstream master and format HTML

This commit is contained in:
Tyler Vigario 2020-06-01 17:24:21 -07:00
commit d2e32a3af5
No known key found for this signature in database
GPG Key ID: 4D670648A0376AA4
11 changed files with 339 additions and 626 deletions

View File

@ -11,7 +11,7 @@ import interface
import media.system import media.system
import util import util
import variables as var import variables as var
from librb import radiobrowser from pyradios import RadioBrowser
from database import SettingsDatabase, MusicDatabase, Condition from database import SettingsDatabase, MusicDatabase, Condition
from media.item import item_id_generators, dict_to_item, dicts_to_items from media.item import item_id_generators, dict_to_item, dicts_to_items
from media.cache import get_cached_wrapper_from_scrap, get_cached_wrapper_by_id, get_cached_wrappers_by_tags, \ from media.cache import get_cached_wrapper_from_scrap, get_cached_wrapper_by_id, get_cached_wrappers_by_tags, \
@ -74,8 +74,8 @@ def register_all_commands(bot):
bot.register_command(constants.commands('change_user_password'), cmd_user_password, no_partial_match=True) bot.register_command(constants.commands('change_user_password'), cmd_user_password, no_partial_match=True)
# Just for debug use # Just for debug use
bot.register_command('rtrms', cmd_real_time_rms, True) bot.register_command('rtrms', cmd_real_time_rms, True)
#bot.register_command('loop', cmd_loop_state, True) # bot.register_command('loop', cmd_loop_state, True)
#bot.register_command('item', cmd_item, True) # bot.register_command('item', cmd_item, True)
def send_multi_lines(bot, lines, text, linebreak="<br />"): def send_multi_lines(bot, lines, text, linebreak="<br />"):
@ -86,7 +86,7 @@ def send_multi_lines(bot, lines, text, linebreak="<br />"):
for newline in lines: for newline in lines:
msg += br msg += br
br = linebreak br = linebreak
if bot.mumble.get_max_message_length()\ if bot.mumble.get_max_message_length() \
and (len(msg) + len(newline)) > (bot.mumble.get_max_message_length() - 4): # 4 == len("<br>") and (len(msg) + len(newline)) > (bot.mumble.get_max_message_length() - 4): # 4 == len("<br>")
bot.send_msg(msg, text) bot.send_msg(msg, text)
msg = "" msg = ""
@ -338,11 +338,10 @@ def cmd_play_playlist(bot, user, text, command, parameter):
pass pass
url = util.get_url_from_input(parameter) url = util.get_url_from_input(parameter)
log.debug("cmd: fetching media info from playlist url %s" % url) log.debug(f"cmd: fetching media info from playlist url {url}")
items = get_playlist_info(url=url, start_index=offset, user=user) items = get_playlist_info(url=url, start_index=offset, user=user)
if len(items) > 0: if len(items) > 0:
items = var.playlist.extend(list(map( items = var.playlist.extend(list(map(lambda item: get_cached_wrapper_from_scrap(**item), items)))
lambda item: get_cached_wrapper_from_scrap(**item), items)))
for music in items: for music in items:
log.info("cmd: add to playlist: " + music.format_debug_string()) log.info("cmd: add to playlist: " + music.format_debug_string())
else: else:
@ -386,24 +385,22 @@ def cmd_rb_query(bot, user, text, command, parameter):
bot.send_msg(msg, text) bot.send_msg(msg, text)
else: else:
log.debug('cmd: Found query parameter: ' + parameter) log.debug('cmd: Found query parameter: ' + parameter)
# bot.send_msg('Searching for stations - this may take some seconds...', text) rb = RadioBrowser()
rb_stations = radiobrowser.getstations_byname(parameter) rb_stations = rb.search(name=parameter, name_exact=False)
msg = constants.strings('rb_query_result') msg = constants.strings('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>' 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: if not rb_stations:
log.debug('cmd: No matches found for rbquery ' + parameter) log.debug(f"cmd: No matches found for rbquery {parameter}")
bot.send_msg('Radio-Browser found no matches for ' + parameter, text) bot.send_msg(f"Radio-Browser found no matches for {parameter}", text)
else: else:
for s in rb_stations: for s in rb_stations:
stationid = s['id'] station_id = s['stationuuid']
stationname = s['stationname'] station_name = s['name']
country = s['country'] country = s['countrycode']
codec = s['codec'] codec = s['codec']
bitrate = s['bitrate'] bitrate = s['bitrate']
genre = s['genre'] genre = s['tags']
# msg += f'<tr><td>{stationid}</td><td>{stationname}</td><td>{genre}</td><td>{codec}/{bitrate}</td><td>{country}</td></tr>' msg += f"<tr><td>{station_id}</td><td>{station_name}</td><td>{genre}</td><td>{codec}/{bitrate}</td><td>{country}</td></tr>"
msg += '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s/%s</td><td>%s</td></tr>' % (
stationid, stationname, genre, codec, bitrate, country)
msg += '</table>' msg += '</table>'
# Full message as html table # Full message as html table
if len(msg) <= 5000: if len(msg) <= 5000:
@ -414,10 +411,9 @@ def cmd_rb_query(bot, user, text, command, parameter):
msg = constants.strings('rb_query_result') + ' (shortened L1)' msg = constants.strings('rb_query_result') + ' (shortened L1)'
msg += '\n<table><tr><th>!rbplay ID</th><th>Station Name</th></tr>' msg += '\n<table><tr><th>!rbplay ID</th><th>Station Name</th></tr>'
for s in rb_stations: for s in rb_stations:
stationid = s['id'] station_id = s['stationuuid']
stationname = s['stationname'] station_name = s['name']
# msg += f'<tr><td>{stationid}</td><td>{stationname}</td>' msg += f'<tr><td>{station_id}</td><td>{station_name}</td>'
msg += '<tr><td>%s</td><td>%s</td>' % (stationid, stationname)
msg += '</table>' msg += '</table>'
if len(msg) <= 5000: if len(msg) <= 5000:
bot.send_msg(msg, text) bot.send_msg(msg, text)
@ -427,16 +423,14 @@ def cmd_rb_query(bot, user, text, command, parameter):
msg = constants.strings('rb_query_result') + ' (shortened L2)' msg = constants.strings('rb_query_result') + ' (shortened L2)'
msg += '!rbplay ID - Station Name' msg += '!rbplay ID - Station Name'
for s in rb_stations: for s in rb_stations:
stationid = s['id'] station_id = s['stationuuid']
stationname = s['stationname'][:12] station_name = s['name'][:12]
# msg += f'{stationid} - {stationname}' msg += f'{station_id} - {station_name}'
msg += '%s - %s' % (stationid, stationname)
if len(msg) <= 5000: if len(msg) <= 5000:
bot.send_msg(msg, text) bot.send_msg(msg, text)
# Message still too long # Message still too long
else: else:
bot.send_msg('Query result too long to post (> 5000 characters), please try another query.', bot.send_msg('Query result too long to post (> 5000 characters), please try another query.', text)
text)
def cmd_rb_play(bot, user, text, command, parameter): def cmd_rb_play(bot, user, text, command, parameter):
@ -449,22 +443,21 @@ def cmd_rb_play(bot, user, text, command, parameter):
bot.send_msg(msg, text) bot.send_msg(msg, text)
else: else:
log.debug('cmd: Retreiving url for station ID ' + parameter) log.debug('cmd: Retreiving url for station ID ' + parameter)
rstation = radiobrowser.getstationname_byid(parameter) rb = RadioBrowser()
rstation = rb.station_by_uuid(parameter)
stationname = rstation[0]['name'] stationname = rstation[0]['name']
country = rstation[0]['country'] country = rstation[0]['countrycode']
codec = rstation[0]['codec'] codec = rstation[0]['codec']
bitrate = rstation[0]['bitrate'] bitrate = rstation[0]['bitrate']
genre = rstation[0]['tags'] genre = rstation[0]['tags']
homepage = rstation[0]['homepage'] homepage = rstation[0]['homepage']
url = rstation[0]['url']
msg = 'Radio station added to playlist:' msg = 'Radio station added to playlist:'
# msg += '<table><tr><th>ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th><th>Homepage</th></tr>' + \
# f'<tr><td>{parameter}</td><td>{stationname}</td><td>{genre}</td><td>{codec}/{bitrate}</td><td>{country}</td><td>{homepage}</td></tr></table>'
msg += '<table><tr><th>ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th><th>Homepage</th></tr>' + \ msg += '<table><tr><th>ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th><th>Homepage</th></tr>' + \
'<tr><td>%s</td><td>%s</td><td>%s</td><td>%s/%s</td><td>%s</td><td>%s</td></tr></table>' \ f"<tr><td>{parameter}</td><td>{stationname}</td><td>{genre}</td><td>{codec}/{bitrate}</td><td>{country}</td><td>{homepage}</td></tr></table>"
% (parameter, stationname, genre, codec, bitrate, country, homepage) log.debug(f'cmd: Added station to playlist {stationname}')
log.debug('cmd: Added station to playlist %s' % stationname)
bot.send_msg(msg, text) bot.send_msg(msg, text)
url = radiobrowser.geturl_byid(parameter)
if url != "-1": if url != "-1":
log.info('cmd: Found url: ' + url) log.info('cmd: Found url: ' + url)
music_wrapper = get_cached_wrapper_from_scrap(type='radio', url=url, name=stationname, user=user) music_wrapper = get_cached_wrapper_from_scrap(type='radio', url=url, name=stationname, user=user)
@ -517,7 +510,7 @@ def cmd_yt_search(bot, user, text, command, parameter):
def _yt_format_result(results, start, count): def _yt_format_result(results, start, count):
msg = '<table><tr><th width="10%">Index</th><th>Title</th><th width="20%">Uploader</th></tr>' msg = '<table><tr><th width="10%">Index</th><th>Title</th><th width="20%">Uploader</th></tr>'
for index, item in enumerate(results[start:start+count]): for index, item in enumerate(results[start:start + count]):
msg += '<tr><td>{index:d}</td><td>{title}</td><td>{uploader}</td></tr>'.format( msg += '<tr><td>{index:d}</td><td>{title}</td><td>{uploader}</td></tr>'.format(
index=index + 1, title=item[1], uploader=item[2]) index=index + 1, title=item[1], uploader=item[2])
msg += '</table>' msg += '</table>'
@ -602,10 +595,9 @@ def cmd_volume(bot, user, text, command, parameter):
# The volume is a percentage # The volume is a percentage
if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100: if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
bot.volume_set = float(float(parameter) / 100) bot.volume_set = float(float(parameter) / 100)
bot.send_msg(constants.strings('change_volume', bot.send_msg(constants.strings('change_volume', volume=int(bot.volume_set * 100), user=bot.mumble.users[text.actor]['name']), text)
volume=int(bot.volume_set * 100), user=bot.mumble.users[text.actor]['name']), text)
var.db.set('bot', 'volume', str(bot.volume_set)) var.db.set('bot', 'volume', str(bot.volume_set))
log.info('cmd: volume set to %d' % (bot.volume_set * 100)) log.info(f'cmd: volume set to {bot.volume_set * 100}')
else: else:
bot.send_msg(constants.strings('current_volume', volume=int(bot.volume_set * 100)), text) bot.send_msg(constants.strings('current_volume', volume=int(bot.volume_set * 100)), text)
@ -618,8 +610,7 @@ def cmd_ducking(bot, user, text, command, parameter):
var.db.set('bot', 'ducking', True) var.db.set('bot', 'ducking', True)
bot.ducking_volume = var.config.getfloat("bot", "ducking_volume", fallback=0.05) bot.ducking_volume = var.config.getfloat("bot", "ducking_volume", fallback=0.05)
bot.ducking_threshold = var.config.getint("bot", "ducking_threshold", fallback=5000) bot.ducking_threshold = var.config.getint("bot", "ducking_threshold", fallback=5000)
bot.mumble.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED, bot.mumble.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED, bot.ducking_sound_received)
bot.ducking_sound_received)
bot.mumble.set_receive_sound(True) bot.mumble.set_receive_sound(True)
log.info('cmd: ducking is on') log.info('cmd: ducking is on')
msg = "Ducking on." msg = "Ducking on."
@ -639,10 +630,10 @@ def cmd_ducking_threshold(bot, user, text, command, parameter):
if parameter and parameter.isdigit(): if parameter and parameter.isdigit():
bot.ducking_threshold = int(parameter) bot.ducking_threshold = int(parameter)
var.db.set('bot', 'ducking_threshold', str(bot.ducking_threshold)) var.db.set('bot', 'ducking_threshold', str(bot.ducking_threshold))
msg = "Ducking threshold set to %d." % bot.ducking_threshold msg = f"Ducking threshold set to {bot.ducking_threshold}."
bot.send_msg(msg, text) bot.send_msg(msg, text)
else: else:
msg = "Current ducking threshold is %d." % bot.ducking_threshold msg = f"Current ducking threshold is {bot.ducking_threshold}."
bot.send_msg(msg, text) bot.send_msg(msg, text)
@ -652,11 +643,9 @@ def cmd_ducking_volume(bot, user, text, command, parameter):
# The volume is a percentage # The volume is a percentage
if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100: if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
bot.ducking_volume = float(float(parameter) / 100) bot.ducking_volume = float(float(parameter) / 100)
bot.send_msg(constants.strings('change_ducking_volume', bot.send_msg(constants.strings('change_ducking_volume', volume=int(bot.ducking_volume * 100), user=bot.mumble.users[text.actor]['name']), text)
volume=int(bot.ducking_volume * 100), user=bot.mumble.users[text.actor]['name']), text)
# var.db.set('bot', 'volume', str(bot.volume_set))
var.db.set('bot', 'ducking_volume', str(bot.ducking_volume)) var.db.set('bot', 'ducking_volume', str(bot.ducking_volume))
log.info('cmd: volume on ducking set to %d' % (bot.ducking_volume * 100)) log.info(f'cmd: volume on ducking set to {bot.ducking_volume * 100}')
else: else:
bot.send_msg(constants.strings('current_ducking_volume', volume=int(bot.ducking_volume * 100)), text) bot.send_msg(constants.strings('current_ducking_volume', volume=int(bot.ducking_volume * 100)), text)
@ -826,7 +815,7 @@ def cmd_mode(bot, user, text, command, parameter):
else: else:
var.db.set('playlist', 'playback_mode', parameter) var.db.set('playlist', 'playback_mode', parameter)
var.playlist = media.playlist.get_playlist(parameter, var.playlist) var.playlist = media.playlist.get_playlist(parameter, var.playlist)
log.info("command: playback mode changed to %s." % parameter) log.info(f"command: playback mode changed to {parameter}.")
bot.send_msg(constants.strings("change_mode", mode=var.playlist.mode, bot.send_msg(constants.strings("change_mode", mode=var.playlist.mode,
user=bot.mumble.users[text.actor]['name']), text) user=bot.mumble.users[text.actor]['name']), text)
if parameter == "random": if parameter == "random":
@ -878,8 +867,7 @@ def cmd_add_tag(bot, user, text, command, parameter):
if tags[0]: if tags[0]:
if index.isdigit() and 1 <= int(index) <= len(var.playlist): if index.isdigit() and 1 <= int(index) <= len(var.playlist):
var.playlist[int(index) - 1].add_tags(tags) var.playlist[int(index) - 1].add_tags(tags)
log.info("cmd: add tags %s to song %s" % (", ".join(tags), log.info(f"cmd: add tags {', '.join(tags)} to song {var.playlist[int(index) - 1].format_debug_string()}")
var.playlist[int(index) - 1].format_debug_string()))
bot.send_msg(constants.strings("added_tags", bot.send_msg(constants.strings("added_tags",
tags=", ".join(tags), tags=", ".join(tags),
song=var.playlist[int(index) - 1].format_title()), text) song=var.playlist[int(index) - 1].format_title()), text)
@ -888,8 +876,7 @@ def cmd_add_tag(bot, user, text, command, parameter):
elif index == "*": elif index == "*":
for item in var.playlist: for item in var.playlist:
item.add_tags(tags) item.add_tags(tags)
log.info("cmd: add tags %s to song %s" % (", ".join(tags), log.info(f"cmd: add tags {', '.join(tags)} to song {item.format_debug_string()}")
item.format_debug_string()))
bot.send_msg(constants.strings("added_tags_to_all", tags=", ".join(tags)), text) bot.send_msg(constants.strings("added_tags_to_all", tags=", ".join(tags)), text)
return return
@ -917,15 +904,14 @@ def cmd_remove_tag(bot, user, text, command, parameter):
if index.isdigit() and 1 <= int(index) <= len(var.playlist): if index.isdigit() and 1 <= int(index) <= len(var.playlist):
if tags[0] != "*": if tags[0] != "*":
var.playlist[int(index) - 1].remove_tags(tags) var.playlist[int(index) - 1].remove_tags(tags)
log.info("cmd: remove tags %s from song %s" % (", ".join(tags), log.info(f"cmd: remove tags {', '.join(tags)} from song {var.playlist[int(index) - 1].format_debug_string()}")
var.playlist[int(index) - 1].format_debug_string()))
bot.send_msg(constants.strings("removed_tags", bot.send_msg(constants.strings("removed_tags",
tags=", ".join(tags), tags=", ".join(tags),
song=var.playlist[int(index) - 1].format_title()), text) song=var.playlist[int(index) - 1].format_title()), text)
return return
else: else:
var.playlist[int(index) - 1].clear_tags() var.playlist[int(index) - 1].clear_tags()
log.info("cmd: clear tags from song %s" % (var.playlist[int(index) - 1].format_debug_string())) log.info(f"cmd: clear tags from song {var.playlist[int(index) - 1].format_debug_string()}")
bot.send_msg(constants.strings("cleared_tags", bot.send_msg(constants.strings("cleared_tags",
song=var.playlist[int(index) - 1].format_title()), text) song=var.playlist[int(index) - 1].format_title()), text)
return return
@ -934,14 +920,13 @@ def cmd_remove_tag(bot, user, text, command, parameter):
if tags[0] != "*": if tags[0] != "*":
for item in var.playlist: for item in var.playlist:
item.remove_tags(tags) item.remove_tags(tags)
log.info("cmd: remove tags %s from song %s" % (", ".join(tags), log.info(f"cmd: remove tags {', '.join(tags)} from song {item.format_debug_string()}")
item.format_debug_string()))
bot.send_msg(constants.strings("removed_tags_from_all", tags=", ".join(tags)), text) bot.send_msg(constants.strings("removed_tags_from_all", tags=", ".join(tags)), text)
return return
else: else:
for item in var.playlist: for item in var.playlist:
item.clear_tags() item.clear_tags()
log.info("cmd: clear tags from song %s" % (item.format_debug_string())) 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(constants.strings("cleared_tags_from_all"), text)
return return
@ -969,7 +954,7 @@ def cmd_find_tagged(bot, user, text, command, parameter):
count += 1 count += 1
if count > ITEMS_PER_PAGE: if count > ITEMS_PER_PAGE:
break break
msgs.append("<li><b>{:d}</b> - <b>{}</b> (<i>{}</i>)</li>".format(i+1, item.title, ", ".join(item.tags))) msgs.append("<li><b>{:d}</b> - <b>{}</b> (<i>{}</i>)</li>".format(i + 1, item.title, ", ".join(item.tags)))
if count != 0: if count != 0:
msgs.append("</ul>") msgs.append("</ul>")
@ -1210,7 +1195,7 @@ def cmd_web_user_add(bot, user, text, command, parameter):
if parameter not in web_users: if parameter not in web_users:
web_users.append(parameter) web_users.append(parameter)
var.db.set("privilege", "web_access", json.dumps(web_users)) 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(constants.strings('web_user_list', users=", ".join(web_users)), text)
else: else:
bot.send_msg(constants.strings('command_disabled', command=command), text) bot.send_msg(constants.strings('command_disabled', command=command), text)
@ -1227,7 +1212,7 @@ def cmd_web_user_remove(bot, user, text, command, parameter):
if parameter in web_users: if parameter in web_users:
web_users.remove(parameter) web_users.remove(parameter)
var.db.set("privilege", "web_access", json.dumps(web_users)) 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(constants.strings('web_user_list', users=", ".join(web_users)), text)
else: else:
bot.send_msg(constants.strings('command_disabled', command=command), text) bot.send_msg(constants.strings('command_disabled', command=command), text)
@ -1237,7 +1222,7 @@ def cmd_web_user_list(bot, user, text, command, parameter):
if auth_method == 'password': if auth_method == 'password':
web_users = json.loads(var.db.get("privilege", "web_access", fallback='[]')) 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(constants.strings('web_user_list', users=", ".join(web_users)), text)
else: else:
bot.send_msg(constants.strings('command_disabled', command=command), text) bot.send_msg(constants.strings('command_disabled', command=command), text)

View File

View File

@ -1,33 +0,0 @@
from librb.rbRadios import RadioBrowser
rb = RadioBrowser()
def getstations_byname(query):
results = rb.stations_byname(query)
stations = []
for st in results:
try:
station = {'stationname': st['name'], 'id': st['id'], 'codec': st['codec'], 'bitrate': st['bitrate'], 'country': st['country'], 'homepage': st['homepage'], 'genre': st['tags']}
stations.append(station)
except:
pass
return stations
def geturl_byid(id):
url = rb.playable_station(id)['url']
if url is not None:
return url
else:
return "-1"
def getstationname_byid(id):
return rb.stations_byid(id)
if __name__ == "__main__":
r = getstations_byname('r.sh')
stationinfo = getstationname_byid(96748)
pass

View File

@ -1,16 +0,0 @@
BASE_URL = "http://www.radio-browser.info/webservice/"
endpoints = {
"countries": {1: "{fmt}/countries", 2: "{fmt}/countries/{filter}"},
"codecs": {1: "{fmt}/codecs", 2: "{fmt}/codecs/{filter}"},
"states": {
1: "{fmt}/states",
2: "{fmt}/states/{filter}",
3: "{fmt}/states/{country}/{filter}",
},
"languages": {1: "{fmt}/languages", 2: "{fmt}/languages/{filter}"},
"tags": {1: "{fmt}/tags", 2: "{fmt}/tags/{filter}"},
"stations": {1: "{fmt}/stations", 3: "{fmt}/stations/{by}/{search_term}"},
"playable_station": {3: "{ver}/{fmt}/url/{station_id}"},
"station_search": {1: "{fmt}/stations/search"},
}

View File

@ -1,179 +0,0 @@
import requests
from librb.rbConstants import endpoints, BASE_URL
def request(endpoint, **kwargs):
fmt = kwargs.get("format", "json")
if fmt == "xml":
content_type = "application/%s" % fmt
else:
content_type = "application/%s" % fmt
headers = {"content-type": content_type, "User-Agent": "pyradios/dev"}
params = kwargs.get("params", {})
url = BASE_URL + endpoint
resp = requests.get(url, headers=headers, params=params)
if resp.status_code == 200:
if fmt == "xml":
return resp.text
return resp.json()
return resp.raise_for_status()
class EndPointBuilder:
def __init__(self, fmt="json"):
self.fmt = fmt
self._option = None
self._endpoint = None
@property
def endpoint(self):
return endpoints[self._endpoint][self._option]
def produce_endpoint(self, **parts):
self._option = len(parts)
self._endpoint = parts["endpoint"]
parts.update({"fmt": self.fmt})
return self.endpoint.format(**parts)
class RadioBrowser:
def __init__(self, fmt="json"):
self.fmt = fmt
self.builder = EndPointBuilder(fmt=self.fmt)
def countries(self, filter=""):
endpoint = self.builder.produce_endpoint(endpoint="countries")
return request(endpoint)
def codecs(self, filter=""):
endpoint = self.builder.produce_endpoint(endpoint="codecs")
return request(endpoint)
def states(self, country="", filter=""):
endpoint = self.builder.produce_endpoint(
endpoint="states", country=country, filter=filter
)
return request(endpoint)
def languages(self, filter=""):
endpoint = self.builder.produce_endpoint(endpoint="languages", filter=filter)
return request(endpoint)
def tags(self, filter=""):
endpoint = self.builder.produce_endpoint(endpoint="tags", filter=filter)
return request(endpoint)
def stations(self, **params):
endpoint = self.builder.produce_endpoint(endpoint="stations")
kwargs = {}
if params:
kwargs.update({"params": params})
return request(endpoint, **kwargs)
def stations_byid(self, id):
endpoint = self.builder.produce_endpoint(
endpoint="stations", by="byid", search_term=id
)
return request(endpoint)
def stations_byuuid(self, uuid):
endpoint = self.builder.produce_endpoint(
endpoint="stations", by="byuuid", search_term=uuid
)
return request(endpoint)
def stations_byname(self, name):
endpoint = self.builder.produce_endpoint(
endpoint="stations", by="byname", search_term=name
)
return request(endpoint)
def stations_bynameexact(self, nameexact):
endpoint = self.builder.produce_endpoint(
endpoint="stations", by="bynameexact", search_term=nameexact
)
return request(endpoint)
def stations_bycodec(self, codec):
endpoint = self.builder.produce_endpoint(
endpoint="stations", by="bycodec", search_term=codec
)
return request(endpoint)
def stations_bycodecexact(self, codecexact):
endpoint = self.builder.produce_endpoint(
endpoint="stations", by="bycodecexact", search_term=codecexact
)
return request(endpoint)
def stations_bycountry(self, country):
endpoint = self.builder.produce_endpoint(
endpoint="stations", by="bycountry", search_term=country
)
return request(endpoint)
def stations_bycountryexact(self, countryexact):
endpoint = self.builder.produce_endpoint(
endpoint="stations", by="bycountryexact", search_term=countryexact
)
return request(endpoint)
def stations_bystate(self, state):
endpoint = self.builder.produce_endpoint(
endpoint="stations", by="bystate", search_term=state
)
return request(endpoint)
def stations_bystateexact(self, stateexact):
endpoint = self.builder.produce_endpoint(
endpoint="stations", by="bystateexact", search_term=stateexact
)
return request(endpoint)
#
def stations_bylanguage(self, language):
endpoint = self.builder.produce_endpoint(
endpoint="stations", by="bylanguage", search_term=language
)
return request(endpoint)
def stations_bylanguageexact(self, languageexact):
endpoint = self.builder.produce_endpoint(
endpoint="stations", by="bylanguageexact", search_term=languageexact
)
return request(endpoint)
def stations_bytag(self, tag):
endpoint = self.builder.produce_endpoint(
endpoint="stations", by="bytag", search_term=tag
)
return request(endpoint)
def stations_bytagexact(self, tagexact):
endpoint = self.builder.produce_endpoint(
endpoint="stations", by="bytagexact", search_term=tagexact
)
return request(endpoint)
def playable_station(self, station_id):
endpoint = self.builder.produce_endpoint(
endpoint="playable_station", station_id=station_id, ver="v2"
)
return request(endpoint)
def station_search(self, params, **kwargs):
# http://www.radio-browser.info/webservice#Advanced_station_search
assert isinstance(params, dict), "params is not a dict"
kwargs["params"] = params
endpoint = self.builder.produce_endpoint(endpoint="station_search")
return request(endpoint, **kwargs)

View File

@ -108,6 +108,16 @@ class MusicCache(dict):
self.dir_lock.acquire() self.dir_lock.acquire()
self.log.info("library: rebuild directory cache") self.log.info("library: rebuild directory cache")
files = util.get_recursive_file_list_sorted(var.music_folder) files = util.get_recursive_file_list_sorted(var.music_folder)
# remove deleted files
results = self.db.query_music(Condition().or_equal('type', 'file'))
for result in results:
if result['path'] not in files:
self.log.debug("library: music file missed: %s, delete from library." % result['path'])
self.db.delete_music(Condition().and_equal('id', result['id']))
else:
files.remove(result['path'])
for file in files: for file in files:
results = self.db.query_music(Condition().and_equal('path', file)) results = self.db.query_music(Condition().and_equal('path', file))
if not results: if not results:
@ -129,7 +139,10 @@ class CachedItemWrapper:
self.version = 0 self.version = 0
def item(self): def item(self):
if self.id in self.lib:
return self.lib[self.id] return self.lib[self.id]
else:
raise ValueError(f"Uncached item of id {self.id}.")
def to_dict(self): def to_dict(self):
dict = self.item().to_dict() dict = self.item().to_dict()

View File

@ -35,7 +35,7 @@ class MumbleBot:
def __init__(self, args): def __init__(self, args):
self.log = logging.getLogger("bot") self.log = logging.getLogger("bot")
self.log.info("bot: botamusique version %s, starting..." % self.version) self.log.info(f"bot: botamusique version {self.version}, starting...")
signal.signal(signal.SIGINT, self.ctrl_caught) signal.signal(signal.SIGINT, self.ctrl_caught)
self.cmd_handle = {} self.cmd_handle = {}
self.volume_set = var.config.getfloat('bot', 'volume') self.volume_set = var.config.getfloat('bot', 'volume')
@ -50,8 +50,6 @@ class MumbleBot:
self.channel = var.config.get("server", "channel", fallback=None) self.channel = var.config.get("server", "channel", fallback=None)
var.user = args.user var.user = args.user
var.music_folder = util.solve_filepath(var.config.get('bot', 'music_folder'))
var.tmp_folder = util.solve_filepath(var.config.get('bot', 'tmp_folder'))
var.is_proxified = var.config.getboolean( var.is_proxified = var.config.getboolean(
"webinterface", "is_web_proxified") "webinterface", "is_web_proxified")
self.exit = False self.exit = False
@ -186,8 +184,8 @@ class MumbleBot:
new_version = util.new_release_version() new_version = util.new_release_version()
if version.parse(new_version) > version.parse(self.version): if version.parse(new_version) > version.parse(self.version):
changelog = util.fetch_changelog() changelog = util.fetch_changelog()
self.log.info("update: new version %s found, current installed version %s." % (new_version, self.version)) self.log.info(f"update: new version {new_version} found, current installed version {self.version}.")
self.log.info("update: changelog: " + changelog) self.log.info(f"update: changelog: {changelog}")
changelog = changelog.replace("\n", "<br>") changelog = changelog.replace("\n", "<br>")
self.send_channel_msg(constants.strings('new_version_found', new_version=new_version, changelog=changelog)) self.send_channel_msg(constants.strings('new_version_found', new_version=new_version, changelog=changelog))
else: else:
@ -305,7 +303,7 @@ class MumbleBot:
except: except:
error_traceback = traceback.format_exc() error_traceback = traceback.format_exc()
error = error_traceback.rstrip().split("\n")[-1] error = error_traceback.rstrip().split("\n")[-1]
self.log.error("bot: command %s failed with error: %s\n" % (command_exc, error_traceback)) 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(constants.strings('error_executing_command', command=command_exc, error=error), text)
def send_msg(self, msg, text): def send_msg(self, msg, text):
@ -408,7 +406,7 @@ class MumbleBot:
def async_download(self, item): def async_download(self, item):
th = threading.Thread( th = threading.Thread(
target=self._download, name="Prepare-" + item.id[:7], args=(item,)) target=self._download, name="Prepare-" + item.id[:7], args=(item,))
self.log.info("bot: start preparing item in thread: %s" % item.format_debug_string()) self.log.info(f"bot: start preparing item in thread: {item.format_debug_string()}")
th.daemon = True th.daemon = True
th.start() th.start()
return th return th
@ -443,7 +441,7 @@ class MumbleBot:
while self.thread and self.mumble.sound_output.get_buffer_size() > 0.5 and not self.exit: while self.thread and self.mumble.sound_output.get_buffer_size() > 0.5 and not self.exit:
# If the buffer isn't empty, I cannot send new music part, so I wait # If the buffer isn't empty, I cannot send new music part, so I wait
self._loop_status = 'Wait for buffer %.3f' % self.mumble.sound_output.get_buffer_size() self._loop_status = f'Wait for buffer {self.mumble.sound_output.get_buffer_size():.3f}'
time.sleep(0.01) time.sleep(0.01)
if self.thread: if self.thread:
@ -629,7 +627,7 @@ class MumbleBot:
self.song_start_at = -1 self.song_start_at = -1
if len(var.playlist) > 0: if len(var.playlist) > 0:
self.pause_at_id = var.playlist.current_item().id self.pause_at_id = var.playlist.current_item().id
self.log.info("bot: music paused at %.2f seconds." % self.playhead) self.log.info(f"bot: music paused at {self.playhead:.2f} seconds.")
def resume(self): def resume(self):
self.is_pause = False self.is_pause = False
@ -768,6 +766,8 @@ if __name__ == '__main__':
DatabaseMigration(var.db, var.music_db).migrate() DatabaseMigration(var.db, var.music_db).migrate()
var.music_folder = util.solve_filepath(var.config.get('bot', 'music_folder'))
var.tmp_folder = util.solve_filepath(var.config.get('bot', 'tmp_folder'))
var.cache = MusicCache(var.music_db) var.cache = MusicCache(var.music_db)
if var.config.get("bot", "refresh_cache_on_startup", fallback=True): if var.config.get("bot", "refresh_cache_on_startup", fallback=True):
@ -785,7 +785,7 @@ if __name__ == '__main__':
if playback_mode in ["one-shot", "repeat", "random", "autoplay"]: if playback_mode in ["one-shot", "repeat", "random", "autoplay"]:
var.playlist = media.playlist.get_playlist(playback_mode) var.playlist = media.playlist.get_playlist(playback_mode)
else: else:
raise KeyError("Unknown playback mode '%s'" % playback_mode) raise KeyError(f"Unknown playback mode '{playback_mode}'")
# ====================== # ======================
# Create bot instance # Create bot instance

View File

@ -8,3 +8,4 @@ mutagen
requests requests
packaging packaging
pymumble pymumble
pyradios

File diff suppressed because one or more lines are too long

View File

@ -28,14 +28,12 @@
<button type="button" id="play-pause-btn" class="btn btn-info mb-2 btn-space" onclick="togglePlayPause()"> <button type="button" id="play-pause-btn" class="btn btn-info mb-2 btn-space" onclick="togglePlayPause()">
<i class="fas fa-play"></i> <i class="fas fa-play"></i>
</button> </button>
<button type="button" id="fast-forward-btn" class="btn btn-info mb-2" <button type="button" id="fast-forward-btn" class="btn btn-info mb-2" onclick="request('post', {action : 'next'})">
onclick="request('post', {action : 'next'})">
<i class="fas fa-fast-forward"></i> <i class="fas fa-fast-forward"></i>
</button> </button>
<div class="ml-auto"> <div class="ml-auto">
<div class="dropdown mr-2"> <div class="dropdown mr-2">
<button class="btn btn-secondary dropdown-toggle" type="button" id="play-mode" <button class="btn btn-secondary dropdown-toggle" type="button" id="play-mode" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-tasks mr-2" aria-hidden="true" id="modeIndicator"></i> <i class="fas fa-tasks mr-2" aria-hidden="true" id="modeIndicator"></i>
</button> </button>
<div class="dropdown-menu" aria-labelledby="play-mode"> <div class="dropdown-menu" aria-labelledby="play-mode">
@ -53,8 +51,7 @@
</a> </a>
</div> </div>
</div> </div>
<button type="button" id="volume-popover-btn" class="btn btn-warning ml-1" <button type="button" id="volume-popover-btn" class="btn btn-warning ml-1" onclick="toggleVolumePopover()">
onclick="toggleVolumePopover()">
<i class="fa fa-volume-up" aria-hidden="true"></i> <i class="fa fa-volume-up" aria-hidden="true"></i>
</button> </button>
@ -63,8 +60,7 @@
<i class="fa fa-volume-down" aria-hidden="true"></i> <i class="fa fa-volume-down" aria-hidden="true"></i>
</a> </a>
<input type="range" class="custom-range ml-1" id="volume-slider" min="0" max="1" step="0.01" <input type="range" class="custom-range ml-1" id="volume-slider" min="0" max="1" step="0.01" value="0.5" onchange="setVolumeDelayed(this.value)" />
value="0.5" onchange="setVolumeDelayed(this.value)" />
<a onclick="request('post', {action : 'volume_up'})"> <a onclick="request('post', {action : 'volume_up'})">
<i class="fa fa-volume-up" aria-hidden="true"></i> <i class="fa fa-volume-up" aria-hidden="true"></i>
@ -98,8 +94,7 @@
</tr> </tr>
<tr class="playlist-expand table-dark d-none"> <tr class="playlist-expand table-dark d-none">
<td colspan="4" class="text-center"> <td colspan="4" class="text-center">
<a class="text-muted" href="javascript:">See item <span <a class="text-muted" href="javascript:">See item <span class="playlist-expand-item-range"></span> on the playlist.
class="playlist-expand-item-range"></span> on the playlist.
</a> </a>
</td> </td>
</tr> </tr>
@ -161,16 +156,13 @@
<label>Type</label> <label>Type</label>
<div id="filter-type" class="input-group mb-2"> <div id="filter-type" class="input-group mb-2">
<div class="btn-group btn-group-sm btn-group-toggle"> <div class="btn-group btn-group-sm btn-group-toggle">
<label id="filter-type-file" class="btn btn-secondary" <label id="filter-type-file" class="btn btn-secondary" onclick="setFilterType(event, 'file')">
onclick="setFilterType(event, 'file')">
<input type="checkbox" name="options"> File <input type="checkbox" name="options"> File
</label> </label>
<label id="filter-type-url" class="btn btn-secondary" <label id="filter-type-url" class="btn btn-secondary" onclick="setFilterType(event, 'url')">
onclick="setFilterType(event, 'url')">
<input type="checkbox" name="options"> URL <input type="checkbox" name="options"> URL
</label> </label>
<label id="filter-type-radio" class="btn btn-secondary" <label id="filter-type-radio" class="btn btn-secondary" onclick="setFilterType(event, 'radio')">
onclick="setFilterType(event, 'radio')">
<input type="checkbox" name="options"> Radio <input type="checkbox" name="options"> Radio
</label> </label>
</div> </div>
@ -188,8 +180,7 @@
<label for="filter-keywords">Keywords</label> <label for="filter-keywords">Keywords</label>
<div id="filter-path" class="input-group mb-2"> <div id="filter-path" class="input-group mb-2">
<input class="form-control form-control-sm" id="filter-keywords" name="keywords" <input class="form-control form-control-sm" id="filter-keywords" name="keywords" placeholder="Keywords..." style="margin-top:5px;" />
placeholder="Keywords..." style="margin-top:5px;" />
</div> </div>
</div> </div>
@ -197,8 +188,7 @@
<label for="filter-tag">Tags</label> <label for="filter-tag">Tags</label>
<div id="filter-type mb-2"> <div id="filter-type mb-2">
{% for tag in tags_color_lookup.keys() %} {% for tag in tags_color_lookup.keys() %}
<span id="filter-tag" <span id="filter-tag" class="filter-tag tag-unclicked tag-click badge badge-{{ tags_color_lookup[tag] }}">{{ tag }}</span>
class="filter-tag tag-unclicked tag-click badge badge-{{ tags_color_lookup[tag] }}">{{ tag }}</span>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
@ -226,66 +216,6 @@
</div> </div>
</div> </div>
<label for="filter-keywords">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;" />
</div>
</div>
<div class="library-info-col col-4 d-none d-md-flex" style="padding: 3px;">
<span class="library-item-path text-muted path">Path/to/the/file</span>
<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-tag tag-space badge">Tag</span>
</div>
</div>
</div>
</div>
</div>
</div>
<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/img/loading.svg" />
</div>
<div id="library-item-empty" style="display: none" class="list-group-item library-item">
<img style="margin: auto; width: 35px;" src="static/img/empty_box.svg" />
</div>
<div id="library-item" style="display: none;" class="list-group-item library-item">
<input hidden type="text" class="library-item-id" value="" />
<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">
<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"></path>
<path
d="m20.942859,18.221072l-3.323292,0l0,3.323292l-1.107764,0l0,-3.323292l-3.323292,0l0,-1.107764l3.323292,0l0,-3.323292l1.107764,0l0,3.323292l3.323292,0l0,1.107764z">
</path>
</svg>
</button>
<button class="library-item-add-bottom library-btn btn btn-info btn-sm btn-space" type="button"
title="Add to bottom">
<svg class="library-btn-svg" style="width: 1rem; fill: currentColor;" viewBox="2 2 20 20">
<path d="M2,16H10V14H2M18,14V10H16V14H12V16H16V20H18V16H22V14M14,6H2V8H14M14,10H2V12H14V10Z">
</path>
</svg>
</button>
<button class="library-item-download btn btn-primary btn-sm btn-space" type="button">
<i class="fas fa-download" aria-hidden="true"></i>
</button>
<button class="library-item-trash btn btn-danger btn-sm btn-space" type="button">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</div>
</div>
<div class="library-info-col library-info-title col-5" style="padding: 12px 0;"> <div class="library-info-col library-info-title col-5" style="padding: 12px 0;">
<div> <div>
<span class="library-item-type lead text-muted btn-space">[File]</span> <span class="library-item-type lead text-muted btn-space">[File]</span>
@ -298,31 +228,60 @@
<span class="library-item-path text-muted path">Path/to/the/file</span> <span class="library-item-path text-muted path">Path/to/the/file</span>
<div class="library-item-tags"> <div class="library-item-tags">
<a class="tag-space tag-click library-item-edit"><i class="fas fa-edit" style="color: #AAAAAA"></i></a> <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 <span class="library-item-notag badge badge-light text-muted font-italic">No tag</span>
tag</span>
<span class="library-item-tag tag-space badge">Tag</span> <span class="library-item-tag tag-space badge">Tag</span>
</div> </div>
</div> </div>
<div class="btn-group" role="group" class="mb-2"> <div class="btn-group library-action">
<button type="submit" class="btn btn-secondary mr-1" onclick="addAllResults()"><i class="fas fa-plus" <button class="library-item-add-next btn btn-info btn-sm btn-space" type="button" title="Next to play">
aria-hidden="true"></i> Add All <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"></path>
<path d="m20.942859,18.221072l-3.323292,0l0,3.323292l-1.107764,0l0,-3.323292l-3.323292,0l0,-1.107764l3.323292,0l0,-3.323292l1.107764,0l0,3.323292l3.323292,0l0,1.107764z"></path>
</svg>
</button> </button>
<button type="submit" class="btn btn-secondary mr-1" <button class="library-item-add-bottom library-btn btn btn-info btn-sm btn-space" type="button" title="Add to bottom">
onclick="request('post', {action : 'rescan'}); updateResults()"> <svg class="library-btn-svg" style="width: 1rem; fill: currentColor;" viewBox="2 2 20 20">
<path d="M2,16H10V14H2M18,14V10H16V14H12V16H16V20H18V16H22V14M14,6H2V8H14M14,10H2V12H14V10Z"></path>
</svg>
</button>
<button class="library-item-download btn btn-primary btn-sm btn-space" type="button">
<i class="fas fa-download" aria-hidden="true"></i>
</button>
<button class="library-item-trash btn btn-danger btn-sm btn-space" type="button">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</div>
</div>
<div class="list-group">
<div id="library-pagination" style="margin-left: auto; margin-top: 10px;">
<ul id="library-page-ul" class="pagination pagination">
<li class="library-page-li page-item active">
<a class="library-page-no page-link">1</a>
</li>
</ul>
</div>
</div>
<div class="btn-group mb-2" role="group">
<button type="submit" class="btn btn-secondary mr-1" onclick="addAllResults()"><i class="fas fa-plus" aria-hidden="true"></i> Add All
</button>
<button type="submit" class="btn btn-secondary mr-1" onclick="request('post', {action : 'rescan'}); updateResults()">
<i class="fas fa-sync-alt" aria-hidden="true"></i> Rescan Files <i class="fas fa-sync-alt" aria-hidden="true"></i> Rescan Files
</button> </button>
<button type="submit" class="btn btn-secondary mr-1" onclick="downloadAllResults()"><i class="fas fa-download" <button type="submit" class="btn btn-secondary mr-1" onclick="downloadAllResults()"><i class="fas fa-download" aria-hidden="true"></i> Download All
aria-hidden="true"></i> Download All
</button> </button>
<button type="button" class="btn btn-danger mr-1" data-toggle="modal" data-target="#deleteWarningModal"><i <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>
class="fas fa-trash-alt" aria-hidden="true"></i>
Delete All Delete All
</button> </button>
</div> </div>
<div class="modal fade" id="deleteWarningModal" tabindex="-1" role="dialog" aria-labelledby="Warning-Delete-File" <div class="modal fade" id="deleteWarningModal" tabindex="-1" role="dialog" aria-labelledby="Warning-Delete-File" aria-hidden="true">
aria-hidden="true">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
@ -338,13 +297,16 @@
</div> </div>
<div class="modal-footer"> <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">Close</button>
<button type="button" class="btn btn-danger" data-dismiss="modal" <button type="button" class="btn btn-danger" data-dismiss="modal" onclick="deleteAllResults()">Delete All Listed Files</button>
onclick="deleteAllResults()">Delete All Listed Files</button> </div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- beautify ignore:start -->
{% if upload_enabled %} {% if upload_enabled %}
<div id="upload" class="container mb-3"> <div id="upload" class="container mb-3">
{% else %} {% else %}
@ -362,9 +324,7 @@
<div class="input-group mb-3"> <div class="input-group mb-3">
<div id="uploadField" style="display: flex; width: 100%"> <div id="uploadField" style="display: flex; width: 100%">
<div class="custom-file"> <div class="custom-file">
<input type="file" name="file[]" class="custom-file-input" <input type="file" name="file[]" class="custom-file-input" id="uploadSelectFile" aria-describedby="uploadSubmit" value="Browse Music file" multiple />
id="uploadSelectFile" aria-describedby="uploadSubmit"
value="Browse Music file" multiple />
<label class="custom-file-label" for="uploadSelectFile">Choose file</label> <label class="custom-file-label" for="uploadSelectFile">Choose file</label>
</div> </div>
</div> </div>
@ -377,8 +337,7 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">Upload To</span> <span class="input-group-text">Upload To</span>
</div> </div>
<input class="form-control" list="targetdirs" id="uploadTargetDir" <input class="form-control" list="targetdirs" id="uploadTargetDir" name="targetdir" placeholder="uploads" />
name="targetdir" placeholder="uploads" />
<datalist id="targetdirs"> <datalist id="targetdirs">
{% for dir in dirs %} {% for dir in dirs %}
<option value="{{ dir }}"> <option value="{{ dir }}">
@ -387,8 +346,7 @@
</div> </div>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<button class="btn btn-primary" type="button" id="uploadSubmit"><i <button class="btn btn-primary" type="button" id="uploadSubmit"><i class="fas fa-upload mr-1"></i>Upload!</button>
class="fas fa-upload mr-1"></i>Upload!</button>
</div> </div>
</div> </div>
</div> </div>
@ -397,6 +355,7 @@
</div> </div>
</div> </div>
</div> </div>
<!-- beautify ignore:end -->
<div class="container mb-5"> <div class="container mb-5">
<div class="card-deck"> <div class="card-deck">
@ -409,8 +368,7 @@
<div class="input-group mb-2"> <div class="input-group mb-2">
<input class="form-control" type="text" id="add_url_input" placeholder="URL..."> <input class="form-control" type="text" id="add_url_input" placeholder="URL...">
</div> </div>
<button type="submit" class="btn btn-primary" <button type="submit" class="btn btn-primary" onclick="var $i = $('#add_url_input')[0]; request('post', {add_url : $i.value }); $i.value = ''; ">Add
onclick="var $i = $('#add_url_input')[0]; request('post', {add_url : $i.value }); $i.value = ''; ">Add
URL URL
</button> </button>
</div> </div>
@ -424,8 +382,7 @@
<div class="input-group mb-2"> <div class="input-group mb-2">
<input class="form-control" type="text" id="add_radio_input" placeholder="Radio Address..."> <input class="form-control" type="text" id="add_radio_input" placeholder="Radio Address...">
</div> </div>
<button type="submit" class="btn btn-primary" <button type="submit" class="btn btn-primary" onclick="var $i = $('#add_radio_input')[0]; request('post', {add_radio : $i.value }); $i.value = '';">Add
onclick="var $i = $('#add_radio_input')[0]; request('post', {add_radio : $i.value }); $i.value = '';">Add
Radio Radio
</button> </button>
</div> </div>
@ -433,12 +390,11 @@
</div> </div>
</div> </div>
<div class="floating-button" style="bottom: 120px;" onclick="togglePlayer()"> <div class="floating-button" style="bottom: 120px;" onclick="togglePlayer()">
<i class="fas fa-play" aria-hidden="true"></i> <i class="fas fa-play" aria-hidden="true"></i>
</div> </div>
<div id="theme-switch-btn" class="floating-button" style="bottom: 50px;"> <div class="floating-button" style="bottom: 50px;" onclick="switchTheme()">
<i class="fas fa-lightbulb" aria-hidden="true"></i> <i class="fas fa-lightbulb" aria-hidden="true"></i>
</div> </div>
@ -455,16 +411,13 @@
<img id="playerArtwork" src="static/img/unknown-album.png" style="display: none;" /> <img id="playerArtwork" src="static/img/unknown-album.png" style="display: none;" />
<div id="playerInfo"> <div id="playerInfo">
<div id="playerActionBox"> <div id="playerActionBox">
<button id="playerPlayBtn" class="btn btn-primary btn-sm btn-space" style="display: none" <button id="playerPlayBtn" class="btn btn-primary btn-sm btn-space" style="display: none" onclick="request('post', {action: 'resume'})">
onclick="request('post', {action: 'resume'})">
<i class="fas fa-play"></i> <i class="fas fa-play"></i>
</button> </button>
<button id="playerPauseBtn" class="btn btn-primary btn-sm btn-space" style="display: none" <button id="playerPauseBtn" class="btn btn-primary btn-sm btn-space" style="display: none" onclick="request('post', {action: 'pause'})">
onclick="request('post', {action: 'pause'})">
<i class="fas fa-pause"></i> <i class="fas fa-pause"></i>
</button> </button>
<button id="playerSkipBtn" class="btn btn-primary btn-sm" <button id="playerSkipBtn" class="btn btn-primary btn-sm" onclick="request('post', {action : 'next'})">
onclick="request('post', {action : 'next'})">
<i class="fas fa-fast-forward"></i> <i class="fas fa-fast-forward"></i>
</button> </button>
</div> </div>
@ -474,8 +427,7 @@
</div> </div>
<span id="playerArtist">Artist</span> <span id="playerArtist">Artist</span>
<div id="playerBarBox" class="progress"> <div id="playerBarBox" class="progress">
<div id="playerBar" class="progress-bar" role="progressbar" aria-valuenow="50" aria-valuemin="0" <div id="playerBar" class="progress-bar" role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100"></div>
aria-valuemax="100"></div>
</div> </div>
</div> </div>
</div> </div>
@ -505,26 +457,21 @@
<input hidden type="text" id="addTagModalItemId" name="id" value=""> <input hidden type="text" id="addTagModalItemId" name="id" value="">
<div class="modal-tag" style="display: none; width: 100%;"> <div class="modal-tag" style="display: none; width: 100%;">
<span class="modal-tag-text tag-space badge badge-pill badge-dark">Tag</span> <span class="modal-tag-text tag-space badge badge-pill badge-dark">Tag</span>
<a class="modal-tag-remove tag-click small"><i <a class="modal-tag-remove tag-click small"><i class="fas fa-times-circle btn-outline-danger"></i></a>
class="fas fa-times-circle btn-outline-danger"></i></a>
</div> </div>
<div id="addTagModalTags" style="margin-left: 5px; margin-bottom: 10px;"> <div id="addTagModalTags" style="margin-left: 5px; margin-bottom: 10px;">
</div> </div>
<div class="input-group"> <div class="input-group">
<input class="form-control form-control-sm btn-space" type="text" id="addTagModalInput" <input class="form-control form-control-sm btn-space" type="text" id="addTagModalInput" placeholder="tag1,tag2,...">
placeholder="tag1,tag2,..."> <button id="addTagModalAddBtn" type="button" class="btn btn-primary btn-sm" onclick="addTagModalAdd()">
<button id="addTagModalAddBtn" type="button" class="btn btn-primary btn-sm"
onclick="addTagModalAdd()">
<i class="fas fa-plus" aria-hidden="true"></i> <i class="fas fa-plus" aria-hidden="true"></i>
Add Add
</button> </button>
</div> </div>
</div> </div>
<div class="modal-footer"> <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">Close</button>
<button id="addTagModalSubmit" type="button" class="btn btn-success" data-dismiss="modal" <button id="addTagModalSubmit" type="button" class="btn btn-success" data-dismiss="modal" onclick="addTagModalSubmit()">Edit!</button>
onclick="addTagModalSubmit()">Edit!</button>
</div> </div>
</div> </div>
</div> </div>
@ -535,8 +482,7 @@
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="uploadTitle"><i class="fas fa-upload mr-1"></i>Uploading files... <h5 class="modal-title" id="uploadTitle"><i class="fas fa-upload mr-1"></i>Uploading files...</h5>
</h5>
</div> </div>
<div id="uploadModalBody" class="modal-body"> <div id="uploadModalBody" class="modal-body">
<div id="uploadSuccessAlert" class="alert alert-success" role="alert" style="display: none"> <div id="uploadSuccessAlert" class="alert alert-success" role="alert" style="display: none">
@ -549,8 +495,7 @@
<span class="uploadItemTitle mr-3"></span> <span class="uploadItemTitle mr-3"></span>
<span class="uploadItemError text-danger"></span> <span class="uploadItemError text-danger"></span>
<div class="progress" style="margin-top: 5px; height: 10px;"> <div class="progress" style="margin-top: 5px; height: 10px;">
<div class="uploadProgress progress-bar" role="progressbar" aria-valuenow="0" <div class="uploadProgress progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
aria-valuemin="0" aria-valuemax="100"></div>
</div> </div>
</div> </div>
</div> </div>
@ -558,9 +503,7 @@
<div class="modal-footer"> <div class="modal-footer">
<button type="button" id="uploadClose" class="btn btn-success" data-dismiss="modal"> <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> Close</button>
<button type="button" id="uploadCancel" class="btn btn-danger" data-toggle="tooltip" <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.">
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> <i class="fas fa-trash-alt mr-1" aria-hidden="true"></i> Cancel</button>
</div> </div>
</div> </div>

View File

@ -27,8 +27,7 @@
<div class="form-group mt-3"> <div class="form-group mt-3">
<label for="token_input">Token</label> <label for="token_input">Token</label>
<div class="input-group"> <div class="input-group">
<input type="password" class="form-control btn-space" id="token_input" name="token" <input type="password" class="form-control btn-space" id="token_input" name="token" placeholder="xxxxxxx">
placeholder="xxxxxxx">
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary">Submit</button>
</div> </div>
</div> </div>