refactor: rewrite confusing part

This commit is contained in:
Terry Geng 2020-04-08 08:55:43 +08:00
parent e1c5fc32e9
commit e86b5ca659
11 changed files with 163 additions and 147 deletions

View File

@ -227,7 +227,7 @@ def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=Fals
# if parameter is {index} # if parameter is {index}
if parameter.isdigit(): if parameter.isdigit():
music_wrappers = get_cached_wrappers_from_dicts(bot, var.music_db.query_music(Condition() music_wrappers = get_cached_wrappers_from_dicts(var.music_db.query_music(Condition()
.and_equal('type', 'file') .and_equal('type', 'file')
.order_by('path') .order_by('path')
.limit(1) .limit(1)
@ -240,7 +240,7 @@ def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=Fals
return return
# assume parameter is a path # assume parameter is a path
music_wrappers = get_cached_wrappers_from_dicts(bot, var.music_db.query_music(Condition().and_equal('path', parameter)), user) music_wrappers = get_cached_wrappers_from_dicts(var.music_db.query_music(Condition().and_equal('path', parameter)), user)
if music_wrappers: if music_wrappers:
var.playlist.append(music_wrappers[0]) var.playlist.append(music_wrappers[0])
log.info("cmd: add to playlist: " + music_wrappers[0].format_debug_string()) log.info("cmd: add to playlist: " + music_wrappers[0].format_debug_string())
@ -248,7 +248,7 @@ def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=Fals
return return
# assume parameter is a folder # assume parameter is a folder
music_wrappers = get_cached_wrappers_from_dicts(bot, var.music_db.query_music(Condition() music_wrappers = get_cached_wrappers_from_dicts(var.music_db.query_music(Condition()
.and_equal('type', 'file') .and_equal('type', 'file')
.and_like('path', parameter + '%')), user) .and_like('path', parameter + '%')), user)
if music_wrappers: if music_wrappers:
@ -268,7 +268,7 @@ def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=Fals
.and_equal('type', 'file') .and_equal('type', 'file')
.and_like('path', '%' + parameter + '%', case_sensitive=False)) .and_like('path', '%' + parameter + '%', case_sensitive=False))
if len(matches) == 1: if len(matches) == 1:
music_wrapper = get_cached_wrapper_from_dict(bot, matches[0], user) music_wrapper = get_cached_wrapper_from_dict(matches[0], user)
var.playlist.append(music_wrapper) var.playlist.append(music_wrapper)
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string()) 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(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
@ -286,7 +286,7 @@ def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=Fals
if do_not_refresh_cache: if do_not_refresh_cache:
bot.send_msg(constants.strings("no_file"), text) bot.send_msg(constants.strings("no_file"), text)
else: else:
var.cache.build_dir_cache(bot) var.cache.build_dir_cache()
cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=True) cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=True)
@ -304,7 +304,7 @@ def cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cach
match = re.search(parameter, file) match = re.search(parameter, file)
if match and match[0]: if match and match[0]:
count += 1 count += 1
music_wrapper = get_cached_wrapper(dict_to_item(bot, file_dict), user) music_wrapper = get_cached_wrapper(dict_to_item(file_dict), user)
music_wrappers.append(music_wrapper) music_wrappers.append(music_wrapper)
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string()) log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
msgs.append("<li><b>{}</b> ({})</li>".format(music_wrapper.item().title, msgs.append("<li><b>{}</b> ({})</li>".format(music_wrapper.item().title,
@ -323,7 +323,7 @@ def cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cach
if do_not_refresh_cache: if do_not_refresh_cache:
bot.send_msg(constants.strings("no_file"), text) bot.send_msg(constants.strings("no_file"), text)
else: else:
var.cache.build_dir_cache(bot) var.cache.build_dir_cache()
cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cache=True) cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cache=True)
except re.error as e: except re.error as e:
@ -338,7 +338,7 @@ def cmd_play_url(bot, user, text, command, parameter):
url = util.get_url_from_input(parameter) url = util.get_url_from_input(parameter)
if url: if url:
music_wrapper = get_cached_wrapper_from_scrap(bot, type='url', url=url, user=user) music_wrapper = get_cached_wrapper_from_scrap(type='url', url=url, user=user)
var.playlist.append(music_wrapper) var.playlist.append(music_wrapper)
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string()) log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
@ -364,7 +364,7 @@ def cmd_play_playlist(bot, user, text, command, parameter):
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(bot, **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:
@ -389,7 +389,7 @@ def cmd_play_radio(bot, user, text, command, parameter):
parameter = parameter.split()[0] parameter = parameter.split()[0]
url = util.get_url_from_input(parameter) url = util.get_url_from_input(parameter)
if url: if url:
music_wrapper = get_cached_wrapper_from_scrap(bot, type='radio', url=url, user=user) music_wrapper = get_cached_wrapper_from_scrap(type='radio', url=url, user=user)
var.playlist.append(music_wrapper) var.playlist.append(music_wrapper)
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string()) log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
@ -489,7 +489,7 @@ def cmd_rb_play(bot, user, text, command, parameter):
url = radiobrowser.geturl_byid(parameter) 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(bot, type='radio', url=url, name=stationname, user=user) music_wrapper = get_cached_wrapper_from_scrap(type='radio', url=url, name=stationname, user=user)
var.playlist.append(music_wrapper) var.playlist.append(music_wrapper)
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string()) log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
bot.async_download_next() bot.async_download_next()
@ -870,7 +870,7 @@ def cmd_play_tags(bot, user, text, command, parameter):
tags = parameter.split(",") tags = parameter.split(",")
tags = list(map(lambda t: t.strip(), tags)) tags = list(map(lambda t: t.strip(), tags))
music_wrappers = get_cached_wrappers_by_tags(bot, tags, user) music_wrappers = get_cached_wrappers_by_tags(tags, user)
for music_wrapper in music_wrappers: for music_wrapper in music_wrappers:
count += 1 count += 1
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string()) log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
@ -991,7 +991,7 @@ def cmd_find_tagged(bot, user, text, command, parameter):
song_shortlist = music_dicts song_shortlist = music_dicts
for i, music_dict in enumerate(music_dicts): for i, music_dict in enumerate(music_dicts):
item = dict_to_item(bot, music_dict) item = dict_to_item(music_dict)
count += 1 count += 1
if count > ITEMS_PER_PAGE: if count > ITEMS_PER_PAGE:
break break
@ -1024,7 +1024,7 @@ def cmd_search_library(bot, user, text, command, parameter):
music_dicts = var.music_db.query_music_by_keywords(keywords) music_dicts = var.music_db.query_music_by_keywords(keywords)
if music_dicts: if music_dicts:
items = dicts_to_items(bot, music_dicts) items = dicts_to_items(music_dicts)
song_shortlist = music_dicts song_shortlist = music_dicts
if len(items) == 1: if len(items) == 1:
@ -1061,7 +1061,7 @@ def cmd_shortlist(bot, user, text, command, parameter):
music_wrappers = [] music_wrappers = []
for kwargs in song_shortlist: for kwargs in song_shortlist:
kwargs['user'] = user kwargs['user'] = user
music_wrapper = get_cached_wrapper_from_scrap(bot, **kwargs) music_wrapper = get_cached_wrapper_from_scrap(**kwargs)
music_wrappers.append(music_wrapper) music_wrappers.append(music_wrapper)
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string()) log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
msgs.append("<li>[{}] <b>{}</b></li>".format(music_wrapper.item().type, music_wrapper.item().title)) msgs.append("<li>[{}] <b>{}</b></li>".format(music_wrapper.item().type, music_wrapper.item().title))
@ -1085,7 +1085,7 @@ def cmd_shortlist(bot, user, text, command, parameter):
if 1 <= index <= len(song_shortlist): if 1 <= index <= len(song_shortlist):
kwargs = song_shortlist[index - 1] kwargs = song_shortlist[index - 1]
kwargs['user'] = user kwargs['user'] = user
music_wrapper = get_cached_wrapper_from_scrap(bot, **kwargs) music_wrapper = get_cached_wrapper_from_scrap(**kwargs)
music_wrappers.append(music_wrapper) music_wrappers.append(music_wrapper)
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string()) log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
msgs.append("<li>[{}] <b>{}</b></li>".format(music_wrapper.item().type, music_wrapper.item().title)) msgs.append("<li>[{}] <b>{}</b></li>".format(music_wrapper.item().type, music_wrapper.item().title))
@ -1104,7 +1104,7 @@ def cmd_shortlist(bot, user, text, command, parameter):
if 1 <= index <= len(song_shortlist): if 1 <= index <= len(song_shortlist):
kwargs = song_shortlist[index - 1] kwargs = song_shortlist[index - 1]
kwargs['user'] = user kwargs['user'] = user
music_wrapper = get_cached_wrapper_from_scrap(bot, **kwargs) music_wrapper = get_cached_wrapper_from_scrap(**kwargs)
var.playlist.append(music_wrapper) var.playlist.append(music_wrapper)
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string()) 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(constants.strings('file_added', item=music_wrapper.format_song_string()))
@ -1128,7 +1128,7 @@ def cmd_delete_from_library(bot, user, text, command, parameter):
if 1 <= index <= len(song_shortlist): if 1 <= index <= len(song_shortlist):
music_dict = song_shortlist[index - 1] music_dict = song_shortlist[index - 1]
if 'id' in music_dict: if 'id' in music_dict:
music_wrapper = get_cached_wrapper_by_id(bot, music_dict['id'], user) music_wrapper = get_cached_wrapper_by_id(music_dict['id'], user)
log.info("cmd: remove from library: " + music_wrapper.format_debug_string()) log.info("cmd: remove from library: " + music_wrapper.format_debug_string())
msgs.append("<li>[{}] <b>{}</b></li>".format(music_wrapper.item().type, music_wrapper.item().title)) msgs.append("<li>[{}] <b>{}</b></li>".format(music_wrapper.item().type, music_wrapper.item().title))
var.playlist.remove_by_id(music_dict['id']) var.playlist.remove_by_id(music_dict['id'])
@ -1150,7 +1150,7 @@ def cmd_delete_from_library(bot, user, text, command, parameter):
if 1 <= index <= len(song_shortlist): if 1 <= index <= len(song_shortlist):
music_dict = song_shortlist[index - 1] music_dict = song_shortlist[index - 1]
if 'id' in music_dict: if 'id' in music_dict:
music_wrapper = get_cached_wrapper_by_id(bot, music_dict['id'], user) 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(constants.strings('file_deleted', item=music_wrapper.format_song_string()), text)
log.info("cmd: remove from library: " + music_wrapper.format_debug_string()) log.info("cmd: remove from library: " + music_wrapper.format_debug_string())
var.playlist.remove_by_id(music_dict['id']) var.playlist.remove_by_id(music_dict['id'])
@ -1177,7 +1177,7 @@ def cmd_drop_database(bot, user, text, command, parameter):
def cmd_refresh_cache(bot, user, text, command, parameter): def cmd_refresh_cache(bot, user, text, command, parameter):
global log global log
if bot.is_admin(user): if bot.is_admin(user):
var.cache.build_dir_cache(bot) var.cache.build_dir_cache()
log.info("command: Local file cache refreshed.") log.info("command: Local file cache refreshed.")
bot.send_msg(constants.strings('cache_refreshed'), text) bot.send_msg(constants.strings('cache_refreshed'), text)
else: else:

View File

@ -204,8 +204,8 @@ page_instruction = Page {current}/{total}. Use <i>!{command} {{page}}</i> to nav
records_omitted = ... records_omitted = ...
bad_url = Bad URL requested. bad_url = Bad URL requested.
preconfigurated_radio = Preconfigurated Radio available: preconfigurated_radio = Preconfigurated Radio available:
unable_download = Error while downloading music... unable_download = Unable to download <b>{item}</b>. Removed from the library.
unable_play = Unable to play {item}. Removed from the library. unable_play = Unable to play <b>{item}</b>. Removed from the library.
which_command = Do you mean <br /> {commands} which_command = Do you mean <br /> {commands}
multiple_matches = File not found! Possible candidates: multiple_matches = File not found! Possible candidates:
queue_contents = Items on the playlist: queue_contents = Items on the playlist:

View File

@ -253,7 +253,7 @@ def post():
log.debug("web: Post request from %s: %s" % (request.remote_addr, str(request.form))) log.debug("web: Post request from %s: %s" % (request.remote_addr, str(request.form)))
if 'add_item_at_once' in request.form: if 'add_item_at_once' in request.form:
music_wrapper = get_cached_wrapper_by_id(var.bot, request.form['add_item_at_once'], user) music_wrapper = get_cached_wrapper_by_id(request.form['add_item_at_once'], user)
if music_wrapper: if music_wrapper:
var.playlist.insert(var.playlist.current_index + 1, music_wrapper) var.playlist.insert(var.playlist.current_index + 1, music_wrapper)
log.info('web: add to playlist(next): ' + music_wrapper.format_debug_string()) log.info('web: add to playlist(next): ' + music_wrapper.format_debug_string())
@ -265,7 +265,7 @@ def post():
abort(404) abort(404)
if 'add_item_bottom' in request.form: if 'add_item_bottom' in request.form:
music_wrapper = get_cached_wrapper_by_id(var.bot, request.form['add_item_bottom'], user) music_wrapper = get_cached_wrapper_by_id(request.form['add_item_bottom'], user)
if music_wrapper: if music_wrapper:
var.playlist.append(music_wrapper) var.playlist.append(music_wrapper)
@ -274,7 +274,7 @@ def post():
abort(404) abort(404)
elif 'add_item_next' in request.form: elif 'add_item_next' in request.form:
music_wrapper = get_cached_wrapper_by_id(var.bot, request.form['add_item_next'], user) music_wrapper = get_cached_wrapper_by_id(request.form['add_item_next'], user)
if music_wrapper: if music_wrapper:
var.playlist.insert(var.playlist.current_index + 1, music_wrapper) var.playlist.insert(var.playlist.current_index + 1, music_wrapper)
log.info('web: add to playlist(next): ' + music_wrapper.format_debug_string()) log.info('web: add to playlist(next): ' + music_wrapper.format_debug_string())
@ -282,7 +282,7 @@ def post():
abort(404) abort(404)
elif 'add_url' in request.form: elif 'add_url' in request.form:
music_wrapper = get_cached_wrapper_from_scrap(var.bot, type='url', url=request.form['add_url'], user=user) music_wrapper = get_cached_wrapper_from_scrap(type='url', url=request.form['add_url'], user=user)
var.playlist.append(music_wrapper) var.playlist.append(music_wrapper)
log.info("web: add to playlist: " + music_wrapper.format_debug_string()) log.info("web: add to playlist: " + music_wrapper.format_debug_string())
@ -292,7 +292,7 @@ def post():
elif 'add_radio' in request.form: elif 'add_radio' in request.form:
url = request.form['add_radio'] url = request.form['add_radio']
music_wrapper = get_cached_wrapper_from_scrap(var.bot, type='radio', url=url, user=user) music_wrapper = get_cached_wrapper_from_scrap(type='radio', url=url, user=user)
var.playlist.append(music_wrapper) var.playlist.append(music_wrapper)
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string()) log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
@ -335,7 +335,7 @@ def post():
elif 'delete_item_from_library' in request.form: elif 'delete_item_from_library' in request.form:
_id = request.form['delete_item_from_library'] _id = request.form['delete_item_from_library']
var.playlist.remove_by_id(_id) var.playlist.remove_by_id(_id)
item = var.cache.get_item_by_id(var.bot, _id) item = var.cache.get_item_by_id(_id)
if os.path.isfile(item.uri()): if os.path.isfile(item.uri()):
log.info("web: delete file " + item.uri()) log.info("web: delete file " + item.uri())
@ -345,7 +345,7 @@ def post():
time.sleep(0.1) time.sleep(0.1)
elif 'add_tag' in request.form: elif 'add_tag' in request.form:
music_wrappers = get_cached_wrappers_by_tags(var.bot, [request.form['add_tag']], user) music_wrappers = get_cached_wrappers_by_tags([request.form['add_tag']], user)
for music_wrapper in music_wrappers: for music_wrapper in music_wrappers:
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string()) log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
var.playlist.extend(music_wrappers) var.playlist.extend(music_wrappers)
@ -373,7 +373,7 @@ def post():
var.db.set('playlist', 'playback_mode', "autoplay") var.db.set('playlist', 'playback_mode', "autoplay")
log.info("web: playback mode changed to autoplay.") log.info("web: playback mode changed to autoplay.")
if action == "rescan": if action == "rescan":
var.cache.build_dir_cache(var.bot) var.cache.build_dir_cache()
log.info("web: Local file cache refreshed.") log.info("web: Local file cache refreshed.")
elif action == "stop": elif action == "stop":
if var.config.getboolean("bot", "clear_when_stop_in_oneshot", fallback=False) \ if var.config.getboolean("bot", "clear_when_stop_in_oneshot", fallback=False) \
@ -462,7 +462,7 @@ def library():
return ('', 204) return ('', 204)
if request.form['action'] == 'add': if request.form['action'] == 'add':
items = dicts_to_items(var.bot, var.music_db.query_music(condition)) items = dicts_to_items(var.music_db.query_music(condition))
music_wrappers = [] music_wrappers = []
for item in items: for item in items:
music_wrapper = get_cached_wrapper(item, user) music_wrapper = get_cached_wrapper(item, user)
@ -474,10 +474,10 @@ def library():
return redirect("./", code=302) return redirect("./", code=302)
elif request.form['action'] == 'delete': elif request.form['action'] == 'delete':
items = dicts_to_items(var.bot, var.music_db.query_music(condition)) items = dicts_to_items(var.music_db.query_music(condition))
for item in items: for item in items:
var.playlist.remove_by_id(item.id) var.playlist.remove_by_id(item.id)
item = var.cache.get_item_by_id(var.bot, item.id) item = var.cache.get_item_by_id(item.id)
if os.path.isfile(item.uri()): if os.path.isfile(item.uri()):
log.info("web: delete file " + item.uri()) log.info("web: delete file " + item.uri())
@ -500,7 +500,7 @@ def library():
current_page = 1 current_page = 1
condition.limit(ITEM_PER_PAGE) condition.limit(ITEM_PER_PAGE)
items = dicts_to_items(var.bot, var.music_db.query_music(condition)) items = dicts_to_items(var.music_db.query_music(condition))
results = [] results = []
for item in items: for item in items:
@ -531,7 +531,7 @@ def library():
elif request.form['action'] == 'edit_tags': elif request.form['action'] == 'edit_tags':
tags = list(dict.fromkeys(request.form['tags'].split(","))) # remove duplicated items tags = list(dict.fromkeys(request.form['tags'].split(","))) # remove duplicated items
if request.form['id'] in var.cache: if request.form['id'] in var.cache:
music_wrapper = get_cached_wrapper_by_id(var.bot, request.form['id'], user) music_wrapper = get_cached_wrapper_by_id(request.form['id'], user)
music_wrapper.clear_tags() music_wrapper.clear_tags()
music_wrapper.add_tags(tags) music_wrapper.add_tags(tags)
var.playlist.version += 1 var.playlist.version += 1
@ -591,7 +591,7 @@ def upload():
else: else:
continue continue
var.cache.build_dir_cache(var.bot) var.cache.build_dir_cache()
var.music_db.manage_special_tags() var.music_db.manage_special_tags()
log.info("web: Local file cache refreshed.") log.info("web: Local file cache refreshed.")
@ -603,8 +603,7 @@ def download():
global log global log
if 'id' in request.args and request.args['id']: if 'id' in request.args and request.args['id']:
item = dicts_to_items(var.bot, item = dicts_to_items(var.music_db.query_music(
var.music_db.query_music(
Condition().and_equal('id', request.args['id'])))[0] Condition().and_equal('id', request.args['id'])))[0]
requested_file = item.uri() requested_file = item.uri()
@ -618,7 +617,7 @@ def download():
else: else:
condition = build_library_query_condition(request.args) condition = build_library_query_condition(request.args)
items = dicts_to_items(var.bot, var.music_db.query_music(condition)) items = dicts_to_items(var.music_db.query_music(condition))
zipfile = util.zipdir([item.uri() for item in items]) zipfile = util.zipdir([item.uri() for item in items])

View File

@ -21,12 +21,12 @@ class MusicCache(dict):
self.log = logging.getLogger("bot") self.log = logging.getLogger("bot")
self.dir_lock = threading.Lock() self.dir_lock = threading.Lock()
def get_item_by_id(self, bot, id): # Why all these functions need a bot? Because it need the bot to send message! def get_item_by_id(self, id):
if id in self: if id in self:
return self[id] return self[id]
# if not cached, query the database # if not cached, query the database
item = self.fetch(bot, id) item = self.fetch(id)
if item is not None: if item is not None:
self[id] = item self[id] = item
self.log.debug("library: music found in database: %s" % item.format_debug_string()) self.log.debug("library: music found in database: %s" % item.format_debug_string())
@ -36,7 +36,7 @@ class MusicCache(dict):
# print(id) # print(id)
# raise KeyError("Unable to fetch item from the database! Please try to refresh the cache by !recache.") # raise KeyError("Unable to fetch item from the database! Please try to refresh the cache by !recache.")
def get_item(self, bot, **kwargs): def get_item(self, **kwargs):
# kwargs should provide type and id, and parameters to build the item if not existed in the library. # kwargs should provide type and id, and parameters to build the item if not existed in the library.
# if cached # if cached
if 'id' in kwargs: if 'id' in kwargs:
@ -48,31 +48,31 @@ class MusicCache(dict):
return self[id] return self[id]
# if not cached, query the database # if not cached, query the database
item = self.fetch(bot, id) item = self.fetch(id)
if item is not None: if item is not None:
self[id] = item self[id] = item
self.log.debug("library: music found in database: %s" % item.format_debug_string()) self.log.debug("library: music found in database: %s" % item.format_debug_string())
return item return item
# if not in the database, build one # if not in the database, build one
self[id] = item_builders[kwargs['type']](bot, **kwargs) # newly built item will not be saved immediately self[id] = item_builders[kwargs['type']](**kwargs) # newly built item will not be saved immediately
return self[id] return self[id]
def get_items_by_tags(self, bot, tags): def get_items_by_tags(self, tags):
music_dicts = self.db.query_music_by_tags(tags) music_dicts = self.db.query_music_by_tags(tags)
items = [] items = []
if music_dicts: if music_dicts:
for music_dict in music_dicts: for music_dict in music_dicts:
id = music_dict['id'] id = music_dict['id']
self[id] = dict_to_item(bot, music_dict) self[id] = dict_to_item(music_dict)
items.append(self[id]) items.append(self[id])
return items return items
def fetch(self, bot, id): def fetch(self, id):
music_dict = self.db.query_music_by_id(id) music_dict = self.db.query_music_by_id(id)
if music_dict: if music_dict:
self[id] = dict_to_item(bot, music_dict) self[id] = dict_to_item(music_dict)
return self[id] return self[id]
else: else:
return None return None
@ -83,7 +83,7 @@ class MusicCache(dict):
self.db.manage_special_tags() self.db.manage_special_tags()
def free_and_delete(self, id): def free_and_delete(self, id):
item = self.get_item_by_id(None, id) item = self.get_item_by_id(id)
if item: if item:
self.log.debug("library: DELETE item from the database: %s" % item.format_debug_string()) self.log.debug("library: DELETE item from the database: %s" % item.format_debug_string())
@ -104,14 +104,14 @@ class MusicCache(dict):
self.log.debug("library: all cache freed") self.log.debug("library: all cache freed")
self.clear() self.clear()
def build_dir_cache(self, bot): def build_dir_cache(self):
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)
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:
item = item_builders['file'](bot, path=file) item = item_builders['file'](path=file)
self.log.debug("library: music save into database: %s" % item.format_debug_string()) self.log.debug("library: music save into database: %s" % item.format_debug_string())
self.db.insert_music(item.to_dict()) self.db.insert_music(item.to_dict())
@ -207,33 +207,33 @@ def get_cached_wrappers(items, user):
return wrappers return wrappers
def get_cached_wrapper_from_scrap(bot, **kwargs): def get_cached_wrapper_from_scrap(**kwargs):
item = var.cache.get_item(bot, **kwargs) item = var.cache.get_item(**kwargs)
if 'user' not in kwargs: if 'user' not in kwargs:
raise KeyError("Which user added this song?") raise KeyError("Which user added this song?")
return CachedItemWrapper(var.cache, item.id, kwargs['type'], kwargs['user']) return CachedItemWrapper(var.cache, item.id, kwargs['type'], kwargs['user'])
def get_cached_wrapper_from_dict(bot, dict_from_db, user): def get_cached_wrapper_from_dict(dict_from_db, user):
if dict_from_db: if dict_from_db:
item = dict_to_item(bot, dict_from_db) item = dict_to_item(dict_from_db)
return get_cached_wrapper(item, user) return get_cached_wrapper(item, user)
return None return None
def get_cached_wrappers_from_dicts(bot, dicts_from_db, user): def get_cached_wrappers_from_dicts(dicts_from_db, user):
items = [] items = []
for dict_from_db in dicts_from_db: for dict_from_db in dicts_from_db:
if dict_from_db: if dict_from_db:
items.append(get_cached_wrapper_from_dict(bot, dict_from_db, user)) items.append(get_cached_wrapper_from_dict(dict_from_db, user))
return items return items
def get_cached_wrapper_by_id(bot, id, user): def get_cached_wrapper_by_id(id, user):
item = var.cache.get_item_by_id(bot, id) item = var.cache.get_item_by_id(id)
if item: if item:
return CachedItemWrapper(var.cache, item.id, item.type, user) return CachedItemWrapper(var.cache, item.id, item.type, user)
def get_cached_wrappers_by_tags(bot, tags, user): def get_cached_wrappers_by_tags(tags, user):
items = var.cache.get_items_by_tags(bot, tags) items = var.cache.get_items_by_tags(tags)
ret = [] ret = []
for item in items: for item in items:
ret.append(CachedItemWrapper(var.cache, item.id, item.type, user)) ret.append(CachedItemWrapper(var.cache, item.id, item.type, user))

View File

@ -7,7 +7,7 @@ import mutagen
from PIL import Image from PIL import Image
import variables as var import variables as var
from media.item import BaseItem, item_builders, item_loaders, item_id_generators from media.item import BaseItem, item_builders, item_loaders, item_id_generators, ValidationFailedError
import constants import constants
''' '''
@ -22,12 +22,12 @@ type : file
''' '''
def file_item_builder(bot, **kwargs): def file_item_builder(**kwargs):
return FileItem(bot, kwargs['path']) return FileItem(kwargs['path'])
def file_item_loader(bot, _dict): def file_item_loader(_dict):
return FileItem(bot, "", _dict) return FileItem("", _dict)
def file_item_id_generator(**kwargs): def file_item_id_generator(**kwargs):
@ -40,9 +40,9 @@ item_id_generators['file'] = file_item_id_generator
class FileItem(BaseItem): class FileItem(BaseItem):
def __init__(self, bot, path, from_dict=None): def __init__(self, path, from_dict=None):
if not from_dict: if not from_dict:
super().__init__(bot) super().__init__()
self.path = path self.path = path
self.title = "" self.title = ""
self.artist = "" self.artist = ""
@ -53,7 +53,7 @@ class FileItem(BaseItem):
self.ready = "yes" self.ready = "yes"
self.keywords = self.title + " " + self.artist self.keywords = self.title + " " + self.artist
else: else:
super().__init__(bot, from_dict) super().__init__(from_dict)
self.artist = from_dict['artist'] self.artist = from_dict['artist']
self.thumbnail = from_dict['thumbnail'] self.thumbnail = from_dict['thumbnail']
if not self.validate(): if not self.validate():
@ -71,8 +71,7 @@ class FileItem(BaseItem):
if not os.path.exists(self.uri()): if not os.path.exists(self.uri()):
self.log.info( self.log.info(
"file: music file missed for %s" % self.format_debug_string()) "file: music file missed for %s" % self.format_debug_string())
self.send_client_message(constants.strings('file_missed', file=self.path)) raise ValidationFailedError(constants.strings('file_missed', file=self.path))
return False
if not self.keywords: if not self.keywords:
self.keywords = self.title + " " + self.artist # migrate from previous version self.keywords = self.title + " " + self.artist # migrate from previous version

View File

@ -5,12 +5,12 @@ item_loaders = {}
item_id_generators = {} item_id_generators = {}
def example_builder(bot, **kwargs): def example_builder(**kwargs):
return BaseItem(bot) return BaseItem()
def example_loader(bot, _dict): def example_loader(_dict):
return BaseItem(bot, from_dict=_dict) return BaseItem(from_dict=_dict)
def example_id_generator(**kwargs): def example_id_generator(**kwargs):
@ -22,22 +22,28 @@ item_loaders['base'] = example_loader
item_id_generators['base'] = example_id_generator item_id_generators['base'] = example_id_generator
def dicts_to_items(bot, music_dicts): def dicts_to_items(music_dicts):
items = [] items = []
for music_dict in music_dicts: for music_dict in music_dicts:
type = music_dict['type'] type = music_dict['type']
items.append(item_loaders[type](bot, music_dict)) items.append(item_loaders[type](music_dict))
return items return items
def dict_to_item(bot, music_dict): def dict_to_item(music_dict):
type = music_dict['type'] type = music_dict['type']
return item_loaders[type](bot, music_dict) return item_loaders[type](music_dict)
class ValidationFailedError(Exception):
def __init__(self, msg = None):
self.msg = msg
class PreparationFailedError(Exception):
def __init__(self, msg = None):
self.msg = msg
class BaseItem: class BaseItem:
def __init__(self, bot, from_dict=None): def __init__(self, from_dict=None):
self.bot = bot
self.log = logging.getLogger("bot") self.log = logging.getLogger("bot")
self.type = "base" self.type = "base"
self.title = "" self.title = ""
@ -64,7 +70,7 @@ class BaseItem:
return True if self.ready == "failed" else False return True if self.ready == "failed" else False
def validate(self): def validate(self):
return False raise ValidationFailedError(None)
def uri(self): def uri(self):
raise raise
@ -104,10 +110,6 @@ class BaseItem:
def display_type(self): def display_type(self):
return "" return ""
def send_client_message(self, msg):
if self.bot:
self.bot.send_channel_msg(msg) # TODO: this is way too ugly. It mixed up bot with items. Change it into exceptions in the future.
def to_dict(self): def to_dict(self):
return {"type": self.type, return {"type": self.type,
"id": self.id, "id": self.id,

View File

@ -7,6 +7,7 @@ import time
import variables as var import variables as var
from media.cache import CachedItemWrapper, get_cached_wrapper_from_dict, get_cached_wrapper_by_id from media.cache import CachedItemWrapper, get_cached_wrapper_from_dict, get_cached_wrapper_by_id
from database import Condition from database import Condition
from media.item import ValidationFailedError, PreparationFailedError
def get_playlist(mode, _list=None, index=None): def get_playlist(mode, _list=None, index=None):
@ -196,7 +197,7 @@ class BasePlaylist(list):
items.sort(key=lambda v: int(v[0])) items.sort(key=lambda v: int(v[0]))
for item in items: for item in items:
item = json.loads(item[1]) item = json.loads(item[1])
music_wrapper = get_cached_wrapper_by_id(var.bot, item['id'], item['user']) music_wrapper = get_cached_wrapper_by_id(item['id'], item['user'])
if music_wrapper: if music_wrapper:
music_wrappers.append(music_wrapper) music_wrappers.append(music_wrapper)
self.from_list(music_wrappers, current_index) self.from_list(music_wrappers, current_index)
@ -224,33 +225,23 @@ class BasePlaylist(list):
item = self.pending_items.pop() item = self.pending_items.pop()
self.log.debug("playlist: validating %s" % item.format_debug_string()) self.log.debug("playlist: validating %s" % item.format_debug_string())
ver = item.version ver = item.version
if not item.validate() or item.is_failed():
try:
item.validate()
except ValidationFailedError as e:
self.log.debug("playlist: validating failed.") self.log.debug("playlist: validating failed.")
if var.bot:
var.bot.send_channel_msg(e.msg)
var.cache.free_and_delete(item.id) var.cache.free_and_delete(item.id)
self.remove_by_id(item.id) self.remove_by_id(item.id)
continue continue
if item.version > ver: if item.version > ver:
self.version += 1 self.version += 1
self.log.debug("playlist: validating finished.") self.log.debug("playlist: validating finished.")
self.validating_thread_lock.release() self.validating_thread_lock.release()
def async_prepare(self, index):
th = threading.Thread(
target=self._prepare, name="Prepare-" + self[index].id[:7], args=(index,))
self.log.info(
"%s: start preparing item in thread: " % self[index].item().type + self[index].format_debug_string())
th.daemon = True
th.start()
return th
def _prepare(self, index):
item = self[index]
ver = item.version
item.prepare()
if item.version > ver:
self.version += 1
class OneshotPlaylist(BasePlaylist): class OneshotPlaylist(BasePlaylist):
def __init__(self): def __init__(self):
@ -363,7 +354,7 @@ class AutoPlaylist(OneshotPlaylist):
Condition().and_like('tags', "%don't autoplay,%"))) Condition().and_like('tags', "%don't autoplay,%")))
if dicts: if dicts:
_list = [get_cached_wrapper_from_dict(var.bot, _dict, "AutoPlay") for _dict in dicts] _list = [get_cached_wrapper_from_dict(_dict, "AutoPlay") for _dict in dicts]
self.from_list(_list, -1) self.from_list(_list, -1)
# def from_list(self, _list, current_index): # def from_list(self, _list, current_index):

View File

@ -86,15 +86,15 @@ def get_radio_title(url):
return url return url
def radio_item_builder(bot, **kwargs): def radio_item_builder(**kwargs):
if 'name' in kwargs: if 'name' in kwargs:
return RadioItem(bot, kwargs['url'], kwargs['name']) return RadioItem(kwargs['url'], kwargs['name'])
else: else:
return RadioItem(bot, kwargs['url'], '') return RadioItem(kwargs['url'], '')
def radio_item_loader(bot, _dict): def radio_item_loader(_dict):
return RadioItem(bot, "", "", _dict) return RadioItem("", "", _dict)
def radio_item_id_generator(**kwargs): def radio_item_id_generator(**kwargs):
@ -107,9 +107,9 @@ item_id_generators['radio'] = radio_item_id_generator
class RadioItem(BaseItem): class RadioItem(BaseItem):
def __init__(self, bot, url, name="", from_dict=None): def __init__(self, url, name="", from_dict=None):
if from_dict is None: if from_dict is None:
super().__init__(bot) super().__init__()
self.url = url self.url = url
if not name: if not name:
self.title = get_radio_server_description(self.url) # The title of the radio station self.title = get_radio_server_description(self.url) # The title of the radio station
@ -117,7 +117,7 @@ class RadioItem(BaseItem):
self.title = name self.title = name
self.id = hashlib.md5(url.encode()).hexdigest() self.id = hashlib.md5(url.encode()).hexdigest()
else: else:
super().__init__(bot, from_dict) super().__init__(from_dict)
self.url = from_dict['url'] self.url = from_dict['url']
self.title = from_dict['title'] self.title = from_dict['title']

View File

@ -12,18 +12,19 @@ import base64
import constants import constants
import media import media
import variables as var import variables as var
from media.item import BaseItem, item_builders, item_loaders, item_id_generators from media.item import BaseItem, item_builders, item_loaders, item_id_generators, ValidationFailedError, \
PreparationFailedError
import media.system import media.system
log = logging.getLogger("bot") log = logging.getLogger("bot")
def url_item_builder(bot, **kwargs): def url_item_builder(**kwargs):
return URLItem(bot, kwargs['url']) return URLItem(kwargs['url'])
def url_item_loader(bot, _dict): def url_item_loader(_dict):
return URLItem(bot, "", _dict) return URLItem("", _dict)
def url_item_id_generator(**kwargs): def url_item_id_generator(**kwargs):
@ -36,10 +37,10 @@ item_id_generators['url'] = url_item_id_generator
class URLItem(BaseItem): class URLItem(BaseItem):
def __init__(self, bot, url, from_dict=None): def __init__(self, url, from_dict=None):
self.validating_lock = threading.Lock() self.validating_lock = threading.Lock()
if from_dict is None: if from_dict is None:
super().__init__(bot) super().__init__()
self.url = url if url[-1] != "/" else url[:-1] self.url = url if url[-1] != "/" else url[:-1]
self.title = '' self.title = ''
self.duration = 0 self.duration = 0
@ -47,7 +48,7 @@ class URLItem(BaseItem):
self.path = var.tmp_folder + self.id + ".mp3" self.path = var.tmp_folder + self.id + ".mp3"
self.thumbnail = '' self.thumbnail = ''
else: else:
super().__init__(bot, from_dict) super().__init__(from_dict)
self.url = from_dict['url'] self.url = from_dict['url']
self.duration = from_dict['duration'] self.duration = from_dict['duration']
self.path = from_dict['path'] self.path = from_dict['path']
@ -77,10 +78,10 @@ class URLItem(BaseItem):
self.validating_lock.release() self.validating_lock.release()
return True return True
if self.ready == 'failed': # if self.ready == 'failed':
self.validating_lock.release() # self.validating_lock.release()
return False # return False
#
if os.path.exists(self.path): if os.path.exists(self.path):
self.validating_lock.release() self.validating_lock.release()
self.ready = "yes" self.ready = "yes"
@ -97,8 +98,7 @@ class URLItem(BaseItem):
# Check the length, useful in case of playlist, it wasn't checked before) # Check the length, useful in case of playlist, it wasn't checked before)
log.info( log.info(
"url: " + self.url + " has a duration of " + str(self.duration) + " min -- too long") "url: " + self.url + " has a duration of " + str(self.duration) + " min -- too long")
self.send_client_message(constants.strings('too_long', song=self.title)) raise ValidationFailedError(constants.strings('too_long', song=self.title))
return False
else: else:
self.ready = "validated" self.ready = "validated"
self.version += 1 # notify wrapper to save me self.version += 1 # notify wrapper to save me
@ -136,8 +136,7 @@ class URLItem(BaseItem):
if not succeed: if not succeed:
self.ready = 'failed' self.ready = 'failed'
self.log.error("url: error while fetching info from the URL") self.log.error("url: error while fetching info from the URL")
self.send_client_message(constants.strings('unable_download')) raise ValidationFailedError(constants.strings('unable_download', item=self.format_title()))
return False
def _download(self): def _download(self):
media.system.clear_tmp_folder(var.tmp_folder, var.config.getint('bot', 'tmp_folder_max_size')) media.system.clear_tmp_folder(var.tmp_folder, var.config.getint('bot', 'tmp_folder_max_size'))
@ -190,10 +189,9 @@ class URLItem(BaseItem):
else: else:
for f in glob.glob(base_path + "*"): for f in glob.glob(base_path + "*"):
os.remove(f) os.remove(f)
self.send_client_message(constants.strings('unable_download'))
self.ready = "failed" self.ready = "failed"
self.downloading = False self.downloading = False
return False raise PreparationFailedError(constants.strings('unable_download', item=self.format_title()))
def _read_thumbnail_from_file(self, path_thumbnail): def _read_thumbnail_from_file(self, path_thumbnail):
if os.path.isfile(path_thumbnail): if os.path.isfile(path_thumbnail):

View File

@ -51,16 +51,15 @@ def get_playlist_info(url, start_index=0, user=""):
return items return items
def playlist_url_item_builder(bot, **kwargs): def playlist_url_item_builder(**kwargs):
return PlaylistURLItem(bot, return PlaylistURLItem(kwargs['url'],
kwargs['url'],
kwargs['title'], kwargs['title'],
kwargs['playlist_url'], kwargs['playlist_url'],
kwargs['playlist_title']) kwargs['playlist_title'])
def playlist_url_item_loader(bot, _dict): def playlist_url_item_loader(_dict):
return PlaylistURLItem(bot, "", "", "", "", _dict) return PlaylistURLItem("", "", "", "", _dict)
item_builders['url_from_playlist'] = playlist_url_item_builder item_builders['url_from_playlist'] = playlist_url_item_builder
@ -69,14 +68,14 @@ item_id_generators['url_from_playlist'] = url_item_id_generator
class PlaylistURLItem(URLItem): class PlaylistURLItem(URLItem):
def __init__(self, bot, url, title, playlist_url, playlist_title, from_dict=None): def __init__(self, url, title, playlist_url, playlist_title, from_dict=None):
if from_dict is None: if from_dict is None:
super().__init__(bot, url) super().__init__(url)
self.title = title self.title = title
self.playlist_url = playlist_url self.playlist_url = playlist_url
self.playlist_title = playlist_title self.playlist_title = playlist_title
else: else:
super().__init__(bot, "", from_dict) super().__init__("", from_dict)
self.playlist_title = from_dict['playlist_title'] self.playlist_title = from_dict['playlist_title']
self.playlist_url = from_dict['playlist_url'] self.playlist_url = from_dict['playlist_url']

View File

@ -24,6 +24,7 @@ import command
import constants import constants
from database import SettingsDatabase, MusicDatabase from database import SettingsDatabase, MusicDatabase
import media.system import media.system
from media.item import ValidationFailedError, PreparationFailedError
from media.playlist import BasePlaylist from media.playlist import BasePlaylist
from media.cache import MusicCache from media.cache import MusicCache
@ -393,14 +394,36 @@ class MumbleBot:
# however, for performance consideration, youtube playlist won't be validate when added. # however, for performance consideration, youtube playlist won't be validate when added.
# the validation has to be done here. # the validation has to be done here.
next = var.playlist.next_item() next = var.playlist.next_item()
if next.validate(): try:
next.validate()
if not next.is_ready(): if not next.is_ready():
var.playlist.async_prepare(var.playlist.next_index()) self.async_download(next)
break break
else: except ValidationFailedError as e:
self.send_channel_msg(e.msg)
var.playlist.remove_by_id(next.id) var.playlist.remove_by_id(next.id)
var.cache.free_and_delete(next.id) var.cache.free_and_delete(next.id)
def async_download(self, item):
th = threading.Thread(
target=self._download, name="Prepare-" + item.id[:7], args=(item,))
self.log.info("bot: start preparing item in thread: %s" % item.format_debug_string())
th.daemon = True
th.start()
return th
def _download(self, item):
ver = item.version
try:
item.prepare()
except PreparationFailedError as e:
self.send_channel_msg(e.msg)
return False
if item.version > ver:
var.playlist.version += 1
# ======================= # =======================
# Loop # Loop
# ======================= # =======================
@ -460,13 +483,17 @@ class MumbleBot:
if not self.wait_for_ready: # if wait_for_ready flag is not true, move to the next song. if not self.wait_for_ready: # if wait_for_ready flag is not true, move to the next song.
if var.playlist.next(): if var.playlist.next():
current = var.playlist.current_item() current = var.playlist.current_item()
if current.validate(): try:
current.validate()
if not current.is_ready(): if not current.is_ready():
self.log.info("bot: current music isn't ready, start downloading.") self.log.info("bot: current music isn't ready, start downloading.")
var.playlist.async_prepare(var.playlist.current_index) self.async_download(current)
self.send_channel_msg(constants.strings('download_in_progress', item=current.format_title())) self.send_channel_msg(
constants.strings('download_in_progress', item=current.format_title()))
self.wait_for_ready = True self.wait_for_ready = True
else:
except ValidationFailedError as e:
self.send_channel_msg(e.msg)
var.playlist.remove_by_id(current.id) var.playlist.remove_by_id(current.id)
var.cache.free_and_delete(current.id) var.cache.free_and_delete(current.id)
else: else:
@ -480,6 +507,7 @@ class MumbleBot:
self.async_download_next() self.async_download_next()
elif current.is_failed(): elif current.is_failed():
var.playlist.remove_by_id(current.id) var.playlist.remove_by_id(current.id)
self.wait_for_ready = False
else: else:
self._loop_status = 'Wait for the next item to be ready' self._loop_status = 'Wait for the next item to be ready'
else: else:
@ -709,7 +737,7 @@ if __name__ == '__main__':
command.register_all_commands(var.bot) command.register_all_commands(var.bot)
if var.config.get("bot", "refresh_cache_on_startup", fallback=True): if var.config.get("bot", "refresh_cache_on_startup", fallback=True):
var.cache.build_dir_cache(var.bot) var.cache.build_dir_cache()
# load playlist # load playlist
if var.config.getboolean('bot', 'save_playlist', fallback=True): if var.config.getboolean('bot', 'save_playlist', fallback=True):